import { Injectable, ComponentRef, ViewContainerRef, ApplicationRef, createComponent, EnvironmentInjector, inject, signal } from '@angular/core'; import html2canvas from 'html2canvas'; import jsPDF from 'jspdf'; import { Eintrittskarte } from '@infinimotion/model-frontend'; @Injectable({ providedIn: 'root', }) export class PdfService { private ticketsGreatedSignal = signal(0); private totalTicketsSignal = signal(0); readonly ticketsGreated = this.ticketsGreatedSignal.asReadonly(); readonly totalTickets = this.totalTicketsSignal.asReadonly(); appRef = inject(ApplicationRef); injector = inject(EnvironmentInjector); async genTicket(tickets: Eintrittskarte[], ticketComponent: any): Promise { if (tickets.length === 0) { throw new Error('Keine Tickets zum Generieren vorhanden'); } this.ticketsGreatedSignal.set(0) this.totalTicketsSignal.set(tickets.length) // Container für temporäres Rendering erstellen const container = document.createElement('div'); container.style.position = 'fixed'; container.style.left = '-9999px'; container.style.top = '0'; document.body.appendChild(container); const componentRefs: ComponentRef[] = []; try { // Ticket-Format: 210mm x 99mm const ticketWidthMM = 210; const ticketHeightMM = 99; const pdf = new jsPDF({ orientation: 'landscape', unit: 'mm', format: [ticketWidthMM, ticketHeightMM] }); for (let i = 0; i < tickets.length; i++) { const ticket = tickets[i]; // Komponente dynamisch erstellen const componentRef = createComponent(ticketComponent, { environmentInjector: this.injector }); // Ticket-Daten an die Komponente übergeben (componentRef.instance as any).ticket = ticket; // Komponente ins DOM einfügen this.appRef.attachView(componentRef.hostView); container.appendChild(componentRef.location.nativeElement); componentRefs.push(componentRef); // Change Detection triggern componentRef.changeDetectorRef.detectChanges(); // Kurz warten, damit alles gerendert ist await new Promise(requestAnimationFrame); // HTML zu Canvas konvertieren const canvas = await html2canvas(componentRef.location.nativeElement, { scale: 2, backgroundColor: '#ffffff', logging: false, useCORS: true }); const imgData = canvas.toDataURL('image/png'); if (i > 0) { pdf.addPage([ticketWidthMM, ticketHeightMM], 'landscape'); } // Bild ins PDF einfügen pdf.addImage(imgData, 'PNG', 0, 0, ticketWidthMM, ticketHeightMM); this.ticketsGreatedSignal.set(this.ticketsGreatedSignal() + 1) } const fileName = this.generateFileName(tickets); pdf.save(fileName); } catch (error) { console.error('Fehler beim Generieren des PDFs:', error); throw new Error('Das PDF konnte nicht erstellt werden. Bitte versuche es erneut.'); } finally { componentRefs.forEach(ref => { this.appRef.detachView(ref.hostView); ref.destroy(); }); document.body.removeChild(container); } } private generateFileName(tickets: Eintrittskarte[]): string { const orderCode = tickets[0].order.code; const timestamp = new Date().getTime(); return `Ticket_${orderCode}_${timestamp}.pdf`; } downloadTextFile(content: string, filename: string = 'textdatei.txt'): void { const blob = new Blob([content], { type: 'text/plain;charset=utf-8' }); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(url); } downloadJsonFile(data: any, filename: string = 'data.json'): void { const content = JSON.stringify(data, null, 2); const blob = new Blob([content], { type: 'application/json' }); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(url); } }