ProyectoSammi/pdfGenerator.js
2026-08-19 17:28:16 -04:00

166 lines
5.5 KiB
JavaScript

const { dialog } = require('electron');
const PDFDocument = require('pdfkit');
const fs = require('fs');
const path = require('path');
async function generatePDF(data, tipo = 'orden') {
console.log(`📄 [PDF] Generando PDF tipo "${tipo}" para ${data.length} registros`);
if (!Array.isArray(data)) throw new Error('Datos inválidos');
const { filePath } = await dialog.showSaveDialog({
title: tipo === 'orden' ? "Guardar Orden de Mérito" : "Guardar Lista de No Recomendados",
filters: [{ name: "PDF Files", extensions: ["pdf"] }]
});
if (!filePath) throw new Error('Operación cancelada');
return new Promise((resolve, reject) => {
const doc = new PDFDocument({ margin: 30, size: 'letter', bufferPages: true });
const stream = fs.createWriteStream(filePath);
stream.on('error', err => {
console.error("❌ Error en el stream:", err.message);
reject(new Error("Fallo en la escritura del archivo PDF"));
});
stream.on('finish', () => {
console.log("✅ PDF finalizado:", filePath);
resolve({ success: true, path: filePath });
});
doc.pipe(stream);
const generateContent = async () => {
try {
const logoLeft = path.join(__dirname, 'assets', 'imagen1.png');
const logoRight = path.join(__dirname, 'assets', 'imagen2.png');
const logoWidth = 60; // Tamaño unificado para ambos logos
if (fs.existsSync(logoLeft)) {
doc.image(logoLeft, 30, 30, { width: logoWidth });
}
if (fs.existsSync(logoRight)) {
doc.image(logoRight, doc.page.width - 30 - logoWidth, 30, { width: logoWidth });
}
// Espacio para los logos (que están a altura Y = 30), dejamos un poco más abajo el texto
const headerStartY = 100;
doc.font('Helvetica-Bold')
.fontSize(10)
.text('REPÚBLICA BOLIVARIANA DE VENEZUELA', { align: 'center', baseline: 'top', continued: false })
.moveDown(0.5)
.text('MINISTERIO DEL PODER POPULAR PARA LA DEFENSA', { align: 'center' })
.moveDown(0.5)
.text('EJÉRCITO BOLIVARIANO', { align: 'center' })
.moveDown(0.5)
.text('DIRECCIÓN DE TECNOLOGIA DE LA INFORMACIÓN Y LAS COMUNICACIONES', { align: 'center' });
doc.moveDown(2); // espacio después del encabezado
// Título según tipo
const title = tipo === 'orden' ? 'ORDEN DE MÉRITO OFICIAL' : 'LISTADO DE NO RECOMENDADOS';
doc.fontSize(12).text(title, { align: 'center' }).moveDown(2);
// Filtrado de data
const isOrden = tipo === 'orden';
const filteredData = isOrden
? [...data].sort((a, b) => (b.resultado ?? b.niea ?? 0) - (a.resultado ?? a.niea ?? 0))
: data.filter(item => !item.condicion?.toLowerCase().includes('recomendado'));
// Columnas
const headers = isOrden
? ['ID', 'GRADO', 'NOMBRE Y APELLIDO', 'CÉDULA', 'NIEA', 'RESULTADO']
: ['ID', 'GRADO', 'NOMBRE Y APELLIDO', 'CÉDULA', 'MOTIVO'];
const widths = isOrden
? [30, 70, 200, 90, 60, 60]
: [30, 70, 220, 90, 100];
let y = doc.y + 10;
doc.font('Helvetica-Bold').fontSize(9);
drawRow(y, headers, widths);
y += 22;
filteredData.forEach((item, index) => {
if (y > doc.page.height - 100) {
doc.addPage();
y = 40;
}
const row = isOrden
? [
index + 1,
item.grado || 'N/D',
item.nombreApellido || 'N/D',
item.cedula?.toString() || 'N/D',
(item.niea ?? item.resultado ?? 0).toFixed(3),
(item.resultado ?? item.niea ?? 0).toFixed(3)
]
: [
index + 1,
item.grado || 'N/D',
item.nombreApellido || 'N/D',
item.cedula?.toString() || 'N/D',
item.condicion || 'Sin especificar'
];
doc.font('Helvetica').fontSize(8);
drawRow(y, row, widths);
y += 22;
});
// Firmas
const signatureY = y + 30;
doc.fontSize(9)
.text('_________________________', 50, signatureY, { width: 200, align: 'center' })
.text('CNEL MIGUEL J. RODRÍGUEZ', 50, signatureY + 15, { width: 200, align: 'center' })
.text('_________________________', doc.page.width - 250, signatureY, { width: 200, align: 'center' })
.text('MAYO JOSÉ M. SÁNCHEZ', doc.page.width - 250, signatureY + 15, { width: 200, align: 'center' });
} catch (err) {
throw new Error("Contenido falló: " + err.message);
}
};
function drawRow(y, rowData, widths) {
let x = 30;
rowData.forEach((text, i) => {
doc.text(text.toString(), x + 5, y + 5, {
width: widths[i] - 10,
height: 20,
align: 'left',
ellipsis: true
});
x += widths[i];
});
drawBorders(y, widths);
}
function drawBorders(y, widths) {
let x = 30;
widths.forEach((w, i) => {
if (i < widths.length - 1) {
doc.moveTo(x + w, y).lineTo(x + w, y + 22).stroke();
}
x += w;
});
doc.moveTo(30, y).lineTo(doc.page.width - 30, y).stroke();
}
generateContent()
.then(() => doc.end())
.catch(err => {
console.error("❌ Error al generar contenido:", err.message);
doc.end();
reject(err);
});
});
}
module.exports = { generatePDF };