perf(compression): add vite-plugin-compression for gzip and Brotli support (#6279)

Signed-off-by: Balázs Szücs <[email protected]>
This commit is contained in:
brios
2026-06-05 18:49:09 +01:00
committed by GitHub
parent 9866d6e12d
commit 0dff192281
12 changed files with 316 additions and 33 deletions
@@ -89,7 +89,7 @@ import { RedactionAPIBridge } from "@app/components/viewer/RedactionAPIBridge";
import { DocumentPermissionsAPIBridge } from "@app/components/viewer/DocumentPermissionsAPIBridge";
import { DocumentReadyWrapper } from "@app/components/viewer/DocumentReadyWrapper";
import { ActiveDocumentProvider } from "@app/components/viewer/ActiveDocumentContext";
import { absoluteWithBasePath } from "@app/constants/app";
import { pdfiumWasmUrl } from "@app/services/wasmPrecompiler";
import { FormFieldOverlay } from "@app/tools/formFill/FormFieldOverlay";
import { ButtonAppearanceOverlay } from "@app/tools/formFill/ButtonAppearanceOverlay";
import SignatureFieldOverlay from "@app/components/viewer/SignatureFieldOverlay";
@@ -296,7 +296,7 @@ export function LocalEmbedPDF({
// Initialize the engine with the React hook - use local WASM for offline support
const { engine, isLoading, error } = usePdfiumEngine({
wasmUrl: absoluteWithBasePath("/pdfium/pdfium.wasm"),
wasmUrl: pdfiumWasmUrl,
});
// Early return if no file or URL provided
@@ -12,9 +12,34 @@
* `getSignatures`, `saveAsCopy`, …) wrap the `PdfEngine` interface so callers
* never have to deal with raw pointers or Tasks.
*/
import { init, type WrappedPdfiumModule } from "@embedpdf/pdfium";
import {
init,
type WrappedPdfiumModule,
type PdfiumModule,
} from "@embedpdf/pdfium";
import {
pdfiumWasmModulePromise,
startEagerWasmCompilation,
pdfiumWasmUrl,
} from "@app/services/wasmPrecompiler";
import type { FormField, WidgetCoordinates } from "@app/tools/formFill/types";
interface ExtendedPdfiumRuntime {
HEAPU8: Uint8Array;
HEAPF32: Float32Array;
}
interface PdfiumModuleOverrides extends Partial<PdfiumModule> {
locateFile?: (url: string, scriptDirectory?: string) => string;
instantiateWasm?: (
imports: WebAssembly.Imports,
successCallback: (
instance: WebAssembly.Instance,
module: WebAssembly.Module,
) => void,
) => void;
}
// PDF form field type constants (matching PDFium C API FPDF_FORMFIELD_* values)
const FPDF_FORMFIELD_UNKNOWN = 0;
const FPDF_FORMFIELD_PUSHBUTTON = 1;
@@ -46,8 +71,7 @@ let _module: WrappedPdfiumModule | null = null;
* Resolve the absolute WASM URL using the same pattern as LocalEmbedPDF.
*/
function wasmUrl(): string {
const base = (import.meta as any).env?.BASE_URL ?? "/";
return `${base}pdfium/pdfium.wasm`.replace(/\/\//g, "/");
return pdfiumWasmUrl;
}
/**
@@ -59,9 +83,47 @@ function wasmUrl(): string {
export async function getPdfiumModule(): Promise<WrappedPdfiumModule> {
if (_module) return _module;
if (!_initPromise) {
_initPromise = init({
// Ensure eager compilation has started if PDF service is requested before idle timeout
startEagerWasmCompilation();
const overrides: PdfiumModuleOverrides = {
locateFile: () => wasmUrl(),
} as any).then((m) => {
};
// Eagerly reuse pre-compiled WASM module from app boot if available
overrides.instantiateWasm = (
imports: WebAssembly.Imports,
successCallback: (
instance: WebAssembly.Instance,
module: WebAssembly.Module,
) => void,
) => {
pdfiumWasmModulePromise
.then((wasmModule) => {
if (wasmModule) {
return WebAssembly.instantiate(wasmModule, imports).then(
(instance) => {
successCallback(instance, wasmModule);
},
);
} else {
throw new Error("No pre-compiled WASM module found");
}
})
.catch((err: unknown) => {
console.warn(
"Eager WebAssembly instantiation failed, falling back to streaming compilation:",
err,
);
WebAssembly.instantiateStreaming(fetch(wasmUrl()), imports).then(
(result) => {
successCallback(result.instance, result.module);
},
);
});
};
_initPromise = init(overrides as Partial<PdfiumModule>).then((m) => {
// Call PDFiumExt_Init to ensure extensions (form fill etc.) are set up
try {
m.PDFiumExt_Init();
@@ -110,9 +172,9 @@ export function readAnnotRectAdjusted(
annotPtr: number,
rectBuf: number,
): boolean {
const ext = (m as any).EPDFAnnot_GetRect;
const ext = m.EPDFAnnot_GetRect;
if (typeof ext === "function") {
return ext.call(m, annotPtr, rectBuf);
return ext(annotPtr, rectBuf);
}
return m.FPDFAnnot_GetRect(annotPtr, rectBuf);
}
@@ -186,15 +248,13 @@ export function readEffectivePageBox(
let result: PageBox | null = null;
try {
// CropBox is the effective visible area
if (
(m as any).FPDFPage_GetCropBox(pagePtr, buf, buf + 4, buf + 8, buf + 12)
) {
if (m.FPDFPage_GetCropBox(pagePtr, buf, buf + 4, buf + 8, buf + 12)) {
result = read();
}
// Fall back to MediaBox
if (
!result &&
(m as any).FPDFPage_GetMediaBox(pagePtr, buf, buf + 4, buf + 8, buf + 12)
m.FPDFPage_GetMediaBox(pagePtr, buf, buf + 4, buf + 8, buf + 12)
) {
result = read();
}
@@ -226,7 +286,7 @@ function copyToWasmHeap(
bytes: Uint8Array,
ptr: number,
): void {
new Uint8Array((m.pdfium.wasmExports as any).memory.buffer).set(bytes, ptr);
(m.pdfium as typeof m.pdfium & ExtendedPdfiumRuntime).HEAPU8.set(bytes, ptr);
}
/**
@@ -1338,9 +1398,13 @@ async function renderWidgetAppearance(
formEnvPtr: number,
dpr: number,
): Promise<ImageData | null> {
const pdfiumWasm = m.pdfium as any;
const matrixPtr = m.pdfium.wasmExports.malloc(6 * 4);
const matrixView = new Float32Array(pdfiumWasm.HEAPF32.buffer, matrixPtr, 6);
const pdfiumRuntime = m.pdfium as typeof m.pdfium & ExtendedPdfiumRuntime;
const matrixView = new Float32Array(
pdfiumRuntime.HEAPF32.buffer,
matrixPtr,
6,
);
const sx = wDev / pdfW;
const sy = hDev / pdfH;
matrixView.set([sx, 0, 0, -sy, -sx * annotLeft, sy * annotTop]);
@@ -1364,7 +1428,7 @@ async function renderWidgetAppearance(
let imageData: ImageData | null = null;
if (ok) {
const rgba = new Uint8ClampedArray(
pdfiumWasm.HEAPU8.buffer.slice(heapPtr, heapPtr + bytes),
pdfiumRuntime.HEAPU8.subarray(heapPtr, heapPtr + bytes),
);
let hasVisible = false;
for (let i = 3; i < rgba.length; i += 4) {
@@ -1417,7 +1481,7 @@ async function renderWidgetAppearance(
m.FPDFBitmap_Destroy(bmp2);
const rgba2 = new Uint8ClampedArray(
pdfiumWasm.HEAPU8.buffer.slice(heap2, heap2 + bytes),
pdfiumRuntime.HEAPU8.subarray(heap2, heap2 + bytes),
);
let hasVisible2 = false;
for (let i = 3; i < rgba2.length; i += 4) {
@@ -1560,8 +1624,6 @@ export async function renderSignatureFieldAppearances(
const hDev = Math.max(1, Math.round(pdfH * dpr));
const stride = wDev * 4;
const bytes = stride * hDev;
const pdfiumWasm = m.pdfium as any;
const heapPtr = m.pdfium.wasmExports.malloc(bytes);
const bitmapPtr = m.FPDFBitmap_CreateEx(
wDev,
@@ -1577,8 +1639,10 @@ export async function renderSignatureFieldAppearances(
const sx = wDev / pdfW;
const sy = hDev / pdfH;
const matrixPtr = m.pdfium.wasmExports.malloc(6 * 4);
const pdfiumRuntime = m.pdfium as typeof m.pdfium &
ExtendedPdfiumRuntime;
const matrixView = new Float32Array(
pdfiumWasm.HEAPF32.buffer,
pdfiumRuntime.HEAPF32.buffer,
matrixPtr,
6,
);
@@ -1603,7 +1667,7 @@ export async function renderSignatureFieldAppearances(
if (ok) {
const rgba = new Uint8ClampedArray(
pdfiumWasm.HEAPU8.buffer.slice(heapPtr, heapPtr + bytes),
pdfiumRuntime.HEAPU8.subarray(heapPtr, heapPtr + bytes),
);
let hasVisible = false;
for (let i = 3; i < rgba.length; i += 4) {
@@ -1663,7 +1727,7 @@ export async function renderSignatureFieldAppearances(
m.FPDFBitmap_Destroy(bmp2);
const rgba2 = new Uint8ClampedArray(
pdfiumWasm.HEAPU8.buffer.slice(heap2, heap2 + bytes),
pdfiumRuntime.HEAPU8.subarray(heap2, heap2 + bytes),
);
let hasVisible2 = false;
for (let i = 3; i < rgba2.length; i += 4) {
@@ -0,0 +1,54 @@
import { BASE_PATH } from "@app/constants/app";
import pdfiumWasmAssetUrl from "@embedpdf/pdfium/pdfium.wasm?url";
const getWasmUrl = (): string => {
if (
pdfiumWasmAssetUrl.startsWith("http://") ||
pdfiumWasmAssetUrl.startsWith("https://") ||
pdfiumWasmAssetUrl.startsWith("//")
) {
return pdfiumWasmAssetUrl;
}
const origin = typeof window !== "undefined" ? window.location.origin : "";
if (import.meta.env.DEV) {
return `${origin}${BASE_PATH}/pdfium/pdfium.wasm`;
}
const cleanAssetUrl = pdfiumWasmAssetUrl
.replace(/^\.\//, "")
.replace(/^\//, "");
return `${origin}${BASE_PATH}/${cleanAssetUrl}`;
};
export const pdfiumWasmUrl = getWasmUrl();
let resolvePromise: (module: WebAssembly.Module | null) => void;
let compilationStarted = false;
export const pdfiumWasmModulePromise = new Promise<WebAssembly.Module | null>(
(resolve) => {
resolvePromise = resolve;
},
);
export function startEagerWasmCompilation(): void {
if (compilationStarted) return;
compilationStarted = true;
if (
typeof WebAssembly === "object" &&
typeof WebAssembly.compileStreaming === "function"
) {
WebAssembly.compileStreaming(fetch(pdfiumWasmUrl))
.then(resolvePromise)
.catch((err) => {
console.warn(
"Eager WASM compilation failed or not supported in this environment:",
err,
);
resolvePromise(null);
});
} else {
resolvePromise(null);
}
}