# Piano: Sistema di Report PDF con Editor Visuale ## Obiettivo Creare un sistema completo di generazione report PDF con: - Editor grafico drag-and-drop (stile Canva) - Potenza di JasperReports (data binding, paginazione, formule) - Metalinguaggio esportabile/importabile (tipo LaTeX) - Salvataggio template riutilizzabili - Supporto immagini e font personalizzati ## Architettura Proposta ### 1. Metalinguaggio Template (APRT - Apollinare Report Template) ```json { "version": "1.0", "meta": { "name": "Scheda Evento", "description": "Template per stampa evento", "author": "admin", "createdAt": "2025-01-15", "pageSize": "A4", "orientation": "portrait", "margins": { "top": 20, "right": 15, "bottom": 20, "left": 15 } }, "resources": { "fonts": [ { "id": "font1", "name": "Roboto", "url": "/fonts/roboto.ttf" } ], "images": [ { "id": "logo", "name": "Logo Aziendale", "url": "/images/logo.png" } ] }, "dataSources": { "evento": { "type": "object", "schema": "Evento" }, "ospiti": { "type": "array", "schema": "EventoDettaglioOspiti" }, "costi": { "type": "array", "schema": "EventoAltroCosto" } }, "sections": [ { "type": "header", "height": 80, "repeatOnPages": true, "elements": [...] }, { "type": "body", "elements": [...] }, { "type": "detail", "dataSource": "ospiti", "elements": [...] }, { "type": "footer", "height": 40, "repeatOnPages": true, "elements": [...] } ], "elements": [ { "id": "elem1", "type": "text", "position": { "x": 10, "y": 10, "width": 200, "height": 30 }, "style": { "fontFamily": "font1", "fontSize": 24, "fontWeight": "bold", "color": "#333333", "textAlign": "left" }, "content": { "type": "static", "value": "SCHEDA EVENTO" } }, { "id": "elem2", "type": "text", "position": { "x": 10, "y": 50, "width": 150, "height": 20 }, "content": { "type": "binding", "expression": "{{evento.codice}}" } }, { "id": "elem3", "type": "image", "position": { "x": 450, "y": 10, "width": 100, "height": 60 }, "content": { "type": "resource", "resourceId": "logo" } }, { "id": "elem4", "type": "table", "position": { "x": 10, "y": 200, "width": 550, "height": "auto" }, "dataSource": "ospiti", "columns": [ { "field": "tipoOspite.descrizione", "header": "Tipo", "width": 150 }, { "field": "numero", "header": "Quantità", "width": 100 }, { "field": "costoUnitario", "header": "Costo Unit.", "width": 100, "format": "currency" }, { "field": "costoTotale", "header": "Totale", "width": 100, "format": "currency" } ] }, { "id": "elem5", "type": "shape", "position": { "x": 10, "y": 180, "width": 550, "height": 2 }, "style": { "backgroundColor": "#000000" } }, { "id": "pageNum", "type": "text", "section": "footer", "position": { "x": 250, "y": 10, "width": 100, "height": 20 }, "content": { "type": "expression", "value": "Pagina {{$pageNumber}} di {{$totalPages}}" } } ] } ``` ### 2. Struttura Backend #### Nuove Entità ``` ReportTemplate ├── Id ├── Nome ├── Descrizione ├── Categoria (Evento, Cliente, Articoli, etc.) ├── TemplateJson (il metalinguaggio APRT) ├── Thumbnail (preview del template) ├── Attivo ├── CreatedAt/By, UpdatedAt/By ReportFont ├── Id ├── Nome ├── FontFamily ├── FontData (BLOB - file TTF/OTF) ├── MimeType ReportImage ├── Id ├── Nome ├── Categoria ├── ImageData (BLOB) ├── MimeType ├── Width, Height ``` #### Nuovi Controller ``` ReportTemplatesController ├── GET /api/report-templates # Lista template ├── GET /api/report-templates/{id} # Dettaglio ├── POST /api/report-templates # Crea ├── PUT /api/report-templates/{id} # Aggiorna ├── DELETE /api/report-templates/{id} # Elimina ├── POST /api/report-templates/{id}/clone # Duplica ├── GET /api/report-templates/{id}/export # Esporta .aprt ├── POST /api/report-templates/import # Importa .aprt ReportResourcesController ├── GET /api/report-resources/fonts # Lista font ├── POST /api/report-resources/fonts # Upload font ├── DELETE /api/report-resources/fonts/{id} ├── GET /api/report-resources/images # Lista immagini ├── POST /api/report-resources/images # Upload immagine ├── DELETE /api/report-resources/images/{id} ReportGeneratorController ├── POST /api/reports/generate # Genera PDF │ Body: { templateId, dataContext: { eventoId, ... } } ├── POST /api/reports/preview # Anteprima (PNG/HTML) ``` #### Servizio Generazione PDF Useremo **QuestPDF** per la generazione: - Supporto nativo .NET - API fluent per layout complessi - Font personalizzati - Immagini - Paginazione automatica - Performance eccellenti ```csharp public class ReportGeneratorService { public byte[] GeneratePdf(ReportTemplate template, object dataContext) { var parsed = ParseTemplate(template.TemplateJson); var document = Document.Create(container => { container.Page(page => { page.Size(parsed.PageSize); page.Margin(parsed.Margins); if (parsed.Header != null) page.Header().Element(c => RenderSection(c, parsed.Header, dataContext)); page.Content().Element(c => RenderContent(c, parsed, dataContext)); if (parsed.Footer != null) page.Footer().Element(c => RenderSection(c, parsed.Footer, dataContext)); }); }); return document.GeneratePdf(); } } ``` ### 3. Frontend - Editor Visuale #### Componenti Principali ``` frontend/src/ ├── pages/ │ ├── ReportEditorPage.tsx # Editor principale │ └── ReportTemplatesPage.tsx # Lista template ├── components/ │ └── reportEditor/ │ ├── ReportEditor.tsx # Container principale │ ├── Canvas.tsx # Area di disegno (Fabric.js o Konva) │ ├── Toolbar.tsx # Barra strumenti (text, image, shape, table) │ ├── PropertiesPanel.tsx # Pannello proprietà elemento selezionato │ ├── DataBindingPanel.tsx # Pannello per mappare dati │ ├── LayersPanel.tsx # Gestione livelli/elementi │ ├── ResourcesPanel.tsx # Font e immagini disponibili │ ├── PageSettings.tsx # Impostazioni pagina │ ├── PreviewModal.tsx # Anteprima PDF │ └── elements/ │ ├── TextElement.tsx │ ├── ImageElement.tsx │ ├── ShapeElement.tsx │ ├── TableElement.tsx │ └── BarcodeElement.tsx ├── services/ │ └── reportService.ts └── types/ └── report.ts # Tipi TypeScript per APRT ``` #### Libreria Canvas **Fabric.js** è la scelta migliore: - Drag & drop nativo - Selezione multipla - Ridimensionamento con handle - Rotazione elementi - Serializzazione JSON - Supporto testo, immagini, forme - Griglia e snap - Undo/redo #### Flusso Editor ``` ┌─────────────────────────────────────────────────────────────────┐ │ Toolbar: [Text] [Image] [Shape] [Table] [Line] [Barcode] │ ├─────────────┬───────────────────────────────────┬───────────────┤ │ │ │ │ │ Layers │ CANVAS │ Properties │ │ Panel │ ┌─────────────────┐ │ Panel │ │ │ │ HEADER │ │ │ │ □ Logo │ │ [Logo] [Titolo]│ │ Position │ │ □ Titolo │ ├─────────────────┤ │ x: 10 y: 10 │ │ □ Data │ │ │ │ w: 200 h: 30 │ │ □ Tabella │ │ BODY │ │ │ │ □ Footer │ │ │ │ Style │ │ │ │ [Data Evento] │ │ Font: Roboto │ │ │ │ [Cliente] │ │ Size: 24 │ │ │ │ [Tabella] │ │ Color: #333 │ │ │ │ │ │ │ │ │ ├─────────────────┤ │ Data Binding │ │ │ │ FOOTER │ │ {{evento. │ │ │ │ [Pag X di Y] │ │ codice}} │ │ │ └─────────────────┘ │ │ │ │ │ │ ├─────────────┴───────────────────────────────────┴───────────────┤ │ Data Sources: [evento] [ospiti] [costi] [risorse] │ │ Available Fields: codice, dataEvento, cliente.ragioneSociale...│ └─────────────────────────────────────────────────────────────────┘ ``` ### 4. Implementazione Step-by-Step #### Fase 1: Backend Foundation 1. Creare entità `ReportTemplate`, `ReportFont`, `ReportImage` 2. Aggiornare DbContext e migrare database 3. Creare `ReportTemplatesController` con CRUD base 4. Creare `ReportResourcesController` per upload font/immagini 5. Installare e configurare QuestPDF 6. Creare `ReportGeneratorService` base #### Fase 2: Metalinguaggio Parser 1. Definire classi C# per il metalinguaggio APRT 2. Implementare parser JSON → oggetti 3. Implementare renderer elementi → QuestPDF 4. Gestire binding dati con espressioni {{campo}} 5. Implementare paginazione e sezioni ripetute #### Fase 3: Frontend Editor Base 1. Installare Fabric.js (`fabric`) 2. Creare pagina `ReportEditorPage` 3. Implementare `Canvas` con Fabric.js 4. Implementare `Toolbar` per aggiungere elementi 5. Implementare `PropertiesPanel` per editing proprietà 6. Implementare serializzazione canvas → APRT #### Fase 4: Data Binding 1. Creare `DataBindingPanel` con schema dati disponibili 2. Implementare drag-drop campi su elementi 3. Supportare espressioni {{campo.sottocampo}} 4. Implementare formattazione (currency, date, number) 5. Supportare espressioni condizionali #### Fase 5: Tabelle e Repeater 1. Implementare `TableElement` con colonne configurabili 2. Supportare data source array per righe ripetute 3. Implementare auto-height per tabelle 4. Gestire page break automatici #### Fase 6: Risorse e Upload 1. Implementare upload font custom 2. Implementare upload immagini 3. Creare libreria risorse condivise 4. Preview font e immagini #### Fase 7: Preview e Generazione 1. Implementare preview real-time (canvas → PNG) 2. Implementare generazione PDF finale 3. Download PDF 4. Stampa diretta #### Fase 8: Import/Export 1. Implementare export .aprt (JSON + risorse embedded base64) 2. Implementare import .aprt 3. Validazione template importati ### 5. Template Esempio: Scheda Evento Creeremo un template predefinito per la stampa eventi con: - Header con logo aziendale e titolo - Dati evento (codice, data, cliente, location) - Tabella ospiti con subtotali - Tabella costi aggiuntivi - Riepilogo totali - Note - Footer con paginazione ### 6. Dipendenze da Aggiungere **Backend (NuGet):** ```xml ``` **Frontend (npm):** ```json { "fabric": "^6.0.0", "file-saver": "^2.0.5", "@types/fabric": "^5.3.0" } ``` ### 7. Routes Frontend ```typescript // App.tsx - nuove routes } /> } /> } /> ``` ### 8. Stima Componenti | Componente | File | Complessità | |------------|------|-------------| | Entità + DbContext | 3 file | Bassa | | Controllers | 3 file | Media | | ReportGeneratorService | 1 file | Alta | | APRT Parser | 1 file | Media | | ReportEditorPage | 1 file | Alta | | Canvas (Fabric.js) | 1 file | Alta | | Toolbar | 1 file | Bassa | | PropertiesPanel | 1 file | Media | | DataBindingPanel | 1 file | Media | | LayersPanel | 1 file | Bassa | | Elementi (5 tipi) | 5 file | Media | | Services frontend | 1 file | Bassa | | Types | 1 file | Bassa | **Totale: ~20 file, complessità alta** --- ## Decisioni Architetturali 1. **QuestPDF** invece di iTextSharp (licenza più permissiva, API moderna) 2. **Fabric.js** invece di Konva (più features per editing) 3. **JSON** come metalinguaggio (leggibile, facile da parsare) 4. **Embedded resources** negli export (portabilità completa) 5. **Real-time preview** via canvas (no round-trip server) ## Note Implementative - Il canvas Fabric.js lavora in pixel, convertiremo in mm per la stampa - I font custom vanno registrati in QuestPDF all'avvio - Le immagini BLOB vanno convertite in base64 per Fabric.js - La paginazione è gestita lato server da QuestPDF - L'editor salva solo il JSON, la generazione PDF è on-demand