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:
87
src/js/utils/pymupdf-loader.ts
Normal file
87
src/js/utils/pymupdf-loader.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { WasmProvider } from './wasm-provider.js';
|
||||
|
||||
let cachedPyMuPDF: any = null;
|
||||
let loadPromise: Promise<any> | null = null;
|
||||
|
||||
export interface PyMuPDFInterface {
|
||||
load(): Promise<void>;
|
||||
compressPdf(
|
||||
file: Blob,
|
||||
options: any
|
||||
): Promise<{ blob: Blob; compressedSize: number }>;
|
||||
convertToPdf(file: Blob, ext: string): Promise<Blob>;
|
||||
extractText(file: Blob, options?: any): Promise<string>;
|
||||
extractImages(file: Blob): Promise<Array<{ data: Uint8Array; ext: string }>>;
|
||||
extractTables(file: Blob): Promise<any[]>;
|
||||
toSvg(file: Blob, pageNum: number): Promise<string>;
|
||||
renderPageToImage(file: Blob, pageNum: number, scale: number): Promise<Blob>;
|
||||
getPageCount(file: Blob): Promise<number>;
|
||||
rasterizePdf(file: Blob | File, options: any): Promise<Blob>;
|
||||
}
|
||||
|
||||
export async function loadPyMuPDF(): Promise<any> {
|
||||
if (cachedPyMuPDF) {
|
||||
return cachedPyMuPDF;
|
||||
}
|
||||
|
||||
if (loadPromise) {
|
||||
return loadPromise;
|
||||
}
|
||||
|
||||
loadPromise = (async () => {
|
||||
if (!WasmProvider.isConfigured('pymupdf')) {
|
||||
throw new Error(
|
||||
'PyMuPDF is not configured. Please configure it in Advanced Settings.'
|
||||
);
|
||||
}
|
||||
if (!WasmProvider.isConfigured('ghostscript')) {
|
||||
throw new Error(
|
||||
'Ghostscript is not configured. PyMuPDF requires Ghostscript for some operations. Please configure both in Advanced Settings.'
|
||||
);
|
||||
}
|
||||
|
||||
const pymupdfUrl = WasmProvider.getUrl('pymupdf')!;
|
||||
const gsUrl = WasmProvider.getUrl('ghostscript')!;
|
||||
const normalizedPymupdf = pymupdfUrl.endsWith('/')
|
||||
? pymupdfUrl
|
||||
: `${pymupdfUrl}/`;
|
||||
|
||||
try {
|
||||
const wrapperUrl = `${normalizedPymupdf}dist/index.js`;
|
||||
const module = await import(/* @vite-ignore */ wrapperUrl);
|
||||
|
||||
if (typeof module.PyMuPDF !== 'function') {
|
||||
throw new Error(
|
||||
'PyMuPDF module did not export expected PyMuPDF class.'
|
||||
);
|
||||
}
|
||||
|
||||
cachedPyMuPDF = new module.PyMuPDF({
|
||||
assetPath: `${normalizedPymupdf}assets/`,
|
||||
ghostscriptUrl: gsUrl,
|
||||
});
|
||||
|
||||
await cachedPyMuPDF.load();
|
||||
|
||||
console.log('[PyMuPDF Loader] Successfully loaded from CDN');
|
||||
return cachedPyMuPDF;
|
||||
} catch (error: any) {
|
||||
loadPromise = null;
|
||||
throw new Error(`Failed to load PyMuPDF from CDN: ${error.message}`);
|
||||
}
|
||||
})();
|
||||
|
||||
return loadPromise;
|
||||
}
|
||||
|
||||
export function isPyMuPDFAvailable(): boolean {
|
||||
return (
|
||||
WasmProvider.isConfigured('pymupdf') &&
|
||||
WasmProvider.isConfigured('ghostscript')
|
||||
);
|
||||
}
|
||||
|
||||
export function clearPyMuPDFCache(): void {
|
||||
cachedPyMuPDF = null;
|
||||
loadPromise = null;
|
||||
}
|
||||
Reference in New Issue
Block a user