fix(web): legacy-сборка pdf.js — «Не удалось открыть PDF» в Яндекс Браузере (#90)

Копия исправления arm-web #22 (контур разворачивается из web/). Обычная сборка pdfjs-dist 5.7 в воркере
вызывает Uint8Array.prototype.toHex (Chromium 140+), в Яндекс Браузере getDocument падает
«a.toHex is not a function». PdfPages берёт модуль и воркер из pdfjs-dist/legacy/build через
widgets/DocumentReview/pdfjs.ts; регрессионный тест открывает PDF без Uint8Array.prototype.toHex.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-09 19:41:19 +04:00
parent 71e55b19ca
commit ca6f2b7cc4
3 changed files with 63 additions and 5 deletions

View File

@ -1,10 +1,6 @@
import * as pdfjsLib from 'pdfjs-dist';
import type { PDFDocumentProxy, RenderTask } from 'pdfjs-dist';
import { useCallback, useEffect, useRef, useState } from 'react';
// worker собирается вместе с приложением (без CDN)
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString();
import { pdfjsLib } from './pdfjs';
export const MIN_ZOOM = 0.5;
export const MAX_ZOOM = 3;

View File

@ -0,0 +1,54 @@
import { afterEach, describe, expect, it } from 'vitest';
// под vitest `new URL(<пакет>, import.meta.url)` из pdfjs.ts не резолвится сборщиком — воркер берём по пути от корня проекта
const WORKER = new URL('../../../node_modules/pdfjs-dist/legacy/build/pdf.worker.min.mjs', import.meta.url).href;
/** Минимальный PDF из одной страницы с корректной таблицей xref. */
function minimalPdf(): Uint8Array {
const objects = [
'<< /Type /Catalog /Pages 2 0 R >>',
'<< /Type /Pages /Kids [3 0 R] /Count 1 >>',
'<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 100] >>',
];
let body = '%PDF-1.4\n';
const offsets: number[] = [];
objects.forEach((obj, i) => {
offsets.push(body.length);
body += `${i + 1} 0 obj\n${obj}\nendobj\n`;
});
const xref = body.length;
body += `xref\n0 ${objects.length + 1}\n0000000000 65535 f \n`;
for (const o of offsets) {
body += `${String(o).padStart(10, '0')} 00000 n \n`;
}
body += `trailer\n<< /Size ${objects.length + 1} /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`;
return new TextEncoder().encode(body);
}
type ToHexProto = { toHex?: unknown };
const proto = Uint8Array.prototype as ToHexProto;
const nativeToHex = proto.toHex;
afterEach(() => {
if (nativeToHex === undefined) {
delete proto.toHex;
} else {
proto.toHex = nativeToHex;
}
});
describe('pdfjs (сборка для браузеров без Uint8Array.prototype.toHex)', () => {
// Яндекс Браузер (ядро Chromium < 140) не имеет Uint8Array.prototype.toHex; обычная сборка pdf.js 5.x вызывает его
// при открытии документа (отпечаток) и падает «a.toHex is not a function». Legacy-сборка несёт полифилл.
it('открывает PDF, когда Uint8Array.prototype.toHex отсутствует', async () => {
delete proto.toHex;
const { pdfjsLib } = await import('./pdfjs');
pdfjsLib.GlobalWorkerOptions.workerSrc = WORKER;
const doc = await pdfjsLib.getDocument({ data: minimalPdf() }).promise;
try {
expect(doc.numPages).toBe(1);
} finally {
await doc.destroy();
}
});
});

View File

@ -0,0 +1,8 @@
// Legacy-сборка pdf.js: несёт полифиллы (в т. ч. Uint8Array.prototype.toHex, Chromium 140+), без которых обычная
// сборка падает при открытии документа в Яндекс Браузере — «a.toHex is not a function» (arm-web #22). Воркер — та же
// сборка, собирается вместе с приложением (без CDN).
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.mjs';
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/legacy/build/pdf.worker.min.mjs', import.meta.url).toString();
export { pdfjsLib };