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
This commit is contained in:
@@ -1,13 +1,32 @@
|
||||
const baseUrl = self.location.href.substring(0, self.location.href.lastIndexOf('/workers/') + 1);
|
||||
self.importScripts(baseUrl + 'coherentpdf.browser.min.js');
|
||||
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());
|
||||
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));
|
||||
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);
|
||||
@@ -23,7 +42,13 @@ function parsePageRange(rangeString, totalPages) {
|
||||
return Array.from(pages).sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
function addAttachmentsToPDFInWorker(pdfBuffer, attachmentBuffers, attachmentNames, attachmentLevel, pageRange) {
|
||||
function addAttachmentsToPDFInWorker(
|
||||
pdfBuffer,
|
||||
attachmentBuffers,
|
||||
attachmentNames,
|
||||
attachmentLevel,
|
||||
pageRange
|
||||
) {
|
||||
try {
|
||||
const uint8Array = new Uint8Array(pdfBuffer);
|
||||
|
||||
@@ -33,18 +58,21 @@ function addAttachmentsToPDFInWorker(pdfBuffer, attachmentBuffers, attachmentNam
|
||||
} catch (error) {
|
||||
const errorMsg = error.message || error.toString();
|
||||
|
||||
if (errorMsg.includes('Failed to read PDF') ||
|
||||
if (
|
||||
errorMsg.includes('Failed to read PDF') ||
|
||||
errorMsg.includes('Could not read object') ||
|
||||
errorMsg.includes('No /Root entry') ||
|
||||
errorMsg.includes('PDFError')) {
|
||||
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'
|
||||
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}`
|
||||
message: `Failed to load PDF: ${errorMsg}`,
|
||||
});
|
||||
}
|
||||
return;
|
||||
@@ -57,7 +85,7 @@ function addAttachmentsToPDFInWorker(pdfBuffer, attachmentBuffers, attachmentNam
|
||||
if (!pageRange) {
|
||||
self.postMessage({
|
||||
status: 'error',
|
||||
message: 'Page range is required for page-level attachments.'
|
||||
message: 'Page range is required for page-level attachments.',
|
||||
});
|
||||
coherentpdf.deletePdf(pdf);
|
||||
return;
|
||||
@@ -66,7 +94,7 @@ function addAttachmentsToPDFInWorker(pdfBuffer, attachmentBuffers, attachmentNam
|
||||
if (targetPages.length === 0) {
|
||||
self.postMessage({
|
||||
status: 'error',
|
||||
message: 'Invalid page range specified.'
|
||||
message: 'Invalid page range specified.',
|
||||
});
|
||||
coherentpdf.deletePdf(pdf);
|
||||
return;
|
||||
@@ -82,21 +110,25 @@ function addAttachmentsToPDFInWorker(pdfBuffer, attachmentBuffers, attachmentNam
|
||||
coherentpdf.attachFileFromMemory(attachmentData, attachmentName, pdf);
|
||||
} else {
|
||||
for (const pageNum of targetPages) {
|
||||
coherentpdf.attachFileToPageFromMemory(attachmentData, attachmentName, pdf, pageNum);
|
||||
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}`
|
||||
message: `Failed to attach file ${attachmentNames[i]}: ${error.message || error}`,
|
||||
});
|
||||
coherentpdf.deletePdf(pdf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the modified PDF
|
||||
const modifiedBytes = coherentpdf.toMemory(pdf, false, false);
|
||||
coherentpdf.deletePdf(pdf);
|
||||
|
||||
@@ -105,22 +137,46 @@ function addAttachmentsToPDFInWorker(pdfBuffer, attachmentBuffers, attachmentNam
|
||||
modifiedBytes.byteOffset + modifiedBytes.byteLength
|
||||
);
|
||||
|
||||
self.postMessage({
|
||||
status: 'success',
|
||||
modifiedPDF: buffer
|
||||
}, [buffer]);
|
||||
|
||||
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.'
|
||||
message:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Unknown error occurred while adding attachments.',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
self.onmessage = (e) => {
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user