- generazione del PDF riscritto totalmente
- ora è possibile generarlo anche da backend - FIX: corretto la qualità del PDF e la dimensione non esatta in pixel...
This commit is contained in:
@@ -60,6 +60,14 @@ const { exec } = require('child_process');
|
||||
|
||||
const execPromise = util.promisify(exec);
|
||||
|
||||
const GenPdf = require('../modules/GenPdf');
|
||||
|
||||
const genPdf = new GenPdf();
|
||||
|
||||
(async () => {
|
||||
await genPdf.launch();
|
||||
})();
|
||||
|
||||
async function updateProductInfo(recproductInfoAttuale, product, idapp, mycatstr) {
|
||||
if (!recproductInfoAttuale || !mycatstr) return recproductInfoAttuale;
|
||||
|
||||
@@ -160,7 +168,7 @@ async function compressPdf(inputFile, outputFile, compressione) {
|
||||
}
|
||||
|
||||
// Valori ammessi per -dPDFSETTINGS di Ghostscript
|
||||
const validSettings = ['screen', 'ebook', 'printer', 'prepress', 'default'];
|
||||
const validSettings = ['screen', 'printer', 'high', 'maximum'];
|
||||
if (!validSettings.includes(compressione)) {
|
||||
console.warn(`Compressione '${compressione}' non valida, uso 'ebook'`);
|
||||
compressione = 'ebook';
|
||||
@@ -228,56 +236,204 @@ async function compressPdfWithPs2Pdf(inputFile, outputFile, compression = 'ebook
|
||||
}
|
||||
}
|
||||
|
||||
async function convertPDF_GS(inputFile, outputFile, width, height) {
|
||||
// Verifica che il file di input esista
|
||||
if (!(await tools.isFileExistsAsync(inputFile))) {
|
||||
throw new Error(`Il file di input non esiste: ${inputFile}`);
|
||||
}
|
||||
|
||||
// Converti cm in punti (1 cm = 28.34646 punti)
|
||||
const widthPt = width * 28.34646;
|
||||
const heightPt = height * 28.34646;
|
||||
|
||||
// Arrotonda i valori
|
||||
const widthPx = Math.round(widthPt);
|
||||
const heightPx = Math.round(heightPt);
|
||||
|
||||
// Comando Ghostscript
|
||||
const gsCommand = [
|
||||
'-sDEVICE=pdfwrite',
|
||||
'-dCompatibilityLevel=1.4',
|
||||
'-dPDFSETTINGS=/prepress',
|
||||
'-dNOPAUSE',
|
||||
'-dQUIET',
|
||||
'-dBATCH',
|
||||
`-dDEVICEWIDTHPOINTS=${widthPx}`,
|
||||
`-dDEVICEHEIGHTPOINTS=${heightPx}`,
|
||||
'-dDPIx=300',
|
||||
'-dDPIy=300',
|
||||
`-sOutputFile=${outputFile}`,
|
||||
inputFile,
|
||||
].join(' ');
|
||||
|
||||
// DIAGNOSI COMPLETA DEL PROBLEMA
|
||||
async function diagnosePDFProblem() {
|
||||
console.log('=== DIAGNOSI COMPLETA SISTEMA ===');
|
||||
|
||||
// 1. Test ambiente Node.js
|
||||
console.log('Node.js version:', process.version);
|
||||
console.log('Platform:', process.platform);
|
||||
console.log('Architecture:', process.arch);
|
||||
console.log('Current working directory:', process.cwd());
|
||||
|
||||
// 2. Test moduli disponibili
|
||||
try {
|
||||
console.log(gsCommand);
|
||||
await gs.executeSync(gsCommand); // Assicurati che executeSync restituisca una promessa o usa la funzione corretta se è sincrona.
|
||||
console.log('Conversione completata con successo!');
|
||||
|
||||
// Verifica che il file di output sia stato generato
|
||||
if (await tools.isFileExistsAsync(outputFile)) {
|
||||
console.log('File di output generato:', outputFile);
|
||||
} else {
|
||||
console.error('Il File di output NON è stato generato! :', outputFile);
|
||||
}
|
||||
console.log('Testing gs module...');
|
||||
console.log('gs object:', typeof gs, Object.keys(gs || {}));
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
throw new Error(`Il file di input non esiste: ${inputFile}`);
|
||||
} else if (error instanceof gs.GhostscriptError) {
|
||||
throw new Error(`Errore Ghostscript: ${error.message}`);
|
||||
} else {
|
||||
throw new Error(`Errore durante la conversione: ${error.message}`);
|
||||
}
|
||||
console.log('gs module error:', error.message);
|
||||
}
|
||||
|
||||
// 3. Test comando di sistema
|
||||
const { exec } = require('child_process');
|
||||
const util = require('util');
|
||||
const execAsync = util.promisify(exec);
|
||||
|
||||
try {
|
||||
console.log('Testing gs command from system...');
|
||||
const { stdout, stderr } = await execAsync('gs -version');
|
||||
console.log('GS Version stdout:', stdout);
|
||||
console.log('GS Version stderr:', stderr);
|
||||
} catch (error) {
|
||||
console.log('System gs command failed:', error.message);
|
||||
}
|
||||
|
||||
// 4. Test scrittura file
|
||||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
const testFile = path.join(process.cwd(), 'test_write.txt');
|
||||
|
||||
try {
|
||||
await fs.writeFile(testFile, 'test content');
|
||||
console.log('File write test: SUCCESS');
|
||||
await fs.unlink(testFile);
|
||||
} catch (error) {
|
||||
console.log('File write test: FAILED -', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ALTERNATIVA 1: Usando child_process direttamente
|
||||
async function convertPDF_ChildProcess(inputFile, outputFile, width, height, compressionLevel = 'screen') {
|
||||
const { spawn } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
console.log('=== CONVERSIONE CON CHILD_PROCESS E COMPRESSIONE ===');
|
||||
|
||||
// Verifica input
|
||||
if (!(await tools.isFileExistsAsync(inputFile))) {
|
||||
throw new Error(`File input non trovato: ${inputFile}`);
|
||||
}
|
||||
|
||||
// Assicurati che la directory output esista
|
||||
const outputDir = path.dirname(outputFile);
|
||||
const fs = require('fs').promises;
|
||||
try {
|
||||
await fs.mkdir(outputDir, { recursive: true });
|
||||
} catch (error) {
|
||||
// Directory già esistente, ok
|
||||
}
|
||||
|
||||
// const widthPt = Math.round(width * 28.34646);
|
||||
// const heightPt = Math.round(height * 28.34646);
|
||||
const widthPt = Math.round(width * 28.34646* 10);
|
||||
const heightPt = Math.round(height * 28.34646 * 10);
|
||||
|
||||
// Parametri di compressione ottimizzati
|
||||
const compressionSettings = {
|
||||
'maximum': [
|
||||
'-dPDFSETTINGS=/screen',
|
||||
'-dDownsampleColorImages=true',
|
||||
'-dColorImageResolution=72',
|
||||
'-dDownsampleGrayImages=true',
|
||||
'-dGrayImageResolution=72',
|
||||
'-dDownsampleMonoImages=true',
|
||||
'-dMonoImageResolution=72'
|
||||
],
|
||||
'high': [
|
||||
'-dPDFSETTINGS=/ebook',
|
||||
'-dDownsampleColorImages=true',
|
||||
'-dColorImageResolution=150',
|
||||
'-dDownsampleGrayImages=true',
|
||||
'-dGrayImageResolution=150'
|
||||
],
|
||||
'printer': [
|
||||
'-dPDFSETTINGS=/printer',
|
||||
'-dDownsampleColorImages=true',
|
||||
'-dColorImageResolution=300'
|
||||
],
|
||||
'screen': [
|
||||
'-dPDFSETTINGS=/screen',
|
||||
'-dDownsampleColorImages=true',
|
||||
'-dColorImageResolution=96',
|
||||
'-dDownsampleGrayImages=true',
|
||||
'-dGrayImageResolution=96'
|
||||
]
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const args = [
|
||||
'-sDEVICE=pdfwrite',
|
||||
'-dCompatibilityLevel=1.4',
|
||||
'-dNOPAUSE',
|
||||
'-dQUIET',
|
||||
'-dBATCH',
|
||||
'-dSAFER',
|
||||
|
||||
// Parametri di compressione
|
||||
...compressionSettings[compressionLevel] || compressionSettings['screen'],
|
||||
'-dCompressFonts=true',
|
||||
'-dSubsetFonts=true',
|
||||
'-dColorImageFilter=/DCTEncode',
|
||||
'-dGrayImageFilter=/DCTEncode',
|
||||
'-dEmbedAllFonts=true',
|
||||
|
||||
// Dimensioni pagina
|
||||
`-g${widthPt}x${heightPt}`,
|
||||
'-dFIXEDMEDIA',
|
||||
// '-dPDFFitPage',
|
||||
|
||||
// Output
|
||||
`-sOutputFile=${outputFile}`,
|
||||
inputFile
|
||||
];
|
||||
|
||||
console.log('Spawning gs with compression args:', args.join(' '));
|
||||
|
||||
const gsProcess = spawn('gs', args, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
gsProcess.stdout.on('data', (data) => {
|
||||
stdout += data.toString();
|
||||
if (stdout.length < 1000) { // Evita log troppo lunghi
|
||||
console.log('GS OUT:', data.toString().trim());
|
||||
}
|
||||
});
|
||||
|
||||
gsProcess.stderr.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
if (stderr.length < 1000) {
|
||||
console.log('GS ERR:', data.toString().trim());
|
||||
}
|
||||
});
|
||||
|
||||
gsProcess.on('close', async (code) => {
|
||||
console.log(`GS process closed with code: ${code}`);
|
||||
|
||||
if (code === 0) {
|
||||
// Attendi e verifica
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const exists = await tools.isFileExistsAsync(outputFile);
|
||||
if (exists) {
|
||||
// Verifica dimensioni per confermare compressione
|
||||
try {
|
||||
const originalStats = await tools.getFileStatsAsync(inputFile);
|
||||
const newStats = await tools.getFileStatsAsync(outputFile);
|
||||
const compressionRatio = ((originalStats.size - newStats.size) / originalStats.size * 100).toFixed(1);
|
||||
|
||||
console.log(`📁 File originale: ${(originalStats.size / 1024 / 1024).toFixed(2)} MB`);
|
||||
console.log(`📁 File compresso: ${(newStats.size / 1024 / 1024).toFixed(2)} MB`);
|
||||
console.log(`🗜️ Compressione: ${compressionRatio}%`);
|
||||
console.log('✅ SUCCESS: File generato e compresso');
|
||||
} catch (statsError) {
|
||||
console.log('Warning: impossibile calcolare statistiche compressione');
|
||||
}
|
||||
|
||||
resolve(outputFile);
|
||||
} else {
|
||||
console.log('❌ FAIL: File non generato nonostante exit code 0');
|
||||
reject(new Error('File non generato nonostante successo processo'));
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
reject(new Error(`Ghostscript failed with code ${code}: ${stderr}`));
|
||||
}
|
||||
});
|
||||
|
||||
gsProcess.on('error', (error) => {
|
||||
console.log('GS process error:', error);
|
||||
reject(new Error(`Failed to start Ghostscript: ${error.message}`));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function extractPdfInfo(inputFile) {
|
||||
@@ -436,9 +592,11 @@ async function ConvertPDF_Generatore(options, instampa) {
|
||||
if (options.compressione) {
|
||||
const mostrainfo = true;
|
||||
fileout_compressed = tools.removeFileExtension(fileout) + `_compressed.pdf`;
|
||||
await diagnosePDFProblem();
|
||||
|
||||
// await compressPdf(fileout, fileout_compressed, options.compressione);
|
||||
await compressPdfWithPs2Pdf(fileout, fileout_compressed, options.compressione);
|
||||
// await compressPdfWithPs2Pdf(fileout, fileout_compressed, options.compressione);
|
||||
await convertPDF_ChildProcess(fileout, fileout_compressed, options.width, options.height, options.compressione);
|
||||
|
||||
// if (mostrainfo) extractPdfInfo(fileout_compressed);
|
||||
}
|
||||
@@ -2489,4 +2647,21 @@ router.post('/miab', authenticate, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/generate-pdf', async (req, res) => {
|
||||
const { url, filename } = req.body;
|
||||
|
||||
if (!url || !filename) {
|
||||
return res.status(400).json({ error: 'url e filename sono richiesti' });
|
||||
}
|
||||
|
||||
try {
|
||||
const pdfPath = await genPdf.generatePdfFromUrl(url, filename);
|
||||
res.download(pdfPath);
|
||||
} catch (error) {
|
||||
console.error('Errore generazione PDF:', error);
|
||||
res.status(500).json({ error: 'Errore generazione PDF' });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -2019,7 +2019,7 @@ async function load(req, res, version = '0') {
|
||||
|
||||
const { data, totalTime, slowCalls } = await measurePromises(promises);
|
||||
// console.log('Risultati delle promise:', data);
|
||||
console.log('Tempo di esecuzione:', totalTime, 'secondi');
|
||||
// console.log('Tempo di esecuzione:', totalTime, 'secondi');
|
||||
//console.log('Le 10 chiamate più lente:', slowCalls);
|
||||
|
||||
// Aggiornamento delle informazioni dell'utente, se presente
|
||||
@@ -2276,68 +2276,63 @@ router.post('/upload_from_other_server/:dir', authenticate, (req, res) => {
|
||||
});
|
||||
|
||||
// Funzione principale che gestisce l'upload
|
||||
async function uploadFile(req, res, version, options) {
|
||||
quality = options?.quality || 'original';
|
||||
const dir = tools.invertescapeslash(req.params.dir);
|
||||
async function uploadFile(req, res, version, options = {}) {
|
||||
const quality = options.quality || 'original';
|
||||
const dirParam = req.params.dir || '';
|
||||
const dir = tools.invertescapeslash(dirParam);
|
||||
const idapp = req.user.idapp;
|
||||
|
||||
const form = new formidable.IncomingForm();
|
||||
form.parse(req);
|
||||
// Determina la cartella base
|
||||
const dirmain = version > 0 && tools.sulServer() ? '' : server_constants.DIR_PUBLIC_LOCALE;
|
||||
const baseUploadFolder = tools.getdirByIdApp(idapp) + dirmain + server_constants.DIR_UPLOAD;
|
||||
|
||||
let dirmain = version > 0 && tools.sulServer() ? '' : server_constants.DIR_PUBLIC_LOCALE;
|
||||
const mydir2 = folder + '/' + dir;
|
||||
tools.mkdirpath(mydir2);
|
||||
form.uploadDir = mydir2;
|
||||
// Directory di upload specifica
|
||||
const uploadDir = path.join(baseUploadFolder, dir);
|
||||
|
||||
try {
|
||||
form.on('fileBegin', async function (name, file) {
|
||||
console.log('1) Uploading ' + file.originalFilename);
|
||||
const mydir = folder + '/' + file.newFilename;
|
||||
file.path = mydir;
|
||||
// Crea la cartella se non esiste
|
||||
await tools.mkdirpath(uploadDir);
|
||||
|
||||
// Configura formidable
|
||||
const form = new formidable.IncomingForm({ uploadDir });
|
||||
|
||||
// Promisifica il parsing per usare await
|
||||
const files = await new Promise((resolve, reject) => {
|
||||
form.parse(req, (err, fields, files) => {
|
||||
if (err) reject(err);
|
||||
else resolve(files);
|
||||
});
|
||||
});
|
||||
|
||||
form.on('file', async function (name, file) {
|
||||
try {
|
||||
console.log('2) Uploading ' + file.originalFilename);
|
||||
const mydir = tools.getdirByIdApp(idapp) + dirmain + server_constants.DIR_UPLOAD + '/' + dir;
|
||||
await tools.mkdirpath(mydir);
|
||||
// files può contenere uno o più file: gestiamo uno per semplicità
|
||||
for (const key in files) {
|
||||
if (Object.prototype.hasOwnProperty.call(files, key)) {
|
||||
const file = files[key];
|
||||
|
||||
file.name = file.originalFilename;
|
||||
const resized_img = mydir + server_constants.PREFIX_IMG + file.originalFilename;
|
||||
const oldpath = file.newFilename;
|
||||
const fromfile = '.' + server_constants.DIR_UPLOAD + '/' + dir + '/' + oldpath;
|
||||
const tofile = '.' + server_constants.DIR_UPLOAD + '/' + dir + '/' + file.originalFilename;
|
||||
console.log('File ricevuto:', file[0].originalFilename);
|
||||
|
||||
|
||||
const oldFIle = file[0].filepath || file[0].path;
|
||||
|
||||
// Copia del file
|
||||
await moveFile(fromfile, tofile, res);
|
||||
// Nuovo percorso per il file rinominato
|
||||
const newFilePath = path.join(uploadDir, file[0].originalFilename);
|
||||
|
||||
// Ridimensionamento immagine
|
||||
await handleImageResizing(tofile, quality);
|
||||
// Sposta e rinomina il file (move è la funzione ottimizzata)
|
||||
// await move(file[0].filepath || file[0].path, newFilePath);
|
||||
await tools.move(oldFIle, newFilePath);
|
||||
|
||||
res.end();
|
||||
} catch (e) {
|
||||
console.log('Error during file processing: ', e);
|
||||
res.status(400).send();
|
||||
// Eventuale ridimensionamento immagine
|
||||
await handleImageResizing(newFilePath, quality);
|
||||
|
||||
console.log(`File processato e salvato in: ${newFilePath}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
form.on('end', function () {
|
||||
console.log('-> Upload completed');
|
||||
});
|
||||
|
||||
form.on('aborted', () => {
|
||||
console.error('Request aborted by the user');
|
||||
res.status(400).send();
|
||||
});
|
||||
|
||||
form.on('error', (err) => {
|
||||
console.error('Error Uploading', err?.message);
|
||||
res.status(400).send();
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('Error', e);
|
||||
res.status(200).send('Upload completato con successo');
|
||||
} catch (err) {
|
||||
console.error('Errore durante uploadFile:', err);
|
||||
if (!res.headersSent) {
|
||||
res.status(500).send("Errore durante l'upload");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2345,7 +2340,7 @@ async function uploadFile(req, res, version, options) {
|
||||
async function moveFile(fromfile, tofile, res) {
|
||||
console.log('Moving file from ' + fromfile + ' to ' + tofile);
|
||||
try {
|
||||
if (!tools.sulServer()) {
|
||||
if (false && !tools.sulServer()) {
|
||||
console.log('Copying file (locally):', fromfile, 'to', tofile);
|
||||
await tools.execScriptNoOutput("sudo cp -R '" + fromfile + "' '" + tofile + "'");
|
||||
res.end();
|
||||
@@ -2367,7 +2362,7 @@ async function handleImageResizing(filePath, quality) {
|
||||
|
||||
try {
|
||||
// Ridimensionamento per la qualità "small"
|
||||
|
||||
|
||||
if (quality === 'small' || quality === 'both') {
|
||||
await resizeImage(filePath, resizedSmallPath, 64, 64);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user