Files
bentopdf/public/workers/add-attachments.worker.js
alam00000 2c85ca74e9 feat: separate AGPL libraries and add dynamic WASM loading
- Add WASM settings page for configuring external AGPL modules
- Implement dynamic loading for PyMuPDF, Ghostscript, and CoherentPDF
- Add Cloudflare Worker proxy for serving WASM files with CORS
- Update all affected tool pages to check WASM availability
- Add showWasmRequiredDialog for missing module configuration

Documentation:
- Update README, licensing.html, and docs to clarify AGPL components
  are not bundled and must be configured separately
- Add WASM-PROXY.md deployment guide with recommended source URLs
- Rename "CPDF" to "CoherentPDF" for consistency
2026-01-27 15:26:26 +05:30

190 lines
4.8 KiB
JavaScript

let cpdfLoaded = false;
function loadCpdf(cpdfUrl) {
if (cpdfLoaded) return Promise.resolve();
return new Promise((resolve, reject) => {
if (typeof coherentpdf !== 'undefined') {
cpdfLoaded = true;
resolve();
return;
}
try {
self.importScripts(cpdfUrl);
cpdfLoaded = true;
resolve();
} catch (error) {
reject(new Error('Failed to load CoherentPDF: ' + error.message));
}
});
}
function parsePageRange(rangeString, totalPages) {
const pages = new Set();
const parts = rangeString.split(',').map((s) => s.trim());
for (const part of parts) {
if (part.includes('-')) {
const [start, end] = part.split('-').map((s) => parseInt(s.trim(), 10));
if (isNaN(start) || isNaN(end)) continue;
for (let i = Math.max(1, start); i <= Math.min(totalPages, end); i++) {
pages.add(i);
}
} else {
const pageNum = parseInt(part, 10);
if (!isNaN(pageNum) && pageNum >= 1 && pageNum <= totalPages) {
pages.add(pageNum);
}
}
}
return Array.from(pages).sort((a, b) => a - b);
}
function addAttachmentsToPDFInWorker(
pdfBuffer,
attachmentBuffers,
attachmentNames,
attachmentLevel,
pageRange
) {
try {
const uint8Array = new Uint8Array(pdfBuffer);
let pdf;
try {
pdf = coherentpdf.fromMemory(uint8Array, '');
} catch (error) {
const errorMsg = error.message || error.toString();
if (
errorMsg.includes('Failed to read PDF') ||
errorMsg.includes('Could not read object') ||
errorMsg.includes('No /Root entry') ||
errorMsg.includes('PDFError')
) {
self.postMessage({
status: 'error',
message:
'The PDF file has structural issues and cannot be processed. The file may be corrupted, incomplete, or created with non-standard tools. Please try:\n\n• Opening and re-saving the PDF in another PDF viewer\n• Using a different PDF file\n• Repairing the PDF with a PDF repair tool',
});
} else {
self.postMessage({
status: 'error',
message: `Failed to load PDF: ${errorMsg}`,
});
}
return;
}
const totalPages = coherentpdf.pages(pdf);
let targetPages = [];
if (attachmentLevel === 'page') {
if (!pageRange) {
self.postMessage({
status: 'error',
message: 'Page range is required for page-level attachments.',
});
coherentpdf.deletePdf(pdf);
return;
}
targetPages = parsePageRange(pageRange, totalPages);
if (targetPages.length === 0) {
self.postMessage({
status: 'error',
message: 'Invalid page range specified.',
});
coherentpdf.deletePdf(pdf);
return;
}
}
for (let i = 0; i < attachmentBuffers.length; i++) {
try {
const attachmentData = new Uint8Array(attachmentBuffers[i]);
const attachmentName = attachmentNames[i];
if (attachmentLevel === 'document') {
coherentpdf.attachFileFromMemory(attachmentData, attachmentName, pdf);
} else {
for (const pageNum of targetPages) {
coherentpdf.attachFileToPageFromMemory(
attachmentData,
attachmentName,
pdf,
pageNum
);
}
}
} catch (error) {
console.warn(`Failed to attach file ${attachmentNames[i]}:`, error);
self.postMessage({
status: 'error',
message: `Failed to attach file ${attachmentNames[i]}: ${error.message || error}`,
});
coherentpdf.deletePdf(pdf);
return;
}
}
const modifiedBytes = coherentpdf.toMemory(pdf, false, false);
coherentpdf.deletePdf(pdf);
const buffer = modifiedBytes.buffer.slice(
modifiedBytes.byteOffset,
modifiedBytes.byteOffset + modifiedBytes.byteLength
);
self.postMessage(
{
status: 'success',
modifiedPDF: buffer,
},
[buffer]
);
} catch (error) {
self.postMessage({
status: 'error',
message:
error instanceof Error
? error.message
: 'Unknown error occurred while adding attachments.',
});
}
}
self.onmessage = async function (e) {
const { cpdfUrl } = e.data;
if (!cpdfUrl) {
self.postMessage({
status: 'error',
message:
'CoherentPDF URL not provided. Please configure it in WASM Settings.',
});
return;
}
try {
await loadCpdf(cpdfUrl);
} catch (error) {
self.postMessage({
status: 'error',
message: error.message,
});
return;
}
if (e.data.command === 'add-attachments') {
addAttachmentsToPDFInWorker(
e.data.pdfBuffer,
e.data.attachmentBuffers,
e.data.attachmentNames,
e.data.attachmentLevel || 'document',
e.data.pageRange || ''
);
}
};