@file-viewer/renderer-pdf 2.1.25 → 2.1.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +9 -2
- package/README.md +9 -2
- package/dist/pdf.js +331 -21
- package/dist/pdfFontFallback.d.ts +33 -0
- package/dist/pdfFontFallback.js +282 -0
- package/dist/pdfIdentityFontRepair.d.ts +6 -0
- package/dist/pdfIdentityFontRepair.js +452 -0
- package/package.json +5 -3
package/README.en.md
CHANGED
|
@@ -25,21 +25,28 @@ const options = {
|
|
|
25
25
|
|
|
26
26
|
## Offline Assets
|
|
27
27
|
|
|
28
|
-
PDF preview depends on the PDF.js worker, cMaps, WASM helpers, and
|
|
28
|
+
PDF preview depends on the PDF.js worker, cMaps, WASM helpers, standard fonts, and an offline fallback for unembedded CJK fonts. Asset paths use the same unified options as `@file-viewer/core`:
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
31
|
const options = {
|
|
32
32
|
renderers: pdfRenderer,
|
|
33
33
|
pdf: {
|
|
34
|
+
assetBaseUrl: '/workspace/',
|
|
34
35
|
workerUrl: '/vendor/pdf/pdf.worker.mjs',
|
|
35
36
|
cMapUrl: '/vendor/pdf/cmaps/',
|
|
36
37
|
wasmUrl: '/vendor/pdf/wasm/',
|
|
37
38
|
standardFontDataUrl: '/vendor/pdf/standard_fonts/',
|
|
39
|
+
cjkFontFallbackPath: '/vendor/pdf/fonts/',
|
|
40
|
+
identityFontRepair: true,
|
|
38
41
|
},
|
|
39
42
|
}
|
|
40
43
|
```
|
|
41
44
|
|
|
42
|
-
When no explicit URL is
|
|
45
|
+
When no explicit PDF asset URL is configured, the renderer first uses the public base exposed by build tools such as Vite and can fall back to the page entry script. An app deployed under `/workspace/` therefore still requests `/workspace/vendor/pdf/pdf.worker.mjs` while its current SPA route is `/workspace/c/`. UMI, custom proxies, and builds whose public path cannot be detected can set `pdf.assetBaseUrl: '/workspace/'` to pin the base for every PDF offline asset. If the host app did not run `file-viewer-copy-assets`, does not use `@file-viewer/vite-plugin`, or a local dev server falls back to HTML for that path, the renderer lazy-loads the packaged PDF.js worker handler as a compatibility fallback so preview does not fail with `Setting up fake worker failed`. For best performance, complete cMap/standard-font/WASM decoding, or strict offline deployments, still copy viewer assets and point these URLs at real static files. The pinned PDF.js 5.4 stable line loads local JBIG2, JPEG 2000, color-management, and related helpers through `wasmUrl`; viewer assets and the Vite plugin copy them without relying on the public internet.
|
|
46
|
+
|
|
47
|
+
`cjkFontFallback` is enabled by default. When a PDF references `MicrosoftYaHei-Bold`, SimSun, SimHei, or another CJK font without embedding the font program, the renderer prefers an installed system font and otherwise aliases the original PDF font name to the bundled Noto Sans SC variable font. Only WOFF2 shards needed by the current page text are loaded. The first page waits for the font before rendering, and later pages are redrawn when they introduce new glyphs. Set the option to `false` to disable this behavior, or point `cjkFontFallbackPath` at a self-hosted directory with the same structure.
|
|
48
|
+
|
|
49
|
+
`identityFontRepair` is also enabled by default for malformed PDFs that write TrueType glyph IDs through `Identity-H`/`Identity-V` but omit `ToUnicode`. The repair module is loaded only after CJK font substitution and multiple control-character glyphs are detected. If the same PDF embeds a usable same-family TrueType font, its cmap is used to rebuild the Unicode mapping for the in-memory preview. The repair is applied only to a preview copy and never overwrites the original file or download source; an unsafe or unsupported repair falls back to the original preview. Set the option to `false` to disable this compatibility path.
|
|
43
50
|
|
|
44
51
|
## Migration Note
|
|
45
52
|
|
package/README.md
CHANGED
|
@@ -25,21 +25,28 @@ const options = {
|
|
|
25
25
|
|
|
26
26
|
## 离线资源
|
|
27
27
|
|
|
28
|
-
PDF 预览依赖 PDF.js worker、cMaps、WASM
|
|
28
|
+
PDF 预览依赖 PDF.js worker、cMaps、WASM、standard fonts,以及未嵌入中文字体的离线回退资源。资源路径沿用 `@file-viewer/core` 的统一 options:
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
31
|
const options = {
|
|
32
32
|
renderers: pdfRenderer,
|
|
33
33
|
pdf: {
|
|
34
|
+
assetBaseUrl: '/workspace/',
|
|
34
35
|
workerUrl: '/vendor/pdf/pdf.worker.mjs',
|
|
35
36
|
cMapUrl: '/vendor/pdf/cmaps/',
|
|
36
37
|
wasmUrl: '/vendor/pdf/wasm/',
|
|
37
38
|
standardFontDataUrl: '/vendor/pdf/standard_fonts/',
|
|
39
|
+
cjkFontFallbackPath: '/vendor/pdf/fonts/',
|
|
40
|
+
identityFontRepair: true,
|
|
38
41
|
},
|
|
39
42
|
}
|
|
40
43
|
```
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
默认未显式配置时,渲染器优先使用 Vite 等构建器提供的公开基址,并可从页面入口脚本回退识别部署子路径。因此应用部署在 `/workspace/`、当前 SPA 路由为 `/workspace/c/` 时,仍会请求 `/workspace/vendor/pdf/pdf.worker.mjs`。UMI、自定义代理或无法自动识别的构建环境可以设置 `pdf.assetBaseUrl: '/workspace/'` 固定全部 PDF 离线资源的基址。如果项目没有执行 `file-viewer-copy-assets`、没有使用 `@file-viewer/vite-plugin`,或者本地临时服务器把该路径回退成 HTML,PDF renderer 会自动懒加载包内 PDF.js worker handler 作为兼容兜底,避免 `Setting up fake worker failed` 直接中断预览。需要最佳性能、完整 cMap/standard fonts/WASM 解码或严格离线部署时,仍建议复制 viewer assets 并配置真实静态地址。当前锁定的 PDF.js 5.4 稳定线会通过 `wasmUrl` 本地加载 JBIG2、JPEG 2000、颜色管理等辅助资源;这些资源由 viewer assets 和 Vite 插件统一复制,不依赖公网。
|
|
46
|
+
|
|
47
|
+
`cjkFontFallback` 默认开启。PDF 引用了 `MicrosoftYaHei-Bold`、宋体、黑体或其他中文字体但没有嵌入字体数据时,渲染器优先使用本机字体;本机缺失时,会把原 PDF 字体名映射到本地 Noto Sans SC 可变字体,并根据当前页实际文字只加载需要的 WOFF2 分片。首屏会在字体就绪后再渲染,后续页发现新字形时会自动重绘。可设为 `false` 完全关闭,或用 `cjkFontFallbackPath` 指向同结构的自托管字体目录。
|
|
48
|
+
|
|
49
|
+
`identityFontRepair` 也默认开启,用于少数把 TrueType 字形 ID 写入 `Identity-H`/`Identity-V`、却遗漏 `ToUnicode` 的异常 PDF。只有检测到中文字体替换和多个控制字符乱码时,渲染器才会懒加载修复模块;若文件内同时嵌入了可用的同族 TrueType 字体,就从其 cmap 重建内存预览所需的 Unicode 映射。修复只作用于预览副本,不覆盖原始文件和下载源;无法安全修复时继续使用原预览。可设为 `false` 关闭。
|
|
43
50
|
|
|
44
51
|
## 迁移说明
|
|
45
52
|
|
package/dist/pdf.js
CHANGED
|
@@ -3,6 +3,7 @@ import { EventBus, GenericL10n, PDFFindController, PDFLinkService, PDFViewer, }
|
|
|
3
3
|
import { registerFileViewerSearchProvider, registerFileViewerZoomProvider, unregisterFileViewerSearchProvider, unregisterFileViewerZoomProvider, registerFileViewerViewStateProvider, unregisterFileViewerViewStateProvider, createFileViewerZoomChangeEmitter, createFileViewerViewStateChange, createFileViewerViewStateChangeEmitter, createFileViewerTranslator, buildPrintPageStyle, formatCssPixels, DEFAULT_PDF_RANGE_CHUNK_SIZE, resolveFileViewerLocale, resolveFileViewerFitScale, } from '@file-viewer/core';
|
|
4
4
|
import { DEFAULT_FILE_VIEWER_PDF_WORKER_PATH, resolveFileViewerPdfAssetUrls, } from '@file-viewer/core/assets';
|
|
5
5
|
import { pdfViewerStyle } from './pdfStyles.js';
|
|
6
|
+
import { collectMalformedIdentityFontNames, createPdfCjkFontFallbackManager, detectMalformedIdentityCjkFontFamilies, } from './pdfFontFallback.js';
|
|
6
7
|
export const DEFAULT_FILE_VIEWER_PDF_WORKER_URL = DEFAULT_FILE_VIEWER_PDF_WORKER_PATH;
|
|
7
8
|
const MIN_SCALE = 0.2;
|
|
8
9
|
const MAX_SCALE = 3;
|
|
@@ -19,6 +20,7 @@ const normalizedPdfViewerStyle = pdfViewerStyle
|
|
|
19
20
|
.replace(/--page-border-image:\s*url\(images\/shadow\.png\)\s*9 9 repeat;/g, '--page-border-image:none;')
|
|
20
21
|
.replace(/background:\s*url\("\.\/images\/loading-icon\.gif"\)\s*center no-repeat;/g, 'background:none;');
|
|
21
22
|
const pdfJsConsoleErrorSuppressions = new WeakMap();
|
|
23
|
+
const pdfJsConsoleWarningSuppressions = new WeakMap();
|
|
22
24
|
const createStyle = (documentRef) => {
|
|
23
25
|
const style = documentRef.createElement('style');
|
|
24
26
|
style.textContent = `${normalizedPdfViewerStyle}
|
|
@@ -164,6 +166,63 @@ const suppressPdfJsDestroyedTransportPageInitErrors = (view) => {
|
|
|
164
166
|
}, PDF_JS_DESTROY_CONSOLE_SUPPRESSION_MS);
|
|
165
167
|
};
|
|
166
168
|
};
|
|
169
|
+
const isPdfJsMissingSystemFontWarning = (args) => {
|
|
170
|
+
const [message] = args;
|
|
171
|
+
return typeof message === 'string' &&
|
|
172
|
+
/^(?:Warning:\s*)?Cannot load system font: .+installing it could help to improve PDF rendering\.$/.test(message);
|
|
173
|
+
};
|
|
174
|
+
const suppressPdfJsMissingSystemFontWarnings = (view) => {
|
|
175
|
+
const consoleRef = (view.console ||
|
|
176
|
+
globalThis.console);
|
|
177
|
+
if (!consoleRef || typeof consoleRef.warn !== 'function') {
|
|
178
|
+
return () => { };
|
|
179
|
+
}
|
|
180
|
+
let suppression = pdfJsConsoleWarningSuppressions.get(consoleRef);
|
|
181
|
+
if (!suppression) {
|
|
182
|
+
const originalWarn = consoleRef.warn;
|
|
183
|
+
suppression = {
|
|
184
|
+
originalWarn,
|
|
185
|
+
patchedWarn: (...args) => {
|
|
186
|
+
if (isPdfJsMissingSystemFontWarning(args)) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
return originalWarn.apply(consoleRef, args);
|
|
190
|
+
},
|
|
191
|
+
depth: 0,
|
|
192
|
+
restoreTimer: undefined,
|
|
193
|
+
};
|
|
194
|
+
pdfJsConsoleWarningSuppressions.set(consoleRef, suppression);
|
|
195
|
+
consoleRef.warn = suppression.patchedWarn;
|
|
196
|
+
}
|
|
197
|
+
else if (suppression.restoreTimer !== undefined) {
|
|
198
|
+
view.clearTimeout(suppression.restoreTimer);
|
|
199
|
+
suppression.restoreTimer = undefined;
|
|
200
|
+
}
|
|
201
|
+
suppression.depth += 1;
|
|
202
|
+
let released = false;
|
|
203
|
+
return () => {
|
|
204
|
+
if (released) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
released = true;
|
|
208
|
+
const current = pdfJsConsoleWarningSuppressions.get(consoleRef);
|
|
209
|
+
if (!current) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
current.depth = Math.max(0, current.depth - 1);
|
|
213
|
+
if (current.depth || current.restoreTimer !== undefined) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
current.restoreTimer = view.setTimeout(() => {
|
|
217
|
+
current.restoreTimer = undefined;
|
|
218
|
+
if (current.depth || consoleRef.warn !== current.patchedWarn) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
consoleRef.warn = current.originalWarn;
|
|
222
|
+
pdfJsConsoleWarningSuppressions.delete(consoleRef);
|
|
223
|
+
}, PDF_JS_DESTROY_CONSOLE_SUPPRESSION_MS);
|
|
224
|
+
};
|
|
225
|
+
};
|
|
167
226
|
const isConfiguredUrl = (value) => {
|
|
168
227
|
return value !== undefined && value !== null && String(value).trim().length > 0;
|
|
169
228
|
};
|
|
@@ -195,6 +254,50 @@ const installBundledPdfFakeWorker = async () => {
|
|
|
195
254
|
const resolvePdfWorkerUrl = (options, documentBaseUrl) => {
|
|
196
255
|
return resolveFileViewerPdfAssetUrls(options, documentBaseUrl).workerUrl;
|
|
197
256
|
};
|
|
257
|
+
const readBundlerPublicBaseUrl = () => {
|
|
258
|
+
var _a;
|
|
259
|
+
try {
|
|
260
|
+
const value = (_a = import.meta.env) === null || _a === void 0 ? void 0 : _a.BASE_URL;
|
|
261
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
return '';
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
const resolvePdfRuntimeAssetBaseUrl = (documentRef) => {
|
|
268
|
+
const documentBaseUrl = documentRef.baseURI || documentRef.URL || 'file:///';
|
|
269
|
+
const bundlerBaseUrl = readBundlerPublicBaseUrl();
|
|
270
|
+
if (bundlerBaseUrl && bundlerBaseUrl !== '.' && bundlerBaseUrl !== './') {
|
|
271
|
+
try {
|
|
272
|
+
return new URL(bundlerBaseUrl.endsWith('/') ? bundlerBaseUrl : `${bundlerBaseUrl}/`, documentBaseUrl).href;
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// Continue with DOM-based public-path detection.
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// An explicit <base> is authoritative and already reflected by baseURI.
|
|
279
|
+
if (documentRef.querySelector('base[href]')) {
|
|
280
|
+
return documentBaseUrl;
|
|
281
|
+
}
|
|
282
|
+
// Entry scripts remain under the public deployment base when an SPA route
|
|
283
|
+
// changes document.baseURI. This covers Vite and common UMI/Webpack layouts.
|
|
284
|
+
for (const script of Array.from(documentRef.querySelectorAll('script[src]'))) {
|
|
285
|
+
try {
|
|
286
|
+
const scriptUrl = new URL(script.src || script.getAttribute('src') || '', documentBaseUrl);
|
|
287
|
+
const assetDirectory = scriptUrl.pathname.match(/^(.*\/)(?:assets|static)\/[^/]+$/i);
|
|
288
|
+
if (assetDirectory) {
|
|
289
|
+
return new URL(assetDirectory[1], scriptUrl.origin).href;
|
|
290
|
+
}
|
|
291
|
+
if (/\/(?:umi|main|index)(?:[.-][^/]*)?\.(?:m?js)$/i.test(scriptUrl.pathname)) {
|
|
292
|
+
return new URL('./', scriptUrl).href;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
// Ignore unrelated or malformed script URLs.
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return documentBaseUrl;
|
|
300
|
+
};
|
|
198
301
|
const buildOutlineItems = (items, prefix = 'outline', getFallbackTitle = index => `Outline ${index + 1}`) => items.map((item, index) => {
|
|
199
302
|
const id = `${prefix}-${index}`;
|
|
200
303
|
const children = Array.isArray(item.items)
|
|
@@ -209,7 +312,7 @@ const buildOutlineItems = (items, prefix = 'outline', getFallbackTitle = index =
|
|
|
209
312
|
};
|
|
210
313
|
});
|
|
211
314
|
export default async function renderPdf(buffer, target, context) {
|
|
212
|
-
var _a, _b, _c;
|
|
315
|
+
var _a, _b, _c, _d;
|
|
213
316
|
const documentRef = target.ownerDocument || document;
|
|
214
317
|
const targetWindow = documentRef.defaultView || (typeof window !== 'undefined' ? window : null);
|
|
215
318
|
const t = createFileViewerTranslator(context === null || context === void 0 ? void 0 : context.options);
|
|
@@ -218,6 +321,10 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
218
321
|
throw new Error(t('pdf.error.browserWindow'));
|
|
219
322
|
}
|
|
220
323
|
const options = (_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.pdf;
|
|
324
|
+
const pdfRuntimeAssetBaseUrl = resolvePdfRuntimeAssetBaseUrl(documentRef);
|
|
325
|
+
const cjkFontFallbackEnabled = (options === null || options === void 0 ? void 0 : options.cjkFontFallback) !== false;
|
|
326
|
+
const identityFontRepairEnabled = (options === null || options === void 0 ? void 0 : options.identityFontRepair) !== false;
|
|
327
|
+
const fontInspectionEnabled = cjkFontFallbackEnabled || identityFontRepairEnabled;
|
|
221
328
|
const initialViewState = (options === null || options === void 0 ? void 0 : options.initialViewState) || ((_b = context === null || context === void 0 ? void 0 : context.options) === null || _b === void 0 ? void 0 : _b.initialViewState) || null;
|
|
222
329
|
const navigationEnabled = (options === null || options === void 0 ? void 0 : options.navigation) !== false;
|
|
223
330
|
const toolbarVisible = (options === null || options === void 0 ? void 0 : options.toolbar) !== false;
|
|
@@ -260,6 +367,10 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
260
367
|
let pdfSearchWaiters = [];
|
|
261
368
|
const pdfThumbnails = new Map();
|
|
262
369
|
const pendingPdfThumbnails = new Set();
|
|
370
|
+
const pdfCjkFontFallbackPageLoads = new Map();
|
|
371
|
+
const pdfCjkFontFallbackRenderHandledPages = new Set();
|
|
372
|
+
let pdfCjkFontFallbackManager = null;
|
|
373
|
+
let restorePdfJsMissingSystemFontWarnings = () => { };
|
|
263
374
|
const pdfContext = {
|
|
264
375
|
viewer: null,
|
|
265
376
|
linkService: null,
|
|
@@ -269,6 +380,17 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
269
380
|
document: null,
|
|
270
381
|
search: '',
|
|
271
382
|
};
|
|
383
|
+
const ensurePdfPageCjkFontFallback = (pageNumber, page) => {
|
|
384
|
+
if (!pdfCjkFontFallbackManager) {
|
|
385
|
+
return Promise.resolve(false);
|
|
386
|
+
}
|
|
387
|
+
let pending = pdfCjkFontFallbackPageLoads.get(pageNumber);
|
|
388
|
+
if (!pending) {
|
|
389
|
+
pending = pdfCjkFontFallbackManager.ensurePage(page);
|
|
390
|
+
pdfCjkFontFallbackPageLoads.set(pageNumber, pending);
|
|
391
|
+
}
|
|
392
|
+
return pending;
|
|
393
|
+
};
|
|
272
394
|
const root = createElement(documentRef, 'div', 'pdf-shell');
|
|
273
395
|
root.dataset.viewerSearchProvider = 'pdf';
|
|
274
396
|
root.dataset.viewerZoomProvider = 'pdf';
|
|
@@ -426,6 +548,7 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
426
548
|
if (destroyed || pdfContext.document !== pdfDocument) {
|
|
427
549
|
return;
|
|
428
550
|
}
|
|
551
|
+
await ensurePdfPageCjkFontFallback(pageNumber, page);
|
|
429
552
|
const baseViewport = page.getViewport({
|
|
430
553
|
scale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
|
431
554
|
rotation: currentRotation,
|
|
@@ -444,7 +567,7 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
444
567
|
}
|
|
445
568
|
canvas.width = Math.max(1, Math.ceil(renderViewport.width));
|
|
446
569
|
canvas.height = Math.max(1, Math.ceil(renderViewport.height));
|
|
447
|
-
await page.render({ canvasContext, viewport: renderViewport }).promise;
|
|
570
|
+
await page.render({ canvas, canvasContext, viewport: renderViewport }).promise;
|
|
448
571
|
if (destroyed || pdfContext.document !== pdfDocument) {
|
|
449
572
|
return;
|
|
450
573
|
}
|
|
@@ -463,6 +586,64 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
463
586
|
pendingPdfThumbnails.delete(pageNumber);
|
|
464
587
|
}
|
|
465
588
|
};
|
|
589
|
+
const renderFirstPageThumbnail = async (captureOptions) => {
|
|
590
|
+
var _a, _b, _c, _d, _e, _f;
|
|
591
|
+
const pdfDocument = pdfContext.document;
|
|
592
|
+
if (!pdfDocument) {
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
if ((_a = captureOptions.signal) === null || _a === void 0 ? void 0 : _a.aborted) {
|
|
596
|
+
throw captureOptions.signal.reason;
|
|
597
|
+
}
|
|
598
|
+
const page = await pdfDocument.getPage(1);
|
|
599
|
+
await ensurePdfPageCjkFontFallback(1, page);
|
|
600
|
+
const baseViewport = page.getViewport({ scale: 1, rotation: currentRotation });
|
|
601
|
+
const scale = Math.max(0.1, Math.min(captureOptions.width / Math.max(baseViewport.width, 1), captureOptions.height / Math.max(baseViewport.height, 1)));
|
|
602
|
+
const viewport = page.getViewport({ scale, rotation: currentRotation });
|
|
603
|
+
const canvas = documentRef.createElement('canvas');
|
|
604
|
+
const canvasContext = canvas.getContext('2d');
|
|
605
|
+
if (!canvasContext) {
|
|
606
|
+
return null;
|
|
607
|
+
}
|
|
608
|
+
canvas.width = Math.max(1, Math.ceil(viewport.width));
|
|
609
|
+
canvas.height = Math.max(1, Math.ceil(viewport.height));
|
|
610
|
+
const renderTask = page.render({ canvas, canvasContext, viewport });
|
|
611
|
+
const cancelRender = () => renderTask.cancel();
|
|
612
|
+
(_b = captureOptions.signal) === null || _b === void 0 ? void 0 : _b.addEventListener('abort', cancelRender, { once: true });
|
|
613
|
+
try {
|
|
614
|
+
await renderTask.promise;
|
|
615
|
+
if ((_c = captureOptions.signal) === null || _c === void 0 ? void 0 : _c.aborted) {
|
|
616
|
+
throw captureOptions.signal.reason;
|
|
617
|
+
}
|
|
618
|
+
return await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
|
|
619
|
+
}
|
|
620
|
+
catch (error) {
|
|
621
|
+
if ((_d = captureOptions.signal) === null || _d === void 0 ? void 0 : _d.aborted) {
|
|
622
|
+
throw captureOptions.signal.reason;
|
|
623
|
+
}
|
|
624
|
+
throw error;
|
|
625
|
+
}
|
|
626
|
+
finally {
|
|
627
|
+
(_e = captureOptions.signal) === null || _e === void 0 ? void 0 : _e.removeEventListener('abort', cancelRender);
|
|
628
|
+
canvas.width = 0;
|
|
629
|
+
canvas.height = 0;
|
|
630
|
+
(_f = page.cleanup) === null || _f === void 0 ? void 0 : _f.call(page);
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
(_d = context === null || context === void 0 ? void 0 : context.registerThumbnailAdapter) === null || _d === void 0 ? void 0 : _d.call(context, {
|
|
634
|
+
beforeCapture: async ({ signal }) => {
|
|
635
|
+
while (loadStatus === 'loading' && !destroyed) {
|
|
636
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
637
|
+
throw signal.reason;
|
|
638
|
+
}
|
|
639
|
+
await new Promise(resolve => targetWindow.setTimeout(resolve, 16));
|
|
640
|
+
}
|
|
641
|
+
if (loadStatus === 'error') {
|
|
642
|
+
throw new Error(errorMessage || t('pdf.error.loadFailed'));
|
|
643
|
+
}
|
|
644
|
+
},
|
|
645
|
+
capture: renderFirstPageThumbnail,
|
|
646
|
+
});
|
|
466
647
|
const ensureThumbnailObserver = () => {
|
|
467
648
|
if (!thumbnailsEnabled || thumbnailObserver || typeof targetWindow.IntersectionObserver !== 'function') {
|
|
468
649
|
return;
|
|
@@ -597,7 +778,7 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
597
778
|
}
|
|
598
779
|
};
|
|
599
780
|
const createPdfWorker = async () => {
|
|
600
|
-
const workerUrl = resolvePdfWorkerUrl(options,
|
|
781
|
+
const workerUrl = resolvePdfWorkerUrl(options, pdfRuntimeAssetBaseUrl);
|
|
601
782
|
const hasExplicitWorkerUrl = isConfiguredUrl(options === null || options === void 0 ? void 0 : options.workerUrl);
|
|
602
783
|
const shouldUseRealWorker = !!(targetWindow === null || targetWindow === void 0 ? void 0 : targetWindow.Worker) &&
|
|
603
784
|
(hasExplicitWorkerUrl || await canUseResolvedPdfWorkerUrl(workerUrl));
|
|
@@ -1195,6 +1376,7 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
1195
1376
|
throw new Error(t('pdf.error.unloaded'));
|
|
1196
1377
|
}
|
|
1197
1378
|
const page = await pdfDocument.getPage(pageNumber);
|
|
1379
|
+
await ensurePdfPageCjkFontFallback(pageNumber, page);
|
|
1198
1380
|
const baseViewport = page.getViewport({
|
|
1199
1381
|
scale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
|
1200
1382
|
rotation: currentRotation,
|
|
@@ -1213,7 +1395,7 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
1213
1395
|
}
|
|
1214
1396
|
canvas.width = Math.ceil(renderViewport.width);
|
|
1215
1397
|
canvas.height = Math.ceil(renderViewport.height);
|
|
1216
|
-
await page.render({ canvasContext, viewport: renderViewport }).promise;
|
|
1398
|
+
await page.render({ canvas, canvasContext, viewport: renderViewport }).promise;
|
|
1217
1399
|
const pageTitle = t('pdf.export.pageTitle', { title: exportOptions.title, page: pageNumber });
|
|
1218
1400
|
const pageStyle = [
|
|
1219
1401
|
`--viewer-print-page-width:${formatCssPixels(pageWidth)}`,
|
|
@@ -1229,8 +1411,13 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
1229
1411
|
return `<div class="pdf-export-document">${pagesHtml.join('')}</div>`;
|
|
1230
1412
|
};
|
|
1231
1413
|
const loadFile = async () => {
|
|
1232
|
-
var _a, _b;
|
|
1414
|
+
var _a, _b, _c;
|
|
1233
1415
|
const requestVersion = ++loadVersion;
|
|
1416
|
+
restorePdfJsMissingSystemFontWarnings();
|
|
1417
|
+
restorePdfJsMissingSystemFontWarnings = () => { };
|
|
1418
|
+
pdfCjkFontFallbackManager = null;
|
|
1419
|
+
pdfCjkFontFallbackPageLoads.clear();
|
|
1420
|
+
pdfCjkFontFallbackRenderHandledPages.clear();
|
|
1234
1421
|
loadStatus = 'loading';
|
|
1235
1422
|
errorMessage = '';
|
|
1236
1423
|
pdfContext.document = null;
|
|
@@ -1258,6 +1445,7 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
1258
1445
|
linkService: pdfLinkService,
|
|
1259
1446
|
findController: pdfFindController,
|
|
1260
1447
|
l10n: new GenericL10n(resolvedLocale),
|
|
1448
|
+
enableAutoLinking: false,
|
|
1261
1449
|
});
|
|
1262
1450
|
pdfContext.viewer = pdfViewer;
|
|
1263
1451
|
pdfContext.linkService = pdfLinkService;
|
|
@@ -1314,12 +1502,46 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
1314
1502
|
emitViewStateChange('zoom-change', 'viewer');
|
|
1315
1503
|
}
|
|
1316
1504
|
});
|
|
1317
|
-
eventBus.on('pagerendered',
|
|
1505
|
+
eventBus.on('pagerendered', ({ pageNumber }) => {
|
|
1506
|
+
scheduleLegacyPageDimensionPatch();
|
|
1507
|
+
if (!pdfCjkFontFallbackManager ||
|
|
1508
|
+
pdfCjkFontFallbackRenderHandledPages.has(pageNumber)) {
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1511
|
+
pdfCjkFontFallbackRenderHandledPages.add(pageNumber);
|
|
1512
|
+
const pdfDocument = pdfContext.document;
|
|
1513
|
+
const alreadyPrepared = pdfCjkFontFallbackPageLoads.has(pageNumber);
|
|
1514
|
+
if (!pdfDocument || alreadyPrepared) {
|
|
1515
|
+
return;
|
|
1516
|
+
}
|
|
1517
|
+
void pdfDocument.getPage(pageNumber)
|
|
1518
|
+
.then(page => ensurePdfPageCjkFontFallback(pageNumber, page))
|
|
1519
|
+
.then(fontLoaded => {
|
|
1520
|
+
var _a;
|
|
1521
|
+
if (fontLoaded &&
|
|
1522
|
+
!destroyed &&
|
|
1523
|
+
pdfContext.document === pdfDocument) {
|
|
1524
|
+
(_a = pdfContext.viewer) === null || _a === void 0 ? void 0 : _a.refresh();
|
|
1525
|
+
}
|
|
1526
|
+
})
|
|
1527
|
+
.catch(error => {
|
|
1528
|
+
console.warn('[file-viewer] Unable to inspect a PDF page for CJK font fallback.', error);
|
|
1529
|
+
});
|
|
1530
|
+
});
|
|
1318
1531
|
if (!(context === null || context === void 0 ? void 0 : context.streamUrl) && !buffer.byteLength) {
|
|
1319
1532
|
throw new Error(t('pdf.error.missingSource'));
|
|
1320
1533
|
}
|
|
1321
|
-
const
|
|
1322
|
-
|
|
1534
|
+
const pdfAssets = resolveFileViewerPdfAssetUrls(options, pdfRuntimeAssetBaseUrl);
|
|
1535
|
+
if (cjkFontFallbackEnabled) {
|
|
1536
|
+
pdfCjkFontFallbackManager = createPdfCjkFontFallbackManager({
|
|
1537
|
+
documentRef,
|
|
1538
|
+
fontAssetPath: pdfAssets.cjkFontFallbackPath,
|
|
1539
|
+
onWarning: (message, error) => {
|
|
1540
|
+
console.warn(`[file-viewer] ${message}`, error || '');
|
|
1541
|
+
},
|
|
1542
|
+
});
|
|
1543
|
+
restorePdfJsMissingSystemFontWarnings = suppressPdfJsMissingSystemFontWarnings(targetWindow);
|
|
1544
|
+
}
|
|
1323
1545
|
const source = (context === null || context === void 0 ? void 0 : context.streamUrl)
|
|
1324
1546
|
? {
|
|
1325
1547
|
url: context.streamUrl,
|
|
@@ -1329,18 +1551,24 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
1329
1551
|
: {
|
|
1330
1552
|
data: buffer,
|
|
1331
1553
|
};
|
|
1332
|
-
const
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1554
|
+
const createLoadingResource = async (loadingSource) => {
|
|
1555
|
+
const worker = await createPdfWorker();
|
|
1556
|
+
const loadingTask = getDocument({
|
|
1557
|
+
...loadingSource,
|
|
1558
|
+
worker: worker || undefined,
|
|
1559
|
+
cMapUrl: pdfAssets.cMapUrl,
|
|
1560
|
+
wasmUrl: pdfAssets.wasmUrl,
|
|
1561
|
+
standardFontDataUrl: pdfAssets.standardFontDataUrl,
|
|
1562
|
+
useWorkerFetch: true,
|
|
1563
|
+
cMapPacked: true,
|
|
1564
|
+
enableXfa: true,
|
|
1565
|
+
fontExtraProperties: fontInspectionEnabled,
|
|
1566
|
+
});
|
|
1567
|
+
return { loadingTask, worker };
|
|
1568
|
+
};
|
|
1569
|
+
resource = await createLoadingResource(source);
|
|
1342
1570
|
pdfContext.resource = resource;
|
|
1343
|
-
|
|
1571
|
+
let pdfDocument = await resource.loadingTask.promise;
|
|
1344
1572
|
if (destroyed || requestVersion !== loadVersion || pdfContext.resource !== resource) {
|
|
1345
1573
|
if (pdfContext.resource === resource) {
|
|
1346
1574
|
pdfContext.resource = null;
|
|
@@ -1348,10 +1576,86 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
1348
1576
|
}
|
|
1349
1577
|
return;
|
|
1350
1578
|
}
|
|
1579
|
+
let firstPageTextContent = null;
|
|
1580
|
+
let firstPageForInspection = null;
|
|
1581
|
+
if (fontInspectionEnabled && pdfDocument.numPages > 0) {
|
|
1582
|
+
const firstPage = await pdfDocument.getPage(1);
|
|
1583
|
+
firstPageForInspection = firstPage;
|
|
1584
|
+
firstPageTextContent = await firstPageForInspection.getTextContent();
|
|
1585
|
+
}
|
|
1586
|
+
if (identityFontRepairEnabled && firstPageTextContent) {
|
|
1587
|
+
const malformedFontNames = collectMalformedIdentityFontNames(firstPageTextContent);
|
|
1588
|
+
if (malformedFontNames.length && firstPageForInspection) {
|
|
1589
|
+
await firstPageForInspection.getOperatorList();
|
|
1590
|
+
}
|
|
1591
|
+
const malformedFontFamilies = new Map();
|
|
1592
|
+
for (const fontName of malformedFontNames) {
|
|
1593
|
+
try {
|
|
1594
|
+
const family = (_b = firstPageForInspection === null || firstPageForInspection === void 0 ? void 0 : firstPageForInspection.commonObjs.get(fontName)) === null || _b === void 0 ? void 0 : _b.name;
|
|
1595
|
+
if (family) {
|
|
1596
|
+
malformedFontFamilies.set(fontName, family);
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
catch {
|
|
1600
|
+
// A font object that is still unresolved cannot be repaired safely.
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
const candidateFamilies = detectMalformedIdentityCjkFontFamilies(firstPageTextContent, fontName => malformedFontFamilies.get(fontName) || '');
|
|
1604
|
+
if (candidateFamilies.length) {
|
|
1605
|
+
let replacementResource = null;
|
|
1606
|
+
try {
|
|
1607
|
+
const sourceBytes = await pdfDocument.getData();
|
|
1608
|
+
const { repairMalformedIdentityCjkFonts } = await import('./pdfIdentityFontRepair.js');
|
|
1609
|
+
const repaired = await repairMalformedIdentityCjkFonts(sourceBytes, candidateFamilies);
|
|
1610
|
+
if (repaired.repairedFonts > 0) {
|
|
1611
|
+
const previousResource = resource;
|
|
1612
|
+
replacementResource = await createLoadingResource({ data: repaired.bytes });
|
|
1613
|
+
const replacementDocument = await replacementResource.loadingTask.promise;
|
|
1614
|
+
const repairedFirstPage = await replacementDocument.getPage(1);
|
|
1615
|
+
const replacementTextContent = await repairedFirstPage.getTextContent();
|
|
1616
|
+
if (destroyed ||
|
|
1617
|
+
requestVersion !== loadVersion ||
|
|
1618
|
+
pdfContext.resource !== previousResource) {
|
|
1619
|
+
await destroyPdfResource(replacementResource);
|
|
1620
|
+
return;
|
|
1621
|
+
}
|
|
1622
|
+
resource = replacementResource;
|
|
1623
|
+
replacementResource = null;
|
|
1624
|
+
pdfContext.resource = resource;
|
|
1625
|
+
pdfDocument = replacementDocument;
|
|
1626
|
+
firstPageTextContent = replacementTextContent;
|
|
1627
|
+
await destroyPdfResource(previousResource);
|
|
1628
|
+
console.info(`[file-viewer] Repaired ${repaired.repairedFonts} malformed PDF Identity CJK font mapping(s).`);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
catch (error) {
|
|
1632
|
+
if (replacementResource) {
|
|
1633
|
+
await destroyPdfResource(replacementResource);
|
|
1634
|
+
}
|
|
1635
|
+
console.warn('[file-viewer] Unable to repair a malformed PDF Identity CJK font; continuing with the original preview.', error);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
if (destroyed || requestVersion !== loadVersion || pdfContext.resource !== resource) {
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1351
1642
|
pageCount = pdfDocument.numPages;
|
|
1352
1643
|
currentPage = 1;
|
|
1353
1644
|
pdfContext.document = pdfDocument;
|
|
1354
|
-
(
|
|
1645
|
+
if (pdfCjkFontFallbackManager && pageCount > 0) {
|
|
1646
|
+
if (firstPageTextContent) {
|
|
1647
|
+
pdfCjkFontFallbackPageLoads.set(1, pdfCjkFontFallbackManager.ensureTextContent(firstPageTextContent));
|
|
1648
|
+
await pdfCjkFontFallbackPageLoads.get(1);
|
|
1649
|
+
}
|
|
1650
|
+
else {
|
|
1651
|
+
const firstPage = await pdfDocument.getPage(1);
|
|
1652
|
+
await ensurePdfPageCjkFontFallback(1, firstPage);
|
|
1653
|
+
}
|
|
1654
|
+
if (destroyed || requestVersion !== loadVersion || pdfContext.document !== pdfDocument) {
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
(_c = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _c === void 0 ? void 0 : _c.call(context, {
|
|
1355
1659
|
includeDocumentStyles: false,
|
|
1356
1660
|
printStyle: buildPdfPrintStyle,
|
|
1357
1661
|
toHtml: renderPdfPagesForExport,
|
|
@@ -1433,9 +1737,14 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
1433
1737
|
return {
|
|
1434
1738
|
$el: root,
|
|
1435
1739
|
unmount() {
|
|
1436
|
-
var _a;
|
|
1740
|
+
var _a, _b;
|
|
1437
1741
|
destroyed = true;
|
|
1438
1742
|
loadVersion += 1;
|
|
1743
|
+
restorePdfJsMissingSystemFontWarnings();
|
|
1744
|
+
restorePdfJsMissingSystemFontWarnings = () => { };
|
|
1745
|
+
pdfCjkFontFallbackManager = null;
|
|
1746
|
+
pdfCjkFontFallbackPageLoads.clear();
|
|
1747
|
+
pdfCjkFontFallbackRenderHandledPages.clear();
|
|
1439
1748
|
targetWindow.cancelAnimationFrame(fitFrame);
|
|
1440
1749
|
targetWindow.cancelAnimationFrame(pageDimensionFrame);
|
|
1441
1750
|
targetWindow.cancelAnimationFrame(scrollStateFrame);
|
|
@@ -1449,6 +1758,7 @@ export default async function renderPdf(buffer, target, context) {
|
|
|
1449
1758
|
unregisterFileViewerViewStateProvider(root);
|
|
1450
1759
|
outlineItems = [];
|
|
1451
1760
|
(_a = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _a === void 0 ? void 0 : _a.call(context, null);
|
|
1761
|
+
(_b = context === null || context === void 0 ? void 0 : context.registerThumbnailAdapter) === null || _b === void 0 ? void 0 : _b.call(context, null);
|
|
1452
1762
|
const resource = pdfContext.resource;
|
|
1453
1763
|
pdfContext.viewer = null;
|
|
1454
1764
|
pdfContext.linkService = null;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
type PdfTextContentItem = {
|
|
2
|
+
str?: string;
|
|
3
|
+
fontName?: string;
|
|
4
|
+
};
|
|
5
|
+
type PdfTextContentStyle = {
|
|
6
|
+
fontSubstitution?: string;
|
|
7
|
+
};
|
|
8
|
+
export type PdfTextContent = {
|
|
9
|
+
items?: PdfTextContentItem[];
|
|
10
|
+
styles?: Record<string, PdfTextContentStyle>;
|
|
11
|
+
};
|
|
12
|
+
export type PdfTextContentPage = {
|
|
13
|
+
getTextContent: () => Promise<PdfTextContent>;
|
|
14
|
+
};
|
|
15
|
+
export interface PdfCjkFontFallbackManager {
|
|
16
|
+
prepare: () => Promise<boolean>;
|
|
17
|
+
ensureTextContent: (textContent: PdfTextContent) => Promise<boolean>;
|
|
18
|
+
ensurePage: (page: PdfTextContentPage) => Promise<boolean>;
|
|
19
|
+
}
|
|
20
|
+
export interface CreatePdfCjkFontFallbackManagerOptions {
|
|
21
|
+
documentRef: Document;
|
|
22
|
+
fontAssetPath: string;
|
|
23
|
+
onWarning?: (message: string, error?: unknown) => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Some PDF generators write TrueType glyph IDs through Identity-H but omit
|
|
27
|
+
* ToUnicode. PDF.js then exposes those glyph IDs as control characters, so a
|
|
28
|
+
* replacement font alone cannot recover the intended text.
|
|
29
|
+
*/
|
|
30
|
+
export declare const detectMalformedIdentityCjkFontFamilies: (textContent: PdfTextContent, resolveFontFamily?: (fontName: string) => string) => string[];
|
|
31
|
+
export declare const collectMalformedIdentityFontNames: (textContent: PdfTextContent) => string[];
|
|
32
|
+
export declare const createPdfCjkFontFallbackManager: ({ documentRef, fontAssetPath, onWarning, }: CreatePdfCjkFontFallbackManagerOptions) => PdfCjkFontFallbackManager;
|
|
33
|
+
export {};
|