diff --git a/packages/engines/src/lib/pdfium/web/direct-engine.ts b/packages/engines/src/lib/pdfium/web/direct-engine.ts index 3aee2a553..7bf1e40a8 100644 --- a/packages/engines/src/lib/pdfium/web/direct-engine.ts +++ b/packages/engines/src/lib/pdfium/web/direct-engine.ts @@ -23,6 +23,16 @@ export interface CreatePdfiumEngineOptions { * when it encounters text that requires fonts not embedded in the PDF. */ fontFallback?: FontFallbackConfig; + /** + * URL to the PDFium worker script (worker mode only). + * Avoids `worker-src blob:` in strict CSP environments. + */ + workerUrl?: string; + /** + * URL to the image encoder worker script (worker mode only). + * Avoids `worker-src blob:` in strict CSP environments. + */ + encoderWorkerUrl?: string; } /** diff --git a/packages/engines/src/lib/pdfium/web/worker-engine.ts b/packages/engines/src/lib/pdfium/web/worker-engine.ts index 06436251a..ab2fece91 100644 --- a/packages/engines/src/lib/pdfium/web/worker-engine.ts +++ b/packages/engines/src/lib/pdfium/web/worker-engine.ts @@ -29,6 +29,27 @@ export interface CreatePdfiumEngineOptions { * when it encounters text that requires fonts not embedded in the PDF. */ fontFallback?: FontFallbackConfig; + /** + * URL to the PDFium worker script. + * When provided, the worker is loaded from this URL instead of a blob: URL, + * allowing strict CSP without `worker-src blob:`. + * + * @example + * // Vite + * workerUrl: new URL('@embedpdf/engines/pdfium-worker', import.meta.url).href + * // Or a static asset + * workerUrl: '/assets/embedpdf-worker.js' + */ + workerUrl?: string; + /** + * URL to the image encoder worker script. + * When provided, the encoder pool workers are loaded from this URL instead + * of a blob: URL, allowing strict CSP without `worker-src blob:`. + * + * @example + * encoderWorkerUrl: '/assets/embedpdf-encoder-worker.js' + */ + encoderWorkerUrl?: string; } /** @@ -70,22 +91,20 @@ export function createPdfiumEngine( ? { logger: options as Logger } : (options as CreatePdfiumEngineOptions) || {}; - const { logger, encoderPoolSize, fontFallback } = config; + const { logger, encoderPoolSize, fontFallback, workerUrl, encoderWorkerUrl } = config; - // Create PDFium worker - const worker = new Worker( - URL.createObjectURL(new Blob([__WEBWORKER_BODY__], { type: 'application/javascript' })), - { - type: 'module', - }, - ); + // Create PDFium worker — use a static URL when provided to avoid blob: in CSP + const resolvedWorkerUrl = + workerUrl ?? + URL.createObjectURL(new Blob([__WEBWORKER_BODY__], { type: 'application/javascript' })); + const worker = new Worker(resolvedWorkerUrl, { type: 'module' }); // Create RemoteExecutor (proxy to worker) - handles wasmInit internally const remoteExecutor = new RemoteExecutor(worker, { wasmUrl, logger, fontFallback }); - const finalEncoderWorkerUrl = URL.createObjectURL( - new Blob([__ENCODER_WORKER_BODY__], { type: 'application/javascript' }), - ); + const finalEncoderWorkerUrl = + encoderWorkerUrl ?? + URL.createObjectURL(new Blob([__ENCODER_WORKER_BODY__], { type: 'application/javascript' })); const encoderPool = new ImageEncoderWorkerPool( encoderPoolSize ?? 2, finalEncoderWorkerUrl, diff --git a/packages/engines/src/shared/hooks/use-pdfium-engine.ts b/packages/engines/src/shared/hooks/use-pdfium-engine.ts index 9bd1ed617..843e23549 100644 --- a/packages/engines/src/shared/hooks/use-pdfium-engine.ts +++ b/packages/engines/src/shared/hooks/use-pdfium-engine.ts @@ -13,6 +13,10 @@ interface UsePdfiumEngineProps { * Font fallback configuration for handling missing fonts in PDFs. */ fontFallback?: FontFallbackConfig; + /** URL to the PDFium worker script. Avoids `worker-src blob:` in strict CSP. */ + workerUrl?: string; + /** URL to the image encoder worker script. Avoids `worker-src blob:` in strict CSP. */ + encoderWorkerUrl?: string; } export function usePdfiumEngine(config?: UsePdfiumEngineProps) { @@ -22,6 +26,8 @@ export function usePdfiumEngine(config?: UsePdfiumEngineProps) { logger, encoderPoolSize, fontFallback, + workerUrl, + encoderWorkerUrl, } = config ?? {}; const [engine, setEngine] = useState(null); @@ -42,6 +48,8 @@ export function usePdfiumEngine(config?: UsePdfiumEngineProps) { logger, encoderPoolSize, fontFallback, + workerUrl, + encoderWorkerUrl, }); engineRef.current = pdfEngine; setEngine(pdfEngine); @@ -61,7 +69,7 @@ export function usePdfiumEngine(config?: UsePdfiumEngineProps) { engineRef.current = null; }, ignore); }; - }, [wasmUrl, worker, logger, fontFallback]); + }, [wasmUrl, worker, logger, fontFallback, workerUrl, encoderWorkerUrl]); return { engine, isLoading: loading, error }; } diff --git a/packages/engines/src/svelte/hooks/use-pdfium-engine.svelte.ts b/packages/engines/src/svelte/hooks/use-pdfium-engine.svelte.ts index 7b9158645..28230930e 100644 --- a/packages/engines/src/svelte/hooks/use-pdfium-engine.svelte.ts +++ b/packages/engines/src/svelte/hooks/use-pdfium-engine.svelte.ts @@ -12,10 +12,14 @@ export interface UsePdfiumEngineProps { * Font fallback configuration for handling missing fonts in PDFs. */ fontFallback?: FontFallbackConfig; + /** URL to the PDFium worker script. Avoids `worker-src blob:` in strict CSP. */ + workerUrl?: string; + /** URL to the image encoder worker script. Avoids `worker-src blob:` in strict CSP. */ + encoderWorkerUrl?: string; } export function usePdfiumEngine(config?: UsePdfiumEngineProps) { - const { wasmUrl = defaultWasmUrl, worker = true, logger, fontFallback } = config ?? {}; + const { wasmUrl = defaultWasmUrl, worker = true, logger, fontFallback, workerUrl, encoderWorkerUrl } = config ?? {}; // Create a reactive state object const state = $state({ @@ -38,7 +42,7 @@ export function usePdfiumEngine(config?: UsePdfiumEngineProps) { ? await import('@embedpdf/engines/pdfium-worker-engine') : await import('@embedpdf/engines/pdfium-direct-engine'); - const pdfEngine = await createPdfiumEngine(wasmUrl, { logger, fontFallback }); + const pdfEngine = await createPdfiumEngine(wasmUrl, { logger, fontFallback, workerUrl, encoderWorkerUrl }); engineRef = pdfEngine; state.engine = pdfEngine; state.isLoading = false; diff --git a/packages/engines/src/vue/composables/use-pdfium-engine.ts b/packages/engines/src/vue/composables/use-pdfium-engine.ts index b72b08181..5ba03e815 100644 --- a/packages/engines/src/vue/composables/use-pdfium-engine.ts +++ b/packages/engines/src/vue/composables/use-pdfium-engine.ts @@ -13,6 +13,10 @@ interface UsePdfiumEngineProps { * Font fallback configuration for handling missing fonts in PDFs. */ fontFallback?: FontFallbackConfig; + /** URL to the PDFium worker script. Avoids `worker-src blob:` in strict CSP. */ + workerUrl?: string; + /** URL to the image encoder worker script. Avoids `worker-src blob:` in strict CSP. */ + encoderWorkerUrl?: string; } interface UsePdfiumEngineResult { @@ -26,7 +30,7 @@ interface UsePdfiumEngineResult { * and keeps its lifetime tied to the component. */ export function usePdfiumEngine(props: UsePdfiumEngineProps = {}): UsePdfiumEngineResult { - const { wasmUrl = defaultWasmUrl, worker = true, logger, fontFallback } = props; + const { wasmUrl = defaultWasmUrl, worker = true, logger, fontFallback, workerUrl, encoderWorkerUrl } = props; const engine = ref(null); const isLoading = ref(true); @@ -51,7 +55,7 @@ export function usePdfiumEngine(props: UsePdfiumEngineProps = {}): UsePdfiumEngi ? await import('@embedpdf/engines/pdfium-worker-engine') : await import('@embedpdf/engines/pdfium-direct-engine'); - const pdfEngine = await createPdfiumEngine(wasmUrl, { logger, fontFallback }); + const pdfEngine = await createPdfiumEngine(wasmUrl, { logger, fontFallback, workerUrl, encoderWorkerUrl }); engine.value = pdfEngine; isLoading.value = false; } catch (e) {