From f868a9250f6296cc65c5dd97f5f4ccc91a3d3880 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Fri, 16 Oct 2026 13:28:29 +0400 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=BA=D0=B0=D0=BD=20PDF=20=D1=81=20JBIG2?= =?UTF-8?q?=20=D0=BD=D0=B5=20=D1=80=D0=B8=D1=81=D0=BE=D0=B2=D0=B0=D0=BB?= =?UTF-8?q?=D1=81=D1=8F:=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=20pdf.js=20wasmUrl=20=D0=B8=20=D0=BA=D0=BE=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20wasm=20=D0=B2=20=D1=81?= =?UTF-8?q?=D0=B1=D0=BE=D1=80=D0=BA=D1=83=20(#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pdf.js 5.x декодирует JBIG2/JPEG2000/ICC через wasm и без опции wasmUrl падает: «JBig2 failed to initialize», «Unable to decode image» (AURORA-1635). - rsbuild: output.copy node_modules/pdfjs-dist/wasm -> static/pdfjs/wasm; - pdfjsOptions.ts: pdfjsWasmUrl(APP_ADDRESS) с учётом ROOT_PATH (+ тест); - PdfPages: getDocument({ wasmUrl }). Legacy-сборка pdf.js сохранена (#22). Co-Authored-By: Claude Opus 5.5 --- rsbuild.config.ts | 2 ++ src/widgets/DocumentReview/PdfPages.tsx | 4 +++- src/widgets/DocumentReview/pdfjsOptions.test.ts | 17 +++++++++++++++++ src/widgets/DocumentReview/pdfjsOptions.ts | 14 ++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/widgets/DocumentReview/pdfjsOptions.test.ts create mode 100644 src/widgets/DocumentReview/pdfjsOptions.ts 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}/`; +} -- 2.47.2