diff --git a/rsbuild.config.ts b/rsbuild.config.ts index 29003b1..9bcc497 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -12,6 +12,8 @@ export default defineConfig({ }, }, output: { + // wasm-декодеры pdf.js (JBIG2, JPEG2000, ICC): без них скан не рисуется (AURORA-1635). Путь = PDFJS_WASM_DIR + copy: [{ from: 'node_modules/pdfjs-dist/wasm', to: 'static/pdfjs/wasm' }], filename: { js: '[name]_script.[contenthash:8].js', css: '[name]_style.[contenthash:8].css', diff --git a/src/widgets/DocumentReview/PdfPages.tsx b/src/widgets/DocumentReview/PdfPages.tsx index 12cb4e7..ba3ce11 100644 --- a/src/widgets/DocumentReview/PdfPages.tsx +++ b/src/widgets/DocumentReview/PdfPages.tsx @@ -1,6 +1,8 @@ import type { PDFDocumentProxy, RenderTask } from 'pdfjs-dist'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Config } from '../../conf/config'; import { pdfjsLib } from './pdfjs'; +import { pdfjsWasmUrl } from './pdfjsOptions'; export const MIN_ZOOM = 0.5; export const MAX_ZOOM = 3; @@ -22,7 +24,7 @@ export default function PdfPages({ data, zoom }: { data: ArrayBuffer; zoom: numb let cancelled = false; async function load() { // pdf.js забирает переданный буфер себе, поэтому отдаём копию (эффект может выполниться повторно) - pdf = await pdfjsLib.getDocument({ data: data.slice(0) }).promise; + pdf = await pdfjsLib.getDocument({ data: data.slice(0), wasmUrl: pdfjsWasmUrl(Config.APP_ADDRESS) }).promise; if (cancelled) { pdf.destroy(); return; diff --git a/src/widgets/DocumentReview/pdfjsOptions.test.ts b/src/widgets/DocumentReview/pdfjsOptions.test.ts new file mode 100644 index 0000000..e35f4ca --- /dev/null +++ b/src/widgets/DocumentReview/pdfjsOptions.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from 'vitest'; +import { PDFJS_WASM_DIR, pdfjsWasmUrl } from './pdfjsOptions'; + +describe('pdfjsWasmUrl', () => { + it('кладёт wasm под префикс приложения и заканчивается слэшем (pdf.js дописывает к нему имя файла)', () => { + expect(pdfjsWasmUrl('/aurora/apps/fastapi-dkb-armka')).toBe(`/aurora/apps/fastapi-dkb-armka/${PDFJS_WASM_DIR}/`); + }); + + it('не удваивает слэши, если префикс уже заканчивается слэшем', () => { + expect(pdfjsWasmUrl('/aurora/apps/fastapi-dkb-armka/')).toBe(`/aurora/apps/fastapi-dkb-armka/${PDFJS_WASM_DIR}/`); + }); + + it('при корневом префиксе даёт путь от корня', () => { + expect(pdfjsWasmUrl('/')).toBe(`/${PDFJS_WASM_DIR}/`); + expect(pdfjsWasmUrl('')).toBe(`/${PDFJS_WASM_DIR}/`); + }); +}); diff --git a/src/widgets/DocumentReview/pdfjsOptions.ts b/src/widgets/DocumentReview/pdfjsOptions.ts new file mode 100644 index 0000000..480b277 --- /dev/null +++ b/src/widgets/DocumentReview/pdfjsOptions.ts @@ -0,0 +1,14 @@ +// Опции pdf.js для getDocument. Чистый модуль (без импорта pdfjs-dist), чтобы его можно было тестировать. +// +// pdf.js 5.x декодирует сканы JBIG2 и JPEG2000 и цветовые профили ICC через wasm-файлы (jbig2.wasm, openjpeg.wasm, +// qcms_bg.wasm). Без опции wasmUrl он их не находит: «JBig2 failed to initialize», «Unable to decode image», страница +// остаётся пустой (AURORA-1635). Файлы копирует в сборку rsbuild.config.ts (output.copy) в каталог PDFJS_WASM_DIR. + +/** Каталог wasm-файлов pdf.js в собранном приложении (относительно корня приложения). Тот же путь — в rsbuild.config.ts. */ +export const PDFJS_WASM_DIR = 'static/pdfjs/wasm'; + +/** Адрес каталога wasm: с учётом префикса приложения (ROOT_PATH) и обязательным слэшем в конце — pdf.js дописывает к нему имя файла. */ +export function pdfjsWasmUrl(appAddress: string): string { + const base = appAddress.replace(/\/+$/, ''); + return `${base}/${PDFJS_WASM_DIR}/`; +}