- 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)
213 lines
6.5 KiB
JavaScript
213 lines
6.5 KiB
JavaScript
#!/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);
|