script per stampa badge last minute
This commit is contained in:
@@ -174,3 +174,4 @@ cython_debug/
|
|||||||
# PyPI configuration file
|
# PyPI configuration file
|
||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
|
*.pdf
|
||||||
@@ -1,3 +1,64 @@
|
|||||||
# grafica-badge-itdevcon
|
# 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