espace-paie-odentas/find-placeholder-positions.js
odentas 59749d481b feat: Migration Cloudinary vers Poppler pour conversion PDF→JPEG
- Remplacer Cloudinary (US) par solution 100% AWS eu-west-3
- Lambda odentas-sign-pdf-converter avec pdftoppm
- Lambda Layer poppler-utils v5 avec dépendances complètes
- Trigger S3 ObjectCreated pour conversion automatique
- Support multi-pages validé (PDF 3 pages)
- Stockage images dans S3 odentas-docs
- PDFImageViewer pour affichage images converties
- Conformité RGPD garantie (données EU uniquement)
2025-10-28 10:22:45 +01:00

213 lines
6.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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);