Se rendre au contenu

Historique du code conçu pour le service principal de ce site

Merci surtout à Gemini pour son aide.

<meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Suppresseur de Publicités pour Bordereaux</title>

    <script src="https://cdn.tailwindcss.com"></script>

    <style>

        body {

            font-family: 'Inter', sans-serif;

        }

        .a4-paper {

            border: 2px solid #000;

            display: grid;

            grid-template-columns: repeat(2, 1fr);

            grid-template-rows: repeat(2, 1fr);

            cursor: pointer;

            background-color: #f0f0f0; /* Couleur de fond initiale du papier */

        }

        .quadrant {

            border: 1px dashed #666;

            display: flex;

            justify-content: center;

            align-items: center;

            transition: background-color 0.2s ease-in-out;

        }

        .quadrant:hover {

            background-color: #e0e0e0;

        }

        .quadrant.selected {

            background-color: #ffffff !important; /* Blanc pour la zone sélectionnée */

            border: 2px solid #3b82f6; /* Bleu pour indiquer la sélection active */

        }

        /* Style pour le message d'erreur/info */

        #message-box {

            position: fixed;

            top: 20px;

            left: 50%;

            transform: translateX(-50%);

            padding: 10px 20px;

            border-radius: 8px;

            color: white;

            z-index: 1000;

            display: none; /* Caché par défaut */

            box-shadow: 0 4px 6px rgba(0,0,0,0.1);

        }

        #message-box.success {

            background-color: #28a745; /* Vert pour succès */

        }

        #message-box.error {

            background-color: #dc3545; /* Rouge pour erreur */

        }

        #message-box.info {

            background-color: #17a2b8; /* Bleu pour info */

        }


        /* Styles pour les icônes dans les quadrants (optionnel, pour visibilité) */

        .quadrant-icon {

            font-size: 10px; /* Ajustez la taille selon vos besoins */

            color: #888;

        }


        /* Dimensions A4 Portrait: 210mm x 297mm. Ratio approx 1:1.414 */

        /* Dimensions A4 Paysage: 297mm x 210mm. Ratio approx 1.414:1 */

        /* Pour l'affichage, utilisons des tailles plus petites en gardant le ratio */

        .portrait {

            width: 100px; /* Largeur fixe pour l'icône portrait */

            height: 141.4px; /* Hauteur calculée pour garder le ratio A4 */

        }

        .landscape {

            width: 141.4px; /* Largeur fixe pour l'icône paysage */

            height: 100px; /* Hauteur calculée pour garder le ratio A4 */

        }

    </style>




    <div id="message-box"></div>


    <div class="bg-white p-6 md:p-8 rounded-xl shadow-2xl w-full max-w-3xl">

        <header class="mb-6 text-center">

            <h1 class="text-2xl md:text-3xl font-bold text-gray-800">Suppresseur de Publicités pour Bordereaux</h1>

            <p class="text-gray-600 mt-2">Sélectionnez la zone de la publicité à masquer sur votre bordereau.</p>

        </header>


        <main>

            <section class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">1. Choisissez l'orientation et la zone à masquer :</h2>

                <div class="flex flex-col sm:flex-row justify-center items-center gap-6 sm:gap-10">

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Portrait</p>

                        <div id="portrait-paper" class="a4-paper portrait rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="portrait" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Paysage</p>

                        <div id="landscape-paper" class="a4-paper landscape rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="landscape" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                </div>

            </section>


            <section class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">2. Téléversez votre bordereau (image) :</h2>

                <div class="flex flex-col items-center">

                    <input type="file" id="imageUpload" accept="image/*" class="block w-full max-w-xs text-sm text-gray-500

                        file:mr-4 file:py-2 file:px-4

                        file:rounded-lg file:border-0

                        file:text-sm file:font-semibold

                        file:bg-blue-50 file:text-blue-700

                        hover:file:bg-blue-100

                        mb-4 p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">

                   

                    <button id="processButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out disabled:opacity-50" disabled="">

                        Masquer la Publicité

                    </button>

                </div>

            </section>


            <section>

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">3. Résultat :</h2>

                <div class="flex flex-col items-center">

                    <canvas id="imageCanvas" class="border border-gray-400 rounded-lg shadow-md max-w-full h-auto" style="display: none;"></canvas>

                    <a id="downloadLink" class="hidden mt-4 bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out">

                        Télécharger l'image modifiée

                    </a>

                </div>

            </section>

        </main>


        <footer class="mt-8 text-center">

            <p class="text-xs text-gray-500">© 2025 Outil de suppression de publicités. HG: Haut Gauche, HD: Haut Droite, BG: Bas Gauche, BD: Bas Droite.</p>

        </footer>

    </div>


    <script>

        // Références aux éléments DOM

        const portraitPaper = document.getElementById('portrait-paper');

        const landscapePaper = document.getElementById('landscape-paper');

        const allQuadrants = document.querySelectorAll('.quadrant');

        const imageUpload = document.getElementById('imageUpload');

        const processButton = document.getElementById('processButton');

        const imageCanvas = document.getElementById('imageCanvas');

        const downloadLink = document.getElementById('downloadLink');

        const messageBox = document.getElementById('message-box');

        const ctx = imageCanvas.getContext('2d');


        // État de la sélection

        let selectedQuadrantInfo = {

            orientation: null, // 'portrait' ou 'landscape'

            quadrantIndex: null // 0: TL, 1: TR, 2: BL, 3: BR

        };

        let uploadedImage = null;


        // Fonction pour afficher les messages

        function showMessage(text, type = 'info', duration = 3000) {

            messageBox.textContent = text;

            messageBox.className = ''; // Réinitialiser les classes

            messageBox.classList.add(type); // Ajouter la classe de type (success, error, info)

            messageBox.style.display = 'block';

            setTimeout(() => {

                messageBox.style.display = 'none';

            }, duration);

        }


        // Gestion de la sélection des quadrants

        allQuadrants.forEach(quadrant => {

            quadrant.addEventListener('click', () => {

                // Désélectionner tous les quadrants

                allQuadrants.forEach(q => q.classList.remove('selected'));

                // Sélectionner le quadrant cliqué

                quadrant.classList.add('selected');

               

                selectedQuadrantInfo.orientation = quadrant.dataset.orientation;

                selectedQuadrantInfo.quadrantIndex = parseInt(quadrant.dataset.quadrant);

               

                // Activer le bouton de traitement si une image est chargée et un quadrant est sélectionné

                checkProcessButtonState();

                showMessage(`Zone sélectionnée: ${selectedQuadrantInfo.orientation}, quadrant ${selectedQuadrantInfo.quadrantIndex + 1}`, 'info');

            });

        });


        // Gestion du téléversement d'image

        imageUpload.addEventListener('change', (event) => {

            const file = event.target.files[0];

            if (file && file.type.startsWith('image/')) {

                const reader = new FileReader();

                reader.onload = (e) => {

                    uploadedImage = new Image();

                    uploadedImage.onload = () => {

                        // Afficher l'image originale sur le canvas (optionnel, ou attendre le traitement)

                        // imageCanvas.width = uploadedImage.width;

                        // imageCanvas.height = uploadedImage.height;

                        // ctx.drawImage(uploadedImage, 0, 0);

                        // imageCanvas.style.display = 'block';

                        checkProcessButtonState();

                        showMessage('Image chargée avec succès.', 'success');

                        downloadLink.classList.add('hidden'); // Cacher le lien de téléchargement si une nouvelle image est chargée

                    };

                    uploadedImage.onerror = () => {

                        showMessage('Erreur lors du chargement de l\'image.', 'error');

                        uploadedImage = null;

                        checkProcessButtonState();

                    }

                    uploadedImage.src = e.target.result;

                };

                reader.readAsDataURL(file);

            } else {

                showMessage('Veuillez sélectionner un fichier image valide (JPEG, PNG, GIF, etc.).', 'error');

                uploadedImage = null;

                imageUpload.value = ''; // Réinitialiser le champ de fichier

                checkProcessButtonState();

            }

        });


        // Vérifier si le bouton de traitement doit être activé

        function checkProcessButtonState() {

            if (uploadedImage && selectedQuadrantInfo.quadrantIndex !== null) {

                processButton.disabled = false;

            } else {

                processButton.disabled = true;

            }

        }


        // Gestion du clic sur le bouton "Masquer la Publicité"

        processButton.addEventListener('click', () => {

            if (!uploadedImage) {

                showMessage('Veuillez d\'abord téléverser une image.', 'error');

                return;

            }

            if (selectedQuadrantInfo.quadrantIndex === null) {

                showMessage('Veuillez sélectionner une zone à masquer sur les icônes A4.', 'error');

                return;

            }


            // Configurer le canvas avec les dimensions de l'image

            imageCanvas.width = uploadedImage.width;

            imageCanvas.height = uploadedImage.height;


            // Dessiner l'image originale

            ctx.drawImage(uploadedImage, 0, 0);


            // Calculer les coordonnées du rectangle blanc

            const imgWidth = uploadedImage.width;

            const imgHeight = uploadedImage.height;

            let rectX, rectY, rectWidth, rectHeight;


            rectWidth = imgWidth / 2;

            rectHeight = imgHeight / 2;


            switch (selectedQuadrantInfo.quadrantIndex) {

                case 0: // Haut Gauche

                    rectX = 0;

                    rectY = 0;

                    break;

                case 1: // Haut Droite

                    rectX = imgWidth / 2;

                    rectY = 0;

                    break;

                case 2: // Bas Gauche

                    rectX = 0;

                    rectY = imgHeight / 2;

                    break;

                case 3: // Bas Droite

                    rectX = imgWidth / 2;

                    rectY = imgHeight / 2;

                    break;

            }


            // Dessiner le rectangle blanc

            ctx.fillStyle = 'white';

            ctx.fillRect(rectX, rectY, rectWidth, rectHeight);

           

            // Afficher le canvas et le lien de téléchargement

            imageCanvas.style.display = 'block';

            downloadLink.href = imageCanvas.toDataURL('image/png'); // Proposer en PNG par défaut

            downloadLink.download = 'bordereau_modifie.png';

            downloadLink.classList.remove('hidden');

            showMessage('Publicité masquée ! Vous pouvez télécharger l\'image.', 'success');

        });


        // Initialiser l'état du bouton

        checkProcessButtonState();


    </script>

<meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Suppresseur de Publicités pour Bordereaux</title>

    <script src="https://cdn.tailwindcss.com"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>

    <script>

        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';

    </script>

    <style>

        body {

            font-family: 'Inter', sans-serif;

        }

        .a4-paper {

            border: 2px solid #000;

            display: grid;

            grid-template-columns: repeat(2, 1fr);

            grid-template-rows: repeat(2, 1fr);

            cursor: pointer;

            background-color: #f0f0f0;

        }

        .quadrant {

            border: 1px dashed #666;

            display: flex;

            justify-content: center;

            align-items: center;

            transition: background-color 0.2s ease-in-out;

        }

        .quadrant:hover {

            background-color: #e0e0e0;

        }

        .quadrant.selected {

            background-color: #ffffff !important;

            border: 2px solid #3b82f6;

        }

        #message-box {

            position: fixed;

            top: 20px;

            left: 50%;

            transform: translateX(-50%);

            padding: 10px 20px;

            border-radius: 8px;

            color: white;

            z-index: 1000;

            display: none;

            box-shadow: 0 4px 6px rgba(0,0,0,0.1);

        }

        #message-box.success { background-color: #28a745; }

        #message-box.error { background-color: #dc3545; }

        #message-box.info { background-color: #17a2b8; }


        .quadrant-icon { font-size: 10px; color: #888; }

        .portrait { width: 100px; height: 141.4px; }

        .landscape { width: 141.4px; height: 100px; }


        #imageCanvas {

            cursor: grab; /* Curseur pour indiquer que la zone est déplaçable */

        }

        #imageCanvas.dragging {

            cursor: grabbing;

        }

    </style>




    <div id="message-box"></div>


    <div class="bg-white p-6 md:p-8 rounded-xl shadow-2xl w-full max-w-3xl">

        <header class="mb-6 text-center">

            <h1 class="text-2xl md:text-3xl font-bold text-gray-800">Suppresseur de Publicités pour Bordereaux</h1>

            <p class="text-gray-600 mt-2">Masquez les publicités sur vos bordereaux Vinted et autres.</p>

        </header>


        <main>

            <section id="step1Selection" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">1. Choisissez la zone initiale de la publicité :</h2>

                <div class="flex flex-col sm:flex-row justify-center items-center gap-6 sm:gap-10">

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Portrait</p>

                        <div id="portrait-paper" class="a4-paper portrait rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="portrait" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Paysage</p>

                        <div id="landscape-paper" class="a4-paper landscape rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="landscape" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                </div>

            </section>


            <section id="step2Upload" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">2. Téléversez votre bordereau (Image ou PDF) :</h2>

                <div class="flex flex-col items-center">

                    <input type="file" id="imageUpload" accept="image/*,application/pdf" class="block w-full max-w-xs text-sm text-gray-500

                        file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold

                        file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100

                        mb-4 p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">

                </div>

            </section>

           

            <section id="step3Adjust" class="mb-6 text-center">

                 <h2 class="text-xl font-semibold text-gray-700 mb-3">3. Ajustez la zone et validez :</h2>

                <button id="initiateProcessingButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out disabled:opacity-50" disabled="">

                    Afficher l'image et ajuster la zone

                </button>

                <div class="flex flex-col items-center mt-4">

                    <canvas id="imageCanvas" class="border border-gray-400 rounded-lg shadow-md max-w-full h-auto" style="display: none;"></canvas>

                    <button id="applyAndDownloadButton" class="hidden mt-4 bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out">

                        Masquer la Pub et Télécharger

                    </button>

                </div>

            </section>

        </main>

        </div>


    <script>

        // Références DOM

        const allQuadrants = document.querySelectorAll('.quadrant');

        const imageUpload = document.getElementById('imageUpload');

        const initiateProcessingButton = document.getElementById('initiateProcessingButton');

        const imageCanvas = document.getElementById('imageCanvas');

        const applyAndDownloadButton = document.getElementById('applyAndDownloadButton');

        const messageBox = document.getElementById('message-box');

        const ctx = imageCanvas.getContext('2d');


        // Sections UI

        const step1Selection = document.getElementById('step1Selection');

        const step2Upload = document.getElementById('step2Upload');

        const step3Adjust = document.getElementById('step3Adjust');



        // État global

        let selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

        let uploadedImage = null; // Contiendra l'objet Image (après chargement direct ou conversion PDF)

        let pdfDoc = null; // Pour stocker le document PDF chargé

       

        let currentSelectionRect = { x: 0, y: 0, w: 0, h: 0, isDefined: false };

        let isDraggingSelection = false;

        let dragStartCoords = { x: 0, y: 0 }; // Coordonnées souris au début du drag

        let rectStartCoords = { x: 0, y: 0 }; // Coordonnées du rectangle au début du drag


        // --- Fonctions Utilitaires ---

        function showMessage(text, type = 'info', duration = 3500) {

            messageBox.textContent = text;

            messageBox.className = ''; // Reset classes

            messageBox.classList.add(type);

            messageBox.style.display = 'block';

            setTimeout(() => { messageBox.style.display = 'none'; }, duration);

        }


        function checkInitiateButtonState() {

            initiateProcessingButton.disabled = !(uploadedImage && selectedQuadrantInfo.quadrantIndex !== null);

        }


        // --- Gestion des Événements ---


        // 1. Sélection du Quadrant Initial

        allQuadrants.forEach(quadrant => {

            quadrant.addEventListener('click', () => {

                allQuadrants.forEach(q => q.classList.remove('selected'));

                quadrant.classList.add('selected');

                selectedQuadrantInfo.orientation = quadrant.dataset.orientation;

                selectedQuadrantInfo.quadrantIndex = parseInt(quadrant.dataset.quadrant);

                checkInitiateButtonState();

                showMessage(`Zone initiale: ${selectedQuadrantInfo.orientation}, quadrant ${selectedQuadrantInfo.quadrantIndex + 1}. Téléversez une image/PDF.`, 'info');

            });

        });


        // 2. Téléversement Image/PDF

        imageUpload.addEventListener('change', (event) => {

            const file = event.target.files[0];

            if (!file) return;


            resetCanvasAndState(); // Réinitialiser si un nouveau fichier est chargé


            if (file.type.startsWith('image/')) {

                const reader = new FileReader();

                reader.onload = (e) => {

                    uploadedImage = new Image();

                    uploadedImage.onload = () => {

                        showMessage('Image chargée. Cliquez sur "Afficher et ajuster".', 'success');

                        checkInitiateButtonState();

                    };

                    uploadedImage.onerror = () => {

                        showMessage('Erreur de chargement de l\'image.', 'error'); uploadedImage = null;

                    }

                    uploadedImage.src = e.target.result;

                };

                reader.readAsDataURL(file);

            } else if (file.type === 'application/pdf') {

                const fileReader = new FileReader();

                fileReader.onload = function() {

                    const typedarray = new Uint8Array(this.result);

                    pdfjsLib.getDocument(typedarray).promise.then(pdfDoc_ => {

                        pdfDoc = pdfDoc_;

                        renderPdfPage(1); // Afficher la première page

                    }).catch(err => {

                        showMessage('Erreur chargement PDF: ' + (err.message || err), 'error'); uploadedImage = null;

                        checkInitiateButtonState();

                    });

                };

                fileReader.readAsArrayBuffer(file);

            } else {

                showMessage('Type de fichier non supporté. Utilisez JPEG, PNG, GIF ou PDF.', 'error');

                uploadedImage = null;

                imageUpload.value = '';

            }

            checkInitiateButtonState();

        });


        function renderPdfPage(pageNum) {

            if (!pdfDoc) return;

            pdfDoc.getPage(pageNum).then(page => {

                const viewport = page.getViewport({ scale: 2.0 }); // Augmenter l'échelle pour une meilleure qualité

                const tempCanvas = document.createElement('canvas');

                const tempCtx = tempCanvas.getContext('2d');

                tempCanvas.height = viewport.height;

                tempCanvas.width = viewport.width;


                page.render({ canvasContext: tempCtx, viewport: viewport }).promise.then(() => {

                    uploadedImage = new Image();

                    uploadedImage.onload = () => {

                        showMessage('Page PDF chargée. Cliquez sur "Afficher et ajuster".', 'success');

                        checkInitiateButtonState();

                    };

                    uploadedImage.onerror = () => {

                        showMessage('Erreur conversion PDF en image.', 'error'); uploadedImage = null;

                    }

                    uploadedImage.src = tempCanvas.toDataURL('image/png'); // Convertir en PNG pour qualité

                });

            }).catch(err => {

                showMessage('Erreur rendu page PDF: ' + (err.message || err), 'error'); uploadedImage = null;

                checkInitiateButtonState();

            });

        }


        // 3. Bouton "Afficher l'image et ajuster la zone"

        initiateProcessingButton.addEventListener('click', () => {

            if (!uploadedImage || selectedQuadrantInfo.quadrantIndex === null) {

                showMessage('Veuillez sélectionner une zone initiale ET téléverser un fichier.', 'error');

                return;

            }

            setupInteractiveStage();

            initiateProcessingButton.classList.add('hidden');

            applyAndDownloadButton.classList.remove('hidden');

            imageCanvas.style.display = 'block';

            showMessage('Ajustez le rectangle rouge, puis validez.', 'info');

        });


        function setupInteractiveStage() {

            imageCanvas.width = uploadedImage.width;

            imageCanvas.height = uploadedImage.height;

           

            // Calcul du rectangle initial basé sur le quadrant

            const imgW = uploadedImage.width;

            const imgH = uploadedImage.height;

            currentSelectionRect.w = imgW / 2;

            currentSelectionRect.h = imgH / 2;


            switch (selectedQuadrantInfo.quadrantIndex) {

                case 0: currentSelectionRect.x = 0; currentSelectionRect.y = 0; break; // HG

                case 1: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = 0; break; // HD

                case 2: currentSelectionRect.x = 0; currentSelectionRect.y = imgH / 2; break; // BG

                case 3: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = imgH / 2; break; // BD

            }

            currentSelectionRect.isDefined = true;

            drawCanvasWithSelection();

        }

       

        function drawCanvasWithSelection(isFinal=false) {

            if (!uploadedImage) return;

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            ctx.drawImage(uploadedImage, 0, 0);


            if (currentSelectionRect.isDefined) {

                if (isFinal) {

                    ctx.fillStyle = 'white';

                    ctx.fillRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                } else {

                    ctx.strokeStyle = 'red';

                    ctx.lineWidth = 3;

                    ctx.strokeRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    // Dessiner une petite poignée (optionnel, pour indiquer la déplaçabilité)

                    ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';

                    ctx.fillRect(currentSelectionRect.x + currentSelectionRect.w/2 - 10, currentSelectionRect.y + currentSelectionRect.h/2 - 10, 20, 20);

                }

            }

        }


        // Gestion du glisser-déposer du rectangle de sélection

        imageCanvas.addEventListener('mousedown', (e) => {

            if (!currentSelectionRect.isDefined || !uploadedImage) return;

            const rect = imageCanvas.getBoundingClientRect();

            const mouseX = (e.clientX - rect.left) * (imageCanvas.width / rect.width);

            const mouseY = (e.clientY - rect.top) * (imageCanvas.height / rect.height);


            // Vérifier si le clic est dans le rectangle

            if (mouseX >= currentSelectionRect.x && mouseX <= currentSelectionRect.x + currentSelectionRect.w &&

                mouseY >= currentSelectionRect.y && mouseY <= currentSelectionRect.y + currentSelectionRect.h) {

                isDraggingSelection = true;

                imageCanvas.classList.add('dragging');

                dragStartCoords.x = mouseX;

                dragStartCoords.y = mouseY;

                rectStartCoords.x = currentSelectionRect.x;

                rectStartCoords.y = currentSelectionRect.y;

            }

        });


        imageCanvas.addEventListener('mousemove', (e) => {

            if (!isDraggingSelection || !currentSelectionRect.isDefined || !uploadedImage) return;

            const rect = imageCanvas.getBoundingClientRect();

            const mouseX = (e.clientX - rect.left) * (imageCanvas.width / rect.width);

            const mouseY = (e.clientY - rect.top) * (imageCanvas.height / rect.height);


            const deltaX = mouseX - dragStartCoords.x;

            const deltaY = mouseY - dragStartCoords.y;


            currentSelectionRect.x = rectStartCoords.x + deltaX;

            currentSelectionRect.y = rectStartCoords.y + deltaY;


            // Contraintes pour que le rectangle ne sorte pas du canvas

            currentSelectionRect.x = Math.max(0, Math.min(currentSelectionRect.x, imageCanvas.width - currentSelectionRect.w));

            currentSelectionRect.y = Math.max(0, Math.min(currentSelectionRect.y, imageCanvas.height - currentSelectionRect.h));

           

            drawCanvasWithSelection();

        });


        imageCanvas.addEventListener('mouseup', () => {

            if (isDraggingSelection) {

                isDraggingSelection = false;

                imageCanvas.classList.remove('dragging');

            }

        });

         imageCanvas.addEventListener('mouseleave', () => { // Arrêter le drag si la souris quitte le canvas

            if (isDraggingSelection) {

                isDraggingSelection = false;

                imageCanvas.classList.remove('dragging');

            }

        });



        // 4. Bouton "Masquer la Pub et Télécharger"

        applyAndDownloadButton.addEventListener('click', () => {

            if (!currentSelectionRect.isDefined || !uploadedImage) {

                showMessage('Aucune zone valide définie ou image chargée.', 'error');

                return;

            }

            drawCanvasWithSelection(true); // true pour dessiner le rectangle blanc final


            // Logique de téléchargement améliorée pour iOS

            try {

                const dataURL = imageCanvas.toDataURL('image/png');

                const newWindow = window.open();

                if (newWindow) {

                    newWindow.document.write('<img src="' + dataURL + '" alt="Image modifiée" style="max-width:100%;"/> <br><p>Sur mobile, maintenez l\'appui sur l\'image pour l\'enregistrer. Sur ordinateur, clic droit > Enregistrer l\'image sous...</p>');

                    showMessage('Image prête dans un nouvel onglet.', 'success');

                } else {

                     // Si window.open est bloqué, fournir un lien direct (peut ne pas fonctionner sur iOS pour le téléchargement direct)

                    const link = document.createElement('a');

                    link.href = dataURL;

                    link.download = 'bordereau_modifie.png';

                    document.body.appendChild(link);

                    link.click();

                    document.body.removeChild(link);

                    showMessage('Tentative de téléchargement direct. Si bloqué, vérifiez les pop-ups.', 'info');

                }

            } catch (err) {

                showMessage('Erreur lors de la préparation du téléchargement: ' + (err.message || err), 'error');

            }

           

            // Réinitialiser pour une nouvelle opération

            // resetCanvasAndState(); // Optionnel: ou laisser l'utilisateur recommencer manuellement

            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

            // imageCanvas.style.display = 'none'; // Cacher le canvas après téléchargement

        });


        function resetCanvasAndState() {

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            imageCanvas.style.display = 'none';

            uploadedImage = null;

            pdfDoc = null;

            currentSelectionRect.isDefined = false;

            isDraggingSelection = false;

            initiateProcessingButton.disabled = true;

            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

            imageUpload.value = ''; // Important pour permettre de re-téléverser le même fichier

            // Ne pas réinitialiser selectedQuadrantInfo ici, l'utilisateur peut vouloir l'utiliser à nouveau

        }


        // Initialisation

        checkInitiateButtonState();


    </script>

<!DOCTYPE html>

<html lang="fr">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Suppresseur de Publicités pour Bordereaux</title>

    <script src="https://cdn.tailwindcss.com"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>

    <script>

        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';

    </script>

    <style>

        body {

            font-family: 'Inter', sans-serif;

        }

        .a4-paper {

            border: 2px solid #000;

            display: grid;

            grid-template-columns: repeat(2, 1fr);

            grid-template-rows: repeat(2, 1fr);

            cursor: pointer;

            background-color: #f0f0f0;

        }

        .quadrant {

            border: 1px dashed #666;

            display: flex;

            justify-content: center;

            align-items: center;

            transition: background-color 0.2s ease-in-out;

        }

        .quadrant:hover {

            background-color: #e0e0e0;

        }

        .quadrant.selected {

            background-color: #ffffff !important;

            border: 2px solid #3b82f6;

        }

        #message-box {

            position: fixed;

            top: 20px;

            left: 50%;

            transform: translateX(-50%);

            padding: 10px 20px;

            border-radius: 8px;

            color: white;

            z-index: 1000;

            display: none;

            box-shadow: 0 4px 6px rgba(0,0,0,0.1);

        }

        #message-box.success { background-color: #28a745; }

        #message-box.error { background-color: #dc3545; }

        #message-box.info { background-color: #17a2b8; }


        .quadrant-icon { font-size: 10px; color: #888; }

        .portrait { width: 100px; height: 141.4px; }

        .landscape { width: 141.4px; height: 100px; }


        #imageCanvas {

            /* Le curseur sera géré dynamiquement par JavaScript */

        }

        .handle { /* Style pour les poignées de redimensionnement */

            position: absolute; /* Positionné par JS */

            width: 10px;

            height: 10px;

            background-color: rgba(255, 0, 0, 0.7);

            border: 1px solid rgba(255, 255, 255, 0.9);

            border-radius: 50%; /* Rond pour une meilleure prise */

            z-index: 10; /* Au-dessus du canvas mais sous les messages */

        }

    </style>

</head>

<body class="bg-gray-100 min-h-screen flex flex-col items-center justify-center p-4">


    <div id="message-box"></div>


    <div class="bg-white p-6 md:p-8 rounded-xl shadow-2xl w-full max-w-3xl">

        <header class="mb-6 text-center">

            <h1 class="text-2xl md:text-3xl font-bold text-gray-800">Suppresseur de Publicités pour Bordereaux</h1>

            <p class="text-gray-600 mt-2">Masquez les publicités sur vos bordereaux Vinted et autres.</p>

        </header>


        <main>

            <section id="step1Selection" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">1. Choisissez la zone initiale approximative :</h2>

                <div class="flex flex-col sm:flex-row justify-center items-center gap-6 sm:gap-10">

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Portrait</p>

                        <div id="portrait-paper" class="a4-paper portrait rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="portrait" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Paysage</p>

                        <div id="landscape-paper" class="a4-paper landscape rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="landscape" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                </div>

            </section>


            <section id="step2Upload" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">2. Téléversez votre bordereau (Image ou PDF) :</h2>

                <div class="flex flex-col items-center">

                    <input type="file" id="imageUpload" accept="image/*,application/pdf" class="block w-full max-w-xs text-sm text-gray-500

                        file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold

                        file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100

                        mb-4 p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">

                </div>

            </section>

            <section id="step3Adjust" class="mb-6 text-center">

                 <h2 class="text-xl font-semibold text-gray-700 mb-3">3. Ajustez la zone et validez :</h2>

                <button id="initiateProcessingButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out disabled:opacity-50" disabled>

                    Afficher et Ajuster la Zone

                </button>

                <div id="canvasContainer" class="relative flex flex-col items-center mt-4"> <canvas id="imageCanvas" class="border border-gray-400 rounded-lg shadow-md max-w-full h-auto" style="display: none;"></canvas>

                    </div>

                <button id="applyAndDownloadButton" class="hidden mt-4 bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out">

                    Masquer la Pub et Télécharger

                </button>

            </section>

        </main>

    </div>


    <script>

        // Références DOM

        const allQuadrants = document.querySelectorAll('.quadrant');

        const imageUpload = document.getElementById('imageUpload');

        const initiateProcessingButton = document.getElementById('initiateProcessingButton');

        const imageCanvas = document.getElementById('imageCanvas');

        const canvasContainer = document.getElementById('canvasContainer');

        const applyAndDownloadButton = document.getElementById('applyAndDownloadButton');

        const messageBox = document.getElementById('message-box');

        const ctx = imageCanvas.getContext('2d');


        // État global

        let selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

        let uploadedImage = null;

        let pdfDoc = null;

        let currentSelectionRect = { x: 0, y: 0, w: 0, h: 0, isDefined: false };

        let activeDragAction = null; // 'move', 'resize-tl', 'resize-t', 'resize-tr', etc.

        let dragStartCoords = { x: 0, y: 0 };

        let rectStartCoords = { x: 0, y: 0, w: 0, h: 0 };

        const HANDLE_SIZE = 10; // Taille des poignées de redimensionnement

        let handles = []; // Pour stocker les éléments DOM des poignées


        // --- Fonctions Utilitaires ---

        function showMessage(text, type = 'info', duration = 3500) {

            messageBox.textContent = text;

            messageBox.className = '';

            messageBox.classList.add(type);

            messageBox.style.display = 'block';

            setTimeout(() => { messageBox.style.display = 'none'; }, duration);

        }


        function checkInitiateButtonState() {

            initiateProcessingButton.disabled = !(uploadedImage && selectedQuadrantInfo.quadrantIndex !== null);

        }


        // --- Gestion des Événements ---


        // 1. Sélection du Quadrant Initial

        allQuadrants.forEach(quadrant => {

            quadrant.addEventListener('click', () => {

                allQuadrants.forEach(q => q.classList.remove('selected'));

                quadrant.classList.add('selected');

                selectedQuadrantInfo.orientation = quadrant.dataset.orientation;

                selectedQuadrantInfo.quadrantIndex = parseInt(quadrant.dataset.quadrant);

                checkInitiateButtonState();

                showMessage(`Zone initiale: ${selectedQuadrantInfo.orientation}, quadrant ${selectedQuadrantInfo.quadrantIndex + 1}. Téléversez une image/PDF.`, 'info');

            });

        });


        // 2. Téléversement Image/PDF

        imageUpload.addEventListener('change', (event) => {

            const file = event.target.files[0];

            if (!file) return;

            resetCanvasAndState();


            if (file.type.startsWith('image/')) {

                const reader = new FileReader();

                reader.onload = (e) => {

                    uploadedImage = new Image();

                    uploadedImage.onload = () => {

                        showMessage('Image chargée. Cliquez sur "Afficher et Ajuster".', 'success');

                        checkInitiateButtonState();

                    };

                    uploadedImage.onerror = () => {

                        showMessage('Erreur de chargement de l\'image.', 'error'); uploadedImage = null;

                    }

                    uploadedImage.src = e.target.result;

                };

                reader.readAsDataURL(file);

            } else if (file.type === 'application/pdf') {

                const fileReader = new FileReader();

                fileReader.onload = function() {

                    const typedarray = new Uint8Array(this.result);

                    pdfjsLib.getDocument(typedarray).promise.then(pdfDoc_ => {

                        pdfDoc = pdfDoc_;

                        renderPdfPage(1);

                    }).catch(err => {

                        showMessage('Erreur chargement PDF: ' + (err.message || err), 'error'); uploadedImage = null;

                        checkInitiateButtonState();

                    });

                };

                fileReader.readAsArrayBuffer(file);

            } else {

                showMessage('Type de fichier non supporté. Utilisez JPEG, PNG, GIF ou PDF.', 'error');

                uploadedImage = null; imageUpload.value = '';

            }

            checkInitiateButtonState();

        });


        function renderPdfPage(pageNum) {

            if (!pdfDoc) return;

            pdfDoc.getPage(pageNum).then(page => {

                const viewport = page.getViewport({ scale: 2.5 }); // Échelle augmentée pour meilleure qualité

                const tempCanvas = document.createElement('canvas');

                const tempCtx = tempCanvas.getContext('2d');

                tempCanvas.height = viewport.height;

                tempCanvas.width = viewport.width;


                page.render({ canvasContext: tempCtx, viewport: viewport }).promise.then(() => {

                    uploadedImage = new Image();

                    uploadedImage.onload = () => {

                        showMessage('Page PDF chargée. Cliquez sur "Afficher et Ajuster".', 'success');

                        checkInitiateButtonState();

                    };

                    uploadedImage.onerror = () => {

                        showMessage('Erreur conversion PDF en image.', 'error'); uploadedImage = null;

                    }

                    uploadedImage.src = tempCanvas.toDataURL('image/png');

                });

            }).catch(err => {

                showMessage('Erreur rendu page PDF: ' + (err.message || err), 'error'); uploadedImage = null;

                checkInitiateButtonState();

            });

        }


        // 3. Bouton "Afficher et Ajuster la Zone"

        initiateProcessingButton.addEventListener('click', () => {

            if (!uploadedImage || selectedQuadrantInfo.quadrantIndex === null) {

                showMessage('Veuillez sélectionner une zone initiale ET téléverser un fichier.', 'error');

                return;

            }

            setupInteractiveStage();

            initiateProcessingButton.classList.add('hidden');

            applyAndDownloadButton.classList.remove('hidden');

            imageCanvas.style.display = 'block';

            showMessage('Ajustez le rectangle (déplacez/redimensionnez), puis validez.', 'info');

        });


        function setupInteractiveStage() {

            imageCanvas.width = uploadedImage.width;

            imageCanvas.height = uploadedImage.height;

            const imgW = uploadedImage.width;

            const imgH = uploadedImage.height;

            currentSelectionRect.w = imgW / 2;

            currentSelectionRect.h = imgH / 2;


            switch (selectedQuadrantInfo.quadrantIndex) {

                case 0: currentSelectionRect.x = 0; currentSelectionRect.y = 0; break;

                case 1: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = 0; break;

                case 2: currentSelectionRect.x = 0; currentSelectionRect.y = imgH / 2; break;

                case 3: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = imgH / 2; break;

            }

            currentSelectionRect.isDefined = true;

            createHandles();

            drawCanvasWithSelectionAndHandles();

        }

        function drawCanvasWithSelectionAndHandles(isFinal = false) {

            if (!uploadedImage) return;

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            ctx.drawImage(uploadedImage, 0, 0);


            if (currentSelectionRect.isDefined) {

                if (isFinal) {

                    ctx.fillStyle = 'white';

                    ctx.fillRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    removeHandles(); // Cacher les poignées pour l'image finale

                } else {

                    ctx.strokeStyle = 'rgba(255, 0, 0, 0.8)';

                    ctx.lineWidth = 2;

                    ctx.strokeRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    updateHandlesPositions();

                }

            }

        }


        function createHandles() {

            removeHandles(); // S'assurer qu'il n'y a pas d'anciennes poignées

            const handleTypes = ['tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br']; // Top-Left, Top, Top-Right, etc.

            handleTypes.forEach(type => {

                const handle = document.createElement('div');

                handle.classList.add('handle');

                handle.dataset.type = type;

                canvasContainer.appendChild(handle);

                handles.push(handle);

            });

            updateHandlesPositions(); // Positionner correctement

        }


        function removeHandles() {

            handles.forEach(h => h.remove());

            handles = [];

        }


        function updateHandlesPositions() {

            if (!currentSelectionRect.isDefined || handles.length === 0) return;

            const { x, y, w, h } = currentSelectionRect;

            const canvasRect = imageCanvas.getBoundingClientRect(); // Pour positionner les poignées par rapport au canvas affiché


            const scaleX = canvasRect.width / imageCanvas.width;

            const scaleY = canvasRect.height / imageCanvas.height;


            const positions = {

                'tl': { left: x * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                't':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'tr': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'l':  { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'r':  { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'bl': { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'b':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'br': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 }

            };


            handles.forEach(handle => {

                const type = handle.dataset.type;

                handle.style.left = `${positions[type].left}px`;

                handle.style.top = `${positions[type].top}px`;

                handle.style.cursor = getResizeCursor(type);

                handle.style.display = 'block';

            });

        }

        function getResizeCursor(handleType) {

            switch (handleType) {

                case 'tl': case 'br': return 'nwse-resize';

                case 'tr': case 'bl': return 'nesw-resize';

                case 't':  case 'b':  return 'ns-resize';

                case 'l':  case 'r':  return 'ew-resize';

                default: return 'default';

            }

        }


        // Convertir les coordonnées de la souris de l'écran vers le canvas

        function getMousePosOnCanvas(event) {

            const rect = imageCanvas.getBoundingClientRect();

            return {

                x: (event.clientX - rect.left) * (imageCanvas.width / rect.width),

                y: (event.clientY - rect.top) * (imageCanvas.height / rect.height)

            };

        }

        // Gestion du Drag & Resize

        canvasContainer.addEventListener('mousedown', (e) => {

            if (!currentSelectionRect.isDefined || !uploadedImage) return;

            const target = e.target;

            const mousePos = getMousePosOnCanvas(e);


            if (target.classList.contains('handle')) { // Clic sur une poignée

                activeDragAction = `resize-${target.dataset.type}`;

            } else if (target === imageCanvas && // Clic sur le canvas

                       mousePos.x >= currentSelectionRect.x && mousePos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                       mousePos.y >= currentSelectionRect.y && mousePos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                activeDragAction = 'move';

            } else {

                return; // Clic en dehors

            }

            dragStartCoords = mousePos;

            rectStartCoords = { ...currentSelectionRect }; // Copie de l'état initial du rectangle

            e.preventDefault(); // Empêcher la sélection de texte, etc.

        });


        document.addEventListener('mousemove', (e) => { // Écouter sur document pour un drag plus fluide

            if (!activeDragAction || !currentSelectionRect.isDefined) {

                // Mettre à jour le curseur si on survole le canvas ou les poignées

                if (e.target === imageCanvas || e.target.classList.contains('handle')) {

                    const mousePos = getMousePosOnCanvas(e);

                    let cursor = 'default';

                    if (currentSelectionRect.isDefined) {

                        // Vérifier si on survole une poignée (même si pas en drag)

                        for (const handle of handles) {

                            const handleRect = handle.getBoundingClientRect();

                            const canvasRect = imageCanvas.getBoundingClientRect();

                            // Convertir les coordonnées de la poignée en coordonnées relatives au canvas

                            const handleCanvasX = (handleRect.left - canvasRect.left + HANDLE_SIZE/2) * (imageCanvas.width / canvasRect.width);

                            const handleCanvasY = (handleRect.top - canvasRect.top + HANDLE_SIZE/2) * (imageCanvas.height / canvasRect.height);

                            // Petite zone de tolérance pour la détection du survol de la poignée

                            if (Math.abs(mousePos.x - handleCanvasX) < HANDLE_SIZE && Math.abs(mousePos.y - handleCanvasY) < HANDLE_SIZE * 2) {

                                cursor = getResizeCursor(handle.dataset.type);

                                break;

                            }

                        }

                        if (cursor === 'default' && // Si pas sur une poignée, vérifier si sur le rectangle

                            mousePos.x >= currentSelectionRect.x && mousePos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                            mousePos.y >= currentSelectionRect.y && mousePos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                            cursor = 'move';

                        }

                    }

                    imageCanvas.style.cursor = cursor;

                }

                return;

            }


            const mousePos = getMousePosOnCanvas(e);

            const deltaX = mousePos.x - dragStartCoords.x;

            const deltaY = mousePos.y - dragStartCoords.y;

            let { x, y, w, h } = rectStartCoords; // Partir de l'état au début du drag


            if (activeDragAction === 'move') {

                x += deltaX;

                y += deltaY;

            } else if (activeDragAction.startsWith('resize-')) {

                const type = activeDragAction.split('-')[1];

                if (type.includes('l')) { x += deltaX; w -= deltaX; }

                if (type.includes('r')) { w += deltaX; }

                if (type.includes('t')) { y += deltaY; h -= deltaY; }

                if (type.includes('b')) { h += deltaY; }

            }


            // Assurer que la largeur/hauteur ne deviennent pas négatives ou trop petites

            const minSize = 20; // Taille minimale pour le rectangle

            if (w < minSize) {

                if (activeDragAction.includes('l')) x = rectStartCoords.x + rectStartCoords.w - minSize;

                w = minSize;

            }

            if (h < minSize) {

                if (activeDragAction.includes('t')) y = rectStartCoords.y + rectStartCoords.h - minSize;

                h = minSize;

            }

            // Contraintes pour que le rectangle ne sorte pas du canvas

            x = Math.max(0, Math.min(x, imageCanvas.width - w));

            y = Math.max(0, Math.min(y, imageCanvas.height - h));

            // S'assurer que w et h ne dépassent pas les limites du canvas après déplacement de x,y

            w = Math.min(w, imageCanvas.width - x);

            h = Math.min(h, imageCanvas.height - y);



            currentSelectionRect = { x, y, w, h, isDefined: true };

            drawCanvasWithSelectionAndHandles();

            e.preventDefault();

        });


        document.addEventListener('mouseup', (e) => {

            if (activeDragAction) {

                activeDragAction = null;

                imageCanvas.style.cursor = 'default'; // Réinitialiser le curseur

                // S'assurer que les poignées sont bien positionnées après le drag

                updateHandlesPositions();

            }

        });

        // 4. Bouton "Masquer la Pub et Télécharger"

        applyAndDownloadButton.addEventListener('click', () => {

            if (!currentSelectionRect.isDefined || !uploadedImage) {

                showMessage('Aucune zone valide définie ou image chargée.', 'error');

                return;

            }

            drawCanvasWithSelectionAndHandles(true); // true pour dessiner le rectangle blanc final


            try {

                const dataURL = imageCanvas.toDataURL('image/png');

                const link = document.createElement('a');

                link.href = dataURL;

                link.download = 'bordereau_modifie.png';

                // Tentative de téléchargement direct

                document.body.appendChild(link);

                link.click();

                document.body.removeChild(link);

                showMessage('Téléchargement lancé...', 'success');


            } catch (err) {

                showMessage('Erreur lors de la préparation du téléchargement. Essayez d\'enregistrer l\'image manuellement.', 'error');

                 // Fallback: ouvrir dans une nouvelle fenêtre si le téléchargement direct échoue ou est bloqué

                try {

                    const dataURL = imageCanvas.toDataURL('image/png');

                    const newWindow = window.open();

                    if (newWindow) {

                        newWindow.document.write('<img src="' + dataURL + '" alt="Image modifiée" style="max-width:100%;"/> <br><p>Sur mobile, maintenez l\'appui sur l\'image pour l\'enregistrer. Sur ordinateur, clic droit > Enregistrer l\'image sous...</p>');

                        showMessage('Image prête dans un nouvel onglet (téléchargement direct a pu échouer).', 'info');

                    } else {

                        showMessage('Impossible d\'ouvrir un nouvel onglet. Veuillez vérifier les bloqueurs de pop-up.', 'error');

                    }

                } catch (fallbackError) {

                     showMessage('Erreur critique de téléchargement: ' + (fallbackError.message || fallbackError), 'error');

                }

            }

            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

            removeHandles(); // Nettoyer les poignées après l'opération

            // imageCanvas.style.display = 'none'; // Optionnel: cacher le canvas

        });


        function resetCanvasAndState() {

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            imageCanvas.style.display = 'none';

            removeHandles();

            uploadedImage = null;

            pdfDoc = null;

            currentSelectionRect.isDefined = false;

            activeDragAction = null;

            initiateProcessingButton.disabled = true;

            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

            imageUpload.value = '';

        }


        // Initialisation

        checkInitiateButtonState();

        window.addEventListener('resize', () => { // Mettre à jour la position des poignées si la fenêtre est redimensionnée

            if (currentSelectionRect.isDefined && handles.length > 0) {

                updateHandlesPositions();

            }

        });


    </script>

</body>

</html>



<!DOCTYPE html>

<html lang="fr">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

    <title>Suppresseur de Publicités pour Bordereaux</title>

    <script src="https://cdn.tailwindcss.com"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>

    <script>

        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';

    </script>

    <style>

        body {

            font-family: 'Inter', sans-serif;

            overscroll-behavior-y: contain; /* Empêche le "pull-to-refresh" sur mobile pendant le drag */

        }

        .a4-paper {

            border: 2px solid #000;

            display: grid;

            grid-template-columns: repeat(2, 1fr);

            grid-template-rows: repeat(2, 1fr);

            cursor: pointer;

            background-color: #f0f0f0;

        }

        .quadrant {

            border: 1px dashed #666;

            display: flex;

            justify-content: center;

            align-items: center;

            transition: background-color 0.2s ease-in-out;

        }

        .quadrant:hover {

            background-color: #e0e0e0;

        }

        .quadrant.selected {

            background-color: #ffffff !important;

            border: 2px solid #3b82f6;

        }

        #message-box {

            position: fixed;

            top: 20px;

            left: 50%;

            transform: translateX(-50%);

            padding: 10px 20px;

            border-radius: 8px;

            color: white;

            z-index: 1000;

            display: none;

            box-shadow: 0 4px 6px rgba(0,0,0,0.1);

        }

        #message-box.success { background-color: #28a745; }

        #message-box.error { background-color: #dc3545; }

        #message-box.info { background-color: #17a2b8; }


        .quadrant-icon { font-size: 10px; color: #888; }

        .portrait { width: 100px; height: 141.4px; }

        .landscape { width: 141.4px; height: 100px; }


        #imageCanvas {

            touch-action: none; /* Important pour la gestion tactile personnalisée */

        }

        .handle {

            position: absolute;

            width: 12px; /* Légèrement plus grand pour le tactile */

            height: 12px;

            background-color: rgba(255, 0, 0, 0.7);

            border: 1px solid rgba(255, 255, 255, 0.9);

            border-radius: 50%;

            z-index: 10;

            touch-action: none; /* Empêche le défilement lors de l'interaction avec les poignées */

        }

    </style>

</head>

<body class="bg-gray-100 min-h-screen flex flex-col items-center justify-center p-4">


    <div id="message-box"></div>


    <div class="bg-white p-6 md:p-8 rounded-xl shadow-2xl w-full max-w-3xl">

        <header class="mb-6 text-center">

            <h1 class="text-2xl md:text-3xl font-bold text-gray-800">Suppresseur de Publicités pour Bordereaux</h1>

            <p class="text-gray-600 mt-2">Masquez les publicités sur vos bordereaux Vinted et autres.</p>

        </header>


        <main>

            <section id="step1Selection" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">1. Choisissez la zone initiale approximative :</h2>

                <div class="flex flex-col sm:flex-row justify-center items-center gap-6 sm:gap-10">

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Portrait</p>

                        <div id="portrait-paper" class="a4-paper portrait rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="portrait" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Paysage</p>

                        <div id="landscape-paper" class="a4-paper landscape rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="landscape" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                </div>

            </section>


            <section id="step2Upload" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">2. Téléversez votre bordereau (Image ou PDF) :</h2>

                <div class="flex flex-col items-center">

                    <input type="file" id="imageUpload" accept="image/*,application/pdf" class="block w-full max-w-xs text-sm text-gray-500

                        file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold

                        file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100

                        mb-4 p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">

                </div>

            </section>

            <section id="step3Adjust" class="mb-6 text-center">

                 <h2 class="text-xl font-semibold text-gray-700 mb-3">3. Ajustez la zone et validez :</h2>

                <button id="initiateProcessingButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out disabled:opacity-50" disabled>

                    Afficher et Ajuster la Zone

                </button>

                <div id="canvasContainer" class="relative flex flex-col items-center mt-4">

                    <canvas id="imageCanvas" class="border border-gray-400 rounded-lg shadow-md max-w-full h-auto" style="display: none;"></canvas>

                    </div>

                <button id="applyAndDownloadButton" class="hidden mt-4 bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out">

                    Masquer la Pub et Télécharger

                </button>

            </section>

        </main>

    </div>


    <script>

        // Références DOM

        const allQuadrants = document.querySelectorAll('.quadrant');

        const imageUpload = document.getElementById('imageUpload');

        const initiateProcessingButton = document.getElementById('initiateProcessingButton');

        const imageCanvas = document.getElementById('imageCanvas');

        const canvasContainer = document.getElementById('canvasContainer');

        const applyAndDownloadButton = document.getElementById('applyAndDownloadButton');

        const messageBox = document.getElementById('message-box');

        const ctx = imageCanvas.getContext('2d');


        // État global

        let selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

        let uploadedImage = null;

        let pdfDoc = null;

        let currentSelectionRect = { x: 0, y: 0, w: 0, h: 0, isDefined: false };

        let activeDragAction = null;

        let dragStartCoords = { x: 0, y: 0 };

        let rectStartCoords = { x: 0, y: 0, w: 0, h: 0 };

        const HANDLE_SIZE = 12; // Augmenté pour le tactile

        let handles = [];


        // --- Fonctions Utilitaires ---

        function showMessage(text, type = 'info', duration = 4000) {

            messageBox.textContent = text;

            messageBox.className = '';

            messageBox.classList.add(type);

            messageBox.style.display = 'block';

            setTimeout(() => { messageBox.style.display = 'none'; }, duration);

        }


        function checkInitiateButtonState() {

            initiateProcessingButton.disabled = !(uploadedImage && selectedQuadrantInfo.quadrantIndex !== null);

        }


        // --- Gestion des Événements ---


        allQuadrants.forEach(quadrant => {

            quadrant.addEventListener('click', () => {

                allQuadrants.forEach(q => q.classList.remove('selected'));

                quadrant.classList.add('selected');

                selectedQuadrantInfo.orientation = quadrant.dataset.orientation;

                selectedQuadrantInfo.quadrantIndex = parseInt(quadrant.dataset.quadrant);

                checkInitiateButtonState();

                showMessage(`Zone initiale: ${selectedQuadrantInfo.orientation}, Q${selectedQuadrantInfo.quadrantIndex + 1}. Téléversez un fichier.`, 'info');

            });

        });


        imageUpload.addEventListener('change', (event) => {

            const file = event.target.files[0];

            if (!file) return;

            resetCanvasAndState();


            if (file.type.startsWith('image/')) {

                const reader = new FileReader();

                reader.onload = (e) => {

                    uploadedImage = new Image();

                    uploadedImage.onload = () => {

                        showMessage('Image chargée. Cliquez sur "Afficher et Ajuster".', 'success');

                        checkInitiateButtonState();

                    };

                    uploadedImage.onerror = () => {

                        showMessage('Erreur de chargement image.', 'error'); uploadedImage = null;

                    }

                    uploadedImage.src = e.target.result;

                };

                reader.readAsDataURL(file);

            } else if (file.type === 'application/pdf') {

                const fileReader = new FileReader();

                fileReader.onload = function() {

                    const typedarray = new Uint8Array(this.result);

                    pdfjsLib.getDocument(typedarray).promise.then(pdfDoc_ => {

                        pdfDoc = pdfDoc_;

                        renderPdfPage(1);

                    }).catch(err => {

                        showMessage('Erreur chargement PDF: ' + (err.message || err), 'error'); uploadedImage = null;

                        checkInitiateButtonState();

                    });

                };

                fileReader.readAsArrayBuffer(file);

            } else {

                showMessage('Type de fichier non supporté (Image/PDF).', 'error');

                uploadedImage = null; imageUpload.value = '';

            }

            checkInitiateButtonState();

        });


        function renderPdfPage(pageNum) {

            if (!pdfDoc) return;

            pdfDoc.getPage(pageNum).then(page => {

                const viewport = page.getViewport({ scale: 2.5 });

                const tempCanvas = document.createElement('canvas');

                const tempCtx = tempCanvas.getContext('2d');

                tempCanvas.height = viewport.height;

                tempCanvas.width = viewport.width;


                page.render({ canvasContext: tempCtx, viewport: viewport }).promise.then(() => {

                    uploadedImage = new Image();

                    uploadedImage.onload = () => {

                        showMessage('Page PDF chargée. Cliquez sur "Afficher et Ajuster".', 'success');

                        checkInitiateButtonState();

                    };

                    uploadedImage.onerror = () => {

                        showMessage('Erreur conversion PDF.', 'error'); uploadedImage = null;

                    }

                    uploadedImage.src = tempCanvas.toDataURL('image/png');

                });

            }).catch(err => {

                showMessage('Erreur rendu PDF: ' + (err.message || err), 'error'); uploadedImage = null;

                checkInitiateButtonState();

            });

        }


        initiateProcessingButton.addEventListener('click', () => {

            if (!uploadedImage || selectedQuadrantInfo.quadrantIndex === null) {

                showMessage('Sélectionnez zone ET téléversez un fichier.', 'error');

                return;

            }

            setupInteractiveStage();

            initiateProcessingButton.classList.add('hidden');

            applyAndDownloadButton.classList.remove('hidden');

            imageCanvas.style.display = 'block';

            showMessage('Ajustez le rectangle, puis validez.', 'info');

        });


        function setupInteractiveStage() {

            imageCanvas.width = uploadedImage.width;

            imageCanvas.height = uploadedImage.height;

            const imgW = uploadedImage.width;

            const imgH = uploadedImage.height;

            currentSelectionRect.w = imgW / 2;

            currentSelectionRect.h = imgH / 2;


            switch (selectedQuadrantInfo.quadrantIndex) {

                case 0: currentSelectionRect.x = 0; currentSelectionRect.y = 0; break;

                case 1: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = 0; break;

                case 2: currentSelectionRect.x = 0; currentSelectionRect.y = imgH / 2; break;

                case 3: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = imgH / 2; break;

            }

            currentSelectionRect.isDefined = true;

            createHandles();

            drawCanvasWithSelectionAndHandles();

        }

        function drawCanvasWithSelectionAndHandles(isFinal = false) {

            if (!uploadedImage) return;

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            ctx.drawImage(uploadedImage, 0, 0);


            if (currentSelectionRect.isDefined) {

                if (isFinal) {

                    ctx.fillStyle = 'white';

                    ctx.fillRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    removeHandles();

                } else {

                    ctx.strokeStyle = 'rgba(255, 0, 0, 0.8)';

                    ctx.lineWidth = 2;

                    ctx.strokeRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    updateHandlesPositions();

                }

            }

        }


        function createHandles() {

            removeHandles();

            const handleTypes = ['tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br'];

            handleTypes.forEach(type => {

                const handle = document.createElement('div');

                handle.classList.add('handle');

                handle.dataset.type = type;

                canvasContainer.appendChild(handle);

                handles.push(handle);

            });

            updateHandlesPositions();

        }


        function removeHandles() {

            handles.forEach(h => h.remove());

            handles = [];

        }


        function updateHandlesPositions() {

            if (!currentSelectionRect.isDefined || handles.length === 0 || !imageCanvas.offsetParent) return; // Vérif que canvas est visible

            const { x, y, w, h } = currentSelectionRect;

            const canvasRect = imageCanvas.getBoundingClientRect();


            const scaleX = canvasRect.width / imageCanvas.width;

            const scaleY = canvasRect.height / imageCanvas.height;


            const positions = {

                'tl': { left: x * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                't':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'tr': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'l':  { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'r':  { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'bl': { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'b':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'br': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 }

            };


            handles.forEach(handle => {

                const type = handle.dataset.type;

                handle.style.left = `${canvasRect.left + positions[type].left - canvasContainer.getBoundingClientRect().left}px`;

                handle.style.top = `${canvasRect.top + positions[type].top - canvasContainer.getBoundingClientRect().top}px`;

                handle.style.cursor = getResizeCursor(type);

                handle.style.display = 'block';

            });

        }

        function getResizeCursor(handleType) {

            switch (handleType) {

                case 'tl': case 'br': return 'nwse-resize';

                case 'tr': case 'bl': return 'nesw-resize';

                case 't':  case 'b':  return 'ns-resize';

                case 'l':  case 'r':  return 'ew-resize';

                default: return 'default';

            }

        }


        function getEventPosOnCanvas(event) {

            const rect = imageCanvas.getBoundingClientRect();

            let clientX, clientY;

            if (event.touches && event.touches.length > 0) {

                clientX = event.touches[0].clientX;

                clientY = event.touches[0].clientY;

            } else {

                clientX = event.clientX;

                clientY = event.clientY;

            }

            return {

                x: (clientX - rect.left) * (imageCanvas.width / rect.width),

                y: (clientY - rect.top) * (imageCanvas.height / rect.height)

            };

        }

        function handleInteractionStart(e) {

            if (!currentSelectionRect.isDefined || !uploadedImage) return;

            const target = e.target;

            const eventPos = getEventPosOnCanvas(e);


            if (target.classList.contains('handle')) {

                activeDragAction = `resize-${target.dataset.type}`;

            } else if (target === imageCanvas &&

                       eventPos.x >= currentSelectionRect.x && eventPos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                       eventPos.y >= currentSelectionRect.y && eventPos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                activeDragAction = 'move';

            } else {

                return;

            }

            if (activeDragAction && e.cancelable) { // Empêcher le défilement SEULEMENT si une action de drag/resize commence

                 e.preventDefault();

            }


            dragStartCoords = eventPos;

            rectStartCoords = { ...currentSelectionRect };

        }


        function handleInteractionMove(e) {

            if (!activeDragAction || !currentSelectionRect.isDefined) {

                 // Gérer le curseur pour la souris même si pas en drag

                if (e.type === 'mousemove' && (e.target === imageCanvas || e.target.classList.contains('handle'))) {

                    const mousePos = getEventPosOnCanvas(e);

                    let cursor = 'default';

                    if (currentSelectionRect.isDefined) {

                        for (const handle of handles) {

                            const handleRect = handle.getBoundingClientRect();

                            const canvasRect = imageCanvas.getBoundingClientRect();

                            const handleCanvasX = (handleRect.left - canvasRect.left + HANDLE_SIZE/2) * (imageCanvas.width / canvasRect.width);

                            const handleCanvasY = (handleRect.top - canvasRect.top + HANDLE_SIZE/2) * (imageCanvas.height / canvasRect.height);

                            if (Math.abs(mousePos.x - handleCanvasX) < HANDLE_SIZE && Math.abs(mousePos.y - handleCanvasY) < HANDLE_SIZE * 1.5) { // Tolérance augmentée

                                cursor = getResizeCursor(handle.dataset.type);

                                break;

                            }

                        }

                        if (cursor === 'default' &&

                            mousePos.x >= currentSelectionRect.x && mousePos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                            mousePos.y >= currentSelectionRect.y && mousePos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                            cursor = 'move';

                        }

                    }

                    imageCanvas.style.cursor = cursor;

                }

                return;

            }


            if (e.cancelable) {

                e.preventDefault(); // Empêcher le défilement pendant le drag/resize actif

            }


            const eventPos = getEventPosOnCanvas(e);

            const deltaX = eventPos.x - dragStartCoords.x;

            const deltaY = eventPos.y - dragStartCoords.y;

            let { x, y, w, h } = rectStartCoords;


            if (activeDragAction === 'move') {

                x += deltaX;

                y += deltaY;

            } else if (activeDragAction.startsWith('resize-')) {

                const type = activeDragAction.split('-')[1];

                if (type.includes('l')) { x += deltaX; w -= deltaX; }

                if (type.includes('r')) { w += deltaX; }

                if (type.includes('t')) { y += deltaY; h -= deltaY; }

                if (type.includes('b')) { h += deltaY; }

            }


            const minSize = 20;

            if (w < minSize) {

                if (activeDragAction.includes('l') || activeDragAction.includes('tl') || activeDragAction.includes('bl')) x = rectStartCoords.x + rectStartCoords.w - minSize;

                w = minSize;

            }

            if (h < minSize) {

                if (activeDragAction.includes('t') || activeDragAction.includes('tl') || activeDragAction.includes('tr')) y = rectStartCoords.y + rectStartCoords.h - minSize;

                h = minSize;

            }

            x = Math.max(0, Math.min(x, imageCanvas.width - w));

            y = Math.max(0, Math.min(y, imageCanvas.height - h));

            w = Math.min(w, imageCanvas.width - x);

            h = Math.min(h, imageCanvas.height - y);


            currentSelectionRect = { x, y, w, h, isDefined: true };

            drawCanvasWithSelectionAndHandles();

        }


        function handleInteractionEnd(e) {

            if (activeDragAction) {

                activeDragAction = null;

                imageCanvas.style.cursor = 'default';

                updateHandlesPositions();

            }

        }


        // Attacher les écouteurs d'événements pour souris et tactile

        canvasContainer.addEventListener('mousedown', handleInteractionStart);

        document.addEventListener('mousemove', handleInteractionMove);

        document.addEventListener('mouseup', handleInteractionEnd);


        canvasContainer.addEventListener('touchstart', handleInteractionStart, { passive: false });

        document.addEventListener('touchmove', handleInteractionMove, { passive: false });

        document.addEventListener('touchend', handleInteractionEnd);

        applyAndDownloadButton.addEventListener('click', () => {

            if (!currentSelectionRect.isDefined || !uploadedImage) {

                showMessage('Zone non définie ou image non chargée.', 'error');

                return;

            }

            drawCanvasWithSelectionAndHandles(true);


            try {

                const dataURL = imageCanvas.toDataURL('image/png');

                const link = document.createElement('a');

                link.href = dataURL;

                link.download = 'bordereau_modifie.png';

                document.body.appendChild(link);

                link.click();

                document.body.removeChild(link);

                showMessage('Téléchargement lancé...', 'success');


            } catch (err) {

                showMessage('Erreur téléchargement. Essayez d\'enregistrer l\'image manuellement.', 'error');

                try {

                    const dataURL = imageCanvas.toDataURL('image/png');

                    const newWindow = window.open();

                    if (newWindow) {

                        newWindow.document.open();

                        newWindow.document.write(

                            '<!DOCTYPE html><html lang="fr"><head><meta name="viewport" content="width=device-width, initial-scale=1.0">' +

                            '<title>Image Modifiée</title><style>body{margin:0; display:flex; flex-direction:column; justify-content:center; align-items:center; min-height:100vh; background-color:#f0f0f0; text-align:center; font-family:sans-serif;} img{max-width:95%; max-height:80vh; object-fit:contain; box-shadow: 0 0 10px rgba(0,0,0,0.2); margin-bottom:15px;} p{color:#333;}</style></head>' +

                            '<body><img src="' + dataURL + '" alt="Image modifiée"/><p>Sur mobile (iOS/Android): Appuyez longuement sur l\'image et choisissez "Ajouter à Photos" ou "Télécharger l\'image".<br>Sur ordinateur: Clic droit > "Enregistrer l\'image sous...".</p></body></html>'

                        );

                        newWindow.document.close();

                        showMessage('Image prête dans un nouvel onglet. Suivez les instructions pour enregistrer.', 'info');

                    } else {

                        showMessage('Impossible d\'ouvrir un nouvel onglet. Vérifiez les bloqueurs de pop-up.', 'error');

                    }

                } catch (fallbackError) {

                     showMessage('Erreur critique de téléchargement: ' + (fallbackError.message || fallbackError), 'error');

                }

            }

            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

            removeHandles();

        });


        function resetCanvasAndState() {

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            imageCanvas.style.display = 'none';

            removeHandles();

            uploadedImage = null;

            pdfDoc = null;

            currentSelectionRect.isDefined = false;

            activeDragAction = null;

            initiateProcessingButton.disabled = true;

            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

            imageUpload.value = '';

        }


        checkInitiateButtonState();

        window.addEventListener('resize', () => {

            if (currentSelectionRect.isDefined && handles.length > 0 && imageCanvas.offsetParent) {

                updateHandlesPositions();

            }

        });


    </script>

</body>

</html>





<!DOCTYPE html>

<html lang="fr">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

    <title>Suppresseur de Publicités pour Bordereaux (Batch)</title>

    <script src="https://cdn.tailwindcss.com"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>

    <script>

        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';

    </script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>

    <style>

        body {

            font-family: 'Inter', sans-serif;

            overscroll-behavior-y: contain;

        }

        .a4-paper {

            border: 2px solid #000;

            display: grid;

            grid-template-columns: repeat(2, 1fr);

            grid-template-rows: repeat(2, 1fr);

            cursor: pointer;

            background-color: #f0f0f0;

        }

        .quadrant {

            border: 1px dashed #666;

            display: flex;

            justify-content: center;

            align-items: center;

            transition: background-color 0.2s ease-in-out;

        }

        .quadrant:hover {

            background-color: #e0e0e0;

        }

        .quadrant.selected {

            background-color: #ffffff !important;

            border: 2px solid #3b82f6;

        }

        #message-box {

            position: fixed;

            top: 20px;

            left: 50%;

            transform: translateX(-50%);

            padding: 10px 20px;

            border-radius: 8px;

            color: white;

            z-index: 1000;

            display: none;

            box-shadow: 0 4px 6px rgba(0,0,0,0.1);

        }

        #message-box.success { background-color: #28a745; }

        #message-box.error { background-color: #dc3545; }

        #message-box.info { background-color: #17a2b8; }


        .quadrant-icon { font-size: 10px; color: #888; }

        .portrait { width: 100px; height: 141.4px; }

        .landscape { width: 141.4px; height: 100px; }


        #imageCanvas {

            touch-action: none;

        }

        .handle {

            position: absolute;

            width: 14px; /* Encore un peu plus grand pour le tactile */

            height: 14px;

            background-color: rgba(255, 0, 0, 0.6);

            border: 1px solid rgba(255, 255, 255, 0.8);

            border-radius: 50%;

            z-index: 10;

            touch-action: none;

        }

        /* Style pour le loader */

        #loader {

            border: 5px solid #f3f3f3; /* Light grey */

            border-top: 5px solid #3498db; /* Blue */

            border-radius: 50%;

            width: 40px;

            height: 40px;

            animation: spin 1s linear infinite;

            margin: 10px auto;

            display: none; /* Caché par défaut */

        }

        @keyframes spin {

            0% { transform: rotate(0deg); }

            100% { transform: rotate(360deg); }

        }

    </style>

</head>

<body class="bg-gray-100 min-h-screen flex flex-col items-center justify-center p-4">


    <div id="message-box"></div>


    <div class="bg-white p-6 md:p-8 rounded-xl shadow-2xl w-full max-w-3xl">

        <header class="mb-6 text-center">

            <h1 class="text-2xl md:text-3xl font-bold text-gray-800">Suppresseur de Publicités pour Bordereaux (Batch)</h1>

            <p class="text-gray-600 mt-2">Masquez les pubs sur plusieurs fichiers à la fois.</p>

        </header>


        <main>

            <section id="step1Selection" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">1. Zone initiale approximative :</h2>

                 <div class="flex flex-col sm:flex-row justify-center items-center gap-6 sm:gap-10">

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Portrait</p>

                        <div id="portrait-paper" class="a4-paper portrait rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="portrait" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Paysage</p>

                        <div id="landscape-paper" class="a4-paper landscape rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="landscape" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                </div>

            </section>


            <section id="step2Upload" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">2. Téléversez vos fichiers (Images ou PDF) :</h2>

                <div class="flex flex-col items-center">

                    <input type="file" id="fileUpload" accept="image/*,application/pdf" multiple class="block w-full max-w-md text-sm text-gray-500

                        file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold

                        file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100

                        mb-2 p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">

                    <div class="my-2 text-sm text-gray-600">Format de sortie pour images :

                        <select id="imageOutputFormat" class="ml-2 p-1 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-blue-500">

                            <option value="image/png">PNG (Défaut)</option>

                            <option value="image/jpeg">JPEG</option>

                        </select>

                    </div>

                </div>

            </section>

            <section id="step3Adjust" class="mb-6 text-center">

                 <h2 class="text-xl font-semibold text-gray-700 mb-3">3. Ajustez la zone sur le 1er fichier et validez :</h2>

                <button id="initiateProcessingButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out disabled:opacity-50" disabled>

                    Afficher 1er Fichier et Ajuster Zone

                </button>

                <div id="canvasContainer" class="relative flex flex-col items-center mt-4">

                    <canvas id="imageCanvas" class="border border-gray-400 rounded-lg shadow-md max-w-full h-auto" style="display: none;"></canvas>

                </div>

                <div id="loader"></div>

                <button id="applyAndDownloadButton" class="hidden mt-4 bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out">

                    Appliquer à Tous et Télécharger

                </button>

            </section>

        </main>

    </div>


    <script>

        const { jsPDF } = window.jspdf; // Accéder à jsPDF depuis l'objet global window.jspdf


        // Références DOM

        const allQuadrants = document.querySelectorAll('.quadrant');

        const fileUpload = document.getElementById('fileUpload');

        const imageOutputFormatSelect = document.getElementById('imageOutputFormat');

        const initiateProcessingButton = document.getElementById('initiateProcessingButton');

        const imageCanvas = document.getElementById('imageCanvas');

        const canvasContainer = document.getElementById('canvasContainer');

        const applyAndDownloadButton = document.getElementById('applyAndDownloadButton');

        const messageBox = document.getElementById('message-box');

        const loader = document.getElementById('loader');

        const ctx = imageCanvas.getContext('2d');


        // État global

        let selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

        let allUploadedFiles = []; // Stocke tous les fichiers téléversés

        let currentFileForAdjustment = null; // L'objet Image ou PDF du premier fichier pour l'ajustement

        let currentSelectionRect = { x: 0, y: 0, w: 0, h: 0, isDefined: false };

        let activeDragAction = null;

        let dragStartCoords = { x: 0, y: 0 };

        let rectStartCoords = { x: 0, y: 0, w: 0, h: 0 };

        const HANDLE_SIZE = 14;

        let handles = [];


        function showMessage(text, type = 'info', duration = 4000) {

            messageBox.textContent = text;

            messageBox.className = '';

            messageBox.classList.add(type);

            messageBox.style.display = 'block';

            setTimeout(() => { messageBox.style.display = 'none'; }, duration);

        }


        function checkInitiateButtonState() {

            initiateProcessingButton.disabled = !(allUploadedFiles.length > 0 && selectedQuadrantInfo.quadrantIndex !== null);

        }


        allQuadrants.forEach(quadrant => {

            quadrant.addEventListener('click', () => {

                allQuadrants.forEach(q => q.classList.remove('selected'));

                quadrant.classList.add('selected');

                selectedQuadrantInfo.orientation = quadrant.dataset.orientation;

                selectedQuadrantInfo.quadrantIndex = parseInt(quadrant.dataset.quadrant);

                checkInitiateButtonState();

                showMessage(`Zone initiale: ${selectedQuadrantInfo.orientation}, Q${selectedQuadrantInfo.quadrantIndex + 1}. Téléversez des fichiers.`, 'info');

            });

        });


        fileUpload.addEventListener('change', (event) => {

            if (event.target.files.length === 0) {

                allUploadedFiles = [];

                currentFileForAdjustment = null;

                resetCanvasAndState(false); // Ne pas réinitialiser la sélection de quadrant

                checkInitiateButtonState();

                return;

            }

            allUploadedFiles = Array.from(event.target.files);

            currentFileForAdjustment = null; // Sera défini lors du clic sur "Afficher"

            resetCanvasAndState(false);


            if (allUploadedFiles.length > 0) {

                showMessage(`${allUploadedFiles.length} fichier(s) sélectionné(s). Prêt à ajuster le 1er.`, 'success');

            }

            checkInitiateButtonState();

        });

        async function loadFileForAdjustment(file) {

            return new Promise((resolve, reject) => {

                if (file.type.startsWith('image/')) {

                    const reader = new FileReader();

                    reader.onload = (e) => {

                        const img = new Image();

                        img.onload = () => resolve(img);

                        img.onerror = () => reject(new Error('Erreur chargement image pour ajustement.'));

                        img.src = e.target.result;

                    };

                    reader.onerror = () => reject(new Error('Erreur lecture fichier image.'));

                    reader.readAsDataURL(file);

                } else if (file.type === 'application/pdf') {

                    const fileReader = new FileReader();

                    fileReader.onload = function() {

                        const typedarray = new Uint8Array(this.result);

                        pdfjsLib.getDocument(typedarray).promise.then(pdfDoc_ => {

                            // Pour l'ajustement, on ne rend que la première page du premier PDF

                            pdfDoc_.getPage(1).then(page => {

                                const viewport = page.getViewport({ scale: 2.0 }); // Échelle suffisante pour l'aperçu

                                const tempCanvas = document.createElement('canvas');

                                const tempCtx = tempCanvas.getContext('2d');

                                tempCanvas.height = viewport.height;

                                tempCanvas.width = viewport.width;

                                page.render({ canvasContext: tempCtx, viewport: viewport }).promise.then(() => {

                                    const img = new Image();

                                    img.onload = () => resolve(img); // On passe une image de la page PDF

                                    img.onerror = () => reject(new Error('Erreur conversion 1ère page PDF en image.'));

                                    img.src = tempCanvas.toDataURL('image/png');

                                }).catch(reject);

                            }).catch(reject);

                        }).catch(reject);

                    };

                    fileReader.onerror = () => reject(new Error('Erreur lecture fichier PDF.'));

                    fileReader.readAsArrayBuffer(file);

                } else {

                    reject(new Error('Type de fichier non supporté pour l\'ajustement.'));

                }

            });

        }


        initiateProcessingButton.addEventListener('click', async () => {

            if (allUploadedFiles.length === 0 || selectedQuadrantInfo.quadrantIndex === null) {

                showMessage('Sélectionnez zone ET téléversez au moins un fichier.', 'error');

                return;

            }

            loader.style.display = 'block';

            initiateProcessingButton.disabled = true;


            try {

                currentFileForAdjustment = await loadFileForAdjustment(allUploadedFiles[0]);

                setupInteractiveStage(currentFileForAdjustment); // Passe l'image chargée (ou l'image de la 1ère page PDF)

                initiateProcessingButton.classList.add('hidden');

                applyAndDownloadButton.classList.remove('hidden');

                imageCanvas.style.display = 'block';

                showMessage('Ajustez le rectangle sur ce 1er fichier, puis validez.', 'info');

            } catch (error) {

                showMessage(error.message || 'Erreur chargement du 1er fichier.', 'error');

                console.error("Error loading first file for adjustment:", error);

            } finally {

                loader.style.display = 'none';

                initiateProcessingButton.disabled = false; // Réactiver en cas d'erreur

            }

        });


        function setupInteractiveStage(imageToAdjust) { // imageToAdjust est un objet Image

            imageCanvas.width = imageToAdjust.width;

            imageCanvas.height = imageToAdjust.height;

            const imgW = imageToAdjust.width;

            const imgH = imageToAdjust.height;

            currentSelectionRect.w = imgW / 2;

            currentSelectionRect.h = imgH / 2;


            switch (selectedQuadrantInfo.quadrantIndex) {

                case 0: currentSelectionRect.x = 0; currentSelectionRect.y = 0; break;

                case 1: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = 0; break;

                case 2: currentSelectionRect.x = 0; currentSelectionRect.y = imgH / 2; break;

                case 3: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = imgH / 2; break;

            }

            currentSelectionRect.isDefined = true;

            createHandles();

            drawCanvasWithSelectionAndHandles(imageToAdjust); // Dessine l'image passée

        }

        function drawCanvasWithSelectionAndHandles(imageToDraw, isFinal = false) {

            if (!imageToDraw) return; // S'assurer qu'il y a une image à dessiner

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            ctx.drawImage(imageToDraw, 0, 0, imageCanvas.width, imageCanvas.height); // S'assurer que l'image est dessinée aux dimensions du canvas


            if (currentSelectionRect.isDefined) {

                if (isFinal) {

                    ctx.fillStyle = 'white';

                    ctx.fillRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    removeHandles();

                } else {

                    ctx.strokeStyle = 'rgba(255, 0, 0, 0.8)';

                    ctx.lineWidth = 2;

                    ctx.strokeRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    updateHandlesPositions();

                }

            }

        }


        function createHandles() {

            removeHandles();

            const handleTypes = ['tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br'];

            handleTypes.forEach(type => {

                const handle = document.createElement('div');

                handle.classList.add('handle');

                handle.dataset.type = type;

                canvasContainer.appendChild(handle);

                handles.push(handle);

            });

            updateHandlesPositions();

        }


        function removeHandles() {

            handles.forEach(h => h.remove());

            handles = [];

        }


        function updateHandlesPositions() {

            if (!currentSelectionRect.isDefined || handles.length === 0 || !imageCanvas.offsetParent) return;

            const { x, y, w, h } = currentSelectionRect;

            const canvasRect = imageCanvas.getBoundingClientRect();


            const scaleX = canvasRect.width / imageCanvas.width;

            const scaleY = canvasRect.height / imageCanvas.height;


            const positions = {

                'tl': { left: x * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                't':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'tr': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'l':  { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'r':  { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'bl': { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'b':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'br': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 }

            };


            handles.forEach(handle => {

                const type = handle.dataset.type;

                // Positionnement relatif au canvasContainer

                handle.style.left = `${canvasRect.left + positions[type].left - canvasContainer.getBoundingClientRect().left}px`;

                handle.style.top = `${canvasRect.top + positions[type].top - canvasContainer.getBoundingClientRect().top}px`;

                handle.style.cursor = getResizeCursor(type);

                handle.style.display = 'block';

            });

        }

        function getResizeCursor(handleType) { /* ... (inchangé) ... */

            switch (handleType) {

                case 'tl': case 'br': return 'nwse-resize';

                case 'tr': case 'bl': return 'nesw-resize';

                case 't':  case 'b':  return 'ns-resize';

                case 'l':  case 'r':  return 'ew-resize';

                default: return 'default';

            }

        }

        function getEventPosOnCanvas(event) { /* ... (inchangé) ... */

            const rect = imageCanvas.getBoundingClientRect();

            let clientX, clientY;

            if (event.touches && event.touches.length > 0) {

                clientX = event.touches[0].clientX;

                clientY = event.touches[0].clientY;

            } else {

                clientX = event.clientX;

                clientY = event.clientY;

            }

            return {

                x: (clientX - rect.left) * (imageCanvas.width / rect.width),

                y: (clientY - rect.top) * (imageCanvas.height / rect.height)

            };

        }

        function handleInteractionStart(e) { /* ... (inchangé, utilise currentFileForAdjustment pour la condition) ... */

            if (!currentSelectionRect.isDefined || !currentFileForAdjustment) return; // Modifié ici

            const target = e.target;

            const eventPos = getEventPosOnCanvas(e);


            if (target.classList.contains('handle')) {

                activeDragAction = `resize-${target.dataset.type}`;

            } else if (target === imageCanvas &&

                       eventPos.x >= currentSelectionRect.x && eventPos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                       eventPos.y >= currentSelectionRect.y && eventPos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                activeDragAction = 'move';

            } else {

                return;

            }

            if (activeDragAction && e.cancelable) {

                 e.preventDefault();

            }

            dragStartCoords = eventPos;

            rectStartCoords = { ...currentSelectionRect };

        }

        function handleInteractionMove(e) { /* ... (inchangé, utilise currentFileForAdjustment pour dessiner) ... */

            if (!activeDragAction || !currentSelectionRect.isDefined) {

                if (e.type === 'mousemove' && (e.target === imageCanvas || e.target.classList.contains('handle'))) {

                    const mousePos = getEventPosOnCanvas(e);

                    let cursor = 'default';

                    if (currentSelectionRect.isDefined) {

                        for (const handle of handles) {

                            const handleRect = handle.getBoundingClientRect();

                            const canvasRect = imageCanvas.getBoundingClientRect();

                            const handleCanvasX = (handleRect.left - canvasRect.left + HANDLE_SIZE/2) * (imageCanvas.width / canvasRect.width);

                            const handleCanvasY = (handleRect.top - canvasRect.top + HANDLE_SIZE/2) * (imageCanvas.height / canvasRect.height);

                            if (Math.abs(mousePos.x - handleCanvasX) < HANDLE_SIZE && Math.abs(mousePos.y - handleCanvasY) < HANDLE_SIZE * 1.5) {

                                cursor = getResizeCursor(handle.dataset.type);

                                break;

                            }

                        }

                        if (cursor === 'default' &&

                            mousePos.x >= currentSelectionRect.x && mousePos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                            mousePos.y >= currentSelectionRect.y && mousePos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                            cursor = 'move';

                        }

                    }

                    imageCanvas.style.cursor = cursor;

                }

                return;

            }


            if (e.cancelable) {

                e.preventDefault();

            }


            const eventPos = getEventPosOnCanvas(e);

            const deltaX = eventPos.x - dragStartCoords.x;

            const deltaY = eventPos.y - dragStartCoords.y;

            let { x, y, w, h } = rectStartCoords;


            if (activeDragAction === 'move') {

                x += deltaX;

                y += deltaY;

            } else if (activeDragAction.startsWith('resize-')) {

                const type = activeDragAction.split('-')[1];

                if (type.includes('l')) { x += deltaX; w -= deltaX; }

                if (type.includes('r')) { w += deltaX; }

                if (type.includes('t')) { y += deltaY; h -= deltaY; }

                if (type.includes('b')) { h += deltaY; }

            }


            const minSize = 20;

            if (w < minSize) {

                if (activeDragAction.includes('l') || activeDragAction.includes('tl') || activeDragAction.includes('bl')) x = rectStartCoords.x + rectStartCoords.w - minSize;

                w = minSize;

            }

            if (h < minSize) {

                if (activeDragAction.includes('t') || activeDragAction.includes('tl') || activeDragAction.includes('tr')) y = rectStartCoords.y + rectStartCoords.h - minSize;

                h = minSize;

            }

            x = Math.max(0, Math.min(x, imageCanvas.width - w));

            y = Math.max(0, Math.min(y, imageCanvas.height - h));

            w = Math.min(w, imageCanvas.width - x);

            h = Math.min(h, imageCanvas.height - y);


            currentSelectionRect = { x, y, w, h, isDefined: true };

            drawCanvasWithSelectionAndHandles(currentFileForAdjustment); // Redessine avec l'image du 1er fichier

        }

        function handleInteractionEnd(e) { /* ... (inchangé) ... */

             if (activeDragAction) {

                activeDragAction = null;

                imageCanvas.style.cursor = 'default';

                updateHandlesPositions();

            }

        }


        canvasContainer.addEventListener('mousedown', handleInteractionStart);

        document.addEventListener('mousemove', handleInteractionMove);

        document.addEventListener('mouseup', handleInteractionEnd);

        canvasContainer.addEventListener('touchstart', handleInteractionStart, { passive: false });

        document.addEventListener('touchmove', handleInteractionMove, { passive: false });

        document.addEventListener('touchend', handleInteractionEnd);

        applyAndDownloadButton.addEventListener('click', async () => {

            if (!currentSelectionRect.isDefined || allUploadedFiles.length === 0) {

                showMessage('Zone non définie ou aucun fichier chargé.', 'error');

                return;

            }

            loader.style.display = 'block';

            applyAndDownloadButton.disabled = true;

            let filesProcessedCount = 0;


            for (let i = 0; i < allUploadedFiles.length; i++) {

                const file = allUploadedFiles[i];

                const originalFilename = file.name.substring(0, file.name.lastIndexOf('.')) || file.name;

                showMessage(`Traitement de ${file.name} (${i+1}/${allUploadedFiles.length})...`, 'info', 60000); // Longue durée pour le message


                try {

                    if (file.type.startsWith('image/')) {

                        const img = await loadFileForAdjustment(file); // Réutiliser pour charger l'image

                        const tempCanvas = document.createElement('canvas');

                        const tempCtx = tempCanvas.getContext('2d');

                        tempCanvas.width = img.width;

                        tempCanvas.height = img.height;

                        tempCtx.drawImage(img, 0, 0);

                        tempCtx.fillStyle = 'white';

                        tempCtx.fillRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                        const outputFormat = imageOutputFormatSelect.value;

                        const extension = outputFormat === 'image/jpeg' ? 'jpg' : 'png';

                        const dataURL = tempCanvas.toDataURL(outputFormat, outputFormat === 'image/jpeg' ? 0.9 : undefined); // Qualité pour JPEG

                        triggerDownload(dataURL, `${originalFilename}_modifie.${extension}`);

                        filesProcessedCount++;


                    } else if (file.type === 'application/pdf') {

                        const pdfOutput = new jsPDF();

                        const fileReader = new FileReader();

                        // Promesse pour lire le fichier PDF

                        const arrayBuffer = await new Promise((resolve, reject) => {

                            fileReader.onload = () => resolve(fileReader.result);

                            fileReader.onerror = reject;

                            fileReader.readAsArrayBuffer(file);

                        });


                        const pdfDoc = await pdfjsLib.getDocument(new Uint8Array(arrayBuffer)).promise;

                        const numPages = pdfDoc.numPages;


                        for (let j = 1; j <= numPages; j++) {

                            const page = await pdfDoc.getPage(j);

                            const viewport = page.getViewport({ scale: 2.0 }); // Bonne échelle pour la qualité

                            const tempCanvas = document.createElement('canvas');

                            const tempCtx = tempCanvas.getContext('2d');

                            tempCanvas.width = viewport.width;

                            tempCanvas.height = viewport.height;


                            await page.render({ canvasContext: tempCtx, viewport: viewport }).promise;

                            tempCtx.fillStyle = 'white';

                            tempCtx.fillRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                            const pageDataUrl = tempCanvas.toDataURL('image/png');

                            if (j > 1) {

                                pdfOutput.addPage([viewport.width, viewport.height]); // Utiliser les dimensions exactes

                            }

                            pdfOutput.addImage(pageDataUrl, 'PNG', 0, 0, viewport.width, viewport.height);

                        }

                        pdfOutput.save(`${originalFilename}_modifie.pdf`);

                        filesProcessedCount++;

                    }

                    await new Promise(resolve => setTimeout(resolve, 200)); // Petite pause entre les téléchargements

                } catch (err) {

                    console.error("Erreur traitement fichier:", file.name, err);

                    showMessage(`Erreur avec ${file.name}: ${err.message || 'Inconnue'}`, 'error');

                }

            }

            loader.style.display = 'none';

            applyAndDownloadButton.disabled = false;

            if (filesProcessedCount > 0) {

                showMessage(`${filesProcessedCount} fichier(s) traité(s) et téléchargé(s).`, 'success');

            } else if (allUploadedFiles.length > 0) {

                 showMessage(`Aucun fichier n'a pu être traité avec succès.`, 'error');

            }


            // Réinitialisation partielle pour permettre un nouveau lot avec la même sélection

            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

            // Ne pas appeler resetCanvasAndState() complètement pour garder la sélection

            // fileUpload.value = ''; // L'utilisateur peut vouloir re-téléverser

            // allUploadedFiles = []; // Sera réinitialisé au prochain téléversement

        });


        function triggerDownload(dataURL, filename) {

            const link = document.createElement('a');

            link.href = dataURL;

            link.download = filename;

            document.body.appendChild(link);

            link.click();

            document.body.removeChild(link);

        }


        function resetCanvasAndState(resetQuadrantSelection = true) {

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            imageCanvas.style.display = 'none';

            removeHandles();

            currentFileForAdjustment = null; // Réinitialiser l'image d'ajustement

            // allUploadedFiles = []; // Ne pas réinitialiser ici, se fait au 'change' de fileUpload

            currentSelectionRect.isDefined = false;

            activeDragAction = null;

            initiateProcessingButton.disabled = true; // Sera réactivé par checkInitiateButtonState

            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

            // fileUpload.value = ''; // Ne pas réinitialiser ici pour permettre à l'utilisateur de voir les fichiers sélectionnés


            if (resetQuadrantSelection) {

                allQuadrants.forEach(q => q.classList.remove('selected'));

                selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

            }

        }


        checkInitiateButtonState();

        window.addEventListener('resize', () => {

            if (currentSelectionRect.isDefined && handles.length > 0 && imageCanvas.offsetParent) {

                updateHandlesPositions();

            }

        });


    </script>

</body>

</html>




<!DOCTYPE html>

<html lang="fr">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

    <title>Suppresseur de Publicités pour Bordereaux (Batch)</title>

    <script src="https://cdn.tailwindcss.com"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>

    <script>

        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';

    </script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>

    <style>

        body {

            font-family: 'Inter', sans-serif;

            overscroll-behavior-y: contain;

        }

        .a4-paper {

            border: 2px solid #000;

            display: grid;

            grid-template-columns: repeat(2, 1fr);

            grid-template-rows: repeat(2, 1fr);

            cursor: pointer;

            background-color: #f0f0f0;

        }

        .quadrant {

            border: 1px dashed #666;

            display: flex;

            justify-content: center;

            align-items: center;

            transition: background-color 0.2s ease-in-out;

        }

        .quadrant:hover {

            background-color: #e0e0e0;

        }

        .quadrant.selected {

            background-color: #ffffff !important;

            border: 2px solid #3b82f6;

        }

        #message-box {

            position: fixed;

            top: 20px;

            left: 50%;

            transform: translateX(-50%);

            padding: 10px 20px;

            border-radius: 8px;

            color: white;

            z-index: 1000;

            display: none;

            box-shadow: 0 4px 6px rgba(0,0,0,0.1);

        }

        #message-box.success { background-color: #28a745; }

        #message-box.error { background-color: #dc3545; }

        #message-box.info { background-color: #17a2b8; }


        .quadrant-icon { font-size: 10px; color: #888; }

        .portrait { width: 100px; height: 141.4px; }

        .landscape { width: 141.4px; height: 100px; }


        #imageCanvas {

            touch-action: none;

        }

        .handle {

            position: absolute;

            width: 14px;

            height: 14px;

            background-color: rgba(255, 0, 0, 0.6);

            border: 1px solid rgba(255, 255, 255, 0.8);

            border-radius: 50%;

            z-index: 10;

            touch-action: none;

        }

        #loader {

            border: 5px solid #f3f3f3;

            border-top: 5px solid #3498db;

            border-radius: 50%;

            width: 40px;

            height: 40px;

            animation: spin 1s linear infinite;

            margin: 10px auto;

            display: none;

        }

        @keyframes spin {

            0% { transform: rotate(0deg); }

            100% { transform: rotate(360deg); }

        }

    </style>

</head>

<body class="bg-gray-100 min-h-screen flex flex-col items-center justify-center p-4">


    <div id="message-box"></div>


    <div class="bg-white p-6 md:p-8 rounded-xl shadow-2xl w-full max-w-3xl">

        <header class="mb-6 text-center">

            <h1 class="text-2xl md:text-3xl font-bold text-gray-800">Suppresseur de Publicités pour Bordereaux (Batch)</h1>

            <p class="text-gray-600 mt-2">Masquez les pubs sur plusieurs fichiers à la fois.</p>

        </header>


        <main>

            <section id="step1Selection" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">1. Zone initiale approximative :</h2>

                 <div class="flex flex-col sm:flex-row justify-center items-center gap-6 sm:gap-10">

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Portrait</p>

                        <div id="portrait-paper" class="a4-paper portrait rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="portrait" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Paysage</p>

                        <div id="landscape-paper" class="a4-paper landscape rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="landscape" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                </div>

            </section>


            <section id="step2Upload" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">2. Téléversez vos fichiers (Images ou PDF) :</h2>

                <div class="flex flex-col items-center">

                    <input type="file" id="fileUpload" accept="image/*,application/pdf" multiple class="block w-full max-w-md text-sm text-gray-500

                        file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold

                        file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100

                        mb-2 p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">

                    <div class="my-2 text-sm text-gray-600">Format de sortie pour images :

                        <select id="imageOutputFormat" class="ml-2 p-1 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-blue-500">

                            <option value="image/png">PNG (Défaut)</option>

                            <option value="image/jpeg">JPEG</option>

                            <option value="application/pdf">PDF (pour images)</option>

                        </select>

                    </div>

                </div>

            </section>

            <section id="step3Adjust" class="mb-6 text-center">

                 <h2 class="text-xl font-semibold text-gray-700 mb-3">3. Ajustez la zone sur le 1er fichier et validez :</h2>

                <button id="initiateProcessingButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out disabled:opacity-50" disabled>

                    Afficher 1er Fichier et Ajuster Zone

                </button>

                <div id="canvasContainer" class="relative flex flex-col items-center mt-4">

                    <canvas id="imageCanvas" class="border border-gray-400 rounded-lg shadow-md max-w-full h-auto" style="display: none;"></canvas>

                </div>

                <div id="loader"></div>

                <button id="applyAndDownloadButton" class="hidden mt-4 bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out">

                    Appliquer à Tous et Télécharger

                </button>

            </section>

        </main>

    </div>


    <script>

        const { jsPDF } = window.jspdf;


        // Références DOM

        const allQuadrants = document.querySelectorAll('.quadrant');

        const fileUpload = document.getElementById('fileUpload');

        const imageOutputFormatSelect = document.getElementById('imageOutputFormat');

        const initiateProcessingButton = document.getElementById('initiateProcessingButton');

        const imageCanvas = document.getElementById('imageCanvas');

        const canvasContainer = document.getElementById('canvasContainer');

        const applyAndDownloadButton = document.getElementById('applyAndDownloadButton');

        const messageBox = document.getElementById('message-box');

        const loader = document.getElementById('loader');

        const ctx = imageCanvas.getContext('2d');


        // État global

        let selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

        let allUploadedFiles = [];

        let currentFileForAdjustment = null;

        let currentSelectionRect = { x: 0, y: 0, w: 0, h: 0, isDefined: false };

        let activeDragAction = null;

        let dragStartCoords = { x: 0, y: 0 };

        let rectStartCoords = { x: 0, y: 0, w: 0, h: 0 };

        const HANDLE_SIZE = 14;

        let handles = [];


        function showMessage(text, type = 'info', duration = 4000) {

            messageBox.textContent = text;

            messageBox.className = '';

            messageBox.classList.add(type);

            messageBox.style.display = 'block';

            setTimeout(() => { messageBox.style.display = 'none'; }, duration);

        }


        function checkInitiateButtonState() {

            initiateProcessingButton.disabled = !(allUploadedFiles.length > 0 && selectedQuadrantInfo.quadrantIndex !== null);

        }


        allQuadrants.forEach(quadrant => {

            quadrant.addEventListener('click', () => {

                allQuadrants.forEach(q => q.classList.remove('selected'));

                quadrant.classList.add('selected');

                selectedQuadrantInfo.orientation = quadrant.dataset.orientation;

                selectedQuadrantInfo.quadrantIndex = parseInt(quadrant.dataset.quadrant);

                checkInitiateButtonState();

                showMessage(`Zone initiale: ${selectedQuadrantInfo.orientation}, Q${selectedQuadrantInfo.quadrantIndex + 1}. Téléversez des fichiers.`, 'info');

            });

        });


        fileUpload.addEventListener('change', (event) => {

            if (event.target.files.length === 0) {

                allUploadedFiles = [];

                currentFileForAdjustment = null;

                resetCanvasAndState(false);

                checkInitiateButtonState();

                return;

            }

            allUploadedFiles = Array.from(event.target.files);

            currentFileForAdjustment = null;

            resetCanvasAndState(false);


            if (allUploadedFiles.length > 0) {

                showMessage(`${allUploadedFiles.length} fichier(s) sélectionné(s). Prêt à ajuster le 1er.`, 'success');

            }

            checkInitiateButtonState();

        });

        async function loadFileForAdjustment(file) {

            return new Promise((resolve, reject) => {

                if (file.type.startsWith('image/')) {

                    const reader = new FileReader();

                    reader.onload = (e) => {

                        const img = new Image();

                        img.onload = () => resolve(img);

                        img.onerror = () => reject(new Error('Erreur chargement image pour ajustement.'));

                        img.src = e.target.result;

                    };

                    reader.onerror = () => reject(new Error('Erreur lecture fichier image.'));

                    reader.readAsDataURL(file);

                } else if (file.type === 'application/pdf') {

                    const fileReader = new FileReader();

                    fileReader.onload = function() {

                        const typedarray = new Uint8Array(this.result);

                        pdfjsLib.getDocument(typedarray).promise.then(pdfDoc_ => {

                            pdfDoc_.getPage(1).then(page => {

                                const viewport = page.getViewport({ scale: 2.0 });

                                const tempCanvas = document.createElement('canvas');

                                const tempCtx = tempCanvas.getContext('2d');

                                tempCanvas.height = viewport.height;

                                tempCanvas.width = viewport.width;

                                page.render({ canvasContext: tempCtx, viewport: viewport }).promise.then(() => {

                                    const img = new Image();

                                    img.onload = () => resolve(img);

                                    img.onerror = () => reject(new Error('Erreur conversion 1ère page PDF en image.'));

                                    img.src = tempCanvas.toDataURL('image/png');

                                }).catch(reject);

                            }).catch(reject);

                        }).catch(reject);

                    };

                    fileReader.onerror = () => reject(new Error('Erreur lecture fichier PDF.'));

                    fileReader.readAsArrayBuffer(file);

                } else {

                    reject(new Error('Type de fichier non supporté pour l\'ajustement.'));

                }

            });

        }


        initiateProcessingButton.addEventListener('click', async () => {

            if (allUploadedFiles.length === 0 || selectedQuadrantInfo.quadrantIndex === null) {

                showMessage('Sélectionnez zone ET téléversez au moins un fichier.', 'error');

                return;

            }

            loader.style.display = 'block';

            initiateProcessingButton.disabled = true;


            try {

                currentFileForAdjustment = await loadFileForAdjustment(allUploadedFiles[0]);

                setupInteractiveStage(currentFileForAdjustment);

                initiateProcessingButton.classList.add('hidden');

                applyAndDownloadButton.classList.remove('hidden');

                imageCanvas.style.display = 'block';

                showMessage('Ajustez le rectangle sur ce 1er fichier, puis validez.', 'info');

            } catch (error) {

                showMessage(error.message || 'Erreur chargement du 1er fichier.', 'error');

                console.error("Error loading first file for adjustment:", error);

            } finally {

                loader.style.display = 'none';

                initiateProcessingButton.disabled = false;

            }

        });


        function setupInteractiveStage(imageToAdjust) {

            imageCanvas.width = imageToAdjust.width;

            imageCanvas.height = imageToAdjust.height;

            const imgW = imageToAdjust.width;

            const imgH = imageToAdjust.height;

            currentSelectionRect.w = imgW / 2;

            currentSelectionRect.h = imgH / 2;


            switch (selectedQuadrantInfo.quadrantIndex) {

                case 0: currentSelectionRect.x = 0; currentSelectionRect.y = 0; break;

                case 1: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = 0; break;

                case 2: currentSelectionRect.x = 0; currentSelectionRect.y = imgH / 2; break;

                case 3: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = imgH / 2; break;

            }

            currentSelectionRect.isDefined = true;

            createHandles();

            drawCanvasWithSelectionAndHandles(imageToAdjust);

        }

        function drawCanvasWithSelectionAndHandles(imageToDraw, isFinal = false) {

            if (!imageToDraw) return;

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            ctx.drawImage(imageToDraw, 0, 0, imageCanvas.width, imageCanvas.height);


            if (currentSelectionRect.isDefined) {

                if (isFinal) {

                    ctx.fillStyle = 'white';

                    ctx.fillRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    removeHandles();

                } else {

                    ctx.strokeStyle = 'rgba(255, 0, 0, 0.8)';

                    ctx.lineWidth = 2;

                    ctx.strokeRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    updateHandlesPositions();

                }

            }

        }


        function createHandles() {

            removeHandles();

            const handleTypes = ['tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br'];

            handleTypes.forEach(type => {

                const handle = document.createElement('div');

                handle.classList.add('handle');

                handle.dataset.type = type;

                canvasContainer.appendChild(handle);

                handles.push(handle);

            });

            updateHandlesPositions();

        }


        function removeHandles() {

            handles.forEach(h => h.remove());

            handles = [];

        }


        function updateHandlesPositions() {

            if (!currentSelectionRect.isDefined || handles.length === 0 || !imageCanvas.offsetParent) return;

            const { x, y, w, h } = currentSelectionRect;

            const canvasRect = imageCanvas.getBoundingClientRect();


            const scaleX = canvasRect.width / imageCanvas.width;

            const scaleY = canvasRect.height / imageCanvas.height;


            const positions = {

                'tl': { left: x * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                't':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'tr': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'l':  { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'r':  { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'bl': { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'b':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'br': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 }

            };


            handles.forEach(handle => {

                const type = handle.dataset.type;

                handle.style.left = `${canvasRect.left + positions[type].left - canvasContainer.getBoundingClientRect().left}px`;

                handle.style.top = `${canvasRect.top + positions[type].top - canvasContainer.getBoundingClientRect().top}px`;

                handle.style.cursor = getResizeCursor(type);

                handle.style.display = 'block';

            });

        }

        function getResizeCursor(handleType) {

            switch (handleType) {

                case 'tl': case 'br': return 'nwse-resize';

                case 'tr': case 'bl': return 'nesw-resize';

                case 't':  case 'b':  return 'ns-resize';

                case 'l':  case 'r':  return 'ew-resize';

                default: return 'default';

            }

        }

        function getEventPosOnCanvas(event) {

            const rect = imageCanvas.getBoundingClientRect();

            let clientX, clientY;

            if (event.touches && event.touches.length > 0) {

                clientX = event.touches[0].clientX;

                clientY = event.touches[0].clientY;

            } else {

                clientX = event.clientX;

                clientY = event.clientY;

            }

            return {

                x: (clientX - rect.left) * (imageCanvas.width / rect.width),

                y: (clientY - rect.top) * (imageCanvas.height / rect.height)

            };

        }

        function handleInteractionStart(e) {

            if (!currentSelectionRect.isDefined || !currentFileForAdjustment) return;

            const target = e.target;

            const eventPos = getEventPosOnCanvas(e);


            if (target.classList.contains('handle')) {

                activeDragAction = `resize-${target.dataset.type}`;

            } else if (target === imageCanvas &&

                       eventPos.x >= currentSelectionRect.x && eventPos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                       eventPos.y >= currentSelectionRect.y && eventPos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                activeDragAction = 'move';

            } else {

                return;

            }

            if (activeDragAction && e.cancelable) {

                 e.preventDefault();

            }

            dragStartCoords = eventPos;

            rectStartCoords = { ...currentSelectionRect };

        }

        function handleInteractionMove(e) {

            if (!activeDragAction || !currentSelectionRect.isDefined) {

                if (e.type === 'mousemove' && (e.target === imageCanvas || e.target.classList.contains('handle'))) {

                    const mousePos = getEventPosOnCanvas(e);

                    let cursor = 'default';

                    if (currentSelectionRect.isDefined) {

                        for (const handle of handles) {

                            const handleRect = handle.getBoundingClientRect();

                            const canvasRect = imageCanvas.getBoundingClientRect();

                            const handleCanvasX = (handleRect.left - canvasRect.left + HANDLE_SIZE/2) * (imageCanvas.width / canvasRect.width);

                            const handleCanvasY = (handleRect.top - canvasRect.top + HANDLE_SIZE/2) * (imageCanvas.height / canvasRect.height);

                            if (Math.abs(mousePos.x - handleCanvasX) < HANDLE_SIZE && Math.abs(mousePos.y - handleCanvasY) < HANDLE_SIZE * 1.5) {

                                cursor = getResizeCursor(handle.dataset.type);

                                break;

                            }

                        }

                        if (cursor === 'default' &&

                            mousePos.x >= currentSelectionRect.x && mousePos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                            mousePos.y >= currentSelectionRect.y && mousePos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                            cursor = 'move';

                        }

                    }

                    imageCanvas.style.cursor = cursor;

                }

                return;

            }


            if (e.cancelable) {

                e.preventDefault();

            }


            const eventPos = getEventPosOnCanvas(e);

            const deltaX = eventPos.x - dragStartCoords.x;

            const deltaY = eventPos.y - dragStartCoords.y;

            let { x, y, w, h } = rectStartCoords;


            if (activeDragAction === 'move') {

                x += deltaX;

                y += deltaY;

            } else if (activeDragAction.startsWith('resize-')) {

                const type = activeDragAction.split('-')[1];

                if (type.includes('l')) { x += deltaX; w -= deltaX; }

                if (type.includes('r')) { w += deltaX; }

                if (type.includes('t')) { y += deltaY; h -= deltaY; }

                if (type.includes('b')) { h += deltaY; }

            }


            const minSize = 20;

            if (w < minSize) {

                if (activeDragAction.includes('l') || activeDragAction.includes('tl') || activeDragAction.includes('bl')) x = rectStartCoords.x + rectStartCoords.w - minSize;

                w = minSize;

            }

            if (h < minSize) {

                if (activeDragAction.includes('t') || activeDragAction.includes('tl') || activeDragAction.includes('tr')) y = rectStartCoords.y + rectStartCoords.h - minSize;

                h = minSize;

            }

            x = Math.max(0, Math.min(x, imageCanvas.width - w));

            y = Math.max(0, Math.min(y, imageCanvas.height - h));

            w = Math.min(w, imageCanvas.width - x);

            h = Math.min(h, imageCanvas.height - y);


            currentSelectionRect = { x, y, w, h, isDefined: true };

            drawCanvasWithSelectionAndHandles(currentFileForAdjustment);

        }

        function handleInteractionEnd(e) {

             if (activeDragAction) {

                activeDragAction = null;

                imageCanvas.style.cursor = 'default';

                updateHandlesPositions();

            }

        }


        canvasContainer.addEventListener('mousedown', handleInteractionStart);

        document.addEventListener('mousemove', handleInteractionMove);

        document.addEventListener('mouseup', handleInteractionEnd);

        canvasContainer.addEventListener('touchstart', handleInteractionStart, { passive: false });

        document.addEventListener('touchmove', handleInteractionMove, { passive: false });

        document.addEventListener('touchend', handleInteractionEnd);

        applyAndDownloadButton.addEventListener('click', async () => {

            if (!currentSelectionRect.isDefined || allUploadedFiles.length === 0) {

                showMessage('Zone non définie ou aucun fichier chargé.', 'error');

                return;

            }

            loader.style.display = 'block';

            applyAndDownloadButton.disabled = true;

            let filesProcessedCount = 0;


            for (let i = 0; i < allUploadedFiles.length; i++) {

                const file = allUploadedFiles[i];

                const originalFilename = file.name.substring(0, file.name.lastIndexOf('.')) || file.name;

                showMessage(`Traitement de ${file.name} (${i+1}/${allUploadedFiles.length})...`, 'info', 60000);


                try {

                    if (file.type.startsWith('image/')) {

                        const img = await loadFileForAdjustment(file);

                        const tempCanvas = document.createElement('canvas');

                        const tempCtx = tempCanvas.getContext('2d');

                        tempCanvas.width = img.width;

                        tempCanvas.height = img.height;

                        tempCtx.drawImage(img, 0, 0);

                        tempCtx.fillStyle = 'white';

                        tempCtx.fillRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                        const outputFormat = imageOutputFormatSelect.value;


                        if (outputFormat === 'application/pdf') {

                            const pdfOutput = new jsPDF({

                                orientation: img.width > img.height ? 'l' : 'p',

                                unit: 'px', // Using pixels as unit for direct mapping from image dimensions

                                format: [img.width, img.height]

                            });

                            const pageDataUrl = tempCanvas.toDataURL('image/png');

                            pdfOutput.addImage(pageDataUrl, 'PNG', 0, 0, img.width, img.height);

                            pdfOutput.save(`${originalFilename}_modifie.pdf`);

                        } else {

                            const extension = outputFormat === 'image/jpeg' ? 'jpg' : 'png';

                            const dataURL = tempCanvas.toDataURL(outputFormat, outputFormat === 'image/jpeg' ? 0.9 : undefined);

                            triggerDownload(dataURL, `${originalFilename}_modifie.${extension}`);

                        }

                        filesProcessedCount++;


                    } else if (file.type === 'application/pdf') {

                        let pdfOutput; // Declare here, initialize on first page


                        const fileReader = new FileReader();

                        const arrayBuffer = await new Promise((resolve, reject) => {

                            fileReader.onload = () => resolve(fileReader.result);

                            fileReader.onerror = reject;

                            fileReader.readAsArrayBuffer(file);

                        });


                        const pdfDocInstance = await pdfjsLib.getDocument(new Uint8Array(arrayBuffer)).promise;

                        const numPages = pdfDocInstance.numPages;


                        for (let j = 1; j <= numPages; j++) {

                            const page = await pdfDocInstance.getPage(j);

                            const viewport = page.getViewport({ scale: 2.0 });

                            if (j === 1) {

                                // Initialize jsPDF for *this* PDF file using its first page's dimensions

                                pdfOutput = new jsPDF({

                                    orientation: viewport.width > viewport.height ? 'l' : 'p',

                                    unit: 'pt', // pdf.js viewport units are points. jsPDF will handle conversion if its internal unit is different.

                                    format: [viewport.width, viewport.height]

                                });

                            } else {

                                // Add subsequent pages with their own dimensions

                                pdfOutput.addPage([viewport.width, viewport.height], viewport.width > viewport.height ? 'l' : 'p');

                            }

                            const tempCanvas = document.createElement('canvas');

                            const tempCtx = tempCanvas.getContext('2d');

                            tempCanvas.width = viewport.width;  

                            tempCanvas.height = viewport.height;


                            await page.render({ canvasContext: tempCtx, viewport: viewport }).promise;

                            tempCtx.fillStyle = 'white';

                            tempCtx.fillRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                            const pageDataUrl = tempCanvas.toDataURL('image/png');

                            pdfOutput.addImage(pageDataUrl, 'PNG', 0, 0, viewport.width, viewport.height);

                        }

                        if (pdfOutput) { // Ensure pdfOutput was initialized

                           pdfOutput.save(`${originalFilename}_modifie.pdf`);

                        } else if (numPages > 0) { // Should not happen if numPages > 0

                            console.error("pdfOutput was not initialized for PDF:", file.name);

                            showMessage(`Erreur interne lors de la création du PDF pour ${file.name}`, 'error');

                        }

                        filesProcessedCount++;

                    }

                    await new Promise(resolve => setTimeout(resolve, 200));

                } catch (err) {

                    console.error("Erreur traitement fichier:", file.name, err);

                    showMessage(`Erreur avec ${file.name}: ${err.message || 'Inconnue'}`, 'error');

                }

            }

            loader.style.display = 'none';

            applyAndDownloadButton.disabled = false;

            if (filesProcessedCount > 0) {

                showMessage(`${filesProcessedCount} fichier(s) traité(s) et téléchargé(s).`, 'success');

            } else if (allUploadedFiles.length > 0) {

                 showMessage(`Aucun fichier n'a pu être traité avec succès.`, 'error');

            }


            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

        });


        function triggerDownload(dataURL, filename) {

            const link = document.createElement('a');

            link.href = dataURL;

            link.download = filename;

            document.body.appendChild(link);

            link.click();

            document.body.removeChild(link);

        }


        function resetCanvasAndState(resetQuadrantSelection = true) {

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            imageCanvas.style.display = 'none';

            removeHandles();

            currentFileForAdjustment = null;

            currentSelectionRect.isDefined = false;

            activeDragAction = null;

            initiateProcessingButton.disabled = true;

            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');


            if (resetQuadrantSelection) {

                allQuadrants.forEach(q => q.classList.remove('selected'));

                selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

            }

        }


        checkInitiateButtonState();

        window.addEventListener('resize', () => {

            if (currentSelectionRect.isDefined && handles.length > 0 && imageCanvas.offsetParent) {

                updateHandlesPositions();

            }

        });


    </script>

</body>

</html>




<!DOCTYPE html>

<html lang="fr">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

    <title>Suppresseur de Publicités pour Bordereaux (Batch)</title>

    <script src="https://cdn.tailwindcss.com"></script>

    <!-- PDF.js library -->

    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>

    <script>

        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';

    </script>

    <!-- jsPDF library -->

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>

    <style>

        body {

            font-family: 'Inter', sans-serif;

            overscroll-behavior-y: contain;

        }

        .a4-paper {

            border: 2px solid #000;

            display: grid;

            grid-template-columns: repeat(2, 1fr);

            grid-template-rows: repeat(2, 1fr);

            cursor: pointer;

            background-color: #f0f0f0;

        }

        .quadrant {

            border: 1px dashed #666;

            display: flex;

            justify-content: center;

            align-items: center;

            transition: background-color 0.2s ease-in-out;

        }

        .quadrant:hover {

            background-color: #e0e0e0;

        }

        .quadrant.selected {

            background-color: #ffffff !important;

            border: 2px solid #3b82f6;

        }

        #message-box {

            position: fixed;

            top: 20px;

            left: 50%;

            transform: translateX(-50%);

            padding: 10px 20px;

            border-radius: 8px;

            color: white;

            z-index: 1000;

            display: none;

            box-shadow: 0 4px 6px rgba(0,0,0,0.1);

        }

        #message-box.success { background-color: #28a745; }

        #message-box.error { background-color: #dc3545; }

        #message-box.info { background-color: #17a2b8; }


        .quadrant-icon { font-size: 10px; color: #888; }

        .portrait { width: 100px; height: 141.4px; }

        .landscape { width: 141.4px; height: 100px; }


        #imageCanvas {

            touch-action: none;

        }

        .handle {

            position: absolute;

            width: 14px;

            height: 14px;

            background-color: rgba(255, 0, 0, 0.6);

            border: 1px solid rgba(255, 255, 255, 0.8);

            border-radius: 50%;

            z-index: 10;

            touch-action: none;

        }

        #loader {

            border: 5px solid #f3f3f3;

            border-top: 5px solid #3498db;

            border-radius: 50%;

            width: 40px;

            height: 40px;

            animation: spin 1s linear infinite;

            margin: 10px auto;

            display: none;

        }

        @keyframes spin {

            0% { transform: rotate(0deg); }

            100% { transform: rotate(360deg); }

        }

    </style>

</head>

<body class="bg-gray-100 min-h-screen flex flex-col items-center justify-center p-4">


    <div id="message-box"></div>


    <div class="bg-white p-6 md:p-8 rounded-xl shadow-2xl w-full max-w-3xl">

        <header class="mb-6 text-center">

            <h1 class="text-2xl md:text-3xl font-bold text-gray-800">Suppresseur de Publicités pour Bordereaux (Batch)</h1>

            <p class="text-gray-600 mt-2">Masquez les pubs sur plusieurs fichiers à la fois.</p>

        </header>


        <main>

            <section id="step1Selection" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">1. Zone initiale approximative :</h2>

                 <div class="flex flex-col sm:flex-row justify-center items-center gap-6 sm:gap-10">

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Portrait</p>

                        <div id="portrait-paper" class="a4-paper portrait rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="portrait" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Paysage</p>

                        <div id="landscape-paper" class="a4-paper landscape rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="landscape" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                </div>

            </section>


            <section id="step2Upload" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">2. Téléversez vos fichiers (Images ou PDF) :</h2>

                <div class="flex flex-col items-center">

                    <input type="file" id="fileUpload" accept="image/*,application/pdf" multiple class="block w-full max-w-md text-sm text-gray-500

                        file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold

                        file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100

                        mb-2 p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">

                    <div class="my-2 text-sm text-gray-600">Format de sortie (si applicable) :

                        <select id="outputFormatSelect" class="ml-2 p-1 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-blue-500">

                            <option value="source">Comme source (PDF reste PDF, Image reste Image)</option>

                            <option value="image/png">PNG</option>

                            <option value="image/jpeg">JPEG</option>

                            <option value="application/pdf">PDF (convertir tout en PDF)</option>

                        </select>

                    </div>

                </div>

            </section>

            <section id="step3Adjust" class="mb-6 text-center">

                 <h2 class="text-xl font-semibold text-gray-700 mb-3">3. Ajustez la zone sur le 1er fichier et validez :</h2>

                <button id="initiateProcessingButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out disabled:opacity-50" disabled>

                    Afficher 1er Fichier et Ajuster Zone

                </button>

                <div id="canvasContainer" class="relative flex flex-col items-center mt-4">

                    <canvas id="imageCanvas" class="border border-gray-400 rounded-lg shadow-md max-w-full h-auto" style="display: none;"></canvas>

                </div>

                <div id="loader"></div>

                <button id="applyAndDownloadButton" class="hidden mt-4 bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out">

                    Appliquer à Tous et Télécharger

                </button>

            </section>

        </main>

    </div>


    <script>

        const { jsPDF } = window.jspdf;


        // Références DOM

        const allQuadrants = document.querySelectorAll('.quadrant');

        const fileUpload = document.getElementById('fileUpload');

        const outputFormatSelect = document.getElementById('outputFormatSelect');

        const initiateProcessingButton = document.getElementById('initiateProcessingButton');

        const imageCanvas = document.getElementById('imageCanvas');

        const canvasContainer = document.getElementById('canvasContainer');

        const applyAndDownloadButton = document.getElementById('applyAndDownloadButton');

        const messageBox = document.getElementById('message-box');

        const loader = document.getElementById('loader');

        const ctx = imageCanvas.getContext('2d');


        // État global

        let selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

        let allUploadedFiles = [];

        let currentFileForAdjustment = null;

        let currentSelectionRect = { x: 0, y: 0, w: 0, h: 0, isDefined: false };

        let activeDragAction = null;

        let dragStartCoords = { x: 0, y: 0 };

        let rectStartCoords = { x: 0, y: 0, w: 0, h: 0 };

        const HANDLE_SIZE = 14;

        let handles = [];


        // --- Constantes pour PDF A4 (en points, 1pt = 1/72 inch) ---

        const A4_WIDTH_PT = 595.28;

        const A4_HEIGHT_PT = 841.89;



        function showMessage(text, type = 'info', duration = 4000) {

            messageBox.textContent = text;

            messageBox.className = '';

            messageBox.classList.add(type);

            messageBox.style.display = 'block';

            setTimeout(() => { messageBox.style.display = 'none'; }, duration);

        }


        function checkInitiateButtonState() {

            initiateProcessingButton.disabled = !(allUploadedFiles.length > 0 && selectedQuadrantInfo.quadrantIndex !== null);

        }


        allQuadrants.forEach(quadrant => {

            quadrant.addEventListener('click', () => {

                allQuadrants.forEach(q => q.classList.remove('selected'));

                quadrant.classList.add('selected');

                selectedQuadrantInfo.orientation = quadrant.dataset.orientation;

                selectedQuadrantInfo.quadrantIndex = parseInt(quadrant.dataset.quadrant);

                checkInitiateButtonState();

                showMessage(`Zone initiale: ${selectedQuadrantInfo.orientation}, Q${selectedQuadrantInfo.quadrantIndex + 1}. Téléversez des fichiers.`, 'info');

            });

        });


        fileUpload.addEventListener('change', (event) => {

            if (event.target.files.length === 0) {

                allUploadedFiles = [];

                currentFileForAdjustment = null;

                resetCanvasAndState(false);

                checkInitiateButtonState();

                return;

            }

            allUploadedFiles = Array.from(event.target.files);

            currentFileForAdjustment = null;

            resetCanvasAndState(false);


            if (allUploadedFiles.length > 0) {

                showMessage(`${allUploadedFiles.length} fichier(s) sélectionné(s). Prêt à ajuster le 1er.`, 'success');

            }

            checkInitiateButtonState();

        });

        async function loadFileForAdjustment(file) {

            return new Promise((resolve, reject) => {

                if (file.type.startsWith('image/')) {

                    const reader = new FileReader();

                    reader.onload = (e) => {

                        const img = new Image();

                        img.onload = () => resolve(img);

                        img.onerror = () => reject(new Error('Erreur chargement image pour ajustement.'));

                        img.src = e.target.result;

                    };

                    reader.onerror = () => reject(new Error('Erreur lecture fichier image.'));

                    reader.readAsDataURL(file);

                } else if (file.type === 'application/pdf') {

                    const fileReader = new FileReader();

                    fileReader.onload = function() {

                        const typedarray = new Uint8Array(this.result);

                        pdfjsLib.getDocument(typedarray).promise.then(pdfDoc_ => {

                            pdfDoc_.getPage(1).then(page => {

                                const viewport = page.getViewport({ scale: 2.0 }); // Scale for good quality preview

                                const tempCanvas = document.createElement('canvas');

                                const tempCtx = tempCanvas.getContext('2d');

                                tempCanvas.height = viewport.height;

                                tempCanvas.width = viewport.width;

                                page.render({ canvasContext: tempCtx, viewport: viewport }).promise.then(() => {

                                    const img = new Image();

                                    img.onload = () => resolve(img);

                                    img.onerror = () => reject(new Error('Erreur conversion 1ère page PDF en image.'));

                                    img.src = tempCanvas.toDataURL('image/png');

                                }).catch(reject);

                            }).catch(reject);

                        }).catch(reject);

                    };

                    fileReader.onerror = () => reject(new Error('Erreur lecture fichier PDF.'));

                    fileReader.readAsArrayBuffer(file);

                } else {

                    reject(new Error('Type de fichier non supporté pour l\'ajustement.'));

                }

            });

        }


        initiateProcessingButton.addEventListener('click', async () => {

            if (allUploadedFiles.length === 0 || selectedQuadrantInfo.quadrantIndex === null) {

                showMessage('Sélectionnez zone ET téléversez au moins un fichier.', 'error');

                return;

            }

            loader.style.display = 'block';

            initiateProcessingButton.disabled = true;


            try {

                currentFileForAdjustment = await loadFileForAdjustment(allUploadedFiles[0]);

                setupInteractiveStage(currentFileForAdjustment);

                initiateProcessingButton.classList.add('hidden');

                applyAndDownloadButton.classList.remove('hidden');

                imageCanvas.style.display = 'block';

                showMessage('Ajustez le rectangle sur ce 1er fichier, puis validez.', 'info');

            } catch (error) {

                showMessage(error.message || 'Erreur chargement du 1er fichier.', 'error');

                console.error("Error loading first file for adjustment:", error);

            } finally {

                loader.style.display = 'none';

                initiateProcessingButton.disabled = false;

            }

        });


        function setupInteractiveStage(imageToAdjust) {

            imageCanvas.width = imageToAdjust.width;

            imageCanvas.height = imageToAdjust.height;

            const imgW = imageToAdjust.width;

            const imgH = imageToAdjust.height;

            // Scale the initial rectangle based on the preview image dimensions

            // The currentSelectionRect coordinates will be in the pixel space of this preview image.

            currentSelectionRect.w = imgW / 2;

            currentSelectionRect.h = imgH / 2;


            switch (selectedQuadrantInfo.quadrantIndex) {

                case 0: currentSelectionRect.x = 0; currentSelectionRect.y = 0; break;

                case 1: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = 0; break;

                case 2: currentSelectionRect.x = 0; currentSelectionRect.y = imgH / 2; break;

                case 3: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = imgH / 2; break;

            }

            currentSelectionRect.isDefined = true;

            createHandles();

            drawCanvasWithSelectionAndHandles(imageToAdjust);

        }

        function drawCanvasWithSelectionAndHandles(imageToDraw, isFinal = false) {

            if (!imageToDraw) return;

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            ctx.drawImage(imageToDraw, 0, 0, imageCanvas.width, imageCanvas.height);


            if (currentSelectionRect.isDefined) {

                if (isFinal) {

                    ctx.fillStyle = 'white';

                    ctx.fillRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    removeHandles();

                } else {

                    ctx.strokeStyle = 'rgba(255, 0, 0, 0.8)';

                    ctx.lineWidth = 2;

                    ctx.strokeRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    updateHandlesPositions();

                }

            }

        }


        function createHandles() {

            removeHandles();

            const handleTypes = ['tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br'];

            handleTypes.forEach(type => {

                const handle = document.createElement('div');

                handle.classList.add('handle');

                handle.dataset.type = type;

                canvasContainer.appendChild(handle);

                handles.push(handle);

            });

            updateHandlesPositions();

        }


        function removeHandles() {

            handles.forEach(h => h.remove());

            handles = [];

        }


        function updateHandlesPositions() {

            if (!currentSelectionRect.isDefined || handles.length === 0 || !imageCanvas.offsetParent) return;

            const { x, y, w, h } = currentSelectionRect;

            const canvasRect = imageCanvas.getBoundingClientRect();


            const scaleX = canvasRect.width / imageCanvas.width;

            const scaleY = canvasRect.height / imageCanvas.height;


            const positions = {

                'tl': { left: x * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                't':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'tr': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'l':  { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'r':  { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'bl': { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'b':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'br': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 }

            };


            handles.forEach(handle => {

                const type = handle.dataset.type;

                handle.style.left = `${canvasRect.left + positions[type].left - canvasContainer.getBoundingClientRect().left}px`;

                handle.style.top = `${canvasRect.top + positions[type].top - canvasContainer.getBoundingClientRect().top}px`;

                handle.style.cursor = getResizeCursor(type);

                handle.style.display = 'block';

            });

        }

        function getResizeCursor(handleType) {

            switch (handleType) {

                case 'tl': case 'br': return 'nwse-resize';

                case 'tr': case 'bl': return 'nesw-resize';

                case 't':  case 'b':  return 'ns-resize';

                case 'l':  case 'r':  return 'ew-resize';

                default: return 'default';

            }

        }

        function getEventPosOnCanvas(event) {

            const rect = imageCanvas.getBoundingClientRect();

            let clientX, clientY;

            if (event.touches && event.touches.length > 0) {

                clientX = event.touches[0].clientX;

                clientY = event.touches[0].clientY;

            } else {

                clientX = event.clientX;

                clientY = event.clientY;

            }

            return {

                x: (clientX - rect.left) * (imageCanvas.width / rect.width),

                y: (clientY - rect.top) * (imageCanvas.height / rect.height)

            };

        }

        function handleInteractionStart(e) {

            if (!currentSelectionRect.isDefined || !currentFileForAdjustment) return;

            const target = e.target;

            const eventPos = getEventPosOnCanvas(e);


            if (target.classList.contains('handle')) {

                activeDragAction = `resize-${target.dataset.type}`;

            } else if (target === imageCanvas &&

                       eventPos.x >= currentSelectionRect.x && eventPos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                       eventPos.y >= currentSelectionRect.y && eventPos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                activeDragAction = 'move';

            } else {

                return;

            }

            if (activeDragAction && e.cancelable) {

                 e.preventDefault();

            }

            dragStartCoords = eventPos;

            rectStartCoords = { ...currentSelectionRect };

        }

        function handleInteractionMove(e) {

            if (!activeDragAction || !currentSelectionRect.isDefined) {

                if (e.type === 'mousemove' && (e.target === imageCanvas || e.target.classList.contains('handle'))) {

                    const mousePos = getEventPosOnCanvas(e);

                    let cursor = 'default';

                    if (currentSelectionRect.isDefined) {

                        for (const handle of handles) {

                            const handleRect = handle.getBoundingClientRect();

                            const canvasRect = imageCanvas.getBoundingClientRect();

                            const handleCanvasX = (handleRect.left - canvasRect.left + HANDLE_SIZE/2) * (imageCanvas.width / canvasRect.width);

                            const handleCanvasY = (handleRect.top - canvasRect.top + HANDLE_SIZE/2) * (imageCanvas.height / canvasRect.height);

                            if (Math.abs(mousePos.x - handleCanvasX) < HANDLE_SIZE && Math.abs(mousePos.y - handleCanvasY) < HANDLE_SIZE * 1.5) {

                                cursor = getResizeCursor(handle.dataset.type);

                                break;

                            }

                        }

                        if (cursor === 'default' &&

                            mousePos.x >= currentSelectionRect.x && mousePos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                            mousePos.y >= currentSelectionRect.y && mousePos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                            cursor = 'move';

                        }

                    }

                    imageCanvas.style.cursor = cursor;

                }

                return;

            }


            if (e.cancelable) {

                e.preventDefault();

            }


            const eventPos = getEventPosOnCanvas(e);

            const deltaX = eventPos.x - dragStartCoords.x;

            const deltaY = eventPos.y - dragStartCoords.y;

            let { x, y, w, h } = rectStartCoords;


            if (activeDragAction === 'move') {

                x += deltaX;

                y += deltaY;

            } else if (activeDragAction.startsWith('resize-')) {

                const type = activeDragAction.split('-')[1];

                if (type.includes('l')) { x += deltaX; w -= deltaX; }

                if (type.includes('r')) { w += deltaX; }

                if (type.includes('t')) { y += deltaY; h -= deltaY; }

                if (type.includes('b')) { h += deltaY; }

            }


            const minSize = 20;

            if (w < minSize) {

                if (activeDragAction.includes('l') || activeDragAction.includes('tl') || activeDragAction.includes('bl')) x = rectStartCoords.x + rectStartCoords.w - minSize;

                w = minSize;

            }

            if (h < minSize) {

                if (activeDragAction.includes('t') || activeDragAction.includes('tl') || activeDragAction.includes('tr')) y = rectStartCoords.y + rectStartCoords.h - minSize;

                h = minSize;

            }

            x = Math.max(0, Math.min(x, imageCanvas.width - w));

            y = Math.max(0, Math.min(y, imageCanvas.height - h));

            w = Math.min(w, imageCanvas.width - x);

            h = Math.min(h, imageCanvas.height - y);


            currentSelectionRect = { x, y, w, h, isDefined: true };

            drawCanvasWithSelectionAndHandles(currentFileForAdjustment);

        }

        function handleInteractionEnd(e) {

             if (activeDragAction) {

                activeDragAction = null;

                imageCanvas.style.cursor = 'default';

                updateHandlesPositions();

            }

        }


        canvasContainer.addEventListener('mousedown', handleInteractionStart);

        document.addEventListener('mousemove', handleInteractionMove);

        document.addEventListener('mouseup', handleInteractionEnd);

        canvasContainer.addEventListener('touchstart', handleInteractionStart, { passive: false });

        document.addEventListener('touchmove', handleInteractionMove, { passive: false });

        document.addEventListener('touchend', handleInteractionEnd);

        applyAndDownloadButton.addEventListener('click', async () => {

            if (!currentSelectionRect.isDefined || allUploadedFiles.length === 0) {

                showMessage('Zone non définie ou aucun fichier chargé.', 'error');

                return;

            }

            loader.style.display = 'block';

            applyAndDownloadButton.disabled = true;

            let filesProcessedCount = 0;

            const chosenOutputFormat = outputFormatSelect.value;


            for (let i = 0; i < allUploadedFiles.length; i++) {

                const file = allUploadedFiles[i];

                const originalFilename = file.name.substring(0, file.name.lastIndexOf('.')) || file.name;

                showMessage(`Traitement de ${file.name} (${i+1}/${allUploadedFiles.length})...`, 'info', 60000);


                try {

                    // Determine target format based on original file type and user selection

                    let targetFormat = chosenOutputFormat;

                    if (chosenOutputFormat === 'source') {

                        targetFormat = file.type.startsWith('image/') ? 'image/png' : 'application/pdf'; // Default to PNG for images if "source"

                        if (file.type.startsWith('image/jpeg')) targetFormat = 'image/jpeg';

                    }

                    // --- Process IMAGE type input file ---

                    if (file.type.startsWith('image/')) {

                        const img = await loadFileForAdjustment(file);

                        const tempCanvas = document.createElement('canvas');

                        const tempCtx = tempCanvas.getContext('2d');

                        tempCanvas.width = img.width;

                        tempCanvas.height = img.height;

                        tempCtx.drawImage(img, 0, 0);

                        tempCtx.fillStyle = 'white';

                        // Apply selection rectangle. Coordinates are relative to the source image dimensions.

                        tempCtx.fillRect(currentSelectionRect.x * (img.width / currentFileForAdjustment.width),

                                         currentSelectionRect.y * (img.height / currentFileForAdjustment.height),

                                         currentSelectionRect.w * (img.width / currentFileForAdjustment.width),

                                         currentSelectionRect.h * (img.height / currentFileForAdjustment.height));

                        if (targetFormat === 'application/pdf') {

                            const pdfOutput = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' });

                            const pageDataUrl = tempCanvas.toDataURL('image/png');

                            const targetWidthPt = img.width > img.height ? A4_HEIGHT_PT : A4_WIDTH_PT; // Use A4 landscape if image is landscape

                            const targetHeightPt = img.width > img.height ? A4_WIDTH_PT : A4_HEIGHT_PT;

                            if(img.width < img.height) { // Portrait image

                                pdfOutput.internal.pageSize.width = A4_WIDTH_PT;

                                pdfOutput.internal.pageSize.height = A4_HEIGHT_PT;

                            } else { // Landscape image

                                 pdfOutput.internal.pageSize.width = A4_HEIGHT_PT;

                                 pdfOutput.internal.pageSize.height = A4_WIDTH_PT;

                            }


                            const ratio = Math.min(targetWidthPt / img.width, targetHeightPt / img.height);

                            const scaledWidth = img.width * ratio;

                            const scaledHeight = img.height * ratio;

                            const xOffset = (targetWidthPt - scaledWidth) / 2;

                            const yOffset = (targetHeightPt - scaledHeight) / 2;


                            pdfOutput.addImage(pageDataUrl, 'PNG', xOffset, yOffset, scaledWidth, scaledHeight);

                            pdfOutput.save(`${originalFilename}_modifie.pdf`);

                        } else {

                            const extension = targetFormat === 'image/jpeg' ? 'jpg' : 'png';

                            const dataURL = tempCanvas.toDataURL(targetFormat, targetFormat === 'image/jpeg' ? 0.9 : undefined);

                            triggerDownload(dataURL, `${originalFilename}_modifie.${extension}`);

                        }

                        filesProcessedCount++;


                    // --- Process PDF type input file ---

                    } else if (file.type === 'application/pdf') {

                        const fileReader = new FileReader();

                        const arrayBuffer = await new Promise((resolve, reject) => {

                            fileReader.onload = () => resolve(fileReader.result);

                            fileReader.onerror = reject;

                            fileReader.readAsArrayBuffer(file);

                        });

                        const pdfDocInstance = await pdfjsLib.getDocument(new Uint8Array(arrayBuffer)).promise;

                        const numPages = pdfDocInstance.numPages;


                        if (targetFormat === 'image/png' || targetFormat === 'image/jpeg') { // PDF to Single Image (first page)

                            const page = await pdfDocInstance.getPage(1);

                            const viewport = page.getViewport({ scale: 2.0 }); // Good scale for image quality

                            const tempCanvas = document.createElement('canvas');

                            const tempCtx = tempCanvas.getContext('2d');

                            tempCanvas.width = viewport.width;

                            tempCanvas.height = viewport.height;

                            await page.render({ canvasContext: tempCtx, viewport: viewport }).promise;

                            tempCtx.fillStyle = 'white';

                            // Apply selection rectangle, scaling it from preview image space to current PDF page space

                            const scaleToPageX = viewport.width / currentFileForAdjustment.width;

                            const scaleToPageY = viewport.height / currentFileForAdjustment.height;

                            tempCtx.fillRect(currentSelectionRect.x * scaleToPageX,

                                             currentSelectionRect.y * scaleToPageY,

                                             currentSelectionRect.w * scaleToPageX,

                                             currentSelectionRect.h * scaleToPageY);


                            const extension = targetFormat === 'image/jpeg' ? 'jpg' : 'png';

                            const dataURL = tempCanvas.toDataURL(targetFormat, targetFormat === 'image/jpeg' ? 0.9 : undefined);

                            triggerDownload(dataURL, `${originalFilename}_page1_modifie.${extension}`);


                        } else { // PDF to PDF (multi-page)

                            let pdfOutput;

                            for (let j = 1; j <= numPages; j++) {

                                const page = await pdfDocInstance.getPage(j);

                                const viewport = page.getViewport({ scale: 2.0 }); // Render at good resolution

                                const pageTargetWidthPt = viewport.width > viewport.height ? A4_HEIGHT_PT : A4_WIDTH_PT;

                                const pageTargetHeightPt = viewport.width > viewport.height ? A4_WIDTH_PT : A4_HEIGHT_PT;

                                let orientation = viewport.width > viewport.height ? 'l' : 'p';


                                if (j === 1) {

                                    pdfOutput = new jsPDF({ orientation: orientation, unit: 'pt', format: [pageTargetWidthPt, pageTargetHeightPt] });

                                } else {

                                    pdfOutput.addPage([pageTargetWidthPt, pageTargetHeightPt], orientation);

                                }

                                const tempCanvas = document.createElement('canvas');

                                const tempCtx = tempCanvas.getContext('2d');

                                tempCanvas.width = viewport.width;  

                                tempCanvas.height = viewport.height;

                                await page.render({ canvasContext: tempCtx, viewport: viewport }).promise;

                                tempCtx.fillStyle = 'white';

                                // Scale selection rectangle from preview image space to current PDF page space

                                const scaleToPageX = viewport.width / currentFileForAdjustment.width;

                                const scaleToPageY = viewport.height / currentFileForAdjustment.height;

                                tempCtx.fillRect(currentSelectionRect.x * scaleToPageX,

                                                 currentSelectionRect.y * scaleToPageY,

                                                 currentSelectionRect.w * scaleToPageX,

                                                 currentSelectionRect.h * scaleToPageY);

                                const pageDataUrl = tempCanvas.toDataURL('image/png');

                                // Add image to fit the A4-like page

                                const ratio = Math.min(pageTargetWidthPt / viewport.width, pageTargetHeightPt / viewport.height);

                                const scaledWidth = viewport.width * ratio;

                                const scaledHeight = viewport.height * ratio;

                                const xOffset = (pageTargetWidthPt - scaledWidth) / 2;

                                const yOffset = (pageTargetHeightPt - scaledHeight) / 2;

                                pdfOutput.addImage(pageDataUrl, 'PNG', xOffset, yOffset, scaledWidth, scaledHeight);

                            }

                            if (pdfOutput) {

                               pdfOutput.save(`${originalFilename}_modifie.pdf`);

                            } else if (numPages > 0) {

                                console.error("pdfOutput was not initialized for PDF:", file.name);

                                showMessage(`Erreur interne lors de la création du PDF pour ${file.name}`, 'error');

                            }

                        }

                        filesProcessedCount++;

                    }

                    await new Promise(resolve => setTimeout(resolve, 200));

                } catch (err) {

                    console.error("Erreur traitement fichier:", file.name, err);

                    showMessage(`Erreur avec ${file.name}: ${err.message || 'Inconnue'}`, 'error');

                }

            }

            loader.style.display = 'none';

            applyAndDownloadButton.disabled = false;

            if (filesProcessedCount > 0) {

                showMessage(`${filesProcessedCount} fichier(s) traité(s) et téléchargé(s).`, 'success');

            } else if (allUploadedFiles.length > 0) {

                 showMessage(`Aucun fichier n'a pu être traité avec succès.`, 'error');

            }


            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

        });


        function triggerDownload(dataURL, filename) {

            const link = document.createElement('a');

            link.href = dataURL;

            link.download = filename;

            document.body.appendChild(link);

            link.click();

            document.body.removeChild(link);

        }


        function resetCanvasAndState(resetQuadrantSelection = true) {

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            imageCanvas.style.display = 'none';

            removeHandles();

            currentFileForAdjustment = null;

            currentSelectionRect.isDefined = false;

            activeDragAction = null;

            initiateProcessingButton.disabled = true;

            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');


            if (resetQuadrantSelection) {

                allQuadrants.forEach(q => q.classList.remove('selected'));

                selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

            }

        }


        checkInitiateButtonState();

        window.addEventListener('resize', () => {

            if (currentSelectionRect.isDefined && handles.length > 0 && imageCanvas.offsetParent) {

                updateHandlesPositions();

            }

        });


    </script>

</body>

</html>






<!DOCTYPE html>

<html lang="fr">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

    <title>Suppresseur de Publicités pour Bordereaux (Batch)</title>

    <script src="https://cdn.tailwindcss.com"></script>

    <!-- PDF.js library -->

    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>

    <script>

        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';

    </script>

    <!-- jsPDF library -->

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>

    <style>

        body {

            font-family: 'Inter', sans-serif;

            overscroll-behavior-y: contain;

        }

        .a4-paper {

            border: 2px solid #000;

            display: grid;

            grid-template-columns: repeat(2, 1fr);

            grid-template-rows: repeat(2, 1fr);

            cursor: pointer;

            background-color: #f0f0f0;

        }

        .quadrant {

            border: 1px dashed #666;

            display: flex;

            justify-content: center;

            align-items: center;

            transition: background-color 0.2s ease-in-out;

        }

        .quadrant:hover {

            background-color: #e0e0e0;

        }

        .quadrant.selected {

            background-color: #ffffff !important;

            border: 2px solid #3b82f6;

        }

        #message-box {

            position: fixed;

            top: 20px;

            left: 50%;

            transform: translateX(-50%);

            padding: 10px 20px;

            border-radius: 8px;

            color: white;

            z-index: 1000;

            display: none;

            box-shadow: 0 4px 6px rgba(0,0,0,0.1);

        }

        #message-box.success { background-color: #28a745; }

        #message-box.error { background-color: #dc3545; }

        #message-box.info { background-color: #17a2b8; }


        .quadrant-icon { font-size: 10px; color: #888; }

        .portrait { width: 100px; height: 141.4px; }

        .landscape { width: 141.4px; height: 100px; }


        #imageCanvas {

            touch-action: none;

        }

        .handle {

            position: absolute;

            width: 14px;

            height: 14px;

            background-color: rgba(255, 0, 0, 0.6);

            border: 1px solid rgba(255, 255, 255, 0.8);

            border-radius: 50%;

            z-index: 10;

            touch-action: none;

        }

        #loader {

            border: 5px solid #f3f3f3;

            border-top: 5px solid #3498db;

            border-radius: 50%;

            width: 40px;

            height: 40px;

            animation: spin 1s linear infinite;

            margin: 10px auto;

            display: none;

        }

        @keyframes spin {

            0% { transform: rotate(0deg); }

            100% { transform: rotate(360deg); }

        }

    </style>

</head>

<body class="bg-gray-100 min-h-screen flex flex-col items-center justify-center p-4">


    <div id="message-box"></div>


    <div class="bg-white p-6 md:p-8 rounded-xl shadow-2xl w-full max-w-3xl">

        <header class="mb-6 text-center">

            <div class="flex justify-between items-center">

                <h1 class="text-2xl md:text-3xl font-bold text-gray-800 flex-grow text-center">Suppresseur de Publicités</h1>

                <button id="resetAllButton" title="Réinitialiser tous les paramètres" class="p-2 text-gray-500 hover:text-blue-600 transition-colors">

                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rotate-ccw">

                        <path d="M3 2v6h6"/>

                        <path d="M3.31 15A9 9 0 0 0 20.7 7.5"/>

                        <path d="M21 22v-6h-6"/>

                        <path d="M20.7 9A9 9 0 0 0 3.31 16.5"/>

                    </svg>

                </button>

            </div>

            <p class="text-gray-600 mt-1">Masquez les pubs sur plusieurs fichiers à la fois.</p>

        </header>


        <main>

            <section id="step1Selection" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">1. Zone initiale approximative :</h2>

                 <div class="flex flex-col sm:flex-row justify-center items-center gap-6 sm:gap-10">

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Portrait</p>

                        <div id="portrait-paper" class="a4-paper portrait rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="portrait" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Paysage</p>

                        <div id="landscape-paper" class="a4-paper landscape rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="landscape" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                </div>

            </section>


            <section id="step2Upload" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">2. Téléversez vos fichiers (Images ou PDF) :</h2>

                <div class="flex flex-col items-center">

                    <input type="file" id="fileUpload" accept="image/*,application/pdf" multiple class="block w-full max-w-md text-sm text-gray-500

                        file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold

                        file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100

                        mb-2 p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">

                    <div class="my-2 text-sm text-gray-600">Format de sortie (si applicable) :

                        <select id="outputFormatSelect" class="ml-2 p-1 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-blue-500">

                            <option value="source">Comme source (PDF reste PDF, Image reste Image)</option>

                            <option value="image/png">PNG</option>

                            <option value="image/jpeg">JPEG</option>

                            <option value="application/pdf">PDF (convertir tout en PDF)</option>

                        </select>

                    </div>

                </div>

            </section>

            <section id="step3Adjust" class="mb-6 text-center">

                 <h2 class="text-xl font-semibold text-gray-700 mb-3">3. Ajustez la zone sur le 1er fichier et validez :</h2>

                <button id="initiateProcessingButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out disabled:opacity-50" disabled>

                    Afficher 1er Fichier et Ajuster Zone

                </button>

                <div id="canvasContainer" class="relative flex flex-col items-center mt-4">

                    <canvas id="imageCanvas" class="border border-gray-400 rounded-lg shadow-md max-w-full h-auto" style="display: none;"></canvas>

                </div>

                <div id="loader"></div>

                <button id="applyAndDownloadButton" class="hidden mt-4 bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out">

                    Appliquer à Tous et Télécharger

                </button>

            </section>

        </main>

    </div>


    <script>

        const { jsPDF } = window.jspdf;


        // Références DOM

        const allQuadrants = document.querySelectorAll('.quadrant');

        const fileUpload = document.getElementById('fileUpload');

        const outputFormatSelect = document.getElementById('outputFormatSelect');

        const initiateProcessingButton = document.getElementById('initiateProcessingButton');

        const imageCanvas = document.getElementById('imageCanvas');

        const canvasContainer = document.getElementById('canvasContainer');

        const applyAndDownloadButton = document.getElementById('applyAndDownloadButton');

        const messageBox = document.getElementById('message-box');

        const loader = document.getElementById('loader');

        const resetAllButton = document.getElementById('resetAllButton'); // Nouveau bouton

        const ctx = imageCanvas.getContext('2d');


        // État global

        let selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

        let allUploadedFiles = [];

        let currentFileForAdjustment = null;

        let currentSelectionRect = { x: 0, y: 0, w: 0, h: 0, isDefined: false };

        let activeDragAction = null;

        let dragStartCoords = { x: 0, y: 0 };

        let rectStartCoords = { x: 0, y: 0, w: 0, h: 0 };

        const HANDLE_SIZE = 14;

        let handles = [];


        // --- Constantes pour PDF A4 (en points, 1pt = 1/72 inch) ---

        const A4_WIDTH_PT = 595.28;

        const A4_HEIGHT_PT = 841.89;



        function showMessage(text, type = 'info', duration = 4000) {

            messageBox.textContent = text;

            messageBox.className = '';

            messageBox.classList.add(type);

            messageBox.style.display = 'block';

            setTimeout(() => { messageBox.style.display = 'none'; }, duration);

        }


        function checkInitiateButtonState() {

            initiateProcessingButton.disabled = !(allUploadedFiles.length > 0 && selectedQuadrantInfo.quadrantIndex !== null);

        }


        allQuadrants.forEach(quadrant => {

            quadrant.addEventListener('click', () => {

                allQuadrants.forEach(q => q.classList.remove('selected'));

                quadrant.classList.add('selected');

                selectedQuadrantInfo.orientation = quadrant.dataset.orientation;

                selectedQuadrantInfo.quadrantIndex = parseInt(quadrant.dataset.quadrant);

                checkInitiateButtonState();

                showMessage(`Zone initiale: ${selectedQuadrantInfo.orientation}, Q${selectedQuadrantInfo.quadrantIndex + 1}. Téléversez des fichiers.`, 'info');

            });

        });


        fileUpload.addEventListener('change', (event) => {

            if (event.target.files.length === 0) {

                allUploadedFiles = [];

                currentFileForAdjustment = null;

                resetCanvasAndState(false);

                checkInitiateButtonState();

                return;

            }

            allUploadedFiles = Array.from(event.target.files);

            currentFileForAdjustment = null;

            resetCanvasAndState(false);


            if (allUploadedFiles.length > 0) {

                showMessage(`${allUploadedFiles.length} fichier(s) sélectionné(s). Prêt à ajuster le 1er.`, 'success');

            }

            checkInitiateButtonState();

        });

        async function loadFileForAdjustment(file) {

            return new Promise((resolve, reject) => {

                if (file.type.startsWith('image/')) {

                    const reader = new FileReader();

                    reader.onload = (e) => {

                        const img = new Image();

                        img.onload = () => resolve(img);

                        img.onerror = () => reject(new Error('Erreur chargement image pour ajustement.'));

                        img.src = e.target.result;

                    };

                    reader.onerror = () => reject(new Error('Erreur lecture fichier image.'));

                    reader.readAsDataURL(file);

                } else if (file.type === 'application/pdf') {

                    const fileReader = new FileReader();

                    fileReader.onload = function() {

                        const typedarray = new Uint8Array(this.result);

                        pdfjsLib.getDocument(typedarray).promise.then(pdfDoc_ => {

                            pdfDoc_.getPage(1).then(page => {

                                const viewport = page.getViewport({ scale: 2.0 });

                                const tempCanvas = document.createElement('canvas');

                                const tempCtx = tempCanvas.getContext('2d');

                                tempCanvas.height = viewport.height;

                                tempCanvas.width = viewport.width;

                                page.render({ canvasContext: tempCtx, viewport: viewport }).promise.then(() => {

                                    const img = new Image();

                                    img.onload = () => resolve(img);

                                    img.onerror = () => reject(new Error('Erreur conversion 1ère page PDF en image.'));

                                    img.src = tempCanvas.toDataURL('image/png');

                                }).catch(reject);

                            }).catch(reject);

                        }).catch(reject);

                    };

                    fileReader.onerror = () => reject(new Error('Erreur lecture fichier PDF.'));

                    fileReader.readAsArrayBuffer(file);

                } else {

                    reject(new Error('Type de fichier non supporté pour l\'ajustement.'));

                }

            });

        }


        initiateProcessingButton.addEventListener('click', async () => {

            if (allUploadedFiles.length === 0 || selectedQuadrantInfo.quadrantIndex === null) {

                showMessage('Sélectionnez zone ET téléversez au moins un fichier.', 'error');

                return;

            }

            loader.style.display = 'block';

            initiateProcessingButton.disabled = true;


            try {

                currentFileForAdjustment = await loadFileForAdjustment(allUploadedFiles[0]);

                setupInteractiveStage(currentFileForAdjustment);

                initiateProcessingButton.classList.add('hidden');

                applyAndDownloadButton.classList.remove('hidden');

                imageCanvas.style.display = 'block';

                showMessage('Ajustez le rectangle sur ce 1er fichier, puis validez.', 'info');

            } catch (error) {

                showMessage(error.message || 'Erreur chargement du 1er fichier.', 'error');

                console.error("Error loading first file for adjustment:", error);

            } finally {

                loader.style.display = 'none';

                initiateProcessingButton.disabled = false;

            }

        });


        function setupInteractiveStage(imageToAdjust) {

            imageCanvas.width = imageToAdjust.width;

            imageCanvas.height = imageToAdjust.height;

            const imgW = imageToAdjust.width;

            const imgH = imageToAdjust.height;

            currentSelectionRect.w = imgW / 2;

            currentSelectionRect.h = imgH / 2;


            switch (selectedQuadrantInfo.quadrantIndex) {

                case 0: currentSelectionRect.x = 0; currentSelectionRect.y = 0; break;

                case 1: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = 0; break;

                case 2: currentSelectionRect.x = 0; currentSelectionRect.y = imgH / 2; break;

                case 3: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = imgH / 2; break;

            }

            currentSelectionRect.isDefined = true;

            createHandles();

            drawCanvasWithSelectionAndHandles(imageToAdjust);

        }

        function drawCanvasWithSelectionAndHandles(imageToDraw, isFinal = false) {

            if (!imageToDraw) return;

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            ctx.drawImage(imageToDraw, 0, 0, imageCanvas.width, imageCanvas.height);


            if (currentSelectionRect.isDefined) {

                if (isFinal) {

                    ctx.fillStyle = 'white';

                    ctx.fillRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    removeHandles();

                } else {

                    ctx.strokeStyle = 'rgba(255, 0, 0, 0.8)';

                    ctx.lineWidth = 2;

                    ctx.strokeRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    updateHandlesPositions();

                }

            }

        }


        function createHandles() {

            removeHandles();

            const handleTypes = ['tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br'];

            handleTypes.forEach(type => {

                const handle = document.createElement('div');

                handle.classList.add('handle');

                handle.dataset.type = type;

                canvasContainer.appendChild(handle);

                handles.push(handle);

            });

            updateHandlesPositions();

        }


        function removeHandles() {

            handles.forEach(h => h.remove());

            handles = [];

        }


        function updateHandlesPositions() {

            if (!currentSelectionRect.isDefined || handles.length === 0 || !imageCanvas.offsetParent) return;

            const { x, y, w, h } = currentSelectionRect;

            const canvasRect = imageCanvas.getBoundingClientRect();


            const scaleX = canvasRect.width / imageCanvas.width;

            const scaleY = canvasRect.height / imageCanvas.height;


            const positions = {

                'tl': { left: x * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                't':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'tr': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'l':  { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'r':  { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'bl': { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'b':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'br': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 }

            };


            handles.forEach(handle => {

                const type = handle.dataset.type;

                handle.style.left = `${canvasRect.left + positions[type].left - canvasContainer.getBoundingClientRect().left}px`;

                handle.style.top = `${canvasRect.top + positions[type].top - canvasContainer.getBoundingClientRect().top}px`;

                handle.style.cursor = getResizeCursor(type);

                handle.style.display = 'block';

            });

        }

        function getResizeCursor(handleType) {

            switch (handleType) {

                case 'tl': case 'br': return 'nwse-resize';

                case 'tr': case 'bl': return 'nesw-resize';

                case 't':  case 'b':  return 'ns-resize';

                case 'l':  case 'r':  return 'ew-resize';

                default: return 'default';

            }

        }

        function getEventPosOnCanvas(event) {

            const rect = imageCanvas.getBoundingClientRect();

            let clientX, clientY;

            if (event.touches && event.touches.length > 0) {

                clientX = event.touches[0].clientX;

                clientY = event.touches[0].clientY;

            } else {

                clientX = event.clientX;

                clientY = event.clientY;

            }

            return {

                x: (clientX - rect.left) * (imageCanvas.width / rect.width),

                y: (clientY - rect.top) * (imageCanvas.height / rect.height)

            };

        }

        function handleInteractionStart(e) {

            if (!currentSelectionRect.isDefined || !currentFileForAdjustment) return;

            const target = e.target;

            const eventPos = getEventPosOnCanvas(e);


            if (target.classList.contains('handle')) {

                activeDragAction = `resize-${target.dataset.type}`;

            } else if (target === imageCanvas &&

                       eventPos.x >= currentSelectionRect.x && eventPos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                       eventPos.y >= currentSelectionRect.y && eventPos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                activeDragAction = 'move';

            } else {

                return;

            }

            if (activeDragAction && e.cancelable) {

                 e.preventDefault();

            }

            dragStartCoords = eventPos;

            rectStartCoords = { ...currentSelectionRect };

        }

        function handleInteractionMove(e) {

            if (!activeDragAction || !currentSelectionRect.isDefined) {

                if (e.type === 'mousemove' && (e.target === imageCanvas || e.target.classList.contains('handle'))) {

                    const mousePos = getEventPosOnCanvas(e);

                    let cursor = 'default';

                    if (currentSelectionRect.isDefined) {

                        for (const handle of handles) {

                            const handleRect = handle.getBoundingClientRect();

                            const canvasRect = imageCanvas.getBoundingClientRect();

                            const handleCanvasX = (handleRect.left - canvasRect.left + HANDLE_SIZE/2) * (imageCanvas.width / canvasRect.width);

                            const handleCanvasY = (handleRect.top - canvasRect.top + HANDLE_SIZE/2) * (imageCanvas.height / canvasRect.height);

                            if (Math.abs(mousePos.x - handleCanvasX) < HANDLE_SIZE && Math.abs(mousePos.y - handleCanvasY) < HANDLE_SIZE * 1.5) {

                                cursor = getResizeCursor(handle.dataset.type);

                                break;

                            }

                        }

                        if (cursor === 'default' &&

                            mousePos.x >= currentSelectionRect.x && mousePos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                            mousePos.y >= currentSelectionRect.y && mousePos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                            cursor = 'move';

                        }

                    }

                    imageCanvas.style.cursor = cursor;

                }

                return;

            }


            if (e.cancelable) {

                e.preventDefault();

            }


            const eventPos = getEventPosOnCanvas(e);

            const deltaX = eventPos.x - dragStartCoords.x;

            const deltaY = eventPos.y - dragStartCoords.y;

            let { x, y, w, h } = rectStartCoords;


            if (activeDragAction === 'move') {

                x += deltaX;

                y += deltaY;

            } else if (activeDragAction.startsWith('resize-')) {

                const type = activeDragAction.split('-')[1];

                if (type.includes('l')) { x += deltaX; w -= deltaX; }

                if (type.includes('r')) { w += deltaX; }

                if (type.includes('t')) { y += deltaY; h -= deltaY; }

                if (type.includes('b')) { h += deltaY; }

            }


            const minSize = 20;

            if (w < minSize) {

                if (activeDragAction.includes('l') || activeDragAction.includes('tl') || activeDragAction.includes('bl')) x = rectStartCoords.x + rectStartCoords.w - minSize;

                w = minSize;

            }

            if (h < minSize) {

                if (activeDragAction.includes('t') || activeDragAction.includes('tl') || activeDragAction.includes('tr')) y = rectStartCoords.y + rectStartCoords.h - minSize;

                h = minSize;

            }

            x = Math.max(0, Math.min(x, imageCanvas.width - w));

            y = Math.max(0, Math.min(y, imageCanvas.height - h));

            w = Math.min(w, imageCanvas.width - x);

            h = Math.min(h, imageCanvas.height - y);


            currentSelectionRect = { x, y, w, h, isDefined: true };

            drawCanvasWithSelectionAndHandles(currentFileForAdjustment);

        }

        function handleInteractionEnd(e) {

             if (activeDragAction) {

                activeDragAction = null;

                imageCanvas.style.cursor = 'default';

                updateHandlesPositions();

            }

        }


        canvasContainer.addEventListener('mousedown', handleInteractionStart);

        document.addEventListener('mousemove', handleInteractionMove);

        document.addEventListener('mouseup', handleInteractionEnd);

        canvasContainer.addEventListener('touchstart', handleInteractionStart, { passive: false });

        document.addEventListener('touchmove', handleInteractionMove, { passive: false });

        document.addEventListener('touchend', handleInteractionEnd);

        applyAndDownloadButton.addEventListener('click', async () => {

            if (!currentSelectionRect.isDefined || allUploadedFiles.length === 0) {

                showMessage('Zone non définie ou aucun fichier chargé.', 'error');

                return;

            }

            loader.style.display = 'block';

            applyAndDownloadButton.disabled = true;

            let filesProcessedCount = 0;

            const chosenOutputFormat = outputFormatSelect.value;


            for (let i = 0; i < allUploadedFiles.length; i++) {

                const file = allUploadedFiles[i];

                const originalFilename = file.name.substring(0, file.name.lastIndexOf('.')) || file.name;

                showMessage(`Traitement de ${file.name} (${i+1}/${allUploadedFiles.length})...`, 'info', 60000);


                try {

                    let effectiveOutputFormat = chosenOutputFormat;

                    if (chosenOutputFormat === 'source') {

                        effectiveOutputFormat = file.type.startsWith('image/') ? (file.type === 'image/jpeg' ? 'image/jpeg' : 'image/png') : 'application/pdf';

                    }

                    if (file.type.startsWith('image/')) {

                        const img = await loadFileForAdjustment(file);

                        const tempCanvas = document.createElement('canvas');

                        const tempCtx = tempCanvas.getContext('2d');

                        tempCanvas.width = img.width;

                        tempCanvas.height = img.height;

                        tempCtx.drawImage(img, 0, 0);

                        tempCtx.fillStyle = 'white';

                        const scaleX = img.width / currentFileForAdjustment.width;

                        const scaleY = img.height / currentFileForAdjustment.height;

                        tempCtx.fillRect(currentSelectionRect.x * scaleX,

                                         currentSelectionRect.y * scaleY,

                                         currentSelectionRect.w * scaleX,

                                         currentSelectionRect.h * scaleY);

                        if (effectiveOutputFormat === 'application/pdf') {

                            const pdfOutput = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' });

                            const pageDataUrl = tempCanvas.toDataURL('image/png');

                            let pageOrientation = img.width > img.height ? 'l' : 'p';

                            let targetWidthPt = pageOrientation === 'l' ? A4_HEIGHT_PT : A4_WIDTH_PT;

                            let targetHeightPt = pageOrientation === 'l' ? A4_WIDTH_PT : A4_HEIGHT_PT;

                            pdfOutput.internal.pageSize.width = targetWidthPt;

                            pdfOutput.internal.pageSize.height = targetHeightPt;


                            const ratio = Math.min(targetWidthPt / img.width, targetHeightPt / img.height);

                            const scaledWidth = img.width * ratio;

                            const scaledHeight = img.height * ratio;

                            const xOffset = (targetWidthPt - scaledWidth) / 2;

                            const yOffset = (targetHeightPt - scaledHeight) / 2;


                            pdfOutput.addImage(pageDataUrl, 'PNG', xOffset, yOffset, scaledWidth, scaledHeight);

                            pdfOutput.save(`${originalFilename}_modifie.pdf`);

                        } else {

                            const extension = effectiveOutputFormat === 'image/jpeg' ? 'jpg' : 'png';

                            const dataURL = tempCanvas.toDataURL(effectiveOutputFormat, effectiveOutputFormat === 'image/jpeg' ? 0.9 : undefined);

                            triggerDownload(dataURL, `${originalFilename}_modifie.${extension}`);

                        }

                        filesProcessedCount++;


                    } else if (file.type === 'application/pdf') {

                        const fileReader = new FileReader();

                        const arrayBuffer = await new Promise((resolve, reject) => {

                            fileReader.onload = () => resolve(fileReader.result);

                            fileReader.onerror = reject;

                            fileReader.readAsArrayBuffer(file);

                        });

                        const pdfDocInstance = await pdfjsLib.getDocument(new Uint8Array(arrayBuffer)).promise;

                        const numPages = pdfDocInstance.numPages;


                        if (effectiveOutputFormat === 'image/png' || effectiveOutputFormat === 'image/jpeg') {

                            const page = await pdfDocInstance.getPage(1); // Only first page for image conversion

                            const viewport = page.getViewport({ scale: 2.0 });

                            const tempCanvas = document.createElement('canvas');

                            const tempCtx = tempCanvas.getContext('2d');

                            tempCanvas.width = viewport.width;

                            tempCanvas.height = viewport.height;

                            await page.render({ canvasContext: tempCtx, viewport: viewport }).promise;

                            tempCtx.fillStyle = 'white';

                            const scaleToPageX = viewport.width / currentFileForAdjustment.width;

                            const scaleToPageY = viewport.height / currentFileForAdjustment.height;

                            tempCtx.fillRect(currentSelectionRect.x * scaleToPageX,

                                             currentSelectionRect.y * scaleToPageY,

                                             currentSelectionRect.w * scaleToPageX,

                                             currentSelectionRect.h * scaleToPageY);


                            const extension = effectiveOutputFormat === 'image/jpeg' ? 'jpg' : 'png';

                            const dataURL = tempCanvas.toDataURL(effectiveOutputFormat, effectiveOutputFormat === 'image/jpeg' ? 0.9 : undefined);

                            triggerDownload(dataURL, `${originalFilename}_page1_modifie.${extension}`);

                        } else { // PDF to PDF

                            let pdfOutput;

                            for (let j = 1; j <= numPages; j++) {

                                const page = await pdfDocInstance.getPage(j);

                                const viewport = page.getViewport({ scale: 2.0 });

                                let pageOrientation = viewport.width > viewport.height ? 'l' : 'p';

                                let pageTargetWidthPt = pageOrientation === 'l' ? A4_HEIGHT_PT : A4_WIDTH_PT;

                                let pageTargetHeightPt = pageOrientation === 'l' ? A4_WIDTH_PT : A4_HEIGHT_PT;


                                if (j === 1) {

                                    pdfOutput = new jsPDF({ orientation: pageOrientation, unit: 'pt', format: [pageTargetWidthPt, pageTargetHeightPt] });

                                } else {

                                    pdfOutput.addPage([pageTargetWidthPt, pageTargetHeightPt], pageOrientation);

                                }

                                const tempCanvas = document.createElement('canvas');

                                const tempCtx = tempCanvas.getContext('2d');

                                tempCanvas.width = viewport.width;  

                                tempCanvas.height = viewport.height;

                                await page.render({ canvasContext: tempCtx, viewport: viewport }).promise;

                                tempCtx.fillStyle = 'white';

                                const scaleToPageX = viewport.width / currentFileForAdjustment.width;

                                const scaleToPageY = viewport.height / currentFileForAdjustment.height;

                                tempCtx.fillRect(currentSelectionRect.x * scaleToPageX,

                                                 currentSelectionRect.y * scaleToPageY,

                                                 currentSelectionRect.w * scaleToPageX,

                                                 currentSelectionRect.h * scaleToPageY);

                                const pageDataUrl = tempCanvas.toDataURL('image/png');

                                const ratio = Math.min(pageTargetWidthPt / viewport.width, pageTargetHeightPt / viewport.height);

                                const scaledWidth = viewport.width * ratio;

                                const scaledHeight = viewport.height * ratio;

                                const xOffset = (pageTargetWidthPt - scaledWidth) / 2;

                                const yOffset = (pageTargetHeightPt - scaledHeight) / 2;

                                pdfOutput.addImage(pageDataUrl, 'PNG', xOffset, yOffset, scaledWidth, scaledHeight);

                            }

                            if (pdfOutput) {

                               pdfOutput.save(`${originalFilename}_modifie.pdf`);

                            } else if (numPages > 0) {

                                console.error("pdfOutput was not initialized for PDF:", file.name);

                                showMessage(`Erreur interne lors de la création du PDF pour ${file.name}`, 'error');

                            }

                        }

                        filesProcessedCount++;

                    }

                    await new Promise(resolve => setTimeout(resolve, 200));

                } catch (err) {

                    console.error("Erreur traitement fichier:", file.name, err);

                    showMessage(`Erreur avec ${file.name}: ${err.message || 'Inconnue'}`, 'error');

                }

            }

            loader.style.display = 'none';

            applyAndDownloadButton.disabled = false;

            if (filesProcessedCount > 0) {

                showMessage(`${filesProcessedCount} fichier(s) traité(s) et téléchargé(s).`, 'success');

            } else if (allUploadedFiles.length > 0) {

                 showMessage(`Aucun fichier n'a pu être traité avec succès.`, 'error');

            }


            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

        });


        function triggerDownload(dataURL, filename) {

            const link = document.createElement('a');

            link.href = dataURL;

            link.download = filename;

            document.body.appendChild(link);

            link.click();

            document.body.removeChild(link);

        }

        // --- Fonction de Réinitialisation Globale ---

        resetAllButton.addEventListener('click', () => {

            resetApplicationState();

            showMessage('Tous les paramètres ont été réinitialisés.', 'info');

        });


        function resetApplicationState() {

            // Réinitialiser la sélection de quadrant

            allQuadrants.forEach(q => q.classList.remove('selected'));

            selectedQuadrantInfo = { orientation: null, quadrantIndex: null };


            // Réinitialiser les fichiers téléversés

            allUploadedFiles = [];

            fileUpload.value = ''; // Effacer la sélection de l'input file


            // Réinitialiser le canvas et l'état associé

            resetCanvasAndState(true); // true pour s'assurer que tout est nettoyé


            // Réinitialiser le sélecteur de format de sortie à sa valeur par défaut

            outputFormatSelect.value = 'source';


            // S'assurer que les boutons sont dans leur état initial

            initiateProcessingButton.disabled = true;

            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

            loader.style.display = 'none';


            // Cacher les messages

            messageBox.style.display = 'none';

        }



        function resetCanvasAndState(resetQuadrantSelectionInternal = true) { // Renommé pour éviter conflit

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            imageCanvas.style.display = 'none';

            removeHandles();

            currentFileForAdjustment = null;

            currentSelectionRect.isDefined = false;

            activeDragAction = null;

            // La désactivation/masquage des boutons est gérée par resetApplicationState ou le flux normal

            if (resetQuadrantSelectionInternal) { // Changé pour utiliser le paramètre interne

                allQuadrants.forEach(q => q.classList.remove('selected'));

                selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

            }

             // S'assurer que le bouton d'initiation est correctement (dés)activé

            checkInitiateButtonState();

        }


        checkInitiateButtonState(); // Appel initial

        window.addEventListener('resize', () => {

            if (currentSelectionRect.isDefined && handles.length > 0 && imageCanvas.offsetParent) {

                updateHandlesPositions();

            }

        });


    </script>

</body>

</html>






<!DOCTYPE html>

<html lang="fr">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

    <title>Suppresseur de Publicités pour Bordereaux (Batch)</title>

    <script src="https://cdn.tailwindcss.com"></script>

    <!-- PDF.js library -->

    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>

    <script>

        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';

    </script>

    <!-- jsPDF library -->

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>

    <style>

        body {

            font-family: 'Inter', sans-serif;

            overscroll-behavior-y: contain;

        }

        .a4-paper {

            border: 2px solid #000;

            display: grid;

            grid-template-columns: repeat(2, 1fr);

            grid-template-rows: repeat(2, 1fr);

            cursor: pointer;

            background-color: #f0f0f0;

        }

        .quadrant {

            border: 1px dashed #666;

            display: flex;

            justify-content: center;

            align-items: center;

            transition: background-color 0.2s ease-in-out;

        }

        .quadrant:hover {

            background-color: #e0e0e0;

        }

        .quadrant.selected {

            background-color: #ffffff !important;

            border: 2px solid #3b82f6;

        }

        #message-box {

            position: fixed;

            top: 20px;

            left: 50%;

            transform: translateX(-50%);

            padding: 10px 20px;

            border-radius: 8px;

            color: white;

            z-index: 1000;

            display: none;

            box-shadow: 0 4px 6px rgba(0,0,0,0.1);

        }

        #message-box.success { background-color: #28a745; }

        #message-box.error { background-color: #dc3545; }

        #message-box.info { background-color: #17a2b8; }


        .quadrant-icon { font-size: 10px; color: #888; }

        .portrait { width: 100px; height: 141.4px; }

        .landscape { width: 141.4px; height: 100px; }


        #imageCanvas {

            touch-action: none;

        }

        .handle {

            position: absolute;

            width: 14px;

            height: 14px;

            background-color: rgba(255, 0, 0, 0.6);

            border: 1px solid rgba(255, 255, 255, 0.8);

            border-radius: 50%;

            z-index: 10;

            touch-action: none;

        }

        #loader {

            border: 5px solid #f3f3f3;

            border-top: 5px solid #3498db;

            border-radius: 50%;

            width: 40px;

            height: 40px;

            animation: spin 1s linear infinite;

            margin: 10px auto;

            display: none;

        }

        @keyframes spin {

            0% { transform: rotate(0deg); }

            100% { transform: rotate(360deg); }

        }

    </style>

</head>

<body class="bg-gray-100 min-h-screen flex flex-col items-center justify-center p-4">


    <div id="message-box"></div>


    <div class="bg-white p-6 md:p-8 rounded-xl shadow-2xl w-full max-w-3xl">

        <header class="mb-6 text-center">

            <div class="flex justify-between items-center">

                <h1 class="text-2xl md:text-3xl font-bold text-gray-800 flex-grow text-center">Suppresseur de Publicités</h1>

                <button id="resetAllButtonTop" title="Réinitialiser tous les paramètres" class="p-2 text-gray-500 hover:text-blue-600 transition-colors">

                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rotate-ccw">

                        <path d="M3 2v6h6"/>

                        <path d="M3.31 15A9 9 0 0 0 20.7 7.5"/>

                        <path d="M21 22v-6h-6"/>

                        <path d="M20.7 9A9 9 0 0 0 3.31 16.5"/>

                    </svg>

                </button>

            </div>

            <p class="text-gray-600 mt-1">Masquez les pubs sur plusieurs fichiers à la fois.</p>

        </header>


        <main>

            <section id="step1Selection" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">1. Zone initiale approximative :</h2>

                 <div class="flex flex-col sm:flex-row justify-center items-center gap-6 sm:gap-10">

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Portrait</p>

                        <div id="portrait-paper" class="a4-paper portrait rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="portrait" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="portrait" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                    <div>

                        <p class="text-center text-sm font-medium text-gray-600 mb-1">Paysage</p>

                        <div id="landscape-paper" class="a4-paper landscape rounded-md overflow-hidden mx-auto">

                            <div class="quadrant" data-orientation="landscape" data-quadrant="0"><span class="quadrant-icon">HG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="1"><span class="quadrant-icon">HD</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="2"><span class="quadrant-icon">BG</span></div>

                            <div class="quadrant" data-orientation="landscape" data-quadrant="3"><span class="quadrant-icon">BD</span></div>

                        </div>

                    </div>

                </div>

            </section>


            <section id="step2Upload" class="mb-6">

                <h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">2. Téléversez vos fichiers (Images ou PDF) :</h2>

                <div class="flex flex-col items-center">

                    <input type="file" id="fileUpload" accept="image/*,application/pdf" multiple class="block w-full max-w-md text-sm text-gray-500

                        file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold

                        file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100

                        mb-2 p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">

                    <div class="my-2 text-sm text-gray-600">Format de sortie (si applicable) :

                        <select id="outputFormatSelect" class="ml-2 p-1 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-blue-500">

                            <option value="source">Comme source (PDF reste PDF, Image reste Image)</option>

                            <option value="image/png">PNG</option>

                            <option value="image/jpeg">JPEG</option>

                            <option value="application/pdf">PDF (convertir tout en PDF)</option>

                        </select>

                    </div>

                </div>

            </section>

            <section id="step3Adjust" class="mb-6 text-center">

                 <h2 class="text-xl font-semibold text-gray-700 mb-3">3. Ajustez la zone sur le 1er fichier et validez :</h2>

                <button id="initiateProcessingButton" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out disabled:opacity-50" disabled>

                    Afficher 1er Fichier et Ajuster Zone

                </button>

                <div id="canvasContainer" class="relative flex flex-col items-center mt-4">

                    <canvas id="imageCanvas" class="border border-gray-400 rounded-lg shadow-md max-w-full h-auto" style="display: none;"></canvas>

                </div>

                <div id="loader"></div>

                <button id="applyAndDownloadButton" class="hidden mt-4 bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-6 rounded-lg shadow-md transition duration-150 ease-in-out">

                    Appliquer à Tous et Télécharger

                </button>

            </section>

        </main>

        <footer class="mt-8 pt-4 border-t border-gray-200 flex justify-center">

             <button id="resetAllButtonBottom" title="Réinitialiser tous les paramètres" class="p-2 text-gray-500 hover:text-blue-600 transition-colors">

                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rotate-ccw">

                    <path d="M3 2v6h6"/>

                    <path d="M3.31 15A9 9 0 0 0 20.7 7.5"/>

                    <path d="M21 22v-6h-6"/>

                    <path d="M20.7 9A9 9 0 0 0 3.31 16.5"/>

                </svg>

            </button>

        </footer>

    </div>


    <script>

        const { jsPDF } = window.jspdf;


        // Références DOM

        const allQuadrants = document.querySelectorAll('.quadrant');

        const fileUpload = document.getElementById('fileUpload');

        const outputFormatSelect = document.getElementById('outputFormatSelect');

        const initiateProcessingButton = document.getElementById('initiateProcessingButton');

        const imageCanvas = document.getElementById('imageCanvas');

        const canvasContainer = document.getElementById('canvasContainer');

        const applyAndDownloadButton = document.getElementById('applyAndDownloadButton');

        const messageBox = document.getElementById('message-box');

        const loader = document.getElementById('loader');

        const resetAllButtonTop = document.getElementById('resetAllButtonTop');

        const resetAllButtonBottom = document.getElementById('resetAllButtonBottom');

        const ctx = imageCanvas.getContext('2d');


        // État global

        let selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

        let allUploadedFiles = [];

        let currentFileForAdjustment = null;

        let currentSelectionRect = { x: 0, y: 0, w: 0, h: 0, isDefined: false };

        let activeDragAction = null;

        let dragStartCoords = { x: 0, y: 0 };

        let rectStartCoords = { x: 0, y: 0, w: 0, h: 0 };

        const HANDLE_SIZE = 14;

        let handles = [];


        // --- Constantes pour PDF A4 (en points, 1pt = 1/72 inch) ---

        const A4_WIDTH_PT = 595.28;

        const A4_HEIGHT_PT = 841.89;



        function showMessage(text, type = 'info', duration = 4000) {

            messageBox.textContent = text;

            messageBox.className = '';

            messageBox.classList.add(type);

            messageBox.style.display = 'block';

            setTimeout(() => { messageBox.style.display = 'none'; }, duration);

        }


        function checkInitiateButtonState() {

            initiateProcessingButton.disabled = !(allUploadedFiles.length > 0 && selectedQuadrantInfo.quadrantIndex !== null);

        }


        allQuadrants.forEach(quadrant => {

            quadrant.addEventListener('click', () => {

                allQuadrants.forEach(q => q.classList.remove('selected'));

                quadrant.classList.add('selected');

                selectedQuadrantInfo.orientation = quadrant.dataset.orientation;

                selectedQuadrantInfo.quadrantIndex = parseInt(quadrant.dataset.quadrant);

                checkInitiateButtonState();

                showMessage(`Zone initiale: ${selectedQuadrantInfo.orientation}, Q${selectedQuadrantInfo.quadrantIndex + 1}. Téléversez des fichiers.`, 'info');

            });

        });


        fileUpload.addEventListener('change', (event) => {

            if (event.target.files.length === 0) {

                allUploadedFiles = [];

                currentFileForAdjustment = null;

                resetCanvasAndState(false);

                checkInitiateButtonState();

                return;

            }

            allUploadedFiles = Array.from(event.target.files);

            currentFileForAdjustment = null;

            resetCanvasAndState(false);


            if (allUploadedFiles.length > 0) {

                showMessage(`${allUploadedFiles.length} fichier(s) sélectionné(s). Prêt à ajuster le 1er.`, 'success');

            }

            checkInitiateButtonState();

        });

        async function loadFileForAdjustment(file) {

            return new Promise((resolve, reject) => {

                if (file.type.startsWith('image/')) {

                    const reader = new FileReader();

                    reader.onload = (e) => {

                        const img = new Image();

                        img.onload = () => resolve(img);

                        img.onerror = () => reject(new Error('Erreur chargement image pour ajustement.'));

                        img.src = e.target.result;

                    };

                    reader.onerror = () => reject(new Error('Erreur lecture fichier image.'));

                    reader.readAsDataURL(file);

                } else if (file.type === 'application/pdf') {

                    const fileReader = new FileReader();

                    fileReader.onload = function() {

                        const typedarray = new Uint8Array(this.result);

                        pdfjsLib.getDocument(typedarray).promise.then(pdfDoc_ => {

                            pdfDoc_.getPage(1).then(page => {

                                const viewport = page.getViewport({ scale: 2.0 });

                                const tempCanvas = document.createElement('canvas');

                                const tempCtx = tempCanvas.getContext('2d');

                                tempCanvas.height = viewport.height;

                                tempCanvas.width = viewport.width;

                                page.render({ canvasContext: tempCtx, viewport: viewport }).promise.then(() => {

                                    const img = new Image();

                                    img.onload = () => resolve(img);

                                    img.onerror = () => reject(new Error('Erreur conversion 1ère page PDF en image.'));

                                    img.src = tempCanvas.toDataURL('image/png');

                                }).catch(reject);

                            }).catch(reject);

                        }).catch(reject);

                    };

                    fileReader.onerror = () => reject(new Error('Erreur lecture fichier PDF.'));

                    fileReader.readAsArrayBuffer(file);

                } else {

                    reject(new Error('Type de fichier non supporté pour l\'ajustement.'));

                }

            });

        }


        initiateProcessingButton.addEventListener('click', async () => {

            if (allUploadedFiles.length === 0 || selectedQuadrantInfo.quadrantIndex === null) {

                showMessage('Sélectionnez zone ET téléversez au moins un fichier.', 'error');

                return;

            }

            loader.style.display = 'block';

            initiateProcessingButton.disabled = true;


            try {

                currentFileForAdjustment = await loadFileForAdjustment(allUploadedFiles[0]);

                setupInteractiveStage(currentFileForAdjustment);

                initiateProcessingButton.classList.add('hidden');

                applyAndDownloadButton.classList.remove('hidden');

                imageCanvas.style.display = 'block';

                showMessage('Ajustez le rectangle sur ce 1er fichier, puis validez.', 'info');

            } catch (error) {

                showMessage(error.message || 'Erreur chargement du 1er fichier.', 'error');

                console.error("Error loading first file for adjustment:", error);

            } finally {

                loader.style.display = 'none';

                initiateProcessingButton.disabled = false;

            }

        });


        function setupInteractiveStage(imageToAdjust) {

            imageCanvas.width = imageToAdjust.width;

            imageCanvas.height = imageToAdjust.height;

            const imgW = imageToAdjust.width;

            const imgH = imageToAdjust.height;

            currentSelectionRect.w = imgW / 2;

            currentSelectionRect.h = imgH / 2;


            switch (selectedQuadrantInfo.quadrantIndex) {

                case 0: currentSelectionRect.x = 0; currentSelectionRect.y = 0; break;

                case 1: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = 0; break;

                case 2: currentSelectionRect.x = 0; currentSelectionRect.y = imgH / 2; break;

                case 3: currentSelectionRect.x = imgW / 2; currentSelectionRect.y = imgH / 2; break;

            }

            currentSelectionRect.isDefined = true;

            createHandles();

            drawCanvasWithSelectionAndHandles(imageToAdjust);

        }

        function drawCanvasWithSelectionAndHandles(imageToDraw, isFinal = false) {

            if (!imageToDraw) return;

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            ctx.drawImage(imageToDraw, 0, 0, imageCanvas.width, imageCanvas.height);


            if (currentSelectionRect.isDefined) {

                if (isFinal) {

                    ctx.fillStyle = 'white';

                    ctx.fillRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    removeHandles();

                } else {

                    ctx.strokeStyle = 'rgba(255, 0, 0, 0.8)';

                    ctx.lineWidth = 2;

                    ctx.strokeRect(currentSelectionRect.x, currentSelectionRect.y, currentSelectionRect.w, currentSelectionRect.h);

                    updateHandlesPositions();

                }

            }

        }


        function createHandles() {

            removeHandles();

            const handleTypes = ['tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br'];

            handleTypes.forEach(type => {

                const handle = document.createElement('div');

                handle.classList.add('handle');

                handle.dataset.type = type;

                canvasContainer.appendChild(handle);

                handles.push(handle);

            });

            updateHandlesPositions();

        }


        function removeHandles() {

            handles.forEach(h => h.remove());

            handles = [];

        }


        function updateHandlesPositions() {

            if (!currentSelectionRect.isDefined || handles.length === 0 || !imageCanvas.offsetParent) return;

            const { x, y, w, h } = currentSelectionRect;

            const canvasRect = imageCanvas.getBoundingClientRect();


            const scaleX = canvasRect.width / imageCanvas.width;

            const scaleY = canvasRect.height / imageCanvas.height;


            const positions = {

                'tl': { left: x * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                't':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'tr': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: y * scaleY - HANDLE_SIZE / 2 },

                'l':  { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'r':  { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h / 2) * scaleY - HANDLE_SIZE / 2 },

                'bl': { left: x * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'b':  { left: (x + w / 2) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 },

                'br': { left: (x + w) * scaleX - HANDLE_SIZE / 2, top: (y + h) * scaleY - HANDLE_SIZE / 2 }

            };


            handles.forEach(handle => {

                const type = handle.dataset.type;

                handle.style.left = `${canvasRect.left + positions[type].left - canvasContainer.getBoundingClientRect().left}px`;

                handle.style.top = `${canvasRect.top + positions[type].top - canvasContainer.getBoundingClientRect().top}px`;

                handle.style.cursor = getResizeCursor(type);

                handle.style.display = 'block';

            });

        }

        function getResizeCursor(handleType) {

            switch (handleType) {

                case 'tl': case 'br': return 'nwse-resize';

                case 'tr': case 'bl': return 'nesw-resize';

                case 't':  case 'b':  return 'ns-resize';

                case 'l':  case 'r':  return 'ew-resize';

                default: return 'default';

            }

        }

        function getEventPosOnCanvas(event) {

            const rect = imageCanvas.getBoundingClientRect();

            let clientX, clientY;

            if (event.touches && event.touches.length > 0) {

                clientX = event.touches[0].clientX;

                clientY = event.touches[0].clientY;

            } else {

                clientX = event.clientX;

                clientY = event.clientY;

            }

            return {

                x: (clientX - rect.left) * (imageCanvas.width / rect.width),

                y: (clientY - rect.top) * (imageCanvas.height / rect.height)

            };

        }

        function handleInteractionStart(e) {

            if (!currentSelectionRect.isDefined || !currentFileForAdjustment) return;

            const target = e.target;

            const eventPos = getEventPosOnCanvas(e);


            if (target.classList.contains('handle')) {

                activeDragAction = `resize-${target.dataset.type}`;

            } else if (target === imageCanvas &&

                       eventPos.x >= currentSelectionRect.x && eventPos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                       eventPos.y >= currentSelectionRect.y && eventPos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                activeDragAction = 'move';

            } else {

                return;

            }

            if (activeDragAction && e.cancelable) {

                 e.preventDefault();

            }

            dragStartCoords = eventPos;

            rectStartCoords = { ...currentSelectionRect };

        }

        function handleInteractionMove(e) {

            if (!activeDragAction || !currentSelectionRect.isDefined) {

                if (e.type === 'mousemove' && (e.target === imageCanvas || e.target.classList.contains('handle'))) {

                    const mousePos = getEventPosOnCanvas(e);

                    let cursor = 'default';

                    if (currentSelectionRect.isDefined) {

                        for (const handle of handles) {

                            const handleRect = handle.getBoundingClientRect();

                            const canvasRect = imageCanvas.getBoundingClientRect();

                            const handleCanvasX = (handleRect.left - canvasRect.left + HANDLE_SIZE/2) * (imageCanvas.width / canvasRect.width);

                            const handleCanvasY = (handleRect.top - canvasRect.top + HANDLE_SIZE/2) * (imageCanvas.height / canvasRect.height);

                            if (Math.abs(mousePos.x - handleCanvasX) < HANDLE_SIZE && Math.abs(mousePos.y - handleCanvasY) < HANDLE_SIZE * 1.5) {

                                cursor = getResizeCursor(handle.dataset.type);

                                break;

                            }

                        }

                        if (cursor === 'default' &&

                            mousePos.x >= currentSelectionRect.x && mousePos.x <= currentSelectionRect.x + currentSelectionRect.w &&

                            mousePos.y >= currentSelectionRect.y && mousePos.y <= currentSelectionRect.y + currentSelectionRect.h) {

                            cursor = 'move';

                        }

                    }

                    imageCanvas.style.cursor = cursor;

                }

                return;

            }


            if (e.cancelable) {

                e.preventDefault();

            }


            const eventPos = getEventPosOnCanvas(e);

            const deltaX = eventPos.x - dragStartCoords.x;

            const deltaY = eventPos.y - dragStartCoords.y;

            let { x, y, w, h } = rectStartCoords;


            if (activeDragAction === 'move') {

                x += deltaX;

                y += deltaY;

            } else if (activeDragAction.startsWith('resize-')) {

                const type = activeDragAction.split('-')[1];

                if (type.includes('l')) { x += deltaX; w -= deltaX; }

                if (type.includes('r')) { w += deltaX; }

                if (type.includes('t')) { y += deltaY; h -= deltaY; }

                if (type.includes('b')) { h += deltaY; }

            }


            const minSize = 20;

            if (w < minSize) {

                if (activeDragAction.includes('l') || activeDragAction.includes('tl') || activeDragAction.includes('bl')) x = rectStartCoords.x + rectStartCoords.w - minSize;

                w = minSize;

            }

            if (h < minSize) {

                if (activeDragAction.includes('t') || activeDragAction.includes('tl') || activeDragAction.includes('tr')) y = rectStartCoords.y + rectStartCoords.h - minSize;

                h = minSize;

            }

            x = Math.max(0, Math.min(x, imageCanvas.width - w));

            y = Math.max(0, Math.min(y, imageCanvas.height - h));

            w = Math.min(w, imageCanvas.width - x);

            h = Math.min(h, imageCanvas.height - y);


            currentSelectionRect = { x, y, w, h, isDefined: true };

            drawCanvasWithSelectionAndHandles(currentFileForAdjustment);

        }

        function handleInteractionEnd(e) {

             if (activeDragAction) {

                activeDragAction = null;

                imageCanvas.style.cursor = 'default';

                updateHandlesPositions();

            }

        }


        canvasContainer.addEventListener('mousedown', handleInteractionStart);

        document.addEventListener('mousemove', handleInteractionMove);

        document.addEventListener('mouseup', handleInteractionEnd);

        canvasContainer.addEventListener('touchstart', handleInteractionStart, { passive: false });

        document.addEventListener('touchmove', handleInteractionMove, { passive: false });

        document.addEventListener('touchend', handleInteractionEnd);

        applyAndDownloadButton.addEventListener('click', async () => {

            if (!currentSelectionRect.isDefined || allUploadedFiles.length === 0) {

                showMessage('Zone non définie ou aucun fichier chargé.', 'error');

                return;

            }

            loader.style.display = 'block';

            applyAndDownloadButton.disabled = true;

            let filesProcessedCount = 0;

            const chosenOutputFormat = outputFormatSelect.value;


            for (let i = 0; i < allUploadedFiles.length; i++) {

                const file = allUploadedFiles[i];

                const originalFilename = file.name.substring(0, file.name.lastIndexOf('.')) || file.name;

                showMessage(`Traitement de ${file.name} (${i+1}/${allUploadedFiles.length})...`, 'info', 60000);


                try {

                    let effectiveOutputFormat = chosenOutputFormat;

                    if (chosenOutputFormat === 'source') {

                        effectiveOutputFormat = file.type.startsWith('image/') ? (file.type === 'image/jpeg' ? 'image/jpeg' : 'image/png') : 'application/pdf';

                    }

                    if (file.type.startsWith('image/')) {

                        const img = await loadFileForAdjustment(file);

                        const tempCanvas = document.createElement('canvas');

                        const tempCtx = tempCanvas.getContext('2d');

                        tempCanvas.width = img.width;

                        tempCanvas.height = img.height;

                        tempCtx.drawImage(img, 0, 0);

                        tempCtx.fillStyle = 'white';

                        const scaleX = img.width / currentFileForAdjustment.width;

                        const scaleY = img.height / currentFileForAdjustment.height;

                        tempCtx.fillRect(currentSelectionRect.x * scaleX,

                                         currentSelectionRect.y * scaleY,

                                         currentSelectionRect.w * scaleX,

                                         currentSelectionRect.h * scaleY);

                        if (effectiveOutputFormat === 'application/pdf') {

                            const pdfOutput = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' });

                            const pageDataUrl = tempCanvas.toDataURL('image/png');

                            let pageOrientation = img.width > img.height ? 'l' : 'p';

                            let targetWidthPt = pageOrientation === 'l' ? A4_HEIGHT_PT : A4_WIDTH_PT;

                            let targetHeightPt = pageOrientation === 'l' ? A4_WIDTH_PT : A4_HEIGHT_PT;

                            pdfOutput.internal.pageSize.width = targetWidthPt;

                            pdfOutput.internal.pageSize.height = targetHeightPt;


                            const ratio = Math.min(targetWidthPt / img.width, targetHeightPt / img.height);

                            const scaledWidth = img.width * ratio;

                            const scaledHeight = img.height * ratio;

                            const xOffset = (targetWidthPt - scaledWidth) / 2;

                            const yOffset = (targetHeightPt - scaledHeight) / 2;


                            pdfOutput.addImage(pageDataUrl, 'PNG', xOffset, yOffset, scaledWidth, scaledHeight);

                            pdfOutput.save(`${originalFilename}_modifie.pdf`);

                        } else {

                            const extension = effectiveOutputFormat === 'image/jpeg' ? 'jpg' : 'png';

                            const dataURL = tempCanvas.toDataURL(effectiveOutputFormat, effectiveOutputFormat === 'image/jpeg' ? 0.9 : undefined);

                            triggerDownload(dataURL, `${originalFilename}_modifie.${extension}`);

                        }

                        filesProcessedCount++;


                    } else if (file.type === 'application/pdf') {

                        const fileReader = new FileReader();

                        const arrayBuffer = await new Promise((resolve, reject) => {

                            fileReader.onload = () => resolve(fileReader.result);

                            fileReader.onerror = reject;

                            fileReader.readAsArrayBuffer(file);

                        });

                        const pdfDocInstance = await pdfjsLib.getDocument(new Uint8Array(arrayBuffer)).promise;

                        const numPages = pdfDocInstance.numPages;


                        if (effectiveOutputFormat === 'image/png' || effectiveOutputFormat === 'image/jpeg') {

                            const page = await pdfDocInstance.getPage(1);

                            const viewport = page.getViewport({ scale: 2.0 });

                            const tempCanvas = document.createElement('canvas');

                            const tempCtx = tempCanvas.getContext('2d');

                            tempCanvas.width = viewport.width;

                            tempCanvas.height = viewport.height;

                            await page.render({ canvasContext: tempCtx, viewport: viewport }).promise;

                            tempCtx.fillStyle = 'white';

                            const scaleToPageX = viewport.width / currentFileForAdjustment.width;

                            const scaleToPageY = viewport.height / currentFileForAdjustment.height;

                            tempCtx.fillRect(currentSelectionRect.x * scaleToPageX,

                                             currentSelectionRect.y * scaleToPageY,

                                             currentSelectionRect.w * scaleToPageX,

                                             currentSelectionRect.h * scaleToPageY);


                            const extension = effectiveOutputFormat === 'image/jpeg' ? 'jpg' : 'png';

                            const dataURL = tempCanvas.toDataURL(effectiveOutputFormat, effectiveOutputFormat === 'image/jpeg' ? 0.9 : undefined);

                            triggerDownload(dataURL, `${originalFilename}_page1_modifie.${extension}`);

                        } else { // PDF to PDF

                            let pdfOutput;

                            for (let j = 1; j <= numPages; j++) {

                                const page = await pdfDocInstance.getPage(j);

                                const viewport = page.getViewport({ scale: 2.0 });

                                let pageOrientation = viewport.width > viewport.height ? 'l' : 'p';

                                let pageTargetWidthPt = pageOrientation === 'l' ? A4_HEIGHT_PT : A4_WIDTH_PT;

                                let pageTargetHeightPt = pageOrientation === 'l' ? A4_WIDTH_PT : A4_HEIGHT_PT;


                                if (j === 1) {

                                    pdfOutput = new jsPDF({ orientation: pageOrientation, unit: 'pt', format: [pageTargetWidthPt, pageTargetHeightPt] });

                                } else {

                                    pdfOutput.addPage([pageTargetWidthPt, pageTargetHeightPt], pageOrientation);

                                }

                                const tempCanvas = document.createElement('canvas');

                                const tempCtx = tempCanvas.getContext('2d');

                                tempCanvas.width = viewport.width;  

                                tempCanvas.height = viewport.height;

                                await page.render({ canvasContext: tempCtx, viewport: viewport }).promise;

                                tempCtx.fillStyle = 'white';

                                const scaleToPageX = viewport.width / currentFileForAdjustment.width;

                                const scaleToPageY = viewport.height / currentFileForAdjustment.height;

                                tempCtx.fillRect(currentSelectionRect.x * scaleToPageX,

                                                 currentSelectionRect.y * scaleToPageY,

                                                 currentSelectionRect.w * scaleToPageX,

                                                 currentSelectionRect.h * scaleToPageY);

                                const pageDataUrl = tempCanvas.toDataURL('image/png');

                                const ratio = Math.min(pageTargetWidthPt / viewport.width, pageTargetHeightPt / viewport.height);

                                const scaledWidth = viewport.width * ratio;

                                const scaledHeight = viewport.height * ratio;

                                const xOffset = (pageTargetWidthPt - scaledWidth) / 2;

                                const yOffset = (pageTargetHeightPt - scaledHeight) / 2;

                                pdfOutput.addImage(pageDataUrl, 'PNG', xOffset, yOffset, scaledWidth, scaledHeight);

                            }

                            if (pdfOutput) {

                               pdfOutput.save(`${originalFilename}_modifie.pdf`);

                            } else if (numPages > 0) {

                                console.error("pdfOutput was not initialized for PDF:", file.name);

                                showMessage(`Erreur interne lors de la création du PDF pour ${file.name}`, 'error');

                            }

                        }

                        filesProcessedCount++;

                    }

                    await new Promise(resolve => setTimeout(resolve, 200));

                } catch (err) {

                    console.error("Erreur traitement fichier:", file.name, err);

                    showMessage(`Erreur avec ${file.name}: ${err.message || 'Inconnue'}`, 'error');

                }

            }

            loader.style.display = 'none';

            applyAndDownloadButton.disabled = false;

            if (filesProcessedCount > 0) {

                showMessage(`${filesProcessedCount} fichier(s) traité(s) et téléchargé(s).`, 'success');

            } else if (allUploadedFiles.length > 0) {

                 showMessage(`Aucun fichier n'a pu être traité avec succès.`, 'error');

            }


            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

        });


        function triggerDownload(dataURL, filename) {

            const link = document.createElement('a');

            link.href = dataURL;

            link.download = filename;

            document.body.appendChild(link);

            link.click();

            document.body.removeChild(link);

        }

        resetAllButtonTop.addEventListener('click', () => {

            resetApplicationState();

            showMessage('Tous les paramètres ont été réinitialisés.', 'info');

        });

        resetAllButtonBottom.addEventListener('click', () => {

            resetApplicationState();

            showMessage('Tous les paramètres ont été réinitialisés.', 'info');

        });



        function resetApplicationState() {

            allQuadrants.forEach(q => q.classList.remove('selected'));

            selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

            allUploadedFiles = [];

            fileUpload.value = '';

            resetCanvasAndState(true);

            outputFormatSelect.value = 'source';

            initiateProcessingButton.disabled = true;

            initiateProcessingButton.classList.remove('hidden');

            applyAndDownloadButton.classList.add('hidden');

            loader.style.display = 'none';

            messageBox.style.display = 'none';

        }



        function resetCanvasAndState(resetQuadrantSelectionInternal = true) {

            ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);

            imageCanvas.style.display = 'none';

            removeHandles();

            currentFileForAdjustment = null;

            currentSelectionRect.isDefined = false;

            activeDragAction = null;

            if (resetQuadrantSelectionInternal) {

                allQuadrants.forEach(q => q.classList.remove('selected'));

                selectedQuadrantInfo = { orientation: null, quadrantIndex: null };

            }

            checkInitiateButtonState();

        }


        checkInitiateButtonState();

        window.addEventListener('resize', () => {

            if (currentSelectionRect.isDefined && handles.length > 0 && imageCanvas.offsetParent) {

                updateHandlesPositions();

            }

        });


    </script>

</body>

</html>