pub struct Table {
pub columns: Vec<TableColumn>,
pub header: Option<Vec<TableCell>>,
pub rows: Vec<Vec<TableCell>>,
pub striped: Option<Color>,
pub cell_padding: f32,
pub row_offset: usize,
pub common: Common,
}Fields§
§columns: Vec<TableColumn>§header: Option<Vec<TableCell>>§rows: Vec<Vec<TableCell>>§striped: Option<Color>Alternating row background (“Zebra-Streifen”), see
02-elementcatalog-and-features.md. Applies to data rows only (a
striped header would be indistinguishable from a striped data row).
cell_padding: f32Inner spacing on every side of each cell’s content, same default (4pt) header and data rows.
row_offset: usizeAbsolute index of rows[0] within the original, unsplit table —
0 unless this Table is itself the remainder produced by a
previous page’s LayoutResult::Split. Not part of the public
builder surface; exists purely so .striped() keeps alternating
correctly across a page break instead of resetting per page.
common: CommonImplementations§
Source§impl Table
impl Table
Sourcepub fn new() -> Table
pub fn new() -> Table
Examples found in repository?
examples/invoice.rs (line 113)
35fn main() {
36 let mut items = vec![
37 LineItem {
38 description: "Beratungsleistung Projekt Alpha".to_string(),
39 qty: 8,
40 unit_price_cents: 12_000,
41 },
42 LineItem {
43 description: "Lizenz Software-Paket (jährlich)".to_string(),
44 qty: 1,
45 unit_price_cents: 49_900,
46 },
47 LineItem {
48 description: "Individuelle Anpassung / Customizing".to_string(),
49 qty: 3,
50 unit_price_cents: 15_000,
51 },
52 ];
53 // A few filler positions so the table is guaranteed to span more than
54 // one page, demonstrating the header-repeat-on-split behavior.
55 for i in 1..=25 {
56 items.push(LineItem {
57 description: format!("Zusatzposition {i:02}"),
58 qty: 1,
59 unit_price_cents: 990,
60 });
61 }
62
63 let net_total: i64 = items.iter().map(|i| i.qty as i64 * i.unit_price_cents).sum();
64 let vat_rate = 19;
65 let vat_total = net_total * vat_rate / 100;
66 let gross_total = net_total + vat_total;
67
68 let top_margin = 15.0 * MM;
69 let mut doc = Document::new(PageFormat::A4)
70 .margin(Margin::symmetric(20.0 * MM, top_margin))
71 .footer(Footer::new(30.0, |ctx| {
72 Column::new()
73 .gap(2.0)
74 .child(Line::new())
75 .child(
76 Row::new()
77 .gap(20.0)
78 .child(Text::new("Musterbank · IBAN DE12 3456 7890 1234 5678 90 · BIC MUSTDEFF").size(8.0))
79 .child(Text::new("USt-IdNr. DE123456789").size(8.0).flex(1.0)),
80 )
81 .child(Text::new(format!("Seite {} von {}", ctx.page, ctx.total_pages)).size(8.0))
82 .into()
83 }));
84
85 // --- DIN 5008 Form A window-envelope address block ----------------
86 // Window starts ~45mm from the top, ~20mm from the left
87 // (`plan/02-elementcatalog-and-features.md`); ~85x40mm matches a
88 // typical C6/5-long window envelope opening. Position/size are a
89 // documented convention for this recipe, not parsed from any norm
90 // document — a caller with different envelope stock adjusts these.
91 doc.add(Spacer::new(45.0 * MM - top_margin));
92 doc.add(
93 Column::new()
94 .gap(2.0)
95 .width(85.0 * MM)
96 .height(40.0 * MM)
97 .child(Text::new("Muster GmbH · Musterstraße 1 · 12345 Musterstadt").size(7.0))
98 .child(Spacer::new(8.0))
99 .child(Text::new("Empfänger GmbH"))
100 .child(Text::new("Frau Erika Mustermann"))
101 .child(Text::new("Beispielweg 42"))
102 .child(Text::new("54321 Beispielstadt")),
103 );
104 doc.add(Spacer::new(10.0 * MM));
105
106 doc.add(Text::new("Rechnung").heading1());
107 doc.add(Text::new(
108 "Rechnungsnummer: RE-2026-0142 Rechnungsdatum: 20.08.2026 Leistungsdatum: 20.08.2026",
109 ));
110 doc.add(Spacer::new(10.0));
111
112 doc.add(
113 Table::new()
114 .columns([
115 TableColumn::flex(1.0),
116 TableColumn::fixed(50.0).align(Align::End),
117 TableColumn::fixed(65.0).align(Align::End),
118 TableColumn::fixed(70.0).align(Align::End),
119 ])
120 .header(["Beschreibung", "Menge", "Einzelpreis", "Gesamt"])
121 .striped(Color::rgb(0xF5, 0xF5, 0xF5))
122 .from_rows(&items),
123 );
124
125 doc.add(Spacer::new(14.0));
126
127 // --- summary block (Netto/USt/Brutto), right-aligned --------------
128 // Recipe (`plan/02-elementcatalog-and-features.md`): an outer, full-
129 // width auto Column with `.align(Align::End)` positions the fixed-
130 // width (200pt) inner summary Column at the right edge; within it,
131 // each label gets `.flex(1.0)` to push its value to that block's own
132 // right edge. No special "summary block" element needed.
133 doc.add(
134 Column::new()
135 .align(Align::End)
136 .child(Column::new().gap(2.0).width(200.0).children(vec![
137 Element::from(
138 Row::new()
139 .child(Text::new("Nettosumme").flex(1.0))
140 .child(Text::new(format_currency_de(net_total))),
141 ),
142 Element::from(
143 Row::new()
144 .child(Text::new(format!("zzgl. {vat_rate}% USt.")).flex(1.0))
145 .child(Text::new(format_currency_de(vat_total))),
146 ),
147 Element::from(Line::new()),
148 Element::from(
149 Row::new()
150 .child(Text::new("Gesamtbetrag").bold().flex(1.0))
151 .child(Text::new(format_currency_de(gross_total)).bold()),
152 ),
153 ])),
154 );
155
156 common::write_pdf(&doc, "invoice.pdf");
157}Sourcepub fn columns(self, columns: impl IntoIterator<Item = TableColumn>) -> Table
pub fn columns(self, columns: impl IntoIterator<Item = TableColumn>) -> Table
Examples found in repository?
examples/invoice.rs (lines 114-119)
35fn main() {
36 let mut items = vec![
37 LineItem {
38 description: "Beratungsleistung Projekt Alpha".to_string(),
39 qty: 8,
40 unit_price_cents: 12_000,
41 },
42 LineItem {
43 description: "Lizenz Software-Paket (jährlich)".to_string(),
44 qty: 1,
45 unit_price_cents: 49_900,
46 },
47 LineItem {
48 description: "Individuelle Anpassung / Customizing".to_string(),
49 qty: 3,
50 unit_price_cents: 15_000,
51 },
52 ];
53 // A few filler positions so the table is guaranteed to span more than
54 // one page, demonstrating the header-repeat-on-split behavior.
55 for i in 1..=25 {
56 items.push(LineItem {
57 description: format!("Zusatzposition {i:02}"),
58 qty: 1,
59 unit_price_cents: 990,
60 });
61 }
62
63 let net_total: i64 = items.iter().map(|i| i.qty as i64 * i.unit_price_cents).sum();
64 let vat_rate = 19;
65 let vat_total = net_total * vat_rate / 100;
66 let gross_total = net_total + vat_total;
67
68 let top_margin = 15.0 * MM;
69 let mut doc = Document::new(PageFormat::A4)
70 .margin(Margin::symmetric(20.0 * MM, top_margin))
71 .footer(Footer::new(30.0, |ctx| {
72 Column::new()
73 .gap(2.0)
74 .child(Line::new())
75 .child(
76 Row::new()
77 .gap(20.0)
78 .child(Text::new("Musterbank · IBAN DE12 3456 7890 1234 5678 90 · BIC MUSTDEFF").size(8.0))
79 .child(Text::new("USt-IdNr. DE123456789").size(8.0).flex(1.0)),
80 )
81 .child(Text::new(format!("Seite {} von {}", ctx.page, ctx.total_pages)).size(8.0))
82 .into()
83 }));
84
85 // --- DIN 5008 Form A window-envelope address block ----------------
86 // Window starts ~45mm from the top, ~20mm from the left
87 // (`plan/02-elementcatalog-and-features.md`); ~85x40mm matches a
88 // typical C6/5-long window envelope opening. Position/size are a
89 // documented convention for this recipe, not parsed from any norm
90 // document — a caller with different envelope stock adjusts these.
91 doc.add(Spacer::new(45.0 * MM - top_margin));
92 doc.add(
93 Column::new()
94 .gap(2.0)
95 .width(85.0 * MM)
96 .height(40.0 * MM)
97 .child(Text::new("Muster GmbH · Musterstraße 1 · 12345 Musterstadt").size(7.0))
98 .child(Spacer::new(8.0))
99 .child(Text::new("Empfänger GmbH"))
100 .child(Text::new("Frau Erika Mustermann"))
101 .child(Text::new("Beispielweg 42"))
102 .child(Text::new("54321 Beispielstadt")),
103 );
104 doc.add(Spacer::new(10.0 * MM));
105
106 doc.add(Text::new("Rechnung").heading1());
107 doc.add(Text::new(
108 "Rechnungsnummer: RE-2026-0142 Rechnungsdatum: 20.08.2026 Leistungsdatum: 20.08.2026",
109 ));
110 doc.add(Spacer::new(10.0));
111
112 doc.add(
113 Table::new()
114 .columns([
115 TableColumn::flex(1.0),
116 TableColumn::fixed(50.0).align(Align::End),
117 TableColumn::fixed(65.0).align(Align::End),
118 TableColumn::fixed(70.0).align(Align::End),
119 ])
120 .header(["Beschreibung", "Menge", "Einzelpreis", "Gesamt"])
121 .striped(Color::rgb(0xF5, 0xF5, 0xF5))
122 .from_rows(&items),
123 );
124
125 doc.add(Spacer::new(14.0));
126
127 // --- summary block (Netto/USt/Brutto), right-aligned --------------
128 // Recipe (`plan/02-elementcatalog-and-features.md`): an outer, full-
129 // width auto Column with `.align(Align::End)` positions the fixed-
130 // width (200pt) inner summary Column at the right edge; within it,
131 // each label gets `.flex(1.0)` to push its value to that block's own
132 // right edge. No special "summary block" element needed.
133 doc.add(
134 Column::new()
135 .align(Align::End)
136 .child(Column::new().gap(2.0).width(200.0).children(vec![
137 Element::from(
138 Row::new()
139 .child(Text::new("Nettosumme").flex(1.0))
140 .child(Text::new(format_currency_de(net_total))),
141 ),
142 Element::from(
143 Row::new()
144 .child(Text::new(format!("zzgl. {vat_rate}% USt.")).flex(1.0))
145 .child(Text::new(format_currency_de(vat_total))),
146 ),
147 Element::from(Line::new()),
148 Element::from(
149 Row::new()
150 .child(Text::new("Gesamtbetrag").bold().flex(1.0))
151 .child(Text::new(format_currency_de(gross_total)).bold()),
152 ),
153 ])),
154 );
155
156 common::write_pdf(&doc, "invoice.pdf");
157}Sourcepub fn header(
self,
cells: impl IntoIterator<Item = impl Into<TableCell>>,
) -> Table
pub fn header( self, cells: impl IntoIterator<Item = impl Into<TableCell>>, ) -> Table
Examples found in repository?
examples/invoice.rs (line 120)
35fn main() {
36 let mut items = vec![
37 LineItem {
38 description: "Beratungsleistung Projekt Alpha".to_string(),
39 qty: 8,
40 unit_price_cents: 12_000,
41 },
42 LineItem {
43 description: "Lizenz Software-Paket (jährlich)".to_string(),
44 qty: 1,
45 unit_price_cents: 49_900,
46 },
47 LineItem {
48 description: "Individuelle Anpassung / Customizing".to_string(),
49 qty: 3,
50 unit_price_cents: 15_000,
51 },
52 ];
53 // A few filler positions so the table is guaranteed to span more than
54 // one page, demonstrating the header-repeat-on-split behavior.
55 for i in 1..=25 {
56 items.push(LineItem {
57 description: format!("Zusatzposition {i:02}"),
58 qty: 1,
59 unit_price_cents: 990,
60 });
61 }
62
63 let net_total: i64 = items.iter().map(|i| i.qty as i64 * i.unit_price_cents).sum();
64 let vat_rate = 19;
65 let vat_total = net_total * vat_rate / 100;
66 let gross_total = net_total + vat_total;
67
68 let top_margin = 15.0 * MM;
69 let mut doc = Document::new(PageFormat::A4)
70 .margin(Margin::symmetric(20.0 * MM, top_margin))
71 .footer(Footer::new(30.0, |ctx| {
72 Column::new()
73 .gap(2.0)
74 .child(Line::new())
75 .child(
76 Row::new()
77 .gap(20.0)
78 .child(Text::new("Musterbank · IBAN DE12 3456 7890 1234 5678 90 · BIC MUSTDEFF").size(8.0))
79 .child(Text::new("USt-IdNr. DE123456789").size(8.0).flex(1.0)),
80 )
81 .child(Text::new(format!("Seite {} von {}", ctx.page, ctx.total_pages)).size(8.0))
82 .into()
83 }));
84
85 // --- DIN 5008 Form A window-envelope address block ----------------
86 // Window starts ~45mm from the top, ~20mm from the left
87 // (`plan/02-elementcatalog-and-features.md`); ~85x40mm matches a
88 // typical C6/5-long window envelope opening. Position/size are a
89 // documented convention for this recipe, not parsed from any norm
90 // document — a caller with different envelope stock adjusts these.
91 doc.add(Spacer::new(45.0 * MM - top_margin));
92 doc.add(
93 Column::new()
94 .gap(2.0)
95 .width(85.0 * MM)
96 .height(40.0 * MM)
97 .child(Text::new("Muster GmbH · Musterstraße 1 · 12345 Musterstadt").size(7.0))
98 .child(Spacer::new(8.0))
99 .child(Text::new("Empfänger GmbH"))
100 .child(Text::new("Frau Erika Mustermann"))
101 .child(Text::new("Beispielweg 42"))
102 .child(Text::new("54321 Beispielstadt")),
103 );
104 doc.add(Spacer::new(10.0 * MM));
105
106 doc.add(Text::new("Rechnung").heading1());
107 doc.add(Text::new(
108 "Rechnungsnummer: RE-2026-0142 Rechnungsdatum: 20.08.2026 Leistungsdatum: 20.08.2026",
109 ));
110 doc.add(Spacer::new(10.0));
111
112 doc.add(
113 Table::new()
114 .columns([
115 TableColumn::flex(1.0),
116 TableColumn::fixed(50.0).align(Align::End),
117 TableColumn::fixed(65.0).align(Align::End),
118 TableColumn::fixed(70.0).align(Align::End),
119 ])
120 .header(["Beschreibung", "Menge", "Einzelpreis", "Gesamt"])
121 .striped(Color::rgb(0xF5, 0xF5, 0xF5))
122 .from_rows(&items),
123 );
124
125 doc.add(Spacer::new(14.0));
126
127 // --- summary block (Netto/USt/Brutto), right-aligned --------------
128 // Recipe (`plan/02-elementcatalog-and-features.md`): an outer, full-
129 // width auto Column with `.align(Align::End)` positions the fixed-
130 // width (200pt) inner summary Column at the right edge; within it,
131 // each label gets `.flex(1.0)` to push its value to that block's own
132 // right edge. No special "summary block" element needed.
133 doc.add(
134 Column::new()
135 .align(Align::End)
136 .child(Column::new().gap(2.0).width(200.0).children(vec![
137 Element::from(
138 Row::new()
139 .child(Text::new("Nettosumme").flex(1.0))
140 .child(Text::new(format_currency_de(net_total))),
141 ),
142 Element::from(
143 Row::new()
144 .child(Text::new(format!("zzgl. {vat_rate}% USt.")).flex(1.0))
145 .child(Text::new(format_currency_de(vat_total))),
146 ),
147 Element::from(Line::new()),
148 Element::from(
149 Row::new()
150 .child(Text::new("Gesamtbetrag").bold().flex(1.0))
151 .child(Text::new(format_currency_de(gross_total)).bold()),
152 ),
153 ])),
154 );
155
156 common::write_pdf(&doc, "invoice.pdf");
157}pub fn rows( self, rows: impl IntoIterator<Item = impl IntoIterator<Item = impl Into<TableCell>>>, ) -> Table
Sourcepub fn from_rows<T>(self, items: &[T]) -> Tablewhere
T: TableRow,
pub fn from_rows<T>(self, items: &[T]) -> Tablewhere
T: TableRow,
.rows(..) for anything implementing TableRow — one row per item,
in order.
Examples found in repository?
examples/invoice.rs (line 122)
35fn main() {
36 let mut items = vec![
37 LineItem {
38 description: "Beratungsleistung Projekt Alpha".to_string(),
39 qty: 8,
40 unit_price_cents: 12_000,
41 },
42 LineItem {
43 description: "Lizenz Software-Paket (jährlich)".to_string(),
44 qty: 1,
45 unit_price_cents: 49_900,
46 },
47 LineItem {
48 description: "Individuelle Anpassung / Customizing".to_string(),
49 qty: 3,
50 unit_price_cents: 15_000,
51 },
52 ];
53 // A few filler positions so the table is guaranteed to span more than
54 // one page, demonstrating the header-repeat-on-split behavior.
55 for i in 1..=25 {
56 items.push(LineItem {
57 description: format!("Zusatzposition {i:02}"),
58 qty: 1,
59 unit_price_cents: 990,
60 });
61 }
62
63 let net_total: i64 = items.iter().map(|i| i.qty as i64 * i.unit_price_cents).sum();
64 let vat_rate = 19;
65 let vat_total = net_total * vat_rate / 100;
66 let gross_total = net_total + vat_total;
67
68 let top_margin = 15.0 * MM;
69 let mut doc = Document::new(PageFormat::A4)
70 .margin(Margin::symmetric(20.0 * MM, top_margin))
71 .footer(Footer::new(30.0, |ctx| {
72 Column::new()
73 .gap(2.0)
74 .child(Line::new())
75 .child(
76 Row::new()
77 .gap(20.0)
78 .child(Text::new("Musterbank · IBAN DE12 3456 7890 1234 5678 90 · BIC MUSTDEFF").size(8.0))
79 .child(Text::new("USt-IdNr. DE123456789").size(8.0).flex(1.0)),
80 )
81 .child(Text::new(format!("Seite {} von {}", ctx.page, ctx.total_pages)).size(8.0))
82 .into()
83 }));
84
85 // --- DIN 5008 Form A window-envelope address block ----------------
86 // Window starts ~45mm from the top, ~20mm from the left
87 // (`plan/02-elementcatalog-and-features.md`); ~85x40mm matches a
88 // typical C6/5-long window envelope opening. Position/size are a
89 // documented convention for this recipe, not parsed from any norm
90 // document — a caller with different envelope stock adjusts these.
91 doc.add(Spacer::new(45.0 * MM - top_margin));
92 doc.add(
93 Column::new()
94 .gap(2.0)
95 .width(85.0 * MM)
96 .height(40.0 * MM)
97 .child(Text::new("Muster GmbH · Musterstraße 1 · 12345 Musterstadt").size(7.0))
98 .child(Spacer::new(8.0))
99 .child(Text::new("Empfänger GmbH"))
100 .child(Text::new("Frau Erika Mustermann"))
101 .child(Text::new("Beispielweg 42"))
102 .child(Text::new("54321 Beispielstadt")),
103 );
104 doc.add(Spacer::new(10.0 * MM));
105
106 doc.add(Text::new("Rechnung").heading1());
107 doc.add(Text::new(
108 "Rechnungsnummer: RE-2026-0142 Rechnungsdatum: 20.08.2026 Leistungsdatum: 20.08.2026",
109 ));
110 doc.add(Spacer::new(10.0));
111
112 doc.add(
113 Table::new()
114 .columns([
115 TableColumn::flex(1.0),
116 TableColumn::fixed(50.0).align(Align::End),
117 TableColumn::fixed(65.0).align(Align::End),
118 TableColumn::fixed(70.0).align(Align::End),
119 ])
120 .header(["Beschreibung", "Menge", "Einzelpreis", "Gesamt"])
121 .striped(Color::rgb(0xF5, 0xF5, 0xF5))
122 .from_rows(&items),
123 );
124
125 doc.add(Spacer::new(14.0));
126
127 // --- summary block (Netto/USt/Brutto), right-aligned --------------
128 // Recipe (`plan/02-elementcatalog-and-features.md`): an outer, full-
129 // width auto Column with `.align(Align::End)` positions the fixed-
130 // width (200pt) inner summary Column at the right edge; within it,
131 // each label gets `.flex(1.0)` to push its value to that block's own
132 // right edge. No special "summary block" element needed.
133 doc.add(
134 Column::new()
135 .align(Align::End)
136 .child(Column::new().gap(2.0).width(200.0).children(vec![
137 Element::from(
138 Row::new()
139 .child(Text::new("Nettosumme").flex(1.0))
140 .child(Text::new(format_currency_de(net_total))),
141 ),
142 Element::from(
143 Row::new()
144 .child(Text::new(format!("zzgl. {vat_rate}% USt.")).flex(1.0))
145 .child(Text::new(format_currency_de(vat_total))),
146 ),
147 Element::from(Line::new()),
148 Element::from(
149 Row::new()
150 .child(Text::new("Gesamtbetrag").bold().flex(1.0))
151 .child(Text::new(format_currency_de(gross_total)).bold()),
152 ),
153 ])),
154 );
155
156 common::write_pdf(&doc, "invoice.pdf");
157}Sourcepub fn striped(self, color: Color) -> Table
pub fn striped(self, color: Color) -> Table
Examples found in repository?
examples/invoice.rs (line 121)
35fn main() {
36 let mut items = vec![
37 LineItem {
38 description: "Beratungsleistung Projekt Alpha".to_string(),
39 qty: 8,
40 unit_price_cents: 12_000,
41 },
42 LineItem {
43 description: "Lizenz Software-Paket (jährlich)".to_string(),
44 qty: 1,
45 unit_price_cents: 49_900,
46 },
47 LineItem {
48 description: "Individuelle Anpassung / Customizing".to_string(),
49 qty: 3,
50 unit_price_cents: 15_000,
51 },
52 ];
53 // A few filler positions so the table is guaranteed to span more than
54 // one page, demonstrating the header-repeat-on-split behavior.
55 for i in 1..=25 {
56 items.push(LineItem {
57 description: format!("Zusatzposition {i:02}"),
58 qty: 1,
59 unit_price_cents: 990,
60 });
61 }
62
63 let net_total: i64 = items.iter().map(|i| i.qty as i64 * i.unit_price_cents).sum();
64 let vat_rate = 19;
65 let vat_total = net_total * vat_rate / 100;
66 let gross_total = net_total + vat_total;
67
68 let top_margin = 15.0 * MM;
69 let mut doc = Document::new(PageFormat::A4)
70 .margin(Margin::symmetric(20.0 * MM, top_margin))
71 .footer(Footer::new(30.0, |ctx| {
72 Column::new()
73 .gap(2.0)
74 .child(Line::new())
75 .child(
76 Row::new()
77 .gap(20.0)
78 .child(Text::new("Musterbank · IBAN DE12 3456 7890 1234 5678 90 · BIC MUSTDEFF").size(8.0))
79 .child(Text::new("USt-IdNr. DE123456789").size(8.0).flex(1.0)),
80 )
81 .child(Text::new(format!("Seite {} von {}", ctx.page, ctx.total_pages)).size(8.0))
82 .into()
83 }));
84
85 // --- DIN 5008 Form A window-envelope address block ----------------
86 // Window starts ~45mm from the top, ~20mm from the left
87 // (`plan/02-elementcatalog-and-features.md`); ~85x40mm matches a
88 // typical C6/5-long window envelope opening. Position/size are a
89 // documented convention for this recipe, not parsed from any norm
90 // document — a caller with different envelope stock adjusts these.
91 doc.add(Spacer::new(45.0 * MM - top_margin));
92 doc.add(
93 Column::new()
94 .gap(2.0)
95 .width(85.0 * MM)
96 .height(40.0 * MM)
97 .child(Text::new("Muster GmbH · Musterstraße 1 · 12345 Musterstadt").size(7.0))
98 .child(Spacer::new(8.0))
99 .child(Text::new("Empfänger GmbH"))
100 .child(Text::new("Frau Erika Mustermann"))
101 .child(Text::new("Beispielweg 42"))
102 .child(Text::new("54321 Beispielstadt")),
103 );
104 doc.add(Spacer::new(10.0 * MM));
105
106 doc.add(Text::new("Rechnung").heading1());
107 doc.add(Text::new(
108 "Rechnungsnummer: RE-2026-0142 Rechnungsdatum: 20.08.2026 Leistungsdatum: 20.08.2026",
109 ));
110 doc.add(Spacer::new(10.0));
111
112 doc.add(
113 Table::new()
114 .columns([
115 TableColumn::flex(1.0),
116 TableColumn::fixed(50.0).align(Align::End),
117 TableColumn::fixed(65.0).align(Align::End),
118 TableColumn::fixed(70.0).align(Align::End),
119 ])
120 .header(["Beschreibung", "Menge", "Einzelpreis", "Gesamt"])
121 .striped(Color::rgb(0xF5, 0xF5, 0xF5))
122 .from_rows(&items),
123 );
124
125 doc.add(Spacer::new(14.0));
126
127 // --- summary block (Netto/USt/Brutto), right-aligned --------------
128 // Recipe (`plan/02-elementcatalog-and-features.md`): an outer, full-
129 // width auto Column with `.align(Align::End)` positions the fixed-
130 // width (200pt) inner summary Column at the right edge; within it,
131 // each label gets `.flex(1.0)` to push its value to that block's own
132 // right edge. No special "summary block" element needed.
133 doc.add(
134 Column::new()
135 .align(Align::End)
136 .child(Column::new().gap(2.0).width(200.0).children(vec![
137 Element::from(
138 Row::new()
139 .child(Text::new("Nettosumme").flex(1.0))
140 .child(Text::new(format_currency_de(net_total))),
141 ),
142 Element::from(
143 Row::new()
144 .child(Text::new(format!("zzgl. {vat_rate}% USt.")).flex(1.0))
145 .child(Text::new(format_currency_de(vat_total))),
146 ),
147 Element::from(Line::new()),
148 Element::from(
149 Row::new()
150 .child(Text::new("Gesamtbetrag").bold().flex(1.0))
151 .child(Text::new(format_currency_de(gross_total)).bold()),
152 ),
153 ])),
154 );
155
156 common::write_pdf(&doc, "invoice.pdf");
157}pub fn cell_padding(self, padding: f32) -> Table
pub fn width(self, width: f32) -> Table
pub fn height(self, height: f32) -> Table
pub fn flex(self, factor: f32) -> Table
pub fn keep_with_next(self) -> Table
Trait Implementations§
Source§impl Layoutable for Table
impl Layoutable for Table
fn measure(&self, ctx: &LayoutCtx<'_>, constraints: Constraints) -> Size
fn layout( &self, ctx: &LayoutCtx<'_>, area: Rect, warnings: &mut Vec<LayoutWarning>, page: usize, ) -> LayoutResult
Auto Trait Implementations§
impl Freeze for Table
impl RefUnwindSafe for Table
impl Send for Table
impl Sync for Table
impl Unpin for Table
impl UnsafeUnpin for Table
impl UnwindSafe for Table
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