feat(pdf-tools): add linearize pdf tool for web optimization
Add new linearize PDF tool that optimizes PDFs for fast web viewing using qpdf-wasm. Includes UI components, logic implementation, and test updates. The tool processes multiple PDFs and downloads them in a ZIP archive.
This commit is contained in:
122
src/js/logic/linearize.ts
Normal file
122
src/js/logic/linearize.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import createModule from '@neslinesli93/qpdf-wasm';
|
||||
import { showLoader, hideLoader, showAlert } from '../ui';
|
||||
import { readFileAsArrayBuffer, downloadFile } from '../utils/helpers';
|
||||
import { state } from '../state';
|
||||
import JSZip from 'jszip';
|
||||
|
||||
let qpdfInstance: any = null;
|
||||
|
||||
async function initializeQpdf() {
|
||||
if (qpdfInstance) {
|
||||
return qpdfInstance;
|
||||
}
|
||||
showLoader('Initializing optimization engine...');
|
||||
try {
|
||||
qpdfInstance = await createModule({
|
||||
locateFile: () => '/qpdf.wasm',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize qpdf-wasm:', error);
|
||||
showAlert(
|
||||
'Initialization Error',
|
||||
'Could not load the optimization engine. Please refresh the page and try again.'
|
||||
);
|
||||
throw error;
|
||||
} finally {
|
||||
hideLoader();
|
||||
}
|
||||
return qpdfInstance;
|
||||
}
|
||||
|
||||
export async function linearizePdf() {
|
||||
// Check if there are files and at least one PDF
|
||||
const pdfFiles = state.files.filter((file: File) => file.type === 'application/pdf');
|
||||
if (!pdfFiles || pdfFiles.length === 0) {
|
||||
showAlert('No PDF Files', 'Please upload at least one PDF file.');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoader('Optimizing PDFs for web view (linearizing)...');
|
||||
const zip = new JSZip(); // Create a JSZip instance
|
||||
let qpdf: any;
|
||||
let successCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
try {
|
||||
qpdf = await initializeQpdf();
|
||||
|
||||
for (let i = 0; i < pdfFiles.length; i++) {
|
||||
const file = pdfFiles[i];
|
||||
const inputPath = `/input_${i}.pdf`;
|
||||
const outputPath = `/output_${i}.pdf`;
|
||||
|
||||
showLoader(`Optimizing ${file.name} (${i + 1}/${pdfFiles.length})...`);
|
||||
|
||||
try {
|
||||
const fileBuffer = await readFileAsArrayBuffer(file);
|
||||
const uint8Array = new Uint8Array(fileBuffer as ArrayBuffer);
|
||||
|
||||
qpdf.FS.writeFile(inputPath, uint8Array);
|
||||
|
||||
const args = [
|
||||
inputPath,
|
||||
'--linearize',
|
||||
outputPath,
|
||||
];
|
||||
|
||||
qpdf.callMain(args);
|
||||
|
||||
const outputFile = qpdf.FS.readFile(outputPath, { encoding: 'binary' });
|
||||
if (!outputFile || outputFile.length === 0) {
|
||||
console.error(`Linearization resulted in an empty file for ${file.name}.`);
|
||||
throw new Error(`Processing failed for ${file.name}.`);
|
||||
}
|
||||
|
||||
zip.file(`linearized-${file.name}`, outputFile, { binary: true });
|
||||
successCount++;
|
||||
|
||||
} catch (fileError: any) {
|
||||
errorCount++;
|
||||
console.error(`Failed to linearize ${file.name}:`, fileError);
|
||||
// Optionally add an error marker/file to the zip? For now, we just skip.
|
||||
} finally {
|
||||
// Clean up WASM filesystem for this file
|
||||
try {
|
||||
if (qpdf?.FS) {
|
||||
if (qpdf.FS.analyzePath(inputPath).exists) {
|
||||
qpdf.FS.unlink(inputPath);
|
||||
}
|
||||
if (qpdf.FS.analyzePath(outputPath).exists) {
|
||||
qpdf.FS.unlink(outputPath);
|
||||
}
|
||||
}
|
||||
} catch (cleanupError) {
|
||||
console.warn(`Failed to cleanup WASM FS for ${file.name}:`, cleanupError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (successCount === 0) {
|
||||
throw new Error('No PDF files could be linearized.');
|
||||
}
|
||||
|
||||
showLoader('Generating ZIP file...');
|
||||
const zipBlob = await zip.generateAsync({ type: 'blob' });
|
||||
downloadFile(zipBlob, 'linearized-pdfs.zip');
|
||||
|
||||
let alertMessage = `${successCount} PDF(s) linearized successfully.`;
|
||||
if (errorCount > 0) {
|
||||
alertMessage += ` ${errorCount} file(s) failed.`;
|
||||
}
|
||||
showAlert('Processing Complete', alertMessage);
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('Linearization process error:', error);
|
||||
showAlert(
|
||||
'Linearization Failed',
|
||||
`An error occurred: ${error.message || 'Unknown error'}.`
|
||||
);
|
||||
} finally {
|
||||
hideLoader();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user