pub struct Cell<'a> { /* private fields */ }Expand description
A mutable reference to a table cell.
Implementations§
Source§impl<'a> Cell<'a>
impl<'a> Cell<'a>
Sourcepub fn set_text(&mut self, text: &str)
pub fn set_text(&mut self, text: &str)
Set the text of the first paragraph (replacing existing content).
Examples found in repository?
examples/generate_pdf.rs (line 36)
7fn main() {
8 // Test 1: Simple document
9 let doc = Document::new();
10 match doc.to_pdf() {
11 Ok(bytes) => {
12 std::fs::write("/tmp/rdocx_simple.pdf", &bytes).unwrap();
13 println!("Simple PDF: {} bytes -> /tmp/rdocx_simple.pdf", bytes.len());
14 }
15 Err(e) => println!("Simple PDF failed: {e}"),
16 }
17
18 // Test 2: Document with content
19 let mut doc = Document::new();
20 doc.set_title("Test PDF Document");
21 doc.set_author("rdocx-pdf");
22 doc.add_paragraph("Chapter 1: Introduction")
23 .style("Heading1");
24 doc.add_paragraph(
25 "This is a test document generated by rdocx and rendered to PDF. \
26 It demonstrates text rendering with proper font shaping and pagination.",
27 );
28 doc.add_paragraph("Section 1.1").style("Heading2");
29 doc.add_paragraph("More content in a sub-section.");
30
31 {
32 let mut table = doc.add_table(2, 3);
33 for r in 0..2 {
34 for c in 0..3 {
35 if let Some(mut cell) = table.cell(r, c) {
36 cell.set_text(&format!("R{}C{}", r + 1, c + 1));
37 }
38 }
39 }
40 }
41
42 doc.add_paragraph("After the table.");
43
44 match doc.to_pdf() {
45 Ok(bytes) => {
46 std::fs::write("/tmp/rdocx_content.pdf", &bytes).unwrap();
47 println!(
48 "Content PDF: {} bytes -> /tmp/rdocx_content.pdf",
49 bytes.len()
50 );
51 }
52 Err(e) => println!("Content PDF failed: {e}"),
53 }
54
55 // Test 3: From feature_showcase.docx
56 let showcase_path = concat!(
57 env!("CARGO_MANIFEST_DIR"),
58 "/../../samples/feature_showcase.docx"
59 );
60 match Document::open(showcase_path) {
61 Ok(doc) => match doc.to_pdf() {
62 Ok(bytes) => {
63 std::fs::write("/tmp/rdocx_showcase.pdf", &bytes).unwrap();
64 println!(
65 "Showcase PDF: {} bytes -> /tmp/rdocx_showcase.pdf",
66 bytes.len()
67 );
68 }
69 Err(e) => println!("Showcase PDF failed: {e}"),
70 },
71 Err(e) => println!("Failed to open showcase: {e}"),
72 }
73}More examples
examples/template_replace.rs (line 109)
43fn create_template(path: &Path) {
44 let mut doc = Document::new();
45 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
46 doc.set_margins(
47 Length::inches(1.0),
48 Length::inches(1.0),
49 Length::inches(1.0),
50 Length::inches(1.0),
51 );
52
53 doc.set_header("{{company_name}} — Confidential");
54 doc.set_footer("Prepared by {{author_name}} on {{date}}");
55
56 // ── Title ──
57 doc.add_paragraph("{{company_name}}")
58 .style("Heading1")
59 .alignment(Alignment::Center);
60
61 doc.add_paragraph("Project Proposal")
62 .alignment(Alignment::Center);
63
64 doc.add_paragraph("");
65
66 // ── Summary section ──
67 doc.add_paragraph("Executive Summary").style("Heading2");
68
69 doc.add_paragraph(
70 "This proposal outlines the {{project_name}} project for {{company_name}}. \
71 The primary contact is {{contact_name}} ({{contact_email}}). \
72 The proposed start date is {{start_date}} with an estimated duration of {{duration}}.",
73 );
74
75 doc.add_paragraph("");
76
77 // ── Cross-run placeholder (bold label + normal value) ──
78 doc.add_paragraph("Key Details").style("Heading2");
79
80 {
81 let mut p = doc.add_paragraph("");
82 p.add_run("Project: ").bold(true);
83 p.add_run("{{project_name}}");
84 }
85 {
86 let mut p = doc.add_paragraph("");
87 p.add_run("Budget: ").bold(true);
88 p.add_run("{{budget}}");
89 }
90 {
91 let mut p = doc.add_paragraph("");
92 p.add_run("Status: ").bold(true);
93 p.add_run("{{status}}");
94 }
95
96 doc.add_paragraph("");
97
98 // ── Table with placeholders ──
99 doc.add_paragraph("Team Members").style("Heading2");
100
101 {
102 let mut tbl = doc.add_table(4, 3);
103 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
104
105 // Header row
106 for col in 0..3 {
107 tbl.cell(0, col).unwrap().shading("2E75B6");
108 }
109 tbl.cell(0, 0).unwrap().set_text("Name");
110 tbl.cell(0, 1).unwrap().set_text("Role");
111 tbl.cell(0, 2).unwrap().set_text("Email");
112
113 tbl.cell(1, 0).unwrap().set_text("{{member1_name}}");
114 tbl.cell(1, 1).unwrap().set_text("{{member1_role}}");
115 tbl.cell(1, 2).unwrap().set_text("{{member1_email}}");
116
117 tbl.cell(2, 0).unwrap().set_text("{{member2_name}}");
118 tbl.cell(2, 1).unwrap().set_text("{{member2_role}}");
119 tbl.cell(2, 2).unwrap().set_text("{{member2_email}}");
120
121 tbl.cell(3, 0).unwrap().set_text("{{member3_name}}");
122 tbl.cell(3, 1).unwrap().set_text("{{member3_role}}");
123 tbl.cell(3, 2).unwrap().set_text("{{member3_email}}");
124 }
125
126 doc.add_paragraph("");
127
128 // ── Deliverables section ──
129 doc.add_paragraph("Deliverables").style("Heading2");
130
131 doc.add_paragraph("INSERTION_POINT");
132
133 doc.add_paragraph("");
134
135 // ── Signature block ──
136 doc.add_paragraph("Acceptance").style("Heading2");
137
138 doc.add_paragraph(
139 "By signing below, {{company_name}} agrees to the terms outlined in this proposal.",
140 );
141
142 {
143 let mut tbl = doc.add_table(2, 2);
144 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
145 tbl.cell(0, 0)
146 .unwrap()
147 .set_text("Customer: ___________________");
148 tbl.cell(0, 1)
149 .unwrap()
150 .set_text("Provider: ___________________");
151 tbl.cell(1, 0).unwrap().set_text("Date: {{date}}");
152 tbl.cell(1, 1).unwrap().set_text("Date: {{date}}");
153 }
154
155 doc.set_title("{{company_name}} — Project Proposal Template");
156 doc.set_author("Template Generator");
157
158 doc.save(path).unwrap();
159}
160
161/// Open the template, replace all placeholders, insert content, and save.
162fn fill_template(template_path: &Path, output_path: &Path) {
163 let mut doc = Document::open(template_path).unwrap();
164
165 // ── Batch replacement ──
166 let mut replacements = HashMap::new();
167 replacements.insert("{{company_name}}", "Riverside Medical Center");
168 replacements.insert("{{project_name}}", "Network Security Upgrade");
169 replacements.insert("{{contact_name}}", "Dr. Sarah Chen");
170 replacements.insert("{{contact_email}}", "s.chen@riverside.org");
171 replacements.insert("{{start_date}}", "March 1, 2026");
172 replacements.insert("{{duration}}", "12 weeks");
173 replacements.insert("{{budget}}", "$185,000");
174 replacements.insert("{{status}}", "Pending Approval");
175 replacements.insert("{{author_name}}", "James Wilson");
176 replacements.insert("{{date}}", "February 22, 2026");
177
178 // Team members
179 replacements.insert("{{member1_name}}", "James Wilson");
180 replacements.insert("{{member1_role}}", "Project Lead");
181 replacements.insert("{{member1_email}}", "j.wilson@provider.com");
182 replacements.insert("{{member2_name}}", "Maria Garcia");
183 replacements.insert("{{member2_role}}", "Security Architect");
184 replacements.insert("{{member2_email}}", "m.garcia@provider.com");
185 replacements.insert("{{member3_name}}", "David Park");
186 replacements.insert("{{member3_role}}", "Network Engineer");
187 replacements.insert("{{member3_email}}", "d.park@provider.com");
188
189 let count = doc.replace_all(&replacements);
190 println!(" Replaced {} placeholders", count);
191
192 // ── Insert deliverables at the insertion point ──
193 if let Some(idx) = doc.find_content_index("INSERTION_POINT") {
194 // Remove the placeholder paragraph
195 doc.remove_content(idx);
196
197 // Insert deliverables list
198 doc.insert_paragraph(idx, "The following deliverables are included:");
199
200 // Insert a deliverables table
201 let mut tbl = doc.insert_table(idx + 1, 5, 3);
202 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
203
204 for col in 0..3 {
205 tbl.cell(0, col).unwrap().shading("E2EFDA");
206 }
207 tbl.cell(0, 0).unwrap().set_text("Phase");
208 tbl.cell(0, 1).unwrap().set_text("Description");
209 tbl.cell(0, 2).unwrap().set_text("Timeline");
210
211 tbl.cell(1, 0).unwrap().set_text("1. Discovery");
212 tbl.cell(1, 1)
213 .unwrap()
214 .set_text("Network assessment and asset inventory");
215 tbl.cell(1, 2).unwrap().set_text("Weeks 1-3");
216
217 tbl.cell(2, 0).unwrap().set_text("2. Design");
218 tbl.cell(2, 1)
219 .unwrap()
220 .set_text("Security architecture and policy design");
221 tbl.cell(2, 2).unwrap().set_text("Weeks 4-6");
222
223 tbl.cell(3, 0).unwrap().set_text("3. Implementation");
224 tbl.cell(3, 1)
225 .unwrap()
226 .set_text("Deploy monitoring and access controls");
227 tbl.cell(3, 2).unwrap().set_text("Weeks 7-10");
228
229 tbl.cell(4, 0).unwrap().set_text("4. Validation");
230 tbl.cell(4, 1)
231 .unwrap()
232 .set_text("Testing, training, and handover");
233 tbl.cell(4, 2).unwrap().set_text("Weeks 11-12");
234
235 println!(" Inserted deliverables table at position {}", idx);
236 }
237
238 // ── Update metadata ──
239 doc.set_title("Riverside Medical Center — Network Security Upgrade Proposal");
240 doc.set_author("James Wilson");
241 doc.set_subject("Project Proposal");
242 doc.set_keywords("security, network, medical, proposal");
243
244 doc.save(output_path).unwrap();
245}examples/styled_tables.rs (line 54)
24fn generate_styled_tables(path: &Path) {
25 let mut doc = Document::new();
26 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27 doc.set_margins(
28 Length::inches(0.75),
29 Length::inches(0.75),
30 Length::inches(0.75),
31 Length::inches(0.75),
32 );
33
34 doc.add_paragraph("Styled Tables Showcase")
35 .style("Heading1");
36
37 doc.add_paragraph("");
38
39 // =========================================================================
40 // 1. Professional report table with alternating rows
41 // =========================================================================
42 doc.add_paragraph("1. Report Table with Alternating Row Colors")
43 .style("Heading2");
44
45 {
46 let mut tbl = doc.add_table(8, 4);
47 tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48 tbl = tbl.width_pct(100.0);
49
50 // Header row
51 let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52 for (col, h) in headers.iter().enumerate() {
53 tbl.cell(0, col).unwrap().shading("2E75B6");
54 tbl.cell(0, col).unwrap().set_text(h);
55 }
56 tbl.row(0).unwrap().header();
57
58 // Data with alternating shading
59 let data = [
60 ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61 ["Professional", "$189,000", "$201,000", "+6.3%"],
62 ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63 ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64 ["Training", "$22,000", "$28,900", "+31.4%"],
65 ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66 ];
67
68 for (i, row) in data.iter().enumerate() {
69 let row_idx = i + 1;
70 for (col, val) in row.iter().enumerate() {
71 tbl.cell(row_idx, col).unwrap().set_text(val);
72 // Alternate row colors
73 if i % 2 == 0 {
74 tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75 }
76 }
77 }
78
79 // Total row
80 tbl.cell(7, 0).unwrap().set_text("TOTAL");
81 tbl.cell(7, 0).unwrap().shading("D6E4F0");
82 tbl.cell(7, 1).unwrap().set_text("$613,000");
83 tbl.cell(7, 1).unwrap().shading("D6E4F0");
84 tbl.cell(7, 2).unwrap().set_text("$729,900");
85 tbl.cell(7, 2).unwrap().shading("D6E4F0");
86 tbl.cell(7, 3).unwrap().set_text("+19.1%");
87 tbl.cell(7, 3).unwrap().shading("D6E4F0");
88 }
89
90 doc.add_paragraph("");
91
92 // =========================================================================
93 // 2. Invoice-style table with merged header
94 // =========================================================================
95 doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96 .style("Heading2");
97
98 {
99 let mut tbl = doc.add_table(7, 4);
100 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101 tbl = tbl.width_pct(100.0);
102
103 // Merged title row
104 tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105 tbl.cell(0, 0).unwrap().grid_span(4);
106 tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108 // Column headers
109 let headers = ["Item", "Description", "Qty", "Amount"];
110 for (col, h) in headers.iter().enumerate() {
111 tbl.cell(1, col).unwrap().set_text(h);
112 tbl.cell(1, col).unwrap().shading("D6E4F0");
113 }
114
115 // Line items
116 tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117 tbl.cell(2, 1)
118 .unwrap()
119 .set_text("Enterprise License (500 seats)");
120 tbl.cell(2, 2).unwrap().set_text("1");
121 tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123 tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124 tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125 tbl.cell(3, 2).unwrap().set_text("1");
126 tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128 tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129 tbl.cell(4, 1)
130 .unwrap()
131 .set_text("On-site Training (3 days)");
132 tbl.cell(4, 2).unwrap().set_text("1");
133 tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135 // Subtotal
136 tbl.cell(5, 0).unwrap().set_text("Subtotal");
137 tbl.cell(5, 0).unwrap().grid_span(3);
138 tbl.cell(5, 0).unwrap().shading("F2F2F2");
139 tbl.cell(5, 3).unwrap().set_text("$89,500");
140 tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142 // Total
143 tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144 tbl.cell(6, 0).unwrap().grid_span(3);
145 tbl.cell(6, 0).unwrap().shading("1F4E79");
146 tbl.cell(6, 3).unwrap().set_text("$89,500");
147 tbl.cell(6, 3).unwrap().shading("1F4E79");
148 }
149
150 doc.add_paragraph("");
151
152 // =========================================================================
153 // 3. Specification table with vertical merge
154 // =========================================================================
155 doc.add_paragraph("3. Specification Table with Vertical Merges")
156 .style("Heading2");
157
158 {
159 let mut tbl = doc.add_table(8, 3);
160 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161 tbl = tbl.width_pct(100.0);
162
163 // Header
164 tbl.cell(0, 0).unwrap().set_text("Category");
165 tbl.cell(0, 0).unwrap().shading("2E75B6");
166 tbl.cell(0, 1).unwrap().set_text("Specification");
167 tbl.cell(0, 1).unwrap().shading("2E75B6");
168 tbl.cell(0, 2).unwrap().set_text("Value");
169 tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171 // "Hardware" spans 3 rows
172 tbl.cell(1, 0).unwrap().set_text("Hardware");
173 tbl.cell(1, 0).unwrap().v_merge_restart();
174 tbl.cell(1, 0).unwrap().shading("E2EFDA");
175 tbl.cell(1, 0)
176 .unwrap()
177 .vertical_alignment(VerticalAlignment::Center);
178 tbl.cell(1, 1).unwrap().set_text("Processor");
179 tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181 tbl.cell(2, 0).unwrap().v_merge_continue();
182 tbl.cell(2, 1).unwrap().set_text("Memory");
183 tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185 tbl.cell(3, 0).unwrap().v_merge_continue();
186 tbl.cell(3, 1).unwrap().set_text("Storage");
187 tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189 // "Network" spans 2 rows
190 tbl.cell(4, 0).unwrap().set_text("Network");
191 tbl.cell(4, 0).unwrap().v_merge_restart();
192 tbl.cell(4, 0).unwrap().shading("FCE4D6");
193 tbl.cell(4, 0)
194 .unwrap()
195 .vertical_alignment(VerticalAlignment::Center);
196 tbl.cell(4, 1).unwrap().set_text("Ethernet");
197 tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199 tbl.cell(5, 0).unwrap().v_merge_continue();
200 tbl.cell(5, 1).unwrap().set_text("Management");
201 tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203 // "Software" spans 2 rows
204 tbl.cell(6, 0).unwrap().set_text("Software");
205 tbl.cell(6, 0).unwrap().v_merge_restart();
206 tbl.cell(6, 0).unwrap().shading("D6E4F0");
207 tbl.cell(6, 0)
208 .unwrap()
209 .vertical_alignment(VerticalAlignment::Center);
210 tbl.cell(6, 1).unwrap().set_text("Operating System");
211 tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213 tbl.cell(7, 0).unwrap().v_merge_continue();
214 tbl.cell(7, 1).unwrap().set_text("Monitoring");
215 tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216 }
217
218 doc.add_paragraph("");
219
220 // =========================================================================
221 // 4. Nested table (table inside a cell)
222 // =========================================================================
223 doc.add_paragraph("4. Nested Table").style("Heading2");
224
225 {
226 let mut tbl = doc.add_table(2, 2);
227 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228 tbl = tbl.width_pct(100.0);
229 tbl = tbl.cell_margins(
230 Length::twips(72),
231 Length::twips(108),
232 Length::twips(72),
233 Length::twips(108),
234 );
235
236 tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237 tbl.cell(0, 0).unwrap().shading("2E75B6");
238 tbl.cell(0, 1).unwrap().set_text("Project Beta");
239 tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241 // Nested table in cell (1,0)
242 {
243 let mut cell = tbl.cell(1, 0).unwrap();
244 cell.set_text("Milestones:");
245 let mut inner = cell.add_table(3, 2);
246 inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247 inner.cell(0, 0).unwrap().set_text("Phase");
248 inner.cell(0, 0).unwrap().shading("E2EFDA");
249 inner.cell(0, 1).unwrap().set_text("Status");
250 inner.cell(0, 1).unwrap().shading("E2EFDA");
251 inner.cell(1, 0).unwrap().set_text("Design");
252 inner.cell(1, 1).unwrap().set_text("Complete");
253 inner.cell(2, 0).unwrap().set_text("Build");
254 inner.cell(2, 1).unwrap().set_text("In Progress");
255 }
256
257 // Nested table in cell (1,1)
258 {
259 let mut cell = tbl.cell(1, 1).unwrap();
260 cell.set_text("Budget:");
261 let mut inner = cell.add_table(3, 2);
262 inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263 inner.cell(0, 0).unwrap().set_text("Category");
264 inner.cell(0, 0).unwrap().shading("FCE4D6");
265 inner.cell(0, 1).unwrap().set_text("Amount");
266 inner.cell(0, 1).unwrap().shading("FCE4D6");
267 inner.cell(1, 0).unwrap().set_text("Development");
268 inner.cell(1, 1).unwrap().set_text("$120,000");
269 inner.cell(2, 0).unwrap().set_text("Testing");
270 inner.cell(2, 1).unwrap().set_text("$35,000");
271 }
272 }
273
274 doc.add_paragraph("");
275
276 // =========================================================================
277 // 5. Form-style table with labels
278 // =========================================================================
279 doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281 {
282 let mut tbl = doc.add_table(6, 4);
283 tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284 tbl = tbl.width_pct(100.0);
285
286 // Row 0: Full-width title
287 tbl.cell(0, 0)
288 .unwrap()
289 .set_text("Customer Registration Form");
290 tbl.cell(0, 0).unwrap().grid_span(4);
291 tbl.cell(0, 0).unwrap().shading("404040");
292
293 // Row 1: Name fields
294 tbl.cell(1, 0).unwrap().set_text("First Name");
295 tbl.cell(1, 0).unwrap().shading("E8E8E8");
296 tbl.cell(1, 1).unwrap().set_text("John");
297 tbl.cell(1, 2).unwrap().set_text("Last Name");
298 tbl.cell(1, 2).unwrap().shading("E8E8E8");
299 tbl.cell(1, 3).unwrap().set_text("Smith");
300
301 // Row 2: Contact
302 tbl.cell(2, 0).unwrap().set_text("Email");
303 tbl.cell(2, 0).unwrap().shading("E8E8E8");
304 tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305 tbl.cell(2, 1).unwrap().grid_span(3);
306
307 // Row 3: Phone
308 tbl.cell(3, 0).unwrap().set_text("Phone");
309 tbl.cell(3, 0).unwrap().shading("E8E8E8");
310 tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311 tbl.cell(3, 2).unwrap().set_text("Company");
312 tbl.cell(3, 2).unwrap().shading("E8E8E8");
313 tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315 // Row 4: Address (spanning)
316 tbl.cell(4, 0).unwrap().set_text("Address");
317 tbl.cell(4, 0).unwrap().shading("E8E8E8");
318 tbl.cell(4, 1)
319 .unwrap()
320 .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321 tbl.cell(4, 1).unwrap().grid_span(3);
322
323 // Row 5: Notes
324 tbl.cell(5, 0).unwrap().set_text("Notes");
325 tbl.cell(5, 0).unwrap().shading("E8E8E8");
326 tbl.cell(5, 0)
327 .unwrap()
328 .vertical_alignment(VerticalAlignment::Top);
329 {
330 let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331 cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332 cell.add_paragraph("Annual review scheduled for March 2026.");
333 }
334 }
335
336 doc.add_paragraph("");
337
338 // =========================================================================
339 // 6. Comparison table with border styles
340 // =========================================================================
341 doc.add_paragraph("6. Comparison Table with Custom Borders")
342 .style("Heading2");
343
344 {
345 let mut tbl = doc.add_table(5, 3);
346 tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347 tbl = tbl.width_pct(100.0);
348
349 // Header
350 tbl.cell(0, 0).unwrap().set_text("Feature");
351 tbl.cell(0, 0).unwrap().shading("2E75B6");
352 tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353 tbl.cell(0, 1).unwrap().shading("2E75B6");
354 tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355 tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357 tbl.cell(1, 0).unwrap().set_text("Users");
358 tbl.cell(1, 1).unwrap().set_text("Up to 10");
359 tbl.cell(1, 2).unwrap().set_text("Unlimited");
360 tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362 tbl.cell(2, 0).unwrap().set_text("Storage");
363 tbl.cell(2, 1).unwrap().set_text("50 GB");
364 tbl.cell(2, 2).unwrap().set_text("5 TB");
365 tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367 tbl.cell(3, 0).unwrap().set_text("Support");
368 tbl.cell(3, 1).unwrap().set_text("Email only");
369 tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370 tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372 tbl.cell(4, 0).unwrap().set_text("Price");
373 tbl.cell(4, 0).unwrap().shading("F2F2F2");
374 tbl.cell(4, 1).unwrap().set_text("$29/month");
375 tbl.cell(4, 1).unwrap().shading("F2F2F2");
376 tbl.cell(4, 2).unwrap().set_text("$199/month");
377 tbl.cell(4, 2).unwrap().shading("C6EFCE");
378 }
379
380 doc.add_paragraph("");
381
382 // =========================================================================
383 // 7. Wide table with fixed layout and row height
384 // =========================================================================
385 doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386 .style("Heading2");
387
388 {
389 let mut tbl = doc.add_table(4, 5);
390 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391 tbl = tbl.width(Length::inches(7.0));
392 tbl = tbl.layout_fixed();
393
394 // Set column widths
395 for col in 0..5 {
396 tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397 }
398
399 // Header with exact height
400 tbl.row(0).unwrap().height_exact(Length::twips(480));
401 tbl.row(0).unwrap().header();
402 tbl.row(0).unwrap().cant_split();
403
404 let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405 for (col, h) in headers.iter().enumerate() {
406 tbl.cell(0, col).unwrap().set_text(h);
407 tbl.cell(0, col).unwrap().shading("404040");
408 tbl.cell(0, col)
409 .unwrap()
410 .vertical_alignment(VerticalAlignment::Center);
411 }
412
413 // Schedule rows with minimum height
414 tbl.row(1).unwrap().height(Length::twips(600));
415 tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416 tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417 tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418 tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419 tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421 tbl.row(2).unwrap().height(Length::twips(600));
422 tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423 tbl.cell(2, 0).unwrap().shading("D6E4F0");
424 tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425 tbl.cell(2, 1).unwrap().shading("FCE4D6");
426 tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427 tbl.cell(2, 2).unwrap().shading("D6E4F0");
428 tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429 tbl.cell(2, 3).unwrap().shading("E2EFDA");
430 tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431 tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433 tbl.row(3).unwrap().height(Length::twips(600));
434 tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435 tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436 tbl.cell(3, 1).unwrap().shading("D6E4F0");
437 tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438 tbl.cell(3, 2).unwrap().shading("FCE4D6");
439 tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440 tbl.cell(3, 3).unwrap().shading("D6E4F0");
441 tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442 tbl.cell(3, 4).unwrap().shading("E2EFDA");
443 }
444
445 doc.set_title("Styled Tables Showcase");
446 doc.set_author("rdocx");
447
448 doc.save(path).unwrap();
449}examples/generate_samples.rs (line 306)
34fn generate_feature_showcase(path: &Path) {
35 let mut doc = Document::new();
36
37 // =========================================================================
38 // PAGE SETUP & METADATA
39 // =========================================================================
40 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41 doc.set_margins(
42 Length::inches(1.0), // top
43 Length::inches(1.0), // right
44 Length::inches(1.0), // bottom
45 Length::inches(1.0), // left
46 );
47 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48 doc.set_gutter(Length::twips(0));
49
50 doc.set_title("rdocx Feature Showcase");
51 doc.set_author("rdocx Sample Generator");
52 doc.set_subject("Comprehensive feature demonstration");
53 doc.set_keywords("rdocx, docx, rust, sample");
54
55 // Header & Footer
56 doc.set_header("rdocx Feature Showcase");
57 doc.set_footer("Generated by rdocx — Page");
58
59 // Different first page header
60 doc.set_different_first_page(true);
61 doc.set_first_page_header("rdocx");
62 doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64 // =========================================================================
65 // PAGE 1: COVER PAGE — background image, run formatting
66 // =========================================================================
67 let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68 doc.add_background_image(&bg_cover, "cover_bg.png");
69
70 doc.add_paragraph(""); // spacer
71 doc.add_paragraph(""); // spacer
72 doc.add_paragraph(""); // spacer
73
74 {
75 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77 }
78 {
79 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80 p.add_run("Feature Showcase")
81 .size(28.0)
82 .color("FFFFFF")
83 .italic(true);
84 }
85
86 doc.add_paragraph(""); // spacer
87
88 {
89 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90 p.add_run("A comprehensive demonstration of every feature")
91 .size(14.0)
92 .color("CCDDFF");
93 }
94 {
95 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96 p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97 .size(14.0)
98 .color("CCDDFF");
99 }
100
101 // =========================================================================
102 // PAGE 2: TEXT FORMATTING
103 // =========================================================================
104 doc.add_paragraph("").page_break_before(true);
105
106 doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108 doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109 doc.add_paragraph("");
110
111 // --- Paragraph alignment ---
112 doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114 doc.add_paragraph("This paragraph is left-aligned (the default).")
115 .alignment(Alignment::Left);
116 doc.add_paragraph("This paragraph is center-aligned.")
117 .alignment(Alignment::Center);
118 doc.add_paragraph("This paragraph is right-aligned.")
119 .alignment(Alignment::Right);
120 doc.add_paragraph(
121 "This paragraph is justified. To demonstrate justified text properly, it needs \
122 to be long enough to span multiple lines so the word spacing adjustment is visible \
123 across the full width of the text area on the page.",
124 )
125 .alignment(Alignment::Justify);
126
127 doc.add_paragraph("");
128
129 // --- Run formatting ---
130 doc.add_paragraph("Run Formatting").style("Heading2");
131
132 {
133 let mut p = doc.add_paragraph("");
134 p.add_run("Bold text").bold(true);
135 p.add_run(" | ");
136 p.add_run("Italic text").italic(true);
137 p.add_run(" | ");
138 p.add_run("Bold + Italic").bold(true).italic(true);
139 }
140 {
141 let mut p = doc.add_paragraph("");
142 p.add_run("Single underline").underline(true);
143 p.add_run(" | ");
144 p.add_run("Strikethrough").strike(true);
145 p.add_run(" | ");
146 p.add_run("Double strikethrough").double_strike(true);
147 }
148 {
149 let mut p = doc.add_paragraph("");
150 p.add_run("Red text").color("FF0000");
151 p.add_run(" | ");
152 p.add_run("Blue text").color("0000FF");
153 p.add_run(" | ");
154 p.add_run("Green text").color("00AA00");
155 p.add_run(" | ");
156 p.add_run("Highlighted").highlight("FFFF00");
157 }
158 {
159 let mut p = doc.add_paragraph("");
160 p.add_run("8pt small").size(8.0);
161 p.add_run(" | ");
162 p.add_run("11pt normal").size(11.0);
163 p.add_run(" | ");
164 p.add_run("16pt large").size(16.0);
165 p.add_run(" | ");
166 p.add_run("24pt extra-large").size(24.0);
167 }
168 {
169 let mut p = doc.add_paragraph("");
170 p.add_run("Arial font").font("Arial");
171 p.add_run(" | ");
172 p.add_run("Times New Roman font").font("Times New Roman");
173 p.add_run(" | ");
174 p.add_run("Courier New font").font("Courier New");
175 }
176 {
177 let mut p = doc.add_paragraph("");
178 p.add_run("Normal");
179 p.add_run(" H").size(11.0);
180 p.add_run("2").subscript();
181 p.add_run("O (subscript)").size(11.0);
182 p.add_run(" | E = mc").size(11.0);
183 p.add_run("2").superscript();
184 p.add_run(" (superscript)").size(11.0);
185 }
186 {
187 let mut p = doc.add_paragraph("");
188 p.add_run("ALL CAPS").all_caps(true);
189 p.add_run(" | ");
190 p.add_run("Small Caps").small_caps(true);
191 p.add_run(" | ");
192 p.add_run("Expanded spacing")
193 .character_spacing(Length::twips(40));
194 }
195
196 doc.add_paragraph("");
197
198 // --- Paragraph formatting ---
199 doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201 doc.add_paragraph("Paragraph with shading (light green background)")
202 .shading("E2EFDA");
203
204 doc.add_paragraph("Paragraph with bottom border")
205 .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207 doc.add_paragraph("Paragraph with all borders")
208 .border_all(BorderStyle::Single, 4, "FF0000");
209
210 doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211 .indent_left(Length::inches(1.0))
212 .hanging_indent(Length::inches(0.5));
213
214 doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215 .first_line_indent(Length::inches(0.5));
216
217 doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218 .space_before(Length::pt(24.0))
219 .space_after(Length::pt(12.0));
220
221 doc.add_paragraph(
222 "Paragraph with double line spacing. This text should have extra vertical \
223 space between lines to demonstrate the line_spacing_multiple setting.",
224 )
225 .line_spacing_multiple(2.0);
226
227 doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228 .keep_with_next(true);
229 doc.add_paragraph("(This stays with the paragraph above.)");
230
231 // =========================================================================
232 // PAGE 3: LISTS & TAB STOPS
233 // =========================================================================
234 doc.add_paragraph("").page_break_before(true);
235
236 doc.add_paragraph("2. Lists").style("Heading1");
237
238 doc.add_paragraph("Bullet List").style("Heading2");
239
240 doc.add_bullet_list_item("First bullet item", 0);
241 doc.add_bullet_list_item("Second bullet item", 0);
242 doc.add_bullet_list_item("Nested level 1", 1);
243 doc.add_bullet_list_item("Nested level 2", 2);
244 doc.add_bullet_list_item("Back to level 1", 1);
245 doc.add_bullet_list_item("Third bullet item", 0);
246
247 doc.add_paragraph("");
248
249 doc.add_paragraph("Numbered List").style("Heading2");
250
251 doc.add_numbered_list_item("First numbered item", 0);
252 doc.add_numbered_list_item("Second numbered item", 0);
253 doc.add_numbered_list_item("Sub-item A", 1);
254 doc.add_numbered_list_item("Sub-item B", 1);
255 doc.add_numbered_list_item("Third numbered item", 0);
256
257 doc.add_paragraph("");
258
259 // --- Tab stops ---
260 doc.add_paragraph("Tab Stops").style("Heading2");
261
262 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268 doc.add_paragraph("Item\t........\tPrice")
269 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273 doc.add_paragraph("Widget A\t........\t$19.99")
274 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278 doc.add_paragraph("Gadget B\t________\t$249.50")
279 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280 .add_tab_stop_with_leader(
281 TabAlignment::Right,
282 Length::inches(4.0),
283 TabLeader::Underscore,
284 )
285 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287 // =========================================================================
288 // PAGE 4: TABLES
289 // =========================================================================
290 doc.add_paragraph("").page_break_before(true);
291
292 doc.add_paragraph("3. Tables").style("Heading1");
293
294 // --- Basic table with borders ---
295 doc.add_paragraph("Basic Table with Borders")
296 .style("Heading2");
297
298 {
299 let mut tbl = doc.add_table(4, 3);
300 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302 // Header row
303 for col in 0..3 {
304 tbl.cell(0, col).unwrap().shading("2E75B6");
305 }
306 tbl.cell(0, 0).unwrap().set_text("Name");
307 tbl.cell(0, 1).unwrap().set_text("Role");
308 tbl.cell(0, 2).unwrap().set_text("Location");
309
310 tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311 tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312 tbl.cell(1, 2).unwrap().set_text("New York");
313
314 tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315 tbl.cell(2, 1).unwrap().set_text("Product Manager");
316 tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318 tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319 tbl.cell(3, 1).unwrap().set_text("Designer");
320 tbl.cell(3, 2).unwrap().set_text("London");
321 }
322
323 doc.add_paragraph("");
324
325 // --- Table with cell merging ---
326 doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327 .style("Heading2");
328
329 {
330 let mut tbl = doc.add_table(4, 4);
331 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332 tbl = tbl.width_pct(100.0);
333
334 // Header spanning all columns
335 tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336 tbl.cell(0, 0).unwrap().shading("1F4E79");
337 tbl.cell(0, 0).unwrap().grid_span(4);
338
339 // Sub-header
340 tbl.cell(1, 0).unwrap().set_text("Region");
341 tbl.cell(1, 0).unwrap().shading("D6E4F0");
342 tbl.cell(1, 1).unwrap().set_text("Q1");
343 tbl.cell(1, 1).unwrap().shading("D6E4F0");
344 tbl.cell(1, 2).unwrap().set_text("Q2");
345 tbl.cell(1, 2).unwrap().shading("D6E4F0");
346 tbl.cell(1, 3).unwrap().set_text("Total");
347 tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349 // Data
350 tbl.cell(2, 0).unwrap().set_text("North America");
351 tbl.cell(2, 1).unwrap().set_text("$2.4M");
352 tbl.cell(2, 2).unwrap().set_text("$2.7M");
353 tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355 tbl.cell(3, 0).unwrap().set_text("Europe");
356 tbl.cell(3, 1).unwrap().set_text("$1.8M");
357 tbl.cell(3, 2).unwrap().set_text("$2.0M");
358 tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360 // Vertical alignment on data cells
361 tbl.cell(2, 3)
362 .unwrap()
363 .vertical_alignment(VerticalAlignment::Center);
364 tbl.cell(3, 3)
365 .unwrap()
366 .vertical_alignment(VerticalAlignment::Bottom);
367 }
368
369 doc.add_paragraph("");
370
371 // --- Table with vertical merge ---
372 doc.add_paragraph("Table with Vertical Merge")
373 .style("Heading2");
374
375 {
376 let mut tbl = doc.add_table(4, 3);
377 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379 tbl.cell(0, 0).unwrap().set_text("Category");
380 tbl.cell(0, 0).unwrap().shading("E2EFDA");
381 tbl.cell(0, 1).unwrap().set_text("Item");
382 tbl.cell(0, 1).unwrap().shading("E2EFDA");
383 tbl.cell(0, 2).unwrap().set_text("Price");
384 tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386 // "Hardware" spans rows 1-2
387 tbl.cell(1, 0).unwrap().set_text("Hardware");
388 tbl.cell(1, 0).unwrap().v_merge_restart();
389 tbl.cell(1, 1).unwrap().set_text("Laptop");
390 tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392 tbl.cell(2, 0).unwrap().v_merge_continue();
393 tbl.cell(2, 1).unwrap().set_text("Monitor");
394 tbl.cell(2, 2).unwrap().set_text("$450");
395
396 // "Software" on row 3
397 tbl.cell(3, 0).unwrap().set_text("Software");
398 tbl.cell(3, 1).unwrap().set_text("IDE License");
399 tbl.cell(3, 2).unwrap().set_text("$200/yr");
400 }
401
402 doc.add_paragraph("");
403
404 // --- Nested table ---
405 doc.add_paragraph("Nested Table").style("Heading2");
406
407 {
408 let mut tbl = doc.add_table(2, 2);
409 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411 tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412 tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413 tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415 // Nested table inside cell (1,1)
416 {
417 let mut cell = tbl.cell(1, 1).unwrap();
418 cell.set_text("Contains nested table:");
419 let mut nested = cell.add_table(2, 2);
420 nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421 nested.cell(0, 0).unwrap().set_text("Inner A");
422 nested.cell(0, 1).unwrap().set_text("Inner B");
423 nested.cell(1, 0).unwrap().set_text("Inner C");
424 nested.cell(1, 1).unwrap().set_text("Inner D");
425 }
426 }
427
428 // =========================================================================
429 // PAGE 5: IMAGES
430 // =========================================================================
431 doc.add_paragraph("").page_break_before(true);
432
433 doc.add_paragraph("4. Images").style("Heading1");
434
435 doc.add_paragraph("Inline Image").style("Heading2");
436
437 doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438 let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439 doc.add_picture(
440 &inline_img,
441 "inline_chart.png",
442 Length::inches(3.0),
443 Length::inches(0.75),
444 );
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("Header Image").style("Heading2");
449
450 // Replace the text-only header with an image header
451 let header_img = create_sample_png(400, 40, [40, 40, 40]);
452 doc.set_header_image(
453 &header_img,
454 "header_logo.png",
455 Length::inches(2.0),
456 Length::inches(0.2),
457 );
458
459 doc.add_paragraph(
460 "The document header has been replaced with an inline image. \
461 Check the header area at the top of this page.",
462 );
463
464 doc.add_paragraph("");
465 doc.add_paragraph(
466 "Note: The cover page uses a full-page background image behind the text, \
467 demonstrated on page 1 via add_background_image().",
468 );
469
470 // =========================================================================
471 // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472 // =========================================================================
473 doc.add_paragraph("").page_break_before(true);
474
475 doc.add_paragraph("5. Content Manipulation")
476 .style("Heading1");
477
478 // --- Placeholder replacement ---
479 doc.add_paragraph("Placeholder Replacement")
480 .style("Heading2");
481
482 doc.add_paragraph(
483 "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484 );
485
486 {
487 let mut p = doc.add_paragraph("");
488 p.add_run("Customer: ").bold(true);
489 p.add_run("{{customer}}");
490 }
491 {
492 let mut p = doc.add_paragraph("");
493 p.add_run("Date: ").bold(true);
494 p.add_run("{{date}}");
495 }
496 doc.add_paragraph("Reference: {{ref_number}}");
497
498 // Table with placeholders
499 {
500 let mut tbl = doc.add_table(3, 2);
501 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502 tbl.cell(0, 0).unwrap().set_text("Field");
503 tbl.cell(0, 0).unwrap().shading("D6E4F0");
504 tbl.cell(0, 1).unwrap().set_text("Value");
505 tbl.cell(0, 1).unwrap().shading("D6E4F0");
506 tbl.cell(1, 0).unwrap().set_text("Project");
507 tbl.cell(1, 1).unwrap().set_text("{{project}}");
508 tbl.cell(2, 0).unwrap().set_text("Status");
509 tbl.cell(2, 1).unwrap().set_text("{{status}}");
510 }
511
512 // Perform replacements
513 let mut replacements = HashMap::new();
514 replacements.insert("{{customer}}", "Acme Corporation");
515 replacements.insert("{{date}}", "February 22, 2026");
516 replacements.insert("{{ref_number}}", "REF-2026-001");
517 replacements.insert("{{project}}", "Infrastructure Upgrade");
518 replacements.insert("{{status}}", "In Progress");
519 let replace_count = doc.replace_all(&replacements);
520
521 doc.add_paragraph("");
522 doc.add_paragraph(&format!(
523 "(Replaced {} placeholders above — in body text and table cells)",
524 replace_count
525 ));
526
527 doc.add_paragraph("");
528
529 // --- Content insertion ---
530 doc.add_paragraph("Content Insertion").style("Heading2");
531
532 doc.add_paragraph("Section A: First section of content.");
533 doc.add_paragraph("Section C: Third section of content.");
534
535 // Insert "Section B" between A and C
536 if let Some(idx) = doc.find_content_index("Section C") {
537 doc.insert_paragraph(
538 idx,
539 "Section B: Inserted between A and C using find_content_index().",
540 );
541 }
542
543 doc.add_paragraph("");
544 doc.add_paragraph(
545 "The paragraph above ('Section B') was inserted at a specific position \
546 using find_content_index() + insert_paragraph().",
547 );
548
549 // =========================================================================
550 // PAGE 7: LANDSCAPE — section break, wide table
551 // =========================================================================
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554 doc.add_paragraph("6. Mixed Page Orientation")
555 .style("Heading1");
556
557 doc.add_paragraph(
558 "This page is in LANDSCAPE orientation. It was created using a section break \
559 followed by section_landscape(). This is useful for wide tables or charts.",
560 );
561
562 doc.add_paragraph("");
563
564 // Wide table for landscape
565 {
566 let mut tbl = doc.add_table(4, 7);
567 tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570 for (col, h) in headers.iter().enumerate() {
571 tbl.cell(0, col).unwrap().set_text(h);
572 tbl.cell(0, col).unwrap().shading("2E75B6");
573 }
574
575 let data = [
576 [
577 "North America",
578 "$1.2M",
579 "$1.3M",
580 "$1.4M",
581 "$1.5M",
582 "$1.6M",
583 "$7.0M",
584 ],
585 [
586 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587 ],
588 [
589 "Asia Pacific",
590 "$0.5M",
591 "$0.6M",
592 "$0.7M",
593 "$0.7M",
594 "$0.8M",
595 "$3.3M",
596 ],
597 ];
598 for (row_idx, row_data) in data.iter().enumerate() {
599 for (col, val) in row_data.iter().enumerate() {
600 tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601 }
602 }
603 }
604
605 // End landscape, return to portrait
606 doc.add_paragraph("")
607 .section_break(SectionBreak::NextPage)
608 .section_landscape();
609
610 // =========================================================================
611 // PAGE 8: BACK TO PORTRAIT — styles, final notes
612 // =========================================================================
613 doc.add_paragraph("7. Custom Styles & Summary")
614 .style("Heading1");
615
616 doc.add_paragraph(
617 "This final page is back in portrait orientation after a section break. \
618 The document has demonstrated:",
619 );
620
621 doc.add_bullet_list_item(
622 "Page setup: size, margins, header/footer distance, gutter",
623 0,
624 );
625 doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626 doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627 doc.add_bullet_list_item("Background images: full-page behind text", 0);
628 doc.add_bullet_list_item(
629 "Text formatting: bold, italic, underline, strike, color, size, font",
630 0,
631 );
632 doc.add_bullet_list_item(
633 "Advanced run formatting: superscript, subscript, caps, spacing",
634 0,
635 );
636 doc.add_bullet_list_item(
637 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638 0,
639 );
640 doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641 doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642 doc.add_bullet_list_item(
643 "Tables: borders, shading, column spans, row spans, nesting",
644 0,
645 );
646 doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647 doc.add_bullet_list_item("Inline images", 0);
648 doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649 doc.add_bullet_list_item("Content insertion at specific positions", 0);
650 doc.add_bullet_list_item(
651 "Section breaks with mixed portrait/landscape orientation",
652 0,
653 );
654
655 doc.add_paragraph("");
656 doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657 .alignment(Alignment::Center)
658 .shading("E2EFDA")
659 .border_all(BorderStyle::Single, 2, "00AA00");
660
661 doc.save(path).unwrap();
662}examples/generate_all_samples.rs (line 359)
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}
954
955// =============================================================================
956// 3. QUOTE / BILL OF MATERIALS — Teal + orange scheme
957// =============================================================================
958fn generate_quote(_samples_dir: &Path) -> Document {
959 let mut doc = Document::new();
960
961 // Colors: Teal #008B8B, Dark #1A3C3C, Orange #E07020, Light #F0F8F8
962 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
963 doc.set_margins(
964 Length::inches(0.75),
965 Length::inches(0.75),
966 Length::inches(0.75),
967 Length::inches(0.75),
968 );
969 doc.set_title("Quotation QT-2026-0147");
970 doc.set_author("Walter White");
971 doc.set_keywords("quote, BOM, Tensorbee");
972
973 doc.set_header("Tensorbee — Quotation");
974 doc.set_footer("QT-2026-0147 | Page");
975
976 // ── Header Block ──
977 {
978 let mut p = doc.add_paragraph("").alignment(Alignment::Left);
979 p.add_run("TENSORBEE")
980 .bold(true)
981 .size(28.0)
982 .color("008B8B")
983 .font("Helvetica");
984 }
985 doc.add_paragraph("123 Innovation Drive, Suite 400")
986 .alignment(Alignment::Left);
987 doc.add_paragraph("San Francisco, CA 94105 | +1 (415) 555-0199")
988 .alignment(Alignment::Left);
989 doc.add_paragraph("")
990 .border_bottom(BorderStyle::Single, 8, "008B8B");
991
992 // Quote meta
993 doc.add_paragraph("");
994 {
995 let mut tbl = doc.add_table(4, 4).width_pct(100.0);
996 tbl.cell(0, 0).unwrap().set_text("QUOTATION");
997 tbl.cell(0, 0).unwrap().shading("008B8B").grid_span(2);
998 tbl.cell(0, 2).unwrap().set_text("DATE");
999 tbl.cell(0, 2).unwrap().shading("008B8B");
1000 tbl.cell(0, 3).unwrap().set_text("VALID UNTIL");
1001 tbl.cell(0, 3).unwrap().shading("008B8B");
1002 tbl.cell(1, 0).unwrap().set_text("Quote #:");
1003 tbl.cell(1, 1).unwrap().set_text("QT-2026-0147");
1004 tbl.cell(1, 2).unwrap().set_text("Feb 22, 2026");
1005 tbl.cell(1, 3).unwrap().set_text("Mar 22, 2026");
1006 tbl.cell(2, 0).unwrap().set_text("Prepared by:");
1007 tbl.cell(2, 1).unwrap().set_text("Walter White");
1008 tbl.cell(2, 2).unwrap().set_text("Payment:");
1009 tbl.cell(2, 3).unwrap().set_text("Net 30");
1010 tbl.cell(3, 0).unwrap().set_text("Customer:");
1011 tbl.cell(3, 1).unwrap().set_text("Meridian Dynamics LLC");
1012 tbl.cell(3, 2).unwrap().set_text("Currency:");
1013 tbl.cell(3, 3).unwrap().set_text("USD");
1014 }
1015
1016 doc.add_paragraph("");
1017
1018 // ── Bill of Materials ──
1019 {
1020 let mut p = doc.add_paragraph("");
1021 p.add_run("Bill of Materials")
1022 .bold(true)
1023 .size(16.0)
1024 .color("1A3C3C");
1025 }
1026 doc.add_paragraph("");
1027
1028 {
1029 let mut tbl = doc
1030 .add_table(10, 6)
1031 .borders(BorderStyle::Single, 2, "008B8B")
1032 .width_pct(100.0)
1033 .layout_fixed();
1034 // Headers
1035 let hdrs = [
1036 "#",
1037 "Part Number",
1038 "Description",
1039 "Qty",
1040 "Unit Price",
1041 "Total",
1042 ];
1043 for (c, h) in hdrs.iter().enumerate() {
1044 tbl.cell(0, c).unwrap().set_text(h);
1045 tbl.cell(0, c).unwrap().shading("008B8B");
1046 }
1047
1048 let items: Vec<(&str, &str, &str, &str, &str)> = vec![
1049 (
1050 "TB-GPU-A100",
1051 "NVIDIA A100 80GB GPU",
1052 "4",
1053 "$12,500.00",
1054 "$50,000.00",
1055 ),
1056 (
1057 "TB-SRV-R750",
1058 "Dell R750xa Server Chassis",
1059 "2",
1060 "$8,200.00",
1061 "$16,400.00",
1062 ),
1063 (
1064 "TB-RAM-256G",
1065 "256GB DDR5 ECC Memory Module",
1066 "8",
1067 "$890.00",
1068 "$7,120.00",
1069 ),
1070 (
1071 "TB-SSD-3840",
1072 "3.84TB NVMe U.2 SSD",
1073 "8",
1074 "$1,150.00",
1075 "$9,200.00",
1076 ),
1077 (
1078 "TB-NET-CX7",
1079 "ConnectX-7 200GbE NIC",
1080 "4",
1081 "$1,800.00",
1082 "$7,200.00",
1083 ),
1084 (
1085 "TB-SW-48P",
1086 "48-Port 100GbE Switch",
1087 "1",
1088 "$22,000.00",
1089 "$22,000.00",
1090 ),
1091 (
1092 "TB-CAB-RACK",
1093 "42U Server Rack + PDU",
1094 "1",
1095 "$4,500.00",
1096 "$4,500.00",
1097 ),
1098 (
1099 "TB-SVC-INST",
1100 "Installation & Configuration",
1101 "1",
1102 "$8,500.00",
1103 "$8,500.00",
1104 ),
1105 (
1106 "TB-SVC-SUPP",
1107 "3-Year Premium Support",
1108 "1",
1109 "$15,000.00",
1110 "$15,000.00",
1111 ),
1112 ];
1113
1114 for (i, (pn, desc, qty, unit, total)) in items.iter().enumerate() {
1115 let row = i + 1;
1116 tbl.cell(row, 0).unwrap().set_text(&format!("{}", i + 1));
1117 tbl.cell(row, 1).unwrap().set_text(pn);
1118 tbl.cell(row, 2).unwrap().set_text(desc);
1119 tbl.cell(row, 3).unwrap().set_text(qty);
1120 tbl.cell(row, 4).unwrap().set_text(unit);
1121 tbl.cell(row, 5).unwrap().set_text(total);
1122 if i % 2 == 0 {
1123 for c in 0..6 {
1124 tbl.cell(row, c).unwrap().shading("F0F8F8");
1125 }
1126 }
1127 }
1128 }
1129
1130 doc.add_paragraph("");
1131
1132 // ── Totals ──
1133 {
1134 let mut tbl = doc
1135 .add_table(4, 2)
1136 .borders(BorderStyle::Single, 2, "008B8B")
1137 .width(Length::inches(3.5))
1138 .alignment(Alignment::Right);
1139 tbl.cell(0, 0).unwrap().set_text("Subtotal");
1140 tbl.cell(0, 1).unwrap().set_text("$139,920.00");
1141 tbl.cell(1, 0).unwrap().set_text("Shipping & Handling");
1142 tbl.cell(1, 1).unwrap().set_text("$2,500.00");
1143 tbl.cell(2, 0).unwrap().set_text("Tax (8.625%)");
1144 tbl.cell(2, 1).unwrap().set_text("$12,068.10");
1145 tbl.cell(3, 0).unwrap().set_text("TOTAL");
1146 tbl.cell(3, 0).unwrap().shading("E07020");
1147 tbl.cell(3, 1).unwrap().set_text("$154,488.10");
1148 tbl.cell(3, 1).unwrap().shading("E07020");
1149 }
1150
1151 doc.add_paragraph("");
1152
1153 // ── Terms & Conditions ──
1154 {
1155 let mut p = doc.add_paragraph("");
1156 p.add_run("Terms & Conditions")
1157 .bold(true)
1158 .size(14.0)
1159 .color("1A3C3C");
1160 }
1161 doc.add_numbered_list_item(
1162 "This quotation is valid for 30 calendar days from the date of issue.",
1163 0,
1164 );
1165 doc.add_numbered_list_item(
1166 "All prices are in USD and exclusive of applicable taxes unless stated.",
1167 0,
1168 );
1169 doc.add_numbered_list_item("Standard lead time is 4-6 weeks from PO receipt.", 0);
1170 doc.add_numbered_list_item(
1171 "Payment terms: Net 30 from invoice date. 2% discount for payment within 10 days.",
1172 0,
1173 );
1174 doc.add_numbered_list_item(
1175 "Warranty: 3-year manufacturer warranty on all hardware components.",
1176 0,
1177 );
1178 doc.add_numbered_list_item(
1179 "Returns subject to 15% restocking fee if initiated after 14 days.",
1180 0,
1181 );
1182
1183 doc.add_paragraph("");
1184 doc.add_paragraph("")
1185 .border_bottom(BorderStyle::Single, 4, "008B8B");
1186 doc.add_paragraph("");
1187 {
1188 let mut p = doc.add_paragraph("");
1189 p.add_run("Accepted by: ").bold(true);
1190 p.add_run("___________________________ Date: ___________");
1191 }
1192 {
1193 let mut p = doc.add_paragraph("");
1194 p.add_run("Print Name: ").bold(true);
1195 p.add_run("___________________________ Title: ___________");
1196 }
1197
1198 doc
1199}
1200
1201// =============================================================================
1202// 4. INVOICE — Crimson + charcoal scheme
1203// =============================================================================
1204fn generate_invoice(_samples_dir: &Path) -> Document {
1205 let mut doc = Document::new();
1206
1207 // Colors: Crimson #B22222, Charcoal #333333, Light #FAF0F0
1208 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1209 doc.set_margins(
1210 Length::inches(0.75),
1211 Length::inches(0.75),
1212 Length::inches(0.75),
1213 Length::inches(0.75),
1214 );
1215 doc.set_title("Invoice INV-2026-0392");
1216 doc.set_author("Walter White");
1217 doc.set_keywords("invoice, Tensorbee");
1218
1219 doc.set_footer("Tensorbee — Thank you for your business!");
1220
1221 // ── Company & Invoice Header ──
1222 {
1223 let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1224 // Company name left, INVOICE right
1225 {
1226 let mut cell = tbl.cell(0, 0).unwrap();
1227 let mut p = cell.add_paragraph("");
1228 p.add_run("TENSORBEE")
1229 .bold(true)
1230 .size(32.0)
1231 .color("B22222")
1232 .font("Helvetica");
1233 }
1234 {
1235 let mut cell = tbl.cell(0, 1).unwrap();
1236 let mut p = cell.add_paragraph("");
1237 p.add_run("INVOICE").bold(true).size(32.0).color("333333");
1238 }
1239 tbl.cell(1, 0)
1240 .unwrap()
1241 .set_text("123 Innovation Drive, Suite 400");
1242 tbl.cell(1, 1).unwrap().set_text("Invoice #: INV-2026-0392");
1243 tbl.cell(2, 0).unwrap().set_text("San Francisco, CA 94105");
1244 tbl.cell(2, 1).unwrap().set_text("Date: February 22, 2026");
1245 tbl.cell(3, 0).unwrap().set_text("walter@tensorbee.com");
1246 tbl.cell(3, 1).unwrap().set_text("Due Date: March 24, 2026");
1247 }
1248
1249 doc.add_paragraph("")
1250 .border_bottom(BorderStyle::Thick, 8, "B22222");
1251 doc.add_paragraph("");
1252
1253 // ── Bill To / Ship To ──
1254 {
1255 let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1256 tbl.cell(0, 0).unwrap().set_text("BILL TO");
1257 tbl.cell(0, 0).unwrap().shading("B22222");
1258 tbl.cell(0, 1).unwrap().set_text("SHIP TO");
1259 tbl.cell(0, 1).unwrap().shading("B22222");
1260 tbl.cell(1, 0).unwrap().set_text("Meridian Dynamics LLC");
1261 tbl.cell(1, 1).unwrap().set_text("Meridian Dynamics LLC");
1262 tbl.cell(2, 0).unwrap().set_text("456 Enterprise Blvd");
1263 tbl.cell(2, 1).unwrap().set_text("Attn: Server Room B");
1264 tbl.cell(3, 0).unwrap().set_text("Austin, TX 78701");
1265 tbl.cell(3, 1)
1266 .unwrap()
1267 .set_text("456 Enterprise Blvd, Austin, TX 78701");
1268 }
1269
1270 doc.add_paragraph("");
1271
1272 // ── Line Items ──
1273 {
1274 let mut tbl = doc
1275 .add_table(8, 5)
1276 .borders(BorderStyle::Single, 2, "B22222")
1277 .width_pct(100.0);
1278 let hdrs = ["Description", "Qty", "Unit Price", "Tax", "Amount"];
1279 for (c, h) in hdrs.iter().enumerate() {
1280 tbl.cell(0, c).unwrap().set_text(h);
1281 tbl.cell(0, c).unwrap().shading("B22222");
1282 }
1283 let items = [
1284 (
1285 "ML Infrastructure Setup — Phase 1",
1286 "1",
1287 "$45,000.00",
1288 "$3,881.25",
1289 "$48,881.25",
1290 ),
1291 (
1292 "Data Pipeline Development (160 hrs)",
1293 "160",
1294 "$225.00",
1295 "$3,105.00",
1296 "$39,105.00",
1297 ),
1298 (
1299 "GPU Cluster Configuration",
1300 "1",
1301 "$12,000.00",
1302 "$1,035.00",
1303 "$13,035.00",
1304 ),
1305 (
1306 "API Gateway Implementation",
1307 "1",
1308 "$18,500.00",
1309 "$1,595.63",
1310 "$20,095.63",
1311 ),
1312 (
1313 "Load Testing & QA (80 hrs)",
1314 "80",
1315 "$195.00",
1316 "$1,345.50",
1317 "$16,945.50",
1318 ),
1319 (
1320 "Documentation & Training",
1321 "1",
1322 "$8,000.00",
1323 "$690.00",
1324 "$8,690.00",
1325 ),
1326 (
1327 "Project Management (3 months)",
1328 "3",
1329 "$6,500.00",
1330 "$1,679.63",
1331 "$21,179.63",
1332 ),
1333 ];
1334 for (i, (desc, qty, unit, tax, amt)) in items.iter().enumerate() {
1335 let r = i + 1;
1336 tbl.cell(r, 0).unwrap().set_text(desc);
1337 tbl.cell(r, 1).unwrap().set_text(qty);
1338 tbl.cell(r, 2).unwrap().set_text(unit);
1339 tbl.cell(r, 3).unwrap().set_text(tax);
1340 tbl.cell(r, 4).unwrap().set_text(amt);
1341 if i % 2 == 0 {
1342 for c in 0..5 {
1343 tbl.cell(r, c).unwrap().shading("FAF0F0");
1344 }
1345 }
1346 }
1347 }
1348
1349 doc.add_paragraph("");
1350
1351 // ── Totals ──
1352 {
1353 let mut tbl = doc
1354 .add_table(4, 2)
1355 .borders(BorderStyle::Single, 2, "B22222")
1356 .width(Length::inches(3.0))
1357 .alignment(Alignment::Right);
1358 tbl.cell(0, 0).unwrap().set_text("Subtotal");
1359 tbl.cell(0, 1).unwrap().set_text("$154,600.00");
1360 tbl.cell(1, 0).unwrap().set_text("Tax (8.625%)");
1361 tbl.cell(1, 1).unwrap().set_text("$13,334.25");
1362 tbl.cell(2, 0).unwrap().set_text("Discount (5%)");
1363 tbl.cell(2, 1).unwrap().set_text("-$7,730.00");
1364 tbl.cell(3, 0).unwrap().set_text("AMOUNT DUE");
1365 tbl.cell(3, 0).unwrap().shading("B22222");
1366 tbl.cell(3, 1).unwrap().set_text("$160,204.25");
1367 tbl.cell(3, 1).unwrap().shading("B22222");
1368 }
1369
1370 doc.add_paragraph("");
1371 doc.add_paragraph("");
1372
1373 // ── Payment Details ──
1374 {
1375 let mut p = doc.add_paragraph("");
1376 p.add_run("Payment Details")
1377 .bold(true)
1378 .size(14.0)
1379 .color("333333");
1380 }
1381 doc.add_paragraph("Bank: Silicon Valley Bank")
1382 .indent_left(Length::inches(0.3));
1383 doc.add_paragraph("Account: Tensorbee Inc. — 0847-2953-1120")
1384 .indent_left(Length::inches(0.3));
1385 doc.add_paragraph("Routing: 121140399")
1386 .indent_left(Length::inches(0.3));
1387 doc.add_paragraph("Swift: SVBKUS6S")
1388 .indent_left(Length::inches(0.3));
1389
1390 doc.add_paragraph("");
1391
1392 doc.add_paragraph("Please include invoice number INV-2026-0392 in the payment reference.")
1393 .shading("FAF0F0")
1394 .border_all(BorderStyle::Single, 2, "B22222");
1395
1396 doc
1397}
1398
1399// =============================================================================
1400// 5. REPORT — Forest green + earth tones, with images & hierarchical sections
1401// =============================================================================
1402fn generate_report(_samples_dir: &Path) -> Document {
1403 let mut doc = Document::new();
1404
1405 // Colors: Forest #2D5016, Sage #6B8E23, Earth #8B7355, Cream #FFFAF0
1406 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1407 doc.set_margins(
1408 Length::inches(1.0),
1409 Length::inches(1.0),
1410 Length::inches(1.0),
1411 Length::inches(1.0),
1412 );
1413 doc.set_title("Q4 2025 Environmental Impact Report");
1414 doc.set_author("Walter White");
1415 doc.set_subject("Quarterly Environmental Report");
1416 doc.set_keywords("environment, sustainability, report, Tensorbee");
1417
1418 doc.set_different_first_page(true);
1419 doc.set_first_page_header("");
1420 doc.set_header("Tensorbee — Q4 2025 Environmental Impact Report");
1421 doc.set_footer("CONFIDENTIAL — Page");
1422
1423 // ── Cover ──
1424 let cover_bg = create_sample_png(612, 792, [20, 50, 15]);
1425 doc.add_background_image(&cover_bg, "report_cover.png");
1426
1427 for _ in 0..5 {
1428 doc.add_paragraph("");
1429 }
1430 {
1431 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1432 p.add_run("Q4 2025")
1433 .bold(true)
1434 .size(48.0)
1435 .color("FFFFFF")
1436 .font("Georgia");
1437 }
1438 {
1439 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1440 p.add_run("Environmental Impact Report")
1441 .size(24.0)
1442 .color("90EE90")
1443 .font("Georgia")
1444 .italic(true);
1445 }
1446 doc.add_paragraph("");
1447 {
1448 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1449 p.add_run("Tensorbee — Sustainability Division")
1450 .size(14.0)
1451 .color("C0C0C0");
1452 }
1453 {
1454 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1455 p.add_run("Prepared by Walter White, Chief Sustainability Officer")
1456 .size(11.0)
1457 .color("AAAAAA");
1458 }
1459
1460 // ── TOC ──
1461 doc.add_paragraph("").page_break_before(true);
1462 doc.insert_toc(doc.content_count(), 3);
1463
1464 // ── Section 1: Executive Overview ──
1465 doc.add_paragraph("").page_break_before(true);
1466 doc.add_paragraph("Executive Overview").style("Heading1");
1467 doc.add_paragraph(
1468 "This report presents Tensorbee's environmental performance for Q4 2025. Our \
1469 sustainability initiatives have yielded a 34% reduction in carbon emissions compared \
1470 to Q4 2024, exceeding our target of 25%. Key achievements include the transition to \
1471 100% renewable energy in our primary data centers and the launch of our carbon offset \
1472 marketplace.",
1473 )
1474 .first_line_indent(Length::inches(0.3));
1475
1476 // Image: performance chart
1477 let chart_img = create_chart_png(400, 200);
1478 doc.add_paragraph("");
1479 doc.add_picture(
1480 &chart_img,
1481 "performance_chart.png",
1482 Length::inches(5.0),
1483 Length::inches(2.5),
1484 )
1485 .alignment(Alignment::Center);
1486 {
1487 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1488 p.add_run("Figure 1: Quarterly Carbon Emissions (tonnes CO2e)")
1489 .italic(true)
1490 .size(9.0)
1491 .color("666666");
1492 }
1493
1494 // ── Section 2: Energy Consumption ──
1495 doc.add_paragraph("").page_break_before(true);
1496 doc.add_paragraph("Energy Consumption").style("Heading1");
1497
1498 doc.add_paragraph("Data Center Operations")
1499 .style("Heading2");
1500 doc.add_paragraph(
1501 "Our three primary data centers consumed a combined 4.2 GWh in Q4 2025, \
1502 a 12% reduction from Q3 2025 driven by improved cooling efficiency and \
1503 server consolidation.",
1504 );
1505
1506 // Data table
1507 {
1508 let mut tbl = doc
1509 .add_table(5, 4)
1510 .borders(BorderStyle::Single, 4, "2D5016")
1511 .width_pct(100.0);
1512 let hdrs = ["Data Center", "Capacity (MW)", "Usage (GWh)", "PUE"];
1513 for (c, h) in hdrs.iter().enumerate() {
1514 tbl.cell(0, c).unwrap().set_text(h);
1515 tbl.cell(0, c).unwrap().shading("2D5016");
1516 }
1517 let rows = [
1518 ("San Francisco (Primary)", "3.2", "1.8", "1.12"),
1519 ("Dublin (EU)", "2.1", "1.2", "1.18"),
1520 ("Singapore (APAC)", "1.8", "1.2", "1.24"),
1521 ("TOTAL", "7.1", "4.2", "1.17 avg"),
1522 ];
1523 for (i, (dc, cap, use_, pue)) in rows.iter().enumerate() {
1524 tbl.cell(i + 1, 0).unwrap().set_text(dc);
1525 tbl.cell(i + 1, 1).unwrap().set_text(cap);
1526 tbl.cell(i + 1, 2).unwrap().set_text(use_);
1527 tbl.cell(i + 1, 3).unwrap().set_text(pue);
1528 if i == 3 {
1529 for c in 0..4 {
1530 tbl.cell(i + 1, c).unwrap().shading("E2EFDA");
1531 }
1532 }
1533 }
1534 }
1535
1536 doc.add_paragraph("");
1537
1538 doc.add_paragraph("Renewable Energy Mix").style("Heading2");
1539 doc.add_paragraph("Breakdown of energy sources across all facilities:");
1540
1541 doc.add_bullet_list_item("Solar PV: 42% (1.76 GWh)", 0);
1542 doc.add_bullet_list_item("Wind Power Purchase Agreements: 38% (1.60 GWh)", 0);
1543 doc.add_bullet_list_item("Hydroelectric: 12% (0.50 GWh)", 0);
1544 doc.add_bullet_list_item("Grid (non-renewable): 8% (0.34 GWh)", 0);
1545
1546 // ── Section 3: Water & Waste ──
1547 doc.add_paragraph("Water & Waste Management")
1548 .style("Heading1");
1549
1550 doc.add_paragraph("Water Usage").style("Heading2");
1551 doc.add_paragraph(
1552 "Total water consumption was 12.4 million gallons, a 15% reduction from Q3. \
1553 Our closed-loop cooling systems now recycle 78% of water used in cooling operations.",
1554 );
1555
1556 doc.add_paragraph("Waste Reduction").style("Heading2");
1557 doc.add_paragraph("E-waste management results for Q4:")
1558 .keep_with_next(true);
1559 {
1560 let mut tbl = doc
1561 .add_table(4, 3)
1562 .borders(BorderStyle::Single, 2, "6B8E23")
1563 .width_pct(80.0);
1564 tbl.cell(0, 0).unwrap().set_text("Category");
1565 tbl.cell(0, 0).unwrap().shading("6B8E23");
1566 tbl.cell(0, 1).unwrap().set_text("Weight (kg)");
1567 tbl.cell(0, 1).unwrap().shading("6B8E23");
1568 tbl.cell(0, 2).unwrap().set_text("Recycled %");
1569 tbl.cell(0, 2).unwrap().shading("6B8E23");
1570 let rows = [
1571 ("Server Hardware", "2,450", "94%"),
1572 ("Networking Equipment", "820", "91%"),
1573 ("Storage Media", "340", "99%"),
1574 ];
1575 for (i, (cat, wt, pct)) in rows.iter().enumerate() {
1576 tbl.cell(i + 1, 0).unwrap().set_text(cat);
1577 tbl.cell(i + 1, 1).unwrap().set_text(wt);
1578 tbl.cell(i + 1, 2).unwrap().set_text(pct);
1579 }
1580 }
1581
1582 // ── Section 4: Initiatives ──
1583 doc.add_paragraph("").page_break_before(true);
1584 doc.add_paragraph("Q1 2026 Initiatives").style("Heading1");
1585
1586 doc.add_paragraph("Planned Programs").style("Heading2");
1587 doc.add_numbered_list_item(
1588 "Deploy on-site battery storage at SF data center (2 MWh capacity)",
1589 0,
1590 );
1591 doc.add_numbered_list_item("Pilot immersion cooling in Dublin facility", 0);
1592 doc.add_numbered_list_item("Launch employee commute carbon offset program", 0);
1593 doc.add_numbered_list_item("Achieve ISO 14001 certification for Singapore facility", 0);
1594
1595 doc.add_paragraph("Investment Targets").style("Heading2");
1596 doc.add_paragraph("Sustainability CapEx allocation for FY2026:")
1597 .keep_with_next(true);
1598 {
1599 let mut tbl = doc
1600 .add_table(5, 2)
1601 .borders(BorderStyle::Single, 4, "2D5016");
1602 tbl.cell(0, 0).unwrap().set_text("Initiative");
1603 tbl.cell(0, 0).unwrap().shading("2D5016");
1604 tbl.cell(0, 1).unwrap().set_text("Budget");
1605 tbl.cell(0, 1).unwrap().shading("2D5016");
1606 let rows = [
1607 ("Battery Storage", "$1.2M"),
1608 ("Immersion Cooling Pilot", "$800K"),
1609 ("Solar Panel Expansion", "$2.1M"),
1610 ("Carbon Credits", "$500K"),
1611 ];
1612 for (i, (init, budget)) in rows.iter().enumerate() {
1613 tbl.cell(i + 1, 0).unwrap().set_text(init);
1614 tbl.cell(i + 1, 1).unwrap().set_text(budget);
1615 if i % 2 == 0 {
1616 tbl.cell(i + 1, 0).unwrap().shading("FFFAF0");
1617 tbl.cell(i + 1, 1).unwrap().shading("FFFAF0");
1618 }
1619 }
1620 }
1621
1622 // Image: sustainability roadmap
1623 let roadmap_img = create_sample_png(500, 100, [40, 80, 30]);
1624 doc.add_paragraph("");
1625 doc.add_picture(
1626 &roadmap_img,
1627 "roadmap.png",
1628 Length::inches(6.0),
1629 Length::inches(1.2),
1630 )
1631 .alignment(Alignment::Center);
1632 {
1633 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1634 p.add_run("Figure 2: 2026 Sustainability Roadmap")
1635 .italic(true)
1636 .size(9.0)
1637 .color("666666");
1638 }
1639
1640 doc.add_paragraph("");
1641 doc.add_paragraph(
1642 "For questions about this report, contact Walter White at sustainability@tensorbee.com.",
1643 )
1644 .shading("FFFAF0")
1645 .border_all(BorderStyle::Single, 2, "2D5016");
1646
1647 doc
1648}
1649
1650// =============================================================================
1651// 6. LETTER — Slate blue + silver scheme
1652// =============================================================================
1653fn generate_letter(_samples_dir: &Path) -> Document {
1654 let mut doc = Document::new();
1655
1656 // Colors: Slate #4A5568, Blue accent #3182CE
1657 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1658 doc.set_margins(
1659 Length::inches(1.25),
1660 Length::inches(1.25),
1661 Length::inches(1.25),
1662 Length::inches(1.25),
1663 );
1664 doc.set_title("Business Letter");
1665 doc.set_author("Walter White");
1666
1667 // Banner header using raw XML
1668 let logo_img = create_logo_png(220, 48);
1669 let banner = build_header_banner_xml(
1670 "rId1",
1671 &BannerOpts {
1672 bg_color: "4A5568",
1673 banner_width: 7772400,
1674 banner_height: 731520, // ~0.8"
1675 logo_width: 2011680,
1676 logo_height: 438912,
1677 logo_x_offset: 295125,
1678 logo_y_offset: 146304,
1679 },
1680 );
1681 doc.set_raw_header_with_images(
1682 banner,
1683 &[("rId1", &logo_img, "logo.png")],
1684 rdocx_oxml::header_footer::HdrFtrType::Default,
1685 );
1686 doc.set_margins(
1687 Length::twips(2000),
1688 Length::twips(1800),
1689 Length::twips(1440),
1690 Length::twips(1800),
1691 );
1692 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
1693
1694 // Footer with contact
1695 doc.set_footer(
1696 "Tensorbee | 123 Innovation Drive, Suite 400 | San Francisco, CA 94105 | tensorbee.com",
1697 );
1698
1699 // ── Sender Address ──
1700 doc.add_paragraph("Walter White");
1701 doc.add_paragraph("Chief Executive Officer");
1702 doc.add_paragraph("Tensorbee");
1703 doc.add_paragraph("123 Innovation Drive, Suite 400");
1704 doc.add_paragraph("San Francisco, CA 94105");
1705 doc.add_paragraph("");
1706 doc.add_paragraph("February 22, 2026");
1707
1708 doc.add_paragraph("");
1709
1710 // ── Recipient ──
1711 doc.add_paragraph("Ms. Sarah Chen");
1712 doc.add_paragraph("VP of Engineering");
1713 doc.add_paragraph("Meridian Dynamics LLC");
1714 doc.add_paragraph("456 Enterprise Boulevard");
1715 doc.add_paragraph("Austin, TX 78701");
1716
1717 doc.add_paragraph("");
1718
1719 // ── Subject ──
1720 {
1721 let mut p = doc.add_paragraph("");
1722 p.add_run("Re: Strategic Technology Partnership — Phase 2 Expansion")
1723 .bold(true);
1724 }
1725
1726 doc.add_paragraph("");
1727
1728 // ── Body ──
1729 doc.add_paragraph("Dear Ms. Chen,");
1730 doc.add_paragraph("");
1731
1732 doc.add_paragraph(
1733 "I am writing to express Tensorbee's enthusiasm for expanding our strategic technology \
1734 partnership with Meridian Dynamics. Following the successful completion of Phase 1, \
1735 which delivered a 40% improvement in your ML inference pipeline throughput, we believe \
1736 the foundation is firmly established for an ambitious Phase 2 engagement.",
1737 )
1738 .first_line_indent(Length::inches(0.5));
1739
1740 doc.add_paragraph(
1741 "During our recent executive review, your team highlighted three priority areas for \
1742 the next phase of collaboration:",
1743 )
1744 .first_line_indent(Length::inches(0.5));
1745
1746 doc.add_numbered_list_item(
1747 "Real-time anomaly detection for your financial transaction monitoring system, \
1748 targeting sub-100ms latency at 50,000 transactions per second.",
1749 0,
1750 );
1751 doc.add_numbered_list_item(
1752 "Federated learning infrastructure to enable model training across your distributed \
1753 data centers without centralizing sensitive financial data.",
1754 0,
1755 );
1756 doc.add_numbered_list_item(
1757 "MLOps automation to reduce your model deployment cycle from the current 5 days \
1758 to under 4 hours.",
1759 0,
1760 );
1761
1762 doc.add_paragraph(
1763 "Our engineering team has prepared a detailed technical proposal addressing each of \
1764 these areas. We have allocated a dedicated team of eight senior engineers, led by \
1765 our CTO, to ensure continuity with the Phase 1 team your organization has already \
1766 built a productive working relationship with.",
1767 )
1768 .first_line_indent(Length::inches(0.5));
1769
1770 doc.add_paragraph(
1771 "I would welcome the opportunity to present our Phase 2 proposal in person. My \
1772 assistant will reach out to coordinate a meeting at your convenience during the \
1773 first week of March.",
1774 )
1775 .first_line_indent(Length::inches(0.5));
1776
1777 doc.add_paragraph("");
1778
1779 doc.add_paragraph("Warm regards,");
1780 doc.add_paragraph("");
1781 doc.add_paragraph("");
1782
1783 // Signature line
1784 doc.add_paragraph("")
1785 .border_bottom(BorderStyle::Single, 4, "4A5568");
1786 {
1787 let mut p = doc.add_paragraph("");
1788 p.add_run("Walter White").bold(true).size(12.0);
1789 }
1790 {
1791 let mut p = doc.add_paragraph("");
1792 p.add_run("Chief Executive Officer, Tensorbee")
1793 .italic(true)
1794 .size(10.0)
1795 .color("666666");
1796 }
1797 {
1798 let mut p = doc.add_paragraph("");
1799 p.add_run("walter@tensorbee.com | +1 (415) 555-0199")
1800 .size(10.0)
1801 .color("888888");
1802 }
1803
1804 doc
1805}
1806
1807// =============================================================================
1808// 7. EMPLOYMENT CONTRACT — Purple + charcoal scheme
1809// =============================================================================
1810fn generate_contract(_samples_dir: &Path) -> Document {
1811 let mut doc = Document::new();
1812
1813 // Colors: Purple #5B2C6F, Plum #8E44AD, Gray #2C3E50
1814 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1815 doc.set_margins(
1816 Length::inches(1.25),
1817 Length::inches(1.0),
1818 Length::inches(1.0),
1819 Length::inches(1.0),
1820 );
1821 doc.set_title("Employment Agreement");
1822 doc.set_author("Tensorbee HR Department");
1823 doc.set_subject("Employment Contract — Walter White");
1824 doc.set_keywords("employment, contract, agreement, Tensorbee");
1825
1826 doc.set_header("TENSORBEE — Employment Agreement");
1827 doc.set_footer("Employment Agreement — Walter White — Page");
1828
1829 // ── Title Block ──
1830 doc.add_paragraph("")
1831 .border_bottom(BorderStyle::Thick, 12, "5B2C6F");
1832 {
1833 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1834 p.add_run("EMPLOYMENT AGREEMENT")
1835 .bold(true)
1836 .size(24.0)
1837 .color("5B2C6F")
1838 .font("Georgia");
1839 }
1840 doc.add_paragraph("")
1841 .border_bottom(BorderStyle::Thick, 12, "5B2C6F");
1842 doc.add_paragraph("");
1843
1844 // ── Parties ──
1845 doc.add_paragraph(
1846 "This Employment Agreement (\"Agreement\") is entered into as of February 22, 2026 \
1847 (\"Effective Date\"), by and between:",
1848 );
1849
1850 doc.add_paragraph("");
1851
1852 {
1853 let mut p = doc.add_paragraph("").indent_left(Length::inches(0.5));
1854 p.add_run("EMPLOYER: ").bold(true);
1855 p.add_run(
1856 "Tensorbee, Inc., a Delaware corporation with its principal place of business at \
1857 123 Innovation Drive, Suite 400, San Francisco, CA 94105 (\"Company\")",
1858 );
1859 }
1860 doc.add_paragraph("");
1861 {
1862 let mut p = doc.add_paragraph("").indent_left(Length::inches(0.5));
1863 p.add_run("EMPLOYEE: ").bold(true);
1864 p.add_run(
1865 "Walter White, residing at 308 Negra Arroyo Lane, Albuquerque, NM 87104 (\"Employee\")",
1866 );
1867 }
1868
1869 doc.add_paragraph("");
1870 doc.add_paragraph("The Company and Employee are collectively referred to as the \"Parties.\"");
1871 doc.add_paragraph("");
1872
1873 // ── Article 1: Position and Duties ──
1874 doc.add_paragraph("Article 1 — Position and Duties")
1875 .style("Heading1");
1876 {
1877 let mut p = doc.add_paragraph("");
1878 p.add_run("1.1 ").bold(true);
1879 p.add_run("The Company hereby employs the Employee as ");
1880 p.add_run("Chief Executive Officer (CEO)")
1881 .bold(true)
1882 .italic(true);
1883 p.add_run(", reporting directly to the Board of Directors.");
1884 }
1885 {
1886 let mut p = doc.add_paragraph("");
1887 p.add_run("1.2 ").bold(true);
1888 p.add_run("The Employee shall devote full working time, attention, and best efforts to \
1889 the performance of duties as reasonably assigned by the Board, including but not limited to:");
1890 }
1891 doc.add_bullet_list_item(
1892 "Setting and executing the Company's strategic vision and business plan",
1893 0,
1894 );
1895 doc.add_bullet_list_item(
1896 "Overseeing all operations, engineering, and commercial activities",
1897 0,
1898 );
1899 doc.add_bullet_list_item(
1900 "Representing the Company to investors, customers, and the public",
1901 0,
1902 );
1903 doc.add_bullet_list_item("Recruiting, developing, and retaining key talent", 0);
1904
1905 {
1906 let mut p = doc.add_paragraph("");
1907 p.add_run("1.3 ").bold(true);
1908 p.add_run(
1909 "The Employee's primary work location shall be the Company's San Francisco \
1910 headquarters, with reasonable travel as required by business needs.",
1911 );
1912 }
1913
1914 // ── Article 2: Compensation ──
1915 doc.add_paragraph("Article 2 — Compensation and Benefits")
1916 .style("Heading1");
1917 {
1918 let mut p = doc.add_paragraph("");
1919 p.add_run("2.1 Base Salary. ").bold(true);
1920 p.add_run("The Company shall pay the Employee an annual base salary of ");
1921 p.add_run("$375,000.00").bold(true);
1922 p.add_run(" (Three Hundred Seventy-Five Thousand Dollars), payable in accordance with the \
1923 Company's standard payroll schedule, less applicable withholdings and deductions.");
1924 }
1925 {
1926 let mut p = doc.add_paragraph("");
1927 p.add_run("2.2 Annual Bonus. ").bold(true);
1928 p.add_run("The Employee shall be eligible for an annual performance bonus of up to ");
1929 p.add_run("40%").bold(true);
1930 p.add_run(
1931 " of base salary, based on achievement of mutually agreed performance objectives.",
1932 );
1933 }
1934 {
1935 let mut p = doc.add_paragraph("");
1936 p.add_run("2.3 Equity. ").bold(true);
1937 p.add_run("Subject to Board approval, the Employee shall receive a stock option grant of ");
1938 p.add_run("500,000 shares").bold(true);
1939 p.add_run(
1940 " of the Company's common stock, vesting over four (4) years with a one-year cliff, \
1941 at an exercise price equal to the fair market value on the date of grant.",
1942 );
1943 }
1944
1945 // Compensation summary table
1946 doc.add_paragraph("");
1947 {
1948 let mut tbl = doc
1949 .add_table(5, 2)
1950 .borders(BorderStyle::Single, 4, "5B2C6F")
1951 .width_pct(70.0)
1952 .alignment(Alignment::Center);
1953 tbl.cell(0, 0).unwrap().set_text("Compensation Element");
1954 tbl.cell(0, 0).unwrap().shading("5B2C6F");
1955 tbl.cell(0, 1).unwrap().set_text("Value");
1956 tbl.cell(0, 1).unwrap().shading("5B2C6F");
1957 let items = [
1958 ("Base Salary", "$375,000/year"),
1959 ("Target Bonus", "Up to 40% ($150,000)"),
1960 ("Equity Grant", "500,000 shares (4yr vest)"),
1961 ("Total Target Comp", "$525,000 + equity"),
1962 ];
1963 for (i, (elem, val)) in items.iter().enumerate() {
1964 tbl.cell(i + 1, 0).unwrap().set_text(elem);
1965 tbl.cell(i + 1, 1).unwrap().set_text(val);
1966 if i == 3 {
1967 tbl.cell(i + 1, 0).unwrap().shading("E8DAEF");
1968 tbl.cell(i + 1, 1).unwrap().shading("E8DAEF");
1969 }
1970 }
1971 }
1972
1973 // ── Article 3: Benefits ──
1974 doc.add_paragraph("").page_break_before(true);
1975 doc.add_paragraph("Article 3 — Benefits").style("Heading1");
1976 {
1977 let mut p = doc.add_paragraph("");
1978 p.add_run("3.1 ").bold(true);
1979 p.add_run(
1980 "The Employee shall be entitled to participate in all benefit programs \
1981 generally available to senior executives, including:",
1982 );
1983 }
1984 doc.add_bullet_list_item(
1985 "Medical, dental, and vision insurance (100% premium coverage for employee and dependents)",
1986 0,
1987 );
1988 doc.add_bullet_list_item("401(k) retirement plan with 6% company match", 0);
1989 doc.add_bullet_list_item("Life insurance and long-term disability coverage", 0);
1990 doc.add_bullet_list_item("Annual professional development allowance of $10,000", 0);
1991
1992 {
1993 let mut p = doc.add_paragraph("");
1994 p.add_run("3.2 Paid Time Off. ").bold(true);
1995 p.add_run(
1996 "The Employee shall receive 25 days of paid vacation per year, plus Company holidays, \
1997 accruing on a monthly basis.",
1998 );
1999 }
2000
2001 // ── Article 4: Term and Termination ──
2002 doc.add_paragraph("Article 4 — Term and Termination")
2003 .style("Heading1");
2004 {
2005 let mut p = doc.add_paragraph("");
2006 p.add_run("4.1 At-Will Employment. ").bold(true);
2007 p.add_run(
2008 "This Agreement is for at-will employment and may be terminated by either Party \
2009 at any time, with or without cause, subject to the notice provisions herein.",
2010 );
2011 }
2012 {
2013 let mut p = doc.add_paragraph("");
2014 p.add_run("4.2 Notice Period. ").bold(true);
2015 p.add_run("Either Party shall provide ");
2016 p.add_run("ninety (90) days").bold(true);
2017 p.add_run(" written notice of termination, or pay in lieu thereof.");
2018 }
2019 {
2020 let mut p = doc.add_paragraph("");
2021 p.add_run("4.3 Severance. ").bold(true);
2022 p.add_run("In the event of termination by the Company without Cause, the Employee shall \
2023 receive (a) twelve (12) months of base salary continuation, (b) pro-rata bonus \
2024 for the year of termination, and (c) twelve (12) months of COBRA premium coverage.");
2025 }
2026
2027 // ── Article 5: Confidentiality ──
2028 doc.add_paragraph("Article 5 — Confidentiality and IP")
2029 .style("Heading1");
2030 {
2031 let mut p = doc.add_paragraph("");
2032 p.add_run("5.1 ").bold(true);
2033 p.add_run(
2034 "The Employee agrees to maintain strict confidentiality of all proprietary \
2035 information, trade secrets, and business plans of the Company during and after \
2036 employment.",
2037 );
2038 }
2039 {
2040 let mut p = doc.add_paragraph("");
2041 p.add_run("5.2 ").bold(true);
2042 p.add_run(
2043 "All intellectual property, inventions, and works of authorship created by the \
2044 Employee during the term of employment and within the scope of duties shall be \
2045 the sole and exclusive property of the Company.",
2046 );
2047 }
2048
2049 // ── Article 6: Non-Compete ──
2050 doc.add_paragraph("Article 6 — Non-Competition")
2051 .style("Heading1");
2052 {
2053 let mut p = doc.add_paragraph("");
2054 p.add_run("6.1 ").bold(true);
2055 p.add_run("For a period of twelve (12) months following termination, the Employee agrees \
2056 not to engage in any business that directly competes with the Company's core \
2057 business of AI infrastructure and ML operations services, within the United States.");
2058 }
2059
2060 // ── Article 7: General Provisions ──
2061 doc.add_paragraph("Article 7 — General Provisions")
2062 .style("Heading1");
2063 {
2064 let mut p = doc.add_paragraph("");
2065 p.add_run("7.1 Governing Law. ").bold(true);
2066 p.add_run(
2067 "This Agreement shall be governed by and construed in accordance with the laws of \
2068 the State of California.",
2069 );
2070 }
2071 {
2072 let mut p = doc.add_paragraph("");
2073 p.add_run("7.2 Entire Agreement. ").bold(true);
2074 p.add_run(
2075 "This Agreement constitutes the entire agreement between the Parties and supersedes \
2076 all prior negotiations, representations, and agreements.",
2077 );
2078 }
2079 {
2080 let mut p = doc.add_paragraph("");
2081 p.add_run("7.3 Amendment. ").bold(true);
2082 p.add_run(
2083 "This Agreement may only be amended by a written instrument signed by both Parties.",
2084 );
2085 }
2086
2087 // ── Signature Block ──
2088 doc.add_paragraph("").page_break_before(true);
2089 doc.add_paragraph("IN WITNESS WHEREOF, the Parties have executed this Employment Agreement as of the Effective Date.")
2090 .space_after(Length::pt(24.0));
2091
2092 // Signature table
2093 {
2094 let mut tbl = doc.add_table(6, 2).width_pct(100.0);
2095 tbl.cell(0, 0).unwrap().set_text("FOR THE COMPANY:");
2096 tbl.cell(0, 0).unwrap().shading("5B2C6F");
2097 tbl.cell(0, 1).unwrap().set_text("EMPLOYEE:");
2098 tbl.cell(0, 1).unwrap().shading("5B2C6F");
2099
2100 tbl.cell(1, 0).unwrap().set_text("");
2101 tbl.cell(1, 1).unwrap().set_text("");
2102 tbl.row(1).unwrap().height(Length::pt(40.0));
2103
2104 tbl.cell(2, 0)
2105 .unwrap()
2106 .set_text("Signature: ___________________________");
2107 tbl.cell(2, 1)
2108 .unwrap()
2109 .set_text("Signature: ___________________________");
2110 tbl.cell(3, 0)
2111 .unwrap()
2112 .set_text("Name: Board Representative");
2113 tbl.cell(3, 1).unwrap().set_text("Name: Walter White");
2114 tbl.cell(4, 0)
2115 .unwrap()
2116 .set_text("Title: Chair, Board of Directors");
2117 tbl.cell(4, 1)
2118 .unwrap()
2119 .set_text("Title: Chief Executive Officer");
2120 tbl.cell(5, 0).unwrap().set_text("Date: _______________");
2121 tbl.cell(5, 1).unwrap().set_text("Date: _______________");
2122 }
2123
2124 doc
2125}Sourcepub fn add_paragraph(&mut self, text: &str) -> Paragraph<'_>
pub fn add_paragraph(&mut self, text: &str) -> Paragraph<'_>
Add a paragraph to the cell and return a mutable reference.
Examples found in repository?
examples/generate_all_samples.rs (line 1227)
1204fn generate_invoice(_samples_dir: &Path) -> Document {
1205 let mut doc = Document::new();
1206
1207 // Colors: Crimson #B22222, Charcoal #333333, Light #FAF0F0
1208 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1209 doc.set_margins(
1210 Length::inches(0.75),
1211 Length::inches(0.75),
1212 Length::inches(0.75),
1213 Length::inches(0.75),
1214 );
1215 doc.set_title("Invoice INV-2026-0392");
1216 doc.set_author("Walter White");
1217 doc.set_keywords("invoice, Tensorbee");
1218
1219 doc.set_footer("Tensorbee — Thank you for your business!");
1220
1221 // ── Company & Invoice Header ──
1222 {
1223 let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1224 // Company name left, INVOICE right
1225 {
1226 let mut cell = tbl.cell(0, 0).unwrap();
1227 let mut p = cell.add_paragraph("");
1228 p.add_run("TENSORBEE")
1229 .bold(true)
1230 .size(32.0)
1231 .color("B22222")
1232 .font("Helvetica");
1233 }
1234 {
1235 let mut cell = tbl.cell(0, 1).unwrap();
1236 let mut p = cell.add_paragraph("");
1237 p.add_run("INVOICE").bold(true).size(32.0).color("333333");
1238 }
1239 tbl.cell(1, 0)
1240 .unwrap()
1241 .set_text("123 Innovation Drive, Suite 400");
1242 tbl.cell(1, 1).unwrap().set_text("Invoice #: INV-2026-0392");
1243 tbl.cell(2, 0).unwrap().set_text("San Francisco, CA 94105");
1244 tbl.cell(2, 1).unwrap().set_text("Date: February 22, 2026");
1245 tbl.cell(3, 0).unwrap().set_text("walter@tensorbee.com");
1246 tbl.cell(3, 1).unwrap().set_text("Due Date: March 24, 2026");
1247 }
1248
1249 doc.add_paragraph("")
1250 .border_bottom(BorderStyle::Thick, 8, "B22222");
1251 doc.add_paragraph("");
1252
1253 // ── Bill To / Ship To ──
1254 {
1255 let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1256 tbl.cell(0, 0).unwrap().set_text("BILL TO");
1257 tbl.cell(0, 0).unwrap().shading("B22222");
1258 tbl.cell(0, 1).unwrap().set_text("SHIP TO");
1259 tbl.cell(0, 1).unwrap().shading("B22222");
1260 tbl.cell(1, 0).unwrap().set_text("Meridian Dynamics LLC");
1261 tbl.cell(1, 1).unwrap().set_text("Meridian Dynamics LLC");
1262 tbl.cell(2, 0).unwrap().set_text("456 Enterprise Blvd");
1263 tbl.cell(2, 1).unwrap().set_text("Attn: Server Room B");
1264 tbl.cell(3, 0).unwrap().set_text("Austin, TX 78701");
1265 tbl.cell(3, 1)
1266 .unwrap()
1267 .set_text("456 Enterprise Blvd, Austin, TX 78701");
1268 }
1269
1270 doc.add_paragraph("");
1271
1272 // ── Line Items ──
1273 {
1274 let mut tbl = doc
1275 .add_table(8, 5)
1276 .borders(BorderStyle::Single, 2, "B22222")
1277 .width_pct(100.0);
1278 let hdrs = ["Description", "Qty", "Unit Price", "Tax", "Amount"];
1279 for (c, h) in hdrs.iter().enumerate() {
1280 tbl.cell(0, c).unwrap().set_text(h);
1281 tbl.cell(0, c).unwrap().shading("B22222");
1282 }
1283 let items = [
1284 (
1285 "ML Infrastructure Setup — Phase 1",
1286 "1",
1287 "$45,000.00",
1288 "$3,881.25",
1289 "$48,881.25",
1290 ),
1291 (
1292 "Data Pipeline Development (160 hrs)",
1293 "160",
1294 "$225.00",
1295 "$3,105.00",
1296 "$39,105.00",
1297 ),
1298 (
1299 "GPU Cluster Configuration",
1300 "1",
1301 "$12,000.00",
1302 "$1,035.00",
1303 "$13,035.00",
1304 ),
1305 (
1306 "API Gateway Implementation",
1307 "1",
1308 "$18,500.00",
1309 "$1,595.63",
1310 "$20,095.63",
1311 ),
1312 (
1313 "Load Testing & QA (80 hrs)",
1314 "80",
1315 "$195.00",
1316 "$1,345.50",
1317 "$16,945.50",
1318 ),
1319 (
1320 "Documentation & Training",
1321 "1",
1322 "$8,000.00",
1323 "$690.00",
1324 "$8,690.00",
1325 ),
1326 (
1327 "Project Management (3 months)",
1328 "3",
1329 "$6,500.00",
1330 "$1,679.63",
1331 "$21,179.63",
1332 ),
1333 ];
1334 for (i, (desc, qty, unit, tax, amt)) in items.iter().enumerate() {
1335 let r = i + 1;
1336 tbl.cell(r, 0).unwrap().set_text(desc);
1337 tbl.cell(r, 1).unwrap().set_text(qty);
1338 tbl.cell(r, 2).unwrap().set_text(unit);
1339 tbl.cell(r, 3).unwrap().set_text(tax);
1340 tbl.cell(r, 4).unwrap().set_text(amt);
1341 if i % 2 == 0 {
1342 for c in 0..5 {
1343 tbl.cell(r, c).unwrap().shading("FAF0F0");
1344 }
1345 }
1346 }
1347 }
1348
1349 doc.add_paragraph("");
1350
1351 // ── Totals ──
1352 {
1353 let mut tbl = doc
1354 .add_table(4, 2)
1355 .borders(BorderStyle::Single, 2, "B22222")
1356 .width(Length::inches(3.0))
1357 .alignment(Alignment::Right);
1358 tbl.cell(0, 0).unwrap().set_text("Subtotal");
1359 tbl.cell(0, 1).unwrap().set_text("$154,600.00");
1360 tbl.cell(1, 0).unwrap().set_text("Tax (8.625%)");
1361 tbl.cell(1, 1).unwrap().set_text("$13,334.25");
1362 tbl.cell(2, 0).unwrap().set_text("Discount (5%)");
1363 tbl.cell(2, 1).unwrap().set_text("-$7,730.00");
1364 tbl.cell(3, 0).unwrap().set_text("AMOUNT DUE");
1365 tbl.cell(3, 0).unwrap().shading("B22222");
1366 tbl.cell(3, 1).unwrap().set_text("$160,204.25");
1367 tbl.cell(3, 1).unwrap().shading("B22222");
1368 }
1369
1370 doc.add_paragraph("");
1371 doc.add_paragraph("");
1372
1373 // ── Payment Details ──
1374 {
1375 let mut p = doc.add_paragraph("");
1376 p.add_run("Payment Details")
1377 .bold(true)
1378 .size(14.0)
1379 .color("333333");
1380 }
1381 doc.add_paragraph("Bank: Silicon Valley Bank")
1382 .indent_left(Length::inches(0.3));
1383 doc.add_paragraph("Account: Tensorbee Inc. — 0847-2953-1120")
1384 .indent_left(Length::inches(0.3));
1385 doc.add_paragraph("Routing: 121140399")
1386 .indent_left(Length::inches(0.3));
1387 doc.add_paragraph("Swift: SVBKUS6S")
1388 .indent_left(Length::inches(0.3));
1389
1390 doc.add_paragraph("");
1391
1392 doc.add_paragraph("Please include invoice number INV-2026-0392 in the payment reference.")
1393 .shading("FAF0F0")
1394 .border_all(BorderStyle::Single, 2, "B22222");
1395
1396 doc
1397}More examples
examples/styled_tables.rs (line 332)
24fn generate_styled_tables(path: &Path) {
25 let mut doc = Document::new();
26 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27 doc.set_margins(
28 Length::inches(0.75),
29 Length::inches(0.75),
30 Length::inches(0.75),
31 Length::inches(0.75),
32 );
33
34 doc.add_paragraph("Styled Tables Showcase")
35 .style("Heading1");
36
37 doc.add_paragraph("");
38
39 // =========================================================================
40 // 1. Professional report table with alternating rows
41 // =========================================================================
42 doc.add_paragraph("1. Report Table with Alternating Row Colors")
43 .style("Heading2");
44
45 {
46 let mut tbl = doc.add_table(8, 4);
47 tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48 tbl = tbl.width_pct(100.0);
49
50 // Header row
51 let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52 for (col, h) in headers.iter().enumerate() {
53 tbl.cell(0, col).unwrap().shading("2E75B6");
54 tbl.cell(0, col).unwrap().set_text(h);
55 }
56 tbl.row(0).unwrap().header();
57
58 // Data with alternating shading
59 let data = [
60 ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61 ["Professional", "$189,000", "$201,000", "+6.3%"],
62 ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63 ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64 ["Training", "$22,000", "$28,900", "+31.4%"],
65 ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66 ];
67
68 for (i, row) in data.iter().enumerate() {
69 let row_idx = i + 1;
70 for (col, val) in row.iter().enumerate() {
71 tbl.cell(row_idx, col).unwrap().set_text(val);
72 // Alternate row colors
73 if i % 2 == 0 {
74 tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75 }
76 }
77 }
78
79 // Total row
80 tbl.cell(7, 0).unwrap().set_text("TOTAL");
81 tbl.cell(7, 0).unwrap().shading("D6E4F0");
82 tbl.cell(7, 1).unwrap().set_text("$613,000");
83 tbl.cell(7, 1).unwrap().shading("D6E4F0");
84 tbl.cell(7, 2).unwrap().set_text("$729,900");
85 tbl.cell(7, 2).unwrap().shading("D6E4F0");
86 tbl.cell(7, 3).unwrap().set_text("+19.1%");
87 tbl.cell(7, 3).unwrap().shading("D6E4F0");
88 }
89
90 doc.add_paragraph("");
91
92 // =========================================================================
93 // 2. Invoice-style table with merged header
94 // =========================================================================
95 doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96 .style("Heading2");
97
98 {
99 let mut tbl = doc.add_table(7, 4);
100 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101 tbl = tbl.width_pct(100.0);
102
103 // Merged title row
104 tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105 tbl.cell(0, 0).unwrap().grid_span(4);
106 tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108 // Column headers
109 let headers = ["Item", "Description", "Qty", "Amount"];
110 for (col, h) in headers.iter().enumerate() {
111 tbl.cell(1, col).unwrap().set_text(h);
112 tbl.cell(1, col).unwrap().shading("D6E4F0");
113 }
114
115 // Line items
116 tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117 tbl.cell(2, 1)
118 .unwrap()
119 .set_text("Enterprise License (500 seats)");
120 tbl.cell(2, 2).unwrap().set_text("1");
121 tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123 tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124 tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125 tbl.cell(3, 2).unwrap().set_text("1");
126 tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128 tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129 tbl.cell(4, 1)
130 .unwrap()
131 .set_text("On-site Training (3 days)");
132 tbl.cell(4, 2).unwrap().set_text("1");
133 tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135 // Subtotal
136 tbl.cell(5, 0).unwrap().set_text("Subtotal");
137 tbl.cell(5, 0).unwrap().grid_span(3);
138 tbl.cell(5, 0).unwrap().shading("F2F2F2");
139 tbl.cell(5, 3).unwrap().set_text("$89,500");
140 tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142 // Total
143 tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144 tbl.cell(6, 0).unwrap().grid_span(3);
145 tbl.cell(6, 0).unwrap().shading("1F4E79");
146 tbl.cell(6, 3).unwrap().set_text("$89,500");
147 tbl.cell(6, 3).unwrap().shading("1F4E79");
148 }
149
150 doc.add_paragraph("");
151
152 // =========================================================================
153 // 3. Specification table with vertical merge
154 // =========================================================================
155 doc.add_paragraph("3. Specification Table with Vertical Merges")
156 .style("Heading2");
157
158 {
159 let mut tbl = doc.add_table(8, 3);
160 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161 tbl = tbl.width_pct(100.0);
162
163 // Header
164 tbl.cell(0, 0).unwrap().set_text("Category");
165 tbl.cell(0, 0).unwrap().shading("2E75B6");
166 tbl.cell(0, 1).unwrap().set_text("Specification");
167 tbl.cell(0, 1).unwrap().shading("2E75B6");
168 tbl.cell(0, 2).unwrap().set_text("Value");
169 tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171 // "Hardware" spans 3 rows
172 tbl.cell(1, 0).unwrap().set_text("Hardware");
173 tbl.cell(1, 0).unwrap().v_merge_restart();
174 tbl.cell(1, 0).unwrap().shading("E2EFDA");
175 tbl.cell(1, 0)
176 .unwrap()
177 .vertical_alignment(VerticalAlignment::Center);
178 tbl.cell(1, 1).unwrap().set_text("Processor");
179 tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181 tbl.cell(2, 0).unwrap().v_merge_continue();
182 tbl.cell(2, 1).unwrap().set_text("Memory");
183 tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185 tbl.cell(3, 0).unwrap().v_merge_continue();
186 tbl.cell(3, 1).unwrap().set_text("Storage");
187 tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189 // "Network" spans 2 rows
190 tbl.cell(4, 0).unwrap().set_text("Network");
191 tbl.cell(4, 0).unwrap().v_merge_restart();
192 tbl.cell(4, 0).unwrap().shading("FCE4D6");
193 tbl.cell(4, 0)
194 .unwrap()
195 .vertical_alignment(VerticalAlignment::Center);
196 tbl.cell(4, 1).unwrap().set_text("Ethernet");
197 tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199 tbl.cell(5, 0).unwrap().v_merge_continue();
200 tbl.cell(5, 1).unwrap().set_text("Management");
201 tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203 // "Software" spans 2 rows
204 tbl.cell(6, 0).unwrap().set_text("Software");
205 tbl.cell(6, 0).unwrap().v_merge_restart();
206 tbl.cell(6, 0).unwrap().shading("D6E4F0");
207 tbl.cell(6, 0)
208 .unwrap()
209 .vertical_alignment(VerticalAlignment::Center);
210 tbl.cell(6, 1).unwrap().set_text("Operating System");
211 tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213 tbl.cell(7, 0).unwrap().v_merge_continue();
214 tbl.cell(7, 1).unwrap().set_text("Monitoring");
215 tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216 }
217
218 doc.add_paragraph("");
219
220 // =========================================================================
221 // 4. Nested table (table inside a cell)
222 // =========================================================================
223 doc.add_paragraph("4. Nested Table").style("Heading2");
224
225 {
226 let mut tbl = doc.add_table(2, 2);
227 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228 tbl = tbl.width_pct(100.0);
229 tbl = tbl.cell_margins(
230 Length::twips(72),
231 Length::twips(108),
232 Length::twips(72),
233 Length::twips(108),
234 );
235
236 tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237 tbl.cell(0, 0).unwrap().shading("2E75B6");
238 tbl.cell(0, 1).unwrap().set_text("Project Beta");
239 tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241 // Nested table in cell (1,0)
242 {
243 let mut cell = tbl.cell(1, 0).unwrap();
244 cell.set_text("Milestones:");
245 let mut inner = cell.add_table(3, 2);
246 inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247 inner.cell(0, 0).unwrap().set_text("Phase");
248 inner.cell(0, 0).unwrap().shading("E2EFDA");
249 inner.cell(0, 1).unwrap().set_text("Status");
250 inner.cell(0, 1).unwrap().shading("E2EFDA");
251 inner.cell(1, 0).unwrap().set_text("Design");
252 inner.cell(1, 1).unwrap().set_text("Complete");
253 inner.cell(2, 0).unwrap().set_text("Build");
254 inner.cell(2, 1).unwrap().set_text("In Progress");
255 }
256
257 // Nested table in cell (1,1)
258 {
259 let mut cell = tbl.cell(1, 1).unwrap();
260 cell.set_text("Budget:");
261 let mut inner = cell.add_table(3, 2);
262 inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263 inner.cell(0, 0).unwrap().set_text("Category");
264 inner.cell(0, 0).unwrap().shading("FCE4D6");
265 inner.cell(0, 1).unwrap().set_text("Amount");
266 inner.cell(0, 1).unwrap().shading("FCE4D6");
267 inner.cell(1, 0).unwrap().set_text("Development");
268 inner.cell(1, 1).unwrap().set_text("$120,000");
269 inner.cell(2, 0).unwrap().set_text("Testing");
270 inner.cell(2, 1).unwrap().set_text("$35,000");
271 }
272 }
273
274 doc.add_paragraph("");
275
276 // =========================================================================
277 // 5. Form-style table with labels
278 // =========================================================================
279 doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281 {
282 let mut tbl = doc.add_table(6, 4);
283 tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284 tbl = tbl.width_pct(100.0);
285
286 // Row 0: Full-width title
287 tbl.cell(0, 0)
288 .unwrap()
289 .set_text("Customer Registration Form");
290 tbl.cell(0, 0).unwrap().grid_span(4);
291 tbl.cell(0, 0).unwrap().shading("404040");
292
293 // Row 1: Name fields
294 tbl.cell(1, 0).unwrap().set_text("First Name");
295 tbl.cell(1, 0).unwrap().shading("E8E8E8");
296 tbl.cell(1, 1).unwrap().set_text("John");
297 tbl.cell(1, 2).unwrap().set_text("Last Name");
298 tbl.cell(1, 2).unwrap().shading("E8E8E8");
299 tbl.cell(1, 3).unwrap().set_text("Smith");
300
301 // Row 2: Contact
302 tbl.cell(2, 0).unwrap().set_text("Email");
303 tbl.cell(2, 0).unwrap().shading("E8E8E8");
304 tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305 tbl.cell(2, 1).unwrap().grid_span(3);
306
307 // Row 3: Phone
308 tbl.cell(3, 0).unwrap().set_text("Phone");
309 tbl.cell(3, 0).unwrap().shading("E8E8E8");
310 tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311 tbl.cell(3, 2).unwrap().set_text("Company");
312 tbl.cell(3, 2).unwrap().shading("E8E8E8");
313 tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315 // Row 4: Address (spanning)
316 tbl.cell(4, 0).unwrap().set_text("Address");
317 tbl.cell(4, 0).unwrap().shading("E8E8E8");
318 tbl.cell(4, 1)
319 .unwrap()
320 .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321 tbl.cell(4, 1).unwrap().grid_span(3);
322
323 // Row 5: Notes
324 tbl.cell(5, 0).unwrap().set_text("Notes");
325 tbl.cell(5, 0).unwrap().shading("E8E8E8");
326 tbl.cell(5, 0)
327 .unwrap()
328 .vertical_alignment(VerticalAlignment::Top);
329 {
330 let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331 cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332 cell.add_paragraph("Annual review scheduled for March 2026.");
333 }
334 }
335
336 doc.add_paragraph("");
337
338 // =========================================================================
339 // 6. Comparison table with border styles
340 // =========================================================================
341 doc.add_paragraph("6. Comparison Table with Custom Borders")
342 .style("Heading2");
343
344 {
345 let mut tbl = doc.add_table(5, 3);
346 tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347 tbl = tbl.width_pct(100.0);
348
349 // Header
350 tbl.cell(0, 0).unwrap().set_text("Feature");
351 tbl.cell(0, 0).unwrap().shading("2E75B6");
352 tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353 tbl.cell(0, 1).unwrap().shading("2E75B6");
354 tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355 tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357 tbl.cell(1, 0).unwrap().set_text("Users");
358 tbl.cell(1, 1).unwrap().set_text("Up to 10");
359 tbl.cell(1, 2).unwrap().set_text("Unlimited");
360 tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362 tbl.cell(2, 0).unwrap().set_text("Storage");
363 tbl.cell(2, 1).unwrap().set_text("50 GB");
364 tbl.cell(2, 2).unwrap().set_text("5 TB");
365 tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367 tbl.cell(3, 0).unwrap().set_text("Support");
368 tbl.cell(3, 1).unwrap().set_text("Email only");
369 tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370 tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372 tbl.cell(4, 0).unwrap().set_text("Price");
373 tbl.cell(4, 0).unwrap().shading("F2F2F2");
374 tbl.cell(4, 1).unwrap().set_text("$29/month");
375 tbl.cell(4, 1).unwrap().shading("F2F2F2");
376 tbl.cell(4, 2).unwrap().set_text("$199/month");
377 tbl.cell(4, 2).unwrap().shading("C6EFCE");
378 }
379
380 doc.add_paragraph("");
381
382 // =========================================================================
383 // 7. Wide table with fixed layout and row height
384 // =========================================================================
385 doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386 .style("Heading2");
387
388 {
389 let mut tbl = doc.add_table(4, 5);
390 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391 tbl = tbl.width(Length::inches(7.0));
392 tbl = tbl.layout_fixed();
393
394 // Set column widths
395 for col in 0..5 {
396 tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397 }
398
399 // Header with exact height
400 tbl.row(0).unwrap().height_exact(Length::twips(480));
401 tbl.row(0).unwrap().header();
402 tbl.row(0).unwrap().cant_split();
403
404 let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405 for (col, h) in headers.iter().enumerate() {
406 tbl.cell(0, col).unwrap().set_text(h);
407 tbl.cell(0, col).unwrap().shading("404040");
408 tbl.cell(0, col)
409 .unwrap()
410 .vertical_alignment(VerticalAlignment::Center);
411 }
412
413 // Schedule rows with minimum height
414 tbl.row(1).unwrap().height(Length::twips(600));
415 tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416 tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417 tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418 tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419 tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421 tbl.row(2).unwrap().height(Length::twips(600));
422 tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423 tbl.cell(2, 0).unwrap().shading("D6E4F0");
424 tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425 tbl.cell(2, 1).unwrap().shading("FCE4D6");
426 tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427 tbl.cell(2, 2).unwrap().shading("D6E4F0");
428 tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429 tbl.cell(2, 3).unwrap().shading("E2EFDA");
430 tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431 tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433 tbl.row(3).unwrap().height(Length::twips(600));
434 tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435 tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436 tbl.cell(3, 1).unwrap().shading("D6E4F0");
437 tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438 tbl.cell(3, 2).unwrap().shading("FCE4D6");
439 tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440 tbl.cell(3, 3).unwrap().shading("D6E4F0");
441 tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442 tbl.cell(3, 4).unwrap().shading("E2EFDA");
443 }
444
445 doc.set_title("Styled Tables Showcase");
446 doc.set_author("rdocx");
447
448 doc.save(path).unwrap();
449}Sourcepub fn paragraphs(&self) -> impl Iterator<Item = ParagraphRef<'_>>
pub fn paragraphs(&self) -> impl Iterator<Item = ParagraphRef<'_>>
Get an iterator over immutable paragraph references.
Sourcepub fn width(self, length: Length) -> Self
pub fn width(self, length: Length) -> Self
Set cell width.
Examples found in repository?
examples/styled_tables.rs (line 396)
24fn generate_styled_tables(path: &Path) {
25 let mut doc = Document::new();
26 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27 doc.set_margins(
28 Length::inches(0.75),
29 Length::inches(0.75),
30 Length::inches(0.75),
31 Length::inches(0.75),
32 );
33
34 doc.add_paragraph("Styled Tables Showcase")
35 .style("Heading1");
36
37 doc.add_paragraph("");
38
39 // =========================================================================
40 // 1. Professional report table with alternating rows
41 // =========================================================================
42 doc.add_paragraph("1. Report Table with Alternating Row Colors")
43 .style("Heading2");
44
45 {
46 let mut tbl = doc.add_table(8, 4);
47 tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48 tbl = tbl.width_pct(100.0);
49
50 // Header row
51 let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52 for (col, h) in headers.iter().enumerate() {
53 tbl.cell(0, col).unwrap().shading("2E75B6");
54 tbl.cell(0, col).unwrap().set_text(h);
55 }
56 tbl.row(0).unwrap().header();
57
58 // Data with alternating shading
59 let data = [
60 ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61 ["Professional", "$189,000", "$201,000", "+6.3%"],
62 ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63 ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64 ["Training", "$22,000", "$28,900", "+31.4%"],
65 ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66 ];
67
68 for (i, row) in data.iter().enumerate() {
69 let row_idx = i + 1;
70 for (col, val) in row.iter().enumerate() {
71 tbl.cell(row_idx, col).unwrap().set_text(val);
72 // Alternate row colors
73 if i % 2 == 0 {
74 tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75 }
76 }
77 }
78
79 // Total row
80 tbl.cell(7, 0).unwrap().set_text("TOTAL");
81 tbl.cell(7, 0).unwrap().shading("D6E4F0");
82 tbl.cell(7, 1).unwrap().set_text("$613,000");
83 tbl.cell(7, 1).unwrap().shading("D6E4F0");
84 tbl.cell(7, 2).unwrap().set_text("$729,900");
85 tbl.cell(7, 2).unwrap().shading("D6E4F0");
86 tbl.cell(7, 3).unwrap().set_text("+19.1%");
87 tbl.cell(7, 3).unwrap().shading("D6E4F0");
88 }
89
90 doc.add_paragraph("");
91
92 // =========================================================================
93 // 2. Invoice-style table with merged header
94 // =========================================================================
95 doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96 .style("Heading2");
97
98 {
99 let mut tbl = doc.add_table(7, 4);
100 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101 tbl = tbl.width_pct(100.0);
102
103 // Merged title row
104 tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105 tbl.cell(0, 0).unwrap().grid_span(4);
106 tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108 // Column headers
109 let headers = ["Item", "Description", "Qty", "Amount"];
110 for (col, h) in headers.iter().enumerate() {
111 tbl.cell(1, col).unwrap().set_text(h);
112 tbl.cell(1, col).unwrap().shading("D6E4F0");
113 }
114
115 // Line items
116 tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117 tbl.cell(2, 1)
118 .unwrap()
119 .set_text("Enterprise License (500 seats)");
120 tbl.cell(2, 2).unwrap().set_text("1");
121 tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123 tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124 tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125 tbl.cell(3, 2).unwrap().set_text("1");
126 tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128 tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129 tbl.cell(4, 1)
130 .unwrap()
131 .set_text("On-site Training (3 days)");
132 tbl.cell(4, 2).unwrap().set_text("1");
133 tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135 // Subtotal
136 tbl.cell(5, 0).unwrap().set_text("Subtotal");
137 tbl.cell(5, 0).unwrap().grid_span(3);
138 tbl.cell(5, 0).unwrap().shading("F2F2F2");
139 tbl.cell(5, 3).unwrap().set_text("$89,500");
140 tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142 // Total
143 tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144 tbl.cell(6, 0).unwrap().grid_span(3);
145 tbl.cell(6, 0).unwrap().shading("1F4E79");
146 tbl.cell(6, 3).unwrap().set_text("$89,500");
147 tbl.cell(6, 3).unwrap().shading("1F4E79");
148 }
149
150 doc.add_paragraph("");
151
152 // =========================================================================
153 // 3. Specification table with vertical merge
154 // =========================================================================
155 doc.add_paragraph("3. Specification Table with Vertical Merges")
156 .style("Heading2");
157
158 {
159 let mut tbl = doc.add_table(8, 3);
160 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161 tbl = tbl.width_pct(100.0);
162
163 // Header
164 tbl.cell(0, 0).unwrap().set_text("Category");
165 tbl.cell(0, 0).unwrap().shading("2E75B6");
166 tbl.cell(0, 1).unwrap().set_text("Specification");
167 tbl.cell(0, 1).unwrap().shading("2E75B6");
168 tbl.cell(0, 2).unwrap().set_text("Value");
169 tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171 // "Hardware" spans 3 rows
172 tbl.cell(1, 0).unwrap().set_text("Hardware");
173 tbl.cell(1, 0).unwrap().v_merge_restart();
174 tbl.cell(1, 0).unwrap().shading("E2EFDA");
175 tbl.cell(1, 0)
176 .unwrap()
177 .vertical_alignment(VerticalAlignment::Center);
178 tbl.cell(1, 1).unwrap().set_text("Processor");
179 tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181 tbl.cell(2, 0).unwrap().v_merge_continue();
182 tbl.cell(2, 1).unwrap().set_text("Memory");
183 tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185 tbl.cell(3, 0).unwrap().v_merge_continue();
186 tbl.cell(3, 1).unwrap().set_text("Storage");
187 tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189 // "Network" spans 2 rows
190 tbl.cell(4, 0).unwrap().set_text("Network");
191 tbl.cell(4, 0).unwrap().v_merge_restart();
192 tbl.cell(4, 0).unwrap().shading("FCE4D6");
193 tbl.cell(4, 0)
194 .unwrap()
195 .vertical_alignment(VerticalAlignment::Center);
196 tbl.cell(4, 1).unwrap().set_text("Ethernet");
197 tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199 tbl.cell(5, 0).unwrap().v_merge_continue();
200 tbl.cell(5, 1).unwrap().set_text("Management");
201 tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203 // "Software" spans 2 rows
204 tbl.cell(6, 0).unwrap().set_text("Software");
205 tbl.cell(6, 0).unwrap().v_merge_restart();
206 tbl.cell(6, 0).unwrap().shading("D6E4F0");
207 tbl.cell(6, 0)
208 .unwrap()
209 .vertical_alignment(VerticalAlignment::Center);
210 tbl.cell(6, 1).unwrap().set_text("Operating System");
211 tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213 tbl.cell(7, 0).unwrap().v_merge_continue();
214 tbl.cell(7, 1).unwrap().set_text("Monitoring");
215 tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216 }
217
218 doc.add_paragraph("");
219
220 // =========================================================================
221 // 4. Nested table (table inside a cell)
222 // =========================================================================
223 doc.add_paragraph("4. Nested Table").style("Heading2");
224
225 {
226 let mut tbl = doc.add_table(2, 2);
227 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228 tbl = tbl.width_pct(100.0);
229 tbl = tbl.cell_margins(
230 Length::twips(72),
231 Length::twips(108),
232 Length::twips(72),
233 Length::twips(108),
234 );
235
236 tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237 tbl.cell(0, 0).unwrap().shading("2E75B6");
238 tbl.cell(0, 1).unwrap().set_text("Project Beta");
239 tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241 // Nested table in cell (1,0)
242 {
243 let mut cell = tbl.cell(1, 0).unwrap();
244 cell.set_text("Milestones:");
245 let mut inner = cell.add_table(3, 2);
246 inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247 inner.cell(0, 0).unwrap().set_text("Phase");
248 inner.cell(0, 0).unwrap().shading("E2EFDA");
249 inner.cell(0, 1).unwrap().set_text("Status");
250 inner.cell(0, 1).unwrap().shading("E2EFDA");
251 inner.cell(1, 0).unwrap().set_text("Design");
252 inner.cell(1, 1).unwrap().set_text("Complete");
253 inner.cell(2, 0).unwrap().set_text("Build");
254 inner.cell(2, 1).unwrap().set_text("In Progress");
255 }
256
257 // Nested table in cell (1,1)
258 {
259 let mut cell = tbl.cell(1, 1).unwrap();
260 cell.set_text("Budget:");
261 let mut inner = cell.add_table(3, 2);
262 inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263 inner.cell(0, 0).unwrap().set_text("Category");
264 inner.cell(0, 0).unwrap().shading("FCE4D6");
265 inner.cell(0, 1).unwrap().set_text("Amount");
266 inner.cell(0, 1).unwrap().shading("FCE4D6");
267 inner.cell(1, 0).unwrap().set_text("Development");
268 inner.cell(1, 1).unwrap().set_text("$120,000");
269 inner.cell(2, 0).unwrap().set_text("Testing");
270 inner.cell(2, 1).unwrap().set_text("$35,000");
271 }
272 }
273
274 doc.add_paragraph("");
275
276 // =========================================================================
277 // 5. Form-style table with labels
278 // =========================================================================
279 doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281 {
282 let mut tbl = doc.add_table(6, 4);
283 tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284 tbl = tbl.width_pct(100.0);
285
286 // Row 0: Full-width title
287 tbl.cell(0, 0)
288 .unwrap()
289 .set_text("Customer Registration Form");
290 tbl.cell(0, 0).unwrap().grid_span(4);
291 tbl.cell(0, 0).unwrap().shading("404040");
292
293 // Row 1: Name fields
294 tbl.cell(1, 0).unwrap().set_text("First Name");
295 tbl.cell(1, 0).unwrap().shading("E8E8E8");
296 tbl.cell(1, 1).unwrap().set_text("John");
297 tbl.cell(1, 2).unwrap().set_text("Last Name");
298 tbl.cell(1, 2).unwrap().shading("E8E8E8");
299 tbl.cell(1, 3).unwrap().set_text("Smith");
300
301 // Row 2: Contact
302 tbl.cell(2, 0).unwrap().set_text("Email");
303 tbl.cell(2, 0).unwrap().shading("E8E8E8");
304 tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305 tbl.cell(2, 1).unwrap().grid_span(3);
306
307 // Row 3: Phone
308 tbl.cell(3, 0).unwrap().set_text("Phone");
309 tbl.cell(3, 0).unwrap().shading("E8E8E8");
310 tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311 tbl.cell(3, 2).unwrap().set_text("Company");
312 tbl.cell(3, 2).unwrap().shading("E8E8E8");
313 tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315 // Row 4: Address (spanning)
316 tbl.cell(4, 0).unwrap().set_text("Address");
317 tbl.cell(4, 0).unwrap().shading("E8E8E8");
318 tbl.cell(4, 1)
319 .unwrap()
320 .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321 tbl.cell(4, 1).unwrap().grid_span(3);
322
323 // Row 5: Notes
324 tbl.cell(5, 0).unwrap().set_text("Notes");
325 tbl.cell(5, 0).unwrap().shading("E8E8E8");
326 tbl.cell(5, 0)
327 .unwrap()
328 .vertical_alignment(VerticalAlignment::Top);
329 {
330 let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331 cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332 cell.add_paragraph("Annual review scheduled for March 2026.");
333 }
334 }
335
336 doc.add_paragraph("");
337
338 // =========================================================================
339 // 6. Comparison table with border styles
340 // =========================================================================
341 doc.add_paragraph("6. Comparison Table with Custom Borders")
342 .style("Heading2");
343
344 {
345 let mut tbl = doc.add_table(5, 3);
346 tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347 tbl = tbl.width_pct(100.0);
348
349 // Header
350 tbl.cell(0, 0).unwrap().set_text("Feature");
351 tbl.cell(0, 0).unwrap().shading("2E75B6");
352 tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353 tbl.cell(0, 1).unwrap().shading("2E75B6");
354 tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355 tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357 tbl.cell(1, 0).unwrap().set_text("Users");
358 tbl.cell(1, 1).unwrap().set_text("Up to 10");
359 tbl.cell(1, 2).unwrap().set_text("Unlimited");
360 tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362 tbl.cell(2, 0).unwrap().set_text("Storage");
363 tbl.cell(2, 1).unwrap().set_text("50 GB");
364 tbl.cell(2, 2).unwrap().set_text("5 TB");
365 tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367 tbl.cell(3, 0).unwrap().set_text("Support");
368 tbl.cell(3, 1).unwrap().set_text("Email only");
369 tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370 tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372 tbl.cell(4, 0).unwrap().set_text("Price");
373 tbl.cell(4, 0).unwrap().shading("F2F2F2");
374 tbl.cell(4, 1).unwrap().set_text("$29/month");
375 tbl.cell(4, 1).unwrap().shading("F2F2F2");
376 tbl.cell(4, 2).unwrap().set_text("$199/month");
377 tbl.cell(4, 2).unwrap().shading("C6EFCE");
378 }
379
380 doc.add_paragraph("");
381
382 // =========================================================================
383 // 7. Wide table with fixed layout and row height
384 // =========================================================================
385 doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386 .style("Heading2");
387
388 {
389 let mut tbl = doc.add_table(4, 5);
390 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391 tbl = tbl.width(Length::inches(7.0));
392 tbl = tbl.layout_fixed();
393
394 // Set column widths
395 for col in 0..5 {
396 tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397 }
398
399 // Header with exact height
400 tbl.row(0).unwrap().height_exact(Length::twips(480));
401 tbl.row(0).unwrap().header();
402 tbl.row(0).unwrap().cant_split();
403
404 let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405 for (col, h) in headers.iter().enumerate() {
406 tbl.cell(0, col).unwrap().set_text(h);
407 tbl.cell(0, col).unwrap().shading("404040");
408 tbl.cell(0, col)
409 .unwrap()
410 .vertical_alignment(VerticalAlignment::Center);
411 }
412
413 // Schedule rows with minimum height
414 tbl.row(1).unwrap().height(Length::twips(600));
415 tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416 tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417 tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418 tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419 tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421 tbl.row(2).unwrap().height(Length::twips(600));
422 tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423 tbl.cell(2, 0).unwrap().shading("D6E4F0");
424 tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425 tbl.cell(2, 1).unwrap().shading("FCE4D6");
426 tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427 tbl.cell(2, 2).unwrap().shading("D6E4F0");
428 tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429 tbl.cell(2, 3).unwrap().shading("E2EFDA");
430 tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431 tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433 tbl.row(3).unwrap().height(Length::twips(600));
434 tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435 tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436 tbl.cell(3, 1).unwrap().shading("D6E4F0");
437 tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438 tbl.cell(3, 2).unwrap().shading("FCE4D6");
439 tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440 tbl.cell(3, 3).unwrap().shading("D6E4F0");
441 tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442 tbl.cell(3, 4).unwrap().shading("E2EFDA");
443 }
444
445 doc.set_title("Styled Tables Showcase");
446 doc.set_author("rdocx");
447
448 doc.save(path).unwrap();
449}More examples
examples/generate_all_samples.rs (line 474)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87 let mut doc = Document::new();
88
89 // ── Page Setup & Metadata ──
90 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91 doc.set_margins(
92 Length::inches(1.0),
93 Length::inches(1.0),
94 Length::inches(1.0),
95 Length::inches(1.0),
96 );
97 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98 doc.set_gutter(Length::twips(0));
99 doc.set_title("rdocx Feature Showcase");
100 doc.set_author("rdocx Sample Generator");
101 doc.set_subject("Comprehensive feature demonstration");
102 doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104 // Headers & Footers with different first page
105 doc.set_different_first_page(true);
106 doc.set_first_page_header("rdocx");
107 doc.set_first_page_footer("Feature Showcase — Cover Page");
108 doc.set_header("rdocx Feature Showcase");
109 doc.set_footer("Generated by rdocx");
110
111 // ── COVER PAGE ──
112 let bg = create_sample_png(612, 792, [20, 45, 90]);
113 doc.add_background_image(&bg, "cover_bg.png");
114
115 for _ in 0..3 {
116 doc.add_paragraph("");
117 }
118 {
119 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121 }
122 {
123 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124 p.add_run("Complete Feature Showcase")
125 .size(28.0)
126 .color("FFFFFF")
127 .italic(true);
128 }
129 doc.add_paragraph("");
130 {
131 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132 p.add_run("Every feature of the rdocx Rust library")
133 .size(13.0)
134 .color("B0C4DE");
135 }
136 {
137 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138 p.add_run("demonstrated in a single document.")
139 .size(13.0)
140 .color("B0C4DE");
141 }
142
143 // ── TABLE OF CONTENTS ──
144 doc.add_paragraph("").page_break_before(true);
145 doc.insert_toc(doc.content_count(), 3);
146
147 // ── SECTION 1: TEXT FORMATTING ──
148 doc.add_paragraph("").page_break_before(true);
149 doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151 // Paragraph Alignment
152 doc.add_paragraph("1.1 Paragraph Alignment")
153 .style("Heading2");
154 doc.add_paragraph("Left-aligned paragraph (default).")
155 .alignment(Alignment::Left);
156 doc.add_paragraph("Center-aligned paragraph.")
157 .alignment(Alignment::Center);
158 doc.add_paragraph("Right-aligned paragraph.")
159 .alignment(Alignment::Right);
160 doc.add_paragraph(
161 "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162 distributes extra space across word gaps so lines fill the full width of the text area.",
163 )
164 .alignment(Alignment::Justify);
165
166 doc.add_paragraph("");
167
168 // Run Formatting
169 doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170 {
171 let mut p = doc.add_paragraph("");
172 p.add_run("Bold").bold(true);
173 p.add_run(" | ");
174 p.add_run("Italic").italic(true);
175 p.add_run(" | ");
176 p.add_run("Bold Italic").bold(true).italic(true);
177 p.add_run(" | ");
178 p.add_run("Underline").underline(true);
179 p.add_run(" | ");
180 p.add_run("Strikethrough").strike(true);
181 p.add_run(" | ");
182 p.add_run("Double Strike").double_strike(true);
183 }
184 {
185 let mut p = doc.add_paragraph("");
186 p.add_run("Red").color("FF0000");
187 p.add_run(" | ");
188 p.add_run("Blue").color("0000FF");
189 p.add_run(" | ");
190 p.add_run("Green").color("00AA00");
191 p.add_run(" | ");
192 p.add_run("Highlighted").highlight("FFFF00");
193 }
194 {
195 let mut p = doc.add_paragraph("");
196 p.add_run("8pt").size(8.0);
197 p.add_run(" | ");
198 p.add_run("11pt").size(11.0);
199 p.add_run(" | ");
200 p.add_run("16pt").size(16.0);
201 p.add_run(" | ");
202 p.add_run("24pt").size(24.0);
203 }
204 {
205 let mut p = doc.add_paragraph("");
206 p.add_run("Arial").font("Arial");
207 p.add_run(" | ");
208 p.add_run("Times New Roman").font("Times New Roman");
209 p.add_run(" | ");
210 p.add_run("Courier New").font("Courier New");
211 }
212 {
213 let mut p = doc.add_paragraph("");
214 p.add_run("H");
215 p.add_run("2").subscript();
216 p.add_run("O (subscript) | E=mc");
217 p.add_run("2").superscript();
218 p.add_run(" (superscript)");
219 }
220 {
221 let mut p = doc.add_paragraph("");
222 p.add_run("ALL CAPS").all_caps(true);
223 p.add_run(" | ");
224 p.add_run("Small Caps").small_caps(true);
225 p.add_run(" | ");
226 p.add_run("Expanded").character_spacing(Length::twips(40));
227 p.add_run(" | ");
228 p.add_run("Hidden text (not visible)").hidden(true);
229 }
230
231 doc.add_paragraph("");
232
233 // Underline Styles
234 doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235 {
236 let mut p = doc.add_paragraph("");
237 p.add_run("Single ")
238 .underline_style(rdocx::UnderlineStyle::Single);
239 p.add_run("Double ")
240 .underline_style(rdocx::UnderlineStyle::Double);
241 p.add_run("Thick ")
242 .underline_style(rdocx::UnderlineStyle::Thick);
243 p.add_run("Dotted ")
244 .underline_style(rdocx::UnderlineStyle::Dotted);
245 p.add_run("Dash ")
246 .underline_style(rdocx::UnderlineStyle::Dash);
247 p.add_run("Wave ")
248 .underline_style(rdocx::UnderlineStyle::Wave);
249 }
250
251 // ── SECTION 2: PARAGRAPH FORMATTING ──
252 doc.add_paragraph("").page_break_before(true);
253 doc.add_paragraph("2. Paragraph Formatting")
254 .style("Heading1");
255
256 doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257 doc.add_paragraph("Paragraph with light green shading")
258 .shading("E2EFDA");
259 doc.add_paragraph("Paragraph with bottom border")
260 .border_bottom(BorderStyle::Single, 6, "2E75B6");
261 doc.add_paragraph("Paragraph with all borders (red)")
262 .border_all(BorderStyle::Single, 4, "FF0000");
263
264 doc.add_paragraph("2.2 Indentation").style("Heading2");
265 doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266 .indent_left(Length::inches(1.0))
267 .hanging_indent(Length::inches(0.5));
268 doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269 .first_line_indent(Length::inches(0.5));
270 doc.add_paragraph("Both left and right indent (0.75 inches each)")
271 .indent_left(Length::inches(0.75))
272 .indent_right(Length::inches(0.75));
273
274 doc.add_paragraph("2.3 Spacing & Line Height")
275 .style("Heading2");
276 doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277 .space_before(Length::pt(24.0))
278 .space_after(Length::pt(12.0));
279 doc.add_paragraph(
280 "Double line spacing paragraph. This text should have extra vertical space between \
281 lines to demonstrate line_spacing_multiple(2.0).",
282 )
283 .line_spacing_multiple(2.0);
284 doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285 .line_spacing(20.0);
286
287 doc.add_paragraph("2.4 Pagination Controls")
288 .style("Heading2");
289 doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290 .keep_with_next(true);
291 doc.add_paragraph("(This paragraph was kept with the one above.)");
292 doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293 .keep_together(true);
294 doc.add_paragraph("widow_control — prevents widow/orphan lines")
295 .widow_control(true);
296
297 // ── SECTION 3: LISTS ──
298 doc.add_paragraph("").page_break_before(true);
299 doc.add_paragraph("3. Lists").style("Heading1");
300
301 doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302 doc.add_bullet_list_item("First item", 0);
303 doc.add_bullet_list_item("Second item", 0);
304 doc.add_bullet_list_item("Nested level 1", 1);
305 doc.add_bullet_list_item("Nested level 2", 2);
306 doc.add_bullet_list_item("Back to level 1", 1);
307 doc.add_bullet_list_item("Third item", 0);
308
309 doc.add_paragraph("");
310
311 doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312 doc.add_numbered_list_item("First numbered item", 0);
313 doc.add_numbered_list_item("Second numbered item", 0);
314 doc.add_numbered_list_item("Sub-item A", 1);
315 doc.add_numbered_list_item("Sub-item B", 1);
316 doc.add_numbered_list_item("Third numbered item", 0);
317
318 // ── SECTION 4: TAB STOPS ──
319 doc.add_paragraph("");
320 doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322 doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329 doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330 doc.add_paragraph("Item\t\tPrice")
331 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334 doc.add_paragraph("Widget\t\t$19.99")
335 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338 doc.add_paragraph("Gadget\t\t$249.50")
339 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340 .add_tab_stop_with_leader(
341 TabAlignment::Right,
342 Length::inches(4.0),
343 TabLeader::Underscore,
344 )
345 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347 // ── SECTION 5: TABLES ──
348 doc.add_paragraph("").page_break_before(true);
349 doc.add_paragraph("5. Tables").style("Heading1");
350
351 doc.add_paragraph("5.1 Basic Table").style("Heading2");
352 {
353 let mut tbl = doc
354 .add_table(4, 3)
355 .borders(BorderStyle::Single, 4, "000000");
356 for col in 0..3 {
357 tbl.cell(0, col).unwrap().shading("2E75B6");
358 }
359 tbl.cell(0, 0).unwrap().set_text("Name");
360 tbl.cell(0, 1).unwrap().set_text("Role");
361 tbl.cell(0, 2).unwrap().set_text("Location");
362 tbl.cell(1, 0).unwrap().set_text("Walter White");
363 tbl.cell(1, 1).unwrap().set_text("CEO");
364 tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365 tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366 tbl.cell(2, 1).unwrap().set_text("CTO");
367 tbl.cell(2, 2).unwrap().set_text("Remote");
368 tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369 tbl.cell(3, 1).unwrap().set_text("Security");
370 tbl.cell(3, 2).unwrap().set_text("Washington");
371 }
372
373 doc.add_paragraph("");
374
375 doc.add_paragraph("5.2 Column Span & Cell Shading")
376 .style("Heading2");
377 {
378 let mut tbl = doc
379 .add_table(3, 4)
380 .borders(BorderStyle::Single, 4, "000000")
381 .width_pct(100.0);
382 tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383 tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384 tbl.cell(1, 0).unwrap().set_text("Region");
385 tbl.cell(1, 0).unwrap().shading("D6E4F0");
386 tbl.cell(1, 1).unwrap().set_text("Q1");
387 tbl.cell(1, 1).unwrap().shading("D6E4F0");
388 tbl.cell(1, 2).unwrap().set_text("Q2");
389 tbl.cell(1, 2).unwrap().shading("D6E4F0");
390 tbl.cell(1, 3).unwrap().set_text("Total");
391 tbl.cell(1, 3).unwrap().shading("D6E4F0");
392 tbl.cell(2, 0).unwrap().set_text("Americas");
393 tbl.cell(2, 1).unwrap().set_text("$2.4M");
394 tbl.cell(2, 2).unwrap().set_text("$2.7M");
395 tbl.cell(2, 3).unwrap().set_text("$5.1M");
396 }
397
398 doc.add_paragraph("");
399
400 doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401 {
402 let mut tbl = doc
403 .add_table(4, 3)
404 .borders(BorderStyle::Single, 4, "000000");
405 tbl.cell(0, 0).unwrap().set_text("Category");
406 tbl.cell(0, 0).unwrap().shading("E2EFDA");
407 tbl.cell(0, 1).unwrap().set_text("Item");
408 tbl.cell(0, 1).unwrap().shading("E2EFDA");
409 tbl.cell(0, 2).unwrap().set_text("Price");
410 tbl.cell(0, 2).unwrap().shading("E2EFDA");
411 tbl.cell(1, 0).unwrap().set_text("Hardware");
412 tbl.cell(1, 0).unwrap().v_merge_restart();
413 tbl.cell(1, 1).unwrap().set_text("Laptop");
414 tbl.cell(1, 2).unwrap().set_text("$1,200");
415 tbl.cell(2, 0).unwrap().v_merge_continue();
416 tbl.cell(2, 1).unwrap().set_text("Monitor");
417 tbl.cell(2, 2).unwrap().set_text("$450");
418 tbl.cell(3, 0).unwrap().set_text("Software");
419 tbl.cell(3, 1).unwrap().set_text("IDE License");
420 tbl.cell(3, 2).unwrap().set_text("$200/yr");
421 }
422
423 doc.add_paragraph("");
424
425 doc.add_paragraph("5.4 Nested Table").style("Heading2");
426 {
427 let mut tbl = doc
428 .add_table(2, 2)
429 .borders(BorderStyle::Single, 6, "2E75B6");
430 tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431 tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432 tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433 {
434 let mut cell = tbl.cell(1, 1).unwrap();
435 cell.set_text("Nested table below:");
436 let mut nested = cell
437 .add_table(2, 2)
438 .borders(BorderStyle::Single, 2, "FF6600");
439 nested.cell(0, 0).unwrap().set_text("A");
440 nested.cell(0, 1).unwrap().set_text("B");
441 nested.cell(1, 0).unwrap().set_text("C");
442 nested.cell(1, 1).unwrap().set_text("D");
443 }
444 }
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449 .style("Heading2");
450 {
451 let mut tbl = doc
452 .add_table(2, 3)
453 .borders(BorderStyle::Single, 4, "666666");
454 tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455 tbl.cell(0, 0).unwrap().set_text("Top");
456 tbl.cell(0, 0)
457 .unwrap()
458 .vertical_alignment(VerticalAlignment::Top)
459 .shading("FFF2CC");
460 tbl.cell(0, 1).unwrap().set_text("Center");
461 tbl.cell(0, 1)
462 .unwrap()
463 .vertical_alignment(VerticalAlignment::Center)
464 .shading("D9E2F3");
465 tbl.cell(0, 2).unwrap().set_text("Bottom");
466 tbl.cell(0, 2)
467 .unwrap()
468 .vertical_alignment(VerticalAlignment::Bottom)
469 .shading("E2EFDA");
470 tbl.row(1).unwrap().cant_split();
471 tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472 tbl.cell(1, 0).unwrap().no_wrap();
473 tbl.cell(1, 1).unwrap().set_text("Fixed width");
474 tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475 tbl.cell(1, 2).unwrap().set_text("Normal");
476 }
477
478 // ── SECTION 6: IMAGES ──
479 doc.add_paragraph("").page_break_before(true);
480 doc.add_paragraph("6. Images").style("Heading1");
481
482 doc.add_paragraph("6.1 Inline Image").style("Heading2");
483 doc.add_paragraph("A blue gradient image below:");
484 let img = create_sample_png(200, 50, [0, 80, 200]);
485 doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487 doc.add_paragraph("");
488 doc.add_paragraph("6.2 Header Image").style("Heading2");
489 let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490 doc.set_header_image(
491 &hdr_img,
492 "header_logo.png",
493 Length::inches(2.0),
494 Length::inches(0.2),
495 );
496 doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498 doc.add_paragraph("");
499 doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501 // ── SECTION 7: CONTENT MANIPULATION ──
502 doc.add_paragraph("").page_break_before(true);
503 doc.add_paragraph("7. Content Manipulation")
504 .style("Heading1");
505
506 doc.add_paragraph("7.1 Placeholder Replacement")
507 .style("Heading2");
508 doc.add_paragraph("Customer: {{customer}}");
509 doc.add_paragraph("Date: {{date}}");
510 doc.add_paragraph("Reference: {{ref_number}}");
511 {
512 let mut tbl = doc
513 .add_table(2, 2)
514 .borders(BorderStyle::Single, 4, "000000");
515 tbl.cell(0, 0).unwrap().set_text("Project");
516 tbl.cell(0, 0).unwrap().shading("D6E4F0");
517 tbl.cell(0, 1).unwrap().set_text("{{project}}");
518 tbl.cell(1, 0).unwrap().set_text("Status");
519 tbl.cell(1, 0).unwrap().shading("D6E4F0");
520 tbl.cell(1, 1).unwrap().set_text("{{status}}");
521 }
522
523 let mut replacements = HashMap::new();
524 replacements.insert("{{customer}}", "Tensorbee Inc.");
525 replacements.insert("{{date}}", "February 22, 2026");
526 replacements.insert("{{ref_number}}", "TB-2026-001");
527 replacements.insert("{{project}}", "Infrastructure Upgrade");
528 replacements.insert("{{status}}", "In Progress");
529 let n = doc.replace_all(&replacements);
530 doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532 doc.add_paragraph("");
533
534 doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535 doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536 let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537 doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539 doc.add_paragraph("");
540
541 doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542 doc.add_paragraph("Section A: First content.");
543 doc.add_paragraph("Section C: Third content.");
544 if let Some(idx) = doc.find_content_index("Section C") {
545 doc.insert_paragraph(
546 idx,
547 "Section B: Inserted between A and C via find_content_index().",
548 );
549 }
550
551 // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553 doc.add_paragraph("8. Section Breaks & Orientation")
554 .style("Heading1");
555 doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557 {
558 let mut tbl = doc
559 .add_table(3, 7)
560 .borders(BorderStyle::Single, 4, "2E75B6");
561 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562 for (c, h) in headers.iter().enumerate() {
563 tbl.cell(0, c).unwrap().set_text(h);
564 tbl.cell(0, c).unwrap().shading("2E75B6");
565 }
566 let data = [
567 [
568 "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569 ],
570 [
571 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572 ],
573 ];
574 for (r, row) in data.iter().enumerate() {
575 for (c, v) in row.iter().enumerate() {
576 tbl.cell(r + 1, c).unwrap().set_text(v);
577 }
578 }
579 }
580
581 doc.add_paragraph("")
582 .section_break(SectionBreak::NextPage)
583 .section_landscape();
584
585 // ── SECTION 9: CUSTOM STYLES ──
586 doc.add_paragraph("9. Custom Styles").style("Heading1");
587 doc.add_style(
588 StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589 .based_on("Normal")
590 .paragraph_properties({
591 let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592 ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593 val: "clear".to_string(),
594 color: None,
595 fill: Some("FFF2CC".to_string()),
596 });
597 ppr
598 })
599 .run_properties({
600 let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601 rpr.bold = Some(true);
602 rpr.color = Some("C45911".to_string());
603 rpr
604 }),
605 );
606 doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607 .style("CustomHighlight");
608
609 doc.add_paragraph("");
610
611 // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612 doc.add_paragraph("10. Document Intelligence API")
613 .style("Heading1");
614
615 let wc = doc.word_count();
616 let hc = doc.headings().len();
617 let ic = doc.images().len();
618 let lc = doc.links().len();
619 doc.add_paragraph(&format!("Word count: {wc}"));
620 doc.add_paragraph(&format!("Heading count: {hc}"));
621 doc.add_paragraph(&format!("Image count: {ic}"));
622 doc.add_paragraph(&format!("Link count: {lc}"));
623
624 let outline = doc.document_outline();
625 doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627 let issues = doc.audit_accessibility();
628 doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629 for issue in &issues {
630 doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631 }
632
633 // ── SECTION 11: DOCUMENT MERGING ──
634 doc.add_paragraph("");
635 doc.add_paragraph("11. Document Merging").style("Heading1");
636 {
637 let mut other = Document::new();
638 other.add_paragraph("This paragraph was merged from another document using append().");
639 doc.append(&other);
640 }
641
642 doc.add_paragraph("");
643
644 // ── SUMMARY ──
645 doc.add_paragraph("Summary of Demonstrated Features")
646 .style("Heading1");
647 let features = [
648 "Page setup: size, margins, header/footer distance, gutter",
649 "Document metadata: title, author, subject, keywords",
650 "Headers and footers: text, images, different first page",
651 "Background images (full-page behind text)",
652 "Table of Contents generation with bookmarks",
653 "Text formatting: bold, italic, underline styles, strike, color, size, font",
654 "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656 "Pagination controls: keep-with-next, keep-together, widow control",
657 "Bullet and numbered lists with nesting",
658 "Tab stops with dot/underscore/hyphen leaders",
659 "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660 "Row properties: header rows, exact height, cant-split",
661 "Cell properties: width, no-wrap, vertical alignment",
662 "Inline images and header images",
663 "Placeholder replacement (single and batch)",
664 "Regex-based find and replace",
665 "Content insertion at specific positions",
666 "Section breaks with mixed portrait/landscape",
667 "Custom paragraph and character styles",
668 "Document intelligence: word count, headings, outline, images, links",
669 "Accessibility audit",
670 "Document merging (append)",
671 "Export: DOCX, PDF, HTML, Markdown",
672 ];
673 for f in &features {
674 doc.add_bullet_list_item(f, 0);
675 }
676 doc.add_paragraph("");
677 doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678 .alignment(Alignment::Center)
679 .shading("E2EFDA")
680 .border_all(BorderStyle::Single, 2, "00AA00");
681
682 doc
683}Sourcepub fn shading(self, fill_color: &str) -> Self
pub fn shading(self, fill_color: &str) -> Self
Set cell background shading color.
Examples found in repository?
examples/template_replace.rs (line 107)
43fn create_template(path: &Path) {
44 let mut doc = Document::new();
45 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
46 doc.set_margins(
47 Length::inches(1.0),
48 Length::inches(1.0),
49 Length::inches(1.0),
50 Length::inches(1.0),
51 );
52
53 doc.set_header("{{company_name}} — Confidential");
54 doc.set_footer("Prepared by {{author_name}} on {{date}}");
55
56 // ── Title ──
57 doc.add_paragraph("{{company_name}}")
58 .style("Heading1")
59 .alignment(Alignment::Center);
60
61 doc.add_paragraph("Project Proposal")
62 .alignment(Alignment::Center);
63
64 doc.add_paragraph("");
65
66 // ── Summary section ──
67 doc.add_paragraph("Executive Summary").style("Heading2");
68
69 doc.add_paragraph(
70 "This proposal outlines the {{project_name}} project for {{company_name}}. \
71 The primary contact is {{contact_name}} ({{contact_email}}). \
72 The proposed start date is {{start_date}} with an estimated duration of {{duration}}.",
73 );
74
75 doc.add_paragraph("");
76
77 // ── Cross-run placeholder (bold label + normal value) ──
78 doc.add_paragraph("Key Details").style("Heading2");
79
80 {
81 let mut p = doc.add_paragraph("");
82 p.add_run("Project: ").bold(true);
83 p.add_run("{{project_name}}");
84 }
85 {
86 let mut p = doc.add_paragraph("");
87 p.add_run("Budget: ").bold(true);
88 p.add_run("{{budget}}");
89 }
90 {
91 let mut p = doc.add_paragraph("");
92 p.add_run("Status: ").bold(true);
93 p.add_run("{{status}}");
94 }
95
96 doc.add_paragraph("");
97
98 // ── Table with placeholders ──
99 doc.add_paragraph("Team Members").style("Heading2");
100
101 {
102 let mut tbl = doc.add_table(4, 3);
103 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
104
105 // Header row
106 for col in 0..3 {
107 tbl.cell(0, col).unwrap().shading("2E75B6");
108 }
109 tbl.cell(0, 0).unwrap().set_text("Name");
110 tbl.cell(0, 1).unwrap().set_text("Role");
111 tbl.cell(0, 2).unwrap().set_text("Email");
112
113 tbl.cell(1, 0).unwrap().set_text("{{member1_name}}");
114 tbl.cell(1, 1).unwrap().set_text("{{member1_role}}");
115 tbl.cell(1, 2).unwrap().set_text("{{member1_email}}");
116
117 tbl.cell(2, 0).unwrap().set_text("{{member2_name}}");
118 tbl.cell(2, 1).unwrap().set_text("{{member2_role}}");
119 tbl.cell(2, 2).unwrap().set_text("{{member2_email}}");
120
121 tbl.cell(3, 0).unwrap().set_text("{{member3_name}}");
122 tbl.cell(3, 1).unwrap().set_text("{{member3_role}}");
123 tbl.cell(3, 2).unwrap().set_text("{{member3_email}}");
124 }
125
126 doc.add_paragraph("");
127
128 // ── Deliverables section ──
129 doc.add_paragraph("Deliverables").style("Heading2");
130
131 doc.add_paragraph("INSERTION_POINT");
132
133 doc.add_paragraph("");
134
135 // ── Signature block ──
136 doc.add_paragraph("Acceptance").style("Heading2");
137
138 doc.add_paragraph(
139 "By signing below, {{company_name}} agrees to the terms outlined in this proposal.",
140 );
141
142 {
143 let mut tbl = doc.add_table(2, 2);
144 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
145 tbl.cell(0, 0)
146 .unwrap()
147 .set_text("Customer: ___________________");
148 tbl.cell(0, 1)
149 .unwrap()
150 .set_text("Provider: ___________________");
151 tbl.cell(1, 0).unwrap().set_text("Date: {{date}}");
152 tbl.cell(1, 1).unwrap().set_text("Date: {{date}}");
153 }
154
155 doc.set_title("{{company_name}} — Project Proposal Template");
156 doc.set_author("Template Generator");
157
158 doc.save(path).unwrap();
159}
160
161/// Open the template, replace all placeholders, insert content, and save.
162fn fill_template(template_path: &Path, output_path: &Path) {
163 let mut doc = Document::open(template_path).unwrap();
164
165 // ── Batch replacement ──
166 let mut replacements = HashMap::new();
167 replacements.insert("{{company_name}}", "Riverside Medical Center");
168 replacements.insert("{{project_name}}", "Network Security Upgrade");
169 replacements.insert("{{contact_name}}", "Dr. Sarah Chen");
170 replacements.insert("{{contact_email}}", "s.chen@riverside.org");
171 replacements.insert("{{start_date}}", "March 1, 2026");
172 replacements.insert("{{duration}}", "12 weeks");
173 replacements.insert("{{budget}}", "$185,000");
174 replacements.insert("{{status}}", "Pending Approval");
175 replacements.insert("{{author_name}}", "James Wilson");
176 replacements.insert("{{date}}", "February 22, 2026");
177
178 // Team members
179 replacements.insert("{{member1_name}}", "James Wilson");
180 replacements.insert("{{member1_role}}", "Project Lead");
181 replacements.insert("{{member1_email}}", "j.wilson@provider.com");
182 replacements.insert("{{member2_name}}", "Maria Garcia");
183 replacements.insert("{{member2_role}}", "Security Architect");
184 replacements.insert("{{member2_email}}", "m.garcia@provider.com");
185 replacements.insert("{{member3_name}}", "David Park");
186 replacements.insert("{{member3_role}}", "Network Engineer");
187 replacements.insert("{{member3_email}}", "d.park@provider.com");
188
189 let count = doc.replace_all(&replacements);
190 println!(" Replaced {} placeholders", count);
191
192 // ── Insert deliverables at the insertion point ──
193 if let Some(idx) = doc.find_content_index("INSERTION_POINT") {
194 // Remove the placeholder paragraph
195 doc.remove_content(idx);
196
197 // Insert deliverables list
198 doc.insert_paragraph(idx, "The following deliverables are included:");
199
200 // Insert a deliverables table
201 let mut tbl = doc.insert_table(idx + 1, 5, 3);
202 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
203
204 for col in 0..3 {
205 tbl.cell(0, col).unwrap().shading("E2EFDA");
206 }
207 tbl.cell(0, 0).unwrap().set_text("Phase");
208 tbl.cell(0, 1).unwrap().set_text("Description");
209 tbl.cell(0, 2).unwrap().set_text("Timeline");
210
211 tbl.cell(1, 0).unwrap().set_text("1. Discovery");
212 tbl.cell(1, 1)
213 .unwrap()
214 .set_text("Network assessment and asset inventory");
215 tbl.cell(1, 2).unwrap().set_text("Weeks 1-3");
216
217 tbl.cell(2, 0).unwrap().set_text("2. Design");
218 tbl.cell(2, 1)
219 .unwrap()
220 .set_text("Security architecture and policy design");
221 tbl.cell(2, 2).unwrap().set_text("Weeks 4-6");
222
223 tbl.cell(3, 0).unwrap().set_text("3. Implementation");
224 tbl.cell(3, 1)
225 .unwrap()
226 .set_text("Deploy monitoring and access controls");
227 tbl.cell(3, 2).unwrap().set_text("Weeks 7-10");
228
229 tbl.cell(4, 0).unwrap().set_text("4. Validation");
230 tbl.cell(4, 1)
231 .unwrap()
232 .set_text("Testing, training, and handover");
233 tbl.cell(4, 2).unwrap().set_text("Weeks 11-12");
234
235 println!(" Inserted deliverables table at position {}", idx);
236 }
237
238 // ── Update metadata ──
239 doc.set_title("Riverside Medical Center — Network Security Upgrade Proposal");
240 doc.set_author("James Wilson");
241 doc.set_subject("Project Proposal");
242 doc.set_keywords("security, network, medical, proposal");
243
244 doc.save(output_path).unwrap();
245}More examples
examples/styled_tables.rs (line 53)
24fn generate_styled_tables(path: &Path) {
25 let mut doc = Document::new();
26 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27 doc.set_margins(
28 Length::inches(0.75),
29 Length::inches(0.75),
30 Length::inches(0.75),
31 Length::inches(0.75),
32 );
33
34 doc.add_paragraph("Styled Tables Showcase")
35 .style("Heading1");
36
37 doc.add_paragraph("");
38
39 // =========================================================================
40 // 1. Professional report table with alternating rows
41 // =========================================================================
42 doc.add_paragraph("1. Report Table with Alternating Row Colors")
43 .style("Heading2");
44
45 {
46 let mut tbl = doc.add_table(8, 4);
47 tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48 tbl = tbl.width_pct(100.0);
49
50 // Header row
51 let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52 for (col, h) in headers.iter().enumerate() {
53 tbl.cell(0, col).unwrap().shading("2E75B6");
54 tbl.cell(0, col).unwrap().set_text(h);
55 }
56 tbl.row(0).unwrap().header();
57
58 // Data with alternating shading
59 let data = [
60 ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61 ["Professional", "$189,000", "$201,000", "+6.3%"],
62 ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63 ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64 ["Training", "$22,000", "$28,900", "+31.4%"],
65 ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66 ];
67
68 for (i, row) in data.iter().enumerate() {
69 let row_idx = i + 1;
70 for (col, val) in row.iter().enumerate() {
71 tbl.cell(row_idx, col).unwrap().set_text(val);
72 // Alternate row colors
73 if i % 2 == 0 {
74 tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75 }
76 }
77 }
78
79 // Total row
80 tbl.cell(7, 0).unwrap().set_text("TOTAL");
81 tbl.cell(7, 0).unwrap().shading("D6E4F0");
82 tbl.cell(7, 1).unwrap().set_text("$613,000");
83 tbl.cell(7, 1).unwrap().shading("D6E4F0");
84 tbl.cell(7, 2).unwrap().set_text("$729,900");
85 tbl.cell(7, 2).unwrap().shading("D6E4F0");
86 tbl.cell(7, 3).unwrap().set_text("+19.1%");
87 tbl.cell(7, 3).unwrap().shading("D6E4F0");
88 }
89
90 doc.add_paragraph("");
91
92 // =========================================================================
93 // 2. Invoice-style table with merged header
94 // =========================================================================
95 doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96 .style("Heading2");
97
98 {
99 let mut tbl = doc.add_table(7, 4);
100 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101 tbl = tbl.width_pct(100.0);
102
103 // Merged title row
104 tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105 tbl.cell(0, 0).unwrap().grid_span(4);
106 tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108 // Column headers
109 let headers = ["Item", "Description", "Qty", "Amount"];
110 for (col, h) in headers.iter().enumerate() {
111 tbl.cell(1, col).unwrap().set_text(h);
112 tbl.cell(1, col).unwrap().shading("D6E4F0");
113 }
114
115 // Line items
116 tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117 tbl.cell(2, 1)
118 .unwrap()
119 .set_text("Enterprise License (500 seats)");
120 tbl.cell(2, 2).unwrap().set_text("1");
121 tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123 tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124 tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125 tbl.cell(3, 2).unwrap().set_text("1");
126 tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128 tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129 tbl.cell(4, 1)
130 .unwrap()
131 .set_text("On-site Training (3 days)");
132 tbl.cell(4, 2).unwrap().set_text("1");
133 tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135 // Subtotal
136 tbl.cell(5, 0).unwrap().set_text("Subtotal");
137 tbl.cell(5, 0).unwrap().grid_span(3);
138 tbl.cell(5, 0).unwrap().shading("F2F2F2");
139 tbl.cell(5, 3).unwrap().set_text("$89,500");
140 tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142 // Total
143 tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144 tbl.cell(6, 0).unwrap().grid_span(3);
145 tbl.cell(6, 0).unwrap().shading("1F4E79");
146 tbl.cell(6, 3).unwrap().set_text("$89,500");
147 tbl.cell(6, 3).unwrap().shading("1F4E79");
148 }
149
150 doc.add_paragraph("");
151
152 // =========================================================================
153 // 3. Specification table with vertical merge
154 // =========================================================================
155 doc.add_paragraph("3. Specification Table with Vertical Merges")
156 .style("Heading2");
157
158 {
159 let mut tbl = doc.add_table(8, 3);
160 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161 tbl = tbl.width_pct(100.0);
162
163 // Header
164 tbl.cell(0, 0).unwrap().set_text("Category");
165 tbl.cell(0, 0).unwrap().shading("2E75B6");
166 tbl.cell(0, 1).unwrap().set_text("Specification");
167 tbl.cell(0, 1).unwrap().shading("2E75B6");
168 tbl.cell(0, 2).unwrap().set_text("Value");
169 tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171 // "Hardware" spans 3 rows
172 tbl.cell(1, 0).unwrap().set_text("Hardware");
173 tbl.cell(1, 0).unwrap().v_merge_restart();
174 tbl.cell(1, 0).unwrap().shading("E2EFDA");
175 tbl.cell(1, 0)
176 .unwrap()
177 .vertical_alignment(VerticalAlignment::Center);
178 tbl.cell(1, 1).unwrap().set_text("Processor");
179 tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181 tbl.cell(2, 0).unwrap().v_merge_continue();
182 tbl.cell(2, 1).unwrap().set_text("Memory");
183 tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185 tbl.cell(3, 0).unwrap().v_merge_continue();
186 tbl.cell(3, 1).unwrap().set_text("Storage");
187 tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189 // "Network" spans 2 rows
190 tbl.cell(4, 0).unwrap().set_text("Network");
191 tbl.cell(4, 0).unwrap().v_merge_restart();
192 tbl.cell(4, 0).unwrap().shading("FCE4D6");
193 tbl.cell(4, 0)
194 .unwrap()
195 .vertical_alignment(VerticalAlignment::Center);
196 tbl.cell(4, 1).unwrap().set_text("Ethernet");
197 tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199 tbl.cell(5, 0).unwrap().v_merge_continue();
200 tbl.cell(5, 1).unwrap().set_text("Management");
201 tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203 // "Software" spans 2 rows
204 tbl.cell(6, 0).unwrap().set_text("Software");
205 tbl.cell(6, 0).unwrap().v_merge_restart();
206 tbl.cell(6, 0).unwrap().shading("D6E4F0");
207 tbl.cell(6, 0)
208 .unwrap()
209 .vertical_alignment(VerticalAlignment::Center);
210 tbl.cell(6, 1).unwrap().set_text("Operating System");
211 tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213 tbl.cell(7, 0).unwrap().v_merge_continue();
214 tbl.cell(7, 1).unwrap().set_text("Monitoring");
215 tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216 }
217
218 doc.add_paragraph("");
219
220 // =========================================================================
221 // 4. Nested table (table inside a cell)
222 // =========================================================================
223 doc.add_paragraph("4. Nested Table").style("Heading2");
224
225 {
226 let mut tbl = doc.add_table(2, 2);
227 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228 tbl = tbl.width_pct(100.0);
229 tbl = tbl.cell_margins(
230 Length::twips(72),
231 Length::twips(108),
232 Length::twips(72),
233 Length::twips(108),
234 );
235
236 tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237 tbl.cell(0, 0).unwrap().shading("2E75B6");
238 tbl.cell(0, 1).unwrap().set_text("Project Beta");
239 tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241 // Nested table in cell (1,0)
242 {
243 let mut cell = tbl.cell(1, 0).unwrap();
244 cell.set_text("Milestones:");
245 let mut inner = cell.add_table(3, 2);
246 inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247 inner.cell(0, 0).unwrap().set_text("Phase");
248 inner.cell(0, 0).unwrap().shading("E2EFDA");
249 inner.cell(0, 1).unwrap().set_text("Status");
250 inner.cell(0, 1).unwrap().shading("E2EFDA");
251 inner.cell(1, 0).unwrap().set_text("Design");
252 inner.cell(1, 1).unwrap().set_text("Complete");
253 inner.cell(2, 0).unwrap().set_text("Build");
254 inner.cell(2, 1).unwrap().set_text("In Progress");
255 }
256
257 // Nested table in cell (1,1)
258 {
259 let mut cell = tbl.cell(1, 1).unwrap();
260 cell.set_text("Budget:");
261 let mut inner = cell.add_table(3, 2);
262 inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263 inner.cell(0, 0).unwrap().set_text("Category");
264 inner.cell(0, 0).unwrap().shading("FCE4D6");
265 inner.cell(0, 1).unwrap().set_text("Amount");
266 inner.cell(0, 1).unwrap().shading("FCE4D6");
267 inner.cell(1, 0).unwrap().set_text("Development");
268 inner.cell(1, 1).unwrap().set_text("$120,000");
269 inner.cell(2, 0).unwrap().set_text("Testing");
270 inner.cell(2, 1).unwrap().set_text("$35,000");
271 }
272 }
273
274 doc.add_paragraph("");
275
276 // =========================================================================
277 // 5. Form-style table with labels
278 // =========================================================================
279 doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281 {
282 let mut tbl = doc.add_table(6, 4);
283 tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284 tbl = tbl.width_pct(100.0);
285
286 // Row 0: Full-width title
287 tbl.cell(0, 0)
288 .unwrap()
289 .set_text("Customer Registration Form");
290 tbl.cell(0, 0).unwrap().grid_span(4);
291 tbl.cell(0, 0).unwrap().shading("404040");
292
293 // Row 1: Name fields
294 tbl.cell(1, 0).unwrap().set_text("First Name");
295 tbl.cell(1, 0).unwrap().shading("E8E8E8");
296 tbl.cell(1, 1).unwrap().set_text("John");
297 tbl.cell(1, 2).unwrap().set_text("Last Name");
298 tbl.cell(1, 2).unwrap().shading("E8E8E8");
299 tbl.cell(1, 3).unwrap().set_text("Smith");
300
301 // Row 2: Contact
302 tbl.cell(2, 0).unwrap().set_text("Email");
303 tbl.cell(2, 0).unwrap().shading("E8E8E8");
304 tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305 tbl.cell(2, 1).unwrap().grid_span(3);
306
307 // Row 3: Phone
308 tbl.cell(3, 0).unwrap().set_text("Phone");
309 tbl.cell(3, 0).unwrap().shading("E8E8E8");
310 tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311 tbl.cell(3, 2).unwrap().set_text("Company");
312 tbl.cell(3, 2).unwrap().shading("E8E8E8");
313 tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315 // Row 4: Address (spanning)
316 tbl.cell(4, 0).unwrap().set_text("Address");
317 tbl.cell(4, 0).unwrap().shading("E8E8E8");
318 tbl.cell(4, 1)
319 .unwrap()
320 .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321 tbl.cell(4, 1).unwrap().grid_span(3);
322
323 // Row 5: Notes
324 tbl.cell(5, 0).unwrap().set_text("Notes");
325 tbl.cell(5, 0).unwrap().shading("E8E8E8");
326 tbl.cell(5, 0)
327 .unwrap()
328 .vertical_alignment(VerticalAlignment::Top);
329 {
330 let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331 cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332 cell.add_paragraph("Annual review scheduled for March 2026.");
333 }
334 }
335
336 doc.add_paragraph("");
337
338 // =========================================================================
339 // 6. Comparison table with border styles
340 // =========================================================================
341 doc.add_paragraph("6. Comparison Table with Custom Borders")
342 .style("Heading2");
343
344 {
345 let mut tbl = doc.add_table(5, 3);
346 tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347 tbl = tbl.width_pct(100.0);
348
349 // Header
350 tbl.cell(0, 0).unwrap().set_text("Feature");
351 tbl.cell(0, 0).unwrap().shading("2E75B6");
352 tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353 tbl.cell(0, 1).unwrap().shading("2E75B6");
354 tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355 tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357 tbl.cell(1, 0).unwrap().set_text("Users");
358 tbl.cell(1, 1).unwrap().set_text("Up to 10");
359 tbl.cell(1, 2).unwrap().set_text("Unlimited");
360 tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362 tbl.cell(2, 0).unwrap().set_text("Storage");
363 tbl.cell(2, 1).unwrap().set_text("50 GB");
364 tbl.cell(2, 2).unwrap().set_text("5 TB");
365 tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367 tbl.cell(3, 0).unwrap().set_text("Support");
368 tbl.cell(3, 1).unwrap().set_text("Email only");
369 tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370 tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372 tbl.cell(4, 0).unwrap().set_text("Price");
373 tbl.cell(4, 0).unwrap().shading("F2F2F2");
374 tbl.cell(4, 1).unwrap().set_text("$29/month");
375 tbl.cell(4, 1).unwrap().shading("F2F2F2");
376 tbl.cell(4, 2).unwrap().set_text("$199/month");
377 tbl.cell(4, 2).unwrap().shading("C6EFCE");
378 }
379
380 doc.add_paragraph("");
381
382 // =========================================================================
383 // 7. Wide table with fixed layout and row height
384 // =========================================================================
385 doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386 .style("Heading2");
387
388 {
389 let mut tbl = doc.add_table(4, 5);
390 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391 tbl = tbl.width(Length::inches(7.0));
392 tbl = tbl.layout_fixed();
393
394 // Set column widths
395 for col in 0..5 {
396 tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397 }
398
399 // Header with exact height
400 tbl.row(0).unwrap().height_exact(Length::twips(480));
401 tbl.row(0).unwrap().header();
402 tbl.row(0).unwrap().cant_split();
403
404 let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405 for (col, h) in headers.iter().enumerate() {
406 tbl.cell(0, col).unwrap().set_text(h);
407 tbl.cell(0, col).unwrap().shading("404040");
408 tbl.cell(0, col)
409 .unwrap()
410 .vertical_alignment(VerticalAlignment::Center);
411 }
412
413 // Schedule rows with minimum height
414 tbl.row(1).unwrap().height(Length::twips(600));
415 tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416 tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417 tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418 tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419 tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421 tbl.row(2).unwrap().height(Length::twips(600));
422 tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423 tbl.cell(2, 0).unwrap().shading("D6E4F0");
424 tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425 tbl.cell(2, 1).unwrap().shading("FCE4D6");
426 tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427 tbl.cell(2, 2).unwrap().shading("D6E4F0");
428 tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429 tbl.cell(2, 3).unwrap().shading("E2EFDA");
430 tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431 tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433 tbl.row(3).unwrap().height(Length::twips(600));
434 tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435 tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436 tbl.cell(3, 1).unwrap().shading("D6E4F0");
437 tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438 tbl.cell(3, 2).unwrap().shading("FCE4D6");
439 tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440 tbl.cell(3, 3).unwrap().shading("D6E4F0");
441 tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442 tbl.cell(3, 4).unwrap().shading("E2EFDA");
443 }
444
445 doc.set_title("Styled Tables Showcase");
446 doc.set_author("rdocx");
447
448 doc.save(path).unwrap();
449}examples/generate_samples.rs (line 304)
34fn generate_feature_showcase(path: &Path) {
35 let mut doc = Document::new();
36
37 // =========================================================================
38 // PAGE SETUP & METADATA
39 // =========================================================================
40 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41 doc.set_margins(
42 Length::inches(1.0), // top
43 Length::inches(1.0), // right
44 Length::inches(1.0), // bottom
45 Length::inches(1.0), // left
46 );
47 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48 doc.set_gutter(Length::twips(0));
49
50 doc.set_title("rdocx Feature Showcase");
51 doc.set_author("rdocx Sample Generator");
52 doc.set_subject("Comprehensive feature demonstration");
53 doc.set_keywords("rdocx, docx, rust, sample");
54
55 // Header & Footer
56 doc.set_header("rdocx Feature Showcase");
57 doc.set_footer("Generated by rdocx — Page");
58
59 // Different first page header
60 doc.set_different_first_page(true);
61 doc.set_first_page_header("rdocx");
62 doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64 // =========================================================================
65 // PAGE 1: COVER PAGE — background image, run formatting
66 // =========================================================================
67 let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68 doc.add_background_image(&bg_cover, "cover_bg.png");
69
70 doc.add_paragraph(""); // spacer
71 doc.add_paragraph(""); // spacer
72 doc.add_paragraph(""); // spacer
73
74 {
75 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77 }
78 {
79 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80 p.add_run("Feature Showcase")
81 .size(28.0)
82 .color("FFFFFF")
83 .italic(true);
84 }
85
86 doc.add_paragraph(""); // spacer
87
88 {
89 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90 p.add_run("A comprehensive demonstration of every feature")
91 .size(14.0)
92 .color("CCDDFF");
93 }
94 {
95 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96 p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97 .size(14.0)
98 .color("CCDDFF");
99 }
100
101 // =========================================================================
102 // PAGE 2: TEXT FORMATTING
103 // =========================================================================
104 doc.add_paragraph("").page_break_before(true);
105
106 doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108 doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109 doc.add_paragraph("");
110
111 // --- Paragraph alignment ---
112 doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114 doc.add_paragraph("This paragraph is left-aligned (the default).")
115 .alignment(Alignment::Left);
116 doc.add_paragraph("This paragraph is center-aligned.")
117 .alignment(Alignment::Center);
118 doc.add_paragraph("This paragraph is right-aligned.")
119 .alignment(Alignment::Right);
120 doc.add_paragraph(
121 "This paragraph is justified. To demonstrate justified text properly, it needs \
122 to be long enough to span multiple lines so the word spacing adjustment is visible \
123 across the full width of the text area on the page.",
124 )
125 .alignment(Alignment::Justify);
126
127 doc.add_paragraph("");
128
129 // --- Run formatting ---
130 doc.add_paragraph("Run Formatting").style("Heading2");
131
132 {
133 let mut p = doc.add_paragraph("");
134 p.add_run("Bold text").bold(true);
135 p.add_run(" | ");
136 p.add_run("Italic text").italic(true);
137 p.add_run(" | ");
138 p.add_run("Bold + Italic").bold(true).italic(true);
139 }
140 {
141 let mut p = doc.add_paragraph("");
142 p.add_run("Single underline").underline(true);
143 p.add_run(" | ");
144 p.add_run("Strikethrough").strike(true);
145 p.add_run(" | ");
146 p.add_run("Double strikethrough").double_strike(true);
147 }
148 {
149 let mut p = doc.add_paragraph("");
150 p.add_run("Red text").color("FF0000");
151 p.add_run(" | ");
152 p.add_run("Blue text").color("0000FF");
153 p.add_run(" | ");
154 p.add_run("Green text").color("00AA00");
155 p.add_run(" | ");
156 p.add_run("Highlighted").highlight("FFFF00");
157 }
158 {
159 let mut p = doc.add_paragraph("");
160 p.add_run("8pt small").size(8.0);
161 p.add_run(" | ");
162 p.add_run("11pt normal").size(11.0);
163 p.add_run(" | ");
164 p.add_run("16pt large").size(16.0);
165 p.add_run(" | ");
166 p.add_run("24pt extra-large").size(24.0);
167 }
168 {
169 let mut p = doc.add_paragraph("");
170 p.add_run("Arial font").font("Arial");
171 p.add_run(" | ");
172 p.add_run("Times New Roman font").font("Times New Roman");
173 p.add_run(" | ");
174 p.add_run("Courier New font").font("Courier New");
175 }
176 {
177 let mut p = doc.add_paragraph("");
178 p.add_run("Normal");
179 p.add_run(" H").size(11.0);
180 p.add_run("2").subscript();
181 p.add_run("O (subscript)").size(11.0);
182 p.add_run(" | E = mc").size(11.0);
183 p.add_run("2").superscript();
184 p.add_run(" (superscript)").size(11.0);
185 }
186 {
187 let mut p = doc.add_paragraph("");
188 p.add_run("ALL CAPS").all_caps(true);
189 p.add_run(" | ");
190 p.add_run("Small Caps").small_caps(true);
191 p.add_run(" | ");
192 p.add_run("Expanded spacing")
193 .character_spacing(Length::twips(40));
194 }
195
196 doc.add_paragraph("");
197
198 // --- Paragraph formatting ---
199 doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201 doc.add_paragraph("Paragraph with shading (light green background)")
202 .shading("E2EFDA");
203
204 doc.add_paragraph("Paragraph with bottom border")
205 .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207 doc.add_paragraph("Paragraph with all borders")
208 .border_all(BorderStyle::Single, 4, "FF0000");
209
210 doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211 .indent_left(Length::inches(1.0))
212 .hanging_indent(Length::inches(0.5));
213
214 doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215 .first_line_indent(Length::inches(0.5));
216
217 doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218 .space_before(Length::pt(24.0))
219 .space_after(Length::pt(12.0));
220
221 doc.add_paragraph(
222 "Paragraph with double line spacing. This text should have extra vertical \
223 space between lines to demonstrate the line_spacing_multiple setting.",
224 )
225 .line_spacing_multiple(2.0);
226
227 doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228 .keep_with_next(true);
229 doc.add_paragraph("(This stays with the paragraph above.)");
230
231 // =========================================================================
232 // PAGE 3: LISTS & TAB STOPS
233 // =========================================================================
234 doc.add_paragraph("").page_break_before(true);
235
236 doc.add_paragraph("2. Lists").style("Heading1");
237
238 doc.add_paragraph("Bullet List").style("Heading2");
239
240 doc.add_bullet_list_item("First bullet item", 0);
241 doc.add_bullet_list_item("Second bullet item", 0);
242 doc.add_bullet_list_item("Nested level 1", 1);
243 doc.add_bullet_list_item("Nested level 2", 2);
244 doc.add_bullet_list_item("Back to level 1", 1);
245 doc.add_bullet_list_item("Third bullet item", 0);
246
247 doc.add_paragraph("");
248
249 doc.add_paragraph("Numbered List").style("Heading2");
250
251 doc.add_numbered_list_item("First numbered item", 0);
252 doc.add_numbered_list_item("Second numbered item", 0);
253 doc.add_numbered_list_item("Sub-item A", 1);
254 doc.add_numbered_list_item("Sub-item B", 1);
255 doc.add_numbered_list_item("Third numbered item", 0);
256
257 doc.add_paragraph("");
258
259 // --- Tab stops ---
260 doc.add_paragraph("Tab Stops").style("Heading2");
261
262 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268 doc.add_paragraph("Item\t........\tPrice")
269 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273 doc.add_paragraph("Widget A\t........\t$19.99")
274 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278 doc.add_paragraph("Gadget B\t________\t$249.50")
279 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280 .add_tab_stop_with_leader(
281 TabAlignment::Right,
282 Length::inches(4.0),
283 TabLeader::Underscore,
284 )
285 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287 // =========================================================================
288 // PAGE 4: TABLES
289 // =========================================================================
290 doc.add_paragraph("").page_break_before(true);
291
292 doc.add_paragraph("3. Tables").style("Heading1");
293
294 // --- Basic table with borders ---
295 doc.add_paragraph("Basic Table with Borders")
296 .style("Heading2");
297
298 {
299 let mut tbl = doc.add_table(4, 3);
300 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302 // Header row
303 for col in 0..3 {
304 tbl.cell(0, col).unwrap().shading("2E75B6");
305 }
306 tbl.cell(0, 0).unwrap().set_text("Name");
307 tbl.cell(0, 1).unwrap().set_text("Role");
308 tbl.cell(0, 2).unwrap().set_text("Location");
309
310 tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311 tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312 tbl.cell(1, 2).unwrap().set_text("New York");
313
314 tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315 tbl.cell(2, 1).unwrap().set_text("Product Manager");
316 tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318 tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319 tbl.cell(3, 1).unwrap().set_text("Designer");
320 tbl.cell(3, 2).unwrap().set_text("London");
321 }
322
323 doc.add_paragraph("");
324
325 // --- Table with cell merging ---
326 doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327 .style("Heading2");
328
329 {
330 let mut tbl = doc.add_table(4, 4);
331 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332 tbl = tbl.width_pct(100.0);
333
334 // Header spanning all columns
335 tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336 tbl.cell(0, 0).unwrap().shading("1F4E79");
337 tbl.cell(0, 0).unwrap().grid_span(4);
338
339 // Sub-header
340 tbl.cell(1, 0).unwrap().set_text("Region");
341 tbl.cell(1, 0).unwrap().shading("D6E4F0");
342 tbl.cell(1, 1).unwrap().set_text("Q1");
343 tbl.cell(1, 1).unwrap().shading("D6E4F0");
344 tbl.cell(1, 2).unwrap().set_text("Q2");
345 tbl.cell(1, 2).unwrap().shading("D6E4F0");
346 tbl.cell(1, 3).unwrap().set_text("Total");
347 tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349 // Data
350 tbl.cell(2, 0).unwrap().set_text("North America");
351 tbl.cell(2, 1).unwrap().set_text("$2.4M");
352 tbl.cell(2, 2).unwrap().set_text("$2.7M");
353 tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355 tbl.cell(3, 0).unwrap().set_text("Europe");
356 tbl.cell(3, 1).unwrap().set_text("$1.8M");
357 tbl.cell(3, 2).unwrap().set_text("$2.0M");
358 tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360 // Vertical alignment on data cells
361 tbl.cell(2, 3)
362 .unwrap()
363 .vertical_alignment(VerticalAlignment::Center);
364 tbl.cell(3, 3)
365 .unwrap()
366 .vertical_alignment(VerticalAlignment::Bottom);
367 }
368
369 doc.add_paragraph("");
370
371 // --- Table with vertical merge ---
372 doc.add_paragraph("Table with Vertical Merge")
373 .style("Heading2");
374
375 {
376 let mut tbl = doc.add_table(4, 3);
377 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379 tbl.cell(0, 0).unwrap().set_text("Category");
380 tbl.cell(0, 0).unwrap().shading("E2EFDA");
381 tbl.cell(0, 1).unwrap().set_text("Item");
382 tbl.cell(0, 1).unwrap().shading("E2EFDA");
383 tbl.cell(0, 2).unwrap().set_text("Price");
384 tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386 // "Hardware" spans rows 1-2
387 tbl.cell(1, 0).unwrap().set_text("Hardware");
388 tbl.cell(1, 0).unwrap().v_merge_restart();
389 tbl.cell(1, 1).unwrap().set_text("Laptop");
390 tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392 tbl.cell(2, 0).unwrap().v_merge_continue();
393 tbl.cell(2, 1).unwrap().set_text("Monitor");
394 tbl.cell(2, 2).unwrap().set_text("$450");
395
396 // "Software" on row 3
397 tbl.cell(3, 0).unwrap().set_text("Software");
398 tbl.cell(3, 1).unwrap().set_text("IDE License");
399 tbl.cell(3, 2).unwrap().set_text("$200/yr");
400 }
401
402 doc.add_paragraph("");
403
404 // --- Nested table ---
405 doc.add_paragraph("Nested Table").style("Heading2");
406
407 {
408 let mut tbl = doc.add_table(2, 2);
409 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411 tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412 tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413 tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415 // Nested table inside cell (1,1)
416 {
417 let mut cell = tbl.cell(1, 1).unwrap();
418 cell.set_text("Contains nested table:");
419 let mut nested = cell.add_table(2, 2);
420 nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421 nested.cell(0, 0).unwrap().set_text("Inner A");
422 nested.cell(0, 1).unwrap().set_text("Inner B");
423 nested.cell(1, 0).unwrap().set_text("Inner C");
424 nested.cell(1, 1).unwrap().set_text("Inner D");
425 }
426 }
427
428 // =========================================================================
429 // PAGE 5: IMAGES
430 // =========================================================================
431 doc.add_paragraph("").page_break_before(true);
432
433 doc.add_paragraph("4. Images").style("Heading1");
434
435 doc.add_paragraph("Inline Image").style("Heading2");
436
437 doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438 let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439 doc.add_picture(
440 &inline_img,
441 "inline_chart.png",
442 Length::inches(3.0),
443 Length::inches(0.75),
444 );
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("Header Image").style("Heading2");
449
450 // Replace the text-only header with an image header
451 let header_img = create_sample_png(400, 40, [40, 40, 40]);
452 doc.set_header_image(
453 &header_img,
454 "header_logo.png",
455 Length::inches(2.0),
456 Length::inches(0.2),
457 );
458
459 doc.add_paragraph(
460 "The document header has been replaced with an inline image. \
461 Check the header area at the top of this page.",
462 );
463
464 doc.add_paragraph("");
465 doc.add_paragraph(
466 "Note: The cover page uses a full-page background image behind the text, \
467 demonstrated on page 1 via add_background_image().",
468 );
469
470 // =========================================================================
471 // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472 // =========================================================================
473 doc.add_paragraph("").page_break_before(true);
474
475 doc.add_paragraph("5. Content Manipulation")
476 .style("Heading1");
477
478 // --- Placeholder replacement ---
479 doc.add_paragraph("Placeholder Replacement")
480 .style("Heading2");
481
482 doc.add_paragraph(
483 "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484 );
485
486 {
487 let mut p = doc.add_paragraph("");
488 p.add_run("Customer: ").bold(true);
489 p.add_run("{{customer}}");
490 }
491 {
492 let mut p = doc.add_paragraph("");
493 p.add_run("Date: ").bold(true);
494 p.add_run("{{date}}");
495 }
496 doc.add_paragraph("Reference: {{ref_number}}");
497
498 // Table with placeholders
499 {
500 let mut tbl = doc.add_table(3, 2);
501 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502 tbl.cell(0, 0).unwrap().set_text("Field");
503 tbl.cell(0, 0).unwrap().shading("D6E4F0");
504 tbl.cell(0, 1).unwrap().set_text("Value");
505 tbl.cell(0, 1).unwrap().shading("D6E4F0");
506 tbl.cell(1, 0).unwrap().set_text("Project");
507 tbl.cell(1, 1).unwrap().set_text("{{project}}");
508 tbl.cell(2, 0).unwrap().set_text("Status");
509 tbl.cell(2, 1).unwrap().set_text("{{status}}");
510 }
511
512 // Perform replacements
513 let mut replacements = HashMap::new();
514 replacements.insert("{{customer}}", "Acme Corporation");
515 replacements.insert("{{date}}", "February 22, 2026");
516 replacements.insert("{{ref_number}}", "REF-2026-001");
517 replacements.insert("{{project}}", "Infrastructure Upgrade");
518 replacements.insert("{{status}}", "In Progress");
519 let replace_count = doc.replace_all(&replacements);
520
521 doc.add_paragraph("");
522 doc.add_paragraph(&format!(
523 "(Replaced {} placeholders above — in body text and table cells)",
524 replace_count
525 ));
526
527 doc.add_paragraph("");
528
529 // --- Content insertion ---
530 doc.add_paragraph("Content Insertion").style("Heading2");
531
532 doc.add_paragraph("Section A: First section of content.");
533 doc.add_paragraph("Section C: Third section of content.");
534
535 // Insert "Section B" between A and C
536 if let Some(idx) = doc.find_content_index("Section C") {
537 doc.insert_paragraph(
538 idx,
539 "Section B: Inserted between A and C using find_content_index().",
540 );
541 }
542
543 doc.add_paragraph("");
544 doc.add_paragraph(
545 "The paragraph above ('Section B') was inserted at a specific position \
546 using find_content_index() + insert_paragraph().",
547 );
548
549 // =========================================================================
550 // PAGE 7: LANDSCAPE — section break, wide table
551 // =========================================================================
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554 doc.add_paragraph("6. Mixed Page Orientation")
555 .style("Heading1");
556
557 doc.add_paragraph(
558 "This page is in LANDSCAPE orientation. It was created using a section break \
559 followed by section_landscape(). This is useful for wide tables or charts.",
560 );
561
562 doc.add_paragraph("");
563
564 // Wide table for landscape
565 {
566 let mut tbl = doc.add_table(4, 7);
567 tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570 for (col, h) in headers.iter().enumerate() {
571 tbl.cell(0, col).unwrap().set_text(h);
572 tbl.cell(0, col).unwrap().shading("2E75B6");
573 }
574
575 let data = [
576 [
577 "North America",
578 "$1.2M",
579 "$1.3M",
580 "$1.4M",
581 "$1.5M",
582 "$1.6M",
583 "$7.0M",
584 ],
585 [
586 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587 ],
588 [
589 "Asia Pacific",
590 "$0.5M",
591 "$0.6M",
592 "$0.7M",
593 "$0.7M",
594 "$0.8M",
595 "$3.3M",
596 ],
597 ];
598 for (row_idx, row_data) in data.iter().enumerate() {
599 for (col, val) in row_data.iter().enumerate() {
600 tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601 }
602 }
603 }
604
605 // End landscape, return to portrait
606 doc.add_paragraph("")
607 .section_break(SectionBreak::NextPage)
608 .section_landscape();
609
610 // =========================================================================
611 // PAGE 8: BACK TO PORTRAIT — styles, final notes
612 // =========================================================================
613 doc.add_paragraph("7. Custom Styles & Summary")
614 .style("Heading1");
615
616 doc.add_paragraph(
617 "This final page is back in portrait orientation after a section break. \
618 The document has demonstrated:",
619 );
620
621 doc.add_bullet_list_item(
622 "Page setup: size, margins, header/footer distance, gutter",
623 0,
624 );
625 doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626 doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627 doc.add_bullet_list_item("Background images: full-page behind text", 0);
628 doc.add_bullet_list_item(
629 "Text formatting: bold, italic, underline, strike, color, size, font",
630 0,
631 );
632 doc.add_bullet_list_item(
633 "Advanced run formatting: superscript, subscript, caps, spacing",
634 0,
635 );
636 doc.add_bullet_list_item(
637 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638 0,
639 );
640 doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641 doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642 doc.add_bullet_list_item(
643 "Tables: borders, shading, column spans, row spans, nesting",
644 0,
645 );
646 doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647 doc.add_bullet_list_item("Inline images", 0);
648 doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649 doc.add_bullet_list_item("Content insertion at specific positions", 0);
650 doc.add_bullet_list_item(
651 "Section breaks with mixed portrait/landscape orientation",
652 0,
653 );
654
655 doc.add_paragraph("");
656 doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657 .alignment(Alignment::Center)
658 .shading("E2EFDA")
659 .border_all(BorderStyle::Single, 2, "00AA00");
660
661 doc.save(path).unwrap();
662}examples/generate_all_samples.rs (line 357)
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}
954
955// =============================================================================
956// 3. QUOTE / BILL OF MATERIALS — Teal + orange scheme
957// =============================================================================
958fn generate_quote(_samples_dir: &Path) -> Document {
959 let mut doc = Document::new();
960
961 // Colors: Teal #008B8B, Dark #1A3C3C, Orange #E07020, Light #F0F8F8
962 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
963 doc.set_margins(
964 Length::inches(0.75),
965 Length::inches(0.75),
966 Length::inches(0.75),
967 Length::inches(0.75),
968 );
969 doc.set_title("Quotation QT-2026-0147");
970 doc.set_author("Walter White");
971 doc.set_keywords("quote, BOM, Tensorbee");
972
973 doc.set_header("Tensorbee — Quotation");
974 doc.set_footer("QT-2026-0147 | Page");
975
976 // ── Header Block ──
977 {
978 let mut p = doc.add_paragraph("").alignment(Alignment::Left);
979 p.add_run("TENSORBEE")
980 .bold(true)
981 .size(28.0)
982 .color("008B8B")
983 .font("Helvetica");
984 }
985 doc.add_paragraph("123 Innovation Drive, Suite 400")
986 .alignment(Alignment::Left);
987 doc.add_paragraph("San Francisco, CA 94105 | +1 (415) 555-0199")
988 .alignment(Alignment::Left);
989 doc.add_paragraph("")
990 .border_bottom(BorderStyle::Single, 8, "008B8B");
991
992 // Quote meta
993 doc.add_paragraph("");
994 {
995 let mut tbl = doc.add_table(4, 4).width_pct(100.0);
996 tbl.cell(0, 0).unwrap().set_text("QUOTATION");
997 tbl.cell(0, 0).unwrap().shading("008B8B").grid_span(2);
998 tbl.cell(0, 2).unwrap().set_text("DATE");
999 tbl.cell(0, 2).unwrap().shading("008B8B");
1000 tbl.cell(0, 3).unwrap().set_text("VALID UNTIL");
1001 tbl.cell(0, 3).unwrap().shading("008B8B");
1002 tbl.cell(1, 0).unwrap().set_text("Quote #:");
1003 tbl.cell(1, 1).unwrap().set_text("QT-2026-0147");
1004 tbl.cell(1, 2).unwrap().set_text("Feb 22, 2026");
1005 tbl.cell(1, 3).unwrap().set_text("Mar 22, 2026");
1006 tbl.cell(2, 0).unwrap().set_text("Prepared by:");
1007 tbl.cell(2, 1).unwrap().set_text("Walter White");
1008 tbl.cell(2, 2).unwrap().set_text("Payment:");
1009 tbl.cell(2, 3).unwrap().set_text("Net 30");
1010 tbl.cell(3, 0).unwrap().set_text("Customer:");
1011 tbl.cell(3, 1).unwrap().set_text("Meridian Dynamics LLC");
1012 tbl.cell(3, 2).unwrap().set_text("Currency:");
1013 tbl.cell(3, 3).unwrap().set_text("USD");
1014 }
1015
1016 doc.add_paragraph("");
1017
1018 // ── Bill of Materials ──
1019 {
1020 let mut p = doc.add_paragraph("");
1021 p.add_run("Bill of Materials")
1022 .bold(true)
1023 .size(16.0)
1024 .color("1A3C3C");
1025 }
1026 doc.add_paragraph("");
1027
1028 {
1029 let mut tbl = doc
1030 .add_table(10, 6)
1031 .borders(BorderStyle::Single, 2, "008B8B")
1032 .width_pct(100.0)
1033 .layout_fixed();
1034 // Headers
1035 let hdrs = [
1036 "#",
1037 "Part Number",
1038 "Description",
1039 "Qty",
1040 "Unit Price",
1041 "Total",
1042 ];
1043 for (c, h) in hdrs.iter().enumerate() {
1044 tbl.cell(0, c).unwrap().set_text(h);
1045 tbl.cell(0, c).unwrap().shading("008B8B");
1046 }
1047
1048 let items: Vec<(&str, &str, &str, &str, &str)> = vec![
1049 (
1050 "TB-GPU-A100",
1051 "NVIDIA A100 80GB GPU",
1052 "4",
1053 "$12,500.00",
1054 "$50,000.00",
1055 ),
1056 (
1057 "TB-SRV-R750",
1058 "Dell R750xa Server Chassis",
1059 "2",
1060 "$8,200.00",
1061 "$16,400.00",
1062 ),
1063 (
1064 "TB-RAM-256G",
1065 "256GB DDR5 ECC Memory Module",
1066 "8",
1067 "$890.00",
1068 "$7,120.00",
1069 ),
1070 (
1071 "TB-SSD-3840",
1072 "3.84TB NVMe U.2 SSD",
1073 "8",
1074 "$1,150.00",
1075 "$9,200.00",
1076 ),
1077 (
1078 "TB-NET-CX7",
1079 "ConnectX-7 200GbE NIC",
1080 "4",
1081 "$1,800.00",
1082 "$7,200.00",
1083 ),
1084 (
1085 "TB-SW-48P",
1086 "48-Port 100GbE Switch",
1087 "1",
1088 "$22,000.00",
1089 "$22,000.00",
1090 ),
1091 (
1092 "TB-CAB-RACK",
1093 "42U Server Rack + PDU",
1094 "1",
1095 "$4,500.00",
1096 "$4,500.00",
1097 ),
1098 (
1099 "TB-SVC-INST",
1100 "Installation & Configuration",
1101 "1",
1102 "$8,500.00",
1103 "$8,500.00",
1104 ),
1105 (
1106 "TB-SVC-SUPP",
1107 "3-Year Premium Support",
1108 "1",
1109 "$15,000.00",
1110 "$15,000.00",
1111 ),
1112 ];
1113
1114 for (i, (pn, desc, qty, unit, total)) in items.iter().enumerate() {
1115 let row = i + 1;
1116 tbl.cell(row, 0).unwrap().set_text(&format!("{}", i + 1));
1117 tbl.cell(row, 1).unwrap().set_text(pn);
1118 tbl.cell(row, 2).unwrap().set_text(desc);
1119 tbl.cell(row, 3).unwrap().set_text(qty);
1120 tbl.cell(row, 4).unwrap().set_text(unit);
1121 tbl.cell(row, 5).unwrap().set_text(total);
1122 if i % 2 == 0 {
1123 for c in 0..6 {
1124 tbl.cell(row, c).unwrap().shading("F0F8F8");
1125 }
1126 }
1127 }
1128 }
1129
1130 doc.add_paragraph("");
1131
1132 // ── Totals ──
1133 {
1134 let mut tbl = doc
1135 .add_table(4, 2)
1136 .borders(BorderStyle::Single, 2, "008B8B")
1137 .width(Length::inches(3.5))
1138 .alignment(Alignment::Right);
1139 tbl.cell(0, 0).unwrap().set_text("Subtotal");
1140 tbl.cell(0, 1).unwrap().set_text("$139,920.00");
1141 tbl.cell(1, 0).unwrap().set_text("Shipping & Handling");
1142 tbl.cell(1, 1).unwrap().set_text("$2,500.00");
1143 tbl.cell(2, 0).unwrap().set_text("Tax (8.625%)");
1144 tbl.cell(2, 1).unwrap().set_text("$12,068.10");
1145 tbl.cell(3, 0).unwrap().set_text("TOTAL");
1146 tbl.cell(3, 0).unwrap().shading("E07020");
1147 tbl.cell(3, 1).unwrap().set_text("$154,488.10");
1148 tbl.cell(3, 1).unwrap().shading("E07020");
1149 }
1150
1151 doc.add_paragraph("");
1152
1153 // ── Terms & Conditions ──
1154 {
1155 let mut p = doc.add_paragraph("");
1156 p.add_run("Terms & Conditions")
1157 .bold(true)
1158 .size(14.0)
1159 .color("1A3C3C");
1160 }
1161 doc.add_numbered_list_item(
1162 "This quotation is valid for 30 calendar days from the date of issue.",
1163 0,
1164 );
1165 doc.add_numbered_list_item(
1166 "All prices are in USD and exclusive of applicable taxes unless stated.",
1167 0,
1168 );
1169 doc.add_numbered_list_item("Standard lead time is 4-6 weeks from PO receipt.", 0);
1170 doc.add_numbered_list_item(
1171 "Payment terms: Net 30 from invoice date. 2% discount for payment within 10 days.",
1172 0,
1173 );
1174 doc.add_numbered_list_item(
1175 "Warranty: 3-year manufacturer warranty on all hardware components.",
1176 0,
1177 );
1178 doc.add_numbered_list_item(
1179 "Returns subject to 15% restocking fee if initiated after 14 days.",
1180 0,
1181 );
1182
1183 doc.add_paragraph("");
1184 doc.add_paragraph("")
1185 .border_bottom(BorderStyle::Single, 4, "008B8B");
1186 doc.add_paragraph("");
1187 {
1188 let mut p = doc.add_paragraph("");
1189 p.add_run("Accepted by: ").bold(true);
1190 p.add_run("___________________________ Date: ___________");
1191 }
1192 {
1193 let mut p = doc.add_paragraph("");
1194 p.add_run("Print Name: ").bold(true);
1195 p.add_run("___________________________ Title: ___________");
1196 }
1197
1198 doc
1199}
1200
1201// =============================================================================
1202// 4. INVOICE — Crimson + charcoal scheme
1203// =============================================================================
1204fn generate_invoice(_samples_dir: &Path) -> Document {
1205 let mut doc = Document::new();
1206
1207 // Colors: Crimson #B22222, Charcoal #333333, Light #FAF0F0
1208 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1209 doc.set_margins(
1210 Length::inches(0.75),
1211 Length::inches(0.75),
1212 Length::inches(0.75),
1213 Length::inches(0.75),
1214 );
1215 doc.set_title("Invoice INV-2026-0392");
1216 doc.set_author("Walter White");
1217 doc.set_keywords("invoice, Tensorbee");
1218
1219 doc.set_footer("Tensorbee — Thank you for your business!");
1220
1221 // ── Company & Invoice Header ──
1222 {
1223 let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1224 // Company name left, INVOICE right
1225 {
1226 let mut cell = tbl.cell(0, 0).unwrap();
1227 let mut p = cell.add_paragraph("");
1228 p.add_run("TENSORBEE")
1229 .bold(true)
1230 .size(32.0)
1231 .color("B22222")
1232 .font("Helvetica");
1233 }
1234 {
1235 let mut cell = tbl.cell(0, 1).unwrap();
1236 let mut p = cell.add_paragraph("");
1237 p.add_run("INVOICE").bold(true).size(32.0).color("333333");
1238 }
1239 tbl.cell(1, 0)
1240 .unwrap()
1241 .set_text("123 Innovation Drive, Suite 400");
1242 tbl.cell(1, 1).unwrap().set_text("Invoice #: INV-2026-0392");
1243 tbl.cell(2, 0).unwrap().set_text("San Francisco, CA 94105");
1244 tbl.cell(2, 1).unwrap().set_text("Date: February 22, 2026");
1245 tbl.cell(3, 0).unwrap().set_text("walter@tensorbee.com");
1246 tbl.cell(3, 1).unwrap().set_text("Due Date: March 24, 2026");
1247 }
1248
1249 doc.add_paragraph("")
1250 .border_bottom(BorderStyle::Thick, 8, "B22222");
1251 doc.add_paragraph("");
1252
1253 // ── Bill To / Ship To ──
1254 {
1255 let mut tbl = doc.add_table(4, 2).width_pct(100.0);
1256 tbl.cell(0, 0).unwrap().set_text("BILL TO");
1257 tbl.cell(0, 0).unwrap().shading("B22222");
1258 tbl.cell(0, 1).unwrap().set_text("SHIP TO");
1259 tbl.cell(0, 1).unwrap().shading("B22222");
1260 tbl.cell(1, 0).unwrap().set_text("Meridian Dynamics LLC");
1261 tbl.cell(1, 1).unwrap().set_text("Meridian Dynamics LLC");
1262 tbl.cell(2, 0).unwrap().set_text("456 Enterprise Blvd");
1263 tbl.cell(2, 1).unwrap().set_text("Attn: Server Room B");
1264 tbl.cell(3, 0).unwrap().set_text("Austin, TX 78701");
1265 tbl.cell(3, 1)
1266 .unwrap()
1267 .set_text("456 Enterprise Blvd, Austin, TX 78701");
1268 }
1269
1270 doc.add_paragraph("");
1271
1272 // ── Line Items ──
1273 {
1274 let mut tbl = doc
1275 .add_table(8, 5)
1276 .borders(BorderStyle::Single, 2, "B22222")
1277 .width_pct(100.0);
1278 let hdrs = ["Description", "Qty", "Unit Price", "Tax", "Amount"];
1279 for (c, h) in hdrs.iter().enumerate() {
1280 tbl.cell(0, c).unwrap().set_text(h);
1281 tbl.cell(0, c).unwrap().shading("B22222");
1282 }
1283 let items = [
1284 (
1285 "ML Infrastructure Setup — Phase 1",
1286 "1",
1287 "$45,000.00",
1288 "$3,881.25",
1289 "$48,881.25",
1290 ),
1291 (
1292 "Data Pipeline Development (160 hrs)",
1293 "160",
1294 "$225.00",
1295 "$3,105.00",
1296 "$39,105.00",
1297 ),
1298 (
1299 "GPU Cluster Configuration",
1300 "1",
1301 "$12,000.00",
1302 "$1,035.00",
1303 "$13,035.00",
1304 ),
1305 (
1306 "API Gateway Implementation",
1307 "1",
1308 "$18,500.00",
1309 "$1,595.63",
1310 "$20,095.63",
1311 ),
1312 (
1313 "Load Testing & QA (80 hrs)",
1314 "80",
1315 "$195.00",
1316 "$1,345.50",
1317 "$16,945.50",
1318 ),
1319 (
1320 "Documentation & Training",
1321 "1",
1322 "$8,000.00",
1323 "$690.00",
1324 "$8,690.00",
1325 ),
1326 (
1327 "Project Management (3 months)",
1328 "3",
1329 "$6,500.00",
1330 "$1,679.63",
1331 "$21,179.63",
1332 ),
1333 ];
1334 for (i, (desc, qty, unit, tax, amt)) in items.iter().enumerate() {
1335 let r = i + 1;
1336 tbl.cell(r, 0).unwrap().set_text(desc);
1337 tbl.cell(r, 1).unwrap().set_text(qty);
1338 tbl.cell(r, 2).unwrap().set_text(unit);
1339 tbl.cell(r, 3).unwrap().set_text(tax);
1340 tbl.cell(r, 4).unwrap().set_text(amt);
1341 if i % 2 == 0 {
1342 for c in 0..5 {
1343 tbl.cell(r, c).unwrap().shading("FAF0F0");
1344 }
1345 }
1346 }
1347 }
1348
1349 doc.add_paragraph("");
1350
1351 // ── Totals ──
1352 {
1353 let mut tbl = doc
1354 .add_table(4, 2)
1355 .borders(BorderStyle::Single, 2, "B22222")
1356 .width(Length::inches(3.0))
1357 .alignment(Alignment::Right);
1358 tbl.cell(0, 0).unwrap().set_text("Subtotal");
1359 tbl.cell(0, 1).unwrap().set_text("$154,600.00");
1360 tbl.cell(1, 0).unwrap().set_text("Tax (8.625%)");
1361 tbl.cell(1, 1).unwrap().set_text("$13,334.25");
1362 tbl.cell(2, 0).unwrap().set_text("Discount (5%)");
1363 tbl.cell(2, 1).unwrap().set_text("-$7,730.00");
1364 tbl.cell(3, 0).unwrap().set_text("AMOUNT DUE");
1365 tbl.cell(3, 0).unwrap().shading("B22222");
1366 tbl.cell(3, 1).unwrap().set_text("$160,204.25");
1367 tbl.cell(3, 1).unwrap().shading("B22222");
1368 }
1369
1370 doc.add_paragraph("");
1371 doc.add_paragraph("");
1372
1373 // ── Payment Details ──
1374 {
1375 let mut p = doc.add_paragraph("");
1376 p.add_run("Payment Details")
1377 .bold(true)
1378 .size(14.0)
1379 .color("333333");
1380 }
1381 doc.add_paragraph("Bank: Silicon Valley Bank")
1382 .indent_left(Length::inches(0.3));
1383 doc.add_paragraph("Account: Tensorbee Inc. — 0847-2953-1120")
1384 .indent_left(Length::inches(0.3));
1385 doc.add_paragraph("Routing: 121140399")
1386 .indent_left(Length::inches(0.3));
1387 doc.add_paragraph("Swift: SVBKUS6S")
1388 .indent_left(Length::inches(0.3));
1389
1390 doc.add_paragraph("");
1391
1392 doc.add_paragraph("Please include invoice number INV-2026-0392 in the payment reference.")
1393 .shading("FAF0F0")
1394 .border_all(BorderStyle::Single, 2, "B22222");
1395
1396 doc
1397}
1398
1399// =============================================================================
1400// 5. REPORT — Forest green + earth tones, with images & hierarchical sections
1401// =============================================================================
1402fn generate_report(_samples_dir: &Path) -> Document {
1403 let mut doc = Document::new();
1404
1405 // Colors: Forest #2D5016, Sage #6B8E23, Earth #8B7355, Cream #FFFAF0
1406 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1407 doc.set_margins(
1408 Length::inches(1.0),
1409 Length::inches(1.0),
1410 Length::inches(1.0),
1411 Length::inches(1.0),
1412 );
1413 doc.set_title("Q4 2025 Environmental Impact Report");
1414 doc.set_author("Walter White");
1415 doc.set_subject("Quarterly Environmental Report");
1416 doc.set_keywords("environment, sustainability, report, Tensorbee");
1417
1418 doc.set_different_first_page(true);
1419 doc.set_first_page_header("");
1420 doc.set_header("Tensorbee — Q4 2025 Environmental Impact Report");
1421 doc.set_footer("CONFIDENTIAL — Page");
1422
1423 // ── Cover ──
1424 let cover_bg = create_sample_png(612, 792, [20, 50, 15]);
1425 doc.add_background_image(&cover_bg, "report_cover.png");
1426
1427 for _ in 0..5 {
1428 doc.add_paragraph("");
1429 }
1430 {
1431 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1432 p.add_run("Q4 2025")
1433 .bold(true)
1434 .size(48.0)
1435 .color("FFFFFF")
1436 .font("Georgia");
1437 }
1438 {
1439 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1440 p.add_run("Environmental Impact Report")
1441 .size(24.0)
1442 .color("90EE90")
1443 .font("Georgia")
1444 .italic(true);
1445 }
1446 doc.add_paragraph("");
1447 {
1448 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1449 p.add_run("Tensorbee — Sustainability Division")
1450 .size(14.0)
1451 .color("C0C0C0");
1452 }
1453 {
1454 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1455 p.add_run("Prepared by Walter White, Chief Sustainability Officer")
1456 .size(11.0)
1457 .color("AAAAAA");
1458 }
1459
1460 // ── TOC ──
1461 doc.add_paragraph("").page_break_before(true);
1462 doc.insert_toc(doc.content_count(), 3);
1463
1464 // ── Section 1: Executive Overview ──
1465 doc.add_paragraph("").page_break_before(true);
1466 doc.add_paragraph("Executive Overview").style("Heading1");
1467 doc.add_paragraph(
1468 "This report presents Tensorbee's environmental performance for Q4 2025. Our \
1469 sustainability initiatives have yielded a 34% reduction in carbon emissions compared \
1470 to Q4 2024, exceeding our target of 25%. Key achievements include the transition to \
1471 100% renewable energy in our primary data centers and the launch of our carbon offset \
1472 marketplace.",
1473 )
1474 .first_line_indent(Length::inches(0.3));
1475
1476 // Image: performance chart
1477 let chart_img = create_chart_png(400, 200);
1478 doc.add_paragraph("");
1479 doc.add_picture(
1480 &chart_img,
1481 "performance_chart.png",
1482 Length::inches(5.0),
1483 Length::inches(2.5),
1484 )
1485 .alignment(Alignment::Center);
1486 {
1487 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1488 p.add_run("Figure 1: Quarterly Carbon Emissions (tonnes CO2e)")
1489 .italic(true)
1490 .size(9.0)
1491 .color("666666");
1492 }
1493
1494 // ── Section 2: Energy Consumption ──
1495 doc.add_paragraph("").page_break_before(true);
1496 doc.add_paragraph("Energy Consumption").style("Heading1");
1497
1498 doc.add_paragraph("Data Center Operations")
1499 .style("Heading2");
1500 doc.add_paragraph(
1501 "Our three primary data centers consumed a combined 4.2 GWh in Q4 2025, \
1502 a 12% reduction from Q3 2025 driven by improved cooling efficiency and \
1503 server consolidation.",
1504 );
1505
1506 // Data table
1507 {
1508 let mut tbl = doc
1509 .add_table(5, 4)
1510 .borders(BorderStyle::Single, 4, "2D5016")
1511 .width_pct(100.0);
1512 let hdrs = ["Data Center", "Capacity (MW)", "Usage (GWh)", "PUE"];
1513 for (c, h) in hdrs.iter().enumerate() {
1514 tbl.cell(0, c).unwrap().set_text(h);
1515 tbl.cell(0, c).unwrap().shading("2D5016");
1516 }
1517 let rows = [
1518 ("San Francisco (Primary)", "3.2", "1.8", "1.12"),
1519 ("Dublin (EU)", "2.1", "1.2", "1.18"),
1520 ("Singapore (APAC)", "1.8", "1.2", "1.24"),
1521 ("TOTAL", "7.1", "4.2", "1.17 avg"),
1522 ];
1523 for (i, (dc, cap, use_, pue)) in rows.iter().enumerate() {
1524 tbl.cell(i + 1, 0).unwrap().set_text(dc);
1525 tbl.cell(i + 1, 1).unwrap().set_text(cap);
1526 tbl.cell(i + 1, 2).unwrap().set_text(use_);
1527 tbl.cell(i + 1, 3).unwrap().set_text(pue);
1528 if i == 3 {
1529 for c in 0..4 {
1530 tbl.cell(i + 1, c).unwrap().shading("E2EFDA");
1531 }
1532 }
1533 }
1534 }
1535
1536 doc.add_paragraph("");
1537
1538 doc.add_paragraph("Renewable Energy Mix").style("Heading2");
1539 doc.add_paragraph("Breakdown of energy sources across all facilities:");
1540
1541 doc.add_bullet_list_item("Solar PV: 42% (1.76 GWh)", 0);
1542 doc.add_bullet_list_item("Wind Power Purchase Agreements: 38% (1.60 GWh)", 0);
1543 doc.add_bullet_list_item("Hydroelectric: 12% (0.50 GWh)", 0);
1544 doc.add_bullet_list_item("Grid (non-renewable): 8% (0.34 GWh)", 0);
1545
1546 // ── Section 3: Water & Waste ──
1547 doc.add_paragraph("Water & Waste Management")
1548 .style("Heading1");
1549
1550 doc.add_paragraph("Water Usage").style("Heading2");
1551 doc.add_paragraph(
1552 "Total water consumption was 12.4 million gallons, a 15% reduction from Q3. \
1553 Our closed-loop cooling systems now recycle 78% of water used in cooling operations.",
1554 );
1555
1556 doc.add_paragraph("Waste Reduction").style("Heading2");
1557 doc.add_paragraph("E-waste management results for Q4:")
1558 .keep_with_next(true);
1559 {
1560 let mut tbl = doc
1561 .add_table(4, 3)
1562 .borders(BorderStyle::Single, 2, "6B8E23")
1563 .width_pct(80.0);
1564 tbl.cell(0, 0).unwrap().set_text("Category");
1565 tbl.cell(0, 0).unwrap().shading("6B8E23");
1566 tbl.cell(0, 1).unwrap().set_text("Weight (kg)");
1567 tbl.cell(0, 1).unwrap().shading("6B8E23");
1568 tbl.cell(0, 2).unwrap().set_text("Recycled %");
1569 tbl.cell(0, 2).unwrap().shading("6B8E23");
1570 let rows = [
1571 ("Server Hardware", "2,450", "94%"),
1572 ("Networking Equipment", "820", "91%"),
1573 ("Storage Media", "340", "99%"),
1574 ];
1575 for (i, (cat, wt, pct)) in rows.iter().enumerate() {
1576 tbl.cell(i + 1, 0).unwrap().set_text(cat);
1577 tbl.cell(i + 1, 1).unwrap().set_text(wt);
1578 tbl.cell(i + 1, 2).unwrap().set_text(pct);
1579 }
1580 }
1581
1582 // ── Section 4: Initiatives ──
1583 doc.add_paragraph("").page_break_before(true);
1584 doc.add_paragraph("Q1 2026 Initiatives").style("Heading1");
1585
1586 doc.add_paragraph("Planned Programs").style("Heading2");
1587 doc.add_numbered_list_item(
1588 "Deploy on-site battery storage at SF data center (2 MWh capacity)",
1589 0,
1590 );
1591 doc.add_numbered_list_item("Pilot immersion cooling in Dublin facility", 0);
1592 doc.add_numbered_list_item("Launch employee commute carbon offset program", 0);
1593 doc.add_numbered_list_item("Achieve ISO 14001 certification for Singapore facility", 0);
1594
1595 doc.add_paragraph("Investment Targets").style("Heading2");
1596 doc.add_paragraph("Sustainability CapEx allocation for FY2026:")
1597 .keep_with_next(true);
1598 {
1599 let mut tbl = doc
1600 .add_table(5, 2)
1601 .borders(BorderStyle::Single, 4, "2D5016");
1602 tbl.cell(0, 0).unwrap().set_text("Initiative");
1603 tbl.cell(0, 0).unwrap().shading("2D5016");
1604 tbl.cell(0, 1).unwrap().set_text("Budget");
1605 tbl.cell(0, 1).unwrap().shading("2D5016");
1606 let rows = [
1607 ("Battery Storage", "$1.2M"),
1608 ("Immersion Cooling Pilot", "$800K"),
1609 ("Solar Panel Expansion", "$2.1M"),
1610 ("Carbon Credits", "$500K"),
1611 ];
1612 for (i, (init, budget)) in rows.iter().enumerate() {
1613 tbl.cell(i + 1, 0).unwrap().set_text(init);
1614 tbl.cell(i + 1, 1).unwrap().set_text(budget);
1615 if i % 2 == 0 {
1616 tbl.cell(i + 1, 0).unwrap().shading("FFFAF0");
1617 tbl.cell(i + 1, 1).unwrap().shading("FFFAF0");
1618 }
1619 }
1620 }
1621
1622 // Image: sustainability roadmap
1623 let roadmap_img = create_sample_png(500, 100, [40, 80, 30]);
1624 doc.add_paragraph("");
1625 doc.add_picture(
1626 &roadmap_img,
1627 "roadmap.png",
1628 Length::inches(6.0),
1629 Length::inches(1.2),
1630 )
1631 .alignment(Alignment::Center);
1632 {
1633 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1634 p.add_run("Figure 2: 2026 Sustainability Roadmap")
1635 .italic(true)
1636 .size(9.0)
1637 .color("666666");
1638 }
1639
1640 doc.add_paragraph("");
1641 doc.add_paragraph(
1642 "For questions about this report, contact Walter White at sustainability@tensorbee.com.",
1643 )
1644 .shading("FFFAF0")
1645 .border_all(BorderStyle::Single, 2, "2D5016");
1646
1647 doc
1648}
1649
1650// =============================================================================
1651// 6. LETTER — Slate blue + silver scheme
1652// =============================================================================
1653fn generate_letter(_samples_dir: &Path) -> Document {
1654 let mut doc = Document::new();
1655
1656 // Colors: Slate #4A5568, Blue accent #3182CE
1657 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1658 doc.set_margins(
1659 Length::inches(1.25),
1660 Length::inches(1.25),
1661 Length::inches(1.25),
1662 Length::inches(1.25),
1663 );
1664 doc.set_title("Business Letter");
1665 doc.set_author("Walter White");
1666
1667 // Banner header using raw XML
1668 let logo_img = create_logo_png(220, 48);
1669 let banner = build_header_banner_xml(
1670 "rId1",
1671 &BannerOpts {
1672 bg_color: "4A5568",
1673 banner_width: 7772400,
1674 banner_height: 731520, // ~0.8"
1675 logo_width: 2011680,
1676 logo_height: 438912,
1677 logo_x_offset: 295125,
1678 logo_y_offset: 146304,
1679 },
1680 );
1681 doc.set_raw_header_with_images(
1682 banner,
1683 &[("rId1", &logo_img, "logo.png")],
1684 rdocx_oxml::header_footer::HdrFtrType::Default,
1685 );
1686 doc.set_margins(
1687 Length::twips(2000),
1688 Length::twips(1800),
1689 Length::twips(1440),
1690 Length::twips(1800),
1691 );
1692 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
1693
1694 // Footer with contact
1695 doc.set_footer(
1696 "Tensorbee | 123 Innovation Drive, Suite 400 | San Francisco, CA 94105 | tensorbee.com",
1697 );
1698
1699 // ── Sender Address ──
1700 doc.add_paragraph("Walter White");
1701 doc.add_paragraph("Chief Executive Officer");
1702 doc.add_paragraph("Tensorbee");
1703 doc.add_paragraph("123 Innovation Drive, Suite 400");
1704 doc.add_paragraph("San Francisco, CA 94105");
1705 doc.add_paragraph("");
1706 doc.add_paragraph("February 22, 2026");
1707
1708 doc.add_paragraph("");
1709
1710 // ── Recipient ──
1711 doc.add_paragraph("Ms. Sarah Chen");
1712 doc.add_paragraph("VP of Engineering");
1713 doc.add_paragraph("Meridian Dynamics LLC");
1714 doc.add_paragraph("456 Enterprise Boulevard");
1715 doc.add_paragraph("Austin, TX 78701");
1716
1717 doc.add_paragraph("");
1718
1719 // ── Subject ──
1720 {
1721 let mut p = doc.add_paragraph("");
1722 p.add_run("Re: Strategic Technology Partnership — Phase 2 Expansion")
1723 .bold(true);
1724 }
1725
1726 doc.add_paragraph("");
1727
1728 // ── Body ──
1729 doc.add_paragraph("Dear Ms. Chen,");
1730 doc.add_paragraph("");
1731
1732 doc.add_paragraph(
1733 "I am writing to express Tensorbee's enthusiasm for expanding our strategic technology \
1734 partnership with Meridian Dynamics. Following the successful completion of Phase 1, \
1735 which delivered a 40% improvement in your ML inference pipeline throughput, we believe \
1736 the foundation is firmly established for an ambitious Phase 2 engagement.",
1737 )
1738 .first_line_indent(Length::inches(0.5));
1739
1740 doc.add_paragraph(
1741 "During our recent executive review, your team highlighted three priority areas for \
1742 the next phase of collaboration:",
1743 )
1744 .first_line_indent(Length::inches(0.5));
1745
1746 doc.add_numbered_list_item(
1747 "Real-time anomaly detection for your financial transaction monitoring system, \
1748 targeting sub-100ms latency at 50,000 transactions per second.",
1749 0,
1750 );
1751 doc.add_numbered_list_item(
1752 "Federated learning infrastructure to enable model training across your distributed \
1753 data centers without centralizing sensitive financial data.",
1754 0,
1755 );
1756 doc.add_numbered_list_item(
1757 "MLOps automation to reduce your model deployment cycle from the current 5 days \
1758 to under 4 hours.",
1759 0,
1760 );
1761
1762 doc.add_paragraph(
1763 "Our engineering team has prepared a detailed technical proposal addressing each of \
1764 these areas. We have allocated a dedicated team of eight senior engineers, led by \
1765 our CTO, to ensure continuity with the Phase 1 team your organization has already \
1766 built a productive working relationship with.",
1767 )
1768 .first_line_indent(Length::inches(0.5));
1769
1770 doc.add_paragraph(
1771 "I would welcome the opportunity to present our Phase 2 proposal in person. My \
1772 assistant will reach out to coordinate a meeting at your convenience during the \
1773 first week of March.",
1774 )
1775 .first_line_indent(Length::inches(0.5));
1776
1777 doc.add_paragraph("");
1778
1779 doc.add_paragraph("Warm regards,");
1780 doc.add_paragraph("");
1781 doc.add_paragraph("");
1782
1783 // Signature line
1784 doc.add_paragraph("")
1785 .border_bottom(BorderStyle::Single, 4, "4A5568");
1786 {
1787 let mut p = doc.add_paragraph("");
1788 p.add_run("Walter White").bold(true).size(12.0);
1789 }
1790 {
1791 let mut p = doc.add_paragraph("");
1792 p.add_run("Chief Executive Officer, Tensorbee")
1793 .italic(true)
1794 .size(10.0)
1795 .color("666666");
1796 }
1797 {
1798 let mut p = doc.add_paragraph("");
1799 p.add_run("walter@tensorbee.com | +1 (415) 555-0199")
1800 .size(10.0)
1801 .color("888888");
1802 }
1803
1804 doc
1805}
1806
1807// =============================================================================
1808// 7. EMPLOYMENT CONTRACT — Purple + charcoal scheme
1809// =============================================================================
1810fn generate_contract(_samples_dir: &Path) -> Document {
1811 let mut doc = Document::new();
1812
1813 // Colors: Purple #5B2C6F, Plum #8E44AD, Gray #2C3E50
1814 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
1815 doc.set_margins(
1816 Length::inches(1.25),
1817 Length::inches(1.0),
1818 Length::inches(1.0),
1819 Length::inches(1.0),
1820 );
1821 doc.set_title("Employment Agreement");
1822 doc.set_author("Tensorbee HR Department");
1823 doc.set_subject("Employment Contract — Walter White");
1824 doc.set_keywords("employment, contract, agreement, Tensorbee");
1825
1826 doc.set_header("TENSORBEE — Employment Agreement");
1827 doc.set_footer("Employment Agreement — Walter White — Page");
1828
1829 // ── Title Block ──
1830 doc.add_paragraph("")
1831 .border_bottom(BorderStyle::Thick, 12, "5B2C6F");
1832 {
1833 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
1834 p.add_run("EMPLOYMENT AGREEMENT")
1835 .bold(true)
1836 .size(24.0)
1837 .color("5B2C6F")
1838 .font("Georgia");
1839 }
1840 doc.add_paragraph("")
1841 .border_bottom(BorderStyle::Thick, 12, "5B2C6F");
1842 doc.add_paragraph("");
1843
1844 // ── Parties ──
1845 doc.add_paragraph(
1846 "This Employment Agreement (\"Agreement\") is entered into as of February 22, 2026 \
1847 (\"Effective Date\"), by and between:",
1848 );
1849
1850 doc.add_paragraph("");
1851
1852 {
1853 let mut p = doc.add_paragraph("").indent_left(Length::inches(0.5));
1854 p.add_run("EMPLOYER: ").bold(true);
1855 p.add_run(
1856 "Tensorbee, Inc., a Delaware corporation with its principal place of business at \
1857 123 Innovation Drive, Suite 400, San Francisco, CA 94105 (\"Company\")",
1858 );
1859 }
1860 doc.add_paragraph("");
1861 {
1862 let mut p = doc.add_paragraph("").indent_left(Length::inches(0.5));
1863 p.add_run("EMPLOYEE: ").bold(true);
1864 p.add_run(
1865 "Walter White, residing at 308 Negra Arroyo Lane, Albuquerque, NM 87104 (\"Employee\")",
1866 );
1867 }
1868
1869 doc.add_paragraph("");
1870 doc.add_paragraph("The Company and Employee are collectively referred to as the \"Parties.\"");
1871 doc.add_paragraph("");
1872
1873 // ── Article 1: Position and Duties ──
1874 doc.add_paragraph("Article 1 — Position and Duties")
1875 .style("Heading1");
1876 {
1877 let mut p = doc.add_paragraph("");
1878 p.add_run("1.1 ").bold(true);
1879 p.add_run("The Company hereby employs the Employee as ");
1880 p.add_run("Chief Executive Officer (CEO)")
1881 .bold(true)
1882 .italic(true);
1883 p.add_run(", reporting directly to the Board of Directors.");
1884 }
1885 {
1886 let mut p = doc.add_paragraph("");
1887 p.add_run("1.2 ").bold(true);
1888 p.add_run("The Employee shall devote full working time, attention, and best efforts to \
1889 the performance of duties as reasonably assigned by the Board, including but not limited to:");
1890 }
1891 doc.add_bullet_list_item(
1892 "Setting and executing the Company's strategic vision and business plan",
1893 0,
1894 );
1895 doc.add_bullet_list_item(
1896 "Overseeing all operations, engineering, and commercial activities",
1897 0,
1898 );
1899 doc.add_bullet_list_item(
1900 "Representing the Company to investors, customers, and the public",
1901 0,
1902 );
1903 doc.add_bullet_list_item("Recruiting, developing, and retaining key talent", 0);
1904
1905 {
1906 let mut p = doc.add_paragraph("");
1907 p.add_run("1.3 ").bold(true);
1908 p.add_run(
1909 "The Employee's primary work location shall be the Company's San Francisco \
1910 headquarters, with reasonable travel as required by business needs.",
1911 );
1912 }
1913
1914 // ── Article 2: Compensation ──
1915 doc.add_paragraph("Article 2 — Compensation and Benefits")
1916 .style("Heading1");
1917 {
1918 let mut p = doc.add_paragraph("");
1919 p.add_run("2.1 Base Salary. ").bold(true);
1920 p.add_run("The Company shall pay the Employee an annual base salary of ");
1921 p.add_run("$375,000.00").bold(true);
1922 p.add_run(" (Three Hundred Seventy-Five Thousand Dollars), payable in accordance with the \
1923 Company's standard payroll schedule, less applicable withholdings and deductions.");
1924 }
1925 {
1926 let mut p = doc.add_paragraph("");
1927 p.add_run("2.2 Annual Bonus. ").bold(true);
1928 p.add_run("The Employee shall be eligible for an annual performance bonus of up to ");
1929 p.add_run("40%").bold(true);
1930 p.add_run(
1931 " of base salary, based on achievement of mutually agreed performance objectives.",
1932 );
1933 }
1934 {
1935 let mut p = doc.add_paragraph("");
1936 p.add_run("2.3 Equity. ").bold(true);
1937 p.add_run("Subject to Board approval, the Employee shall receive a stock option grant of ");
1938 p.add_run("500,000 shares").bold(true);
1939 p.add_run(
1940 " of the Company's common stock, vesting over four (4) years with a one-year cliff, \
1941 at an exercise price equal to the fair market value on the date of grant.",
1942 );
1943 }
1944
1945 // Compensation summary table
1946 doc.add_paragraph("");
1947 {
1948 let mut tbl = doc
1949 .add_table(5, 2)
1950 .borders(BorderStyle::Single, 4, "5B2C6F")
1951 .width_pct(70.0)
1952 .alignment(Alignment::Center);
1953 tbl.cell(0, 0).unwrap().set_text("Compensation Element");
1954 tbl.cell(0, 0).unwrap().shading("5B2C6F");
1955 tbl.cell(0, 1).unwrap().set_text("Value");
1956 tbl.cell(0, 1).unwrap().shading("5B2C6F");
1957 let items = [
1958 ("Base Salary", "$375,000/year"),
1959 ("Target Bonus", "Up to 40% ($150,000)"),
1960 ("Equity Grant", "500,000 shares (4yr vest)"),
1961 ("Total Target Comp", "$525,000 + equity"),
1962 ];
1963 for (i, (elem, val)) in items.iter().enumerate() {
1964 tbl.cell(i + 1, 0).unwrap().set_text(elem);
1965 tbl.cell(i + 1, 1).unwrap().set_text(val);
1966 if i == 3 {
1967 tbl.cell(i + 1, 0).unwrap().shading("E8DAEF");
1968 tbl.cell(i + 1, 1).unwrap().shading("E8DAEF");
1969 }
1970 }
1971 }
1972
1973 // ── Article 3: Benefits ──
1974 doc.add_paragraph("").page_break_before(true);
1975 doc.add_paragraph("Article 3 — Benefits").style("Heading1");
1976 {
1977 let mut p = doc.add_paragraph("");
1978 p.add_run("3.1 ").bold(true);
1979 p.add_run(
1980 "The Employee shall be entitled to participate in all benefit programs \
1981 generally available to senior executives, including:",
1982 );
1983 }
1984 doc.add_bullet_list_item(
1985 "Medical, dental, and vision insurance (100% premium coverage for employee and dependents)",
1986 0,
1987 );
1988 doc.add_bullet_list_item("401(k) retirement plan with 6% company match", 0);
1989 doc.add_bullet_list_item("Life insurance and long-term disability coverage", 0);
1990 doc.add_bullet_list_item("Annual professional development allowance of $10,000", 0);
1991
1992 {
1993 let mut p = doc.add_paragraph("");
1994 p.add_run("3.2 Paid Time Off. ").bold(true);
1995 p.add_run(
1996 "The Employee shall receive 25 days of paid vacation per year, plus Company holidays, \
1997 accruing on a monthly basis.",
1998 );
1999 }
2000
2001 // ── Article 4: Term and Termination ──
2002 doc.add_paragraph("Article 4 — Term and Termination")
2003 .style("Heading1");
2004 {
2005 let mut p = doc.add_paragraph("");
2006 p.add_run("4.1 At-Will Employment. ").bold(true);
2007 p.add_run(
2008 "This Agreement is for at-will employment and may be terminated by either Party \
2009 at any time, with or without cause, subject to the notice provisions herein.",
2010 );
2011 }
2012 {
2013 let mut p = doc.add_paragraph("");
2014 p.add_run("4.2 Notice Period. ").bold(true);
2015 p.add_run("Either Party shall provide ");
2016 p.add_run("ninety (90) days").bold(true);
2017 p.add_run(" written notice of termination, or pay in lieu thereof.");
2018 }
2019 {
2020 let mut p = doc.add_paragraph("");
2021 p.add_run("4.3 Severance. ").bold(true);
2022 p.add_run("In the event of termination by the Company without Cause, the Employee shall \
2023 receive (a) twelve (12) months of base salary continuation, (b) pro-rata bonus \
2024 for the year of termination, and (c) twelve (12) months of COBRA premium coverage.");
2025 }
2026
2027 // ── Article 5: Confidentiality ──
2028 doc.add_paragraph("Article 5 — Confidentiality and IP")
2029 .style("Heading1");
2030 {
2031 let mut p = doc.add_paragraph("");
2032 p.add_run("5.1 ").bold(true);
2033 p.add_run(
2034 "The Employee agrees to maintain strict confidentiality of all proprietary \
2035 information, trade secrets, and business plans of the Company during and after \
2036 employment.",
2037 );
2038 }
2039 {
2040 let mut p = doc.add_paragraph("");
2041 p.add_run("5.2 ").bold(true);
2042 p.add_run(
2043 "All intellectual property, inventions, and works of authorship created by the \
2044 Employee during the term of employment and within the scope of duties shall be \
2045 the sole and exclusive property of the Company.",
2046 );
2047 }
2048
2049 // ── Article 6: Non-Compete ──
2050 doc.add_paragraph("Article 6 — Non-Competition")
2051 .style("Heading1");
2052 {
2053 let mut p = doc.add_paragraph("");
2054 p.add_run("6.1 ").bold(true);
2055 p.add_run("For a period of twelve (12) months following termination, the Employee agrees \
2056 not to engage in any business that directly competes with the Company's core \
2057 business of AI infrastructure and ML operations services, within the United States.");
2058 }
2059
2060 // ── Article 7: General Provisions ──
2061 doc.add_paragraph("Article 7 — General Provisions")
2062 .style("Heading1");
2063 {
2064 let mut p = doc.add_paragraph("");
2065 p.add_run("7.1 Governing Law. ").bold(true);
2066 p.add_run(
2067 "This Agreement shall be governed by and construed in accordance with the laws of \
2068 the State of California.",
2069 );
2070 }
2071 {
2072 let mut p = doc.add_paragraph("");
2073 p.add_run("7.2 Entire Agreement. ").bold(true);
2074 p.add_run(
2075 "This Agreement constitutes the entire agreement between the Parties and supersedes \
2076 all prior negotiations, representations, and agreements.",
2077 );
2078 }
2079 {
2080 let mut p = doc.add_paragraph("");
2081 p.add_run("7.3 Amendment. ").bold(true);
2082 p.add_run(
2083 "This Agreement may only be amended by a written instrument signed by both Parties.",
2084 );
2085 }
2086
2087 // ── Signature Block ──
2088 doc.add_paragraph("").page_break_before(true);
2089 doc.add_paragraph("IN WITNESS WHEREOF, the Parties have executed this Employment Agreement as of the Effective Date.")
2090 .space_after(Length::pt(24.0));
2091
2092 // Signature table
2093 {
2094 let mut tbl = doc.add_table(6, 2).width_pct(100.0);
2095 tbl.cell(0, 0).unwrap().set_text("FOR THE COMPANY:");
2096 tbl.cell(0, 0).unwrap().shading("5B2C6F");
2097 tbl.cell(0, 1).unwrap().set_text("EMPLOYEE:");
2098 tbl.cell(0, 1).unwrap().shading("5B2C6F");
2099
2100 tbl.cell(1, 0).unwrap().set_text("");
2101 tbl.cell(1, 1).unwrap().set_text("");
2102 tbl.row(1).unwrap().height(Length::pt(40.0));
2103
2104 tbl.cell(2, 0)
2105 .unwrap()
2106 .set_text("Signature: ___________________________");
2107 tbl.cell(2, 1)
2108 .unwrap()
2109 .set_text("Signature: ___________________________");
2110 tbl.cell(3, 0)
2111 .unwrap()
2112 .set_text("Name: Board Representative");
2113 tbl.cell(3, 1).unwrap().set_text("Name: Walter White");
2114 tbl.cell(4, 0)
2115 .unwrap()
2116 .set_text("Title: Chair, Board of Directors");
2117 tbl.cell(4, 1)
2118 .unwrap()
2119 .set_text("Title: Chief Executive Officer");
2120 tbl.cell(5, 0).unwrap().set_text("Date: _______________");
2121 tbl.cell(5, 1).unwrap().set_text("Date: _______________");
2122 }
2123
2124 doc
2125}Sourcepub fn vertical_alignment(self, align: VerticalAlignment) -> Self
pub fn vertical_alignment(self, align: VerticalAlignment) -> Self
Set vertical alignment within the cell.
Examples found in repository?
examples/styled_tables.rs (line 177)
24fn generate_styled_tables(path: &Path) {
25 let mut doc = Document::new();
26 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27 doc.set_margins(
28 Length::inches(0.75),
29 Length::inches(0.75),
30 Length::inches(0.75),
31 Length::inches(0.75),
32 );
33
34 doc.add_paragraph("Styled Tables Showcase")
35 .style("Heading1");
36
37 doc.add_paragraph("");
38
39 // =========================================================================
40 // 1. Professional report table with alternating rows
41 // =========================================================================
42 doc.add_paragraph("1. Report Table with Alternating Row Colors")
43 .style("Heading2");
44
45 {
46 let mut tbl = doc.add_table(8, 4);
47 tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48 tbl = tbl.width_pct(100.0);
49
50 // Header row
51 let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52 for (col, h) in headers.iter().enumerate() {
53 tbl.cell(0, col).unwrap().shading("2E75B6");
54 tbl.cell(0, col).unwrap().set_text(h);
55 }
56 tbl.row(0).unwrap().header();
57
58 // Data with alternating shading
59 let data = [
60 ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61 ["Professional", "$189,000", "$201,000", "+6.3%"],
62 ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63 ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64 ["Training", "$22,000", "$28,900", "+31.4%"],
65 ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66 ];
67
68 for (i, row) in data.iter().enumerate() {
69 let row_idx = i + 1;
70 for (col, val) in row.iter().enumerate() {
71 tbl.cell(row_idx, col).unwrap().set_text(val);
72 // Alternate row colors
73 if i % 2 == 0 {
74 tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75 }
76 }
77 }
78
79 // Total row
80 tbl.cell(7, 0).unwrap().set_text("TOTAL");
81 tbl.cell(7, 0).unwrap().shading("D6E4F0");
82 tbl.cell(7, 1).unwrap().set_text("$613,000");
83 tbl.cell(7, 1).unwrap().shading("D6E4F0");
84 tbl.cell(7, 2).unwrap().set_text("$729,900");
85 tbl.cell(7, 2).unwrap().shading("D6E4F0");
86 tbl.cell(7, 3).unwrap().set_text("+19.1%");
87 tbl.cell(7, 3).unwrap().shading("D6E4F0");
88 }
89
90 doc.add_paragraph("");
91
92 // =========================================================================
93 // 2. Invoice-style table with merged header
94 // =========================================================================
95 doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96 .style("Heading2");
97
98 {
99 let mut tbl = doc.add_table(7, 4);
100 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101 tbl = tbl.width_pct(100.0);
102
103 // Merged title row
104 tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105 tbl.cell(0, 0).unwrap().grid_span(4);
106 tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108 // Column headers
109 let headers = ["Item", "Description", "Qty", "Amount"];
110 for (col, h) in headers.iter().enumerate() {
111 tbl.cell(1, col).unwrap().set_text(h);
112 tbl.cell(1, col).unwrap().shading("D6E4F0");
113 }
114
115 // Line items
116 tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117 tbl.cell(2, 1)
118 .unwrap()
119 .set_text("Enterprise License (500 seats)");
120 tbl.cell(2, 2).unwrap().set_text("1");
121 tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123 tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124 tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125 tbl.cell(3, 2).unwrap().set_text("1");
126 tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128 tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129 tbl.cell(4, 1)
130 .unwrap()
131 .set_text("On-site Training (3 days)");
132 tbl.cell(4, 2).unwrap().set_text("1");
133 tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135 // Subtotal
136 tbl.cell(5, 0).unwrap().set_text("Subtotal");
137 tbl.cell(5, 0).unwrap().grid_span(3);
138 tbl.cell(5, 0).unwrap().shading("F2F2F2");
139 tbl.cell(5, 3).unwrap().set_text("$89,500");
140 tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142 // Total
143 tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144 tbl.cell(6, 0).unwrap().grid_span(3);
145 tbl.cell(6, 0).unwrap().shading("1F4E79");
146 tbl.cell(6, 3).unwrap().set_text("$89,500");
147 tbl.cell(6, 3).unwrap().shading("1F4E79");
148 }
149
150 doc.add_paragraph("");
151
152 // =========================================================================
153 // 3. Specification table with vertical merge
154 // =========================================================================
155 doc.add_paragraph("3. Specification Table with Vertical Merges")
156 .style("Heading2");
157
158 {
159 let mut tbl = doc.add_table(8, 3);
160 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161 tbl = tbl.width_pct(100.0);
162
163 // Header
164 tbl.cell(0, 0).unwrap().set_text("Category");
165 tbl.cell(0, 0).unwrap().shading("2E75B6");
166 tbl.cell(0, 1).unwrap().set_text("Specification");
167 tbl.cell(0, 1).unwrap().shading("2E75B6");
168 tbl.cell(0, 2).unwrap().set_text("Value");
169 tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171 // "Hardware" spans 3 rows
172 tbl.cell(1, 0).unwrap().set_text("Hardware");
173 tbl.cell(1, 0).unwrap().v_merge_restart();
174 tbl.cell(1, 0).unwrap().shading("E2EFDA");
175 tbl.cell(1, 0)
176 .unwrap()
177 .vertical_alignment(VerticalAlignment::Center);
178 tbl.cell(1, 1).unwrap().set_text("Processor");
179 tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181 tbl.cell(2, 0).unwrap().v_merge_continue();
182 tbl.cell(2, 1).unwrap().set_text("Memory");
183 tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185 tbl.cell(3, 0).unwrap().v_merge_continue();
186 tbl.cell(3, 1).unwrap().set_text("Storage");
187 tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189 // "Network" spans 2 rows
190 tbl.cell(4, 0).unwrap().set_text("Network");
191 tbl.cell(4, 0).unwrap().v_merge_restart();
192 tbl.cell(4, 0).unwrap().shading("FCE4D6");
193 tbl.cell(4, 0)
194 .unwrap()
195 .vertical_alignment(VerticalAlignment::Center);
196 tbl.cell(4, 1).unwrap().set_text("Ethernet");
197 tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199 tbl.cell(5, 0).unwrap().v_merge_continue();
200 tbl.cell(5, 1).unwrap().set_text("Management");
201 tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203 // "Software" spans 2 rows
204 tbl.cell(6, 0).unwrap().set_text("Software");
205 tbl.cell(6, 0).unwrap().v_merge_restart();
206 tbl.cell(6, 0).unwrap().shading("D6E4F0");
207 tbl.cell(6, 0)
208 .unwrap()
209 .vertical_alignment(VerticalAlignment::Center);
210 tbl.cell(6, 1).unwrap().set_text("Operating System");
211 tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213 tbl.cell(7, 0).unwrap().v_merge_continue();
214 tbl.cell(7, 1).unwrap().set_text("Monitoring");
215 tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216 }
217
218 doc.add_paragraph("");
219
220 // =========================================================================
221 // 4. Nested table (table inside a cell)
222 // =========================================================================
223 doc.add_paragraph("4. Nested Table").style("Heading2");
224
225 {
226 let mut tbl = doc.add_table(2, 2);
227 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228 tbl = tbl.width_pct(100.0);
229 tbl = tbl.cell_margins(
230 Length::twips(72),
231 Length::twips(108),
232 Length::twips(72),
233 Length::twips(108),
234 );
235
236 tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237 tbl.cell(0, 0).unwrap().shading("2E75B6");
238 tbl.cell(0, 1).unwrap().set_text("Project Beta");
239 tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241 // Nested table in cell (1,0)
242 {
243 let mut cell = tbl.cell(1, 0).unwrap();
244 cell.set_text("Milestones:");
245 let mut inner = cell.add_table(3, 2);
246 inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247 inner.cell(0, 0).unwrap().set_text("Phase");
248 inner.cell(0, 0).unwrap().shading("E2EFDA");
249 inner.cell(0, 1).unwrap().set_text("Status");
250 inner.cell(0, 1).unwrap().shading("E2EFDA");
251 inner.cell(1, 0).unwrap().set_text("Design");
252 inner.cell(1, 1).unwrap().set_text("Complete");
253 inner.cell(2, 0).unwrap().set_text("Build");
254 inner.cell(2, 1).unwrap().set_text("In Progress");
255 }
256
257 // Nested table in cell (1,1)
258 {
259 let mut cell = tbl.cell(1, 1).unwrap();
260 cell.set_text("Budget:");
261 let mut inner = cell.add_table(3, 2);
262 inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263 inner.cell(0, 0).unwrap().set_text("Category");
264 inner.cell(0, 0).unwrap().shading("FCE4D6");
265 inner.cell(0, 1).unwrap().set_text("Amount");
266 inner.cell(0, 1).unwrap().shading("FCE4D6");
267 inner.cell(1, 0).unwrap().set_text("Development");
268 inner.cell(1, 1).unwrap().set_text("$120,000");
269 inner.cell(2, 0).unwrap().set_text("Testing");
270 inner.cell(2, 1).unwrap().set_text("$35,000");
271 }
272 }
273
274 doc.add_paragraph("");
275
276 // =========================================================================
277 // 5. Form-style table with labels
278 // =========================================================================
279 doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281 {
282 let mut tbl = doc.add_table(6, 4);
283 tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284 tbl = tbl.width_pct(100.0);
285
286 // Row 0: Full-width title
287 tbl.cell(0, 0)
288 .unwrap()
289 .set_text("Customer Registration Form");
290 tbl.cell(0, 0).unwrap().grid_span(4);
291 tbl.cell(0, 0).unwrap().shading("404040");
292
293 // Row 1: Name fields
294 tbl.cell(1, 0).unwrap().set_text("First Name");
295 tbl.cell(1, 0).unwrap().shading("E8E8E8");
296 tbl.cell(1, 1).unwrap().set_text("John");
297 tbl.cell(1, 2).unwrap().set_text("Last Name");
298 tbl.cell(1, 2).unwrap().shading("E8E8E8");
299 tbl.cell(1, 3).unwrap().set_text("Smith");
300
301 // Row 2: Contact
302 tbl.cell(2, 0).unwrap().set_text("Email");
303 tbl.cell(2, 0).unwrap().shading("E8E8E8");
304 tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305 tbl.cell(2, 1).unwrap().grid_span(3);
306
307 // Row 3: Phone
308 tbl.cell(3, 0).unwrap().set_text("Phone");
309 tbl.cell(3, 0).unwrap().shading("E8E8E8");
310 tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311 tbl.cell(3, 2).unwrap().set_text("Company");
312 tbl.cell(3, 2).unwrap().shading("E8E8E8");
313 tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315 // Row 4: Address (spanning)
316 tbl.cell(4, 0).unwrap().set_text("Address");
317 tbl.cell(4, 0).unwrap().shading("E8E8E8");
318 tbl.cell(4, 1)
319 .unwrap()
320 .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321 tbl.cell(4, 1).unwrap().grid_span(3);
322
323 // Row 5: Notes
324 tbl.cell(5, 0).unwrap().set_text("Notes");
325 tbl.cell(5, 0).unwrap().shading("E8E8E8");
326 tbl.cell(5, 0)
327 .unwrap()
328 .vertical_alignment(VerticalAlignment::Top);
329 {
330 let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331 cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332 cell.add_paragraph("Annual review scheduled for March 2026.");
333 }
334 }
335
336 doc.add_paragraph("");
337
338 // =========================================================================
339 // 6. Comparison table with border styles
340 // =========================================================================
341 doc.add_paragraph("6. Comparison Table with Custom Borders")
342 .style("Heading2");
343
344 {
345 let mut tbl = doc.add_table(5, 3);
346 tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347 tbl = tbl.width_pct(100.0);
348
349 // Header
350 tbl.cell(0, 0).unwrap().set_text("Feature");
351 tbl.cell(0, 0).unwrap().shading("2E75B6");
352 tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353 tbl.cell(0, 1).unwrap().shading("2E75B6");
354 tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355 tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357 tbl.cell(1, 0).unwrap().set_text("Users");
358 tbl.cell(1, 1).unwrap().set_text("Up to 10");
359 tbl.cell(1, 2).unwrap().set_text("Unlimited");
360 tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362 tbl.cell(2, 0).unwrap().set_text("Storage");
363 tbl.cell(2, 1).unwrap().set_text("50 GB");
364 tbl.cell(2, 2).unwrap().set_text("5 TB");
365 tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367 tbl.cell(3, 0).unwrap().set_text("Support");
368 tbl.cell(3, 1).unwrap().set_text("Email only");
369 tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370 tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372 tbl.cell(4, 0).unwrap().set_text("Price");
373 tbl.cell(4, 0).unwrap().shading("F2F2F2");
374 tbl.cell(4, 1).unwrap().set_text("$29/month");
375 tbl.cell(4, 1).unwrap().shading("F2F2F2");
376 tbl.cell(4, 2).unwrap().set_text("$199/month");
377 tbl.cell(4, 2).unwrap().shading("C6EFCE");
378 }
379
380 doc.add_paragraph("");
381
382 // =========================================================================
383 // 7. Wide table with fixed layout and row height
384 // =========================================================================
385 doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386 .style("Heading2");
387
388 {
389 let mut tbl = doc.add_table(4, 5);
390 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391 tbl = tbl.width(Length::inches(7.0));
392 tbl = tbl.layout_fixed();
393
394 // Set column widths
395 for col in 0..5 {
396 tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397 }
398
399 // Header with exact height
400 tbl.row(0).unwrap().height_exact(Length::twips(480));
401 tbl.row(0).unwrap().header();
402 tbl.row(0).unwrap().cant_split();
403
404 let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405 for (col, h) in headers.iter().enumerate() {
406 tbl.cell(0, col).unwrap().set_text(h);
407 tbl.cell(0, col).unwrap().shading("404040");
408 tbl.cell(0, col)
409 .unwrap()
410 .vertical_alignment(VerticalAlignment::Center);
411 }
412
413 // Schedule rows with minimum height
414 tbl.row(1).unwrap().height(Length::twips(600));
415 tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416 tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417 tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418 tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419 tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421 tbl.row(2).unwrap().height(Length::twips(600));
422 tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423 tbl.cell(2, 0).unwrap().shading("D6E4F0");
424 tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425 tbl.cell(2, 1).unwrap().shading("FCE4D6");
426 tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427 tbl.cell(2, 2).unwrap().shading("D6E4F0");
428 tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429 tbl.cell(2, 3).unwrap().shading("E2EFDA");
430 tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431 tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433 tbl.row(3).unwrap().height(Length::twips(600));
434 tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435 tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436 tbl.cell(3, 1).unwrap().shading("D6E4F0");
437 tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438 tbl.cell(3, 2).unwrap().shading("FCE4D6");
439 tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440 tbl.cell(3, 3).unwrap().shading("D6E4F0");
441 tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442 tbl.cell(3, 4).unwrap().shading("E2EFDA");
443 }
444
445 doc.set_title("Styled Tables Showcase");
446 doc.set_author("rdocx");
447
448 doc.save(path).unwrap();
449}More examples
examples/generate_samples.rs (line 363)
34fn generate_feature_showcase(path: &Path) {
35 let mut doc = Document::new();
36
37 // =========================================================================
38 // PAGE SETUP & METADATA
39 // =========================================================================
40 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41 doc.set_margins(
42 Length::inches(1.0), // top
43 Length::inches(1.0), // right
44 Length::inches(1.0), // bottom
45 Length::inches(1.0), // left
46 );
47 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48 doc.set_gutter(Length::twips(0));
49
50 doc.set_title("rdocx Feature Showcase");
51 doc.set_author("rdocx Sample Generator");
52 doc.set_subject("Comprehensive feature demonstration");
53 doc.set_keywords("rdocx, docx, rust, sample");
54
55 // Header & Footer
56 doc.set_header("rdocx Feature Showcase");
57 doc.set_footer("Generated by rdocx — Page");
58
59 // Different first page header
60 doc.set_different_first_page(true);
61 doc.set_first_page_header("rdocx");
62 doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64 // =========================================================================
65 // PAGE 1: COVER PAGE — background image, run formatting
66 // =========================================================================
67 let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68 doc.add_background_image(&bg_cover, "cover_bg.png");
69
70 doc.add_paragraph(""); // spacer
71 doc.add_paragraph(""); // spacer
72 doc.add_paragraph(""); // spacer
73
74 {
75 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77 }
78 {
79 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80 p.add_run("Feature Showcase")
81 .size(28.0)
82 .color("FFFFFF")
83 .italic(true);
84 }
85
86 doc.add_paragraph(""); // spacer
87
88 {
89 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90 p.add_run("A comprehensive demonstration of every feature")
91 .size(14.0)
92 .color("CCDDFF");
93 }
94 {
95 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96 p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97 .size(14.0)
98 .color("CCDDFF");
99 }
100
101 // =========================================================================
102 // PAGE 2: TEXT FORMATTING
103 // =========================================================================
104 doc.add_paragraph("").page_break_before(true);
105
106 doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108 doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109 doc.add_paragraph("");
110
111 // --- Paragraph alignment ---
112 doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114 doc.add_paragraph("This paragraph is left-aligned (the default).")
115 .alignment(Alignment::Left);
116 doc.add_paragraph("This paragraph is center-aligned.")
117 .alignment(Alignment::Center);
118 doc.add_paragraph("This paragraph is right-aligned.")
119 .alignment(Alignment::Right);
120 doc.add_paragraph(
121 "This paragraph is justified. To demonstrate justified text properly, it needs \
122 to be long enough to span multiple lines so the word spacing adjustment is visible \
123 across the full width of the text area on the page.",
124 )
125 .alignment(Alignment::Justify);
126
127 doc.add_paragraph("");
128
129 // --- Run formatting ---
130 doc.add_paragraph("Run Formatting").style("Heading2");
131
132 {
133 let mut p = doc.add_paragraph("");
134 p.add_run("Bold text").bold(true);
135 p.add_run(" | ");
136 p.add_run("Italic text").italic(true);
137 p.add_run(" | ");
138 p.add_run("Bold + Italic").bold(true).italic(true);
139 }
140 {
141 let mut p = doc.add_paragraph("");
142 p.add_run("Single underline").underline(true);
143 p.add_run(" | ");
144 p.add_run("Strikethrough").strike(true);
145 p.add_run(" | ");
146 p.add_run("Double strikethrough").double_strike(true);
147 }
148 {
149 let mut p = doc.add_paragraph("");
150 p.add_run("Red text").color("FF0000");
151 p.add_run(" | ");
152 p.add_run("Blue text").color("0000FF");
153 p.add_run(" | ");
154 p.add_run("Green text").color("00AA00");
155 p.add_run(" | ");
156 p.add_run("Highlighted").highlight("FFFF00");
157 }
158 {
159 let mut p = doc.add_paragraph("");
160 p.add_run("8pt small").size(8.0);
161 p.add_run(" | ");
162 p.add_run("11pt normal").size(11.0);
163 p.add_run(" | ");
164 p.add_run("16pt large").size(16.0);
165 p.add_run(" | ");
166 p.add_run("24pt extra-large").size(24.0);
167 }
168 {
169 let mut p = doc.add_paragraph("");
170 p.add_run("Arial font").font("Arial");
171 p.add_run(" | ");
172 p.add_run("Times New Roman font").font("Times New Roman");
173 p.add_run(" | ");
174 p.add_run("Courier New font").font("Courier New");
175 }
176 {
177 let mut p = doc.add_paragraph("");
178 p.add_run("Normal");
179 p.add_run(" H").size(11.0);
180 p.add_run("2").subscript();
181 p.add_run("O (subscript)").size(11.0);
182 p.add_run(" | E = mc").size(11.0);
183 p.add_run("2").superscript();
184 p.add_run(" (superscript)").size(11.0);
185 }
186 {
187 let mut p = doc.add_paragraph("");
188 p.add_run("ALL CAPS").all_caps(true);
189 p.add_run(" | ");
190 p.add_run("Small Caps").small_caps(true);
191 p.add_run(" | ");
192 p.add_run("Expanded spacing")
193 .character_spacing(Length::twips(40));
194 }
195
196 doc.add_paragraph("");
197
198 // --- Paragraph formatting ---
199 doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201 doc.add_paragraph("Paragraph with shading (light green background)")
202 .shading("E2EFDA");
203
204 doc.add_paragraph("Paragraph with bottom border")
205 .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207 doc.add_paragraph("Paragraph with all borders")
208 .border_all(BorderStyle::Single, 4, "FF0000");
209
210 doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211 .indent_left(Length::inches(1.0))
212 .hanging_indent(Length::inches(0.5));
213
214 doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215 .first_line_indent(Length::inches(0.5));
216
217 doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218 .space_before(Length::pt(24.0))
219 .space_after(Length::pt(12.0));
220
221 doc.add_paragraph(
222 "Paragraph with double line spacing. This text should have extra vertical \
223 space between lines to demonstrate the line_spacing_multiple setting.",
224 )
225 .line_spacing_multiple(2.0);
226
227 doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228 .keep_with_next(true);
229 doc.add_paragraph("(This stays with the paragraph above.)");
230
231 // =========================================================================
232 // PAGE 3: LISTS & TAB STOPS
233 // =========================================================================
234 doc.add_paragraph("").page_break_before(true);
235
236 doc.add_paragraph("2. Lists").style("Heading1");
237
238 doc.add_paragraph("Bullet List").style("Heading2");
239
240 doc.add_bullet_list_item("First bullet item", 0);
241 doc.add_bullet_list_item("Second bullet item", 0);
242 doc.add_bullet_list_item("Nested level 1", 1);
243 doc.add_bullet_list_item("Nested level 2", 2);
244 doc.add_bullet_list_item("Back to level 1", 1);
245 doc.add_bullet_list_item("Third bullet item", 0);
246
247 doc.add_paragraph("");
248
249 doc.add_paragraph("Numbered List").style("Heading2");
250
251 doc.add_numbered_list_item("First numbered item", 0);
252 doc.add_numbered_list_item("Second numbered item", 0);
253 doc.add_numbered_list_item("Sub-item A", 1);
254 doc.add_numbered_list_item("Sub-item B", 1);
255 doc.add_numbered_list_item("Third numbered item", 0);
256
257 doc.add_paragraph("");
258
259 // --- Tab stops ---
260 doc.add_paragraph("Tab Stops").style("Heading2");
261
262 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268 doc.add_paragraph("Item\t........\tPrice")
269 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273 doc.add_paragraph("Widget A\t........\t$19.99")
274 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278 doc.add_paragraph("Gadget B\t________\t$249.50")
279 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280 .add_tab_stop_with_leader(
281 TabAlignment::Right,
282 Length::inches(4.0),
283 TabLeader::Underscore,
284 )
285 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287 // =========================================================================
288 // PAGE 4: TABLES
289 // =========================================================================
290 doc.add_paragraph("").page_break_before(true);
291
292 doc.add_paragraph("3. Tables").style("Heading1");
293
294 // --- Basic table with borders ---
295 doc.add_paragraph("Basic Table with Borders")
296 .style("Heading2");
297
298 {
299 let mut tbl = doc.add_table(4, 3);
300 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302 // Header row
303 for col in 0..3 {
304 tbl.cell(0, col).unwrap().shading("2E75B6");
305 }
306 tbl.cell(0, 0).unwrap().set_text("Name");
307 tbl.cell(0, 1).unwrap().set_text("Role");
308 tbl.cell(0, 2).unwrap().set_text("Location");
309
310 tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311 tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312 tbl.cell(1, 2).unwrap().set_text("New York");
313
314 tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315 tbl.cell(2, 1).unwrap().set_text("Product Manager");
316 tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318 tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319 tbl.cell(3, 1).unwrap().set_text("Designer");
320 tbl.cell(3, 2).unwrap().set_text("London");
321 }
322
323 doc.add_paragraph("");
324
325 // --- Table with cell merging ---
326 doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327 .style("Heading2");
328
329 {
330 let mut tbl = doc.add_table(4, 4);
331 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332 tbl = tbl.width_pct(100.0);
333
334 // Header spanning all columns
335 tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336 tbl.cell(0, 0).unwrap().shading("1F4E79");
337 tbl.cell(0, 0).unwrap().grid_span(4);
338
339 // Sub-header
340 tbl.cell(1, 0).unwrap().set_text("Region");
341 tbl.cell(1, 0).unwrap().shading("D6E4F0");
342 tbl.cell(1, 1).unwrap().set_text("Q1");
343 tbl.cell(1, 1).unwrap().shading("D6E4F0");
344 tbl.cell(1, 2).unwrap().set_text("Q2");
345 tbl.cell(1, 2).unwrap().shading("D6E4F0");
346 tbl.cell(1, 3).unwrap().set_text("Total");
347 tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349 // Data
350 tbl.cell(2, 0).unwrap().set_text("North America");
351 tbl.cell(2, 1).unwrap().set_text("$2.4M");
352 tbl.cell(2, 2).unwrap().set_text("$2.7M");
353 tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355 tbl.cell(3, 0).unwrap().set_text("Europe");
356 tbl.cell(3, 1).unwrap().set_text("$1.8M");
357 tbl.cell(3, 2).unwrap().set_text("$2.0M");
358 tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360 // Vertical alignment on data cells
361 tbl.cell(2, 3)
362 .unwrap()
363 .vertical_alignment(VerticalAlignment::Center);
364 tbl.cell(3, 3)
365 .unwrap()
366 .vertical_alignment(VerticalAlignment::Bottom);
367 }
368
369 doc.add_paragraph("");
370
371 // --- Table with vertical merge ---
372 doc.add_paragraph("Table with Vertical Merge")
373 .style("Heading2");
374
375 {
376 let mut tbl = doc.add_table(4, 3);
377 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379 tbl.cell(0, 0).unwrap().set_text("Category");
380 tbl.cell(0, 0).unwrap().shading("E2EFDA");
381 tbl.cell(0, 1).unwrap().set_text("Item");
382 tbl.cell(0, 1).unwrap().shading("E2EFDA");
383 tbl.cell(0, 2).unwrap().set_text("Price");
384 tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386 // "Hardware" spans rows 1-2
387 tbl.cell(1, 0).unwrap().set_text("Hardware");
388 tbl.cell(1, 0).unwrap().v_merge_restart();
389 tbl.cell(1, 1).unwrap().set_text("Laptop");
390 tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392 tbl.cell(2, 0).unwrap().v_merge_continue();
393 tbl.cell(2, 1).unwrap().set_text("Monitor");
394 tbl.cell(2, 2).unwrap().set_text("$450");
395
396 // "Software" on row 3
397 tbl.cell(3, 0).unwrap().set_text("Software");
398 tbl.cell(3, 1).unwrap().set_text("IDE License");
399 tbl.cell(3, 2).unwrap().set_text("$200/yr");
400 }
401
402 doc.add_paragraph("");
403
404 // --- Nested table ---
405 doc.add_paragraph("Nested Table").style("Heading2");
406
407 {
408 let mut tbl = doc.add_table(2, 2);
409 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411 tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412 tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413 tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415 // Nested table inside cell (1,1)
416 {
417 let mut cell = tbl.cell(1, 1).unwrap();
418 cell.set_text("Contains nested table:");
419 let mut nested = cell.add_table(2, 2);
420 nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421 nested.cell(0, 0).unwrap().set_text("Inner A");
422 nested.cell(0, 1).unwrap().set_text("Inner B");
423 nested.cell(1, 0).unwrap().set_text("Inner C");
424 nested.cell(1, 1).unwrap().set_text("Inner D");
425 }
426 }
427
428 // =========================================================================
429 // PAGE 5: IMAGES
430 // =========================================================================
431 doc.add_paragraph("").page_break_before(true);
432
433 doc.add_paragraph("4. Images").style("Heading1");
434
435 doc.add_paragraph("Inline Image").style("Heading2");
436
437 doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438 let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439 doc.add_picture(
440 &inline_img,
441 "inline_chart.png",
442 Length::inches(3.0),
443 Length::inches(0.75),
444 );
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("Header Image").style("Heading2");
449
450 // Replace the text-only header with an image header
451 let header_img = create_sample_png(400, 40, [40, 40, 40]);
452 doc.set_header_image(
453 &header_img,
454 "header_logo.png",
455 Length::inches(2.0),
456 Length::inches(0.2),
457 );
458
459 doc.add_paragraph(
460 "The document header has been replaced with an inline image. \
461 Check the header area at the top of this page.",
462 );
463
464 doc.add_paragraph("");
465 doc.add_paragraph(
466 "Note: The cover page uses a full-page background image behind the text, \
467 demonstrated on page 1 via add_background_image().",
468 );
469
470 // =========================================================================
471 // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472 // =========================================================================
473 doc.add_paragraph("").page_break_before(true);
474
475 doc.add_paragraph("5. Content Manipulation")
476 .style("Heading1");
477
478 // --- Placeholder replacement ---
479 doc.add_paragraph("Placeholder Replacement")
480 .style("Heading2");
481
482 doc.add_paragraph(
483 "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484 );
485
486 {
487 let mut p = doc.add_paragraph("");
488 p.add_run("Customer: ").bold(true);
489 p.add_run("{{customer}}");
490 }
491 {
492 let mut p = doc.add_paragraph("");
493 p.add_run("Date: ").bold(true);
494 p.add_run("{{date}}");
495 }
496 doc.add_paragraph("Reference: {{ref_number}}");
497
498 // Table with placeholders
499 {
500 let mut tbl = doc.add_table(3, 2);
501 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502 tbl.cell(0, 0).unwrap().set_text("Field");
503 tbl.cell(0, 0).unwrap().shading("D6E4F0");
504 tbl.cell(0, 1).unwrap().set_text("Value");
505 tbl.cell(0, 1).unwrap().shading("D6E4F0");
506 tbl.cell(1, 0).unwrap().set_text("Project");
507 tbl.cell(1, 1).unwrap().set_text("{{project}}");
508 tbl.cell(2, 0).unwrap().set_text("Status");
509 tbl.cell(2, 1).unwrap().set_text("{{status}}");
510 }
511
512 // Perform replacements
513 let mut replacements = HashMap::new();
514 replacements.insert("{{customer}}", "Acme Corporation");
515 replacements.insert("{{date}}", "February 22, 2026");
516 replacements.insert("{{ref_number}}", "REF-2026-001");
517 replacements.insert("{{project}}", "Infrastructure Upgrade");
518 replacements.insert("{{status}}", "In Progress");
519 let replace_count = doc.replace_all(&replacements);
520
521 doc.add_paragraph("");
522 doc.add_paragraph(&format!(
523 "(Replaced {} placeholders above — in body text and table cells)",
524 replace_count
525 ));
526
527 doc.add_paragraph("");
528
529 // --- Content insertion ---
530 doc.add_paragraph("Content Insertion").style("Heading2");
531
532 doc.add_paragraph("Section A: First section of content.");
533 doc.add_paragraph("Section C: Third section of content.");
534
535 // Insert "Section B" between A and C
536 if let Some(idx) = doc.find_content_index("Section C") {
537 doc.insert_paragraph(
538 idx,
539 "Section B: Inserted between A and C using find_content_index().",
540 );
541 }
542
543 doc.add_paragraph("");
544 doc.add_paragraph(
545 "The paragraph above ('Section B') was inserted at a specific position \
546 using find_content_index() + insert_paragraph().",
547 );
548
549 // =========================================================================
550 // PAGE 7: LANDSCAPE — section break, wide table
551 // =========================================================================
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554 doc.add_paragraph("6. Mixed Page Orientation")
555 .style("Heading1");
556
557 doc.add_paragraph(
558 "This page is in LANDSCAPE orientation. It was created using a section break \
559 followed by section_landscape(). This is useful for wide tables or charts.",
560 );
561
562 doc.add_paragraph("");
563
564 // Wide table for landscape
565 {
566 let mut tbl = doc.add_table(4, 7);
567 tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570 for (col, h) in headers.iter().enumerate() {
571 tbl.cell(0, col).unwrap().set_text(h);
572 tbl.cell(0, col).unwrap().shading("2E75B6");
573 }
574
575 let data = [
576 [
577 "North America",
578 "$1.2M",
579 "$1.3M",
580 "$1.4M",
581 "$1.5M",
582 "$1.6M",
583 "$7.0M",
584 ],
585 [
586 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587 ],
588 [
589 "Asia Pacific",
590 "$0.5M",
591 "$0.6M",
592 "$0.7M",
593 "$0.7M",
594 "$0.8M",
595 "$3.3M",
596 ],
597 ];
598 for (row_idx, row_data) in data.iter().enumerate() {
599 for (col, val) in row_data.iter().enumerate() {
600 tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601 }
602 }
603 }
604
605 // End landscape, return to portrait
606 doc.add_paragraph("")
607 .section_break(SectionBreak::NextPage)
608 .section_landscape();
609
610 // =========================================================================
611 // PAGE 8: BACK TO PORTRAIT — styles, final notes
612 // =========================================================================
613 doc.add_paragraph("7. Custom Styles & Summary")
614 .style("Heading1");
615
616 doc.add_paragraph(
617 "This final page is back in portrait orientation after a section break. \
618 The document has demonstrated:",
619 );
620
621 doc.add_bullet_list_item(
622 "Page setup: size, margins, header/footer distance, gutter",
623 0,
624 );
625 doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626 doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627 doc.add_bullet_list_item("Background images: full-page behind text", 0);
628 doc.add_bullet_list_item(
629 "Text formatting: bold, italic, underline, strike, color, size, font",
630 0,
631 );
632 doc.add_bullet_list_item(
633 "Advanced run formatting: superscript, subscript, caps, spacing",
634 0,
635 );
636 doc.add_bullet_list_item(
637 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638 0,
639 );
640 doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641 doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642 doc.add_bullet_list_item(
643 "Tables: borders, shading, column spans, row spans, nesting",
644 0,
645 );
646 doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647 doc.add_bullet_list_item("Inline images", 0);
648 doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649 doc.add_bullet_list_item("Content insertion at specific positions", 0);
650 doc.add_bullet_list_item(
651 "Section breaks with mixed portrait/landscape orientation",
652 0,
653 );
654
655 doc.add_paragraph("");
656 doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657 .alignment(Alignment::Center)
658 .shading("E2EFDA")
659 .border_all(BorderStyle::Single, 2, "00AA00");
660
661 doc.save(path).unwrap();
662}examples/generate_all_samples.rs (line 458)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87 let mut doc = Document::new();
88
89 // ── Page Setup & Metadata ──
90 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91 doc.set_margins(
92 Length::inches(1.0),
93 Length::inches(1.0),
94 Length::inches(1.0),
95 Length::inches(1.0),
96 );
97 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98 doc.set_gutter(Length::twips(0));
99 doc.set_title("rdocx Feature Showcase");
100 doc.set_author("rdocx Sample Generator");
101 doc.set_subject("Comprehensive feature demonstration");
102 doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104 // Headers & Footers with different first page
105 doc.set_different_first_page(true);
106 doc.set_first_page_header("rdocx");
107 doc.set_first_page_footer("Feature Showcase — Cover Page");
108 doc.set_header("rdocx Feature Showcase");
109 doc.set_footer("Generated by rdocx");
110
111 // ── COVER PAGE ──
112 let bg = create_sample_png(612, 792, [20, 45, 90]);
113 doc.add_background_image(&bg, "cover_bg.png");
114
115 for _ in 0..3 {
116 doc.add_paragraph("");
117 }
118 {
119 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121 }
122 {
123 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124 p.add_run("Complete Feature Showcase")
125 .size(28.0)
126 .color("FFFFFF")
127 .italic(true);
128 }
129 doc.add_paragraph("");
130 {
131 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132 p.add_run("Every feature of the rdocx Rust library")
133 .size(13.0)
134 .color("B0C4DE");
135 }
136 {
137 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138 p.add_run("demonstrated in a single document.")
139 .size(13.0)
140 .color("B0C4DE");
141 }
142
143 // ── TABLE OF CONTENTS ──
144 doc.add_paragraph("").page_break_before(true);
145 doc.insert_toc(doc.content_count(), 3);
146
147 // ── SECTION 1: TEXT FORMATTING ──
148 doc.add_paragraph("").page_break_before(true);
149 doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151 // Paragraph Alignment
152 doc.add_paragraph("1.1 Paragraph Alignment")
153 .style("Heading2");
154 doc.add_paragraph("Left-aligned paragraph (default).")
155 .alignment(Alignment::Left);
156 doc.add_paragraph("Center-aligned paragraph.")
157 .alignment(Alignment::Center);
158 doc.add_paragraph("Right-aligned paragraph.")
159 .alignment(Alignment::Right);
160 doc.add_paragraph(
161 "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162 distributes extra space across word gaps so lines fill the full width of the text area.",
163 )
164 .alignment(Alignment::Justify);
165
166 doc.add_paragraph("");
167
168 // Run Formatting
169 doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170 {
171 let mut p = doc.add_paragraph("");
172 p.add_run("Bold").bold(true);
173 p.add_run(" | ");
174 p.add_run("Italic").italic(true);
175 p.add_run(" | ");
176 p.add_run("Bold Italic").bold(true).italic(true);
177 p.add_run(" | ");
178 p.add_run("Underline").underline(true);
179 p.add_run(" | ");
180 p.add_run("Strikethrough").strike(true);
181 p.add_run(" | ");
182 p.add_run("Double Strike").double_strike(true);
183 }
184 {
185 let mut p = doc.add_paragraph("");
186 p.add_run("Red").color("FF0000");
187 p.add_run(" | ");
188 p.add_run("Blue").color("0000FF");
189 p.add_run(" | ");
190 p.add_run("Green").color("00AA00");
191 p.add_run(" | ");
192 p.add_run("Highlighted").highlight("FFFF00");
193 }
194 {
195 let mut p = doc.add_paragraph("");
196 p.add_run("8pt").size(8.0);
197 p.add_run(" | ");
198 p.add_run("11pt").size(11.0);
199 p.add_run(" | ");
200 p.add_run("16pt").size(16.0);
201 p.add_run(" | ");
202 p.add_run("24pt").size(24.0);
203 }
204 {
205 let mut p = doc.add_paragraph("");
206 p.add_run("Arial").font("Arial");
207 p.add_run(" | ");
208 p.add_run("Times New Roman").font("Times New Roman");
209 p.add_run(" | ");
210 p.add_run("Courier New").font("Courier New");
211 }
212 {
213 let mut p = doc.add_paragraph("");
214 p.add_run("H");
215 p.add_run("2").subscript();
216 p.add_run("O (subscript) | E=mc");
217 p.add_run("2").superscript();
218 p.add_run(" (superscript)");
219 }
220 {
221 let mut p = doc.add_paragraph("");
222 p.add_run("ALL CAPS").all_caps(true);
223 p.add_run(" | ");
224 p.add_run("Small Caps").small_caps(true);
225 p.add_run(" | ");
226 p.add_run("Expanded").character_spacing(Length::twips(40));
227 p.add_run(" | ");
228 p.add_run("Hidden text (not visible)").hidden(true);
229 }
230
231 doc.add_paragraph("");
232
233 // Underline Styles
234 doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235 {
236 let mut p = doc.add_paragraph("");
237 p.add_run("Single ")
238 .underline_style(rdocx::UnderlineStyle::Single);
239 p.add_run("Double ")
240 .underline_style(rdocx::UnderlineStyle::Double);
241 p.add_run("Thick ")
242 .underline_style(rdocx::UnderlineStyle::Thick);
243 p.add_run("Dotted ")
244 .underline_style(rdocx::UnderlineStyle::Dotted);
245 p.add_run("Dash ")
246 .underline_style(rdocx::UnderlineStyle::Dash);
247 p.add_run("Wave ")
248 .underline_style(rdocx::UnderlineStyle::Wave);
249 }
250
251 // ── SECTION 2: PARAGRAPH FORMATTING ──
252 doc.add_paragraph("").page_break_before(true);
253 doc.add_paragraph("2. Paragraph Formatting")
254 .style("Heading1");
255
256 doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257 doc.add_paragraph("Paragraph with light green shading")
258 .shading("E2EFDA");
259 doc.add_paragraph("Paragraph with bottom border")
260 .border_bottom(BorderStyle::Single, 6, "2E75B6");
261 doc.add_paragraph("Paragraph with all borders (red)")
262 .border_all(BorderStyle::Single, 4, "FF0000");
263
264 doc.add_paragraph("2.2 Indentation").style("Heading2");
265 doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266 .indent_left(Length::inches(1.0))
267 .hanging_indent(Length::inches(0.5));
268 doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269 .first_line_indent(Length::inches(0.5));
270 doc.add_paragraph("Both left and right indent (0.75 inches each)")
271 .indent_left(Length::inches(0.75))
272 .indent_right(Length::inches(0.75));
273
274 doc.add_paragraph("2.3 Spacing & Line Height")
275 .style("Heading2");
276 doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277 .space_before(Length::pt(24.0))
278 .space_after(Length::pt(12.0));
279 doc.add_paragraph(
280 "Double line spacing paragraph. This text should have extra vertical space between \
281 lines to demonstrate line_spacing_multiple(2.0).",
282 )
283 .line_spacing_multiple(2.0);
284 doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285 .line_spacing(20.0);
286
287 doc.add_paragraph("2.4 Pagination Controls")
288 .style("Heading2");
289 doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290 .keep_with_next(true);
291 doc.add_paragraph("(This paragraph was kept with the one above.)");
292 doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293 .keep_together(true);
294 doc.add_paragraph("widow_control — prevents widow/orphan lines")
295 .widow_control(true);
296
297 // ── SECTION 3: LISTS ──
298 doc.add_paragraph("").page_break_before(true);
299 doc.add_paragraph("3. Lists").style("Heading1");
300
301 doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302 doc.add_bullet_list_item("First item", 0);
303 doc.add_bullet_list_item("Second item", 0);
304 doc.add_bullet_list_item("Nested level 1", 1);
305 doc.add_bullet_list_item("Nested level 2", 2);
306 doc.add_bullet_list_item("Back to level 1", 1);
307 doc.add_bullet_list_item("Third item", 0);
308
309 doc.add_paragraph("");
310
311 doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312 doc.add_numbered_list_item("First numbered item", 0);
313 doc.add_numbered_list_item("Second numbered item", 0);
314 doc.add_numbered_list_item("Sub-item A", 1);
315 doc.add_numbered_list_item("Sub-item B", 1);
316 doc.add_numbered_list_item("Third numbered item", 0);
317
318 // ── SECTION 4: TAB STOPS ──
319 doc.add_paragraph("");
320 doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322 doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329 doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330 doc.add_paragraph("Item\t\tPrice")
331 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334 doc.add_paragraph("Widget\t\t$19.99")
335 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338 doc.add_paragraph("Gadget\t\t$249.50")
339 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340 .add_tab_stop_with_leader(
341 TabAlignment::Right,
342 Length::inches(4.0),
343 TabLeader::Underscore,
344 )
345 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347 // ── SECTION 5: TABLES ──
348 doc.add_paragraph("").page_break_before(true);
349 doc.add_paragraph("5. Tables").style("Heading1");
350
351 doc.add_paragraph("5.1 Basic Table").style("Heading2");
352 {
353 let mut tbl = doc
354 .add_table(4, 3)
355 .borders(BorderStyle::Single, 4, "000000");
356 for col in 0..3 {
357 tbl.cell(0, col).unwrap().shading("2E75B6");
358 }
359 tbl.cell(0, 0).unwrap().set_text("Name");
360 tbl.cell(0, 1).unwrap().set_text("Role");
361 tbl.cell(0, 2).unwrap().set_text("Location");
362 tbl.cell(1, 0).unwrap().set_text("Walter White");
363 tbl.cell(1, 1).unwrap().set_text("CEO");
364 tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365 tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366 tbl.cell(2, 1).unwrap().set_text("CTO");
367 tbl.cell(2, 2).unwrap().set_text("Remote");
368 tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369 tbl.cell(3, 1).unwrap().set_text("Security");
370 tbl.cell(3, 2).unwrap().set_text("Washington");
371 }
372
373 doc.add_paragraph("");
374
375 doc.add_paragraph("5.2 Column Span & Cell Shading")
376 .style("Heading2");
377 {
378 let mut tbl = doc
379 .add_table(3, 4)
380 .borders(BorderStyle::Single, 4, "000000")
381 .width_pct(100.0);
382 tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383 tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384 tbl.cell(1, 0).unwrap().set_text("Region");
385 tbl.cell(1, 0).unwrap().shading("D6E4F0");
386 tbl.cell(1, 1).unwrap().set_text("Q1");
387 tbl.cell(1, 1).unwrap().shading("D6E4F0");
388 tbl.cell(1, 2).unwrap().set_text("Q2");
389 tbl.cell(1, 2).unwrap().shading("D6E4F0");
390 tbl.cell(1, 3).unwrap().set_text("Total");
391 tbl.cell(1, 3).unwrap().shading("D6E4F0");
392 tbl.cell(2, 0).unwrap().set_text("Americas");
393 tbl.cell(2, 1).unwrap().set_text("$2.4M");
394 tbl.cell(2, 2).unwrap().set_text("$2.7M");
395 tbl.cell(2, 3).unwrap().set_text("$5.1M");
396 }
397
398 doc.add_paragraph("");
399
400 doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401 {
402 let mut tbl = doc
403 .add_table(4, 3)
404 .borders(BorderStyle::Single, 4, "000000");
405 tbl.cell(0, 0).unwrap().set_text("Category");
406 tbl.cell(0, 0).unwrap().shading("E2EFDA");
407 tbl.cell(0, 1).unwrap().set_text("Item");
408 tbl.cell(0, 1).unwrap().shading("E2EFDA");
409 tbl.cell(0, 2).unwrap().set_text("Price");
410 tbl.cell(0, 2).unwrap().shading("E2EFDA");
411 tbl.cell(1, 0).unwrap().set_text("Hardware");
412 tbl.cell(1, 0).unwrap().v_merge_restart();
413 tbl.cell(1, 1).unwrap().set_text("Laptop");
414 tbl.cell(1, 2).unwrap().set_text("$1,200");
415 tbl.cell(2, 0).unwrap().v_merge_continue();
416 tbl.cell(2, 1).unwrap().set_text("Monitor");
417 tbl.cell(2, 2).unwrap().set_text("$450");
418 tbl.cell(3, 0).unwrap().set_text("Software");
419 tbl.cell(3, 1).unwrap().set_text("IDE License");
420 tbl.cell(3, 2).unwrap().set_text("$200/yr");
421 }
422
423 doc.add_paragraph("");
424
425 doc.add_paragraph("5.4 Nested Table").style("Heading2");
426 {
427 let mut tbl = doc
428 .add_table(2, 2)
429 .borders(BorderStyle::Single, 6, "2E75B6");
430 tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431 tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432 tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433 {
434 let mut cell = tbl.cell(1, 1).unwrap();
435 cell.set_text("Nested table below:");
436 let mut nested = cell
437 .add_table(2, 2)
438 .borders(BorderStyle::Single, 2, "FF6600");
439 nested.cell(0, 0).unwrap().set_text("A");
440 nested.cell(0, 1).unwrap().set_text("B");
441 nested.cell(1, 0).unwrap().set_text("C");
442 nested.cell(1, 1).unwrap().set_text("D");
443 }
444 }
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449 .style("Heading2");
450 {
451 let mut tbl = doc
452 .add_table(2, 3)
453 .borders(BorderStyle::Single, 4, "666666");
454 tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455 tbl.cell(0, 0).unwrap().set_text("Top");
456 tbl.cell(0, 0)
457 .unwrap()
458 .vertical_alignment(VerticalAlignment::Top)
459 .shading("FFF2CC");
460 tbl.cell(0, 1).unwrap().set_text("Center");
461 tbl.cell(0, 1)
462 .unwrap()
463 .vertical_alignment(VerticalAlignment::Center)
464 .shading("D9E2F3");
465 tbl.cell(0, 2).unwrap().set_text("Bottom");
466 tbl.cell(0, 2)
467 .unwrap()
468 .vertical_alignment(VerticalAlignment::Bottom)
469 .shading("E2EFDA");
470 tbl.row(1).unwrap().cant_split();
471 tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472 tbl.cell(1, 0).unwrap().no_wrap();
473 tbl.cell(1, 1).unwrap().set_text("Fixed width");
474 tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475 tbl.cell(1, 2).unwrap().set_text("Normal");
476 }
477
478 // ── SECTION 6: IMAGES ──
479 doc.add_paragraph("").page_break_before(true);
480 doc.add_paragraph("6. Images").style("Heading1");
481
482 doc.add_paragraph("6.1 Inline Image").style("Heading2");
483 doc.add_paragraph("A blue gradient image below:");
484 let img = create_sample_png(200, 50, [0, 80, 200]);
485 doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487 doc.add_paragraph("");
488 doc.add_paragraph("6.2 Header Image").style("Heading2");
489 let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490 doc.set_header_image(
491 &hdr_img,
492 "header_logo.png",
493 Length::inches(2.0),
494 Length::inches(0.2),
495 );
496 doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498 doc.add_paragraph("");
499 doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501 // ── SECTION 7: CONTENT MANIPULATION ──
502 doc.add_paragraph("").page_break_before(true);
503 doc.add_paragraph("7. Content Manipulation")
504 .style("Heading1");
505
506 doc.add_paragraph("7.1 Placeholder Replacement")
507 .style("Heading2");
508 doc.add_paragraph("Customer: {{customer}}");
509 doc.add_paragraph("Date: {{date}}");
510 doc.add_paragraph("Reference: {{ref_number}}");
511 {
512 let mut tbl = doc
513 .add_table(2, 2)
514 .borders(BorderStyle::Single, 4, "000000");
515 tbl.cell(0, 0).unwrap().set_text("Project");
516 tbl.cell(0, 0).unwrap().shading("D6E4F0");
517 tbl.cell(0, 1).unwrap().set_text("{{project}}");
518 tbl.cell(1, 0).unwrap().set_text("Status");
519 tbl.cell(1, 0).unwrap().shading("D6E4F0");
520 tbl.cell(1, 1).unwrap().set_text("{{status}}");
521 }
522
523 let mut replacements = HashMap::new();
524 replacements.insert("{{customer}}", "Tensorbee Inc.");
525 replacements.insert("{{date}}", "February 22, 2026");
526 replacements.insert("{{ref_number}}", "TB-2026-001");
527 replacements.insert("{{project}}", "Infrastructure Upgrade");
528 replacements.insert("{{status}}", "In Progress");
529 let n = doc.replace_all(&replacements);
530 doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532 doc.add_paragraph("");
533
534 doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535 doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536 let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537 doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539 doc.add_paragraph("");
540
541 doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542 doc.add_paragraph("Section A: First content.");
543 doc.add_paragraph("Section C: Third content.");
544 if let Some(idx) = doc.find_content_index("Section C") {
545 doc.insert_paragraph(
546 idx,
547 "Section B: Inserted between A and C via find_content_index().",
548 );
549 }
550
551 // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553 doc.add_paragraph("8. Section Breaks & Orientation")
554 .style("Heading1");
555 doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557 {
558 let mut tbl = doc
559 .add_table(3, 7)
560 .borders(BorderStyle::Single, 4, "2E75B6");
561 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562 for (c, h) in headers.iter().enumerate() {
563 tbl.cell(0, c).unwrap().set_text(h);
564 tbl.cell(0, c).unwrap().shading("2E75B6");
565 }
566 let data = [
567 [
568 "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569 ],
570 [
571 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572 ],
573 ];
574 for (r, row) in data.iter().enumerate() {
575 for (c, v) in row.iter().enumerate() {
576 tbl.cell(r + 1, c).unwrap().set_text(v);
577 }
578 }
579 }
580
581 doc.add_paragraph("")
582 .section_break(SectionBreak::NextPage)
583 .section_landscape();
584
585 // ── SECTION 9: CUSTOM STYLES ──
586 doc.add_paragraph("9. Custom Styles").style("Heading1");
587 doc.add_style(
588 StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589 .based_on("Normal")
590 .paragraph_properties({
591 let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592 ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593 val: "clear".to_string(),
594 color: None,
595 fill: Some("FFF2CC".to_string()),
596 });
597 ppr
598 })
599 .run_properties({
600 let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601 rpr.bold = Some(true);
602 rpr.color = Some("C45911".to_string());
603 rpr
604 }),
605 );
606 doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607 .style("CustomHighlight");
608
609 doc.add_paragraph("");
610
611 // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612 doc.add_paragraph("10. Document Intelligence API")
613 .style("Heading1");
614
615 let wc = doc.word_count();
616 let hc = doc.headings().len();
617 let ic = doc.images().len();
618 let lc = doc.links().len();
619 doc.add_paragraph(&format!("Word count: {wc}"));
620 doc.add_paragraph(&format!("Heading count: {hc}"));
621 doc.add_paragraph(&format!("Image count: {ic}"));
622 doc.add_paragraph(&format!("Link count: {lc}"));
623
624 let outline = doc.document_outline();
625 doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627 let issues = doc.audit_accessibility();
628 doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629 for issue in &issues {
630 doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631 }
632
633 // ── SECTION 11: DOCUMENT MERGING ──
634 doc.add_paragraph("");
635 doc.add_paragraph("11. Document Merging").style("Heading1");
636 {
637 let mut other = Document::new();
638 other.add_paragraph("This paragraph was merged from another document using append().");
639 doc.append(&other);
640 }
641
642 doc.add_paragraph("");
643
644 // ── SUMMARY ──
645 doc.add_paragraph("Summary of Demonstrated Features")
646 .style("Heading1");
647 let features = [
648 "Page setup: size, margins, header/footer distance, gutter",
649 "Document metadata: title, author, subject, keywords",
650 "Headers and footers: text, images, different first page",
651 "Background images (full-page behind text)",
652 "Table of Contents generation with bookmarks",
653 "Text formatting: bold, italic, underline styles, strike, color, size, font",
654 "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656 "Pagination controls: keep-with-next, keep-together, widow control",
657 "Bullet and numbered lists with nesting",
658 "Tab stops with dot/underscore/hyphen leaders",
659 "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660 "Row properties: header rows, exact height, cant-split",
661 "Cell properties: width, no-wrap, vertical alignment",
662 "Inline images and header images",
663 "Placeholder replacement (single and batch)",
664 "Regex-based find and replace",
665 "Content insertion at specific positions",
666 "Section breaks with mixed portrait/landscape",
667 "Custom paragraph and character styles",
668 "Document intelligence: word count, headings, outline, images, links",
669 "Accessibility audit",
670 "Document merging (append)",
671 "Export: DOCX, PDF, HTML, Markdown",
672 ];
673 for f in &features {
674 doc.add_bullet_list_item(f, 0);
675 }
676 doc.add_paragraph("");
677 doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678 .alignment(Alignment::Center)
679 .shading("E2EFDA")
680 .border_all(BorderStyle::Single, 2, "00AA00");
681
682 doc
683}Sourcepub fn grid_span(self, span: u32) -> Self
pub fn grid_span(self, span: u32) -> Self
Set horizontal merge (gridSpan). This cell spans span columns.
Examples found in repository?
examples/styled_tables.rs (line 105)
24fn generate_styled_tables(path: &Path) {
25 let mut doc = Document::new();
26 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27 doc.set_margins(
28 Length::inches(0.75),
29 Length::inches(0.75),
30 Length::inches(0.75),
31 Length::inches(0.75),
32 );
33
34 doc.add_paragraph("Styled Tables Showcase")
35 .style("Heading1");
36
37 doc.add_paragraph("");
38
39 // =========================================================================
40 // 1. Professional report table with alternating rows
41 // =========================================================================
42 doc.add_paragraph("1. Report Table with Alternating Row Colors")
43 .style("Heading2");
44
45 {
46 let mut tbl = doc.add_table(8, 4);
47 tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48 tbl = tbl.width_pct(100.0);
49
50 // Header row
51 let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52 for (col, h) in headers.iter().enumerate() {
53 tbl.cell(0, col).unwrap().shading("2E75B6");
54 tbl.cell(0, col).unwrap().set_text(h);
55 }
56 tbl.row(0).unwrap().header();
57
58 // Data with alternating shading
59 let data = [
60 ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61 ["Professional", "$189,000", "$201,000", "+6.3%"],
62 ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63 ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64 ["Training", "$22,000", "$28,900", "+31.4%"],
65 ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66 ];
67
68 for (i, row) in data.iter().enumerate() {
69 let row_idx = i + 1;
70 for (col, val) in row.iter().enumerate() {
71 tbl.cell(row_idx, col).unwrap().set_text(val);
72 // Alternate row colors
73 if i % 2 == 0 {
74 tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75 }
76 }
77 }
78
79 // Total row
80 tbl.cell(7, 0).unwrap().set_text("TOTAL");
81 tbl.cell(7, 0).unwrap().shading("D6E4F0");
82 tbl.cell(7, 1).unwrap().set_text("$613,000");
83 tbl.cell(7, 1).unwrap().shading("D6E4F0");
84 tbl.cell(7, 2).unwrap().set_text("$729,900");
85 tbl.cell(7, 2).unwrap().shading("D6E4F0");
86 tbl.cell(7, 3).unwrap().set_text("+19.1%");
87 tbl.cell(7, 3).unwrap().shading("D6E4F0");
88 }
89
90 doc.add_paragraph("");
91
92 // =========================================================================
93 // 2. Invoice-style table with merged header
94 // =========================================================================
95 doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96 .style("Heading2");
97
98 {
99 let mut tbl = doc.add_table(7, 4);
100 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101 tbl = tbl.width_pct(100.0);
102
103 // Merged title row
104 tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105 tbl.cell(0, 0).unwrap().grid_span(4);
106 tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108 // Column headers
109 let headers = ["Item", "Description", "Qty", "Amount"];
110 for (col, h) in headers.iter().enumerate() {
111 tbl.cell(1, col).unwrap().set_text(h);
112 tbl.cell(1, col).unwrap().shading("D6E4F0");
113 }
114
115 // Line items
116 tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117 tbl.cell(2, 1)
118 .unwrap()
119 .set_text("Enterprise License (500 seats)");
120 tbl.cell(2, 2).unwrap().set_text("1");
121 tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123 tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124 tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125 tbl.cell(3, 2).unwrap().set_text("1");
126 tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128 tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129 tbl.cell(4, 1)
130 .unwrap()
131 .set_text("On-site Training (3 days)");
132 tbl.cell(4, 2).unwrap().set_text("1");
133 tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135 // Subtotal
136 tbl.cell(5, 0).unwrap().set_text("Subtotal");
137 tbl.cell(5, 0).unwrap().grid_span(3);
138 tbl.cell(5, 0).unwrap().shading("F2F2F2");
139 tbl.cell(5, 3).unwrap().set_text("$89,500");
140 tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142 // Total
143 tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144 tbl.cell(6, 0).unwrap().grid_span(3);
145 tbl.cell(6, 0).unwrap().shading("1F4E79");
146 tbl.cell(6, 3).unwrap().set_text("$89,500");
147 tbl.cell(6, 3).unwrap().shading("1F4E79");
148 }
149
150 doc.add_paragraph("");
151
152 // =========================================================================
153 // 3. Specification table with vertical merge
154 // =========================================================================
155 doc.add_paragraph("3. Specification Table with Vertical Merges")
156 .style("Heading2");
157
158 {
159 let mut tbl = doc.add_table(8, 3);
160 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161 tbl = tbl.width_pct(100.0);
162
163 // Header
164 tbl.cell(0, 0).unwrap().set_text("Category");
165 tbl.cell(0, 0).unwrap().shading("2E75B6");
166 tbl.cell(0, 1).unwrap().set_text("Specification");
167 tbl.cell(0, 1).unwrap().shading("2E75B6");
168 tbl.cell(0, 2).unwrap().set_text("Value");
169 tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171 // "Hardware" spans 3 rows
172 tbl.cell(1, 0).unwrap().set_text("Hardware");
173 tbl.cell(1, 0).unwrap().v_merge_restart();
174 tbl.cell(1, 0).unwrap().shading("E2EFDA");
175 tbl.cell(1, 0)
176 .unwrap()
177 .vertical_alignment(VerticalAlignment::Center);
178 tbl.cell(1, 1).unwrap().set_text("Processor");
179 tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181 tbl.cell(2, 0).unwrap().v_merge_continue();
182 tbl.cell(2, 1).unwrap().set_text("Memory");
183 tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185 tbl.cell(3, 0).unwrap().v_merge_continue();
186 tbl.cell(3, 1).unwrap().set_text("Storage");
187 tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189 // "Network" spans 2 rows
190 tbl.cell(4, 0).unwrap().set_text("Network");
191 tbl.cell(4, 0).unwrap().v_merge_restart();
192 tbl.cell(4, 0).unwrap().shading("FCE4D6");
193 tbl.cell(4, 0)
194 .unwrap()
195 .vertical_alignment(VerticalAlignment::Center);
196 tbl.cell(4, 1).unwrap().set_text("Ethernet");
197 tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199 tbl.cell(5, 0).unwrap().v_merge_continue();
200 tbl.cell(5, 1).unwrap().set_text("Management");
201 tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203 // "Software" spans 2 rows
204 tbl.cell(6, 0).unwrap().set_text("Software");
205 tbl.cell(6, 0).unwrap().v_merge_restart();
206 tbl.cell(6, 0).unwrap().shading("D6E4F0");
207 tbl.cell(6, 0)
208 .unwrap()
209 .vertical_alignment(VerticalAlignment::Center);
210 tbl.cell(6, 1).unwrap().set_text("Operating System");
211 tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213 tbl.cell(7, 0).unwrap().v_merge_continue();
214 tbl.cell(7, 1).unwrap().set_text("Monitoring");
215 tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216 }
217
218 doc.add_paragraph("");
219
220 // =========================================================================
221 // 4. Nested table (table inside a cell)
222 // =========================================================================
223 doc.add_paragraph("4. Nested Table").style("Heading2");
224
225 {
226 let mut tbl = doc.add_table(2, 2);
227 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228 tbl = tbl.width_pct(100.0);
229 tbl = tbl.cell_margins(
230 Length::twips(72),
231 Length::twips(108),
232 Length::twips(72),
233 Length::twips(108),
234 );
235
236 tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237 tbl.cell(0, 0).unwrap().shading("2E75B6");
238 tbl.cell(0, 1).unwrap().set_text("Project Beta");
239 tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241 // Nested table in cell (1,0)
242 {
243 let mut cell = tbl.cell(1, 0).unwrap();
244 cell.set_text("Milestones:");
245 let mut inner = cell.add_table(3, 2);
246 inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247 inner.cell(0, 0).unwrap().set_text("Phase");
248 inner.cell(0, 0).unwrap().shading("E2EFDA");
249 inner.cell(0, 1).unwrap().set_text("Status");
250 inner.cell(0, 1).unwrap().shading("E2EFDA");
251 inner.cell(1, 0).unwrap().set_text("Design");
252 inner.cell(1, 1).unwrap().set_text("Complete");
253 inner.cell(2, 0).unwrap().set_text("Build");
254 inner.cell(2, 1).unwrap().set_text("In Progress");
255 }
256
257 // Nested table in cell (1,1)
258 {
259 let mut cell = tbl.cell(1, 1).unwrap();
260 cell.set_text("Budget:");
261 let mut inner = cell.add_table(3, 2);
262 inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263 inner.cell(0, 0).unwrap().set_text("Category");
264 inner.cell(0, 0).unwrap().shading("FCE4D6");
265 inner.cell(0, 1).unwrap().set_text("Amount");
266 inner.cell(0, 1).unwrap().shading("FCE4D6");
267 inner.cell(1, 0).unwrap().set_text("Development");
268 inner.cell(1, 1).unwrap().set_text("$120,000");
269 inner.cell(2, 0).unwrap().set_text("Testing");
270 inner.cell(2, 1).unwrap().set_text("$35,000");
271 }
272 }
273
274 doc.add_paragraph("");
275
276 // =========================================================================
277 // 5. Form-style table with labels
278 // =========================================================================
279 doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281 {
282 let mut tbl = doc.add_table(6, 4);
283 tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284 tbl = tbl.width_pct(100.0);
285
286 // Row 0: Full-width title
287 tbl.cell(0, 0)
288 .unwrap()
289 .set_text("Customer Registration Form");
290 tbl.cell(0, 0).unwrap().grid_span(4);
291 tbl.cell(0, 0).unwrap().shading("404040");
292
293 // Row 1: Name fields
294 tbl.cell(1, 0).unwrap().set_text("First Name");
295 tbl.cell(1, 0).unwrap().shading("E8E8E8");
296 tbl.cell(1, 1).unwrap().set_text("John");
297 tbl.cell(1, 2).unwrap().set_text("Last Name");
298 tbl.cell(1, 2).unwrap().shading("E8E8E8");
299 tbl.cell(1, 3).unwrap().set_text("Smith");
300
301 // Row 2: Contact
302 tbl.cell(2, 0).unwrap().set_text("Email");
303 tbl.cell(2, 0).unwrap().shading("E8E8E8");
304 tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305 tbl.cell(2, 1).unwrap().grid_span(3);
306
307 // Row 3: Phone
308 tbl.cell(3, 0).unwrap().set_text("Phone");
309 tbl.cell(3, 0).unwrap().shading("E8E8E8");
310 tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311 tbl.cell(3, 2).unwrap().set_text("Company");
312 tbl.cell(3, 2).unwrap().shading("E8E8E8");
313 tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315 // Row 4: Address (spanning)
316 tbl.cell(4, 0).unwrap().set_text("Address");
317 tbl.cell(4, 0).unwrap().shading("E8E8E8");
318 tbl.cell(4, 1)
319 .unwrap()
320 .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321 tbl.cell(4, 1).unwrap().grid_span(3);
322
323 // Row 5: Notes
324 tbl.cell(5, 0).unwrap().set_text("Notes");
325 tbl.cell(5, 0).unwrap().shading("E8E8E8");
326 tbl.cell(5, 0)
327 .unwrap()
328 .vertical_alignment(VerticalAlignment::Top);
329 {
330 let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331 cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332 cell.add_paragraph("Annual review scheduled for March 2026.");
333 }
334 }
335
336 doc.add_paragraph("");
337
338 // =========================================================================
339 // 6. Comparison table with border styles
340 // =========================================================================
341 doc.add_paragraph("6. Comparison Table with Custom Borders")
342 .style("Heading2");
343
344 {
345 let mut tbl = doc.add_table(5, 3);
346 tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347 tbl = tbl.width_pct(100.0);
348
349 // Header
350 tbl.cell(0, 0).unwrap().set_text("Feature");
351 tbl.cell(0, 0).unwrap().shading("2E75B6");
352 tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353 tbl.cell(0, 1).unwrap().shading("2E75B6");
354 tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355 tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357 tbl.cell(1, 0).unwrap().set_text("Users");
358 tbl.cell(1, 1).unwrap().set_text("Up to 10");
359 tbl.cell(1, 2).unwrap().set_text("Unlimited");
360 tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362 tbl.cell(2, 0).unwrap().set_text("Storage");
363 tbl.cell(2, 1).unwrap().set_text("50 GB");
364 tbl.cell(2, 2).unwrap().set_text("5 TB");
365 tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367 tbl.cell(3, 0).unwrap().set_text("Support");
368 tbl.cell(3, 1).unwrap().set_text("Email only");
369 tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370 tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372 tbl.cell(4, 0).unwrap().set_text("Price");
373 tbl.cell(4, 0).unwrap().shading("F2F2F2");
374 tbl.cell(4, 1).unwrap().set_text("$29/month");
375 tbl.cell(4, 1).unwrap().shading("F2F2F2");
376 tbl.cell(4, 2).unwrap().set_text("$199/month");
377 tbl.cell(4, 2).unwrap().shading("C6EFCE");
378 }
379
380 doc.add_paragraph("");
381
382 // =========================================================================
383 // 7. Wide table with fixed layout and row height
384 // =========================================================================
385 doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386 .style("Heading2");
387
388 {
389 let mut tbl = doc.add_table(4, 5);
390 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391 tbl = tbl.width(Length::inches(7.0));
392 tbl = tbl.layout_fixed();
393
394 // Set column widths
395 for col in 0..5 {
396 tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397 }
398
399 // Header with exact height
400 tbl.row(0).unwrap().height_exact(Length::twips(480));
401 tbl.row(0).unwrap().header();
402 tbl.row(0).unwrap().cant_split();
403
404 let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405 for (col, h) in headers.iter().enumerate() {
406 tbl.cell(0, col).unwrap().set_text(h);
407 tbl.cell(0, col).unwrap().shading("404040");
408 tbl.cell(0, col)
409 .unwrap()
410 .vertical_alignment(VerticalAlignment::Center);
411 }
412
413 // Schedule rows with minimum height
414 tbl.row(1).unwrap().height(Length::twips(600));
415 tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416 tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417 tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418 tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419 tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421 tbl.row(2).unwrap().height(Length::twips(600));
422 tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423 tbl.cell(2, 0).unwrap().shading("D6E4F0");
424 tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425 tbl.cell(2, 1).unwrap().shading("FCE4D6");
426 tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427 tbl.cell(2, 2).unwrap().shading("D6E4F0");
428 tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429 tbl.cell(2, 3).unwrap().shading("E2EFDA");
430 tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431 tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433 tbl.row(3).unwrap().height(Length::twips(600));
434 tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435 tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436 tbl.cell(3, 1).unwrap().shading("D6E4F0");
437 tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438 tbl.cell(3, 2).unwrap().shading("FCE4D6");
439 tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440 tbl.cell(3, 3).unwrap().shading("D6E4F0");
441 tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442 tbl.cell(3, 4).unwrap().shading("E2EFDA");
443 }
444
445 doc.set_title("Styled Tables Showcase");
446 doc.set_author("rdocx");
447
448 doc.save(path).unwrap();
449}More examples
examples/generate_samples.rs (line 337)
34fn generate_feature_showcase(path: &Path) {
35 let mut doc = Document::new();
36
37 // =========================================================================
38 // PAGE SETUP & METADATA
39 // =========================================================================
40 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41 doc.set_margins(
42 Length::inches(1.0), // top
43 Length::inches(1.0), // right
44 Length::inches(1.0), // bottom
45 Length::inches(1.0), // left
46 );
47 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48 doc.set_gutter(Length::twips(0));
49
50 doc.set_title("rdocx Feature Showcase");
51 doc.set_author("rdocx Sample Generator");
52 doc.set_subject("Comprehensive feature demonstration");
53 doc.set_keywords("rdocx, docx, rust, sample");
54
55 // Header & Footer
56 doc.set_header("rdocx Feature Showcase");
57 doc.set_footer("Generated by rdocx — Page");
58
59 // Different first page header
60 doc.set_different_first_page(true);
61 doc.set_first_page_header("rdocx");
62 doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64 // =========================================================================
65 // PAGE 1: COVER PAGE — background image, run formatting
66 // =========================================================================
67 let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68 doc.add_background_image(&bg_cover, "cover_bg.png");
69
70 doc.add_paragraph(""); // spacer
71 doc.add_paragraph(""); // spacer
72 doc.add_paragraph(""); // spacer
73
74 {
75 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77 }
78 {
79 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80 p.add_run("Feature Showcase")
81 .size(28.0)
82 .color("FFFFFF")
83 .italic(true);
84 }
85
86 doc.add_paragraph(""); // spacer
87
88 {
89 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90 p.add_run("A comprehensive demonstration of every feature")
91 .size(14.0)
92 .color("CCDDFF");
93 }
94 {
95 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96 p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97 .size(14.0)
98 .color("CCDDFF");
99 }
100
101 // =========================================================================
102 // PAGE 2: TEXT FORMATTING
103 // =========================================================================
104 doc.add_paragraph("").page_break_before(true);
105
106 doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108 doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109 doc.add_paragraph("");
110
111 // --- Paragraph alignment ---
112 doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114 doc.add_paragraph("This paragraph is left-aligned (the default).")
115 .alignment(Alignment::Left);
116 doc.add_paragraph("This paragraph is center-aligned.")
117 .alignment(Alignment::Center);
118 doc.add_paragraph("This paragraph is right-aligned.")
119 .alignment(Alignment::Right);
120 doc.add_paragraph(
121 "This paragraph is justified. To demonstrate justified text properly, it needs \
122 to be long enough to span multiple lines so the word spacing adjustment is visible \
123 across the full width of the text area on the page.",
124 )
125 .alignment(Alignment::Justify);
126
127 doc.add_paragraph("");
128
129 // --- Run formatting ---
130 doc.add_paragraph("Run Formatting").style("Heading2");
131
132 {
133 let mut p = doc.add_paragraph("");
134 p.add_run("Bold text").bold(true);
135 p.add_run(" | ");
136 p.add_run("Italic text").italic(true);
137 p.add_run(" | ");
138 p.add_run("Bold + Italic").bold(true).italic(true);
139 }
140 {
141 let mut p = doc.add_paragraph("");
142 p.add_run("Single underline").underline(true);
143 p.add_run(" | ");
144 p.add_run("Strikethrough").strike(true);
145 p.add_run(" | ");
146 p.add_run("Double strikethrough").double_strike(true);
147 }
148 {
149 let mut p = doc.add_paragraph("");
150 p.add_run("Red text").color("FF0000");
151 p.add_run(" | ");
152 p.add_run("Blue text").color("0000FF");
153 p.add_run(" | ");
154 p.add_run("Green text").color("00AA00");
155 p.add_run(" | ");
156 p.add_run("Highlighted").highlight("FFFF00");
157 }
158 {
159 let mut p = doc.add_paragraph("");
160 p.add_run("8pt small").size(8.0);
161 p.add_run(" | ");
162 p.add_run("11pt normal").size(11.0);
163 p.add_run(" | ");
164 p.add_run("16pt large").size(16.0);
165 p.add_run(" | ");
166 p.add_run("24pt extra-large").size(24.0);
167 }
168 {
169 let mut p = doc.add_paragraph("");
170 p.add_run("Arial font").font("Arial");
171 p.add_run(" | ");
172 p.add_run("Times New Roman font").font("Times New Roman");
173 p.add_run(" | ");
174 p.add_run("Courier New font").font("Courier New");
175 }
176 {
177 let mut p = doc.add_paragraph("");
178 p.add_run("Normal");
179 p.add_run(" H").size(11.0);
180 p.add_run("2").subscript();
181 p.add_run("O (subscript)").size(11.0);
182 p.add_run(" | E = mc").size(11.0);
183 p.add_run("2").superscript();
184 p.add_run(" (superscript)").size(11.0);
185 }
186 {
187 let mut p = doc.add_paragraph("");
188 p.add_run("ALL CAPS").all_caps(true);
189 p.add_run(" | ");
190 p.add_run("Small Caps").small_caps(true);
191 p.add_run(" | ");
192 p.add_run("Expanded spacing")
193 .character_spacing(Length::twips(40));
194 }
195
196 doc.add_paragraph("");
197
198 // --- Paragraph formatting ---
199 doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201 doc.add_paragraph("Paragraph with shading (light green background)")
202 .shading("E2EFDA");
203
204 doc.add_paragraph("Paragraph with bottom border")
205 .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207 doc.add_paragraph("Paragraph with all borders")
208 .border_all(BorderStyle::Single, 4, "FF0000");
209
210 doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211 .indent_left(Length::inches(1.0))
212 .hanging_indent(Length::inches(0.5));
213
214 doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215 .first_line_indent(Length::inches(0.5));
216
217 doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218 .space_before(Length::pt(24.0))
219 .space_after(Length::pt(12.0));
220
221 doc.add_paragraph(
222 "Paragraph with double line spacing. This text should have extra vertical \
223 space between lines to demonstrate the line_spacing_multiple setting.",
224 )
225 .line_spacing_multiple(2.0);
226
227 doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228 .keep_with_next(true);
229 doc.add_paragraph("(This stays with the paragraph above.)");
230
231 // =========================================================================
232 // PAGE 3: LISTS & TAB STOPS
233 // =========================================================================
234 doc.add_paragraph("").page_break_before(true);
235
236 doc.add_paragraph("2. Lists").style("Heading1");
237
238 doc.add_paragraph("Bullet List").style("Heading2");
239
240 doc.add_bullet_list_item("First bullet item", 0);
241 doc.add_bullet_list_item("Second bullet item", 0);
242 doc.add_bullet_list_item("Nested level 1", 1);
243 doc.add_bullet_list_item("Nested level 2", 2);
244 doc.add_bullet_list_item("Back to level 1", 1);
245 doc.add_bullet_list_item("Third bullet item", 0);
246
247 doc.add_paragraph("");
248
249 doc.add_paragraph("Numbered List").style("Heading2");
250
251 doc.add_numbered_list_item("First numbered item", 0);
252 doc.add_numbered_list_item("Second numbered item", 0);
253 doc.add_numbered_list_item("Sub-item A", 1);
254 doc.add_numbered_list_item("Sub-item B", 1);
255 doc.add_numbered_list_item("Third numbered item", 0);
256
257 doc.add_paragraph("");
258
259 // --- Tab stops ---
260 doc.add_paragraph("Tab Stops").style("Heading2");
261
262 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268 doc.add_paragraph("Item\t........\tPrice")
269 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273 doc.add_paragraph("Widget A\t........\t$19.99")
274 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278 doc.add_paragraph("Gadget B\t________\t$249.50")
279 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280 .add_tab_stop_with_leader(
281 TabAlignment::Right,
282 Length::inches(4.0),
283 TabLeader::Underscore,
284 )
285 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287 // =========================================================================
288 // PAGE 4: TABLES
289 // =========================================================================
290 doc.add_paragraph("").page_break_before(true);
291
292 doc.add_paragraph("3. Tables").style("Heading1");
293
294 // --- Basic table with borders ---
295 doc.add_paragraph("Basic Table with Borders")
296 .style("Heading2");
297
298 {
299 let mut tbl = doc.add_table(4, 3);
300 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302 // Header row
303 for col in 0..3 {
304 tbl.cell(0, col).unwrap().shading("2E75B6");
305 }
306 tbl.cell(0, 0).unwrap().set_text("Name");
307 tbl.cell(0, 1).unwrap().set_text("Role");
308 tbl.cell(0, 2).unwrap().set_text("Location");
309
310 tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311 tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312 tbl.cell(1, 2).unwrap().set_text("New York");
313
314 tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315 tbl.cell(2, 1).unwrap().set_text("Product Manager");
316 tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318 tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319 tbl.cell(3, 1).unwrap().set_text("Designer");
320 tbl.cell(3, 2).unwrap().set_text("London");
321 }
322
323 doc.add_paragraph("");
324
325 // --- Table with cell merging ---
326 doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327 .style("Heading2");
328
329 {
330 let mut tbl = doc.add_table(4, 4);
331 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332 tbl = tbl.width_pct(100.0);
333
334 // Header spanning all columns
335 tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336 tbl.cell(0, 0).unwrap().shading("1F4E79");
337 tbl.cell(0, 0).unwrap().grid_span(4);
338
339 // Sub-header
340 tbl.cell(1, 0).unwrap().set_text("Region");
341 tbl.cell(1, 0).unwrap().shading("D6E4F0");
342 tbl.cell(1, 1).unwrap().set_text("Q1");
343 tbl.cell(1, 1).unwrap().shading("D6E4F0");
344 tbl.cell(1, 2).unwrap().set_text("Q2");
345 tbl.cell(1, 2).unwrap().shading("D6E4F0");
346 tbl.cell(1, 3).unwrap().set_text("Total");
347 tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349 // Data
350 tbl.cell(2, 0).unwrap().set_text("North America");
351 tbl.cell(2, 1).unwrap().set_text("$2.4M");
352 tbl.cell(2, 2).unwrap().set_text("$2.7M");
353 tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355 tbl.cell(3, 0).unwrap().set_text("Europe");
356 tbl.cell(3, 1).unwrap().set_text("$1.8M");
357 tbl.cell(3, 2).unwrap().set_text("$2.0M");
358 tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360 // Vertical alignment on data cells
361 tbl.cell(2, 3)
362 .unwrap()
363 .vertical_alignment(VerticalAlignment::Center);
364 tbl.cell(3, 3)
365 .unwrap()
366 .vertical_alignment(VerticalAlignment::Bottom);
367 }
368
369 doc.add_paragraph("");
370
371 // --- Table with vertical merge ---
372 doc.add_paragraph("Table with Vertical Merge")
373 .style("Heading2");
374
375 {
376 let mut tbl = doc.add_table(4, 3);
377 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379 tbl.cell(0, 0).unwrap().set_text("Category");
380 tbl.cell(0, 0).unwrap().shading("E2EFDA");
381 tbl.cell(0, 1).unwrap().set_text("Item");
382 tbl.cell(0, 1).unwrap().shading("E2EFDA");
383 tbl.cell(0, 2).unwrap().set_text("Price");
384 tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386 // "Hardware" spans rows 1-2
387 tbl.cell(1, 0).unwrap().set_text("Hardware");
388 tbl.cell(1, 0).unwrap().v_merge_restart();
389 tbl.cell(1, 1).unwrap().set_text("Laptop");
390 tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392 tbl.cell(2, 0).unwrap().v_merge_continue();
393 tbl.cell(2, 1).unwrap().set_text("Monitor");
394 tbl.cell(2, 2).unwrap().set_text("$450");
395
396 // "Software" on row 3
397 tbl.cell(3, 0).unwrap().set_text("Software");
398 tbl.cell(3, 1).unwrap().set_text("IDE License");
399 tbl.cell(3, 2).unwrap().set_text("$200/yr");
400 }
401
402 doc.add_paragraph("");
403
404 // --- Nested table ---
405 doc.add_paragraph("Nested Table").style("Heading2");
406
407 {
408 let mut tbl = doc.add_table(2, 2);
409 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411 tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412 tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413 tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415 // Nested table inside cell (1,1)
416 {
417 let mut cell = tbl.cell(1, 1).unwrap();
418 cell.set_text("Contains nested table:");
419 let mut nested = cell.add_table(2, 2);
420 nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421 nested.cell(0, 0).unwrap().set_text("Inner A");
422 nested.cell(0, 1).unwrap().set_text("Inner B");
423 nested.cell(1, 0).unwrap().set_text("Inner C");
424 nested.cell(1, 1).unwrap().set_text("Inner D");
425 }
426 }
427
428 // =========================================================================
429 // PAGE 5: IMAGES
430 // =========================================================================
431 doc.add_paragraph("").page_break_before(true);
432
433 doc.add_paragraph("4. Images").style("Heading1");
434
435 doc.add_paragraph("Inline Image").style("Heading2");
436
437 doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438 let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439 doc.add_picture(
440 &inline_img,
441 "inline_chart.png",
442 Length::inches(3.0),
443 Length::inches(0.75),
444 );
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("Header Image").style("Heading2");
449
450 // Replace the text-only header with an image header
451 let header_img = create_sample_png(400, 40, [40, 40, 40]);
452 doc.set_header_image(
453 &header_img,
454 "header_logo.png",
455 Length::inches(2.0),
456 Length::inches(0.2),
457 );
458
459 doc.add_paragraph(
460 "The document header has been replaced with an inline image. \
461 Check the header area at the top of this page.",
462 );
463
464 doc.add_paragraph("");
465 doc.add_paragraph(
466 "Note: The cover page uses a full-page background image behind the text, \
467 demonstrated on page 1 via add_background_image().",
468 );
469
470 // =========================================================================
471 // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472 // =========================================================================
473 doc.add_paragraph("").page_break_before(true);
474
475 doc.add_paragraph("5. Content Manipulation")
476 .style("Heading1");
477
478 // --- Placeholder replacement ---
479 doc.add_paragraph("Placeholder Replacement")
480 .style("Heading2");
481
482 doc.add_paragraph(
483 "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484 );
485
486 {
487 let mut p = doc.add_paragraph("");
488 p.add_run("Customer: ").bold(true);
489 p.add_run("{{customer}}");
490 }
491 {
492 let mut p = doc.add_paragraph("");
493 p.add_run("Date: ").bold(true);
494 p.add_run("{{date}}");
495 }
496 doc.add_paragraph("Reference: {{ref_number}}");
497
498 // Table with placeholders
499 {
500 let mut tbl = doc.add_table(3, 2);
501 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502 tbl.cell(0, 0).unwrap().set_text("Field");
503 tbl.cell(0, 0).unwrap().shading("D6E4F0");
504 tbl.cell(0, 1).unwrap().set_text("Value");
505 tbl.cell(0, 1).unwrap().shading("D6E4F0");
506 tbl.cell(1, 0).unwrap().set_text("Project");
507 tbl.cell(1, 1).unwrap().set_text("{{project}}");
508 tbl.cell(2, 0).unwrap().set_text("Status");
509 tbl.cell(2, 1).unwrap().set_text("{{status}}");
510 }
511
512 // Perform replacements
513 let mut replacements = HashMap::new();
514 replacements.insert("{{customer}}", "Acme Corporation");
515 replacements.insert("{{date}}", "February 22, 2026");
516 replacements.insert("{{ref_number}}", "REF-2026-001");
517 replacements.insert("{{project}}", "Infrastructure Upgrade");
518 replacements.insert("{{status}}", "In Progress");
519 let replace_count = doc.replace_all(&replacements);
520
521 doc.add_paragraph("");
522 doc.add_paragraph(&format!(
523 "(Replaced {} placeholders above — in body text and table cells)",
524 replace_count
525 ));
526
527 doc.add_paragraph("");
528
529 // --- Content insertion ---
530 doc.add_paragraph("Content Insertion").style("Heading2");
531
532 doc.add_paragraph("Section A: First section of content.");
533 doc.add_paragraph("Section C: Third section of content.");
534
535 // Insert "Section B" between A and C
536 if let Some(idx) = doc.find_content_index("Section C") {
537 doc.insert_paragraph(
538 idx,
539 "Section B: Inserted between A and C using find_content_index().",
540 );
541 }
542
543 doc.add_paragraph("");
544 doc.add_paragraph(
545 "The paragraph above ('Section B') was inserted at a specific position \
546 using find_content_index() + insert_paragraph().",
547 );
548
549 // =========================================================================
550 // PAGE 7: LANDSCAPE — section break, wide table
551 // =========================================================================
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554 doc.add_paragraph("6. Mixed Page Orientation")
555 .style("Heading1");
556
557 doc.add_paragraph(
558 "This page is in LANDSCAPE orientation. It was created using a section break \
559 followed by section_landscape(). This is useful for wide tables or charts.",
560 );
561
562 doc.add_paragraph("");
563
564 // Wide table for landscape
565 {
566 let mut tbl = doc.add_table(4, 7);
567 tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570 for (col, h) in headers.iter().enumerate() {
571 tbl.cell(0, col).unwrap().set_text(h);
572 tbl.cell(0, col).unwrap().shading("2E75B6");
573 }
574
575 let data = [
576 [
577 "North America",
578 "$1.2M",
579 "$1.3M",
580 "$1.4M",
581 "$1.5M",
582 "$1.6M",
583 "$7.0M",
584 ],
585 [
586 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587 ],
588 [
589 "Asia Pacific",
590 "$0.5M",
591 "$0.6M",
592 "$0.7M",
593 "$0.7M",
594 "$0.8M",
595 "$3.3M",
596 ],
597 ];
598 for (row_idx, row_data) in data.iter().enumerate() {
599 for (col, val) in row_data.iter().enumerate() {
600 tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601 }
602 }
603 }
604
605 // End landscape, return to portrait
606 doc.add_paragraph("")
607 .section_break(SectionBreak::NextPage)
608 .section_landscape();
609
610 // =========================================================================
611 // PAGE 8: BACK TO PORTRAIT — styles, final notes
612 // =========================================================================
613 doc.add_paragraph("7. Custom Styles & Summary")
614 .style("Heading1");
615
616 doc.add_paragraph(
617 "This final page is back in portrait orientation after a section break. \
618 The document has demonstrated:",
619 );
620
621 doc.add_bullet_list_item(
622 "Page setup: size, margins, header/footer distance, gutter",
623 0,
624 );
625 doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626 doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627 doc.add_bullet_list_item("Background images: full-page behind text", 0);
628 doc.add_bullet_list_item(
629 "Text formatting: bold, italic, underline, strike, color, size, font",
630 0,
631 );
632 doc.add_bullet_list_item(
633 "Advanced run formatting: superscript, subscript, caps, spacing",
634 0,
635 );
636 doc.add_bullet_list_item(
637 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638 0,
639 );
640 doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641 doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642 doc.add_bullet_list_item(
643 "Tables: borders, shading, column spans, row spans, nesting",
644 0,
645 );
646 doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647 doc.add_bullet_list_item("Inline images", 0);
648 doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649 doc.add_bullet_list_item("Content insertion at specific positions", 0);
650 doc.add_bullet_list_item(
651 "Section breaks with mixed portrait/landscape orientation",
652 0,
653 );
654
655 doc.add_paragraph("");
656 doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657 .alignment(Alignment::Center)
658 .shading("E2EFDA")
659 .border_all(BorderStyle::Single, 2, "00AA00");
660
661 doc.save(path).unwrap();
662}examples/generate_all_samples.rs (line 383)
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}
954
955// =============================================================================
956// 3. QUOTE / BILL OF MATERIALS — Teal + orange scheme
957// =============================================================================
958fn generate_quote(_samples_dir: &Path) -> Document {
959 let mut doc = Document::new();
960
961 // Colors: Teal #008B8B, Dark #1A3C3C, Orange #E07020, Light #F0F8F8
962 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
963 doc.set_margins(
964 Length::inches(0.75),
965 Length::inches(0.75),
966 Length::inches(0.75),
967 Length::inches(0.75),
968 );
969 doc.set_title("Quotation QT-2026-0147");
970 doc.set_author("Walter White");
971 doc.set_keywords("quote, BOM, Tensorbee");
972
973 doc.set_header("Tensorbee — Quotation");
974 doc.set_footer("QT-2026-0147 | Page");
975
976 // ── Header Block ──
977 {
978 let mut p = doc.add_paragraph("").alignment(Alignment::Left);
979 p.add_run("TENSORBEE")
980 .bold(true)
981 .size(28.0)
982 .color("008B8B")
983 .font("Helvetica");
984 }
985 doc.add_paragraph("123 Innovation Drive, Suite 400")
986 .alignment(Alignment::Left);
987 doc.add_paragraph("San Francisco, CA 94105 | +1 (415) 555-0199")
988 .alignment(Alignment::Left);
989 doc.add_paragraph("")
990 .border_bottom(BorderStyle::Single, 8, "008B8B");
991
992 // Quote meta
993 doc.add_paragraph("");
994 {
995 let mut tbl = doc.add_table(4, 4).width_pct(100.0);
996 tbl.cell(0, 0).unwrap().set_text("QUOTATION");
997 tbl.cell(0, 0).unwrap().shading("008B8B").grid_span(2);
998 tbl.cell(0, 2).unwrap().set_text("DATE");
999 tbl.cell(0, 2).unwrap().shading("008B8B");
1000 tbl.cell(0, 3).unwrap().set_text("VALID UNTIL");
1001 tbl.cell(0, 3).unwrap().shading("008B8B");
1002 tbl.cell(1, 0).unwrap().set_text("Quote #:");
1003 tbl.cell(1, 1).unwrap().set_text("QT-2026-0147");
1004 tbl.cell(1, 2).unwrap().set_text("Feb 22, 2026");
1005 tbl.cell(1, 3).unwrap().set_text("Mar 22, 2026");
1006 tbl.cell(2, 0).unwrap().set_text("Prepared by:");
1007 tbl.cell(2, 1).unwrap().set_text("Walter White");
1008 tbl.cell(2, 2).unwrap().set_text("Payment:");
1009 tbl.cell(2, 3).unwrap().set_text("Net 30");
1010 tbl.cell(3, 0).unwrap().set_text("Customer:");
1011 tbl.cell(3, 1).unwrap().set_text("Meridian Dynamics LLC");
1012 tbl.cell(3, 2).unwrap().set_text("Currency:");
1013 tbl.cell(3, 3).unwrap().set_text("USD");
1014 }
1015
1016 doc.add_paragraph("");
1017
1018 // ── Bill of Materials ──
1019 {
1020 let mut p = doc.add_paragraph("");
1021 p.add_run("Bill of Materials")
1022 .bold(true)
1023 .size(16.0)
1024 .color("1A3C3C");
1025 }
1026 doc.add_paragraph("");
1027
1028 {
1029 let mut tbl = doc
1030 .add_table(10, 6)
1031 .borders(BorderStyle::Single, 2, "008B8B")
1032 .width_pct(100.0)
1033 .layout_fixed();
1034 // Headers
1035 let hdrs = [
1036 "#",
1037 "Part Number",
1038 "Description",
1039 "Qty",
1040 "Unit Price",
1041 "Total",
1042 ];
1043 for (c, h) in hdrs.iter().enumerate() {
1044 tbl.cell(0, c).unwrap().set_text(h);
1045 tbl.cell(0, c).unwrap().shading("008B8B");
1046 }
1047
1048 let items: Vec<(&str, &str, &str, &str, &str)> = vec![
1049 (
1050 "TB-GPU-A100",
1051 "NVIDIA A100 80GB GPU",
1052 "4",
1053 "$12,500.00",
1054 "$50,000.00",
1055 ),
1056 (
1057 "TB-SRV-R750",
1058 "Dell R750xa Server Chassis",
1059 "2",
1060 "$8,200.00",
1061 "$16,400.00",
1062 ),
1063 (
1064 "TB-RAM-256G",
1065 "256GB DDR5 ECC Memory Module",
1066 "8",
1067 "$890.00",
1068 "$7,120.00",
1069 ),
1070 (
1071 "TB-SSD-3840",
1072 "3.84TB NVMe U.2 SSD",
1073 "8",
1074 "$1,150.00",
1075 "$9,200.00",
1076 ),
1077 (
1078 "TB-NET-CX7",
1079 "ConnectX-7 200GbE NIC",
1080 "4",
1081 "$1,800.00",
1082 "$7,200.00",
1083 ),
1084 (
1085 "TB-SW-48P",
1086 "48-Port 100GbE Switch",
1087 "1",
1088 "$22,000.00",
1089 "$22,000.00",
1090 ),
1091 (
1092 "TB-CAB-RACK",
1093 "42U Server Rack + PDU",
1094 "1",
1095 "$4,500.00",
1096 "$4,500.00",
1097 ),
1098 (
1099 "TB-SVC-INST",
1100 "Installation & Configuration",
1101 "1",
1102 "$8,500.00",
1103 "$8,500.00",
1104 ),
1105 (
1106 "TB-SVC-SUPP",
1107 "3-Year Premium Support",
1108 "1",
1109 "$15,000.00",
1110 "$15,000.00",
1111 ),
1112 ];
1113
1114 for (i, (pn, desc, qty, unit, total)) in items.iter().enumerate() {
1115 let row = i + 1;
1116 tbl.cell(row, 0).unwrap().set_text(&format!("{}", i + 1));
1117 tbl.cell(row, 1).unwrap().set_text(pn);
1118 tbl.cell(row, 2).unwrap().set_text(desc);
1119 tbl.cell(row, 3).unwrap().set_text(qty);
1120 tbl.cell(row, 4).unwrap().set_text(unit);
1121 tbl.cell(row, 5).unwrap().set_text(total);
1122 if i % 2 == 0 {
1123 for c in 0..6 {
1124 tbl.cell(row, c).unwrap().shading("F0F8F8");
1125 }
1126 }
1127 }
1128 }
1129
1130 doc.add_paragraph("");
1131
1132 // ── Totals ──
1133 {
1134 let mut tbl = doc
1135 .add_table(4, 2)
1136 .borders(BorderStyle::Single, 2, "008B8B")
1137 .width(Length::inches(3.5))
1138 .alignment(Alignment::Right);
1139 tbl.cell(0, 0).unwrap().set_text("Subtotal");
1140 tbl.cell(0, 1).unwrap().set_text("$139,920.00");
1141 tbl.cell(1, 0).unwrap().set_text("Shipping & Handling");
1142 tbl.cell(1, 1).unwrap().set_text("$2,500.00");
1143 tbl.cell(2, 0).unwrap().set_text("Tax (8.625%)");
1144 tbl.cell(2, 1).unwrap().set_text("$12,068.10");
1145 tbl.cell(3, 0).unwrap().set_text("TOTAL");
1146 tbl.cell(3, 0).unwrap().shading("E07020");
1147 tbl.cell(3, 1).unwrap().set_text("$154,488.10");
1148 tbl.cell(3, 1).unwrap().shading("E07020");
1149 }
1150
1151 doc.add_paragraph("");
1152
1153 // ── Terms & Conditions ──
1154 {
1155 let mut p = doc.add_paragraph("");
1156 p.add_run("Terms & Conditions")
1157 .bold(true)
1158 .size(14.0)
1159 .color("1A3C3C");
1160 }
1161 doc.add_numbered_list_item(
1162 "This quotation is valid for 30 calendar days from the date of issue.",
1163 0,
1164 );
1165 doc.add_numbered_list_item(
1166 "All prices are in USD and exclusive of applicable taxes unless stated.",
1167 0,
1168 );
1169 doc.add_numbered_list_item("Standard lead time is 4-6 weeks from PO receipt.", 0);
1170 doc.add_numbered_list_item(
1171 "Payment terms: Net 30 from invoice date. 2% discount for payment within 10 days.",
1172 0,
1173 );
1174 doc.add_numbered_list_item(
1175 "Warranty: 3-year manufacturer warranty on all hardware components.",
1176 0,
1177 );
1178 doc.add_numbered_list_item(
1179 "Returns subject to 15% restocking fee if initiated after 14 days.",
1180 0,
1181 );
1182
1183 doc.add_paragraph("");
1184 doc.add_paragraph("")
1185 .border_bottom(BorderStyle::Single, 4, "008B8B");
1186 doc.add_paragraph("");
1187 {
1188 let mut p = doc.add_paragraph("");
1189 p.add_run("Accepted by: ").bold(true);
1190 p.add_run("___________________________ Date: ___________");
1191 }
1192 {
1193 let mut p = doc.add_paragraph("");
1194 p.add_run("Print Name: ").bold(true);
1195 p.add_run("___________________________ Title: ___________");
1196 }
1197
1198 doc
1199}Sourcepub fn v_merge_restart(self) -> Self
pub fn v_merge_restart(self) -> Self
Start a vertical merge group (this cell is the top of the merged range).
Examples found in repository?
examples/styled_tables.rs (line 173)
24fn generate_styled_tables(path: &Path) {
25 let mut doc = Document::new();
26 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27 doc.set_margins(
28 Length::inches(0.75),
29 Length::inches(0.75),
30 Length::inches(0.75),
31 Length::inches(0.75),
32 );
33
34 doc.add_paragraph("Styled Tables Showcase")
35 .style("Heading1");
36
37 doc.add_paragraph("");
38
39 // =========================================================================
40 // 1. Professional report table with alternating rows
41 // =========================================================================
42 doc.add_paragraph("1. Report Table with Alternating Row Colors")
43 .style("Heading2");
44
45 {
46 let mut tbl = doc.add_table(8, 4);
47 tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48 tbl = tbl.width_pct(100.0);
49
50 // Header row
51 let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52 for (col, h) in headers.iter().enumerate() {
53 tbl.cell(0, col).unwrap().shading("2E75B6");
54 tbl.cell(0, col).unwrap().set_text(h);
55 }
56 tbl.row(0).unwrap().header();
57
58 // Data with alternating shading
59 let data = [
60 ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61 ["Professional", "$189,000", "$201,000", "+6.3%"],
62 ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63 ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64 ["Training", "$22,000", "$28,900", "+31.4%"],
65 ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66 ];
67
68 for (i, row) in data.iter().enumerate() {
69 let row_idx = i + 1;
70 for (col, val) in row.iter().enumerate() {
71 tbl.cell(row_idx, col).unwrap().set_text(val);
72 // Alternate row colors
73 if i % 2 == 0 {
74 tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75 }
76 }
77 }
78
79 // Total row
80 tbl.cell(7, 0).unwrap().set_text("TOTAL");
81 tbl.cell(7, 0).unwrap().shading("D6E4F0");
82 tbl.cell(7, 1).unwrap().set_text("$613,000");
83 tbl.cell(7, 1).unwrap().shading("D6E4F0");
84 tbl.cell(7, 2).unwrap().set_text("$729,900");
85 tbl.cell(7, 2).unwrap().shading("D6E4F0");
86 tbl.cell(7, 3).unwrap().set_text("+19.1%");
87 tbl.cell(7, 3).unwrap().shading("D6E4F0");
88 }
89
90 doc.add_paragraph("");
91
92 // =========================================================================
93 // 2. Invoice-style table with merged header
94 // =========================================================================
95 doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96 .style("Heading2");
97
98 {
99 let mut tbl = doc.add_table(7, 4);
100 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101 tbl = tbl.width_pct(100.0);
102
103 // Merged title row
104 tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105 tbl.cell(0, 0).unwrap().grid_span(4);
106 tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108 // Column headers
109 let headers = ["Item", "Description", "Qty", "Amount"];
110 for (col, h) in headers.iter().enumerate() {
111 tbl.cell(1, col).unwrap().set_text(h);
112 tbl.cell(1, col).unwrap().shading("D6E4F0");
113 }
114
115 // Line items
116 tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117 tbl.cell(2, 1)
118 .unwrap()
119 .set_text("Enterprise License (500 seats)");
120 tbl.cell(2, 2).unwrap().set_text("1");
121 tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123 tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124 tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125 tbl.cell(3, 2).unwrap().set_text("1");
126 tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128 tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129 tbl.cell(4, 1)
130 .unwrap()
131 .set_text("On-site Training (3 days)");
132 tbl.cell(4, 2).unwrap().set_text("1");
133 tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135 // Subtotal
136 tbl.cell(5, 0).unwrap().set_text("Subtotal");
137 tbl.cell(5, 0).unwrap().grid_span(3);
138 tbl.cell(5, 0).unwrap().shading("F2F2F2");
139 tbl.cell(5, 3).unwrap().set_text("$89,500");
140 tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142 // Total
143 tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144 tbl.cell(6, 0).unwrap().grid_span(3);
145 tbl.cell(6, 0).unwrap().shading("1F4E79");
146 tbl.cell(6, 3).unwrap().set_text("$89,500");
147 tbl.cell(6, 3).unwrap().shading("1F4E79");
148 }
149
150 doc.add_paragraph("");
151
152 // =========================================================================
153 // 3. Specification table with vertical merge
154 // =========================================================================
155 doc.add_paragraph("3. Specification Table with Vertical Merges")
156 .style("Heading2");
157
158 {
159 let mut tbl = doc.add_table(8, 3);
160 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161 tbl = tbl.width_pct(100.0);
162
163 // Header
164 tbl.cell(0, 0).unwrap().set_text("Category");
165 tbl.cell(0, 0).unwrap().shading("2E75B6");
166 tbl.cell(0, 1).unwrap().set_text("Specification");
167 tbl.cell(0, 1).unwrap().shading("2E75B6");
168 tbl.cell(0, 2).unwrap().set_text("Value");
169 tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171 // "Hardware" spans 3 rows
172 tbl.cell(1, 0).unwrap().set_text("Hardware");
173 tbl.cell(1, 0).unwrap().v_merge_restart();
174 tbl.cell(1, 0).unwrap().shading("E2EFDA");
175 tbl.cell(1, 0)
176 .unwrap()
177 .vertical_alignment(VerticalAlignment::Center);
178 tbl.cell(1, 1).unwrap().set_text("Processor");
179 tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181 tbl.cell(2, 0).unwrap().v_merge_continue();
182 tbl.cell(2, 1).unwrap().set_text("Memory");
183 tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185 tbl.cell(3, 0).unwrap().v_merge_continue();
186 tbl.cell(3, 1).unwrap().set_text("Storage");
187 tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189 // "Network" spans 2 rows
190 tbl.cell(4, 0).unwrap().set_text("Network");
191 tbl.cell(4, 0).unwrap().v_merge_restart();
192 tbl.cell(4, 0).unwrap().shading("FCE4D6");
193 tbl.cell(4, 0)
194 .unwrap()
195 .vertical_alignment(VerticalAlignment::Center);
196 tbl.cell(4, 1).unwrap().set_text("Ethernet");
197 tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199 tbl.cell(5, 0).unwrap().v_merge_continue();
200 tbl.cell(5, 1).unwrap().set_text("Management");
201 tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203 // "Software" spans 2 rows
204 tbl.cell(6, 0).unwrap().set_text("Software");
205 tbl.cell(6, 0).unwrap().v_merge_restart();
206 tbl.cell(6, 0).unwrap().shading("D6E4F0");
207 tbl.cell(6, 0)
208 .unwrap()
209 .vertical_alignment(VerticalAlignment::Center);
210 tbl.cell(6, 1).unwrap().set_text("Operating System");
211 tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213 tbl.cell(7, 0).unwrap().v_merge_continue();
214 tbl.cell(7, 1).unwrap().set_text("Monitoring");
215 tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216 }
217
218 doc.add_paragraph("");
219
220 // =========================================================================
221 // 4. Nested table (table inside a cell)
222 // =========================================================================
223 doc.add_paragraph("4. Nested Table").style("Heading2");
224
225 {
226 let mut tbl = doc.add_table(2, 2);
227 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228 tbl = tbl.width_pct(100.0);
229 tbl = tbl.cell_margins(
230 Length::twips(72),
231 Length::twips(108),
232 Length::twips(72),
233 Length::twips(108),
234 );
235
236 tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237 tbl.cell(0, 0).unwrap().shading("2E75B6");
238 tbl.cell(0, 1).unwrap().set_text("Project Beta");
239 tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241 // Nested table in cell (1,0)
242 {
243 let mut cell = tbl.cell(1, 0).unwrap();
244 cell.set_text("Milestones:");
245 let mut inner = cell.add_table(3, 2);
246 inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247 inner.cell(0, 0).unwrap().set_text("Phase");
248 inner.cell(0, 0).unwrap().shading("E2EFDA");
249 inner.cell(0, 1).unwrap().set_text("Status");
250 inner.cell(0, 1).unwrap().shading("E2EFDA");
251 inner.cell(1, 0).unwrap().set_text("Design");
252 inner.cell(1, 1).unwrap().set_text("Complete");
253 inner.cell(2, 0).unwrap().set_text("Build");
254 inner.cell(2, 1).unwrap().set_text("In Progress");
255 }
256
257 // Nested table in cell (1,1)
258 {
259 let mut cell = tbl.cell(1, 1).unwrap();
260 cell.set_text("Budget:");
261 let mut inner = cell.add_table(3, 2);
262 inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263 inner.cell(0, 0).unwrap().set_text("Category");
264 inner.cell(0, 0).unwrap().shading("FCE4D6");
265 inner.cell(0, 1).unwrap().set_text("Amount");
266 inner.cell(0, 1).unwrap().shading("FCE4D6");
267 inner.cell(1, 0).unwrap().set_text("Development");
268 inner.cell(1, 1).unwrap().set_text("$120,000");
269 inner.cell(2, 0).unwrap().set_text("Testing");
270 inner.cell(2, 1).unwrap().set_text("$35,000");
271 }
272 }
273
274 doc.add_paragraph("");
275
276 // =========================================================================
277 // 5. Form-style table with labels
278 // =========================================================================
279 doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281 {
282 let mut tbl = doc.add_table(6, 4);
283 tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284 tbl = tbl.width_pct(100.0);
285
286 // Row 0: Full-width title
287 tbl.cell(0, 0)
288 .unwrap()
289 .set_text("Customer Registration Form");
290 tbl.cell(0, 0).unwrap().grid_span(4);
291 tbl.cell(0, 0).unwrap().shading("404040");
292
293 // Row 1: Name fields
294 tbl.cell(1, 0).unwrap().set_text("First Name");
295 tbl.cell(1, 0).unwrap().shading("E8E8E8");
296 tbl.cell(1, 1).unwrap().set_text("John");
297 tbl.cell(1, 2).unwrap().set_text("Last Name");
298 tbl.cell(1, 2).unwrap().shading("E8E8E8");
299 tbl.cell(1, 3).unwrap().set_text("Smith");
300
301 // Row 2: Contact
302 tbl.cell(2, 0).unwrap().set_text("Email");
303 tbl.cell(2, 0).unwrap().shading("E8E8E8");
304 tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305 tbl.cell(2, 1).unwrap().grid_span(3);
306
307 // Row 3: Phone
308 tbl.cell(3, 0).unwrap().set_text("Phone");
309 tbl.cell(3, 0).unwrap().shading("E8E8E8");
310 tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311 tbl.cell(3, 2).unwrap().set_text("Company");
312 tbl.cell(3, 2).unwrap().shading("E8E8E8");
313 tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315 // Row 4: Address (spanning)
316 tbl.cell(4, 0).unwrap().set_text("Address");
317 tbl.cell(4, 0).unwrap().shading("E8E8E8");
318 tbl.cell(4, 1)
319 .unwrap()
320 .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321 tbl.cell(4, 1).unwrap().grid_span(3);
322
323 // Row 5: Notes
324 tbl.cell(5, 0).unwrap().set_text("Notes");
325 tbl.cell(5, 0).unwrap().shading("E8E8E8");
326 tbl.cell(5, 0)
327 .unwrap()
328 .vertical_alignment(VerticalAlignment::Top);
329 {
330 let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331 cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332 cell.add_paragraph("Annual review scheduled for March 2026.");
333 }
334 }
335
336 doc.add_paragraph("");
337
338 // =========================================================================
339 // 6. Comparison table with border styles
340 // =========================================================================
341 doc.add_paragraph("6. Comparison Table with Custom Borders")
342 .style("Heading2");
343
344 {
345 let mut tbl = doc.add_table(5, 3);
346 tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347 tbl = tbl.width_pct(100.0);
348
349 // Header
350 tbl.cell(0, 0).unwrap().set_text("Feature");
351 tbl.cell(0, 0).unwrap().shading("2E75B6");
352 tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353 tbl.cell(0, 1).unwrap().shading("2E75B6");
354 tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355 tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357 tbl.cell(1, 0).unwrap().set_text("Users");
358 tbl.cell(1, 1).unwrap().set_text("Up to 10");
359 tbl.cell(1, 2).unwrap().set_text("Unlimited");
360 tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362 tbl.cell(2, 0).unwrap().set_text("Storage");
363 tbl.cell(2, 1).unwrap().set_text("50 GB");
364 tbl.cell(2, 2).unwrap().set_text("5 TB");
365 tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367 tbl.cell(3, 0).unwrap().set_text("Support");
368 tbl.cell(3, 1).unwrap().set_text("Email only");
369 tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370 tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372 tbl.cell(4, 0).unwrap().set_text("Price");
373 tbl.cell(4, 0).unwrap().shading("F2F2F2");
374 tbl.cell(4, 1).unwrap().set_text("$29/month");
375 tbl.cell(4, 1).unwrap().shading("F2F2F2");
376 tbl.cell(4, 2).unwrap().set_text("$199/month");
377 tbl.cell(4, 2).unwrap().shading("C6EFCE");
378 }
379
380 doc.add_paragraph("");
381
382 // =========================================================================
383 // 7. Wide table with fixed layout and row height
384 // =========================================================================
385 doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386 .style("Heading2");
387
388 {
389 let mut tbl = doc.add_table(4, 5);
390 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391 tbl = tbl.width(Length::inches(7.0));
392 tbl = tbl.layout_fixed();
393
394 // Set column widths
395 for col in 0..5 {
396 tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397 }
398
399 // Header with exact height
400 tbl.row(0).unwrap().height_exact(Length::twips(480));
401 tbl.row(0).unwrap().header();
402 tbl.row(0).unwrap().cant_split();
403
404 let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405 for (col, h) in headers.iter().enumerate() {
406 tbl.cell(0, col).unwrap().set_text(h);
407 tbl.cell(0, col).unwrap().shading("404040");
408 tbl.cell(0, col)
409 .unwrap()
410 .vertical_alignment(VerticalAlignment::Center);
411 }
412
413 // Schedule rows with minimum height
414 tbl.row(1).unwrap().height(Length::twips(600));
415 tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416 tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417 tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418 tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419 tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421 tbl.row(2).unwrap().height(Length::twips(600));
422 tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423 tbl.cell(2, 0).unwrap().shading("D6E4F0");
424 tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425 tbl.cell(2, 1).unwrap().shading("FCE4D6");
426 tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427 tbl.cell(2, 2).unwrap().shading("D6E4F0");
428 tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429 tbl.cell(2, 3).unwrap().shading("E2EFDA");
430 tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431 tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433 tbl.row(3).unwrap().height(Length::twips(600));
434 tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435 tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436 tbl.cell(3, 1).unwrap().shading("D6E4F0");
437 tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438 tbl.cell(3, 2).unwrap().shading("FCE4D6");
439 tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440 tbl.cell(3, 3).unwrap().shading("D6E4F0");
441 tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442 tbl.cell(3, 4).unwrap().shading("E2EFDA");
443 }
444
445 doc.set_title("Styled Tables Showcase");
446 doc.set_author("rdocx");
447
448 doc.save(path).unwrap();
449}More examples
examples/generate_samples.rs (line 388)
34fn generate_feature_showcase(path: &Path) {
35 let mut doc = Document::new();
36
37 // =========================================================================
38 // PAGE SETUP & METADATA
39 // =========================================================================
40 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41 doc.set_margins(
42 Length::inches(1.0), // top
43 Length::inches(1.0), // right
44 Length::inches(1.0), // bottom
45 Length::inches(1.0), // left
46 );
47 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48 doc.set_gutter(Length::twips(0));
49
50 doc.set_title("rdocx Feature Showcase");
51 doc.set_author("rdocx Sample Generator");
52 doc.set_subject("Comprehensive feature demonstration");
53 doc.set_keywords("rdocx, docx, rust, sample");
54
55 // Header & Footer
56 doc.set_header("rdocx Feature Showcase");
57 doc.set_footer("Generated by rdocx — Page");
58
59 // Different first page header
60 doc.set_different_first_page(true);
61 doc.set_first_page_header("rdocx");
62 doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64 // =========================================================================
65 // PAGE 1: COVER PAGE — background image, run formatting
66 // =========================================================================
67 let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68 doc.add_background_image(&bg_cover, "cover_bg.png");
69
70 doc.add_paragraph(""); // spacer
71 doc.add_paragraph(""); // spacer
72 doc.add_paragraph(""); // spacer
73
74 {
75 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77 }
78 {
79 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80 p.add_run("Feature Showcase")
81 .size(28.0)
82 .color("FFFFFF")
83 .italic(true);
84 }
85
86 doc.add_paragraph(""); // spacer
87
88 {
89 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90 p.add_run("A comprehensive demonstration of every feature")
91 .size(14.0)
92 .color("CCDDFF");
93 }
94 {
95 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96 p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97 .size(14.0)
98 .color("CCDDFF");
99 }
100
101 // =========================================================================
102 // PAGE 2: TEXT FORMATTING
103 // =========================================================================
104 doc.add_paragraph("").page_break_before(true);
105
106 doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108 doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109 doc.add_paragraph("");
110
111 // --- Paragraph alignment ---
112 doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114 doc.add_paragraph("This paragraph is left-aligned (the default).")
115 .alignment(Alignment::Left);
116 doc.add_paragraph("This paragraph is center-aligned.")
117 .alignment(Alignment::Center);
118 doc.add_paragraph("This paragraph is right-aligned.")
119 .alignment(Alignment::Right);
120 doc.add_paragraph(
121 "This paragraph is justified. To demonstrate justified text properly, it needs \
122 to be long enough to span multiple lines so the word spacing adjustment is visible \
123 across the full width of the text area on the page.",
124 )
125 .alignment(Alignment::Justify);
126
127 doc.add_paragraph("");
128
129 // --- Run formatting ---
130 doc.add_paragraph("Run Formatting").style("Heading2");
131
132 {
133 let mut p = doc.add_paragraph("");
134 p.add_run("Bold text").bold(true);
135 p.add_run(" | ");
136 p.add_run("Italic text").italic(true);
137 p.add_run(" | ");
138 p.add_run("Bold + Italic").bold(true).italic(true);
139 }
140 {
141 let mut p = doc.add_paragraph("");
142 p.add_run("Single underline").underline(true);
143 p.add_run(" | ");
144 p.add_run("Strikethrough").strike(true);
145 p.add_run(" | ");
146 p.add_run("Double strikethrough").double_strike(true);
147 }
148 {
149 let mut p = doc.add_paragraph("");
150 p.add_run("Red text").color("FF0000");
151 p.add_run(" | ");
152 p.add_run("Blue text").color("0000FF");
153 p.add_run(" | ");
154 p.add_run("Green text").color("00AA00");
155 p.add_run(" | ");
156 p.add_run("Highlighted").highlight("FFFF00");
157 }
158 {
159 let mut p = doc.add_paragraph("");
160 p.add_run("8pt small").size(8.0);
161 p.add_run(" | ");
162 p.add_run("11pt normal").size(11.0);
163 p.add_run(" | ");
164 p.add_run("16pt large").size(16.0);
165 p.add_run(" | ");
166 p.add_run("24pt extra-large").size(24.0);
167 }
168 {
169 let mut p = doc.add_paragraph("");
170 p.add_run("Arial font").font("Arial");
171 p.add_run(" | ");
172 p.add_run("Times New Roman font").font("Times New Roman");
173 p.add_run(" | ");
174 p.add_run("Courier New font").font("Courier New");
175 }
176 {
177 let mut p = doc.add_paragraph("");
178 p.add_run("Normal");
179 p.add_run(" H").size(11.0);
180 p.add_run("2").subscript();
181 p.add_run("O (subscript)").size(11.0);
182 p.add_run(" | E = mc").size(11.0);
183 p.add_run("2").superscript();
184 p.add_run(" (superscript)").size(11.0);
185 }
186 {
187 let mut p = doc.add_paragraph("");
188 p.add_run("ALL CAPS").all_caps(true);
189 p.add_run(" | ");
190 p.add_run("Small Caps").small_caps(true);
191 p.add_run(" | ");
192 p.add_run("Expanded spacing")
193 .character_spacing(Length::twips(40));
194 }
195
196 doc.add_paragraph("");
197
198 // --- Paragraph formatting ---
199 doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201 doc.add_paragraph("Paragraph with shading (light green background)")
202 .shading("E2EFDA");
203
204 doc.add_paragraph("Paragraph with bottom border")
205 .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207 doc.add_paragraph("Paragraph with all borders")
208 .border_all(BorderStyle::Single, 4, "FF0000");
209
210 doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211 .indent_left(Length::inches(1.0))
212 .hanging_indent(Length::inches(0.5));
213
214 doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215 .first_line_indent(Length::inches(0.5));
216
217 doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218 .space_before(Length::pt(24.0))
219 .space_after(Length::pt(12.0));
220
221 doc.add_paragraph(
222 "Paragraph with double line spacing. This text should have extra vertical \
223 space between lines to demonstrate the line_spacing_multiple setting.",
224 )
225 .line_spacing_multiple(2.0);
226
227 doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228 .keep_with_next(true);
229 doc.add_paragraph("(This stays with the paragraph above.)");
230
231 // =========================================================================
232 // PAGE 3: LISTS & TAB STOPS
233 // =========================================================================
234 doc.add_paragraph("").page_break_before(true);
235
236 doc.add_paragraph("2. Lists").style("Heading1");
237
238 doc.add_paragraph("Bullet List").style("Heading2");
239
240 doc.add_bullet_list_item("First bullet item", 0);
241 doc.add_bullet_list_item("Second bullet item", 0);
242 doc.add_bullet_list_item("Nested level 1", 1);
243 doc.add_bullet_list_item("Nested level 2", 2);
244 doc.add_bullet_list_item("Back to level 1", 1);
245 doc.add_bullet_list_item("Third bullet item", 0);
246
247 doc.add_paragraph("");
248
249 doc.add_paragraph("Numbered List").style("Heading2");
250
251 doc.add_numbered_list_item("First numbered item", 0);
252 doc.add_numbered_list_item("Second numbered item", 0);
253 doc.add_numbered_list_item("Sub-item A", 1);
254 doc.add_numbered_list_item("Sub-item B", 1);
255 doc.add_numbered_list_item("Third numbered item", 0);
256
257 doc.add_paragraph("");
258
259 // --- Tab stops ---
260 doc.add_paragraph("Tab Stops").style("Heading2");
261
262 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268 doc.add_paragraph("Item\t........\tPrice")
269 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273 doc.add_paragraph("Widget A\t........\t$19.99")
274 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278 doc.add_paragraph("Gadget B\t________\t$249.50")
279 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280 .add_tab_stop_with_leader(
281 TabAlignment::Right,
282 Length::inches(4.0),
283 TabLeader::Underscore,
284 )
285 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287 // =========================================================================
288 // PAGE 4: TABLES
289 // =========================================================================
290 doc.add_paragraph("").page_break_before(true);
291
292 doc.add_paragraph("3. Tables").style("Heading1");
293
294 // --- Basic table with borders ---
295 doc.add_paragraph("Basic Table with Borders")
296 .style("Heading2");
297
298 {
299 let mut tbl = doc.add_table(4, 3);
300 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302 // Header row
303 for col in 0..3 {
304 tbl.cell(0, col).unwrap().shading("2E75B6");
305 }
306 tbl.cell(0, 0).unwrap().set_text("Name");
307 tbl.cell(0, 1).unwrap().set_text("Role");
308 tbl.cell(0, 2).unwrap().set_text("Location");
309
310 tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311 tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312 tbl.cell(1, 2).unwrap().set_text("New York");
313
314 tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315 tbl.cell(2, 1).unwrap().set_text("Product Manager");
316 tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318 tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319 tbl.cell(3, 1).unwrap().set_text("Designer");
320 tbl.cell(3, 2).unwrap().set_text("London");
321 }
322
323 doc.add_paragraph("");
324
325 // --- Table with cell merging ---
326 doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327 .style("Heading2");
328
329 {
330 let mut tbl = doc.add_table(4, 4);
331 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332 tbl = tbl.width_pct(100.0);
333
334 // Header spanning all columns
335 tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336 tbl.cell(0, 0).unwrap().shading("1F4E79");
337 tbl.cell(0, 0).unwrap().grid_span(4);
338
339 // Sub-header
340 tbl.cell(1, 0).unwrap().set_text("Region");
341 tbl.cell(1, 0).unwrap().shading("D6E4F0");
342 tbl.cell(1, 1).unwrap().set_text("Q1");
343 tbl.cell(1, 1).unwrap().shading("D6E4F0");
344 tbl.cell(1, 2).unwrap().set_text("Q2");
345 tbl.cell(1, 2).unwrap().shading("D6E4F0");
346 tbl.cell(1, 3).unwrap().set_text("Total");
347 tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349 // Data
350 tbl.cell(2, 0).unwrap().set_text("North America");
351 tbl.cell(2, 1).unwrap().set_text("$2.4M");
352 tbl.cell(2, 2).unwrap().set_text("$2.7M");
353 tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355 tbl.cell(3, 0).unwrap().set_text("Europe");
356 tbl.cell(3, 1).unwrap().set_text("$1.8M");
357 tbl.cell(3, 2).unwrap().set_text("$2.0M");
358 tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360 // Vertical alignment on data cells
361 tbl.cell(2, 3)
362 .unwrap()
363 .vertical_alignment(VerticalAlignment::Center);
364 tbl.cell(3, 3)
365 .unwrap()
366 .vertical_alignment(VerticalAlignment::Bottom);
367 }
368
369 doc.add_paragraph("");
370
371 // --- Table with vertical merge ---
372 doc.add_paragraph("Table with Vertical Merge")
373 .style("Heading2");
374
375 {
376 let mut tbl = doc.add_table(4, 3);
377 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379 tbl.cell(0, 0).unwrap().set_text("Category");
380 tbl.cell(0, 0).unwrap().shading("E2EFDA");
381 tbl.cell(0, 1).unwrap().set_text("Item");
382 tbl.cell(0, 1).unwrap().shading("E2EFDA");
383 tbl.cell(0, 2).unwrap().set_text("Price");
384 tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386 // "Hardware" spans rows 1-2
387 tbl.cell(1, 0).unwrap().set_text("Hardware");
388 tbl.cell(1, 0).unwrap().v_merge_restart();
389 tbl.cell(1, 1).unwrap().set_text("Laptop");
390 tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392 tbl.cell(2, 0).unwrap().v_merge_continue();
393 tbl.cell(2, 1).unwrap().set_text("Monitor");
394 tbl.cell(2, 2).unwrap().set_text("$450");
395
396 // "Software" on row 3
397 tbl.cell(3, 0).unwrap().set_text("Software");
398 tbl.cell(3, 1).unwrap().set_text("IDE License");
399 tbl.cell(3, 2).unwrap().set_text("$200/yr");
400 }
401
402 doc.add_paragraph("");
403
404 // --- Nested table ---
405 doc.add_paragraph("Nested Table").style("Heading2");
406
407 {
408 let mut tbl = doc.add_table(2, 2);
409 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411 tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412 tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413 tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415 // Nested table inside cell (1,1)
416 {
417 let mut cell = tbl.cell(1, 1).unwrap();
418 cell.set_text("Contains nested table:");
419 let mut nested = cell.add_table(2, 2);
420 nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421 nested.cell(0, 0).unwrap().set_text("Inner A");
422 nested.cell(0, 1).unwrap().set_text("Inner B");
423 nested.cell(1, 0).unwrap().set_text("Inner C");
424 nested.cell(1, 1).unwrap().set_text("Inner D");
425 }
426 }
427
428 // =========================================================================
429 // PAGE 5: IMAGES
430 // =========================================================================
431 doc.add_paragraph("").page_break_before(true);
432
433 doc.add_paragraph("4. Images").style("Heading1");
434
435 doc.add_paragraph("Inline Image").style("Heading2");
436
437 doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438 let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439 doc.add_picture(
440 &inline_img,
441 "inline_chart.png",
442 Length::inches(3.0),
443 Length::inches(0.75),
444 );
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("Header Image").style("Heading2");
449
450 // Replace the text-only header with an image header
451 let header_img = create_sample_png(400, 40, [40, 40, 40]);
452 doc.set_header_image(
453 &header_img,
454 "header_logo.png",
455 Length::inches(2.0),
456 Length::inches(0.2),
457 );
458
459 doc.add_paragraph(
460 "The document header has been replaced with an inline image. \
461 Check the header area at the top of this page.",
462 );
463
464 doc.add_paragraph("");
465 doc.add_paragraph(
466 "Note: The cover page uses a full-page background image behind the text, \
467 demonstrated on page 1 via add_background_image().",
468 );
469
470 // =========================================================================
471 // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472 // =========================================================================
473 doc.add_paragraph("").page_break_before(true);
474
475 doc.add_paragraph("5. Content Manipulation")
476 .style("Heading1");
477
478 // --- Placeholder replacement ---
479 doc.add_paragraph("Placeholder Replacement")
480 .style("Heading2");
481
482 doc.add_paragraph(
483 "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484 );
485
486 {
487 let mut p = doc.add_paragraph("");
488 p.add_run("Customer: ").bold(true);
489 p.add_run("{{customer}}");
490 }
491 {
492 let mut p = doc.add_paragraph("");
493 p.add_run("Date: ").bold(true);
494 p.add_run("{{date}}");
495 }
496 doc.add_paragraph("Reference: {{ref_number}}");
497
498 // Table with placeholders
499 {
500 let mut tbl = doc.add_table(3, 2);
501 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502 tbl.cell(0, 0).unwrap().set_text("Field");
503 tbl.cell(0, 0).unwrap().shading("D6E4F0");
504 tbl.cell(0, 1).unwrap().set_text("Value");
505 tbl.cell(0, 1).unwrap().shading("D6E4F0");
506 tbl.cell(1, 0).unwrap().set_text("Project");
507 tbl.cell(1, 1).unwrap().set_text("{{project}}");
508 tbl.cell(2, 0).unwrap().set_text("Status");
509 tbl.cell(2, 1).unwrap().set_text("{{status}}");
510 }
511
512 // Perform replacements
513 let mut replacements = HashMap::new();
514 replacements.insert("{{customer}}", "Acme Corporation");
515 replacements.insert("{{date}}", "February 22, 2026");
516 replacements.insert("{{ref_number}}", "REF-2026-001");
517 replacements.insert("{{project}}", "Infrastructure Upgrade");
518 replacements.insert("{{status}}", "In Progress");
519 let replace_count = doc.replace_all(&replacements);
520
521 doc.add_paragraph("");
522 doc.add_paragraph(&format!(
523 "(Replaced {} placeholders above — in body text and table cells)",
524 replace_count
525 ));
526
527 doc.add_paragraph("");
528
529 // --- Content insertion ---
530 doc.add_paragraph("Content Insertion").style("Heading2");
531
532 doc.add_paragraph("Section A: First section of content.");
533 doc.add_paragraph("Section C: Third section of content.");
534
535 // Insert "Section B" between A and C
536 if let Some(idx) = doc.find_content_index("Section C") {
537 doc.insert_paragraph(
538 idx,
539 "Section B: Inserted between A and C using find_content_index().",
540 );
541 }
542
543 doc.add_paragraph("");
544 doc.add_paragraph(
545 "The paragraph above ('Section B') was inserted at a specific position \
546 using find_content_index() + insert_paragraph().",
547 );
548
549 // =========================================================================
550 // PAGE 7: LANDSCAPE — section break, wide table
551 // =========================================================================
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554 doc.add_paragraph("6. Mixed Page Orientation")
555 .style("Heading1");
556
557 doc.add_paragraph(
558 "This page is in LANDSCAPE orientation. It was created using a section break \
559 followed by section_landscape(). This is useful for wide tables or charts.",
560 );
561
562 doc.add_paragraph("");
563
564 // Wide table for landscape
565 {
566 let mut tbl = doc.add_table(4, 7);
567 tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570 for (col, h) in headers.iter().enumerate() {
571 tbl.cell(0, col).unwrap().set_text(h);
572 tbl.cell(0, col).unwrap().shading("2E75B6");
573 }
574
575 let data = [
576 [
577 "North America",
578 "$1.2M",
579 "$1.3M",
580 "$1.4M",
581 "$1.5M",
582 "$1.6M",
583 "$7.0M",
584 ],
585 [
586 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587 ],
588 [
589 "Asia Pacific",
590 "$0.5M",
591 "$0.6M",
592 "$0.7M",
593 "$0.7M",
594 "$0.8M",
595 "$3.3M",
596 ],
597 ];
598 for (row_idx, row_data) in data.iter().enumerate() {
599 for (col, val) in row_data.iter().enumerate() {
600 tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601 }
602 }
603 }
604
605 // End landscape, return to portrait
606 doc.add_paragraph("")
607 .section_break(SectionBreak::NextPage)
608 .section_landscape();
609
610 // =========================================================================
611 // PAGE 8: BACK TO PORTRAIT — styles, final notes
612 // =========================================================================
613 doc.add_paragraph("7. Custom Styles & Summary")
614 .style("Heading1");
615
616 doc.add_paragraph(
617 "This final page is back in portrait orientation after a section break. \
618 The document has demonstrated:",
619 );
620
621 doc.add_bullet_list_item(
622 "Page setup: size, margins, header/footer distance, gutter",
623 0,
624 );
625 doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626 doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627 doc.add_bullet_list_item("Background images: full-page behind text", 0);
628 doc.add_bullet_list_item(
629 "Text formatting: bold, italic, underline, strike, color, size, font",
630 0,
631 );
632 doc.add_bullet_list_item(
633 "Advanced run formatting: superscript, subscript, caps, spacing",
634 0,
635 );
636 doc.add_bullet_list_item(
637 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638 0,
639 );
640 doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641 doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642 doc.add_bullet_list_item(
643 "Tables: borders, shading, column spans, row spans, nesting",
644 0,
645 );
646 doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647 doc.add_bullet_list_item("Inline images", 0);
648 doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649 doc.add_bullet_list_item("Content insertion at specific positions", 0);
650 doc.add_bullet_list_item(
651 "Section breaks with mixed portrait/landscape orientation",
652 0,
653 );
654
655 doc.add_paragraph("");
656 doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657 .alignment(Alignment::Center)
658 .shading("E2EFDA")
659 .border_all(BorderStyle::Single, 2, "00AA00");
660
661 doc.save(path).unwrap();
662}examples/generate_all_samples.rs (line 412)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87 let mut doc = Document::new();
88
89 // ── Page Setup & Metadata ──
90 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91 doc.set_margins(
92 Length::inches(1.0),
93 Length::inches(1.0),
94 Length::inches(1.0),
95 Length::inches(1.0),
96 );
97 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98 doc.set_gutter(Length::twips(0));
99 doc.set_title("rdocx Feature Showcase");
100 doc.set_author("rdocx Sample Generator");
101 doc.set_subject("Comprehensive feature demonstration");
102 doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104 // Headers & Footers with different first page
105 doc.set_different_first_page(true);
106 doc.set_first_page_header("rdocx");
107 doc.set_first_page_footer("Feature Showcase — Cover Page");
108 doc.set_header("rdocx Feature Showcase");
109 doc.set_footer("Generated by rdocx");
110
111 // ── COVER PAGE ──
112 let bg = create_sample_png(612, 792, [20, 45, 90]);
113 doc.add_background_image(&bg, "cover_bg.png");
114
115 for _ in 0..3 {
116 doc.add_paragraph("");
117 }
118 {
119 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121 }
122 {
123 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124 p.add_run("Complete Feature Showcase")
125 .size(28.0)
126 .color("FFFFFF")
127 .italic(true);
128 }
129 doc.add_paragraph("");
130 {
131 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132 p.add_run("Every feature of the rdocx Rust library")
133 .size(13.0)
134 .color("B0C4DE");
135 }
136 {
137 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138 p.add_run("demonstrated in a single document.")
139 .size(13.0)
140 .color("B0C4DE");
141 }
142
143 // ── TABLE OF CONTENTS ──
144 doc.add_paragraph("").page_break_before(true);
145 doc.insert_toc(doc.content_count(), 3);
146
147 // ── SECTION 1: TEXT FORMATTING ──
148 doc.add_paragraph("").page_break_before(true);
149 doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151 // Paragraph Alignment
152 doc.add_paragraph("1.1 Paragraph Alignment")
153 .style("Heading2");
154 doc.add_paragraph("Left-aligned paragraph (default).")
155 .alignment(Alignment::Left);
156 doc.add_paragraph("Center-aligned paragraph.")
157 .alignment(Alignment::Center);
158 doc.add_paragraph("Right-aligned paragraph.")
159 .alignment(Alignment::Right);
160 doc.add_paragraph(
161 "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162 distributes extra space across word gaps so lines fill the full width of the text area.",
163 )
164 .alignment(Alignment::Justify);
165
166 doc.add_paragraph("");
167
168 // Run Formatting
169 doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170 {
171 let mut p = doc.add_paragraph("");
172 p.add_run("Bold").bold(true);
173 p.add_run(" | ");
174 p.add_run("Italic").italic(true);
175 p.add_run(" | ");
176 p.add_run("Bold Italic").bold(true).italic(true);
177 p.add_run(" | ");
178 p.add_run("Underline").underline(true);
179 p.add_run(" | ");
180 p.add_run("Strikethrough").strike(true);
181 p.add_run(" | ");
182 p.add_run("Double Strike").double_strike(true);
183 }
184 {
185 let mut p = doc.add_paragraph("");
186 p.add_run("Red").color("FF0000");
187 p.add_run(" | ");
188 p.add_run("Blue").color("0000FF");
189 p.add_run(" | ");
190 p.add_run("Green").color("00AA00");
191 p.add_run(" | ");
192 p.add_run("Highlighted").highlight("FFFF00");
193 }
194 {
195 let mut p = doc.add_paragraph("");
196 p.add_run("8pt").size(8.0);
197 p.add_run(" | ");
198 p.add_run("11pt").size(11.0);
199 p.add_run(" | ");
200 p.add_run("16pt").size(16.0);
201 p.add_run(" | ");
202 p.add_run("24pt").size(24.0);
203 }
204 {
205 let mut p = doc.add_paragraph("");
206 p.add_run("Arial").font("Arial");
207 p.add_run(" | ");
208 p.add_run("Times New Roman").font("Times New Roman");
209 p.add_run(" | ");
210 p.add_run("Courier New").font("Courier New");
211 }
212 {
213 let mut p = doc.add_paragraph("");
214 p.add_run("H");
215 p.add_run("2").subscript();
216 p.add_run("O (subscript) | E=mc");
217 p.add_run("2").superscript();
218 p.add_run(" (superscript)");
219 }
220 {
221 let mut p = doc.add_paragraph("");
222 p.add_run("ALL CAPS").all_caps(true);
223 p.add_run(" | ");
224 p.add_run("Small Caps").small_caps(true);
225 p.add_run(" | ");
226 p.add_run("Expanded").character_spacing(Length::twips(40));
227 p.add_run(" | ");
228 p.add_run("Hidden text (not visible)").hidden(true);
229 }
230
231 doc.add_paragraph("");
232
233 // Underline Styles
234 doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235 {
236 let mut p = doc.add_paragraph("");
237 p.add_run("Single ")
238 .underline_style(rdocx::UnderlineStyle::Single);
239 p.add_run("Double ")
240 .underline_style(rdocx::UnderlineStyle::Double);
241 p.add_run("Thick ")
242 .underline_style(rdocx::UnderlineStyle::Thick);
243 p.add_run("Dotted ")
244 .underline_style(rdocx::UnderlineStyle::Dotted);
245 p.add_run("Dash ")
246 .underline_style(rdocx::UnderlineStyle::Dash);
247 p.add_run("Wave ")
248 .underline_style(rdocx::UnderlineStyle::Wave);
249 }
250
251 // ── SECTION 2: PARAGRAPH FORMATTING ──
252 doc.add_paragraph("").page_break_before(true);
253 doc.add_paragraph("2. Paragraph Formatting")
254 .style("Heading1");
255
256 doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257 doc.add_paragraph("Paragraph with light green shading")
258 .shading("E2EFDA");
259 doc.add_paragraph("Paragraph with bottom border")
260 .border_bottom(BorderStyle::Single, 6, "2E75B6");
261 doc.add_paragraph("Paragraph with all borders (red)")
262 .border_all(BorderStyle::Single, 4, "FF0000");
263
264 doc.add_paragraph("2.2 Indentation").style("Heading2");
265 doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266 .indent_left(Length::inches(1.0))
267 .hanging_indent(Length::inches(0.5));
268 doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269 .first_line_indent(Length::inches(0.5));
270 doc.add_paragraph("Both left and right indent (0.75 inches each)")
271 .indent_left(Length::inches(0.75))
272 .indent_right(Length::inches(0.75));
273
274 doc.add_paragraph("2.3 Spacing & Line Height")
275 .style("Heading2");
276 doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277 .space_before(Length::pt(24.0))
278 .space_after(Length::pt(12.0));
279 doc.add_paragraph(
280 "Double line spacing paragraph. This text should have extra vertical space between \
281 lines to demonstrate line_spacing_multiple(2.0).",
282 )
283 .line_spacing_multiple(2.0);
284 doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285 .line_spacing(20.0);
286
287 doc.add_paragraph("2.4 Pagination Controls")
288 .style("Heading2");
289 doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290 .keep_with_next(true);
291 doc.add_paragraph("(This paragraph was kept with the one above.)");
292 doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293 .keep_together(true);
294 doc.add_paragraph("widow_control — prevents widow/orphan lines")
295 .widow_control(true);
296
297 // ── SECTION 3: LISTS ──
298 doc.add_paragraph("").page_break_before(true);
299 doc.add_paragraph("3. Lists").style("Heading1");
300
301 doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302 doc.add_bullet_list_item("First item", 0);
303 doc.add_bullet_list_item("Second item", 0);
304 doc.add_bullet_list_item("Nested level 1", 1);
305 doc.add_bullet_list_item("Nested level 2", 2);
306 doc.add_bullet_list_item("Back to level 1", 1);
307 doc.add_bullet_list_item("Third item", 0);
308
309 doc.add_paragraph("");
310
311 doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312 doc.add_numbered_list_item("First numbered item", 0);
313 doc.add_numbered_list_item("Second numbered item", 0);
314 doc.add_numbered_list_item("Sub-item A", 1);
315 doc.add_numbered_list_item("Sub-item B", 1);
316 doc.add_numbered_list_item("Third numbered item", 0);
317
318 // ── SECTION 4: TAB STOPS ──
319 doc.add_paragraph("");
320 doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322 doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329 doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330 doc.add_paragraph("Item\t\tPrice")
331 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334 doc.add_paragraph("Widget\t\t$19.99")
335 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338 doc.add_paragraph("Gadget\t\t$249.50")
339 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340 .add_tab_stop_with_leader(
341 TabAlignment::Right,
342 Length::inches(4.0),
343 TabLeader::Underscore,
344 )
345 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347 // ── SECTION 5: TABLES ──
348 doc.add_paragraph("").page_break_before(true);
349 doc.add_paragraph("5. Tables").style("Heading1");
350
351 doc.add_paragraph("5.1 Basic Table").style("Heading2");
352 {
353 let mut tbl = doc
354 .add_table(4, 3)
355 .borders(BorderStyle::Single, 4, "000000");
356 for col in 0..3 {
357 tbl.cell(0, col).unwrap().shading("2E75B6");
358 }
359 tbl.cell(0, 0).unwrap().set_text("Name");
360 tbl.cell(0, 1).unwrap().set_text("Role");
361 tbl.cell(0, 2).unwrap().set_text("Location");
362 tbl.cell(1, 0).unwrap().set_text("Walter White");
363 tbl.cell(1, 1).unwrap().set_text("CEO");
364 tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365 tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366 tbl.cell(2, 1).unwrap().set_text("CTO");
367 tbl.cell(2, 2).unwrap().set_text("Remote");
368 tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369 tbl.cell(3, 1).unwrap().set_text("Security");
370 tbl.cell(3, 2).unwrap().set_text("Washington");
371 }
372
373 doc.add_paragraph("");
374
375 doc.add_paragraph("5.2 Column Span & Cell Shading")
376 .style("Heading2");
377 {
378 let mut tbl = doc
379 .add_table(3, 4)
380 .borders(BorderStyle::Single, 4, "000000")
381 .width_pct(100.0);
382 tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383 tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384 tbl.cell(1, 0).unwrap().set_text("Region");
385 tbl.cell(1, 0).unwrap().shading("D6E4F0");
386 tbl.cell(1, 1).unwrap().set_text("Q1");
387 tbl.cell(1, 1).unwrap().shading("D6E4F0");
388 tbl.cell(1, 2).unwrap().set_text("Q2");
389 tbl.cell(1, 2).unwrap().shading("D6E4F0");
390 tbl.cell(1, 3).unwrap().set_text("Total");
391 tbl.cell(1, 3).unwrap().shading("D6E4F0");
392 tbl.cell(2, 0).unwrap().set_text("Americas");
393 tbl.cell(2, 1).unwrap().set_text("$2.4M");
394 tbl.cell(2, 2).unwrap().set_text("$2.7M");
395 tbl.cell(2, 3).unwrap().set_text("$5.1M");
396 }
397
398 doc.add_paragraph("");
399
400 doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401 {
402 let mut tbl = doc
403 .add_table(4, 3)
404 .borders(BorderStyle::Single, 4, "000000");
405 tbl.cell(0, 0).unwrap().set_text("Category");
406 tbl.cell(0, 0).unwrap().shading("E2EFDA");
407 tbl.cell(0, 1).unwrap().set_text("Item");
408 tbl.cell(0, 1).unwrap().shading("E2EFDA");
409 tbl.cell(0, 2).unwrap().set_text("Price");
410 tbl.cell(0, 2).unwrap().shading("E2EFDA");
411 tbl.cell(1, 0).unwrap().set_text("Hardware");
412 tbl.cell(1, 0).unwrap().v_merge_restart();
413 tbl.cell(1, 1).unwrap().set_text("Laptop");
414 tbl.cell(1, 2).unwrap().set_text("$1,200");
415 tbl.cell(2, 0).unwrap().v_merge_continue();
416 tbl.cell(2, 1).unwrap().set_text("Monitor");
417 tbl.cell(2, 2).unwrap().set_text("$450");
418 tbl.cell(3, 0).unwrap().set_text("Software");
419 tbl.cell(3, 1).unwrap().set_text("IDE License");
420 tbl.cell(3, 2).unwrap().set_text("$200/yr");
421 }
422
423 doc.add_paragraph("");
424
425 doc.add_paragraph("5.4 Nested Table").style("Heading2");
426 {
427 let mut tbl = doc
428 .add_table(2, 2)
429 .borders(BorderStyle::Single, 6, "2E75B6");
430 tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431 tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432 tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433 {
434 let mut cell = tbl.cell(1, 1).unwrap();
435 cell.set_text("Nested table below:");
436 let mut nested = cell
437 .add_table(2, 2)
438 .borders(BorderStyle::Single, 2, "FF6600");
439 nested.cell(0, 0).unwrap().set_text("A");
440 nested.cell(0, 1).unwrap().set_text("B");
441 nested.cell(1, 0).unwrap().set_text("C");
442 nested.cell(1, 1).unwrap().set_text("D");
443 }
444 }
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449 .style("Heading2");
450 {
451 let mut tbl = doc
452 .add_table(2, 3)
453 .borders(BorderStyle::Single, 4, "666666");
454 tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455 tbl.cell(0, 0).unwrap().set_text("Top");
456 tbl.cell(0, 0)
457 .unwrap()
458 .vertical_alignment(VerticalAlignment::Top)
459 .shading("FFF2CC");
460 tbl.cell(0, 1).unwrap().set_text("Center");
461 tbl.cell(0, 1)
462 .unwrap()
463 .vertical_alignment(VerticalAlignment::Center)
464 .shading("D9E2F3");
465 tbl.cell(0, 2).unwrap().set_text("Bottom");
466 tbl.cell(0, 2)
467 .unwrap()
468 .vertical_alignment(VerticalAlignment::Bottom)
469 .shading("E2EFDA");
470 tbl.row(1).unwrap().cant_split();
471 tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472 tbl.cell(1, 0).unwrap().no_wrap();
473 tbl.cell(1, 1).unwrap().set_text("Fixed width");
474 tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475 tbl.cell(1, 2).unwrap().set_text("Normal");
476 }
477
478 // ── SECTION 6: IMAGES ──
479 doc.add_paragraph("").page_break_before(true);
480 doc.add_paragraph("6. Images").style("Heading1");
481
482 doc.add_paragraph("6.1 Inline Image").style("Heading2");
483 doc.add_paragraph("A blue gradient image below:");
484 let img = create_sample_png(200, 50, [0, 80, 200]);
485 doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487 doc.add_paragraph("");
488 doc.add_paragraph("6.2 Header Image").style("Heading2");
489 let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490 doc.set_header_image(
491 &hdr_img,
492 "header_logo.png",
493 Length::inches(2.0),
494 Length::inches(0.2),
495 );
496 doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498 doc.add_paragraph("");
499 doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501 // ── SECTION 7: CONTENT MANIPULATION ──
502 doc.add_paragraph("").page_break_before(true);
503 doc.add_paragraph("7. Content Manipulation")
504 .style("Heading1");
505
506 doc.add_paragraph("7.1 Placeholder Replacement")
507 .style("Heading2");
508 doc.add_paragraph("Customer: {{customer}}");
509 doc.add_paragraph("Date: {{date}}");
510 doc.add_paragraph("Reference: {{ref_number}}");
511 {
512 let mut tbl = doc
513 .add_table(2, 2)
514 .borders(BorderStyle::Single, 4, "000000");
515 tbl.cell(0, 0).unwrap().set_text("Project");
516 tbl.cell(0, 0).unwrap().shading("D6E4F0");
517 tbl.cell(0, 1).unwrap().set_text("{{project}}");
518 tbl.cell(1, 0).unwrap().set_text("Status");
519 tbl.cell(1, 0).unwrap().shading("D6E4F0");
520 tbl.cell(1, 1).unwrap().set_text("{{status}}");
521 }
522
523 let mut replacements = HashMap::new();
524 replacements.insert("{{customer}}", "Tensorbee Inc.");
525 replacements.insert("{{date}}", "February 22, 2026");
526 replacements.insert("{{ref_number}}", "TB-2026-001");
527 replacements.insert("{{project}}", "Infrastructure Upgrade");
528 replacements.insert("{{status}}", "In Progress");
529 let n = doc.replace_all(&replacements);
530 doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532 doc.add_paragraph("");
533
534 doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535 doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536 let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537 doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539 doc.add_paragraph("");
540
541 doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542 doc.add_paragraph("Section A: First content.");
543 doc.add_paragraph("Section C: Third content.");
544 if let Some(idx) = doc.find_content_index("Section C") {
545 doc.insert_paragraph(
546 idx,
547 "Section B: Inserted between A and C via find_content_index().",
548 );
549 }
550
551 // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553 doc.add_paragraph("8. Section Breaks & Orientation")
554 .style("Heading1");
555 doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557 {
558 let mut tbl = doc
559 .add_table(3, 7)
560 .borders(BorderStyle::Single, 4, "2E75B6");
561 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562 for (c, h) in headers.iter().enumerate() {
563 tbl.cell(0, c).unwrap().set_text(h);
564 tbl.cell(0, c).unwrap().shading("2E75B6");
565 }
566 let data = [
567 [
568 "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569 ],
570 [
571 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572 ],
573 ];
574 for (r, row) in data.iter().enumerate() {
575 for (c, v) in row.iter().enumerate() {
576 tbl.cell(r + 1, c).unwrap().set_text(v);
577 }
578 }
579 }
580
581 doc.add_paragraph("")
582 .section_break(SectionBreak::NextPage)
583 .section_landscape();
584
585 // ── SECTION 9: CUSTOM STYLES ──
586 doc.add_paragraph("9. Custom Styles").style("Heading1");
587 doc.add_style(
588 StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589 .based_on("Normal")
590 .paragraph_properties({
591 let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592 ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593 val: "clear".to_string(),
594 color: None,
595 fill: Some("FFF2CC".to_string()),
596 });
597 ppr
598 })
599 .run_properties({
600 let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601 rpr.bold = Some(true);
602 rpr.color = Some("C45911".to_string());
603 rpr
604 }),
605 );
606 doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607 .style("CustomHighlight");
608
609 doc.add_paragraph("");
610
611 // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612 doc.add_paragraph("10. Document Intelligence API")
613 .style("Heading1");
614
615 let wc = doc.word_count();
616 let hc = doc.headings().len();
617 let ic = doc.images().len();
618 let lc = doc.links().len();
619 doc.add_paragraph(&format!("Word count: {wc}"));
620 doc.add_paragraph(&format!("Heading count: {hc}"));
621 doc.add_paragraph(&format!("Image count: {ic}"));
622 doc.add_paragraph(&format!("Link count: {lc}"));
623
624 let outline = doc.document_outline();
625 doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627 let issues = doc.audit_accessibility();
628 doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629 for issue in &issues {
630 doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631 }
632
633 // ── SECTION 11: DOCUMENT MERGING ──
634 doc.add_paragraph("");
635 doc.add_paragraph("11. Document Merging").style("Heading1");
636 {
637 let mut other = Document::new();
638 other.add_paragraph("This paragraph was merged from another document using append().");
639 doc.append(&other);
640 }
641
642 doc.add_paragraph("");
643
644 // ── SUMMARY ──
645 doc.add_paragraph("Summary of Demonstrated Features")
646 .style("Heading1");
647 let features = [
648 "Page setup: size, margins, header/footer distance, gutter",
649 "Document metadata: title, author, subject, keywords",
650 "Headers and footers: text, images, different first page",
651 "Background images (full-page behind text)",
652 "Table of Contents generation with bookmarks",
653 "Text formatting: bold, italic, underline styles, strike, color, size, font",
654 "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656 "Pagination controls: keep-with-next, keep-together, widow control",
657 "Bullet and numbered lists with nesting",
658 "Tab stops with dot/underscore/hyphen leaders",
659 "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660 "Row properties: header rows, exact height, cant-split",
661 "Cell properties: width, no-wrap, vertical alignment",
662 "Inline images and header images",
663 "Placeholder replacement (single and batch)",
664 "Regex-based find and replace",
665 "Content insertion at specific positions",
666 "Section breaks with mixed portrait/landscape",
667 "Custom paragraph and character styles",
668 "Document intelligence: word count, headings, outline, images, links",
669 "Accessibility audit",
670 "Document merging (append)",
671 "Export: DOCX, PDF, HTML, Markdown",
672 ];
673 for f in &features {
674 doc.add_bullet_list_item(f, 0);
675 }
676 doc.add_paragraph("");
677 doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678 .alignment(Alignment::Center)
679 .shading("E2EFDA")
680 .border_all(BorderStyle::Single, 2, "00AA00");
681
682 doc
683}Sourcepub fn v_merge_continue(self) -> Self
pub fn v_merge_continue(self) -> Self
Continue a vertical merge group (this cell merges with the one above).
Examples found in repository?
examples/styled_tables.rs (line 181)
24fn generate_styled_tables(path: &Path) {
25 let mut doc = Document::new();
26 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27 doc.set_margins(
28 Length::inches(0.75),
29 Length::inches(0.75),
30 Length::inches(0.75),
31 Length::inches(0.75),
32 );
33
34 doc.add_paragraph("Styled Tables Showcase")
35 .style("Heading1");
36
37 doc.add_paragraph("");
38
39 // =========================================================================
40 // 1. Professional report table with alternating rows
41 // =========================================================================
42 doc.add_paragraph("1. Report Table with Alternating Row Colors")
43 .style("Heading2");
44
45 {
46 let mut tbl = doc.add_table(8, 4);
47 tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48 tbl = tbl.width_pct(100.0);
49
50 // Header row
51 let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52 for (col, h) in headers.iter().enumerate() {
53 tbl.cell(0, col).unwrap().shading("2E75B6");
54 tbl.cell(0, col).unwrap().set_text(h);
55 }
56 tbl.row(0).unwrap().header();
57
58 // Data with alternating shading
59 let data = [
60 ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61 ["Professional", "$189,000", "$201,000", "+6.3%"],
62 ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63 ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64 ["Training", "$22,000", "$28,900", "+31.4%"],
65 ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66 ];
67
68 for (i, row) in data.iter().enumerate() {
69 let row_idx = i + 1;
70 for (col, val) in row.iter().enumerate() {
71 tbl.cell(row_idx, col).unwrap().set_text(val);
72 // Alternate row colors
73 if i % 2 == 0 {
74 tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75 }
76 }
77 }
78
79 // Total row
80 tbl.cell(7, 0).unwrap().set_text("TOTAL");
81 tbl.cell(7, 0).unwrap().shading("D6E4F0");
82 tbl.cell(7, 1).unwrap().set_text("$613,000");
83 tbl.cell(7, 1).unwrap().shading("D6E4F0");
84 tbl.cell(7, 2).unwrap().set_text("$729,900");
85 tbl.cell(7, 2).unwrap().shading("D6E4F0");
86 tbl.cell(7, 3).unwrap().set_text("+19.1%");
87 tbl.cell(7, 3).unwrap().shading("D6E4F0");
88 }
89
90 doc.add_paragraph("");
91
92 // =========================================================================
93 // 2. Invoice-style table with merged header
94 // =========================================================================
95 doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96 .style("Heading2");
97
98 {
99 let mut tbl = doc.add_table(7, 4);
100 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101 tbl = tbl.width_pct(100.0);
102
103 // Merged title row
104 tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105 tbl.cell(0, 0).unwrap().grid_span(4);
106 tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108 // Column headers
109 let headers = ["Item", "Description", "Qty", "Amount"];
110 for (col, h) in headers.iter().enumerate() {
111 tbl.cell(1, col).unwrap().set_text(h);
112 tbl.cell(1, col).unwrap().shading("D6E4F0");
113 }
114
115 // Line items
116 tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117 tbl.cell(2, 1)
118 .unwrap()
119 .set_text("Enterprise License (500 seats)");
120 tbl.cell(2, 2).unwrap().set_text("1");
121 tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123 tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124 tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125 tbl.cell(3, 2).unwrap().set_text("1");
126 tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128 tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129 tbl.cell(4, 1)
130 .unwrap()
131 .set_text("On-site Training (3 days)");
132 tbl.cell(4, 2).unwrap().set_text("1");
133 tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135 // Subtotal
136 tbl.cell(5, 0).unwrap().set_text("Subtotal");
137 tbl.cell(5, 0).unwrap().grid_span(3);
138 tbl.cell(5, 0).unwrap().shading("F2F2F2");
139 tbl.cell(5, 3).unwrap().set_text("$89,500");
140 tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142 // Total
143 tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144 tbl.cell(6, 0).unwrap().grid_span(3);
145 tbl.cell(6, 0).unwrap().shading("1F4E79");
146 tbl.cell(6, 3).unwrap().set_text("$89,500");
147 tbl.cell(6, 3).unwrap().shading("1F4E79");
148 }
149
150 doc.add_paragraph("");
151
152 // =========================================================================
153 // 3. Specification table with vertical merge
154 // =========================================================================
155 doc.add_paragraph("3. Specification Table with Vertical Merges")
156 .style("Heading2");
157
158 {
159 let mut tbl = doc.add_table(8, 3);
160 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161 tbl = tbl.width_pct(100.0);
162
163 // Header
164 tbl.cell(0, 0).unwrap().set_text("Category");
165 tbl.cell(0, 0).unwrap().shading("2E75B6");
166 tbl.cell(0, 1).unwrap().set_text("Specification");
167 tbl.cell(0, 1).unwrap().shading("2E75B6");
168 tbl.cell(0, 2).unwrap().set_text("Value");
169 tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171 // "Hardware" spans 3 rows
172 tbl.cell(1, 0).unwrap().set_text("Hardware");
173 tbl.cell(1, 0).unwrap().v_merge_restart();
174 tbl.cell(1, 0).unwrap().shading("E2EFDA");
175 tbl.cell(1, 0)
176 .unwrap()
177 .vertical_alignment(VerticalAlignment::Center);
178 tbl.cell(1, 1).unwrap().set_text("Processor");
179 tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181 tbl.cell(2, 0).unwrap().v_merge_continue();
182 tbl.cell(2, 1).unwrap().set_text("Memory");
183 tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185 tbl.cell(3, 0).unwrap().v_merge_continue();
186 tbl.cell(3, 1).unwrap().set_text("Storage");
187 tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189 // "Network" spans 2 rows
190 tbl.cell(4, 0).unwrap().set_text("Network");
191 tbl.cell(4, 0).unwrap().v_merge_restart();
192 tbl.cell(4, 0).unwrap().shading("FCE4D6");
193 tbl.cell(4, 0)
194 .unwrap()
195 .vertical_alignment(VerticalAlignment::Center);
196 tbl.cell(4, 1).unwrap().set_text("Ethernet");
197 tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199 tbl.cell(5, 0).unwrap().v_merge_continue();
200 tbl.cell(5, 1).unwrap().set_text("Management");
201 tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203 // "Software" spans 2 rows
204 tbl.cell(6, 0).unwrap().set_text("Software");
205 tbl.cell(6, 0).unwrap().v_merge_restart();
206 tbl.cell(6, 0).unwrap().shading("D6E4F0");
207 tbl.cell(6, 0)
208 .unwrap()
209 .vertical_alignment(VerticalAlignment::Center);
210 tbl.cell(6, 1).unwrap().set_text("Operating System");
211 tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213 tbl.cell(7, 0).unwrap().v_merge_continue();
214 tbl.cell(7, 1).unwrap().set_text("Monitoring");
215 tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216 }
217
218 doc.add_paragraph("");
219
220 // =========================================================================
221 // 4. Nested table (table inside a cell)
222 // =========================================================================
223 doc.add_paragraph("4. Nested Table").style("Heading2");
224
225 {
226 let mut tbl = doc.add_table(2, 2);
227 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228 tbl = tbl.width_pct(100.0);
229 tbl = tbl.cell_margins(
230 Length::twips(72),
231 Length::twips(108),
232 Length::twips(72),
233 Length::twips(108),
234 );
235
236 tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237 tbl.cell(0, 0).unwrap().shading("2E75B6");
238 tbl.cell(0, 1).unwrap().set_text("Project Beta");
239 tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241 // Nested table in cell (1,0)
242 {
243 let mut cell = tbl.cell(1, 0).unwrap();
244 cell.set_text("Milestones:");
245 let mut inner = cell.add_table(3, 2);
246 inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247 inner.cell(0, 0).unwrap().set_text("Phase");
248 inner.cell(0, 0).unwrap().shading("E2EFDA");
249 inner.cell(0, 1).unwrap().set_text("Status");
250 inner.cell(0, 1).unwrap().shading("E2EFDA");
251 inner.cell(1, 0).unwrap().set_text("Design");
252 inner.cell(1, 1).unwrap().set_text("Complete");
253 inner.cell(2, 0).unwrap().set_text("Build");
254 inner.cell(2, 1).unwrap().set_text("In Progress");
255 }
256
257 // Nested table in cell (1,1)
258 {
259 let mut cell = tbl.cell(1, 1).unwrap();
260 cell.set_text("Budget:");
261 let mut inner = cell.add_table(3, 2);
262 inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263 inner.cell(0, 0).unwrap().set_text("Category");
264 inner.cell(0, 0).unwrap().shading("FCE4D6");
265 inner.cell(0, 1).unwrap().set_text("Amount");
266 inner.cell(0, 1).unwrap().shading("FCE4D6");
267 inner.cell(1, 0).unwrap().set_text("Development");
268 inner.cell(1, 1).unwrap().set_text("$120,000");
269 inner.cell(2, 0).unwrap().set_text("Testing");
270 inner.cell(2, 1).unwrap().set_text("$35,000");
271 }
272 }
273
274 doc.add_paragraph("");
275
276 // =========================================================================
277 // 5. Form-style table with labels
278 // =========================================================================
279 doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281 {
282 let mut tbl = doc.add_table(6, 4);
283 tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284 tbl = tbl.width_pct(100.0);
285
286 // Row 0: Full-width title
287 tbl.cell(0, 0)
288 .unwrap()
289 .set_text("Customer Registration Form");
290 tbl.cell(0, 0).unwrap().grid_span(4);
291 tbl.cell(0, 0).unwrap().shading("404040");
292
293 // Row 1: Name fields
294 tbl.cell(1, 0).unwrap().set_text("First Name");
295 tbl.cell(1, 0).unwrap().shading("E8E8E8");
296 tbl.cell(1, 1).unwrap().set_text("John");
297 tbl.cell(1, 2).unwrap().set_text("Last Name");
298 tbl.cell(1, 2).unwrap().shading("E8E8E8");
299 tbl.cell(1, 3).unwrap().set_text("Smith");
300
301 // Row 2: Contact
302 tbl.cell(2, 0).unwrap().set_text("Email");
303 tbl.cell(2, 0).unwrap().shading("E8E8E8");
304 tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305 tbl.cell(2, 1).unwrap().grid_span(3);
306
307 // Row 3: Phone
308 tbl.cell(3, 0).unwrap().set_text("Phone");
309 tbl.cell(3, 0).unwrap().shading("E8E8E8");
310 tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311 tbl.cell(3, 2).unwrap().set_text("Company");
312 tbl.cell(3, 2).unwrap().shading("E8E8E8");
313 tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315 // Row 4: Address (spanning)
316 tbl.cell(4, 0).unwrap().set_text("Address");
317 tbl.cell(4, 0).unwrap().shading("E8E8E8");
318 tbl.cell(4, 1)
319 .unwrap()
320 .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321 tbl.cell(4, 1).unwrap().grid_span(3);
322
323 // Row 5: Notes
324 tbl.cell(5, 0).unwrap().set_text("Notes");
325 tbl.cell(5, 0).unwrap().shading("E8E8E8");
326 tbl.cell(5, 0)
327 .unwrap()
328 .vertical_alignment(VerticalAlignment::Top);
329 {
330 let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331 cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332 cell.add_paragraph("Annual review scheduled for March 2026.");
333 }
334 }
335
336 doc.add_paragraph("");
337
338 // =========================================================================
339 // 6. Comparison table with border styles
340 // =========================================================================
341 doc.add_paragraph("6. Comparison Table with Custom Borders")
342 .style("Heading2");
343
344 {
345 let mut tbl = doc.add_table(5, 3);
346 tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347 tbl = tbl.width_pct(100.0);
348
349 // Header
350 tbl.cell(0, 0).unwrap().set_text("Feature");
351 tbl.cell(0, 0).unwrap().shading("2E75B6");
352 tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353 tbl.cell(0, 1).unwrap().shading("2E75B6");
354 tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355 tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357 tbl.cell(1, 0).unwrap().set_text("Users");
358 tbl.cell(1, 1).unwrap().set_text("Up to 10");
359 tbl.cell(1, 2).unwrap().set_text("Unlimited");
360 tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362 tbl.cell(2, 0).unwrap().set_text("Storage");
363 tbl.cell(2, 1).unwrap().set_text("50 GB");
364 tbl.cell(2, 2).unwrap().set_text("5 TB");
365 tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367 tbl.cell(3, 0).unwrap().set_text("Support");
368 tbl.cell(3, 1).unwrap().set_text("Email only");
369 tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370 tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372 tbl.cell(4, 0).unwrap().set_text("Price");
373 tbl.cell(4, 0).unwrap().shading("F2F2F2");
374 tbl.cell(4, 1).unwrap().set_text("$29/month");
375 tbl.cell(4, 1).unwrap().shading("F2F2F2");
376 tbl.cell(4, 2).unwrap().set_text("$199/month");
377 tbl.cell(4, 2).unwrap().shading("C6EFCE");
378 }
379
380 doc.add_paragraph("");
381
382 // =========================================================================
383 // 7. Wide table with fixed layout and row height
384 // =========================================================================
385 doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386 .style("Heading2");
387
388 {
389 let mut tbl = doc.add_table(4, 5);
390 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391 tbl = tbl.width(Length::inches(7.0));
392 tbl = tbl.layout_fixed();
393
394 // Set column widths
395 for col in 0..5 {
396 tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397 }
398
399 // Header with exact height
400 tbl.row(0).unwrap().height_exact(Length::twips(480));
401 tbl.row(0).unwrap().header();
402 tbl.row(0).unwrap().cant_split();
403
404 let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405 for (col, h) in headers.iter().enumerate() {
406 tbl.cell(0, col).unwrap().set_text(h);
407 tbl.cell(0, col).unwrap().shading("404040");
408 tbl.cell(0, col)
409 .unwrap()
410 .vertical_alignment(VerticalAlignment::Center);
411 }
412
413 // Schedule rows with minimum height
414 tbl.row(1).unwrap().height(Length::twips(600));
415 tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416 tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417 tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418 tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419 tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421 tbl.row(2).unwrap().height(Length::twips(600));
422 tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423 tbl.cell(2, 0).unwrap().shading("D6E4F0");
424 tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425 tbl.cell(2, 1).unwrap().shading("FCE4D6");
426 tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427 tbl.cell(2, 2).unwrap().shading("D6E4F0");
428 tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429 tbl.cell(2, 3).unwrap().shading("E2EFDA");
430 tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431 tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433 tbl.row(3).unwrap().height(Length::twips(600));
434 tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435 tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436 tbl.cell(3, 1).unwrap().shading("D6E4F0");
437 tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438 tbl.cell(3, 2).unwrap().shading("FCE4D6");
439 tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440 tbl.cell(3, 3).unwrap().shading("D6E4F0");
441 tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442 tbl.cell(3, 4).unwrap().shading("E2EFDA");
443 }
444
445 doc.set_title("Styled Tables Showcase");
446 doc.set_author("rdocx");
447
448 doc.save(path).unwrap();
449}More examples
examples/generate_samples.rs (line 392)
34fn generate_feature_showcase(path: &Path) {
35 let mut doc = Document::new();
36
37 // =========================================================================
38 // PAGE SETUP & METADATA
39 // =========================================================================
40 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41 doc.set_margins(
42 Length::inches(1.0), // top
43 Length::inches(1.0), // right
44 Length::inches(1.0), // bottom
45 Length::inches(1.0), // left
46 );
47 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48 doc.set_gutter(Length::twips(0));
49
50 doc.set_title("rdocx Feature Showcase");
51 doc.set_author("rdocx Sample Generator");
52 doc.set_subject("Comprehensive feature demonstration");
53 doc.set_keywords("rdocx, docx, rust, sample");
54
55 // Header & Footer
56 doc.set_header("rdocx Feature Showcase");
57 doc.set_footer("Generated by rdocx — Page");
58
59 // Different first page header
60 doc.set_different_first_page(true);
61 doc.set_first_page_header("rdocx");
62 doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64 // =========================================================================
65 // PAGE 1: COVER PAGE — background image, run formatting
66 // =========================================================================
67 let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68 doc.add_background_image(&bg_cover, "cover_bg.png");
69
70 doc.add_paragraph(""); // spacer
71 doc.add_paragraph(""); // spacer
72 doc.add_paragraph(""); // spacer
73
74 {
75 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77 }
78 {
79 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80 p.add_run("Feature Showcase")
81 .size(28.0)
82 .color("FFFFFF")
83 .italic(true);
84 }
85
86 doc.add_paragraph(""); // spacer
87
88 {
89 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90 p.add_run("A comprehensive demonstration of every feature")
91 .size(14.0)
92 .color("CCDDFF");
93 }
94 {
95 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96 p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97 .size(14.0)
98 .color("CCDDFF");
99 }
100
101 // =========================================================================
102 // PAGE 2: TEXT FORMATTING
103 // =========================================================================
104 doc.add_paragraph("").page_break_before(true);
105
106 doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108 doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109 doc.add_paragraph("");
110
111 // --- Paragraph alignment ---
112 doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114 doc.add_paragraph("This paragraph is left-aligned (the default).")
115 .alignment(Alignment::Left);
116 doc.add_paragraph("This paragraph is center-aligned.")
117 .alignment(Alignment::Center);
118 doc.add_paragraph("This paragraph is right-aligned.")
119 .alignment(Alignment::Right);
120 doc.add_paragraph(
121 "This paragraph is justified. To demonstrate justified text properly, it needs \
122 to be long enough to span multiple lines so the word spacing adjustment is visible \
123 across the full width of the text area on the page.",
124 )
125 .alignment(Alignment::Justify);
126
127 doc.add_paragraph("");
128
129 // --- Run formatting ---
130 doc.add_paragraph("Run Formatting").style("Heading2");
131
132 {
133 let mut p = doc.add_paragraph("");
134 p.add_run("Bold text").bold(true);
135 p.add_run(" | ");
136 p.add_run("Italic text").italic(true);
137 p.add_run(" | ");
138 p.add_run("Bold + Italic").bold(true).italic(true);
139 }
140 {
141 let mut p = doc.add_paragraph("");
142 p.add_run("Single underline").underline(true);
143 p.add_run(" | ");
144 p.add_run("Strikethrough").strike(true);
145 p.add_run(" | ");
146 p.add_run("Double strikethrough").double_strike(true);
147 }
148 {
149 let mut p = doc.add_paragraph("");
150 p.add_run("Red text").color("FF0000");
151 p.add_run(" | ");
152 p.add_run("Blue text").color("0000FF");
153 p.add_run(" | ");
154 p.add_run("Green text").color("00AA00");
155 p.add_run(" | ");
156 p.add_run("Highlighted").highlight("FFFF00");
157 }
158 {
159 let mut p = doc.add_paragraph("");
160 p.add_run("8pt small").size(8.0);
161 p.add_run(" | ");
162 p.add_run("11pt normal").size(11.0);
163 p.add_run(" | ");
164 p.add_run("16pt large").size(16.0);
165 p.add_run(" | ");
166 p.add_run("24pt extra-large").size(24.0);
167 }
168 {
169 let mut p = doc.add_paragraph("");
170 p.add_run("Arial font").font("Arial");
171 p.add_run(" | ");
172 p.add_run("Times New Roman font").font("Times New Roman");
173 p.add_run(" | ");
174 p.add_run("Courier New font").font("Courier New");
175 }
176 {
177 let mut p = doc.add_paragraph("");
178 p.add_run("Normal");
179 p.add_run(" H").size(11.0);
180 p.add_run("2").subscript();
181 p.add_run("O (subscript)").size(11.0);
182 p.add_run(" | E = mc").size(11.0);
183 p.add_run("2").superscript();
184 p.add_run(" (superscript)").size(11.0);
185 }
186 {
187 let mut p = doc.add_paragraph("");
188 p.add_run("ALL CAPS").all_caps(true);
189 p.add_run(" | ");
190 p.add_run("Small Caps").small_caps(true);
191 p.add_run(" | ");
192 p.add_run("Expanded spacing")
193 .character_spacing(Length::twips(40));
194 }
195
196 doc.add_paragraph("");
197
198 // --- Paragraph formatting ---
199 doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201 doc.add_paragraph("Paragraph with shading (light green background)")
202 .shading("E2EFDA");
203
204 doc.add_paragraph("Paragraph with bottom border")
205 .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207 doc.add_paragraph("Paragraph with all borders")
208 .border_all(BorderStyle::Single, 4, "FF0000");
209
210 doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211 .indent_left(Length::inches(1.0))
212 .hanging_indent(Length::inches(0.5));
213
214 doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215 .first_line_indent(Length::inches(0.5));
216
217 doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218 .space_before(Length::pt(24.0))
219 .space_after(Length::pt(12.0));
220
221 doc.add_paragraph(
222 "Paragraph with double line spacing. This text should have extra vertical \
223 space between lines to demonstrate the line_spacing_multiple setting.",
224 )
225 .line_spacing_multiple(2.0);
226
227 doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228 .keep_with_next(true);
229 doc.add_paragraph("(This stays with the paragraph above.)");
230
231 // =========================================================================
232 // PAGE 3: LISTS & TAB STOPS
233 // =========================================================================
234 doc.add_paragraph("").page_break_before(true);
235
236 doc.add_paragraph("2. Lists").style("Heading1");
237
238 doc.add_paragraph("Bullet List").style("Heading2");
239
240 doc.add_bullet_list_item("First bullet item", 0);
241 doc.add_bullet_list_item("Second bullet item", 0);
242 doc.add_bullet_list_item("Nested level 1", 1);
243 doc.add_bullet_list_item("Nested level 2", 2);
244 doc.add_bullet_list_item("Back to level 1", 1);
245 doc.add_bullet_list_item("Third bullet item", 0);
246
247 doc.add_paragraph("");
248
249 doc.add_paragraph("Numbered List").style("Heading2");
250
251 doc.add_numbered_list_item("First numbered item", 0);
252 doc.add_numbered_list_item("Second numbered item", 0);
253 doc.add_numbered_list_item("Sub-item A", 1);
254 doc.add_numbered_list_item("Sub-item B", 1);
255 doc.add_numbered_list_item("Third numbered item", 0);
256
257 doc.add_paragraph("");
258
259 // --- Tab stops ---
260 doc.add_paragraph("Tab Stops").style("Heading2");
261
262 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268 doc.add_paragraph("Item\t........\tPrice")
269 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273 doc.add_paragraph("Widget A\t........\t$19.99")
274 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278 doc.add_paragraph("Gadget B\t________\t$249.50")
279 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280 .add_tab_stop_with_leader(
281 TabAlignment::Right,
282 Length::inches(4.0),
283 TabLeader::Underscore,
284 )
285 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287 // =========================================================================
288 // PAGE 4: TABLES
289 // =========================================================================
290 doc.add_paragraph("").page_break_before(true);
291
292 doc.add_paragraph("3. Tables").style("Heading1");
293
294 // --- Basic table with borders ---
295 doc.add_paragraph("Basic Table with Borders")
296 .style("Heading2");
297
298 {
299 let mut tbl = doc.add_table(4, 3);
300 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302 // Header row
303 for col in 0..3 {
304 tbl.cell(0, col).unwrap().shading("2E75B6");
305 }
306 tbl.cell(0, 0).unwrap().set_text("Name");
307 tbl.cell(0, 1).unwrap().set_text("Role");
308 tbl.cell(0, 2).unwrap().set_text("Location");
309
310 tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311 tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312 tbl.cell(1, 2).unwrap().set_text("New York");
313
314 tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315 tbl.cell(2, 1).unwrap().set_text("Product Manager");
316 tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318 tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319 tbl.cell(3, 1).unwrap().set_text("Designer");
320 tbl.cell(3, 2).unwrap().set_text("London");
321 }
322
323 doc.add_paragraph("");
324
325 // --- Table with cell merging ---
326 doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327 .style("Heading2");
328
329 {
330 let mut tbl = doc.add_table(4, 4);
331 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332 tbl = tbl.width_pct(100.0);
333
334 // Header spanning all columns
335 tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336 tbl.cell(0, 0).unwrap().shading("1F4E79");
337 tbl.cell(0, 0).unwrap().grid_span(4);
338
339 // Sub-header
340 tbl.cell(1, 0).unwrap().set_text("Region");
341 tbl.cell(1, 0).unwrap().shading("D6E4F0");
342 tbl.cell(1, 1).unwrap().set_text("Q1");
343 tbl.cell(1, 1).unwrap().shading("D6E4F0");
344 tbl.cell(1, 2).unwrap().set_text("Q2");
345 tbl.cell(1, 2).unwrap().shading("D6E4F0");
346 tbl.cell(1, 3).unwrap().set_text("Total");
347 tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349 // Data
350 tbl.cell(2, 0).unwrap().set_text("North America");
351 tbl.cell(2, 1).unwrap().set_text("$2.4M");
352 tbl.cell(2, 2).unwrap().set_text("$2.7M");
353 tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355 tbl.cell(3, 0).unwrap().set_text("Europe");
356 tbl.cell(3, 1).unwrap().set_text("$1.8M");
357 tbl.cell(3, 2).unwrap().set_text("$2.0M");
358 tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360 // Vertical alignment on data cells
361 tbl.cell(2, 3)
362 .unwrap()
363 .vertical_alignment(VerticalAlignment::Center);
364 tbl.cell(3, 3)
365 .unwrap()
366 .vertical_alignment(VerticalAlignment::Bottom);
367 }
368
369 doc.add_paragraph("");
370
371 // --- Table with vertical merge ---
372 doc.add_paragraph("Table with Vertical Merge")
373 .style("Heading2");
374
375 {
376 let mut tbl = doc.add_table(4, 3);
377 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379 tbl.cell(0, 0).unwrap().set_text("Category");
380 tbl.cell(0, 0).unwrap().shading("E2EFDA");
381 tbl.cell(0, 1).unwrap().set_text("Item");
382 tbl.cell(0, 1).unwrap().shading("E2EFDA");
383 tbl.cell(0, 2).unwrap().set_text("Price");
384 tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386 // "Hardware" spans rows 1-2
387 tbl.cell(1, 0).unwrap().set_text("Hardware");
388 tbl.cell(1, 0).unwrap().v_merge_restart();
389 tbl.cell(1, 1).unwrap().set_text("Laptop");
390 tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392 tbl.cell(2, 0).unwrap().v_merge_continue();
393 tbl.cell(2, 1).unwrap().set_text("Monitor");
394 tbl.cell(2, 2).unwrap().set_text("$450");
395
396 // "Software" on row 3
397 tbl.cell(3, 0).unwrap().set_text("Software");
398 tbl.cell(3, 1).unwrap().set_text("IDE License");
399 tbl.cell(3, 2).unwrap().set_text("$200/yr");
400 }
401
402 doc.add_paragraph("");
403
404 // --- Nested table ---
405 doc.add_paragraph("Nested Table").style("Heading2");
406
407 {
408 let mut tbl = doc.add_table(2, 2);
409 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411 tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412 tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413 tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415 // Nested table inside cell (1,1)
416 {
417 let mut cell = tbl.cell(1, 1).unwrap();
418 cell.set_text("Contains nested table:");
419 let mut nested = cell.add_table(2, 2);
420 nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421 nested.cell(0, 0).unwrap().set_text("Inner A");
422 nested.cell(0, 1).unwrap().set_text("Inner B");
423 nested.cell(1, 0).unwrap().set_text("Inner C");
424 nested.cell(1, 1).unwrap().set_text("Inner D");
425 }
426 }
427
428 // =========================================================================
429 // PAGE 5: IMAGES
430 // =========================================================================
431 doc.add_paragraph("").page_break_before(true);
432
433 doc.add_paragraph("4. Images").style("Heading1");
434
435 doc.add_paragraph("Inline Image").style("Heading2");
436
437 doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438 let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439 doc.add_picture(
440 &inline_img,
441 "inline_chart.png",
442 Length::inches(3.0),
443 Length::inches(0.75),
444 );
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("Header Image").style("Heading2");
449
450 // Replace the text-only header with an image header
451 let header_img = create_sample_png(400, 40, [40, 40, 40]);
452 doc.set_header_image(
453 &header_img,
454 "header_logo.png",
455 Length::inches(2.0),
456 Length::inches(0.2),
457 );
458
459 doc.add_paragraph(
460 "The document header has been replaced with an inline image. \
461 Check the header area at the top of this page.",
462 );
463
464 doc.add_paragraph("");
465 doc.add_paragraph(
466 "Note: The cover page uses a full-page background image behind the text, \
467 demonstrated on page 1 via add_background_image().",
468 );
469
470 // =========================================================================
471 // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472 // =========================================================================
473 doc.add_paragraph("").page_break_before(true);
474
475 doc.add_paragraph("5. Content Manipulation")
476 .style("Heading1");
477
478 // --- Placeholder replacement ---
479 doc.add_paragraph("Placeholder Replacement")
480 .style("Heading2");
481
482 doc.add_paragraph(
483 "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484 );
485
486 {
487 let mut p = doc.add_paragraph("");
488 p.add_run("Customer: ").bold(true);
489 p.add_run("{{customer}}");
490 }
491 {
492 let mut p = doc.add_paragraph("");
493 p.add_run("Date: ").bold(true);
494 p.add_run("{{date}}");
495 }
496 doc.add_paragraph("Reference: {{ref_number}}");
497
498 // Table with placeholders
499 {
500 let mut tbl = doc.add_table(3, 2);
501 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502 tbl.cell(0, 0).unwrap().set_text("Field");
503 tbl.cell(0, 0).unwrap().shading("D6E4F0");
504 tbl.cell(0, 1).unwrap().set_text("Value");
505 tbl.cell(0, 1).unwrap().shading("D6E4F0");
506 tbl.cell(1, 0).unwrap().set_text("Project");
507 tbl.cell(1, 1).unwrap().set_text("{{project}}");
508 tbl.cell(2, 0).unwrap().set_text("Status");
509 tbl.cell(2, 1).unwrap().set_text("{{status}}");
510 }
511
512 // Perform replacements
513 let mut replacements = HashMap::new();
514 replacements.insert("{{customer}}", "Acme Corporation");
515 replacements.insert("{{date}}", "February 22, 2026");
516 replacements.insert("{{ref_number}}", "REF-2026-001");
517 replacements.insert("{{project}}", "Infrastructure Upgrade");
518 replacements.insert("{{status}}", "In Progress");
519 let replace_count = doc.replace_all(&replacements);
520
521 doc.add_paragraph("");
522 doc.add_paragraph(&format!(
523 "(Replaced {} placeholders above — in body text and table cells)",
524 replace_count
525 ));
526
527 doc.add_paragraph("");
528
529 // --- Content insertion ---
530 doc.add_paragraph("Content Insertion").style("Heading2");
531
532 doc.add_paragraph("Section A: First section of content.");
533 doc.add_paragraph("Section C: Third section of content.");
534
535 // Insert "Section B" between A and C
536 if let Some(idx) = doc.find_content_index("Section C") {
537 doc.insert_paragraph(
538 idx,
539 "Section B: Inserted between A and C using find_content_index().",
540 );
541 }
542
543 doc.add_paragraph("");
544 doc.add_paragraph(
545 "The paragraph above ('Section B') was inserted at a specific position \
546 using find_content_index() + insert_paragraph().",
547 );
548
549 // =========================================================================
550 // PAGE 7: LANDSCAPE — section break, wide table
551 // =========================================================================
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554 doc.add_paragraph("6. Mixed Page Orientation")
555 .style("Heading1");
556
557 doc.add_paragraph(
558 "This page is in LANDSCAPE orientation. It was created using a section break \
559 followed by section_landscape(). This is useful for wide tables or charts.",
560 );
561
562 doc.add_paragraph("");
563
564 // Wide table for landscape
565 {
566 let mut tbl = doc.add_table(4, 7);
567 tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570 for (col, h) in headers.iter().enumerate() {
571 tbl.cell(0, col).unwrap().set_text(h);
572 tbl.cell(0, col).unwrap().shading("2E75B6");
573 }
574
575 let data = [
576 [
577 "North America",
578 "$1.2M",
579 "$1.3M",
580 "$1.4M",
581 "$1.5M",
582 "$1.6M",
583 "$7.0M",
584 ],
585 [
586 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587 ],
588 [
589 "Asia Pacific",
590 "$0.5M",
591 "$0.6M",
592 "$0.7M",
593 "$0.7M",
594 "$0.8M",
595 "$3.3M",
596 ],
597 ];
598 for (row_idx, row_data) in data.iter().enumerate() {
599 for (col, val) in row_data.iter().enumerate() {
600 tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601 }
602 }
603 }
604
605 // End landscape, return to portrait
606 doc.add_paragraph("")
607 .section_break(SectionBreak::NextPage)
608 .section_landscape();
609
610 // =========================================================================
611 // PAGE 8: BACK TO PORTRAIT — styles, final notes
612 // =========================================================================
613 doc.add_paragraph("7. Custom Styles & Summary")
614 .style("Heading1");
615
616 doc.add_paragraph(
617 "This final page is back in portrait orientation after a section break. \
618 The document has demonstrated:",
619 );
620
621 doc.add_bullet_list_item(
622 "Page setup: size, margins, header/footer distance, gutter",
623 0,
624 );
625 doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626 doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627 doc.add_bullet_list_item("Background images: full-page behind text", 0);
628 doc.add_bullet_list_item(
629 "Text formatting: bold, italic, underline, strike, color, size, font",
630 0,
631 );
632 doc.add_bullet_list_item(
633 "Advanced run formatting: superscript, subscript, caps, spacing",
634 0,
635 );
636 doc.add_bullet_list_item(
637 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638 0,
639 );
640 doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641 doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642 doc.add_bullet_list_item(
643 "Tables: borders, shading, column spans, row spans, nesting",
644 0,
645 );
646 doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647 doc.add_bullet_list_item("Inline images", 0);
648 doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649 doc.add_bullet_list_item("Content insertion at specific positions", 0);
650 doc.add_bullet_list_item(
651 "Section breaks with mixed portrait/landscape orientation",
652 0,
653 );
654
655 doc.add_paragraph("");
656 doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657 .alignment(Alignment::Center)
658 .shading("E2EFDA")
659 .border_all(BorderStyle::Single, 2, "00AA00");
660
661 doc.save(path).unwrap();
662}examples/generate_all_samples.rs (line 415)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87 let mut doc = Document::new();
88
89 // ── Page Setup & Metadata ──
90 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91 doc.set_margins(
92 Length::inches(1.0),
93 Length::inches(1.0),
94 Length::inches(1.0),
95 Length::inches(1.0),
96 );
97 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98 doc.set_gutter(Length::twips(0));
99 doc.set_title("rdocx Feature Showcase");
100 doc.set_author("rdocx Sample Generator");
101 doc.set_subject("Comprehensive feature demonstration");
102 doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104 // Headers & Footers with different first page
105 doc.set_different_first_page(true);
106 doc.set_first_page_header("rdocx");
107 doc.set_first_page_footer("Feature Showcase — Cover Page");
108 doc.set_header("rdocx Feature Showcase");
109 doc.set_footer("Generated by rdocx");
110
111 // ── COVER PAGE ──
112 let bg = create_sample_png(612, 792, [20, 45, 90]);
113 doc.add_background_image(&bg, "cover_bg.png");
114
115 for _ in 0..3 {
116 doc.add_paragraph("");
117 }
118 {
119 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121 }
122 {
123 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124 p.add_run("Complete Feature Showcase")
125 .size(28.0)
126 .color("FFFFFF")
127 .italic(true);
128 }
129 doc.add_paragraph("");
130 {
131 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132 p.add_run("Every feature of the rdocx Rust library")
133 .size(13.0)
134 .color("B0C4DE");
135 }
136 {
137 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138 p.add_run("demonstrated in a single document.")
139 .size(13.0)
140 .color("B0C4DE");
141 }
142
143 // ── TABLE OF CONTENTS ──
144 doc.add_paragraph("").page_break_before(true);
145 doc.insert_toc(doc.content_count(), 3);
146
147 // ── SECTION 1: TEXT FORMATTING ──
148 doc.add_paragraph("").page_break_before(true);
149 doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151 // Paragraph Alignment
152 doc.add_paragraph("1.1 Paragraph Alignment")
153 .style("Heading2");
154 doc.add_paragraph("Left-aligned paragraph (default).")
155 .alignment(Alignment::Left);
156 doc.add_paragraph("Center-aligned paragraph.")
157 .alignment(Alignment::Center);
158 doc.add_paragraph("Right-aligned paragraph.")
159 .alignment(Alignment::Right);
160 doc.add_paragraph(
161 "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162 distributes extra space across word gaps so lines fill the full width of the text area.",
163 )
164 .alignment(Alignment::Justify);
165
166 doc.add_paragraph("");
167
168 // Run Formatting
169 doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170 {
171 let mut p = doc.add_paragraph("");
172 p.add_run("Bold").bold(true);
173 p.add_run(" | ");
174 p.add_run("Italic").italic(true);
175 p.add_run(" | ");
176 p.add_run("Bold Italic").bold(true).italic(true);
177 p.add_run(" | ");
178 p.add_run("Underline").underline(true);
179 p.add_run(" | ");
180 p.add_run("Strikethrough").strike(true);
181 p.add_run(" | ");
182 p.add_run("Double Strike").double_strike(true);
183 }
184 {
185 let mut p = doc.add_paragraph("");
186 p.add_run("Red").color("FF0000");
187 p.add_run(" | ");
188 p.add_run("Blue").color("0000FF");
189 p.add_run(" | ");
190 p.add_run("Green").color("00AA00");
191 p.add_run(" | ");
192 p.add_run("Highlighted").highlight("FFFF00");
193 }
194 {
195 let mut p = doc.add_paragraph("");
196 p.add_run("8pt").size(8.0);
197 p.add_run(" | ");
198 p.add_run("11pt").size(11.0);
199 p.add_run(" | ");
200 p.add_run("16pt").size(16.0);
201 p.add_run(" | ");
202 p.add_run("24pt").size(24.0);
203 }
204 {
205 let mut p = doc.add_paragraph("");
206 p.add_run("Arial").font("Arial");
207 p.add_run(" | ");
208 p.add_run("Times New Roman").font("Times New Roman");
209 p.add_run(" | ");
210 p.add_run("Courier New").font("Courier New");
211 }
212 {
213 let mut p = doc.add_paragraph("");
214 p.add_run("H");
215 p.add_run("2").subscript();
216 p.add_run("O (subscript) | E=mc");
217 p.add_run("2").superscript();
218 p.add_run(" (superscript)");
219 }
220 {
221 let mut p = doc.add_paragraph("");
222 p.add_run("ALL CAPS").all_caps(true);
223 p.add_run(" | ");
224 p.add_run("Small Caps").small_caps(true);
225 p.add_run(" | ");
226 p.add_run("Expanded").character_spacing(Length::twips(40));
227 p.add_run(" | ");
228 p.add_run("Hidden text (not visible)").hidden(true);
229 }
230
231 doc.add_paragraph("");
232
233 // Underline Styles
234 doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235 {
236 let mut p = doc.add_paragraph("");
237 p.add_run("Single ")
238 .underline_style(rdocx::UnderlineStyle::Single);
239 p.add_run("Double ")
240 .underline_style(rdocx::UnderlineStyle::Double);
241 p.add_run("Thick ")
242 .underline_style(rdocx::UnderlineStyle::Thick);
243 p.add_run("Dotted ")
244 .underline_style(rdocx::UnderlineStyle::Dotted);
245 p.add_run("Dash ")
246 .underline_style(rdocx::UnderlineStyle::Dash);
247 p.add_run("Wave ")
248 .underline_style(rdocx::UnderlineStyle::Wave);
249 }
250
251 // ── SECTION 2: PARAGRAPH FORMATTING ──
252 doc.add_paragraph("").page_break_before(true);
253 doc.add_paragraph("2. Paragraph Formatting")
254 .style("Heading1");
255
256 doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257 doc.add_paragraph("Paragraph with light green shading")
258 .shading("E2EFDA");
259 doc.add_paragraph("Paragraph with bottom border")
260 .border_bottom(BorderStyle::Single, 6, "2E75B6");
261 doc.add_paragraph("Paragraph with all borders (red)")
262 .border_all(BorderStyle::Single, 4, "FF0000");
263
264 doc.add_paragraph("2.2 Indentation").style("Heading2");
265 doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266 .indent_left(Length::inches(1.0))
267 .hanging_indent(Length::inches(0.5));
268 doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269 .first_line_indent(Length::inches(0.5));
270 doc.add_paragraph("Both left and right indent (0.75 inches each)")
271 .indent_left(Length::inches(0.75))
272 .indent_right(Length::inches(0.75));
273
274 doc.add_paragraph("2.3 Spacing & Line Height")
275 .style("Heading2");
276 doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277 .space_before(Length::pt(24.0))
278 .space_after(Length::pt(12.0));
279 doc.add_paragraph(
280 "Double line spacing paragraph. This text should have extra vertical space between \
281 lines to demonstrate line_spacing_multiple(2.0).",
282 )
283 .line_spacing_multiple(2.0);
284 doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285 .line_spacing(20.0);
286
287 doc.add_paragraph("2.4 Pagination Controls")
288 .style("Heading2");
289 doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290 .keep_with_next(true);
291 doc.add_paragraph("(This paragraph was kept with the one above.)");
292 doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293 .keep_together(true);
294 doc.add_paragraph("widow_control — prevents widow/orphan lines")
295 .widow_control(true);
296
297 // ── SECTION 3: LISTS ──
298 doc.add_paragraph("").page_break_before(true);
299 doc.add_paragraph("3. Lists").style("Heading1");
300
301 doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302 doc.add_bullet_list_item("First item", 0);
303 doc.add_bullet_list_item("Second item", 0);
304 doc.add_bullet_list_item("Nested level 1", 1);
305 doc.add_bullet_list_item("Nested level 2", 2);
306 doc.add_bullet_list_item("Back to level 1", 1);
307 doc.add_bullet_list_item("Third item", 0);
308
309 doc.add_paragraph("");
310
311 doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312 doc.add_numbered_list_item("First numbered item", 0);
313 doc.add_numbered_list_item("Second numbered item", 0);
314 doc.add_numbered_list_item("Sub-item A", 1);
315 doc.add_numbered_list_item("Sub-item B", 1);
316 doc.add_numbered_list_item("Third numbered item", 0);
317
318 // ── SECTION 4: TAB STOPS ──
319 doc.add_paragraph("");
320 doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322 doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329 doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330 doc.add_paragraph("Item\t\tPrice")
331 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334 doc.add_paragraph("Widget\t\t$19.99")
335 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338 doc.add_paragraph("Gadget\t\t$249.50")
339 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340 .add_tab_stop_with_leader(
341 TabAlignment::Right,
342 Length::inches(4.0),
343 TabLeader::Underscore,
344 )
345 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347 // ── SECTION 5: TABLES ──
348 doc.add_paragraph("").page_break_before(true);
349 doc.add_paragraph("5. Tables").style("Heading1");
350
351 doc.add_paragraph("5.1 Basic Table").style("Heading2");
352 {
353 let mut tbl = doc
354 .add_table(4, 3)
355 .borders(BorderStyle::Single, 4, "000000");
356 for col in 0..3 {
357 tbl.cell(0, col).unwrap().shading("2E75B6");
358 }
359 tbl.cell(0, 0).unwrap().set_text("Name");
360 tbl.cell(0, 1).unwrap().set_text("Role");
361 tbl.cell(0, 2).unwrap().set_text("Location");
362 tbl.cell(1, 0).unwrap().set_text("Walter White");
363 tbl.cell(1, 1).unwrap().set_text("CEO");
364 tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365 tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366 tbl.cell(2, 1).unwrap().set_text("CTO");
367 tbl.cell(2, 2).unwrap().set_text("Remote");
368 tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369 tbl.cell(3, 1).unwrap().set_text("Security");
370 tbl.cell(3, 2).unwrap().set_text("Washington");
371 }
372
373 doc.add_paragraph("");
374
375 doc.add_paragraph("5.2 Column Span & Cell Shading")
376 .style("Heading2");
377 {
378 let mut tbl = doc
379 .add_table(3, 4)
380 .borders(BorderStyle::Single, 4, "000000")
381 .width_pct(100.0);
382 tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383 tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384 tbl.cell(1, 0).unwrap().set_text("Region");
385 tbl.cell(1, 0).unwrap().shading("D6E4F0");
386 tbl.cell(1, 1).unwrap().set_text("Q1");
387 tbl.cell(1, 1).unwrap().shading("D6E4F0");
388 tbl.cell(1, 2).unwrap().set_text("Q2");
389 tbl.cell(1, 2).unwrap().shading("D6E4F0");
390 tbl.cell(1, 3).unwrap().set_text("Total");
391 tbl.cell(1, 3).unwrap().shading("D6E4F0");
392 tbl.cell(2, 0).unwrap().set_text("Americas");
393 tbl.cell(2, 1).unwrap().set_text("$2.4M");
394 tbl.cell(2, 2).unwrap().set_text("$2.7M");
395 tbl.cell(2, 3).unwrap().set_text("$5.1M");
396 }
397
398 doc.add_paragraph("");
399
400 doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401 {
402 let mut tbl = doc
403 .add_table(4, 3)
404 .borders(BorderStyle::Single, 4, "000000");
405 tbl.cell(0, 0).unwrap().set_text("Category");
406 tbl.cell(0, 0).unwrap().shading("E2EFDA");
407 tbl.cell(0, 1).unwrap().set_text("Item");
408 tbl.cell(0, 1).unwrap().shading("E2EFDA");
409 tbl.cell(0, 2).unwrap().set_text("Price");
410 tbl.cell(0, 2).unwrap().shading("E2EFDA");
411 tbl.cell(1, 0).unwrap().set_text("Hardware");
412 tbl.cell(1, 0).unwrap().v_merge_restart();
413 tbl.cell(1, 1).unwrap().set_text("Laptop");
414 tbl.cell(1, 2).unwrap().set_text("$1,200");
415 tbl.cell(2, 0).unwrap().v_merge_continue();
416 tbl.cell(2, 1).unwrap().set_text("Monitor");
417 tbl.cell(2, 2).unwrap().set_text("$450");
418 tbl.cell(3, 0).unwrap().set_text("Software");
419 tbl.cell(3, 1).unwrap().set_text("IDE License");
420 tbl.cell(3, 2).unwrap().set_text("$200/yr");
421 }
422
423 doc.add_paragraph("");
424
425 doc.add_paragraph("5.4 Nested Table").style("Heading2");
426 {
427 let mut tbl = doc
428 .add_table(2, 2)
429 .borders(BorderStyle::Single, 6, "2E75B6");
430 tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431 tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432 tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433 {
434 let mut cell = tbl.cell(1, 1).unwrap();
435 cell.set_text("Nested table below:");
436 let mut nested = cell
437 .add_table(2, 2)
438 .borders(BorderStyle::Single, 2, "FF6600");
439 nested.cell(0, 0).unwrap().set_text("A");
440 nested.cell(0, 1).unwrap().set_text("B");
441 nested.cell(1, 0).unwrap().set_text("C");
442 nested.cell(1, 1).unwrap().set_text("D");
443 }
444 }
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449 .style("Heading2");
450 {
451 let mut tbl = doc
452 .add_table(2, 3)
453 .borders(BorderStyle::Single, 4, "666666");
454 tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455 tbl.cell(0, 0).unwrap().set_text("Top");
456 tbl.cell(0, 0)
457 .unwrap()
458 .vertical_alignment(VerticalAlignment::Top)
459 .shading("FFF2CC");
460 tbl.cell(0, 1).unwrap().set_text("Center");
461 tbl.cell(0, 1)
462 .unwrap()
463 .vertical_alignment(VerticalAlignment::Center)
464 .shading("D9E2F3");
465 tbl.cell(0, 2).unwrap().set_text("Bottom");
466 tbl.cell(0, 2)
467 .unwrap()
468 .vertical_alignment(VerticalAlignment::Bottom)
469 .shading("E2EFDA");
470 tbl.row(1).unwrap().cant_split();
471 tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472 tbl.cell(1, 0).unwrap().no_wrap();
473 tbl.cell(1, 1).unwrap().set_text("Fixed width");
474 tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475 tbl.cell(1, 2).unwrap().set_text("Normal");
476 }
477
478 // ── SECTION 6: IMAGES ──
479 doc.add_paragraph("").page_break_before(true);
480 doc.add_paragraph("6. Images").style("Heading1");
481
482 doc.add_paragraph("6.1 Inline Image").style("Heading2");
483 doc.add_paragraph("A blue gradient image below:");
484 let img = create_sample_png(200, 50, [0, 80, 200]);
485 doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487 doc.add_paragraph("");
488 doc.add_paragraph("6.2 Header Image").style("Heading2");
489 let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490 doc.set_header_image(
491 &hdr_img,
492 "header_logo.png",
493 Length::inches(2.0),
494 Length::inches(0.2),
495 );
496 doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498 doc.add_paragraph("");
499 doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501 // ── SECTION 7: CONTENT MANIPULATION ──
502 doc.add_paragraph("").page_break_before(true);
503 doc.add_paragraph("7. Content Manipulation")
504 .style("Heading1");
505
506 doc.add_paragraph("7.1 Placeholder Replacement")
507 .style("Heading2");
508 doc.add_paragraph("Customer: {{customer}}");
509 doc.add_paragraph("Date: {{date}}");
510 doc.add_paragraph("Reference: {{ref_number}}");
511 {
512 let mut tbl = doc
513 .add_table(2, 2)
514 .borders(BorderStyle::Single, 4, "000000");
515 tbl.cell(0, 0).unwrap().set_text("Project");
516 tbl.cell(0, 0).unwrap().shading("D6E4F0");
517 tbl.cell(0, 1).unwrap().set_text("{{project}}");
518 tbl.cell(1, 0).unwrap().set_text("Status");
519 tbl.cell(1, 0).unwrap().shading("D6E4F0");
520 tbl.cell(1, 1).unwrap().set_text("{{status}}");
521 }
522
523 let mut replacements = HashMap::new();
524 replacements.insert("{{customer}}", "Tensorbee Inc.");
525 replacements.insert("{{date}}", "February 22, 2026");
526 replacements.insert("{{ref_number}}", "TB-2026-001");
527 replacements.insert("{{project}}", "Infrastructure Upgrade");
528 replacements.insert("{{status}}", "In Progress");
529 let n = doc.replace_all(&replacements);
530 doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532 doc.add_paragraph("");
533
534 doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535 doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536 let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537 doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539 doc.add_paragraph("");
540
541 doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542 doc.add_paragraph("Section A: First content.");
543 doc.add_paragraph("Section C: Third content.");
544 if let Some(idx) = doc.find_content_index("Section C") {
545 doc.insert_paragraph(
546 idx,
547 "Section B: Inserted between A and C via find_content_index().",
548 );
549 }
550
551 // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553 doc.add_paragraph("8. Section Breaks & Orientation")
554 .style("Heading1");
555 doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557 {
558 let mut tbl = doc
559 .add_table(3, 7)
560 .borders(BorderStyle::Single, 4, "2E75B6");
561 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562 for (c, h) in headers.iter().enumerate() {
563 tbl.cell(0, c).unwrap().set_text(h);
564 tbl.cell(0, c).unwrap().shading("2E75B6");
565 }
566 let data = [
567 [
568 "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569 ],
570 [
571 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572 ],
573 ];
574 for (r, row) in data.iter().enumerate() {
575 for (c, v) in row.iter().enumerate() {
576 tbl.cell(r + 1, c).unwrap().set_text(v);
577 }
578 }
579 }
580
581 doc.add_paragraph("")
582 .section_break(SectionBreak::NextPage)
583 .section_landscape();
584
585 // ── SECTION 9: CUSTOM STYLES ──
586 doc.add_paragraph("9. Custom Styles").style("Heading1");
587 doc.add_style(
588 StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589 .based_on("Normal")
590 .paragraph_properties({
591 let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592 ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593 val: "clear".to_string(),
594 color: None,
595 fill: Some("FFF2CC".to_string()),
596 });
597 ppr
598 })
599 .run_properties({
600 let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601 rpr.bold = Some(true);
602 rpr.color = Some("C45911".to_string());
603 rpr
604 }),
605 );
606 doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607 .style("CustomHighlight");
608
609 doc.add_paragraph("");
610
611 // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612 doc.add_paragraph("10. Document Intelligence API")
613 .style("Heading1");
614
615 let wc = doc.word_count();
616 let hc = doc.headings().len();
617 let ic = doc.images().len();
618 let lc = doc.links().len();
619 doc.add_paragraph(&format!("Word count: {wc}"));
620 doc.add_paragraph(&format!("Heading count: {hc}"));
621 doc.add_paragraph(&format!("Image count: {ic}"));
622 doc.add_paragraph(&format!("Link count: {lc}"));
623
624 let outline = doc.document_outline();
625 doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627 let issues = doc.audit_accessibility();
628 doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629 for issue in &issues {
630 doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631 }
632
633 // ── SECTION 11: DOCUMENT MERGING ──
634 doc.add_paragraph("");
635 doc.add_paragraph("11. Document Merging").style("Heading1");
636 {
637 let mut other = Document::new();
638 other.add_paragraph("This paragraph was merged from another document using append().");
639 doc.append(&other);
640 }
641
642 doc.add_paragraph("");
643
644 // ── SUMMARY ──
645 doc.add_paragraph("Summary of Demonstrated Features")
646 .style("Heading1");
647 let features = [
648 "Page setup: size, margins, header/footer distance, gutter",
649 "Document metadata: title, author, subject, keywords",
650 "Headers and footers: text, images, different first page",
651 "Background images (full-page behind text)",
652 "Table of Contents generation with bookmarks",
653 "Text formatting: bold, italic, underline styles, strike, color, size, font",
654 "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656 "Pagination controls: keep-with-next, keep-together, widow control",
657 "Bullet and numbered lists with nesting",
658 "Tab stops with dot/underscore/hyphen leaders",
659 "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660 "Row properties: header rows, exact height, cant-split",
661 "Cell properties: width, no-wrap, vertical alignment",
662 "Inline images and header images",
663 "Placeholder replacement (single and batch)",
664 "Regex-based find and replace",
665 "Content insertion at specific positions",
666 "Section breaks with mixed portrait/landscape",
667 "Custom paragraph and character styles",
668 "Document intelligence: word count, headings, outline, images, links",
669 "Accessibility audit",
670 "Document merging (append)",
671 "Export: DOCX, PDF, HTML, Markdown",
672 ];
673 for f in &features {
674 doc.add_bullet_list_item(f, 0);
675 }
676 doc.add_paragraph("");
677 doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678 .alignment(Alignment::Center)
679 .shading("E2EFDA")
680 .border_all(BorderStyle::Single, 2, "00AA00");
681
682 doc
683}Sourcepub fn no_wrap(self) -> Self
pub fn no_wrap(self) -> Self
Set no-wrap for text in this cell.
Examples found in repository?
examples/generate_all_samples.rs (line 472)
86fn generate_feature_showcase(_samples_dir: &Path) -> Document {
87 let mut doc = Document::new();
88
89 // ── Page Setup & Metadata ──
90 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
91 doc.set_margins(
92 Length::inches(1.0),
93 Length::inches(1.0),
94 Length::inches(1.0),
95 Length::inches(1.0),
96 );
97 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
98 doc.set_gutter(Length::twips(0));
99 doc.set_title("rdocx Feature Showcase");
100 doc.set_author("rdocx Sample Generator");
101 doc.set_subject("Comprehensive feature demonstration");
102 doc.set_keywords("rdocx, docx, rust, sample, showcase");
103
104 // Headers & Footers with different first page
105 doc.set_different_first_page(true);
106 doc.set_first_page_header("rdocx");
107 doc.set_first_page_footer("Feature Showcase — Cover Page");
108 doc.set_header("rdocx Feature Showcase");
109 doc.set_footer("Generated by rdocx");
110
111 // ── COVER PAGE ──
112 let bg = create_sample_png(612, 792, [20, 45, 90]);
113 doc.add_background_image(&bg, "cover_bg.png");
114
115 for _ in 0..3 {
116 doc.add_paragraph("");
117 }
118 {
119 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
120 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
121 }
122 {
123 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
124 p.add_run("Complete Feature Showcase")
125 .size(28.0)
126 .color("FFFFFF")
127 .italic(true);
128 }
129 doc.add_paragraph("");
130 {
131 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
132 p.add_run("Every feature of the rdocx Rust library")
133 .size(13.0)
134 .color("B0C4DE");
135 }
136 {
137 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
138 p.add_run("demonstrated in a single document.")
139 .size(13.0)
140 .color("B0C4DE");
141 }
142
143 // ── TABLE OF CONTENTS ──
144 doc.add_paragraph("").page_break_before(true);
145 doc.insert_toc(doc.content_count(), 3);
146
147 // ── SECTION 1: TEXT FORMATTING ──
148 doc.add_paragraph("").page_break_before(true);
149 doc.add_paragraph("1. Text Formatting").style("Heading1");
150
151 // Paragraph Alignment
152 doc.add_paragraph("1.1 Paragraph Alignment")
153 .style("Heading2");
154 doc.add_paragraph("Left-aligned paragraph (default).")
155 .alignment(Alignment::Left);
156 doc.add_paragraph("Center-aligned paragraph.")
157 .alignment(Alignment::Center);
158 doc.add_paragraph("Right-aligned paragraph.")
159 .alignment(Alignment::Right);
160 doc.add_paragraph(
161 "Justified paragraph — this text is long enough to demonstrate how justified alignment \
162 distributes extra space across word gaps so lines fill the full width of the text area.",
163 )
164 .alignment(Alignment::Justify);
165
166 doc.add_paragraph("");
167
168 // Run Formatting
169 doc.add_paragraph("1.2 Run Formatting").style("Heading2");
170 {
171 let mut p = doc.add_paragraph("");
172 p.add_run("Bold").bold(true);
173 p.add_run(" | ");
174 p.add_run("Italic").italic(true);
175 p.add_run(" | ");
176 p.add_run("Bold Italic").bold(true).italic(true);
177 p.add_run(" | ");
178 p.add_run("Underline").underline(true);
179 p.add_run(" | ");
180 p.add_run("Strikethrough").strike(true);
181 p.add_run(" | ");
182 p.add_run("Double Strike").double_strike(true);
183 }
184 {
185 let mut p = doc.add_paragraph("");
186 p.add_run("Red").color("FF0000");
187 p.add_run(" | ");
188 p.add_run("Blue").color("0000FF");
189 p.add_run(" | ");
190 p.add_run("Green").color("00AA00");
191 p.add_run(" | ");
192 p.add_run("Highlighted").highlight("FFFF00");
193 }
194 {
195 let mut p = doc.add_paragraph("");
196 p.add_run("8pt").size(8.0);
197 p.add_run(" | ");
198 p.add_run("11pt").size(11.0);
199 p.add_run(" | ");
200 p.add_run("16pt").size(16.0);
201 p.add_run(" | ");
202 p.add_run("24pt").size(24.0);
203 }
204 {
205 let mut p = doc.add_paragraph("");
206 p.add_run("Arial").font("Arial");
207 p.add_run(" | ");
208 p.add_run("Times New Roman").font("Times New Roman");
209 p.add_run(" | ");
210 p.add_run("Courier New").font("Courier New");
211 }
212 {
213 let mut p = doc.add_paragraph("");
214 p.add_run("H");
215 p.add_run("2").subscript();
216 p.add_run("O (subscript) | E=mc");
217 p.add_run("2").superscript();
218 p.add_run(" (superscript)");
219 }
220 {
221 let mut p = doc.add_paragraph("");
222 p.add_run("ALL CAPS").all_caps(true);
223 p.add_run(" | ");
224 p.add_run("Small Caps").small_caps(true);
225 p.add_run(" | ");
226 p.add_run("Expanded").character_spacing(Length::twips(40));
227 p.add_run(" | ");
228 p.add_run("Hidden text (not visible)").hidden(true);
229 }
230
231 doc.add_paragraph("");
232
233 // Underline Styles
234 doc.add_paragraph("1.3 Underline Styles").style("Heading2");
235 {
236 let mut p = doc.add_paragraph("");
237 p.add_run("Single ")
238 .underline_style(rdocx::UnderlineStyle::Single);
239 p.add_run("Double ")
240 .underline_style(rdocx::UnderlineStyle::Double);
241 p.add_run("Thick ")
242 .underline_style(rdocx::UnderlineStyle::Thick);
243 p.add_run("Dotted ")
244 .underline_style(rdocx::UnderlineStyle::Dotted);
245 p.add_run("Dash ")
246 .underline_style(rdocx::UnderlineStyle::Dash);
247 p.add_run("Wave ")
248 .underline_style(rdocx::UnderlineStyle::Wave);
249 }
250
251 // ── SECTION 2: PARAGRAPH FORMATTING ──
252 doc.add_paragraph("").page_break_before(true);
253 doc.add_paragraph("2. Paragraph Formatting")
254 .style("Heading1");
255
256 doc.add_paragraph("2.1 Shading & Borders").style("Heading2");
257 doc.add_paragraph("Paragraph with light green shading")
258 .shading("E2EFDA");
259 doc.add_paragraph("Paragraph with bottom border")
260 .border_bottom(BorderStyle::Single, 6, "2E75B6");
261 doc.add_paragraph("Paragraph with all borders (red)")
262 .border_all(BorderStyle::Single, 4, "FF0000");
263
264 doc.add_paragraph("2.2 Indentation").style("Heading2");
265 doc.add_paragraph("1-inch left indent + 0.5-inch hanging indent")
266 .indent_left(Length::inches(1.0))
267 .hanging_indent(Length::inches(0.5));
268 doc.add_paragraph("0.5-inch first-line indent on this paragraph")
269 .first_line_indent(Length::inches(0.5));
270 doc.add_paragraph("Both left and right indent (0.75 inches each)")
271 .indent_left(Length::inches(0.75))
272 .indent_right(Length::inches(0.75));
273
274 doc.add_paragraph("2.3 Spacing & Line Height")
275 .style("Heading2");
276 doc.add_paragraph("Extra space before (24pt) and after (12pt)")
277 .space_before(Length::pt(24.0))
278 .space_after(Length::pt(12.0));
279 doc.add_paragraph(
280 "Double line spacing paragraph. This text should have extra vertical space between \
281 lines to demonstrate line_spacing_multiple(2.0).",
282 )
283 .line_spacing_multiple(2.0);
284 doc.add_paragraph("Exact 20pt line spacing (fixed height).")
285 .line_spacing(20.0);
286
287 doc.add_paragraph("2.4 Pagination Controls")
288 .style("Heading2");
289 doc.add_paragraph("keep_with_next — this paragraph stays with the next")
290 .keep_with_next(true);
291 doc.add_paragraph("(This paragraph was kept with the one above.)");
292 doc.add_paragraph("keep_together — all lines of this paragraph stay on the same page")
293 .keep_together(true);
294 doc.add_paragraph("widow_control — prevents widow/orphan lines")
295 .widow_control(true);
296
297 // ── SECTION 3: LISTS ──
298 doc.add_paragraph("").page_break_before(true);
299 doc.add_paragraph("3. Lists").style("Heading1");
300
301 doc.add_paragraph("3.1 Bullet Lists").style("Heading2");
302 doc.add_bullet_list_item("First item", 0);
303 doc.add_bullet_list_item("Second item", 0);
304 doc.add_bullet_list_item("Nested level 1", 1);
305 doc.add_bullet_list_item("Nested level 2", 2);
306 doc.add_bullet_list_item("Back to level 1", 1);
307 doc.add_bullet_list_item("Third item", 0);
308
309 doc.add_paragraph("");
310
311 doc.add_paragraph("3.2 Numbered Lists").style("Heading2");
312 doc.add_numbered_list_item("First numbered item", 0);
313 doc.add_numbered_list_item("Second numbered item", 0);
314 doc.add_numbered_list_item("Sub-item A", 1);
315 doc.add_numbered_list_item("Sub-item B", 1);
316 doc.add_numbered_list_item("Third numbered item", 0);
317
318 // ── SECTION 4: TAB STOPS ──
319 doc.add_paragraph("");
320 doc.add_paragraph("4. Tab Stops").style("Heading1");
321
322 doc.add_paragraph("4.1 Alignment Tabs").style("Heading2");
323 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
324 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
325 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
326 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
327 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
328
329 doc.add_paragraph("4.2 Tab Leaders").style("Heading2");
330 doc.add_paragraph("Item\t\tPrice")
331 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
332 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
333 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
334 doc.add_paragraph("Widget\t\t$19.99")
335 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
336 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
337 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
338 doc.add_paragraph("Gadget\t\t$249.50")
339 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
340 .add_tab_stop_with_leader(
341 TabAlignment::Right,
342 Length::inches(4.0),
343 TabLeader::Underscore,
344 )
345 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
346
347 // ── SECTION 5: TABLES ──
348 doc.add_paragraph("").page_break_before(true);
349 doc.add_paragraph("5. Tables").style("Heading1");
350
351 doc.add_paragraph("5.1 Basic Table").style("Heading2");
352 {
353 let mut tbl = doc
354 .add_table(4, 3)
355 .borders(BorderStyle::Single, 4, "000000");
356 for col in 0..3 {
357 tbl.cell(0, col).unwrap().shading("2E75B6");
358 }
359 tbl.cell(0, 0).unwrap().set_text("Name");
360 tbl.cell(0, 1).unwrap().set_text("Role");
361 tbl.cell(0, 2).unwrap().set_text("Location");
362 tbl.cell(1, 0).unwrap().set_text("Walter White");
363 tbl.cell(1, 1).unwrap().set_text("CEO");
364 tbl.cell(1, 2).unwrap().set_text("Albuquerque");
365 tbl.cell(2, 0).unwrap().set_text("Jesse Pinkman");
366 tbl.cell(2, 1).unwrap().set_text("CTO");
367 tbl.cell(2, 2).unwrap().set_text("Remote");
368 tbl.cell(3, 0).unwrap().set_text("Hank Schrader");
369 tbl.cell(3, 1).unwrap().set_text("Security");
370 tbl.cell(3, 2).unwrap().set_text("Washington");
371 }
372
373 doc.add_paragraph("");
374
375 doc.add_paragraph("5.2 Column Span & Cell Shading")
376 .style("Heading2");
377 {
378 let mut tbl = doc
379 .add_table(3, 4)
380 .borders(BorderStyle::Single, 4, "000000")
381 .width_pct(100.0);
382 tbl.cell(0, 0).unwrap().set_text("Quarterly Report");
383 tbl.cell(0, 0).unwrap().shading("1F4E79").grid_span(4);
384 tbl.cell(1, 0).unwrap().set_text("Region");
385 tbl.cell(1, 0).unwrap().shading("D6E4F0");
386 tbl.cell(1, 1).unwrap().set_text("Q1");
387 tbl.cell(1, 1).unwrap().shading("D6E4F0");
388 tbl.cell(1, 2).unwrap().set_text("Q2");
389 tbl.cell(1, 2).unwrap().shading("D6E4F0");
390 tbl.cell(1, 3).unwrap().set_text("Total");
391 tbl.cell(1, 3).unwrap().shading("D6E4F0");
392 tbl.cell(2, 0).unwrap().set_text("Americas");
393 tbl.cell(2, 1).unwrap().set_text("$2.4M");
394 tbl.cell(2, 2).unwrap().set_text("$2.7M");
395 tbl.cell(2, 3).unwrap().set_text("$5.1M");
396 }
397
398 doc.add_paragraph("");
399
400 doc.add_paragraph("5.3 Vertical Merge").style("Heading2");
401 {
402 let mut tbl = doc
403 .add_table(4, 3)
404 .borders(BorderStyle::Single, 4, "000000");
405 tbl.cell(0, 0).unwrap().set_text("Category");
406 tbl.cell(0, 0).unwrap().shading("E2EFDA");
407 tbl.cell(0, 1).unwrap().set_text("Item");
408 tbl.cell(0, 1).unwrap().shading("E2EFDA");
409 tbl.cell(0, 2).unwrap().set_text("Price");
410 tbl.cell(0, 2).unwrap().shading("E2EFDA");
411 tbl.cell(1, 0).unwrap().set_text("Hardware");
412 tbl.cell(1, 0).unwrap().v_merge_restart();
413 tbl.cell(1, 1).unwrap().set_text("Laptop");
414 tbl.cell(1, 2).unwrap().set_text("$1,200");
415 tbl.cell(2, 0).unwrap().v_merge_continue();
416 tbl.cell(2, 1).unwrap().set_text("Monitor");
417 tbl.cell(2, 2).unwrap().set_text("$450");
418 tbl.cell(3, 0).unwrap().set_text("Software");
419 tbl.cell(3, 1).unwrap().set_text("IDE License");
420 tbl.cell(3, 2).unwrap().set_text("$200/yr");
421 }
422
423 doc.add_paragraph("");
424
425 doc.add_paragraph("5.4 Nested Table").style("Heading2");
426 {
427 let mut tbl = doc
428 .add_table(2, 2)
429 .borders(BorderStyle::Single, 6, "2E75B6");
430 tbl.cell(0, 0).unwrap().set_text("Outer (0,0)");
431 tbl.cell(0, 1).unwrap().set_text("Outer (0,1)");
432 tbl.cell(1, 0).unwrap().set_text("Outer (1,0)");
433 {
434 let mut cell = tbl.cell(1, 1).unwrap();
435 cell.set_text("Nested table below:");
436 let mut nested = cell
437 .add_table(2, 2)
438 .borders(BorderStyle::Single, 2, "FF6600");
439 nested.cell(0, 0).unwrap().set_text("A");
440 nested.cell(0, 1).unwrap().set_text("B");
441 nested.cell(1, 0).unwrap().set_text("C");
442 nested.cell(1, 1).unwrap().set_text("D");
443 }
444 }
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("5.5 Vertical Alignment & Row Properties")
449 .style("Heading2");
450 {
451 let mut tbl = doc
452 .add_table(2, 3)
453 .borders(BorderStyle::Single, 4, "666666");
454 tbl.row(0).unwrap().height(Length::pt(50.0)).header();
455 tbl.cell(0, 0).unwrap().set_text("Top");
456 tbl.cell(0, 0)
457 .unwrap()
458 .vertical_alignment(VerticalAlignment::Top)
459 .shading("FFF2CC");
460 tbl.cell(0, 1).unwrap().set_text("Center");
461 tbl.cell(0, 1)
462 .unwrap()
463 .vertical_alignment(VerticalAlignment::Center)
464 .shading("D9E2F3");
465 tbl.cell(0, 2).unwrap().set_text("Bottom");
466 tbl.cell(0, 2)
467 .unwrap()
468 .vertical_alignment(VerticalAlignment::Bottom)
469 .shading("E2EFDA");
470 tbl.row(1).unwrap().cant_split();
471 tbl.cell(1, 0).unwrap().set_text("No-wrap cell");
472 tbl.cell(1, 0).unwrap().no_wrap();
473 tbl.cell(1, 1).unwrap().set_text("Fixed width");
474 tbl.cell(1, 1).unwrap().width(Length::inches(2.0));
475 tbl.cell(1, 2).unwrap().set_text("Normal");
476 }
477
478 // ── SECTION 6: IMAGES ──
479 doc.add_paragraph("").page_break_before(true);
480 doc.add_paragraph("6. Images").style("Heading1");
481
482 doc.add_paragraph("6.1 Inline Image").style("Heading2");
483 doc.add_paragraph("A blue gradient image below:");
484 let img = create_sample_png(200, 50, [0, 80, 200]);
485 doc.add_picture(&img, "chart.png", Length::inches(3.0), Length::inches(0.75));
486
487 doc.add_paragraph("");
488 doc.add_paragraph("6.2 Header Image").style("Heading2");
489 let hdr_img = create_sample_png(400, 40, [40, 40, 40]);
490 doc.set_header_image(
491 &hdr_img,
492 "header_logo.png",
493 Length::inches(2.0),
494 Length::inches(0.2),
495 );
496 doc.add_paragraph("The header now contains an inline image (check the top of this page).");
497
498 doc.add_paragraph("");
499 doc.add_paragraph("Note: Page 1 uses a full-page background image (add_background_image).");
500
501 // ── SECTION 7: CONTENT MANIPULATION ──
502 doc.add_paragraph("").page_break_before(true);
503 doc.add_paragraph("7. Content Manipulation")
504 .style("Heading1");
505
506 doc.add_paragraph("7.1 Placeholder Replacement")
507 .style("Heading2");
508 doc.add_paragraph("Customer: {{customer}}");
509 doc.add_paragraph("Date: {{date}}");
510 doc.add_paragraph("Reference: {{ref_number}}");
511 {
512 let mut tbl = doc
513 .add_table(2, 2)
514 .borders(BorderStyle::Single, 4, "000000");
515 tbl.cell(0, 0).unwrap().set_text("Project");
516 tbl.cell(0, 0).unwrap().shading("D6E4F0");
517 tbl.cell(0, 1).unwrap().set_text("{{project}}");
518 tbl.cell(1, 0).unwrap().set_text("Status");
519 tbl.cell(1, 0).unwrap().shading("D6E4F0");
520 tbl.cell(1, 1).unwrap().set_text("{{status}}");
521 }
522
523 let mut replacements = HashMap::new();
524 replacements.insert("{{customer}}", "Tensorbee Inc.");
525 replacements.insert("{{date}}", "February 22, 2026");
526 replacements.insert("{{ref_number}}", "TB-2026-001");
527 replacements.insert("{{project}}", "Infrastructure Upgrade");
528 replacements.insert("{{status}}", "In Progress");
529 let n = doc.replace_all(&replacements);
530 doc.add_paragraph(&format!("({n} placeholders replaced above)"));
531
532 doc.add_paragraph("");
533
534 doc.add_paragraph("7.2 Regex Replacement").style("Heading2");
535 doc.add_paragraph("Emails: user1@example.com and admin@tensorbee.com");
536 let _ = doc.replace_regex(r"\b\w+@\w+\.\w+\b", "[REDACTED]");
537 doc.add_paragraph("(Email addresses above were redacted using regex replacement)");
538
539 doc.add_paragraph("");
540
541 doc.add_paragraph("7.3 Content Insertion").style("Heading2");
542 doc.add_paragraph("Section A: First content.");
543 doc.add_paragraph("Section C: Third content.");
544 if let Some(idx) = doc.find_content_index("Section C") {
545 doc.insert_paragraph(
546 idx,
547 "Section B: Inserted between A and C via find_content_index().",
548 );
549 }
550
551 // ── SECTION 8: SECTION BREAKS & ORIENTATION ──
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553 doc.add_paragraph("8. Section Breaks & Orientation")
554 .style("Heading1");
555 doc.add_paragraph("This page is LANDSCAPE. Useful for wide tables and charts.");
556
557 {
558 let mut tbl = doc
559 .add_table(3, 7)
560 .borders(BorderStyle::Single, 4, "2E75B6");
561 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
562 for (c, h) in headers.iter().enumerate() {
563 tbl.cell(0, c).unwrap().set_text(h);
564 tbl.cell(0, c).unwrap().shading("2E75B6");
565 }
566 let data = [
567 [
568 "Americas", "$1.2M", "$1.3M", "$1.4M", "$1.5M", "$1.6M", "$7.0M",
569 ],
570 [
571 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
572 ],
573 ];
574 for (r, row) in data.iter().enumerate() {
575 for (c, v) in row.iter().enumerate() {
576 tbl.cell(r + 1, c).unwrap().set_text(v);
577 }
578 }
579 }
580
581 doc.add_paragraph("")
582 .section_break(SectionBreak::NextPage)
583 .section_landscape();
584
585 // ── SECTION 9: CUSTOM STYLES ──
586 doc.add_paragraph("9. Custom Styles").style("Heading1");
587 doc.add_style(
588 StyleBuilder::paragraph("CustomHighlight", "Custom Highlight")
589 .based_on("Normal")
590 .paragraph_properties({
591 let mut ppr = rdocx_oxml::properties::CT_PPr::default();
592 ppr.shading = Some(rdocx_oxml::properties::CT_Shd {
593 val: "clear".to_string(),
594 color: None,
595 fill: Some("FFF2CC".to_string()),
596 });
597 ppr
598 })
599 .run_properties({
600 let mut rpr = rdocx_oxml::properties::CT_RPr::default();
601 rpr.bold = Some(true);
602 rpr.color = Some("C45911".to_string());
603 rpr
604 }),
605 );
606 doc.add_paragraph("This paragraph uses a custom style: bold orange text on yellow background.")
607 .style("CustomHighlight");
608
609 doc.add_paragraph("");
610
611 // ── SECTION 10: DOCUMENT INTELLIGENCE ──
612 doc.add_paragraph("10. Document Intelligence API")
613 .style("Heading1");
614
615 let wc = doc.word_count();
616 let hc = doc.headings().len();
617 let ic = doc.images().len();
618 let lc = doc.links().len();
619 doc.add_paragraph(&format!("Word count: {wc}"));
620 doc.add_paragraph(&format!("Heading count: {hc}"));
621 doc.add_paragraph(&format!("Image count: {ic}"));
622 doc.add_paragraph(&format!("Link count: {lc}"));
623
624 let outline = doc.document_outline();
625 doc.add_paragraph(&format!("Top-level outline nodes: {}", outline.len()));
626
627 let issues = doc.audit_accessibility();
628 doc.add_paragraph(&format!("Accessibility issues: {}", issues.len()));
629 for issue in &issues {
630 doc.add_bullet_list_item(&format!("{:?}: {}", issue.severity, issue.message), 0);
631 }
632
633 // ── SECTION 11: DOCUMENT MERGING ──
634 doc.add_paragraph("");
635 doc.add_paragraph("11. Document Merging").style("Heading1");
636 {
637 let mut other = Document::new();
638 other.add_paragraph("This paragraph was merged from another document using append().");
639 doc.append(&other);
640 }
641
642 doc.add_paragraph("");
643
644 // ── SUMMARY ──
645 doc.add_paragraph("Summary of Demonstrated Features")
646 .style("Heading1");
647 let features = [
648 "Page setup: size, margins, header/footer distance, gutter",
649 "Document metadata: title, author, subject, keywords",
650 "Headers and footers: text, images, different first page",
651 "Background images (full-page behind text)",
652 "Table of Contents generation with bookmarks",
653 "Text formatting: bold, italic, underline styles, strike, color, size, font",
654 "Advanced run: superscript, subscript, caps, small caps, spacing, hidden",
655 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
656 "Pagination controls: keep-with-next, keep-together, widow control",
657 "Bullet and numbered lists with nesting",
658 "Tab stops with dot/underscore/hyphen leaders",
659 "Tables: borders, shading, column spans, row spans, vertical alignment, nested tables",
660 "Row properties: header rows, exact height, cant-split",
661 "Cell properties: width, no-wrap, vertical alignment",
662 "Inline images and header images",
663 "Placeholder replacement (single and batch)",
664 "Regex-based find and replace",
665 "Content insertion at specific positions",
666 "Section breaks with mixed portrait/landscape",
667 "Custom paragraph and character styles",
668 "Document intelligence: word count, headings, outline, images, links",
669 "Accessibility audit",
670 "Document merging (append)",
671 "Export: DOCX, PDF, HTML, Markdown",
672 ];
673 for f in &features {
674 doc.add_bullet_list_item(f, 0);
675 }
676 doc.add_paragraph("");
677 doc.add_paragraph("All features built entirely with the rdocx Rust crate.")
678 .alignment(Alignment::Center)
679 .shading("E2EFDA")
680 .border_all(BorderStyle::Single, 2, "00AA00");
681
682 doc
683}Sourcepub fn add_table(&mut self, rows: usize, cols: usize) -> Table<'_>
pub fn add_table(&mut self, rows: usize, cols: usize) -> Table<'_>
Add a nested table inside this cell.
Examples found in repository?
examples/styled_tables.rs (line 245)
24fn generate_styled_tables(path: &Path) {
25 let mut doc = Document::new();
26 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
27 doc.set_margins(
28 Length::inches(0.75),
29 Length::inches(0.75),
30 Length::inches(0.75),
31 Length::inches(0.75),
32 );
33
34 doc.add_paragraph("Styled Tables Showcase")
35 .style("Heading1");
36
37 doc.add_paragraph("");
38
39 // =========================================================================
40 // 1. Professional report table with alternating rows
41 // =========================================================================
42 doc.add_paragraph("1. Report Table with Alternating Row Colors")
43 .style("Heading2");
44
45 {
46 let mut tbl = doc.add_table(8, 4);
47 tbl = tbl.borders(BorderStyle::Single, 2, "BFBFBF");
48 tbl = tbl.width_pct(100.0);
49
50 // Header row
51 let headers = ["Product", "Q1 Sales", "Q2 Sales", "Growth"];
52 for (col, h) in headers.iter().enumerate() {
53 tbl.cell(0, col).unwrap().shading("2E75B6");
54 tbl.cell(0, col).unwrap().set_text(h);
55 }
56 tbl.row(0).unwrap().header();
57
58 // Data with alternating shading
59 let data = [
60 ["Enterprise Suite", "$245,000", "$312,000", "+27.3%"],
61 ["Professional", "$189,000", "$201,000", "+6.3%"],
62 ["Starter Pack", "$67,000", "$84,500", "+26.1%"],
63 ["Add-ons", "$34,000", "$41,200", "+21.2%"],
64 ["Training", "$22,000", "$28,900", "+31.4%"],
65 ["Support Plans", "$56,000", "$62,300", "+11.3%"],
66 ];
67
68 for (i, row) in data.iter().enumerate() {
69 let row_idx = i + 1;
70 for (col, val) in row.iter().enumerate() {
71 tbl.cell(row_idx, col).unwrap().set_text(val);
72 // Alternate row colors
73 if i % 2 == 0 {
74 tbl.cell(row_idx, col).unwrap().shading("F2F7FB");
75 }
76 }
77 }
78
79 // Total row
80 tbl.cell(7, 0).unwrap().set_text("TOTAL");
81 tbl.cell(7, 0).unwrap().shading("D6E4F0");
82 tbl.cell(7, 1).unwrap().set_text("$613,000");
83 tbl.cell(7, 1).unwrap().shading("D6E4F0");
84 tbl.cell(7, 2).unwrap().set_text("$729,900");
85 tbl.cell(7, 2).unwrap().shading("D6E4F0");
86 tbl.cell(7, 3).unwrap().set_text("+19.1%");
87 tbl.cell(7, 3).unwrap().shading("D6E4F0");
88 }
89
90 doc.add_paragraph("");
91
92 // =========================================================================
93 // 2. Invoice-style table with merged header
94 // =========================================================================
95 doc.add_paragraph("2. Invoice Table with Merged Header & Row Spans")
96 .style("Heading2");
97
98 {
99 let mut tbl = doc.add_table(7, 4);
100 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
101 tbl = tbl.width_pct(100.0);
102
103 // Merged title row
104 tbl.cell(0, 0).unwrap().set_text("INVOICE #2026-0042");
105 tbl.cell(0, 0).unwrap().grid_span(4);
106 tbl.cell(0, 0).unwrap().shading("1F4E79");
107
108 // Column headers
109 let headers = ["Item", "Description", "Qty", "Amount"];
110 for (col, h) in headers.iter().enumerate() {
111 tbl.cell(1, col).unwrap().set_text(h);
112 tbl.cell(1, col).unwrap().shading("D6E4F0");
113 }
114
115 // Line items
116 tbl.cell(2, 0).unwrap().set_text("LIC-ENT-500");
117 tbl.cell(2, 1)
118 .unwrap()
119 .set_text("Enterprise License (500 seats)");
120 tbl.cell(2, 2).unwrap().set_text("1");
121 tbl.cell(2, 3).unwrap().set_text("$60,000");
122
123 tbl.cell(3, 0).unwrap().set_text("SVC-IMPL");
124 tbl.cell(3, 1).unwrap().set_text("Implementation Services");
125 tbl.cell(3, 2).unwrap().set_text("1");
126 tbl.cell(3, 3).unwrap().set_text("$25,000");
127
128 tbl.cell(4, 0).unwrap().set_text("SVC-TRAIN");
129 tbl.cell(4, 1)
130 .unwrap()
131 .set_text("On-site Training (3 days)");
132 tbl.cell(4, 2).unwrap().set_text("1");
133 tbl.cell(4, 3).unwrap().set_text("$4,500");
134
135 // Subtotal
136 tbl.cell(5, 0).unwrap().set_text("Subtotal");
137 tbl.cell(5, 0).unwrap().grid_span(3);
138 tbl.cell(5, 0).unwrap().shading("F2F2F2");
139 tbl.cell(5, 3).unwrap().set_text("$89,500");
140 tbl.cell(5, 3).unwrap().shading("F2F2F2");
141
142 // Total
143 tbl.cell(6, 0).unwrap().set_text("TOTAL DUE");
144 tbl.cell(6, 0).unwrap().grid_span(3);
145 tbl.cell(6, 0).unwrap().shading("1F4E79");
146 tbl.cell(6, 3).unwrap().set_text("$89,500");
147 tbl.cell(6, 3).unwrap().shading("1F4E79");
148 }
149
150 doc.add_paragraph("");
151
152 // =========================================================================
153 // 3. Specification table with vertical merge
154 // =========================================================================
155 doc.add_paragraph("3. Specification Table with Vertical Merges")
156 .style("Heading2");
157
158 {
159 let mut tbl = doc.add_table(8, 3);
160 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
161 tbl = tbl.width_pct(100.0);
162
163 // Header
164 tbl.cell(0, 0).unwrap().set_text("Category");
165 tbl.cell(0, 0).unwrap().shading("2E75B6");
166 tbl.cell(0, 1).unwrap().set_text("Specification");
167 tbl.cell(0, 1).unwrap().shading("2E75B6");
168 tbl.cell(0, 2).unwrap().set_text("Value");
169 tbl.cell(0, 2).unwrap().shading("2E75B6");
170
171 // "Hardware" spans 3 rows
172 tbl.cell(1, 0).unwrap().set_text("Hardware");
173 tbl.cell(1, 0).unwrap().v_merge_restart();
174 tbl.cell(1, 0).unwrap().shading("E2EFDA");
175 tbl.cell(1, 0)
176 .unwrap()
177 .vertical_alignment(VerticalAlignment::Center);
178 tbl.cell(1, 1).unwrap().set_text("Processor");
179 tbl.cell(1, 2).unwrap().set_text("Intel Xeon E-2388G");
180
181 tbl.cell(2, 0).unwrap().v_merge_continue();
182 tbl.cell(2, 1).unwrap().set_text("Memory");
183 tbl.cell(2, 2).unwrap().set_text("64 GB DDR4 ECC");
184
185 tbl.cell(3, 0).unwrap().v_merge_continue();
186 tbl.cell(3, 1).unwrap().set_text("Storage");
187 tbl.cell(3, 2).unwrap().set_text("2x 1TB NVMe SSD (RAID 1)");
188
189 // "Network" spans 2 rows
190 tbl.cell(4, 0).unwrap().set_text("Network");
191 tbl.cell(4, 0).unwrap().v_merge_restart();
192 tbl.cell(4, 0).unwrap().shading("FCE4D6");
193 tbl.cell(4, 0)
194 .unwrap()
195 .vertical_alignment(VerticalAlignment::Center);
196 tbl.cell(4, 1).unwrap().set_text("Ethernet");
197 tbl.cell(4, 2).unwrap().set_text("4x 10GbE SFP+");
198
199 tbl.cell(5, 0).unwrap().v_merge_continue();
200 tbl.cell(5, 1).unwrap().set_text("Management");
201 tbl.cell(5, 2).unwrap().set_text("1x 1GbE IPMI");
202
203 // "Software" spans 2 rows
204 tbl.cell(6, 0).unwrap().set_text("Software");
205 tbl.cell(6, 0).unwrap().v_merge_restart();
206 tbl.cell(6, 0).unwrap().shading("D6E4F0");
207 tbl.cell(6, 0)
208 .unwrap()
209 .vertical_alignment(VerticalAlignment::Center);
210 tbl.cell(6, 1).unwrap().set_text("Operating System");
211 tbl.cell(6, 2).unwrap().set_text("Ubuntu 24.04 LTS");
212
213 tbl.cell(7, 0).unwrap().v_merge_continue();
214 tbl.cell(7, 1).unwrap().set_text("Monitoring");
215 tbl.cell(7, 2).unwrap().set_text("Prometheus + Grafana");
216 }
217
218 doc.add_paragraph("");
219
220 // =========================================================================
221 // 4. Nested table (table inside a cell)
222 // =========================================================================
223 doc.add_paragraph("4. Nested Table").style("Heading2");
224
225 {
226 let mut tbl = doc.add_table(2, 2);
227 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
228 tbl = tbl.width_pct(100.0);
229 tbl = tbl.cell_margins(
230 Length::twips(72),
231 Length::twips(108),
232 Length::twips(72),
233 Length::twips(108),
234 );
235
236 tbl.cell(0, 0).unwrap().set_text("Project Alpha");
237 tbl.cell(0, 0).unwrap().shading("2E75B6");
238 tbl.cell(0, 1).unwrap().set_text("Project Beta");
239 tbl.cell(0, 1).unwrap().shading("2E75B6");
240
241 // Nested table in cell (1,0)
242 {
243 let mut cell = tbl.cell(1, 0).unwrap();
244 cell.set_text("Milestones:");
245 let mut inner = cell.add_table(3, 2);
246 inner = inner.borders(BorderStyle::Single, 2, "70AD47");
247 inner.cell(0, 0).unwrap().set_text("Phase");
248 inner.cell(0, 0).unwrap().shading("E2EFDA");
249 inner.cell(0, 1).unwrap().set_text("Status");
250 inner.cell(0, 1).unwrap().shading("E2EFDA");
251 inner.cell(1, 0).unwrap().set_text("Design");
252 inner.cell(1, 1).unwrap().set_text("Complete");
253 inner.cell(2, 0).unwrap().set_text("Build");
254 inner.cell(2, 1).unwrap().set_text("In Progress");
255 }
256
257 // Nested table in cell (1,1)
258 {
259 let mut cell = tbl.cell(1, 1).unwrap();
260 cell.set_text("Budget:");
261 let mut inner = cell.add_table(3, 2);
262 inner = inner.borders(BorderStyle::Single, 2, "ED7D31");
263 inner.cell(0, 0).unwrap().set_text("Category");
264 inner.cell(0, 0).unwrap().shading("FCE4D6");
265 inner.cell(0, 1).unwrap().set_text("Amount");
266 inner.cell(0, 1).unwrap().shading("FCE4D6");
267 inner.cell(1, 0).unwrap().set_text("Development");
268 inner.cell(1, 1).unwrap().set_text("$120,000");
269 inner.cell(2, 0).unwrap().set_text("Testing");
270 inner.cell(2, 1).unwrap().set_text("$35,000");
271 }
272 }
273
274 doc.add_paragraph("");
275
276 // =========================================================================
277 // 5. Form-style table with labels
278 // =========================================================================
279 doc.add_paragraph("5. Form-Style Table").style("Heading2");
280
281 {
282 let mut tbl = doc.add_table(6, 4);
283 tbl = tbl.borders(BorderStyle::Single, 4, "808080");
284 tbl = tbl.width_pct(100.0);
285
286 // Row 0: Full-width title
287 tbl.cell(0, 0)
288 .unwrap()
289 .set_text("Customer Registration Form");
290 tbl.cell(0, 0).unwrap().grid_span(4);
291 tbl.cell(0, 0).unwrap().shading("404040");
292
293 // Row 1: Name fields
294 tbl.cell(1, 0).unwrap().set_text("First Name");
295 tbl.cell(1, 0).unwrap().shading("E8E8E8");
296 tbl.cell(1, 1).unwrap().set_text("John");
297 tbl.cell(1, 2).unwrap().set_text("Last Name");
298 tbl.cell(1, 2).unwrap().shading("E8E8E8");
299 tbl.cell(1, 3).unwrap().set_text("Smith");
300
301 // Row 2: Contact
302 tbl.cell(2, 0).unwrap().set_text("Email");
303 tbl.cell(2, 0).unwrap().shading("E8E8E8");
304 tbl.cell(2, 1).unwrap().set_text("john.smith@example.com");
305 tbl.cell(2, 1).unwrap().grid_span(3);
306
307 // Row 3: Phone
308 tbl.cell(3, 0).unwrap().set_text("Phone");
309 tbl.cell(3, 0).unwrap().shading("E8E8E8");
310 tbl.cell(3, 1).unwrap().set_text("+1 (555) 123-4567");
311 tbl.cell(3, 2).unwrap().set_text("Company");
312 tbl.cell(3, 2).unwrap().shading("E8E8E8");
313 tbl.cell(3, 3).unwrap().set_text("Acme Corp");
314
315 // Row 4: Address (spanning)
316 tbl.cell(4, 0).unwrap().set_text("Address");
317 tbl.cell(4, 0).unwrap().shading("E8E8E8");
318 tbl.cell(4, 1)
319 .unwrap()
320 .set_text("123 Business Ave, Suite 400, Portland, OR 97201");
321 tbl.cell(4, 1).unwrap().grid_span(3);
322
323 // Row 5: Notes
324 tbl.cell(5, 0).unwrap().set_text("Notes");
325 tbl.cell(5, 0).unwrap().shading("E8E8E8");
326 tbl.cell(5, 0)
327 .unwrap()
328 .vertical_alignment(VerticalAlignment::Top);
329 {
330 let mut cell = tbl.cell(5, 1).unwrap().grid_span(3);
331 cell.set_text("Premium customer since 2020. Preferred contact method: email.");
332 cell.add_paragraph("Annual review scheduled for March 2026.");
333 }
334 }
335
336 doc.add_paragraph("");
337
338 // =========================================================================
339 // 6. Comparison table with border styles
340 // =========================================================================
341 doc.add_paragraph("6. Comparison Table with Custom Borders")
342 .style("Heading2");
343
344 {
345 let mut tbl = doc.add_table(5, 3);
346 tbl = tbl.borders(BorderStyle::Double, 4, "2E75B6");
347 tbl = tbl.width_pct(100.0);
348
349 // Header
350 tbl.cell(0, 0).unwrap().set_text("Feature");
351 tbl.cell(0, 0).unwrap().shading("2E75B6");
352 tbl.cell(0, 1).unwrap().set_text("Basic Plan");
353 tbl.cell(0, 1).unwrap().shading("2E75B6");
354 tbl.cell(0, 2).unwrap().set_text("Enterprise Plan");
355 tbl.cell(0, 2).unwrap().shading("2E75B6");
356
357 tbl.cell(1, 0).unwrap().set_text("Users");
358 tbl.cell(1, 1).unwrap().set_text("Up to 10");
359 tbl.cell(1, 2).unwrap().set_text("Unlimited");
360 tbl.cell(1, 2).unwrap().shading("E2EFDA");
361
362 tbl.cell(2, 0).unwrap().set_text("Storage");
363 tbl.cell(2, 1).unwrap().set_text("50 GB");
364 tbl.cell(2, 2).unwrap().set_text("5 TB");
365 tbl.cell(2, 2).unwrap().shading("E2EFDA");
366
367 tbl.cell(3, 0).unwrap().set_text("Support");
368 tbl.cell(3, 1).unwrap().set_text("Email only");
369 tbl.cell(3, 2).unwrap().set_text("24/7 Phone + Email");
370 tbl.cell(3, 2).unwrap().shading("E2EFDA");
371
372 tbl.cell(4, 0).unwrap().set_text("Price");
373 tbl.cell(4, 0).unwrap().shading("F2F2F2");
374 tbl.cell(4, 1).unwrap().set_text("$29/month");
375 tbl.cell(4, 1).unwrap().shading("F2F2F2");
376 tbl.cell(4, 2).unwrap().set_text("$199/month");
377 tbl.cell(4, 2).unwrap().shading("C6EFCE");
378 }
379
380 doc.add_paragraph("");
381
382 // =========================================================================
383 // 7. Wide table with fixed layout and row height
384 // =========================================================================
385 doc.add_paragraph("7. Fixed Layout Table with Row Height Control")
386 .style("Heading2");
387
388 {
389 let mut tbl = doc.add_table(4, 5);
390 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
391 tbl = tbl.width(Length::inches(7.0));
392 tbl = tbl.layout_fixed();
393
394 // Set column widths
395 for col in 0..5 {
396 tbl.cell(0, col).unwrap().width(Length::inches(1.4));
397 }
398
399 // Header with exact height
400 tbl.row(0).unwrap().height_exact(Length::twips(480));
401 tbl.row(0).unwrap().header();
402 tbl.row(0).unwrap().cant_split();
403
404 let headers = ["Mon", "Tue", "Wed", "Thu", "Fri"];
405 for (col, h) in headers.iter().enumerate() {
406 tbl.cell(0, col).unwrap().set_text(h);
407 tbl.cell(0, col).unwrap().shading("404040");
408 tbl.cell(0, col)
409 .unwrap()
410 .vertical_alignment(VerticalAlignment::Center);
411 }
412
413 // Schedule rows with minimum height
414 tbl.row(1).unwrap().height(Length::twips(600));
415 tbl.cell(1, 0).unwrap().set_text("9:00 Standup");
416 tbl.cell(1, 1).unwrap().set_text("9:00 Standup");
417 tbl.cell(1, 2).unwrap().set_text("9:00 Standup");
418 tbl.cell(1, 3).unwrap().set_text("9:00 Standup");
419 tbl.cell(1, 4).unwrap().set_text("9:00 Standup");
420
421 tbl.row(2).unwrap().height(Length::twips(600));
422 tbl.cell(2, 0).unwrap().set_text("10:00 Dev");
423 tbl.cell(2, 0).unwrap().shading("D6E4F0");
424 tbl.cell(2, 1).unwrap().set_text("10:00 Design Review");
425 tbl.cell(2, 1).unwrap().shading("FCE4D6");
426 tbl.cell(2, 2).unwrap().set_text("10:00 Dev");
427 tbl.cell(2, 2).unwrap().shading("D6E4F0");
428 tbl.cell(2, 3).unwrap().set_text("10:00 Sprint Planning");
429 tbl.cell(2, 3).unwrap().shading("E2EFDA");
430 tbl.cell(2, 4).unwrap().set_text("10:00 Dev");
431 tbl.cell(2, 4).unwrap().shading("D6E4F0");
432
433 tbl.row(3).unwrap().height(Length::twips(600));
434 tbl.cell(3, 0).unwrap().set_text("14:00 Code Review");
435 tbl.cell(3, 1).unwrap().set_text("14:00 Dev");
436 tbl.cell(3, 1).unwrap().shading("D6E4F0");
437 tbl.cell(3, 2).unwrap().set_text("14:00 Demo");
438 tbl.cell(3, 2).unwrap().shading("FCE4D6");
439 tbl.cell(3, 3).unwrap().set_text("14:00 Dev");
440 tbl.cell(3, 3).unwrap().shading("D6E4F0");
441 tbl.cell(3, 4).unwrap().set_text("14:00 Retro");
442 tbl.cell(3, 4).unwrap().shading("E2EFDA");
443 }
444
445 doc.set_title("Styled Tables Showcase");
446 doc.set_author("rdocx");
447
448 doc.save(path).unwrap();
449}More examples
examples/generate_samples.rs (line 419)
34fn generate_feature_showcase(path: &Path) {
35 let mut doc = Document::new();
36
37 // =========================================================================
38 // PAGE SETUP & METADATA
39 // =========================================================================
40 doc.set_page_size(Length::inches(8.5), Length::inches(11.0));
41 doc.set_margins(
42 Length::inches(1.0), // top
43 Length::inches(1.0), // right
44 Length::inches(1.0), // bottom
45 Length::inches(1.0), // left
46 );
47 doc.set_header_footer_distance(Length::twips(720), Length::twips(432));
48 doc.set_gutter(Length::twips(0));
49
50 doc.set_title("rdocx Feature Showcase");
51 doc.set_author("rdocx Sample Generator");
52 doc.set_subject("Comprehensive feature demonstration");
53 doc.set_keywords("rdocx, docx, rust, sample");
54
55 // Header & Footer
56 doc.set_header("rdocx Feature Showcase");
57 doc.set_footer("Generated by rdocx — Page");
58
59 // Different first page header
60 doc.set_different_first_page(true);
61 doc.set_first_page_header("rdocx");
62 doc.set_first_page_footer("Feature Showcase — Cover Page");
63
64 // =========================================================================
65 // PAGE 1: COVER PAGE — background image, run formatting
66 // =========================================================================
67 let bg_cover = create_sample_png(612, 792, [30, 60, 120]);
68 doc.add_background_image(&bg_cover, "cover_bg.png");
69
70 doc.add_paragraph(""); // spacer
71 doc.add_paragraph(""); // spacer
72 doc.add_paragraph(""); // spacer
73
74 {
75 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
76 p.add_run("rdocx").bold(true).size(72.0).color("FFFFFF");
77 }
78 {
79 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
80 p.add_run("Feature Showcase")
81 .size(28.0)
82 .color("FFFFFF")
83 .italic(true);
84 }
85
86 doc.add_paragraph(""); // spacer
87
88 {
89 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
90 p.add_run("A comprehensive demonstration of every feature")
91 .size(14.0)
92 .color("CCDDFF");
93 }
94 {
95 let mut p = doc.add_paragraph("").alignment(Alignment::Center);
96 p.add_run("provided by the rdocx Rust crate for DOCX generation.")
97 .size(14.0)
98 .color("CCDDFF");
99 }
100
101 // =========================================================================
102 // PAGE 2: TEXT FORMATTING
103 // =========================================================================
104 doc.add_paragraph("").page_break_before(true);
105
106 doc.add_paragraph("1. Text Formatting").style("Heading1");
107
108 doc.add_paragraph("This section demonstrates paragraph and run-level formatting options.");
109 doc.add_paragraph("");
110
111 // --- Paragraph alignment ---
112 doc.add_paragraph("Paragraph Alignment").style("Heading2");
113
114 doc.add_paragraph("This paragraph is left-aligned (the default).")
115 .alignment(Alignment::Left);
116 doc.add_paragraph("This paragraph is center-aligned.")
117 .alignment(Alignment::Center);
118 doc.add_paragraph("This paragraph is right-aligned.")
119 .alignment(Alignment::Right);
120 doc.add_paragraph(
121 "This paragraph is justified. To demonstrate justified text properly, it needs \
122 to be long enough to span multiple lines so the word spacing adjustment is visible \
123 across the full width of the text area on the page.",
124 )
125 .alignment(Alignment::Justify);
126
127 doc.add_paragraph("");
128
129 // --- Run formatting ---
130 doc.add_paragraph("Run Formatting").style("Heading2");
131
132 {
133 let mut p = doc.add_paragraph("");
134 p.add_run("Bold text").bold(true);
135 p.add_run(" | ");
136 p.add_run("Italic text").italic(true);
137 p.add_run(" | ");
138 p.add_run("Bold + Italic").bold(true).italic(true);
139 }
140 {
141 let mut p = doc.add_paragraph("");
142 p.add_run("Single underline").underline(true);
143 p.add_run(" | ");
144 p.add_run("Strikethrough").strike(true);
145 p.add_run(" | ");
146 p.add_run("Double strikethrough").double_strike(true);
147 }
148 {
149 let mut p = doc.add_paragraph("");
150 p.add_run("Red text").color("FF0000");
151 p.add_run(" | ");
152 p.add_run("Blue text").color("0000FF");
153 p.add_run(" | ");
154 p.add_run("Green text").color("00AA00");
155 p.add_run(" | ");
156 p.add_run("Highlighted").highlight("FFFF00");
157 }
158 {
159 let mut p = doc.add_paragraph("");
160 p.add_run("8pt small").size(8.0);
161 p.add_run(" | ");
162 p.add_run("11pt normal").size(11.0);
163 p.add_run(" | ");
164 p.add_run("16pt large").size(16.0);
165 p.add_run(" | ");
166 p.add_run("24pt extra-large").size(24.0);
167 }
168 {
169 let mut p = doc.add_paragraph("");
170 p.add_run("Arial font").font("Arial");
171 p.add_run(" | ");
172 p.add_run("Times New Roman font").font("Times New Roman");
173 p.add_run(" | ");
174 p.add_run("Courier New font").font("Courier New");
175 }
176 {
177 let mut p = doc.add_paragraph("");
178 p.add_run("Normal");
179 p.add_run(" H").size(11.0);
180 p.add_run("2").subscript();
181 p.add_run("O (subscript)").size(11.0);
182 p.add_run(" | E = mc").size(11.0);
183 p.add_run("2").superscript();
184 p.add_run(" (superscript)").size(11.0);
185 }
186 {
187 let mut p = doc.add_paragraph("");
188 p.add_run("ALL CAPS").all_caps(true);
189 p.add_run(" | ");
190 p.add_run("Small Caps").small_caps(true);
191 p.add_run(" | ");
192 p.add_run("Expanded spacing")
193 .character_spacing(Length::twips(40));
194 }
195
196 doc.add_paragraph("");
197
198 // --- Paragraph formatting ---
199 doc.add_paragraph("Paragraph Formatting").style("Heading2");
200
201 doc.add_paragraph("Paragraph with shading (light green background)")
202 .shading("E2EFDA");
203
204 doc.add_paragraph("Paragraph with bottom border")
205 .border_bottom(BorderStyle::Single, 6, "2E75B6");
206
207 doc.add_paragraph("Paragraph with all borders")
208 .border_all(BorderStyle::Single, 4, "FF0000");
209
210 doc.add_paragraph("Paragraph with 1-inch left indent and hanging indent")
211 .indent_left(Length::inches(1.0))
212 .hanging_indent(Length::inches(0.5));
213
214 doc.add_paragraph("Paragraph with first-line indent of 0.5 inches")
215 .first_line_indent(Length::inches(0.5));
216
217 doc.add_paragraph("Paragraph with extra space before (24pt) and after (12pt)")
218 .space_before(Length::pt(24.0))
219 .space_after(Length::pt(12.0));
220
221 doc.add_paragraph(
222 "Paragraph with double line spacing. This text should have extra vertical \
223 space between lines to demonstrate the line_spacing_multiple setting.",
224 )
225 .line_spacing_multiple(2.0);
226
227 doc.add_paragraph("Paragraph with keep-with-next (won't break from the next paragraph)")
228 .keep_with_next(true);
229 doc.add_paragraph("(This stays with the paragraph above.)");
230
231 // =========================================================================
232 // PAGE 3: LISTS & TAB STOPS
233 // =========================================================================
234 doc.add_paragraph("").page_break_before(true);
235
236 doc.add_paragraph("2. Lists").style("Heading1");
237
238 doc.add_paragraph("Bullet List").style("Heading2");
239
240 doc.add_bullet_list_item("First bullet item", 0);
241 doc.add_bullet_list_item("Second bullet item", 0);
242 doc.add_bullet_list_item("Nested level 1", 1);
243 doc.add_bullet_list_item("Nested level 2", 2);
244 doc.add_bullet_list_item("Back to level 1", 1);
245 doc.add_bullet_list_item("Third bullet item", 0);
246
247 doc.add_paragraph("");
248
249 doc.add_paragraph("Numbered List").style("Heading2");
250
251 doc.add_numbered_list_item("First numbered item", 0);
252 doc.add_numbered_list_item("Second numbered item", 0);
253 doc.add_numbered_list_item("Sub-item A", 1);
254 doc.add_numbered_list_item("Sub-item B", 1);
255 doc.add_numbered_list_item("Third numbered item", 0);
256
257 doc.add_paragraph("");
258
259 // --- Tab stops ---
260 doc.add_paragraph("Tab Stops").style("Heading2");
261
262 doc.add_paragraph("Left\tCenter\tRight\tDecimal")
263 .add_tab_stop(TabAlignment::Left, Length::inches(0.0))
264 .add_tab_stop(TabAlignment::Center, Length::inches(2.5))
265 .add_tab_stop(TabAlignment::Right, Length::inches(5.0))
266 .add_tab_stop(TabAlignment::Decimal, Length::inches(6.5));
267
268 doc.add_paragraph("Item\t........\tPrice")
269 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
270 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
271 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
272
273 doc.add_paragraph("Widget A\t........\t$19.99")
274 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
275 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(4.0), TabLeader::Dot)
276 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
277
278 doc.add_paragraph("Gadget B\t________\t$249.50")
279 .add_tab_stop_with_leader(TabAlignment::Left, Length::inches(0.0), TabLeader::None)
280 .add_tab_stop_with_leader(
281 TabAlignment::Right,
282 Length::inches(4.0),
283 TabLeader::Underscore,
284 )
285 .add_tab_stop_with_leader(TabAlignment::Right, Length::inches(5.0), TabLeader::None);
286
287 // =========================================================================
288 // PAGE 4: TABLES
289 // =========================================================================
290 doc.add_paragraph("").page_break_before(true);
291
292 doc.add_paragraph("3. Tables").style("Heading1");
293
294 // --- Basic table with borders ---
295 doc.add_paragraph("Basic Table with Borders")
296 .style("Heading2");
297
298 {
299 let mut tbl = doc.add_table(4, 3);
300 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
301
302 // Header row
303 for col in 0..3 {
304 tbl.cell(0, col).unwrap().shading("2E75B6");
305 }
306 tbl.cell(0, 0).unwrap().set_text("Name");
307 tbl.cell(0, 1).unwrap().set_text("Role");
308 tbl.cell(0, 2).unwrap().set_text("Location");
309
310 tbl.cell(1, 0).unwrap().set_text("Alice Johnson");
311 tbl.cell(1, 1).unwrap().set_text("Engineering Lead");
312 tbl.cell(1, 2).unwrap().set_text("New York");
313
314 tbl.cell(2, 0).unwrap().set_text("Bob Smith");
315 tbl.cell(2, 1).unwrap().set_text("Product Manager");
316 tbl.cell(2, 2).unwrap().set_text("San Francisco");
317
318 tbl.cell(3, 0).unwrap().set_text("Carol Davis");
319 tbl.cell(3, 1).unwrap().set_text("Designer");
320 tbl.cell(3, 2).unwrap().set_text("London");
321 }
322
323 doc.add_paragraph("");
324
325 // --- Table with cell merging ---
326 doc.add_paragraph("Table with Cell Merging & Vertical Alignment")
327 .style("Heading2");
328
329 {
330 let mut tbl = doc.add_table(4, 4);
331 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
332 tbl = tbl.width_pct(100.0);
333
334 // Header spanning all columns
335 tbl.cell(0, 0).unwrap().set_text("Quarterly Revenue Report");
336 tbl.cell(0, 0).unwrap().shading("1F4E79");
337 tbl.cell(0, 0).unwrap().grid_span(4);
338
339 // Sub-header
340 tbl.cell(1, 0).unwrap().set_text("Region");
341 tbl.cell(1, 0).unwrap().shading("D6E4F0");
342 tbl.cell(1, 1).unwrap().set_text("Q1");
343 tbl.cell(1, 1).unwrap().shading("D6E4F0");
344 tbl.cell(1, 2).unwrap().set_text("Q2");
345 tbl.cell(1, 2).unwrap().shading("D6E4F0");
346 tbl.cell(1, 3).unwrap().set_text("Total");
347 tbl.cell(1, 3).unwrap().shading("D6E4F0");
348
349 // Data
350 tbl.cell(2, 0).unwrap().set_text("North America");
351 tbl.cell(2, 1).unwrap().set_text("$2.4M");
352 tbl.cell(2, 2).unwrap().set_text("$2.7M");
353 tbl.cell(2, 3).unwrap().set_text("$5.1M");
354
355 tbl.cell(3, 0).unwrap().set_text("Europe");
356 tbl.cell(3, 1).unwrap().set_text("$1.8M");
357 tbl.cell(3, 2).unwrap().set_text("$2.0M");
358 tbl.cell(3, 3).unwrap().set_text("$3.8M");
359
360 // Vertical alignment on data cells
361 tbl.cell(2, 3)
362 .unwrap()
363 .vertical_alignment(VerticalAlignment::Center);
364 tbl.cell(3, 3)
365 .unwrap()
366 .vertical_alignment(VerticalAlignment::Bottom);
367 }
368
369 doc.add_paragraph("");
370
371 // --- Table with vertical merge ---
372 doc.add_paragraph("Table with Vertical Merge")
373 .style("Heading2");
374
375 {
376 let mut tbl = doc.add_table(4, 3);
377 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
378
379 tbl.cell(0, 0).unwrap().set_text("Category");
380 tbl.cell(0, 0).unwrap().shading("E2EFDA");
381 tbl.cell(0, 1).unwrap().set_text("Item");
382 tbl.cell(0, 1).unwrap().shading("E2EFDA");
383 tbl.cell(0, 2).unwrap().set_text("Price");
384 tbl.cell(0, 2).unwrap().shading("E2EFDA");
385
386 // "Hardware" spans rows 1-2
387 tbl.cell(1, 0).unwrap().set_text("Hardware");
388 tbl.cell(1, 0).unwrap().v_merge_restart();
389 tbl.cell(1, 1).unwrap().set_text("Laptop");
390 tbl.cell(1, 2).unwrap().set_text("$1,200");
391
392 tbl.cell(2, 0).unwrap().v_merge_continue();
393 tbl.cell(2, 1).unwrap().set_text("Monitor");
394 tbl.cell(2, 2).unwrap().set_text("$450");
395
396 // "Software" on row 3
397 tbl.cell(3, 0).unwrap().set_text("Software");
398 tbl.cell(3, 1).unwrap().set_text("IDE License");
399 tbl.cell(3, 2).unwrap().set_text("$200/yr");
400 }
401
402 doc.add_paragraph("");
403
404 // --- Nested table ---
405 doc.add_paragraph("Nested Table").style("Heading2");
406
407 {
408 let mut tbl = doc.add_table(2, 2);
409 tbl = tbl.borders(BorderStyle::Single, 6, "2E75B6");
410
411 tbl.cell(0, 0).unwrap().set_text("Outer Cell (0,0)");
412 tbl.cell(0, 1).unwrap().set_text("Outer Cell (0,1)");
413 tbl.cell(1, 0).unwrap().set_text("Outer Cell (1,0)");
414
415 // Nested table inside cell (1,1)
416 {
417 let mut cell = tbl.cell(1, 1).unwrap();
418 cell.set_text("Contains nested table:");
419 let mut nested = cell.add_table(2, 2);
420 nested = nested.borders(BorderStyle::Single, 2, "FF6600");
421 nested.cell(0, 0).unwrap().set_text("Inner A");
422 nested.cell(0, 1).unwrap().set_text("Inner B");
423 nested.cell(1, 0).unwrap().set_text("Inner C");
424 nested.cell(1, 1).unwrap().set_text("Inner D");
425 }
426 }
427
428 // =========================================================================
429 // PAGE 5: IMAGES
430 // =========================================================================
431 doc.add_paragraph("").page_break_before(true);
432
433 doc.add_paragraph("4. Images").style("Heading1");
434
435 doc.add_paragraph("Inline Image").style("Heading2");
436
437 doc.add_paragraph("Below is an inline image (200x50 pixels, blue gradient):");
438 let inline_img = create_sample_png(200, 50, [0, 80, 200]);
439 doc.add_picture(
440 &inline_img,
441 "inline_chart.png",
442 Length::inches(3.0),
443 Length::inches(0.75),
444 );
445
446 doc.add_paragraph("");
447
448 doc.add_paragraph("Header Image").style("Heading2");
449
450 // Replace the text-only header with an image header
451 let header_img = create_sample_png(400, 40, [40, 40, 40]);
452 doc.set_header_image(
453 &header_img,
454 "header_logo.png",
455 Length::inches(2.0),
456 Length::inches(0.2),
457 );
458
459 doc.add_paragraph(
460 "The document header has been replaced with an inline image. \
461 Check the header area at the top of this page.",
462 );
463
464 doc.add_paragraph("");
465 doc.add_paragraph(
466 "Note: The cover page uses a full-page background image behind the text, \
467 demonstrated on page 1 via add_background_image().",
468 );
469
470 // =========================================================================
471 // PAGE 6: CONTENT MANIPULATION — placeholder replacement, insertion
472 // =========================================================================
473 doc.add_paragraph("").page_break_before(true);
474
475 doc.add_paragraph("5. Content Manipulation")
476 .style("Heading1");
477
478 // --- Placeholder replacement ---
479 doc.add_paragraph("Placeholder Replacement")
480 .style("Heading2");
481
482 doc.add_paragraph(
483 "Before replacement, this document contained {{customer}} and {{date}} placeholders.",
484 );
485
486 {
487 let mut p = doc.add_paragraph("");
488 p.add_run("Customer: ").bold(true);
489 p.add_run("{{customer}}");
490 }
491 {
492 let mut p = doc.add_paragraph("");
493 p.add_run("Date: ").bold(true);
494 p.add_run("{{date}}");
495 }
496 doc.add_paragraph("Reference: {{ref_number}}");
497
498 // Table with placeholders
499 {
500 let mut tbl = doc.add_table(3, 2);
501 tbl = tbl.borders(BorderStyle::Single, 4, "000000");
502 tbl.cell(0, 0).unwrap().set_text("Field");
503 tbl.cell(0, 0).unwrap().shading("D6E4F0");
504 tbl.cell(0, 1).unwrap().set_text("Value");
505 tbl.cell(0, 1).unwrap().shading("D6E4F0");
506 tbl.cell(1, 0).unwrap().set_text("Project");
507 tbl.cell(1, 1).unwrap().set_text("{{project}}");
508 tbl.cell(2, 0).unwrap().set_text("Status");
509 tbl.cell(2, 1).unwrap().set_text("{{status}}");
510 }
511
512 // Perform replacements
513 let mut replacements = HashMap::new();
514 replacements.insert("{{customer}}", "Acme Corporation");
515 replacements.insert("{{date}}", "February 22, 2026");
516 replacements.insert("{{ref_number}}", "REF-2026-001");
517 replacements.insert("{{project}}", "Infrastructure Upgrade");
518 replacements.insert("{{status}}", "In Progress");
519 let replace_count = doc.replace_all(&replacements);
520
521 doc.add_paragraph("");
522 doc.add_paragraph(&format!(
523 "(Replaced {} placeholders above — in body text and table cells)",
524 replace_count
525 ));
526
527 doc.add_paragraph("");
528
529 // --- Content insertion ---
530 doc.add_paragraph("Content Insertion").style("Heading2");
531
532 doc.add_paragraph("Section A: First section of content.");
533 doc.add_paragraph("Section C: Third section of content.");
534
535 // Insert "Section B" between A and C
536 if let Some(idx) = doc.find_content_index("Section C") {
537 doc.insert_paragraph(
538 idx,
539 "Section B: Inserted between A and C using find_content_index().",
540 );
541 }
542
543 doc.add_paragraph("");
544 doc.add_paragraph(
545 "The paragraph above ('Section B') was inserted at a specific position \
546 using find_content_index() + insert_paragraph().",
547 );
548
549 // =========================================================================
550 // PAGE 7: LANDSCAPE — section break, wide table
551 // =========================================================================
552 doc.add_paragraph("").section_break(SectionBreak::NextPage);
553
554 doc.add_paragraph("6. Mixed Page Orientation")
555 .style("Heading1");
556
557 doc.add_paragraph(
558 "This page is in LANDSCAPE orientation. It was created using a section break \
559 followed by section_landscape(). This is useful for wide tables or charts.",
560 );
561
562 doc.add_paragraph("");
563
564 // Wide table for landscape
565 {
566 let mut tbl = doc.add_table(4, 7);
567 tbl = tbl.borders(BorderStyle::Single, 4, "2E75B6");
568
569 let headers = ["Region", "Jan", "Feb", "Mar", "Apr", "May", "Total"];
570 for (col, h) in headers.iter().enumerate() {
571 tbl.cell(0, col).unwrap().set_text(h);
572 tbl.cell(0, col).unwrap().shading("2E75B6");
573 }
574
575 let data = [
576 [
577 "North America",
578 "$1.2M",
579 "$1.3M",
580 "$1.4M",
581 "$1.5M",
582 "$1.6M",
583 "$7.0M",
584 ],
585 [
586 "Europe", "$0.8M", "$0.9M", "$0.9M", "$1.0M", "$1.1M", "$4.7M",
587 ],
588 [
589 "Asia Pacific",
590 "$0.5M",
591 "$0.6M",
592 "$0.7M",
593 "$0.7M",
594 "$0.8M",
595 "$3.3M",
596 ],
597 ];
598 for (row_idx, row_data) in data.iter().enumerate() {
599 for (col, val) in row_data.iter().enumerate() {
600 tbl.cell(row_idx + 1, col).unwrap().set_text(val);
601 }
602 }
603 }
604
605 // End landscape, return to portrait
606 doc.add_paragraph("")
607 .section_break(SectionBreak::NextPage)
608 .section_landscape();
609
610 // =========================================================================
611 // PAGE 8: BACK TO PORTRAIT — styles, final notes
612 // =========================================================================
613 doc.add_paragraph("7. Custom Styles & Summary")
614 .style("Heading1");
615
616 doc.add_paragraph(
617 "This final page is back in portrait orientation after a section break. \
618 The document has demonstrated:",
619 );
620
621 doc.add_bullet_list_item(
622 "Page setup: size, margins, header/footer distance, gutter",
623 0,
624 );
625 doc.add_bullet_list_item("Document metadata: title, author, subject, keywords", 0);
626 doc.add_bullet_list_item("Headers and footers: text, images, different first page", 0);
627 doc.add_bullet_list_item("Background images: full-page behind text", 0);
628 doc.add_bullet_list_item(
629 "Text formatting: bold, italic, underline, strike, color, size, font",
630 0,
631 );
632 doc.add_bullet_list_item(
633 "Advanced run formatting: superscript, subscript, caps, spacing",
634 0,
635 );
636 doc.add_bullet_list_item(
637 "Paragraph formatting: alignment, borders, shading, spacing, indentation",
638 0,
639 );
640 doc.add_bullet_list_item("Bullet and numbered lists with nesting levels", 0);
641 doc.add_bullet_list_item("Tab stops with dot/underscore leaders", 0);
642 doc.add_bullet_list_item(
643 "Tables: borders, shading, column spans, row spans, nesting",
644 0,
645 );
646 doc.add_bullet_list_item("Vertical alignment in table cells", 0);
647 doc.add_bullet_list_item("Inline images", 0);
648 doc.add_bullet_list_item("Placeholder replacement in body and table cells", 0);
649 doc.add_bullet_list_item("Content insertion at specific positions", 0);
650 doc.add_bullet_list_item(
651 "Section breaks with mixed portrait/landscape orientation",
652 0,
653 );
654
655 doc.add_paragraph("");
656 doc.add_paragraph("All features above were built entirely from scratch using the rdocx API.")
657 .alignment(Alignment::Center)
658 .shading("E2EFDA")
659 .border_all(BorderStyle::Single, 2, "00AA00");
660
661 doc.save(path).unwrap();
662}examples/generate_all_samples.rs (line 437)
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}Auto Trait Implementations§
impl<'a> Freeze for Cell<'a>
impl<'a> RefUnwindSafe for Cell<'a>
impl<'a> Send for Cell<'a>
impl<'a> Sync for Cell<'a>
impl<'a> Unpin for Cell<'a>
impl<'a> UnsafeUnpin for Cell<'a>
impl<'a> !UnwindSafe for Cell<'a>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<U, T> ToOwnedObj<U> for Twhere
U: FromObjRef<T>,
impl<U, T> ToOwnedObj<U> for Twhere
U: FromObjRef<T>,
Source§fn to_owned_obj(&self, data: FontData<'_>) -> U
fn to_owned_obj(&self, data: FontData<'_>) -> U
Convert this type into
T, using the provided data to resolve any offsets.