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:
alam00000
2026-01-27 15:26:11 +05:30
parent f6d432eaa7
commit 2c85ca74e9
75 changed files with 9696 additions and 6587 deletions

101
public/images/badge.svg Normal file
View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 604 129" style="enable-background:new 0 0 604 129;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
</style>
<g>
<g>
<g>
<path class="st0" d="M174.3,3c4.9,0,8.7,2.9,8.7,8.6c0,5.6-3.8,8.5-8.7,8.5h-7.6v11.1h-3.5V3H174.3z M166.7,17.1h7.2
c3,0,5.6-1.8,5.6-5.5c0-3.8-2.5-5.5-5.6-5.5h-7.2V17.1z"/>
<path class="st0" d="M208.8,21.7c0,6.1-4.3,10-9.9,10c-5.6,0-9.9-3.9-9.9-10c0-6.1,4.3-10,9.9-10
C204.5,11.7,208.8,15.6,208.8,21.7z M192.3,21.7c0,4.5,2.9,7.2,6.6,7.2c3.7,0,6.6-2.7,6.6-7.2c0-4.5-2.9-7.1-6.6-7.1
C195.2,14.5,192.3,17.2,192.3,21.7z"/>
<path class="st0" d="M234.4,31.3l-5.2-13.8L224,31.3h-2.6L214.1,12h3.6l5.2,14l5.2-14h2.3l5.3,14l5.2-14h3.5L237,31.3H234.4z"/>
<path class="st0" d="M253,22.9c0.2,3.7,2.6,5.9,6,5.9c2.8,0,4.8-1.3,5.4-3.4l3.2,0.2c-0.8,3.5-4.1,6.1-8.6,6.1
c-5.5,0-9.6-3.7-9.6-10c0-6.3,4-10,9.5-10c5.5,0,8.8,3.7,8.8,9.4v1.8H253z M253,20.3h11.6c-0.1-3.4-2-5.7-5.6-5.7
C255.6,14.5,253.2,16.5,253,20.3z"/>
<path class="st0" d="M285.4,14.9c-3.4,0-5.6,2.3-5.6,5.3v11.1h-3.2V12h3.2v2.9c0.7-1.6,2.5-3.1,5.7-3.1V14.9z"/>
<path class="st0" d="M294.7,22.9c0.2,3.7,2.6,5.9,6,5.9c2.8,0,4.8-1.3,5.4-3.4l3.2,0.2c-0.8,3.5-4.1,6.1-8.6,6.1
c-5.5,0-9.6-3.7-9.6-10c0-6.3,4-10,9.5-10c5.5,0,8.8,3.7,8.8,9.4v1.8H294.7z M294.7,20.3h11.6c-0.1-3.4-2-5.7-5.6-5.7
C297.4,14.5,294.9,16.5,294.7,20.3z"/>
<path class="st0" d="M333.1,31.3v-3.1c-1.1,2-3.6,3.5-6.8,3.5c-5.3,0-9.3-3.8-9.3-10c0-6.2,4-10,9.3-10c3.2,0,5.6,1.4,6.6,3.2V2
h3.2v29.4H333.1z M320.3,21.7c0,4.6,2.8,7.2,6.5,7.2c3.6,0,6.2-2.2,6.2-6.6v-1.1c0-4.3-2.6-6.6-6.2-6.6
C323.1,14.5,320.3,17.1,320.3,21.7z"/>
<path class="st0" d="M361.8,14.9c1.1-1.9,3.4-3.2,6.7-3.2c5.3,0,9.3,3.8,9.3,10c0,6.2-4,10-9.3,10c-3.3,0-5.7-1.5-6.8-3.5v3.1
h-3.1V2h3.2V14.9z M361.9,21.1v1.1c0,4.4,2.6,6.6,6.2,6.6c3.7,0,6.5-2.5,6.5-7.2c0-4.6-2.8-7.1-6.5-7.1
C364.5,14.5,361.9,16.8,361.9,21.1z"/>
<path class="st0" d="M386.3,40.9l4.6-10.7L383.2,12h3.6l5.8,14.5l5.8-14.5h3.6l-12.2,28.9H386.3z"/>
</g>
</g>
<g id="XMLID_2369_">
<g>
<g id="XMLID_281_">
<g id="XMLID_282_">
<g>
<g id="XMLID_283_">
<g id="XMLID_287_">
<path id="XMLID_288_" class="st0" d="M64.4,127l0-24.2c25.6,0,45.5-25.4,35.7-52.3c-3.6-10-11.6-17.9-21.6-21.6
c-27-9.8-52.3,10-52.3,35.7c0,0,0,0,0,0L2,64.7C2,23.8,41.5-8,84.3,5.4c18.7,5.8,33.6,20.7,39.4,39.4
C137,87.6,105.2,127,64.4,127z"/>
</g>
<polygon id="XMLID_286_" class="st1" points="64.4,102.9 40.4,102.9 40.4,78.9 40.4,78.9 64.4,78.9 64.4,78.9 "/>
<polygon id="XMLID_285_" class="st1" points="40.3,121.5 21.8,121.5 21.8,121.5 21.8,102.9 40.4,102.9 40.4,121.5 "/>
<path id="XMLID_284_" class="st1" d="M21.9,102.9H6.3c0,0,0,0,0,0V87.4c0,0,0,0,0,0h15.5c0,0,0,0,0,0V102.9z"/>
</g>
</g>
</g>
</g>
<g id="XMLID_254_">
<path id="XMLID_278_" class="st0" d="M200.9,52.4c-5.5-3.8-12.4-5.8-20.5-5.8h-17.5v55.5h17.5c8,0,14.9-2.1,20.5-6.1
c3-2.1,5.4-5.1,7.1-8.9c1.7-3.7,2.5-8.2,2.5-13.1c0-4.9-0.8-9.3-2.5-13C206.3,57.4,203.9,54.4,200.9,52.4z M173.1,56h5.5
c6.1,0,11.1,1.2,15,3.6c4.2,2.6,6.4,7.4,6.4,14.4c0,7.2-2.2,12.3-6.4,15.1h0c-3.7,2.4-8.7,3.6-14.9,3.6h-5.6V56z"/>
<path id="XMLID_277_" class="st0" d="M222.6,45.9c-1.7,0-3.1,0.6-4.3,1.8c-1.2,1.1-1.8,2.6-1.8,4.2c0,1.7,0.6,3.1,1.8,4.3
c1.2,1.2,2.6,1.8,4.3,1.8c1.7,0,3.1-0.6,4.3-1.8c1.2-1.2,1.8-2.6,1.8-4.3c0-1.7-0.6-3.1-1.8-4.2
C225.7,46.5,224.3,45.9,222.6,45.9z"/>
<rect id="XMLID_276_" x="217.6" y="63" class="st0" width="9.8" height="39.1"/>
<path id="XMLID_273_" class="st0" d="M263.2,66.3c-3-2.6-6.3-4.2-9.9-4.2c-5.4,0-9.9,1.9-13.4,5.6c-3.5,3.7-5.3,8.4-5.3,14.1
c0,5.5,1.8,10.2,5.2,14c3.5,3.7,8,5.5,13.5,5.5c3.8,0,7.1-1.1,9.7-3.1V99c0,3.2-0.9,5.8-2.6,7.5c-1.7,1.7-4.1,2.6-7.1,2.6
c-4.5,0-7.4-1.8-10.9-6.5l-6.7,6.4l0.2,0.3c1.4,2,3.7,4,6.6,5.9c2.9,1.9,6.6,2.8,10.9,2.8c5.8,0,10.6-1.8,14.1-5.4
c3.5-3.6,5.3-8.4,5.3-14.2V63h-9.7V66.3z M260.6,89.4c-1.7,2-3.9,2.9-6.8,2.9c-2.8,0-5-0.9-6.7-2.9c-1.7-1.9-2.5-4.5-2.5-7.7
c0-3.2,0.9-5.8,2.5-7.7c1.7-1.9,3.9-2.9,6.7-2.9c2.8,0,5,1,6.8,2.9c1.7,2,2.6,4.6,2.6,7.7C263.2,84.9,262.3,87.5,260.6,89.4z"/>
<rect id="XMLID_272_" x="281.3" y="63" class="st0" width="9.8" height="39.1"/>
<path id="XMLID_271_" class="st0" d="M286.3,45.9c-1.7,0-3.1,0.6-4.3,1.8c-1.2,1.1-1.8,2.6-1.8,4.2c0,1.7,0.6,3.1,1.8,4.3
c1.2,1.2,2.6,1.8,4.3,1.8c1.7,0,3.1-0.6,4.3-1.8c1.2-1.2,1.8-2.6,1.8-4.3c0-1.7-0.6-3.1-1.8-4.2C289.4,46.5,288,45.9,286.3,45.9
z"/>
<path id="XMLID_270_" class="st0" d="M312.7,52.5H303V63h-5.6v9h5.6v16.2c0,5.1,1,8.7,3,10.8c2,2.1,5.6,3.2,10.6,3.2
c1.6,0,3.2-0.1,4.8-0.2l0.4,0v-9l-3.4,0.2c-2.3,0-3.9-0.4-4.7-1.2c-0.8-0.8-1.1-2.6-1.1-5.2V72h9.2v-9h-9.2V52.5z"/>
<rect id="XMLID_269_" x="368" y="46.6" class="st0" width="9.8" height="55.5"/>
<path id="XMLID_268_" class="st0" d="M477.3,88.2c-1.8,2-3.6,3.7-4.9,4.6v0c-1.4,0.9-3.1,1.3-5.1,1.3c-2.9,0-5.2-1.1-7.1-3.2
c-1.9-2.2-2.8-4.9-2.8-8.3s0.9-6.1,2.8-8.2c1.9-2.2,4.2-3.2,7.1-3.2c3.2,0,6.5,2,9.4,5.4l6.5-6.2l0,0c-4.2-5.5-9.7-8.1-16.1-8.1
c-5.4,0-10.1,2-13.9,5.8c-3.8,3.9-5.7,8.8-5.7,14.6s1.9,10.7,5.7,14.6c3.8,3.9,8.5,5.9,13.9,5.9c7.1,0,12.9-3.1,16.8-8.7
L477.3,88.2z"/>
<path id="XMLID_265_" class="st0" d="M517.7,68.5c-1.4-1.9-3.3-3.5-5.7-4.7c-2.3-1.1-5.1-1.7-8.1-1.7c-5.5,0-10,2-13.4,6
c-3.3,4-4.9,8.9-4.9,14.7c0,5.9,1.8,10.8,5.4,14.6c3.6,3.7,8.4,5.6,14.2,5.6c6.6,0,12.1-2.7,16.2-8l0.2-0.3l-6.4-6.2l0,0
c-0.6,0.7-1.4,1.5-2.2,2.3c-1,0.9-1.9,1.6-2.9,2.1c-1.5,0.7-3.1,1.1-5,1.1c-2.7,0-5-0.8-6.7-2.4c-1.6-1.5-2.6-3.5-2.8-5.9h26.1
l0.1-3.6c0-2.5-0.3-5-1-7.3C520.1,72.6,519.1,70.4,517.7,68.5z M496.2,77.7c0.5-1.9,1.3-3.4,2.6-4.6c1.3-1.3,3.1-2,5.2-2
c2.4,0,4.2,0.7,5.5,2c1.2,1.2,1.8,2.8,2,4.6H496.2z"/>
<path id="XMLID_262_" class="st0" d="M555.5,66L555.5,66c-3-2.5-7.1-3.8-12.3-3.8c-3.3,0-6.3,0.7-9.1,2.1
c-2.6,1.3-5.1,3.5-6.7,6.3l0.1,0.1l6.3,6c2.6-4.1,5.5-5.6,9.3-5.6c2.1,0,3.8,0.6,5.1,1.6c1.3,1.1,1.9,2.5,1.9,4.2v1.9
c-2.4-0.7-4.9-1.1-7.2-1.1c-4.9,0-8.9,1.2-11.8,3.4c-3,2.3-4.5,5.6-4.5,9.8c0,3.7,1.3,6.7,3.8,8.9c2.6,2.1,5.8,3.2,9.5,3.2
c3.7,0,7.3-1.5,10.4-4.1v3.2h9.7V77C560,72.2,558.5,68.5,555.5,66z M538,87.2c1.1-0.8,2.7-1.2,4.7-1.2c2.4,0,4.9,0.5,7.5,1.4
v3.8c-2.1,2-5,3-8.5,3c-1.7,0-3-0.4-3.9-1.1c-0.9-0.7-1.3-1.7-1.3-2.8C536.4,89,536.9,88,538,87.2z"/>
<path id="XMLID_261_" class="st0" d="M597.9,66.7c-2.7-3.1-6.6-4.6-11.5-4.6c-3.9,0-7.1,1.1-9.4,3.3V63h-9.7v39.1h9.8V80.6
c0-3,0.7-5.3,2.1-7c1.4-1.7,3.3-2.5,5.8-2.5c2.2,0,3.9,0.7,5.2,2.2c1.3,1.5,1.9,3.6,1.9,6.2v22.7h9.8V79.5
C602,74.1,600.6,69.8,597.9,66.7z"/>
<path id="XMLID_258_" class="st0" d="M355.6,66L355.6,66c-3-2.5-7.1-3.8-12.3-3.8c-3.3,0-6.3,0.7-9.1,2.1
c-2.6,1.3-5.1,3.5-6.7,6.3l0.1,0.1l6.3,6c2.6-4.1,5.5-5.6,9.3-5.6c2.1,0,3.8,0.6,5.1,1.6c1.3,1.1,1.9,2.5,1.9,4.2v1.9
c-2.4-0.7-4.9-1.1-7.2-1.1c-4.9,0-8.9,1.2-11.8,3.4c-3,2.3-4.5,5.6-4.5,9.8c0,3.7,1.3,6.7,3.8,8.9c2.6,2.1,5.8,3.2,9.5,3.2
c3.7,0,7.3-1.5,10.4-4.1v3.2h9.7V77C360.2,72.2,358.7,68.5,355.6,66z M338.2,87.2c1.1-0.8,2.7-1.2,4.7-1.2
c2.4,0,4.9,0.5,7.5,1.4v3.8c-2.1,2-5,3-8.5,3c-1.7,0-3-0.4-3.9-1.1c-0.9-0.7-1.3-1.7-1.3-2.8C336.6,89,337.1,88,338.2,87.2z"/>
<path id="XMLID_255_" class="st0" d="M413.6,103c-15.8,0-28.6-12.8-28.6-28.6s12.8-28.6,28.6-28.6s28.6,12.8,28.6,28.6
S429.4,103,413.6,103z M413.6,55.8c-10.2,0-18.5,8.3-18.5,18.5s8.3,18.5,18.5,18.5s18.5-8.3,18.5-18.5S423.8,55.8,413.6,55.8z"
/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@@ -5,6 +5,7 @@ interface AddAttachmentsMessage {
pdfBuffer: ArrayBuffer;
attachmentBuffers: ArrayBuffer[];
attachmentNames: string[];
cpdfUrl?: string;
}
interface AddAttachmentsSuccessResponse {
@@ -17,4 +18,6 @@ interface AddAttachmentsErrorResponse {
message: string;
}
type AddAttachmentsResponse = AddAttachmentsSuccessResponse | AddAttachmentsErrorResponse;
type AddAttachmentsResponse =
| AddAttachmentsSuccessResponse
| AddAttachmentsErrorResponse;

View File

@@ -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,

View File

@@ -1,23 +1,24 @@
declare const coherentpdf: typeof import('../../src/types/coherentpdf.global').coherentpdf;
interface InterleaveFile {
name: string;
data: ArrayBuffer;
name: string;
data: ArrayBuffer;
}
interface InterleaveMessage {
command: 'interleave';
files: InterleaveFile[];
command: 'interleave';
files: InterleaveFile[];
cpdfUrl?: string;
}
interface InterleaveSuccessResponse {
status: 'success';
pdfBytes: ArrayBuffer;
status: 'success';
pdfBytes: ArrayBuffer;
}
interface InterleaveErrorResponse {
status: 'error';
message: string;
status: 'error';
message: string;
}
type InterleaveResponse = InterleaveSuccessResponse | InterleaveErrorResponse;

View File

@@ -1,64 +1,109 @@
const baseUrl = self.location.href.substring(0, self.location.href.lastIndexOf('/workers/') + 1);
self.importScripts(baseUrl + 'coherentpdf.browser.min.js');
let cpdfLoaded = false;
self.onmessage = function (e) {
const { command, files } = e.data;
function loadCpdf(cpdfUrl) {
if (cpdfLoaded) return Promise.resolve();
if (command === 'interleave') {
interleavePDFs(files);
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));
}
});
}
self.onmessage = async function (e) {
const { command, files, 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 (command === 'interleave') {
interleavePDFs(files);
}
};
function interleavePDFs(files) {
try {
const loadedPdfs = [];
const pageCounts = [];
try {
const loadedPdfs = [];
const pageCounts = [];
for (const file of files) {
const uint8Array = new Uint8Array(file.data);
const pdfDoc = coherentpdf.fromMemory(uint8Array, "");
loadedPdfs.push(pdfDoc);
pageCounts.push(coherentpdf.pages(pdfDoc));
}
if (loadedPdfs.length < 2) {
throw new Error('At least two PDF files are required for interleaving.');
}
const maxPages = Math.max(...pageCounts);
const pdfsToMerge = [];
const rangesToMerge = [];
for (let i = 1; i <= maxPages; i++) {
for (let j = 0; j < loadedPdfs.length; j++) {
if (i <= pageCounts[j]) {
pdfsToMerge.push(loadedPdfs[j]);
rangesToMerge.push(coherentpdf.range(i, i));
}
}
}
if (pdfsToMerge.length === 0) {
throw new Error('No valid pages to merge.');
}
const mergedPdf = coherentpdf.mergeSame(pdfsToMerge, true, true, rangesToMerge);
const mergedPdfBytes = coherentpdf.toMemory(mergedPdf, false, true);
const buffer = mergedPdfBytes.buffer;
coherentpdf.deletePdf(mergedPdf);
loadedPdfs.forEach(pdf => coherentpdf.deletePdf(pdf));
self.postMessage({
status: 'success',
pdfBytes: buffer
}, [buffer]);
} catch (error) {
self.postMessage({
status: 'error',
message: error.message || 'Unknown error during interleave merge'
});
for (const file of files) {
const uint8Array = new Uint8Array(file.data);
const pdfDoc = coherentpdf.fromMemory(uint8Array, '');
loadedPdfs.push(pdfDoc);
pageCounts.push(coherentpdf.pages(pdfDoc));
}
if (loadedPdfs.length < 2) {
throw new Error('At least two PDF files are required for interleaving.');
}
const maxPages = Math.max(...pageCounts);
const pdfsToMerge = [];
const rangesToMerge = [];
for (let i = 1; i <= maxPages; i++) {
for (let j = 0; j < loadedPdfs.length; j++) {
if (i <= pageCounts[j]) {
pdfsToMerge.push(loadedPdfs[j]);
rangesToMerge.push(coherentpdf.range(i, i));
}
}
}
if (pdfsToMerge.length === 0) {
throw new Error('No valid pages to merge.');
}
const mergedPdf = coherentpdf.mergeSame(
pdfsToMerge,
true,
true,
rangesToMerge
);
const mergedPdfBytes = coherentpdf.toMemory(mergedPdf, false, true);
const buffer = mergedPdfBytes.buffer;
coherentpdf.deletePdf(mergedPdf);
loadedPdfs.forEach((pdf) => coherentpdf.deletePdf(pdf));
self.postMessage(
{
status: 'success',
pdfBytes: buffer,
},
[buffer]
);
} catch (error) {
self.postMessage({
status: 'error',
message: error.message || 'Unknown error during interleave merge',
});
}
}

View File

@@ -4,6 +4,7 @@ interface GetAttachmentsMessage {
command: 'get-attachments';
fileBuffer: ArrayBuffer;
fileName: string;
cpdfUrl?: string;
}
interface EditAttachmentsMessage {
@@ -11,13 +12,21 @@ interface EditAttachmentsMessage {
fileBuffer: ArrayBuffer;
fileName: string;
attachmentsToRemove: number[];
cpdfUrl?: string;
}
type EditAttachmentsWorkerMessage = GetAttachmentsMessage | EditAttachmentsMessage;
type EditAttachmentsWorkerMessage =
| GetAttachmentsMessage
| EditAttachmentsMessage;
interface GetAttachmentsSuccessResponse {
status: 'success';
attachments: Array<{ index: number; name: string; page: number; data: ArrayBuffer }>;
attachments: Array<{
index: number;
name: string;
page: number;
data: ArrayBuffer;
}>;
fileName: string;
}
@@ -37,6 +46,12 @@ interface EditAttachmentsErrorResponse {
message: string;
}
type GetAttachmentsResponse = GetAttachmentsSuccessResponse | GetAttachmentsErrorResponse;
type EditAttachmentsResponse = EditAttachmentsSuccessResponse | EditAttachmentsErrorResponse;
type EditAttachmentsWorkerResponse = GetAttachmentsResponse | EditAttachmentsResponse;
type GetAttachmentsResponse =
| GetAttachmentsSuccessResponse
| GetAttachmentsErrorResponse;
type EditAttachmentsResponse =
| EditAttachmentsSuccessResponse
| EditAttachmentsErrorResponse;
type EditAttachmentsWorkerResponse =
| GetAttachmentsResponse
| EditAttachmentsResponse;

View File

@@ -1,5 +1,24 @@
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 getAttachmentsFromPDFInWorker(fileBuffer, fileName) {
try {
@@ -11,7 +30,7 @@ function getAttachmentsFromPDFInWorker(fileBuffer, fileName) {
} catch (error) {
self.postMessage({
status: 'error',
message: `Failed to load PDF: ${fileName}. Error: ${error.message || error}`
message: `Failed to load PDF: ${fileName}. Error: ${error.message || error}`,
});
return;
}
@@ -23,7 +42,7 @@ function getAttachmentsFromPDFInWorker(fileBuffer, fileName) {
self.postMessage({
status: 'success',
attachments: [],
fileName: fileName
fileName: fileName,
});
coherentpdf.deletePdf(pdf);
return;
@@ -37,13 +56,16 @@ function getAttachmentsFromPDFInWorker(fileBuffer, fileName) {
const attachmentData = coherentpdf.getAttachmentData(i);
const dataArray = new Uint8Array(attachmentData);
const buffer = dataArray.buffer.slice(dataArray.byteOffset, dataArray.byteOffset + dataArray.byteLength);
const buffer = dataArray.buffer.slice(
dataArray.byteOffset,
dataArray.byteOffset + dataArray.byteLength
);
attachments.push({
index: i,
name: String(name),
page: Number(page),
data: buffer
data: buffer,
});
} catch (error) {
console.warn(`Failed to get attachment ${i} from ${fileName}:`, error);
@@ -56,22 +78,27 @@ function getAttachmentsFromPDFInWorker(fileBuffer, fileName) {
const response = {
status: 'success',
attachments: attachments,
fileName: fileName
fileName: fileName,
};
const transferBuffers = attachments.map(att => att.data);
const transferBuffers = attachments.map((att) => att.data);
self.postMessage(response, transferBuffers);
} catch (error) {
self.postMessage({
status: 'error',
message: error instanceof Error
? error.message
: 'Unknown error occurred during attachment listing.'
message:
error instanceof Error
? error.message
: 'Unknown error occurred during attachment listing.',
});
}
}
function editAttachmentsInPDFInWorker(fileBuffer, fileName, attachmentsToRemove) {
function editAttachmentsInPDFInWorker(
fileBuffer,
fileName,
attachmentsToRemove
) {
try {
const uint8Array = new Uint8Array(fileBuffer);
@@ -81,7 +108,7 @@ function editAttachmentsInPDFInWorker(fileBuffer, fileName, attachmentsToRemove)
} catch (error) {
self.postMessage({
status: 'error',
message: `Failed to load PDF: ${fileName}. Error: ${error.message || error}`
message: `Failed to load PDF: ${fileName}. Error: ${error.message || error}`,
});
return;
}
@@ -103,7 +130,7 @@ function editAttachmentsInPDFInWorker(fileBuffer, fileName, attachmentsToRemove)
attachmentsToKeep.push({
name: String(name),
page: Number(page),
data: dataCopy
data: dataCopy,
});
}
}
@@ -114,9 +141,18 @@ function editAttachmentsInPDFInWorker(fileBuffer, fileName, attachmentsToRemove)
for (const attachment of attachmentsToKeep) {
if (attachment.page === 0) {
coherentpdf.attachFileFromMemory(attachment.data, attachment.name, pdf);
coherentpdf.attachFileFromMemory(
attachment.data,
attachment.name,
pdf
);
} else {
coherentpdf.attachFileToPageFromMemory(attachment.data, attachment.name, pdf, attachment.page);
coherentpdf.attachFileToPageFromMemory(
attachment.data,
attachment.name,
pdf,
attachment.page
);
}
}
}
@@ -124,29 +160,58 @@ function editAttachmentsInPDFInWorker(fileBuffer, fileName, attachmentsToRemove)
const modifiedBytes = coherentpdf.toMemory(pdf, false, true);
coherentpdf.deletePdf(pdf);
const buffer = modifiedBytes.buffer.slice(modifiedBytes.byteOffset, modifiedBytes.byteOffset + modifiedBytes.byteLength);
const buffer = modifiedBytes.buffer.slice(
modifiedBytes.byteOffset,
modifiedBytes.byteOffset + modifiedBytes.byteLength
);
const response = {
status: 'success',
modifiedPDF: buffer,
fileName: fileName
fileName: fileName,
};
self.postMessage(response, [response.modifiedPDF]);
} catch (error) {
self.postMessage({
status: 'error',
message: error instanceof Error
? error.message
: 'Unknown error occurred during attachment editing.'
message:
error instanceof Error
? error.message
: 'Unknown error occurred during attachment editing.',
});
}
}
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 === 'get-attachments') {
getAttachmentsFromPDFInWorker(e.data.fileBuffer, e.data.fileName);
} else if (e.data.command === 'edit-attachments') {
editAttachmentsInPDFInWorker(e.data.fileBuffer, e.data.fileName, e.data.attachmentsToRemove);
editAttachmentsInPDFInWorker(
e.data.fileBuffer,
e.data.fileName,
e.data.attachmentsToRemove
);
}
};
};

View File

@@ -4,6 +4,7 @@ interface ExtractAttachmentsMessage {
command: 'extract-attachments';
fileBuffers: ArrayBuffer[];
fileNames: string[];
cpdfUrl?: string;
}
interface ExtractAttachmentSuccessResponse {
@@ -16,4 +17,6 @@ interface ExtractAttachmentErrorResponse {
message: string;
}
type ExtractAttachmentResponse = ExtractAttachmentSuccessResponse | ExtractAttachmentErrorResponse;
type ExtractAttachmentResponse =
| ExtractAttachmentSuccessResponse
| ExtractAttachmentErrorResponse;

View File

@@ -1,5 +1,24 @@
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 extractAttachmentsFromPDFsInWorker(fileBuffers, fileNames) {
try {
@@ -37,7 +56,7 @@ function extractAttachmentsFromPDFsInWorker(fileBuffers, fileNames) {
let uniqueName = attachmentName;
let counter = 1;
while (allAttachments.some(att => att.name === uniqueName)) {
while (allAttachments.some((att) => att.name === uniqueName)) {
const nameParts = attachmentName.split('.');
if (nameParts.length > 1) {
const extension = nameParts.pop();
@@ -56,10 +75,13 @@ function extractAttachmentsFromPDFsInWorker(fileBuffers, fileNames) {
allAttachments.push({
name: uniqueName,
data: attachmentData.buffer.slice(0)
data: attachmentData.buffer.slice(0),
});
} catch (error) {
console.warn(`Failed to extract attachment ${j} from ${fileName}:`, error);
console.warn(
`Failed to extract attachment ${j} from ${fileName}:`,
error
);
}
}
@@ -70,21 +92,21 @@ function extractAttachmentsFromPDFsInWorker(fileBuffers, fileNames) {
if (allAttachments.length === 0) {
self.postMessage({
status: 'error',
message: 'No attachments were found in the selected PDF(s).'
message: 'No attachments were found in the selected PDF(s).',
});
return;
}
const response = {
status: 'success',
attachments: []
attachments: [],
};
const transferBuffers = [];
for (const attachment of allAttachments) {
response.attachments.push({
name: attachment.name,
data: attachment.data
data: attachment.data,
});
transferBuffers.push(attachment.data);
}
@@ -93,15 +115,37 @@ function extractAttachmentsFromPDFsInWorker(fileBuffers, fileNames) {
} catch (error) {
self.postMessage({
status: 'error',
message: error instanceof Error
? error.message
: 'Unknown error occurred during attachment extraction.'
message:
error instanceof Error
? error.message
: 'Unknown error occurred during attachment extraction.',
});
}
}
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 === 'extract-attachments') {
extractAttachmentsFromPDFsInWorker(e.data.fileBuffers, e.data.fileNames);
}
};
};

View File

@@ -4,6 +4,7 @@ interface ConvertJSONToPDFMessage {
command: 'convert';
fileBuffers: ArrayBuffer[];
fileNames: string[];
cpdfUrl?: string;
}
interface JSONToPDFSuccessResponse {
@@ -17,4 +18,3 @@ interface JSONToPDFErrorResponse {
}
type JSONToPDFResponse = JSONToPDFSuccessResponse | JSONToPDFErrorResponse;

View File

@@ -1,5 +1,24 @@
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 convertJSONsToPDFInWorker(fileBuffers, fileNames) {
try {
@@ -15,13 +34,12 @@ function convertJSONsToPDFInWorker(fileBuffers, fileNames) {
try {
pdf = coherentpdf.fromJSONMemory(uint8Array);
} catch (error) {
const errorMsg = error && error.message
? error.message
: 'Unknown error';
const errorMsg =
error && error.message ? error.message : 'Unknown error';
throw new Error(
`Failed to convert "${fileName}" to PDF. ` +
`The JSON file must be in the format produced by cpdf's outputJSONMemory. ` +
`Error: ${errorMsg}`
`The JSON file must be in the format produced by cpdf's outputJSONMemory. ` +
`Error: ${errorMsg}`
);
}
@@ -56,9 +74,29 @@ function convertJSONsToPDFInWorker(fileBuffers, fileNames) {
}
}
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 === 'convert') {
convertJSONsToPDFInWorker(e.data.fileBuffers, e.data.fileNames);
}
};

View File

@@ -1,33 +1,34 @@
declare const coherentpdf: typeof import('../../src/types/coherentpdf.global').coherentpdf;
interface MergeJob {
fileName: string;
rangeType: 'all' | 'specific' | 'single' | 'range';
rangeString?: string;
pageIndex?: number;
startPage?: number;
endPage?: number;
fileName: string;
rangeType: 'all' | 'specific' | 'single' | 'range';
rangeString?: string;
pageIndex?: number;
startPage?: number;
endPage?: number;
}
interface MergeFile {
name: string;
data: ArrayBuffer;
name: string;
data: ArrayBuffer;
}
interface MergeMessage {
command: 'merge';
files: MergeFile[];
jobs: MergeJob[];
command: 'merge';
files: MergeFile[];
jobs: MergeJob[];
cpdfUrl?: string;
}
interface MergeSuccessResponse {
status: 'success';
pdfBytes: ArrayBuffer;
status: 'success';
pdfBytes: ArrayBuffer;
}
interface MergeErrorResponse {
status: 'error';
message: string;
status: 'error';
message: string;
}
type MergeResponse = MergeSuccessResponse | MergeErrorResponse;

View File

@@ -1,71 +1,116 @@
const baseUrl = self.location.href.substring(0, self.location.href.lastIndexOf('/workers/') + 1);
self.importScripts(baseUrl + 'coherentpdf.browser.min.js');
let cpdfLoaded = false;
self.onmessage = function (e) {
const { command, files, jobs } = e.data;
function loadCpdf(cpdfUrl) {
if (cpdfLoaded) return Promise.resolve();
if (command === 'merge') {
mergePDFs(files, jobs);
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));
}
});
}
self.onmessage = async function (e) {
const { command, files, jobs, 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 (command === 'merge') {
mergePDFs(files, jobs);
}
};
function mergePDFs(files, jobs) {
try {
const loadedPdfs = {};
const pdfsToMerge = [];
const rangesToMerge = [];
try {
const loadedPdfs = {};
const pdfsToMerge = [];
const rangesToMerge = [];
for (const file of files) {
const uint8Array = new Uint8Array(file.data);
const pdfDoc = coherentpdf.fromMemory(uint8Array, "");
loadedPdfs[file.name] = pdfDoc;
}
for (const job of jobs) {
const sourcePdf = loadedPdfs[job.fileName];
if (!sourcePdf) continue;
let range;
if (job.rangeType === 'all') {
range = coherentpdf.all(sourcePdf);
} else if (job.rangeType === 'specific') {
if (coherentpdf.validatePagespec(job.rangeString)) {
range = coherentpdf.parsePagespec(sourcePdf, job.rangeString);
} else {
range = coherentpdf.all(sourcePdf);
}
} else if (job.rangeType === 'single') {
const pageNum = job.pageIndex + 1;
range = coherentpdf.range(pageNum, pageNum);
} else if (job.rangeType === 'range') {
range = coherentpdf.range(job.startPage, job.endPage);
}
pdfsToMerge.push(sourcePdf);
rangesToMerge.push(range);
}
if (pdfsToMerge.length === 0) {
throw new Error('No valid files or pages to merge.');
}
const mergedPdf = coherentpdf.mergeSame(pdfsToMerge, true, true, rangesToMerge);
const mergedPdfBytes = coherentpdf.toMemory(mergedPdf, false, true);
const buffer = mergedPdfBytes.buffer;
coherentpdf.deletePdf(mergedPdf);
Object.values(loadedPdfs).forEach(pdf => coherentpdf.deletePdf(pdf));
self.postMessage({
status: 'success',
pdfBytes: buffer
}, [buffer]);
} catch (error) {
self.postMessage({
status: 'error',
message: error.message || 'Unknown error during merge'
});
for (const file of files) {
const uint8Array = new Uint8Array(file.data);
const pdfDoc = coherentpdf.fromMemory(uint8Array, '');
loadedPdfs[file.name] = pdfDoc;
}
for (const job of jobs) {
const sourcePdf = loadedPdfs[job.fileName];
if (!sourcePdf) continue;
let range;
if (job.rangeType === 'all') {
range = coherentpdf.all(sourcePdf);
} else if (job.rangeType === 'specific') {
if (coherentpdf.validatePagespec(job.rangeString)) {
range = coherentpdf.parsePagespec(sourcePdf, job.rangeString);
} else {
range = coherentpdf.all(sourcePdf);
}
} else if (job.rangeType === 'single') {
const pageNum = job.pageIndex + 1;
range = coherentpdf.range(pageNum, pageNum);
} else if (job.rangeType === 'range') {
range = coherentpdf.range(job.startPage, job.endPage);
}
pdfsToMerge.push(sourcePdf);
rangesToMerge.push(range);
}
if (pdfsToMerge.length === 0) {
throw new Error('No valid files or pages to merge.');
}
const mergedPdf = coherentpdf.mergeSame(
pdfsToMerge,
true,
true,
rangesToMerge
);
const mergedPdfBytes = coherentpdf.toMemory(mergedPdf, false, true);
const buffer = mergedPdfBytes.buffer;
coherentpdf.deletePdf(mergedPdf);
Object.values(loadedPdfs).forEach((pdf) => coherentpdf.deletePdf(pdf));
self.postMessage(
{
status: 'success',
pdfBytes: buffer,
},
[buffer]
);
} catch (error) {
self.postMessage({
status: 'error',
message: error.message || 'Unknown error during merge',
});
}
}

View File

@@ -4,6 +4,7 @@ interface ConvertPDFToJSONMessage {
command: 'convert';
fileBuffers: ArrayBuffer[];
fileNames: string[];
cpdfUrl?: string;
}
interface PDFToJSONSuccessResponse {
@@ -17,4 +18,3 @@ interface PDFToJSONErrorResponse {
}
type PDFToJSONResponse = PDFToJSONSuccessResponse | PDFToJSONErrorResponse;

View File

@@ -1,5 +1,24 @@
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 convertPDFsToJSONInWorker(fileBuffers, fileNames) {
try {
@@ -12,8 +31,6 @@ function convertPDFsToJSONInWorker(fileBuffers, fileNames) {
const uint8Array = new Uint8Array(buffer);
const pdf = coherentpdf.fromMemory(uint8Array, '');
//TODO:@ALAM -> add options for users to select these settings
// parse_content: true, no_stream_data: false, decompress_streams: false
const jsonData = coherentpdf.outputJSONMemory(true, false, false, pdf);
const jsonBuffer = jsonData.buffer.slice(0);
@@ -44,9 +61,29 @@ function convertPDFsToJSONInWorker(fileBuffers, fileNames) {
}
}
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 === 'convert') {
convertPDFsToJSONInWorker(e.data.fileBuffers, e.data.fileNames);
}
};

View File

@@ -7,6 +7,7 @@ interface GenerateTOCMessage {
fontSize: number;
fontFamily: number;
addBookmark: boolean;
cpdfUrl?: string;
}
interface TOCSuccessResponse {

View File

@@ -1,5 +1,24 @@
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 generateTableOfContentsInWorker(
pdfData,
@@ -49,7 +68,28 @@ function generateTableOfContentsInWorker(
}
}
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 === 'generate-toc') {
generateTableOfContentsInWorker(
e.data.pdfData,