feat: adds pdf export with imprintor
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
496e2e438f
commit
f6b35f03a5
16 changed files with 1962 additions and 70 deletions
85
priv/pdf_templates/members_export.typ
Normal file
85
priv/pdf_templates/members_export.typ
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
// Typst template for member export (PDF)
|
||||
// Expected sys.inputs.elixir_data:
|
||||
// {
|
||||
// "columns": [{"key": "...", "kind": "...", "label": "..."}, ...],
|
||||
// "rows": [["cell1", "cell2", ...], ...],
|
||||
// "meta": {"generated_at": "...", "member_count": 123}
|
||||
// }
|
||||
|
||||
#set page(
|
||||
paper: "a4",
|
||||
flipped: true,
|
||||
margin: (top: 1.2cm, bottom: 1.2cm, left: 1.0cm, right: 1.0cm)
|
||||
)
|
||||
|
||||
#set text(size: 9pt)
|
||||
#set heading(numbering: none)
|
||||
|
||||
#let data = sys.inputs.elixir_data
|
||||
#let columns = data.at("columns", default: ())
|
||||
#let rows = data.at("rows", default: ())
|
||||
#let meta = data.at("meta", default: (generated_at: "", member_count: rows.len()))
|
||||
|
||||
// Title
|
||||
#align(center)[
|
||||
#text(size: 14pt, weight: "bold")[Mitglieder-Export]
|
||||
]
|
||||
|
||||
#v(0.4cm)
|
||||
|
||||
// Export metadata
|
||||
#set text(size: 8pt, fill: gray)
|
||||
#grid(
|
||||
columns: (1fr, 1fr),
|
||||
gutter: 1cm,
|
||||
[*Erstellt am:* #meta.at("generated_at", default: "")],
|
||||
[*Anzahl Mitglieder:* #meta.at("member_count", default: rows.len())],
|
||||
)
|
||||
|
||||
#v(0.6cm)
|
||||
|
||||
// ---- Horizontal paging config ----
|
||||
#let fixed_count = calc.min(2, columns.len())
|
||||
#let max_dynamic_cols = 6
|
||||
#let fixed_col_width = 1.6fr
|
||||
|
||||
#let fixed_cols = columns.slice(0, fixed_count)
|
||||
#let dynamic_cols = columns.slice(fixed_count, columns.len())
|
||||
#let dynamic_chunks = dynamic_cols.chunks(max_dynamic_cols)
|
||||
|
||||
#let render_chunk(chunk_index, dyn_cols_chunk) = [
|
||||
#let dyn_count = dyn_cols_chunk.len()
|
||||
#let start = fixed_count + chunk_index * max_dynamic_cols
|
||||
|
||||
#let page_cols = fixed_cols + dyn_cols_chunk
|
||||
#let headers = page_cols.map(c => c.at("label", default: ""))
|
||||
|
||||
// widths: fixe breiter, dynamische gleichmäßig
|
||||
#let widths = (fixed_col_width,) * fixed_count + (1fr,) * dyn_count
|
||||
|
||||
#let header_cells = headers.map(h => text(weight: "bold", size: 9pt)[#h])
|
||||
|
||||
// Body cells (row-major), nur die Spalten dieses Chunks
|
||||
#let body_cells = (
|
||||
rows
|
||||
.map(row => row.slice(0, fixed_count) + row.slice(start, start + dyn_count))
|
||||
.map(cells => cells.map(cell => text(size: 8.5pt)[#cell]))
|
||||
.flatten()
|
||||
)
|
||||
|
||||
#table(
|
||||
columns: widths,
|
||||
table.header(..header_cells),
|
||||
..body_cells,
|
||||
)
|
||||
]
|
||||
|
||||
// ---- Output ----
|
||||
#if dynamic_cols.len() == 0 {
|
||||
render_chunk(0, ())
|
||||
} else {
|
||||
for (i, chunk) in dynamic_chunks.enumerate() {
|
||||
render_chunk(i, chunk)
|
||||
if i < dynamic_chunks.len() - 1 { pagebreak() }
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue