83 lines
2.5 KiB
Python
83 lines
2.5 KiB
Python
from __future__ import annotations
|
|
|
|
import io
|
|
|
|
from reportlab.lib import colors
|
|
from reportlab.lib.pagesizes import A4, landscape
|
|
from reportlab.lib.styles import getSampleStyleSheet
|
|
from reportlab.lib.units import cm
|
|
from reportlab.platypus import (
|
|
Paragraph,
|
|
SimpleDocTemplate,
|
|
Spacer,
|
|
Table,
|
|
TableStyle,
|
|
)
|
|
|
|
_PAGE_W, _ = landscape(A4)
|
|
_MARGIN = 1.5 * cm
|
|
_HEADER_BG = colors.HexColor("#1a56db")
|
|
_ROW_BG = colors.HexColor("#eef2ff")
|
|
|
|
|
|
def _pdf_table(rows: list[dict]) -> Table:
|
|
if not rows:
|
|
table_data: list[list] = [["No data available"]]
|
|
n_cols = 1
|
|
else:
|
|
headers = list(rows[0].keys())
|
|
n_cols = len(headers)
|
|
table_data = [headers] + [
|
|
[str(row.get(h, "")) for h in headers] for row in rows
|
|
]
|
|
|
|
col_w = (_PAGE_W - 2 * _MARGIN) / n_cols
|
|
t = Table(table_data, colWidths=[col_w] * n_cols, repeatRows=1)
|
|
|
|
style: list = [
|
|
("BACKGROUND", (0, 0), (-1, 0), _HEADER_BG),
|
|
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
|
|
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
|
|
("FONTSIZE", (0, 0), (-1, 0), 8),
|
|
("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
|
|
("FONTSIZE", (0, 1), (-1, -1), 7),
|
|
("ALIGN", (0, 0), (-1, -1), "LEFT"),
|
|
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
|
|
("GRID", (0, 0), (-1, -1), 0.25, colors.HexColor("#d1d5db")),
|
|
("TOPPADDING", (0, 0), (-1, -1), 3),
|
|
("BOTTOMPADDING", (0, 0), (-1, -1), 3),
|
|
("LEFTPADDING", (0, 0), (-1, -1), 5),
|
|
("RIGHTPADDING", (0, 0), (-1, -1), 5),
|
|
]
|
|
for i in range(1, len(table_data)):
|
|
bg = _ROW_BG if i % 2 == 1 else colors.white
|
|
style.append(("BACKGROUND", (0, i), (-1, i), bg))
|
|
|
|
t.setStyle(TableStyle(style))
|
|
return t
|
|
|
|
|
|
def to_pdf_bytes(rows: list[dict], title: str, subtitle: str = "") -> bytes:
|
|
"""Serialise *rows* to a single-sheet PDF and return the raw bytes."""
|
|
buf = io.BytesIO()
|
|
styles = getSampleStyleSheet()
|
|
story = []
|
|
|
|
story.append(Paragraph(title, styles["Title"]))
|
|
if subtitle:
|
|
story.append(Spacer(1, 0.2 * cm))
|
|
story.append(Paragraph(subtitle, styles["Normal"]))
|
|
story.append(Spacer(1, 0.5 * cm))
|
|
story.append(_pdf_table(rows))
|
|
|
|
doc = SimpleDocTemplate(
|
|
buf,
|
|
pagesize=landscape(A4),
|
|
leftMargin=_MARGIN,
|
|
rightMargin=_MARGIN,
|
|
topMargin=_MARGIN,
|
|
bottomMargin=_MARGIN,
|
|
)
|
|
doc.build(story)
|
|
return buf.getvalue()
|