#!/usr/bin/env node /** * Script pour extraire les positions EXACTES des placeholders depuis un PDF * Utilise pdfjs-dist pour accéder au texte rendu et ses coordonnées */ const fs = require('fs'); const path = require('path'); const https = require('https'); const http = require('http'); // Placeholder patterns à chercher const PATTERNS = [ /Signature Employeur/i, /Signature Employé/i, /Signature Salarié/i, /\{\{Signature[^}]*\}\}/g, ]; /** * Télécharge un fichier depuis une URL */ function downloadFile(url, filepath) { return new Promise((resolve, reject) => { const protocol = url.startsWith('https') ? https : http; const file = fs.createWriteStream(filepath); protocol.get(url, (response) => { response.pipe(file); file.on('finish', () => { file.close(); resolve(); }); }).on('error', (err) => { fs.unlink(filepath, () => {}); reject(err); }); }); } /** * Extrait les positions des placeholders en utilisant une approche basée sur le texte brut */ async function extractPositions(pdfPath) { console.log('\n📄 Extraction des positions des placeholders\n'); console.log(`Fichier: ${pdfPath}\n`); try { // Lire le fichier PDF const pdfBuffer = fs.readFileSync(pdfPath); const pdfText = pdfBuffer.toString('binary'); // Chercher les pages (marqueurs de page dans le PDF) const pages = []; let currentPage = 1; let pageStart = 0; // Chercher les marqueurs "endobj" qui marquent la fin des objets de page const pageMarkerRegex = /\/Type\s*\/Page(?!s)/g; let match; while ((match = pageMarkerRegex.exec(pdfText)) !== null) { pages.push({ num: currentPage, startOffset: pageStart, endOffset: match.index, }); pageStart = match.index; currentPage++; } pages.push({ num: currentPage, startOffset: pageStart, endOffset: pdfText.length, }); console.log(`Total pages détectées: ${pages.length}\n`); // Chercher les placeholders avec les patterns const results = []; for (const pattern of PATTERNS) { pattern.lastIndex = 0; let matchResult; while ((matchResult = pattern.exec(pdfText)) !== null) { const textFound = matchResult[0]; const position = matchResult.index; // Déterminer la page let pageNum = 1; for (const page of pages) { if (position >= page.startOffset && position <= page.endOffset) { pageNum = page.num; break; } } // Essayer d'extraire les coordonnées si c'est un placeholder formaté let role = 'Inconnu'; let width = 150; let height = 60; if (textFound.includes('Employeur')) { role = 'Employeur'; } else if (textFound.includes('Employé') || textFound.includes('Salarié')) { role = 'Salarié'; } // Extraire les dimensions si présentes dans le placeholder const dimensionsMatch = textFound.match(/height=(\d+);width=(\d+)/i); if (dimensionsMatch) { height = parseInt(dimensionsMatch[1]); width = parseInt(dimensionsMatch[2]); } results.push({ text: textFound, page: pageNum, offsetInPDF: position, role, width, height, }); } } if (results.length === 0) { console.log('❌ Aucun placeholder trouvé dans le PDF!\n'); console.log('Cherchez-vous les patterns corrects?\n'); console.log('Patterns en cours de recherche:'); PATTERNS.forEach(p => console.log(` - ${p}`)); console.log('\n'); return; } // Afficher les résultats console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('📍 PLACEHOLDERS TROUVÉS'); console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); results.forEach((result, idx) => { console.log(`${idx + 1}. ${result.text}`); console.log(` Rôle: ${result.role}`); console.log(` Page: ${result.page}`); console.log(` Dimensions: ${result.width} × ${result.height} mm`); console.log(` Position dans PDF: offset ${result.offsetInPDF}\n`); }); // Générer les positions pour test-odentas-sign.js console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('📋 POSITIONS POUR test-odentas-sign.js'); console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); console.log('positions: ['); // Grouper par rôle const byRole = {}; results.forEach(r => { if (!byRole[r.role]) byRole[r.role] = []; byRole[r.role].push(r); }); let xPos = 20; Object.entries(byRole).forEach(([role, items]) => { items.forEach(item => { console.log(` {`); console.log(` role: '${item.role}',`); console.log(` page: ${item.page},`); console.log(` x: ${xPos},`); console.log(` y: 260,`); console.log(` w: ${item.width},`); console.log(` h: ${item.height},`); console.log(` kind: 'signature',`); console.log(` label: '${item.text}',`); console.log(` },`); xPos += item.width + 20; }); }); console.log(']'); // Sauvegarder en JSON const outputPath = pdfPath.replace('.pdf', '-positions.json'); const positionsData = results.map(r => ({ role: r.role, page: r.page, x: 20, // À ajuster manuellement y: 260, // À ajuster manuellement w: r.width, h: r.height, kind: 'signature', label: r.text, })); fs.writeFileSync(outputPath, JSON.stringify(positionsData, null, 2)); console.log(`\n💾 Sauvegardé: ${outputPath}\n`); } catch (error) { console.error('❌ Erreur:', error.message); process.exit(1); } } // Point d'entrée const pdfPath = process.argv[2] || path.join(__dirname, 'test-contrat.pdf'); if (!fs.existsSync(pdfPath)) { console.error(`\n❌ Fichier PDF non trouvé: ${pdfPath}\n`); process.exit(1); } extractPositions(pdfPath).catch(console.error);