/** * ========================================================================= * PLATEFORME ECO-PANNEAU.FR - VERSION 1.0.0 * Moteur PDF (jsPDF) et Prévisualisation A1 * ========================================================================= */ const { NativeQRCode } = window; window.generateA1PDF = async (panneau) => { const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation: 'landscape', unit: 'mm', format: [841, 594] }); let fontLoaded = false; try { const loadFont = async (fontName, fontStyle, url) => { const cacheKey = `${fontName}-${fontStyle}`; let base64 = window.ECO_CONFIG.pdfFontsCache[cacheKey]; if (!base64) { const resp = await fetch(url); if (!resp.ok) { throw new Error("Erreur réseau lors du chargement de la police"); } const buffer = await resp.arrayBuffer(); let binary = ''; const bytes = new Uint8Array(buffer); for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]); } base64 = window.btoa(binary); window.ECO_CONFIG.pdfFontsCache[cacheKey] = base64; } const fileName = `${cacheKey}.ttf`; doc.addFileToVFS(fileName, base64); doc.addFont(fileName, fontName, fontStyle); }; await Promise.all([ loadFont("Roboto", "normal", "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Regular.ttf"), loadFont("Roboto", "bold", "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/fonts/Roboto/Roboto-Medium.ttf") ]); fontLoaded = true; } catch(e) { console.warn("Échec du chargement des polices UTF-8. Bascule sur Helvetica."); } const sanitizeForPDF = (str) => { if (!str) { return ''; } if (fontLoaded) { return str.toString().replace(/[\u1000-\uFFFF]/g, ''); } let s = str.toString() .replace(/[’‘`]/g, "'") .replace(/[“”«»]/g, '"') .replace(/[–—]/g, '-') .replace(/œ/g, 'oe') .replace(/Œ/g, 'Oe') .replace(/€/g, 'EUR'); return s.replace(/[^\x00-\xFF]/g, ""); }; const mainFont = fontLoaded ? "Roboto" : "helvetica"; // Fond blanc doc.setFillColor(255, 255, 255); doc.rect(0, 0, 841, 594, 'F'); const hexToRgb = (hex) => { let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex || '#059669'); if (result) { return { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) }; } return { r: 5, g: 150, b: 105 }; }; const themeRgb = hexToRgb(panneau.themeColor); // Bandeau supérieur couleur doc.setFillColor(themeRgb.r, themeRgb.g, themeRgb.b); doc.rect(0, 0, 841, 45, 'F'); // Bandeau inférieur (publicité ou marque blanche) if (!panneau.hasNoAds) { doc.setFillColor(15, 23, 42); doc.rect(0, 594 - 45, 841, 45, 'F'); doc.setFillColor(16, 185, 129); doc.rect(0, 594 - 50, 841, 5, 'F'); doc.setTextColor(255, 255, 255); doc.setFont(mainFont, "bold"); doc.setFontSize(35); doc.text("eco-panneau.fr | Professionnels du BTP : digitalisez vos obligations.", 420.5, 594 - 18, { align: 'center' }); } // Colonne de gauche (QR Code) const qrSize = 210; const qrX = 50; const qrY = 110; doc.setDrawColor(226, 232, 240); doc.setLineWidth(3); doc.setFillColor(255, 255, 255); doc.roundedRect(qrX - 15, qrY - 15, qrSize + 30, qrSize + 30, 5, 5, 'FD'); const qr = new window.QRCode({ content: `https://eco-panneau.fr/?scan=${panneau.id}`, padding: 0, width: 256, height: 256, ecl: "H" }); const modules = qr.qrcode.modules; const moduleCount = modules.length; const moduleSize = qrSize / moduleCount; doc.setFillColor(15, 23, 42); for (let row = 0; row < moduleCount; row++) { for (let col = 0; col < moduleCount; col++) { if (modules[row][col]) { doc.rect(qrX + col * moduleSize, qrY + row * moduleSize, moduleSize + 0.5, moduleSize + 0.5, 'F'); } } } doc.setTextColor(15, 23, 42); doc.setFontSize(40); doc.setFont(mainFont, "bold"); doc.text("Scannez pour les informations", qrX + qrSize / 2, qrY + qrSize + 50, { align: 'center' }); doc.text("légales et utiles", qrX + qrSize / 2, qrY + qrSize + 70, { align: 'center' }); // Séparateur vertical doc.setDrawColor(226, 232, 240); doc.setLineWidth(2); doc.line(340, 80, 340, panneau.hasNoAds ? 550 : 500); const drawAutoText = (text, x, y, maxW, maxH, startSize, fontName, fontStyle, color) => { const cleanText = sanitizeForPDF(text); if (!cleanText || cleanText.trim() === '') { return; } doc.setFont(fontName, fontStyle); doc.setTextColor(color[0], color[1], color[2]); let fontSize = startSize; doc.setFontSize(fontSize); let lines = doc.splitTextToSize(cleanText, maxW); let loopCount = 0; while (lines.length * (fontSize * 0.352) > maxH && fontSize > 20) { loopCount++; if (loopCount > 20) { break; } fontSize -= 2; doc.setFontSize(fontSize); lines = doc.splitTextToSize(cleanText, maxW); } if (lines.length * (fontSize * 0.352) > maxH) { const maxAllowedLines = Math.floor(maxH / (fontSize * 0.352)); lines = lines.slice(0, maxAllowedLines); if (lines.length > 0) { lines[lines.length - 1] += "..."; } } doc.text(lines, x, y); }; // Colonne de droite (Textes légaux) const textX = 380; const maxTextW = 420; doc.setTextColor(themeRgb.r, themeRgb.g, themeRgb.b); doc.setFontSize(45); doc.setFont(mainFont, "bold"); doc.text("Panneau d'affichage légal", textX, 110); drawAutoText(panneau.name, textX, 140, maxTextW, 120, 120, mainFont, "bold", [15, 23, 42]); drawAutoText(panneau.location, textX, 280, maxTextW, 60, 60, mainFont, "normal", [71, 85, 105]); let boxY = 380; if (panneau.maitreOuvrage || panneau.clientName) { doc.setFillColor(248, 250, 252); doc.setDrawColor(226, 232, 240); doc.roundedRect(textX, boxY, 420, 70, 5, 5, 'FD'); doc.setTextColor(100, 116, 139); doc.setFontSize(30); doc.setFont(mainFont, "bold"); doc.text("Maître d'ouvrage", textX + 15, boxY + 22); drawAutoText((panneau.maitreOuvrage || panneau.clientName), textX + 15, boxY + 48, 390, 30, 60, mainFont, "bold", [15, 23, 42]); boxY += 90; } if (panneau.permitNumber) { doc.setTextColor(148, 163, 184); doc.setFontSize(35); doc.setFont(mainFont, "bold"); doc.text("Permis N°", textX, boxY + 25); drawAutoText(panneau.permitNumber.toString(), textX + 100, boxY + 25, 320, 40, 70, mainFont, "bold", [15, 23, 42]); } return doc.output('blob'); }; const PrintA1View = ({ panneau }) => { if (!panneau) { return
Scannez pour les informations légales et utiles
{panneau.location}
Maître d'ouvrage
{panneau.maitreOuvrage || panneau.clientName}
Permis N°
{panneau.permitNumber}
Professionnels du BTP : digitalisez vos obligations sur eco-panneau.fr