diff --git a/.gitignore b/.gitignore index 36b13f1..bac36ac 100644 --- a/.gitignore +++ b/.gitignore @@ -174,3 +174,4 @@ cython_debug/ # PyPI configuration file .pypirc +*.pdf \ No newline at end of file diff --git a/README.md b/README.md index b2adde2..304a595 100644 --- a/README.md +++ b/README.md @@ -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) \ No newline at end of file +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/`) +- **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": ""}, +] +``` \ No newline at end of file diff --git a/genera_badge.py b/genera_badge.py new file mode 100644 index 0000000..d793c12 --- /dev/null +++ b/genera_badge.py @@ -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() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4d4c326 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +qrcode[pil] +reportlab