refactor(qpdf): move initialization logic to helpers module

Consolidate qpdf initialization code into helpers.ts to avoid duplication
Improve error message for password protected PDFs
Fortify encryption logic by auto-filling owner password when empty
This commit is contained in:
abdullahalam123
2025-10-24 11:37:15 +05:30
parent e0d3075609
commit e3468e3aaa
4 changed files with 47 additions and 89 deletions

View File

@@ -1,31 +1,6 @@
import { showLoader, hideLoader, showAlert } from '../ui.js'; import { showLoader, hideLoader, showAlert } from '../ui.js';
import { downloadFile, readFileAsArrayBuffer } from '../utils/helpers.js'; import { downloadFile, initializeQpdf, readFileAsArrayBuffer } from '../utils/helpers.js';
import { state } from '../state.js'; import { state } from '../state.js';
import createModule from '@neslinesli93/qpdf-wasm';
let qpdfInstance: any = null;
async function initializeQpdf() {
if (qpdfInstance) {
return qpdfInstance;
}
showLoader('Initializing PDF 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 PDF engine. Please refresh the page and try again.'
);
throw error;
} finally {
hideLoader();
}
return qpdfInstance;
}
export async function changePermissions() { export async function changePermissions() {
const file = state.files[0]; const file = state.files[0];
@@ -156,7 +131,7 @@ export async function changePermissions() {
} else { } else {
showAlert( showAlert(
'Processing Failed', 'Processing Failed',
`An error occurred: ${error.message || 'The PDF might be corrupted.'}` `An error occurred: ${error.message || 'The PDF might be corrupted or password protected.'}`
); );
} }
} finally { } finally {

View File

@@ -1,31 +1,6 @@
import { showLoader, hideLoader, showAlert } from '../ui.js'; import { showLoader, hideLoader, showAlert } from '../ui.js';
import { downloadFile, readFileAsArrayBuffer } from '../utils/helpers.js'; import { downloadFile, initializeQpdf, readFileAsArrayBuffer } from '../utils/helpers.js';
import { state } from '../state.js'; import { state } from '../state.js';
import createModule from '@neslinesli93/qpdf-wasm';
let qpdfInstance: any = null;
async function initializeQpdf() {
if (qpdfInstance) {
return qpdfInstance;
}
showLoader('Initializing decryption 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 decryption engine. Please refresh the page and try again.'
);
throw error;
} finally {
hideLoader();
}
return qpdfInstance;
}
export async function decrypt() { export async function decrypt() {
const file = state.files[0]; const file = state.files[0];

View File

@@ -1,38 +1,13 @@
import { showLoader, hideLoader, showAlert } from '../ui.js'; import { showLoader, hideLoader, showAlert } from '../ui.js';
import { downloadFile, readFileAsArrayBuffer } from '../utils/helpers.js'; import { downloadFile, initializeQpdf, readFileAsArrayBuffer } from '../utils/helpers.js';
import { state } from '../state.js'; import { state } from '../state.js';
import createModule from '@neslinesli93/qpdf-wasm';
let qpdfInstance: any = null;
async function initializeQpdf() {
if (qpdfInstance) {
return qpdfInstance;
}
showLoader('Initializing encryption 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 encryption engine. Please refresh the page and try again.'
);
throw error;
} finally {
hideLoader();
}
return qpdfInstance;
}
export async function encrypt() { export async function encrypt() {
const file = state.files[0]; const file = state.files[0];
const userPassword = const userPassword =
(document.getElementById('user-password-input') as HTMLInputElement) (document.getElementById('user-password-input') as HTMLInputElement)
?.value || ''; ?.value || '';
const ownerPassword = const ownerPasswordInput =
(document.getElementById('owner-password-input') as HTMLInputElement) (document.getElementById('owner-password-input') as HTMLInputElement)
?.value || ''; ?.value || '';
@@ -41,6 +16,9 @@ export async function encrypt() {
return; return;
} }
const ownerPassword = ownerPasswordInput || userPassword;
const hasDistinctOwnerPassword = ownerPasswordInput !== '';
const inputPath = '/input.pdf'; const inputPath = '/input.pdf';
const outputPath = '/output.pdf'; const outputPath = '/output.pdf';
let qpdf: any; let qpdf: any;
@@ -61,11 +39,12 @@ export async function encrypt() {
inputPath, inputPath,
'--encrypt', '--encrypt',
userPassword, userPassword,
ownerPassword, // Can be empty ownerPassword,
'256', '256',
]; ];
if (ownerPassword) { // Only add restrictions if a distinct owner password was provided
if (hasDistinctOwnerPassword) {
args.push( args.push(
'--modify=none', '--modify=none',
'--extract=n', '--extract=n',
@@ -78,12 +57,10 @@ export async function encrypt() {
); );
} }
if (!ownerPassword) {
args.push('--allow-insecure');
}
args.push('--', outputPath); args.push('--', outputPath);
console.log(args);
try { try {
qpdf.callMain(args); qpdf.callMain(args);
} catch (qpdfError: any) { } catch (qpdfError: any) {
@@ -106,9 +83,9 @@ export async function encrypt() {
hideLoader(); hideLoader();
let successMessage = 'PDF encrypted successfully with 256-bit AES!'; let successMessage = 'PDF encrypted successfully with 256-bit AES!';
if (!ownerPassword) { if (!hasDistinctOwnerPassword) {
successMessage += successMessage +=
' Note: Without an owner password, the PDF has no usage restrictions.'; ' Note: Without a separate owner password, the PDF has no usage restrictions.';
} }
showAlert('Success', successMessage); showAlert('Success', successMessage);

View File

@@ -1,3 +1,6 @@
import createModule from '@neslinesli93/qpdf-wasm';
import { showLoader, hideLoader, showAlert } from '../ui';
const STANDARD_SIZES = { const STANDARD_SIZES = {
A4: { width: 595.28, height: 841.89 }, A4: { width: 595.28, height: 841.89 },
Letter: { width: 612, height: 792 }, Letter: { width: 612, height: 792 },
@@ -146,3 +149,31 @@ export function formatIsoDate(isoDateString) {
return isoDateString; // Return original string on any error return isoDateString; // Return original string on any error
} }
} }
let qpdfInstance: any = null;
/**
* Initialize qpdf-wasm singleton.
* Subsequent calls return the same instance.
*/
export async function initializeQpdf() {
if (qpdfInstance) return qpdfInstance;
showLoader('Initializing PDF 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 PDF engine. Please refresh the page and try again.'
);
throw error;
} finally {
hideLoader();
}
return qpdfInstance;
}