script per stampa badge last minute
This commit is contained in:
@@ -174,3 +174,4 @@ cython_debug/
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
*.pdf
|
||||
@@ -1,3 +1,64 @@
|
||||
# grafica-badge-itdevcon
|
||||
|
||||
Script python per la creazione delle grafiche dei badge vuoti di itdevcon (per i partecipanti dell'ultimo minuto)
|
||||
Script python per la creazione delle grafiche dei badge vuoti di itdevcon (per i partecipanti dell'ultimo minuto)
|
||||
|
||||
## Setup ambiente
|
||||
|
||||
Prerequisito: [uv](https://docs.astral.sh/uv/getting-started/installation/) installato.
|
||||
|
||||
```bash
|
||||
# Crea e attiva il virtual environment
|
||||
uv venv
|
||||
|
||||
# Installa le dipendenze da requirements.txt
|
||||
uv pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Per attivare manualmente il virtual environment:
|
||||
|
||||
- **Windows (PowerShell):** `.venv\Scripts\Activate.ps1`
|
||||
- **Windows (CMD):** `.venv\Scripts\activate.bat`
|
||||
- **Linux/macOS:** `source .venv/bin/activate`
|
||||
|
||||
## Esecuzione
|
||||
|
||||
Con uv (senza attivare manualmente il venv):
|
||||
|
||||
```bash
|
||||
uv run genera_badge.py
|
||||
```
|
||||
|
||||
Oppure, con il virtual environment attivo:
|
||||
|
||||
```bash
|
||||
python genera_badge.py
|
||||
```
|
||||
|
||||
L'output è un file PDF (`badge_qr.pdf`) con i badge pronti per la stampa.
|
||||
|
||||
## Cosa fa lo script
|
||||
|
||||
`genera_badge.py` genera badge in formato PDF per i partecipanti dell'evento itdevcon.
|
||||
|
||||
Per ogni partecipante crea un badge di **9x5 cm** contenente:
|
||||
|
||||
- **Nome** e **azienda** (centrati, font Helvetica-Bold 18pt)
|
||||
- **QR code** con il link alla pagina di voto live (`https://live.itdevcon.it/web/vote/<id>`)
|
||||
- **Angoli di ritaglio** (segni a "L" sui 4 angoli per facilitare il taglio manuale)
|
||||
|
||||
Il layout è ottimizzato per la stampa su **A4 landscape**: ogni pagina ospita una griglia di **2 righe x 3 colonne** (6 badge per pagina). Questa disposizione è stata scelta per evitare la zona laterale del foglio adesivo pieghevole, che non è stampabile, usando solo le aree "buone" del foglio.
|
||||
|
||||
Il file di output predefinito è `badge_qr.pdf`.
|
||||
|
||||
### Personalizzazione
|
||||
|
||||
I dati dei partecipanti vanno inseriti direttamente nell'array `partecipanti` all'interno dello script (`genera_badge.py`, riga 12). Ogni entry è un dizionario con chiavi `id`, `nome` e opzionale `azienda`.
|
||||
|
||||
Esempio:
|
||||
|
||||
```python
|
||||
partecipanti = [
|
||||
{"id": "ABC123", "nome": "Mario Rossi", "azienda": "ItDevCon Srl"},
|
||||
{"id": "DEF456", "nome": "Anna Bianchi", "azienda": ""},
|
||||
]
|
||||
```
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
import qrcode
|
||||
from reportlab.lib.pagesizes import A4, landscape
|
||||
from reportlab.lib.units import cm
|
||||
from reportlab.lib.utils import ImageReader
|
||||
from reportlab.pdfgen import canvas
|
||||
from PIL import Image
|
||||
import io
|
||||
import os
|
||||
|
||||
# --- CONFIGURAZIONE ---
|
||||
partecipanti = [
|
||||
# {"id": "ABC123", "nome": "Mario Rossi Mrio Rossi Mario R", "azienda": "ItDevCon Srl"},
|
||||
# {"id": "f6d8a77b7348f111", "nome": "Pietro Angelo Polito", "azienda": "Tecnofarma Srl"},
|
||||
# {"id": "f0d8a77b7348f111", "nome": "Helena Giliberti", "azienda": ""},
|
||||
# {"id": "f3d8a77b7348f111", "nome": "Simone Ercolani", "azienda": "Itaca"},
|
||||
# {"id": "f2d8a77b7348f111", "nome": "Emanuele Malvatani", "azienda": "Itaca"},
|
||||
# {"id": "f1d8a77b7348f111", "nome": "Lorenzo Maria Soricetti", "azienda": "Itaca"},
|
||||
# {"id": "f4d8a77b7348f111", "nome": "Luca Malvatani", "azienda": "Itaca"},
|
||||
{"id": "f5d8a77b7348f111", "nome": "Alessio Cimadamore", "azienda": "Itaca"},
|
||||
# Aggiungi quanti partecipanti vuoi
|
||||
]
|
||||
|
||||
base_url = "https://live.itdevcon.it/web/vote/"
|
||||
|
||||
output_pdf = "badge_qr.pdf"
|
||||
|
||||
# Dimensioni singolo badge (cm)
|
||||
badge_w = 9 * cm
|
||||
badge_h = 5 * cm
|
||||
|
||||
# Margini pagina A4 landscape
|
||||
margin_x = 1.0 * cm
|
||||
margin_y = 1.5 * cm
|
||||
|
||||
# Layout griglia su A4 landscape
|
||||
rows_per_page = 2
|
||||
cols_per_page = 3
|
||||
|
||||
# Font
|
||||
font_name = "Helvetica-Bold"
|
||||
font_size = 18
|
||||
font_azienda = "Helvetica"
|
||||
font_size_azienda = 12
|
||||
|
||||
# QR code
|
||||
qr_width = 3.2 * cm # larghezza/altezza del QR nel PDF
|
||||
|
||||
# Colore branding
|
||||
color_brand = "#040073"
|
||||
color_brand_light = "#4A6DAD"
|
||||
|
||||
|
||||
def create_qr_image(data, size=200):
|
||||
"""Genera un'immagine QR code in memoria come BytesIO PNG."""
|
||||
qr = qrcode.QRCode(
|
||||
version=1,
|
||||
error_correction=qrcode.constants.ERROR_CORRECT_M,
|
||||
box_size=10,
|
||||
border=1,
|
||||
)
|
||||
qr.add_data(data)
|
||||
qr.make(fit=True)
|
||||
img = qr.make_image(fill_color=color_brand, back_color="white")
|
||||
img = img.convert("RGB")
|
||||
img = img.resize((size, size), Image.LANCZOS)
|
||||
img_bytes = io.BytesIO()
|
||||
img.save(img_bytes, format="PNG")
|
||||
img_bytes.seek(0)
|
||||
return img_bytes
|
||||
|
||||
|
||||
def draw_badge(c, x, y, id_partecipante, nome, azienda):
|
||||
"""Disegna un singolo badge 9x5 cm con angolo in basso a sinistra a (x, y)."""
|
||||
nome = nome[:26]
|
||||
azienda = azienda[:26]
|
||||
link = f"{base_url}{id_partecipante}"
|
||||
|
||||
# Angoli per facilitare il ritaglio (L di 0.5 cm per angolo)
|
||||
c.setStrokeColorRGB(0, 0, 0)
|
||||
c.setLineWidth(0.5)
|
||||
corner = 0.5 * cm
|
||||
|
||||
# Angolo basso-sinistra
|
||||
c.line(x, y, x + corner, y)
|
||||
c.line(x, y, x, y + corner)
|
||||
|
||||
# Angolo basso-destra
|
||||
c.line(x + badge_w - corner, y, x + badge_w, y)
|
||||
c.line(x + badge_w, y, x + badge_w, y + corner)
|
||||
|
||||
# Angolo alto-sinistra
|
||||
c.line(x, y + badge_h - corner, x, y + badge_h)
|
||||
c.line(x, y + badge_h, x + corner, y + badge_h)
|
||||
|
||||
# Angolo alto-destra
|
||||
c.line(x + badge_w - corner, y + badge_h, x + badge_w, y + badge_h)
|
||||
c.line(x + badge_w, y + badge_h - corner, x + badge_w, y + badge_h)
|
||||
|
||||
# Nome centrato nella parte superiore
|
||||
c.setFillColor(color_brand)
|
||||
c.setFont(font_name, font_size)
|
||||
text_width = c.stringWidth(nome, font_name, font_size)
|
||||
text_x = x + (badge_w - text_width) / 2
|
||||
text_y = y + badge_h - 0.7 * cm
|
||||
c.drawString(text_x, text_y, nome)
|
||||
|
||||
# Azienda centrata subito sotto il nome (font più fine)
|
||||
c.setFillColor(color_brand_light)
|
||||
c.setFont(font_azienda, font_size_azienda)
|
||||
text_width = c.stringWidth(azienda, font_azienda, font_size_azienda)
|
||||
text_x = x + (badge_w - text_width) / 2
|
||||
text_y_azienda = text_y - 0.6 * cm
|
||||
c.drawString(text_x, text_y_azienda, azienda)
|
||||
|
||||
# QR code centrato subito sotto l'azienda
|
||||
qr_bytes = create_qr_image(link, size=200)
|
||||
qr_img = ImageReader(qr_bytes)
|
||||
qr_x = x + (badge_w - qr_width) / 2
|
||||
qr_y = text_y_azienda - qr_width - 0.3 * cm
|
||||
c.drawImage(qr_img, qr_x, qr_y, width=qr_width, height=qr_width)
|
||||
|
||||
|
||||
def generate_pdf():
|
||||
c = canvas.Canvas(output_pdf, pagesize=landscape(A4))
|
||||
page_w, page_h = landscape(A4)
|
||||
|
||||
# Spazio fisso tra badge per facilitare il ritaglio
|
||||
gap = 0.2 * cm
|
||||
|
||||
per_page = rows_per_page * cols_per_page
|
||||
|
||||
for idx, p in enumerate(partecipanti):
|
||||
if idx > 0 and idx % per_page == 0:
|
||||
c.showPage()
|
||||
|
||||
pos_in_page = idx % per_page
|
||||
col = pos_in_page % cols_per_page
|
||||
row = pos_in_page // cols_per_page
|
||||
|
||||
x = page_w - margin_x - badge_w - col * (badge_w + gap)
|
||||
y = page_h - margin_y - (row + 1) * badge_h - row * gap
|
||||
|
||||
draw_badge(c, x, y, p["id"], p["nome"], p.get("azienda", ""))
|
||||
|
||||
c.save()
|
||||
print(f"PDF generato: {os.path.abspath(output_pdf)}")
|
||||
print(f"Totale badge: {len(partecipanti)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_pdf()
|
||||
@@ -0,0 +1,2 @@
|
||||
qrcode[pil]
|
||||
reportlab
|
||||
Reference in New Issue
Block a user