{"id":670,"date":"2026-01-28T11:23:51","date_gmt":"2026-01-28T07:23:51","guid":{"rendered":"https:\/\/www.arparun.re\/?page_id=670"},"modified":"2026-01-28T11:48:12","modified_gmt":"2026-01-28T07:48:12","slug":"photo-id","status":"publish","type":"page","link":"https:\/\/www.arparun.re\/index.php\/photo-id\/","title":{"rendered":"Photo ID"},"content":{"rendered":"\n<div class=\"wp-photo-id-generator\" style=\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 1000px; margin: 20px auto; padding: 0 15px;\">\n    <div style=\"background: white; border-radius: 20px; padding: 30px; box-shadow: 0 5px 25px rgba(0,0,0,0.08);\">\n        <h1 style=\"text-align: center; color: #2c3e50; margin-bottom: 10px; font-size: 28px;\">\ud83d\udcf8 G\u00e9n\u00e9rateur de Photos d&rsquo;Identit\u00e9<\/h1>\n        <p style=\"text-align: center; color: #5a6b82; margin-bottom: 25px; font-size: 16px;\">Cr\u00e9ez vos photos d&rsquo;identit\u00e9 en 30 secondes &#8211; 100% gratuit, aucune donn\u00e9e sauvegard\u00e9e<\/p>\n\n        <!-- Zone d'upload -->\n        <div class=\"upload-zone\" style=\"border: 3px dashed #4361ee; border-radius: 15px; padding: 40px 20px; text-align: center; cursor: pointer; transition: all 0.3s; background: #f8fbff; margin-bottom: 25px;\">\n            <div style=\"font-size: 50px; margin-bottom: 15px;\">\ud83d\udcf7<\/div>\n            <h3 style=\"margin-bottom: 10px;\">Glissez-d\u00e9posez votre photo ici<\/h3>\n            <p style=\"color: #4b5563; margin-top: 8px; font-size: 15px;\">ou cliquez pour parcourir (JPG, PNG, WEBP &#8211; max 10 Mo)<\/p>\n            <input type=\"file\" id=\"wp_photo_upload\" accept=\"image\/*\" style=\"display: none;\">\n        <\/div>\n\n        <!-- Messages de statut -->\n        <div id=\"wp_photo_status\" style=\"text-align: center; margin: 15px 0; padding: 12px 20px; border-radius: 12px; font-weight: 600; display: none; max-width: 600px; margin-left: auto; margin-right: auto;\"><\/div>\n\n        <!-- S\u00e9lecteur de formats -->\n        <div id=\"wp_size_selector\" style=\"display: none; background: #f8fbff; border-radius: 15px; padding: 25px; margin: 25px 0; border: 1px solid #eef4ff;\">\n            <h2 style=\"text-align: center; margin-bottom: 20px; color: #2c3e50; font-size: 22px;\">\u2705 Photo charg\u00e9e ! Choisissez votre format :<\/h2>\n            <div style=\"display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-bottom: 20px;\">\n                <div class=\"size-btn selected\" data-size=\"3x4\" style=\"background: white; border: 2px solid #4361ee; border-radius: 12px; padding: 15px; text-align: center; cursor: pointer; min-width: 110px; box-shadow: 0 3px 10px rgba(67,97,238,0.2); font-weight: 600; color: #3f37c9;\">\n                    Passeport<br><span style=\"font-weight: 700; font-size: 18px;\">3 \u00d7 4 cm<\/span><br><span style=\"font-size: 12px; color: #64748b;\">354 \u00d7 472 px<\/span>\n                <\/div>\n                <div class=\"size-btn\" data-size=\"2x3\" style=\"background: white; border: 2px solid #cbd5e1; border-radius: 12px; padding: 15px; text-align: center; cursor: pointer; min-width: 110px; box-shadow: 0 2px 6px rgba(0,0,0,0.05); transition: all 0.2s;\">\n                    Visa US<br><span style=\"font-weight: 700; font-size: 18px;\">2 \u00d7 3 cm<\/span><br><span style=\"font-size: 12px; color: #64748b;\">236 \u00d7 354 px<\/span>\n                <\/div>\n                <div class=\"size-btn\" data-size=\"4x6\" style=\"background: white; border: 2px solid #cbd5e1; border-radius: 12px; padding: 15px; text-align: center; cursor: pointer; min-width: 110px; box-shadow: 0 2px 6px rgba(0,0,0,0.05); transition: all 0.2s;\">\n                    Permis<br><span style=\"font-weight: 700; font-size: 18px;\">4 \u00d7 6 cm<\/span><br><span style=\"font-size: 12px; color: #64748b;\">472 \u00d7 709 px<\/span>\n                <\/div>\n                <div class=\"size-btn\" data-size=\"id_card\" style=\"background: white; border: 2px solid #cbd5e1; border-radius: 12px; padding: 15px; text-align: center; cursor: pointer; min-width: 110px; box-shadow: 0 2px 6px rgba(0,0,0,0.05); transition: all 0.2s;\">\n                    CNI<br><span style=\"font-weight: 700; font-size: 18px;\">3.5 \u00d7 4.5 cm<\/span><br><span style=\"font-size: 12px; color: #64748b;\">413 \u00d7 531 px<\/span>\n                <\/div>\n            <\/div>\n            <div style=\"text-align: center; padding-top: 15px; border-top: 1px dashed #cbd5e1; font-weight: 500; color: #4b5563;\">\n                \u2139\ufe0f Cliquez sur un format pour g\u00e9n\u00e9rer imm\u00e9diatement votre photo\n            <\/div>\n        <\/div>\n\n        <!-- Canvas container -->\n        <div id=\"wp_canvas_container\" style=\"display: none; display: flex; flex-wrap: wrap; gap: 25px; justify-content: center; margin-top: 25px; padding: 15px 0;\">\n            <div style=\"text-align: center; background: #f8fbff; border-radius: 12px; padding: 15px; min-width: 250px; flex: 1; max-width: 450px;\">\n                <h3 style=\"margin-bottom: 12px; color: #2c3e50; font-size: 18px;\">Votre photo originale<\/h3>\n                <canvas id=\"wp_original_canvas\" width=\"400\" height=\"300\" style=\"border: 2px solid #d1d9e6; border-radius: 8px; background: white; max-width: 100%; display: block; margin: 0 auto; cursor: crosshair; touch-action: none;\"><\/canvas>\n                <p style=\"font-size: 13px; color: #64748b; margin-top: 8px; padding: 8px; background: #eef7ff; border-radius: 8px; margin-top: 10px;\">\n                    <strong>\ud83d\udca1 Astuce :<\/strong> Cliquez + glissez sur votre visage pour ajuster la s\u00e9lection\n                <\/p>\n            <\/div>\n            <div style=\"text-align: center; background: #f8fbff; border-radius: 12px; padding: 15px; min-width: 250px; flex: 1; max-width: 450px;\">\n                <h3 style=\"margin-bottom: 12px; color: #2c3e50; font-size: 18px;\">R\u00e9sultat <span id=\"wp_result_size\" style=\"background: #eef4ff; color: #3f37c9; padding: 2px 10px; border-radius: 20px; font-weight: 600; margin-left: 8px;\">3 \u00d7 4 cm<\/span><\/h3>\n                <canvas id=\"wp_id_canvas\" width=\"354\" height=\"472\" style=\"border: 2px solid #4361ee; border-radius: 8px; background: white; max-width: 100%; display: block; margin: 0 auto; box-shadow: 0 4px 15px rgba(67,97,238,0.15);\"><\/canvas>\n            <\/div>\n        <\/div>\n\n        <!-- Boutons de contr\u00f4le -->\n        <div id=\"wp_controls\" style=\"display: none; display: flex; gap: 15px; justify-content: center; margin-top: 25px; flex-wrap: wrap;\">\n            <button id=\"wp_download_btn\" style=\"background: linear-gradient(135deg, #4361ee 0%, #3f37c9 100%); color: white; border: none; padding: 14px 32px; border-radius: 50px; cursor: pointer; font-size: 17px; font-weight: 600; box-shadow: 0 4px 15px rgba(67,97,238,0.4); display: flex; align-items: center; gap: 10px; min-width: 200px; transition: all 0.3s;\">\n                \u2b07\ufe0f T\u00e9l\u00e9charger (300 DPI)\n            <\/button>\n            <button id=\"wp_reset_btn\" style=\"background: linear-gradient(135deg, #f72585 0%, #b5179e 100%); color: white; border: none; padding: 14px 32px; border-radius: 50px; cursor: pointer; font-size: 17px; font-weight: 600; box-shadow: 0 4px 15px rgba(247,37,133,0.35); display: flex; align-items: center; gap: 10px; min-width: 200px; transition: all 0.3s;\">\n                \ud83d\udd04 Nouvelle photo\n            <\/button>\n        <\/div>\n\n        <!-- Conseils -->\n        <div style=\"background: #eef7ff; border-left: 4px solid #4361ee; padding: 18px; margin-top: 25px; border-radius: 0 10px 10px 0; font-size: 15px;\">\n            <p style=\"margin: 6px 0; display: flex; align-items: flex-start;\">\n                <span style=\"min-width: 130px; font-weight: 600; color: #2c3e50;\">\u2705 Formats officiels<\/span> \n                Passeport, CNI, Visa, Permis de conduire (300 DPI qualit\u00e9 impression)\n            <\/p>\n            <p style=\"margin: 6px 0; display: flex; align-items: flex-start;\">\n                <span style=\"min-width: 130px; font-weight: 600; color: #2c3e50;\">\ud83d\udd12 100% s\u00e9curis\u00e9<\/span> \n                Aucun fichier n&rsquo;est sauvegard\u00e9 &#8211; traitement 100% dans votre navigateur\n            <\/p>\n            <p style=\"margin: 6px 0; display: flex; align-items: flex-start;\">\n                <span style=\"min-width: 130px; font-weight: 600; color: #2c3e50;\">\ud83d\udcf1 Mobile &#038; Desktop<\/span> \n                Interface tactile optimis\u00e9e pour t\u00e9l\u00e9phone et ordinateur\n            <\/p>\n        <\/div>\n    <\/div>\n<\/div>\n\n<script>\ndocument.addEventListener('DOMContentLoaded', function() {\n    \/\/ \u00c9L\u00c9MENTS CRITIQUES - V\u00e9rification stricte\n    const uploadZone = document.querySelector('.wp-photo-id-generator .upload-zone');\n    const fileInput = document.getElementById('wp_photo_upload');\n    const statusEl = document.getElementById('wp_photo_status');\n    const sizeSelector = document.getElementById('wp_size_selector');\n    const canvasContainer = document.getElementById('wp_canvas_container');\n    const controls = document.getElementById('wp_controls');\n    const originalCanvas = document.getElementById('wp_original_canvas');\n    const idCanvas = document.getElementById('wp_id_canvas');\n    const sizeButtons = document.querySelectorAll('.size-btn');\n    const resultSizeEl = document.getElementById('wp_result_size');\n    const downloadBtn = document.getElementById('wp_download_btn');\n    const resetBtn = document.getElementById('wp_reset_btn');\n    \n    \/\/ V\u00e9rification de s\u00e9curit\u00e9\n    if (!originalCanvas || !idCanvas) {\n        console.error('Canvas manquants - g\u00e9n\u00e9rateur d\u00e9sactiv\u00e9');\n        if (statusEl) {\n            statusEl.innerHTML = '\u26a0\ufe0f <strong>Erreur technique<\/strong><br>L\\'outil de g\u00e9n\u00e9ration de photos ne peut pas d\u00e9marrer. Veuillez recharger la page.';\n            statusEl.style.cssText = 'display: block; background: #fff2f2; color: #c52828; border: 1px solid #ffc9c9; padding: 15px; border-radius: 10px; margin-top: 15px; text-align: center;';\n        }\n        return;\n    }\n\n    \/\/ CONSTANTES\n    const CM_TO_PIXELS = 118.11; \/\/ 300 DPI\n    const FORMATS = {\n        \"2x3\": { w: 2, h: 3, name: \"Visa US\" },\n        \"3x4\": { w: 3, h: 4, name: \"Passeport\" },\n        \"4x6\": { w: 4, h: 6, name: \"Permis\" },\n        \"id_card\": { w: 3.5, h: 4.5, name: \"Carte d'identit\u00e9\" }\n    };\n    \n    \/\/ \u00c9TAT\n    let currentImage = null;\n    let selection = null;\n    let isDrawing = false;\n    let startX = 0, startY = 0;\n    let currentFormat = \"3x4\";\n    const ctxOrig = originalCanvas.getContext('2d');\n    const ctxId = idCanvas.getContext('2d');\n    \n    \/\/ FONCTION CRITIQUE : Conversion coordonn\u00e9es canvas (corrige le d\u00e9calage desktop + mobile)\n    function getCanvasCoords(canvas, clientX, clientY) {\n        const rect = canvas.getBoundingClientRect();\n        const scaleX = canvas.width \/ rect.width;\n        const scaleY = canvas.height \/ rect.height;\n        return {\n            x: (clientX - rect.left) * scaleX,\n            y: (clientY - rect.top) * scaleY\n        };\n    }\n\n    \/\/ FONCTIONS UTILITAIRES\n    function showStatus(message, type) {\n        if (!statusEl) return;\n        statusEl.textContent = message;\n        statusEl.style.display = 'block';\n        \n        switch(type) {\n            case 'success':\n                statusEl.style.cssText = 'background: #e6f7ee; color: #1e7e34; border: 1px solid #a7e8c5; border-radius: 12px; padding: 12px 20px; font-weight: 600; display: block;';\n                setTimeout(() => { if (statusEl.textContent === message) statusEl.style.display = 'none'; }, 5000);\n                break;\n            case 'error':\n                statusEl.style.cssText = 'background: #fff2f2; color: #c52828; border: 1px solid #ffc9c9; border-radius: 12px; padding: 12px 20px; font-weight: 600; display: block;';\n                break;\n            default:\n                statusEl.style.cssText = 'background: #eef7ff; color: #2c5282; border: 1px solid #bee3f8; border-radius: 12px; padding: 12px 20px; font-weight: 600; display: block;';\n        }\n    }\n\n    function renderOriginal(img) {\n        \/\/ Calcul des dimensions adapt\u00e9es SANS alt\u00e9rer le ratio\n        const containerWidth = Math.min(450, originalCanvas.parentElement.clientWidth - 30);\n        const scale = Math.min(containerWidth \/ img.width, 350 \/ img.height, 1);\n        \n        \/\/ D\u00e9finir la taille du buffer de dessin (IMPORTANT pour les coordonn\u00e9es)\n        originalCanvas.width = img.width * scale;\n        originalCanvas.height = img.height * scale;\n        \n        \/\/ Dessiner l'image\n        ctxOrig.clearRect(0, 0, originalCanvas.width, originalCanvas.height);\n        ctxOrig.drawImage(img, 0, 0, originalCanvas.width, originalCanvas.height);\n        selection = null;\n    }\n\n    function updateResult() {\n        if (!selection || !currentImage || !ctxId) {\n            console.warn('Impossible de g\u00e9n\u00e9rer le r\u00e9sultat: selection=', selection, 'currentImage=', !!currentImage);\n            return;\n        }\n        \n        try {\n            const format = FORMATS[currentFormat];\n            const targetW = Math.round(format.w * CM_TO_PIXELS);\n            const targetH = Math.round(format.h * CM_TO_PIXELS);\n            \n            \/\/ Redimensionner le canvas de r\u00e9sultat\n            idCanvas.width = targetW;\n            idCanvas.height = targetH;\n            \n            \/\/ Fond blanc\n            ctxId.fillStyle = 'white';\n            ctxId.fillRect(0, 0, targetW, targetH);\n            \n            \/\/ Calculer le recadrage pour conserver le ratio\n            const targetRatio = targetW \/ targetH;\n            const selRatio = selection.width \/ selection.height;\n            let srcX, srcY, srcW, srcH;\n            \n            if (selRatio > targetRatio) {\n                \/\/ Trop large \u2192 recadrer les c\u00f4t\u00e9s\n                const newW = selection.height * targetRatio;\n                srcX = selection.x + (selection.width - newW) \/ 2;\n                srcY = selection.y;\n                srcW = newW;\n                srcH = selection.height;\n            } else {\n                \/\/ Trop haut \u2192 recadrer haut\/bas\n                const newH = selection.width \/ targetRatio;\n                srcX = selection.x;\n                srcY = selection.y + (selection.height - newH) \/ 2;\n                srcW = selection.width;\n                srcH = newH;\n            }\n            \n            \/\/ Dessiner l'image recadr\u00e9e\n            ctxId.drawImage(currentImage, srcX, srcY, srcW, srcH, 0, 0, targetW, targetH);\n            if (resultSizeEl) resultSizeEl.textContent = `${format.w} \u00d7 ${format.h} cm`;\n            \n        } catch (err) {\n            console.error('Erreur dans updateResult:', err);\n            showStatus('\u274c Erreur lors de la g\u00e9n\u00e9ration de la photo', 'error');\n        }\n    }\n\n    \/\/ \u00c9V\u00c9NEMENTS UPLOAD\n    if (uploadZone) {\n        uploadZone.addEventListener('click', () => fileInput?.click());\n        uploadZone.addEventListener('dragover', e => {\n            e.preventDefault();\n            uploadZone.style.borderColor = '#3f37c9';\n            uploadZone.style.backgroundColor = '#eef4ff';\n        });\n        uploadZone.addEventListener('dragleave', () => {\n            uploadZone.style.borderColor = '#4361ee';\n            uploadZone.style.backgroundColor = '#f8fbff';\n        });\n        uploadZone.addEventListener('drop', e => {\n            e.preventDefault();\n            uploadZone.style.borderColor = '#4361ee';\n            uploadZone.style.backgroundColor = '#f8fbff';\n            if (e.dataTransfer?.files?.length) handleFile(e.dataTransfer.files[0]);\n        });\n    }\n    \n    fileInput?.addEventListener('change', e => {\n        if (e.target.files.length) handleFile(e.target.files[0]);\n    });\n\n    function handleFile(file) {\n        if (file.size > 10 * 1024 * 1024) {\n            showStatus('\u274c Fichier trop volumineux (max 10 Mo)', 'error');\n            return;\n        }\n        if (!file.type.match('image.*')) {\n            showStatus('\u274c Format non support\u00e9 (JPG\/PNG uniquement)', 'error');\n            return;\n        }\n        \n        const reader = new FileReader();\n        reader.onload = e => {\n            const img = new Image();\n            img.onload = () => {\n                currentImage = img;\n                renderOriginal(img);\n                \n                \/\/ AFFICHER LES \u00c9L\u00c9MENTS\n                if (sizeSelector) sizeSelector.style.display = 'block';\n                if (canvasContainer) canvasContainer.style.display = 'flex';\n                if (controls) controls.style.display = 'none';\n                \n                showStatus('\u2705 Photo charg\u00e9e ! S\u00e9lectionnez un format ou dessinez sur votre visage', 'success');\n                \n                \/\/ Scroll vers les formats\n                if (sizeSelector) sizeSelector.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n            };\n            img.onerror = () => showStatus('\u274c Erreur: image corrompue ou format non support\u00e9', 'error');\n            img.src = e.target.result;\n        };\n        reader.readAsDataURL(file);\n    }\n\n    \/\/ \u00c9V\u00c9NEMENTS CANVAS - CORRIG\u00c9S POUR TOUS LES APPAREILS\n    function setupCanvasEvents() {\n        \/\/ SOURIS\n        originalCanvas.addEventListener('mousedown', e => {\n            if (!currentImage || isDrawing) return;\n            e.preventDefault();\n            isDrawing = true;\n            const coords = getCanvasCoords(originalCanvas, e.clientX, e.clientY);\n            startX = coords.x;\n            startY = coords.y;\n        });\n        \n        originalCanvas.addEventListener('mousemove', e => {\n            if (!isDrawing || !currentImage) return;\n            e.preventDefault();\n            const coords = getCanvasCoords(originalCanvas, e.clientX, e.clientY);\n            redrawSelection(coords.x, coords.y);\n        });\n        \n        originalCanvas.addEventListener('mouseup', e => {\n            if (!isDrawing || !currentImage) return;\n            e.preventDefault();\n            isDrawing = false;\n            const coords = getCanvasCoords(originalCanvas, e.clientX, e.clientY);\n            finalizeSelection(coords.x, coords.y);\n        });\n        \n        originalCanvas.addEventListener('mouseleave', () => {\n            if (isDrawing) {\n                isDrawing = false;\n                if (currentImage) renderOriginal(currentImage);\n            }\n        });\n        \n        \/\/ TACTILE (CORRIG\u00c9 POUR L'APER\u00c7U)\n        originalCanvas.addEventListener('touchstart', e => {\n            if (!currentImage || isDrawing || e.touches.length > 1) return;\n            e.preventDefault();\n            isDrawing = true;\n            const touch = e.touches[0];\n            const coords = getCanvasCoords(originalCanvas, touch.clientX, touch.clientY);\n            startX = coords.x;\n            startY = coords.y;\n        }, { passive: false });\n        \n        originalCanvas.addEventListener('touchmove', e => {\n            if (!isDrawing || !currentImage || e.touches.length > 1) return;\n            e.preventDefault();\n            const touch = e.touches[0];\n            const coords = getCanvasCoords(originalCanvas, touch.clientX, touch.clientY);\n            redrawSelection(coords.x, coords.y);\n        }, { passive: false });\n        \n        originalCanvas.addEventListener('touchend', e => {\n            if (!isDrawing || !currentImage) return;\n            e.preventDefault();\n            isDrawing = false;\n            \/\/ Utiliser les derni\u00e8res coordonn\u00e9es connues (\u00e9vite les erreurs)\n            const rect = originalCanvas.getBoundingClientRect();\n            const touch = e.changedTouches[0] || { clientX: rect.left + startX, clientY: rect.top + startY };\n            const coords = getCanvasCoords(originalCanvas, touch.clientX, touch.clientY);\n            finalizeSelection(coords.x, coords.y);\n        }, { passive: false });\n    }\n    \n    function redrawSelection(currentX, currentY) {\n        const x = Math.min(startX, currentX);\n        const y = Math.min(startY, currentY);\n        const w = Math.abs(currentX - startX);\n        const h = Math.abs(currentY - startY);\n        \n        renderOriginal(currentImage);\n        ctxOrig.strokeStyle = '#ff0000';\n        ctxOrig.lineWidth = 2;\n        ctxOrig.setLineDash([5, 5]);\n        ctxOrig.strokeRect(x, y, w, h);\n        ctxOrig.setLineDash([]);\n    }\n    \n    function finalizeSelection(endX, endY) {\n        const x = Math.min(startX, endX);\n        const y = Math.min(startY, endY);\n        const w = Math.abs(endX - startX);\n        const h = Math.abs(endY - startY);\n        \n        if (w < 15 || h < 15) {\n            renderOriginal(currentImage);\n            showStatus('\u26a0\ufe0f S\u00e9lection trop petite (minimum 15px)', 'error');\n            return;\n        }\n        \n        \/\/ Conversion en coordonn\u00e9es image originale\n        const scaleX = currentImage.width \/ originalCanvas.width;\n        const scaleY = currentImage.height \/ originalCanvas.height;\n        \n        selection = {\n            x: x * scaleX,\n            y: y * scaleY,\n            width: w * scaleX,\n            height: h * scaleY\n        };\n        \n        \/\/ G\u00e9n\u00e9rer l'aper\u00e7u IMM\u00c9DIATEMENT (correction mobile)\n        updateResult();\n        \n        \/\/ Afficher les contr\u00f4les\n        if (controls) controls.style.display = 'flex';\n        showStatus(`\u2705 S\u00e9lection valid\u00e9e ! Format: ${FORMATS[currentFormat].name}`, 'success');\n    }\n\n    \/\/ GESTION FORMATS\n    sizeButtons.forEach(btn => {\n        btn.addEventListener('click', () => {\n            \/\/ Mise \u00e0 jour UI\n            sizeButtons.forEach(b => {\n                b.style.cssText = b === btn ? \n                    'background: white; border: 2px solid #4361ee; border-radius: 12px; padding: 15px; text-align: center; cursor: pointer; min-width: 110px; box-shadow: 0 3px 10px rgba(67,97,238,0.2); font-weight: 600; color: #3f37c9;' :\n                    'background: white; border: 2px solid #cbd5e1; border-radius: 12px; padding: 15px; text-align: center; cursor: pointer; min-width: 110px; box-shadow: 0 2px 6px rgba(0,0,0,0.05); transition: all 0.2s;';\n            });\n            \n            \/\/ Mise \u00e0 jour logique\n            currentFormat = btn.dataset.size;\n            if (selection) updateResult();\n            showStatus(`\u2705 Format ${FORMATS[currentFormat].name} s\u00e9lectionn\u00e9`, 'success');\n        });\n    });\n\n    \/\/ T\u00c9L\u00c9CHARGEMENT\n    downloadBtn?.addEventListener('click', () => {\n        if (!selection) {\n            showStatus('\u26a0\ufe0f S\u00e9lectionnez d\\'abord une zone sur votre photo', 'error');\n            return;\n        }\n        const format = FORMATS[currentFormat];\n        const link = document.createElement('a');\n        link.download = `photo_identite_${format.name.replace(\/[^a-z0-9]\/gi, '_').toLowerCase()}_${format.w}x${format.h}cm.png`;\n        link.href = idCanvas.toDataURL('image\/png');\n        document.body.appendChild(link);\n        link.click();\n        document.body.removeChild(link);\n        showStatus(`\u2705 T\u00e9l\u00e9chargement termin\u00e9 (${format.name})`, 'success');\n    });\n\n    \/\/ R\u00c9INITIALISATION\n    resetBtn?.addEventListener('click', () => {\n        currentImage = null;\n        selection = null;\n        if (fileInput) fileInput.value = '';\n        \n        \/\/ R\u00e9initialiser l'UI\n        if (sizeSelector) sizeSelector.style.display = 'none';\n        if (canvasContainer) canvasContainer.style.display = 'none';\n        if (controls) controls.style.display = 'none';\n        if (statusEl) statusEl.style.display = 'none';\n        \n        \/\/ Effacer les canvases\n        if (ctxOrig && originalCanvas) {\n            ctxOrig.clearRect(0, 0, originalCanvas.width, originalCanvas.height);\n            ctxOrig.fillStyle = '#f1f5f9';\n            ctxOrig.fillRect(0, 0, originalCanvas.width, originalCanvas.height);\n            ctxOrig.fillStyle = '#64748b';\n            ctxOrig.font = '14px sans-serif';\n            ctxOrig.textAlign = 'center';\n            ctxOrig.fillText('Votre photo appara\u00eetra ici', originalCanvas.width\/2, originalCanvas.height\/2);\n        }\n        \n        if (ctxId && idCanvas) {\n            ctxId.fillStyle = 'white';\n            ctxId.fillRect(0, 0, idCanvas.width, idCanvas.height);\n            ctxId.fillStyle = '#4361ee';\n            ctxId.font = 'bold 16px sans-serif';\n            ctxId.textAlign = 'center';\n            ctxId.fillText('R\u00e9sultat', idCanvas.width\/2, idCanvas.height\/2 - 20);\n            ctxId.font = '14px sans-serif';\n            ctxId.fillStyle = '#64748b';\n            ctxId.fillText('S\u00e9lectionnez un format', idCanvas.width\/2, idCanvas.height\/2 + 5);\n            ctxId.fillText('ou dessinez sur l\\'originale', idCanvas.width\/2, idCanvas.height\/2 + 25);\n        }\n        \n        \/\/ R\u00e9initialiser les boutons\n        sizeButtons.forEach(btn => {\n            if (btn.dataset.size === '3x4') {\n                btn.style.cssText = 'background: white; border: 2px solid #4361ee; border-radius: 12px; padding: 15px; text-align: center; cursor: pointer; min-width: 110px; box-shadow: 0 3px 10px rgba(67,97,238,0.2); font-weight: 600; color: #3f37c9;';\n            } else {\n                btn.style.cssText = 'background: white; border: 2px solid #cbd5e1; border-radius: 12px; padding: 15px; text-align: center; cursor: pointer; min-width: 110px; box-shadow: 0 2px 6px rgba(0,0,0,0.05); transition: all 0.2s;';\n            }\n        });\n        \n        currentFormat = '3x4';\n        if (resultSizeEl) resultSizeEl.textContent = '3 \u00d7 4 cm';\n        showStatus('\u2705 Pr\u00eat pour une nouvelle photo !', 'info');\n    });\n\n    \/\/ INITIALISATION\n    setupCanvasEvents();\n    showStatus('\u2139\ufe0f Cliquez ou glissez une photo pour commencer', 'info');\n    \n    \/\/ Placeholder canvas original\n    if (ctxOrig) {\n        ctxOrig.fillStyle = '#f1f5f9';\n        ctxOrig.fillRect(0, 0, originalCanvas.width, originalCanvas.height);\n        ctxOrig.fillStyle = '#64748b';\n        ctxOrig.font = '14px sans-serif';\n        ctxOrig.textAlign = 'center';\n        ctxOrig.fillText('Votre photo appara\u00eetra ici', originalCanvas.width\/2, originalCanvas.height\/2);\n    }\n    \n    \/\/ Placeholder canvas r\u00e9sultat\n    if (ctxId) {\n        ctxId.fillStyle = '#f8fbff';\n        ctxId.fillRect(0, 0, idCanvas.width, idCanvas.height);\n        ctxId.fillStyle = '#4361ee';\n        ctxId.font = 'bold 16px sans-serif';\n        ctxId.textAlign = 'center';\n        ctxId.fillText('R\u00e9sultat', idCanvas.width\/2, idCanvas.height\/2 - 20);\n        ctxId.font = '14px sans-serif';\n        ctxId.fillStyle = '#64748b';\n        ctxId.fillText('S\u00e9lectionnez un format', idCanvas.width\/2, idCanvas.height\/2 + 5);\n        ctxId.fillText('ou dessinez sur l\\'originale', idCanvas.width\/2, idCanvas.height\/2 + 25);\n    }\n});\n<\/script>\n\n<style>\n\/* CORRECTIONS CRITIQUES POUR WORDPRESS *\/\n.wp-photo-id-generator button:hover {\n    transform: translateY(-2px) !important;\n    box-shadow: 0 6px 20px rgba(0,0,0,0.15) !important;\n}\n\n.wp-photo-id-generator .size-btn:hover:not(.selected) {\n    transform: translateY(-2px) !important;\n    border-color: #4361ee !important;\n    box-shadow: 0 4px 12px rgba(67,97,238,0.25) !important;\n}\n\n\/* FIX CANVAS RESPONSIVE SANS D\u00c9CALAGE *\/\n.wp-photo-id-generator canvas {\n    display: block !important;\n    margin: 0 auto !important;\n    max-width: 100% !important;\n    height: auto !important;\n}\n\n\/* Responsive mobile *\/\n@media (max-width: 768px) {\n    .wp-photo-id-generator .upload-zone {\n        padding: 30px 15px !important;\n    }\n    .wp-photo-id-generator h1 {\n        font-size: 24px !important;\n    }\n    .wp-photo-id-generator [style*=\"flex\"] {\n        flex-direction: column !important;\n        align-items: center !important;\n    }\n    .wp-photo-id-generator button {\n        width: 100% !important;\n        max-width: 350px !important;\n        padding: 16px !important;\n        font-size: 18px !important;\n    }\n    .wp-photo-id-generator .size-btn {\n        width: 100% !important;\n        max-width: 300px !important;\n        margin-bottom: 10px !important;\n    }\n    \/* Fix canvas height mobile *\/\n    #wp_original_canvas, #wp_id_canvas {\n        max-height: 300px !important;\n        width: 100% !important;\n        height: auto !important;\n    }\n}\n<\/style>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Si l&rsquo;application vous \u00e0 \u00e9t\u00e9 utile, Offrer moi une bierre !! C&rsquo;est sympa et \u00e7a m&rsquo;aide \u00e0 maintenir les serveurs ! \ud83d\ude00<\/p>\n\n\n\n<div style=\"display: flex; justify-content: center; align-items: center;\">\n\n\n<script async\n  src=\"https:\/\/js.stripe.com\/v3\/buy-button.js\">\n<\/script>\n\n<stripe-buy-button\n  buy-button-id=\"buy_btn_1RXGoOAOEp7xPNKdvvgCbt2V\"\n  publishable-key=\"pk_live_51QQXMmAOEp7xPNKdUhcTaBFsUvieXz97t7VbgmEfSC8LzRxoqqoJOM4rrwpFnVsRETCnWpXBED5oqyqCCti50YC600OrlOgLNg\"\n>\n<\/stripe-buy-button>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>\ud83d\udcf8 G\u00e9n\u00e9rateur de Photos d&rsquo;Identit\u00e9 Cr\u00e9ez vos photos d&rsquo;identit\u00e9 en 30 secondes &#8211; 100% gratuit, aucune donn\u00e9e sauvegard\u00e9e \ud83d\udcf7 Glissez-d\u00e9posez votre photo ici ou cliquez pour parcourir (JPG, PNG, WEBP &#8211; max 10 Mo) \u2705 Photo charg\u00e9e ! Choisissez votre format : Passeport3 \u00d7 4 cm354 \u00d7 472 px Visa<a href=\"https:\/\/www.arparun.re\/index.php\/photo-id\/\" class=\"read-more\">Read More &#8230;<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-670","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Photo ID - arparun - Informatique Reunion<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.arparun.re\/index.php\/photo-id\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Photo ID - arparun - Informatique Reunion\" \/>\n<meta property=\"og:description\" content=\"\ud83d\udcf8 G\u00e9n\u00e9rateur de Photos d&rsquo;Identit\u00e9 Cr\u00e9ez vos photos d&rsquo;identit\u00e9 en 30 secondes &#8211; 100% gratuit, aucune donn\u00e9e sauvegard\u00e9e \ud83d\udcf7 Glissez-d\u00e9posez votre photo ici ou cliquez pour parcourir (JPG, PNG, WEBP &#8211; max 10 Mo) \u2705 Photo charg\u00e9e ! Choisissez votre format : Passeport3 \u00d7 4 cm354 \u00d7 472 px VisaRead More ...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.arparun.re\/index.php\/photo-id\/\" \/>\n<meta property=\"og:site_name\" content=\"arparun - Informatique Reunion\" \/>\n<meta property=\"article:modified_time\" content=\"2026-01-28T07:48:12+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.arparun.re\/index.php\/photo-id\/\",\"url\":\"https:\/\/www.arparun.re\/index.php\/photo-id\/\",\"name\":\"Photo ID - arparun - Informatique Reunion\",\"isPartOf\":{\"@id\":\"https:\/\/www.arparun.re\/#website\"},\"datePublished\":\"2026-01-28T07:23:51+00:00\",\"dateModified\":\"2026-01-28T07:48:12+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.arparun.re\/index.php\/photo-id\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.arparun.re\/index.php\/photo-id\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.arparun.re\/index.php\/photo-id\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\/\/www.arparun.re\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Photo ID\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.arparun.re\/#website\",\"url\":\"https:\/\/www.arparun.re\/\",\"name\":\"arparun - Informatique Reunion\",\"description\":\"Syst\u00e8mes, R\u00e9seaux et S\u00e9curit\u00e9\",\"publisher\":{\"@id\":\"https:\/\/www.arparun.re\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.arparun.re\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.arparun.re\/#organization\",\"name\":\"Arparun\",\"url\":\"https:\/\/www.arparun.re\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\/\/www.arparun.re\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.arparun.re\/wp-content\/uploads\/2021\/02\/cropped-site.png\",\"contentUrl\":\"https:\/\/www.arparun.re\/wp-content\/uploads\/2021\/02\/cropped-site.png\",\"width\":512,\"height\":512,\"caption\":\"Arparun\"},\"image\":{\"@id\":\"https:\/\/www.arparun.re\/#\/schema\/logo\/image\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Photo ID - arparun - Informatique Reunion","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.arparun.re\/index.php\/photo-id\/","og_locale":"fr_FR","og_type":"article","og_title":"Photo ID - arparun - Informatique Reunion","og_description":"\ud83d\udcf8 G\u00e9n\u00e9rateur de Photos d&rsquo;Identit\u00e9 Cr\u00e9ez vos photos d&rsquo;identit\u00e9 en 30 secondes &#8211; 100% gratuit, aucune donn\u00e9e sauvegard\u00e9e \ud83d\udcf7 Glissez-d\u00e9posez votre photo ici ou cliquez pour parcourir (JPG, PNG, WEBP &#8211; max 10 Mo) \u2705 Photo charg\u00e9e ! Choisissez votre format : Passeport3 \u00d7 4 cm354 \u00d7 472 px VisaRead More ...","og_url":"https:\/\/www.arparun.re\/index.php\/photo-id\/","og_site_name":"arparun - Informatique Reunion","article_modified_time":"2026-01-28T07:48:12+00:00","twitter_card":"summary_large_image","twitter_misc":{"Dur\u00e9e de lecture estim\u00e9e":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.arparun.re\/index.php\/photo-id\/","url":"https:\/\/www.arparun.re\/index.php\/photo-id\/","name":"Photo ID - arparun - Informatique Reunion","isPartOf":{"@id":"https:\/\/www.arparun.re\/#website"},"datePublished":"2026-01-28T07:23:51+00:00","dateModified":"2026-01-28T07:48:12+00:00","breadcrumb":{"@id":"https:\/\/www.arparun.re\/index.php\/photo-id\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.arparun.re\/index.php\/photo-id\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.arparun.re\/index.php\/photo-id\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.arparun.re\/"},{"@type":"ListItem","position":2,"name":"Photo ID"}]},{"@type":"WebSite","@id":"https:\/\/www.arparun.re\/#website","url":"https:\/\/www.arparun.re\/","name":"arparun - Informatique Reunion","description":"Syst\u00e8mes, R\u00e9seaux et S\u00e9curit\u00e9","publisher":{"@id":"https:\/\/www.arparun.re\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.arparun.re\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fr-FR"},{"@type":"Organization","@id":"https:\/\/www.arparun.re\/#organization","name":"Arparun","url":"https:\/\/www.arparun.re\/","logo":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/www.arparun.re\/#\/schema\/logo\/image\/","url":"https:\/\/www.arparun.re\/wp-content\/uploads\/2021\/02\/cropped-site.png","contentUrl":"https:\/\/www.arparun.re\/wp-content\/uploads\/2021\/02\/cropped-site.png","width":512,"height":512,"caption":"Arparun"},"image":{"@id":"https:\/\/www.arparun.re\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/www.arparun.re\/index.php\/wp-json\/wp\/v2\/pages\/670","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.arparun.re\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.arparun.re\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.arparun.re\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.arparun.re\/index.php\/wp-json\/wp\/v2\/comments?post=670"}],"version-history":[{"count":4,"href":"https:\/\/www.arparun.re\/index.php\/wp-json\/wp\/v2\/pages\/670\/revisions"}],"predecessor-version":[{"id":674,"href":"https:\/\/www.arparun.re\/index.php\/wp-json\/wp\/v2\/pages\/670\/revisions\/674"}],"wp:attachment":[{"href":"https:\/\/www.arparun.re\/index.php\/wp-json\/wp\/v2\/media?parent=670"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}