@file-viewer/core 2.0.11 → 2.1.1
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 +2 -2
- package/README.md +2 -2
- package/dist/config/options.d.ts +1 -1
- package/dist/config/options.js +1 -1
- package/dist/contracts/types.d.ts +76 -1
- package/dist/headless.d.ts +3 -3
- package/dist/headless.js +2 -2
- package/dist/index.d.ts +10 -7
- package/dist/index.js +106 -49
- package/dist/lifecycle/operations.d.ts +1 -0
- package/dist/lifecycle/operations.js +65 -6
- package/dist/platform/assets.d.ts +3 -1
- package/dist/platform/assets.js +43 -6
- package/dist/registry/capabilities.d.ts +2 -2
- package/dist/registry/capabilities.js +2 -1
- package/dist/registry/formats.d.ts +20 -7
- package/dist/registry/formats.js +14 -5
- package/dist/registry/registry.d.ts +8 -1
- package/dist/registry/registry.js +29 -0
- package/dist/renderers/image.js +1 -10
- package/dist/renderers/index.d.ts +320 -2
- package/dist/renderers/index.js +27 -157
- package/dist/viewer/createViewer.js +86 -3
- package/package.json +17 -44
- package/dist/renderers/archive.d.ts +0 -2
- package/dist/renderers/archive.js +0 -547
- package/dist/renderers/archiveCache.d.ts +0 -10
- package/dist/renderers/archiveCache.js +0 -96
- package/dist/renderers/archiveFallback.d.ts +0 -7
- package/dist/renderers/archiveFallback.js +0 -166
- package/dist/renderers/archiveShared.d.ts +0 -23
- package/dist/renderers/archiveShared.js +0 -71
- package/dist/renderers/audio.d.ts +0 -8
- package/dist/renderers/audio.js +0 -219
- package/dist/renderers/cad.d.ts +0 -2
- package/dist/renderers/cad.js +0 -446
- package/dist/renderers/code.d.ts +0 -11
- package/dist/renderers/code.js +0 -233
- package/dist/renderers/data.d.ts +0 -7
- package/dist/renderers/data.js +0 -370
- package/dist/renderers/drawing.d.ts +0 -10
- package/dist/renderers/drawing.js +0 -882
- package/dist/renderers/eda.d.ts +0 -2
- package/dist/renderers/eda.js +0 -434
- package/dist/renderers/edaParser.d.ts +0 -77
- package/dist/renderers/edaParser.js +0 -569
- package/dist/renderers/email.d.ts +0 -2
- package/dist/renderers/email.js +0 -463
- package/dist/renderers/epub.d.ts +0 -2
- package/dist/renderers/epub.js +0 -331
- package/dist/renderers/geo.d.ts +0 -2
- package/dist/renderers/geo.js +0 -284
- package/dist/renderers/markdown.d.ts +0 -2
- package/dist/renderers/markdown.js +0 -83
- package/dist/renderers/model.d.ts +0 -2
- package/dist/renderers/model.js +0 -567
- package/dist/renderers/ofd.d.ts +0 -2
- package/dist/renderers/ofd.js +0 -256
- package/dist/renderers/openDocument.d.ts +0 -2
- package/dist/renderers/openDocument.js +0 -122
- package/dist/renderers/pdf.d.ts +0 -3
- package/dist/renderers/pdf.js +0 -1001
- package/dist/renderers/pdfStyles.d.ts +0 -1
- package/dist/renderers/pdfStyles.js +0 -1
- package/dist/renderers/pptx.d.ts +0 -2
- package/dist/renderers/pptx.js +0 -217
- package/dist/renderers/spreadsheet/state.d.ts +0 -80
- package/dist/renderers/spreadsheet/state.js +0 -96
- package/dist/renderers/spreadsheet/view.d.ts +0 -25
- package/dist/renderers/spreadsheet/view.js +0 -833
- package/dist/renderers/spreadsheet/worker/index.d.ts +0 -2
- package/dist/renderers/spreadsheet/worker/index.js +0 -1
- package/dist/renderers/spreadsheet/worker/sheetjs/SheetJsModel.d.ts +0 -73
- package/dist/renderers/spreadsheet/worker/sheetjs/SheetJsModel.js +0 -623
- package/dist/renderers/spreadsheet/worker/sheetjs/color.d.ts +0 -2
- package/dist/renderers/spreadsheet/worker/sheetjs/color.js +0 -73
- package/dist/renderers/spreadsheet/worker/sheetjs/index.d.ts +0 -1
- package/dist/renderers/spreadsheet/worker/sheetjs/index.js +0 -1
- package/dist/renderers/spreadsheet/worker/sheetjs/parser.d.ts +0 -18
- package/dist/renderers/spreadsheet/worker/sheetjs/parser.js +0 -106
- package/dist/renderers/spreadsheet/worker/sheetjs/sheet.worker.d.ts +0 -1
- package/dist/renderers/spreadsheet/worker/sheetjs/sheet.worker.js +0 -11
- package/dist/renderers/spreadsheet/worker/type.d.ts +0 -57
- package/dist/renderers/spreadsheet/worker/type.js +0 -1
- package/dist/renderers/spreadsheet.d.ts +0 -3
- package/dist/renderers/spreadsheet.js +0 -929
- package/dist/renderers/typst.d.ts +0 -8
- package/dist/renderers/typst.js +0 -547
- package/dist/renderers/umd/parser.d.ts +0 -30
- package/dist/renderers/umd/parser.js +0 -408
- package/dist/renderers/umd.d.ts +0 -2
- package/dist/renderers/umd.js +0 -297
- package/dist/renderers/video.d.ts +0 -8
- package/dist/renderers/video.js +0 -108
- package/dist/renderers/wordDoc.d.ts +0 -5
- package/dist/renderers/wordDoc.js +0 -284
- package/dist/renderers/wordDocx.d.ts +0 -5
- package/dist/renderers/wordDocx.js +0 -985
- package/dist/renderers/wordDocx.worker.d.ts +0 -1
- package/dist/renderers/wordDocx.worker.js +0 -96
- package/vendor/ofd/dltech/jbig2/arithmetic_decoder.js +0 -183
- package/vendor/ofd/dltech/jbig2/ccitt.js +0 -1070
- package/vendor/ofd/dltech/jbig2/compatibility.js +0 -12
- package/vendor/ofd/dltech/jbig2/core_utils.js +0 -180
- package/vendor/ofd/dltech/jbig2/is_node.js +0 -27
- package/vendor/ofd/dltech/jbig2/jbig2.js +0 -2589
- package/vendor/ofd/dltech/jbig2/jbig2_stream.js +0 -81
- package/vendor/ofd/dltech/jbig2/primitives.js +0 -387
- package/vendor/ofd/dltech/jbig2/stream.js +0 -1348
- package/vendor/ofd/dltech/jbig2/util.js +0 -972
- package/vendor/ofd/dltech/ofd/ofd.d.ts +0 -11
- package/vendor/ofd/dltech/ofd/ofd.js +0 -100
- package/vendor/ofd/dltech/ofd/ofd_parser.js +0 -395
- package/vendor/ofd/dltech/ofd/ofd_render.js +0 -473
- package/vendor/ofd/dltech/ofd/ofd_util.js +0 -350
- package/vendor/ofd/dltech/ofd/pipeline.js +0 -26
package/dist/renderers/pdf.js
DELETED
|
@@ -1,1001 +0,0 @@
|
|
|
1
|
-
import { getDocument, GlobalWorkerOptions, PDFWorker as PdfJsWorker, PixelsPerInch, } from 'pdfjs-dist/legacy/build/pdf.mjs';
|
|
2
|
-
import { EventBus, GenericL10n, PDFFindController, PDFLinkService, PDFViewer, } from 'pdfjs-dist/legacy/web/pdf_viewer.mjs';
|
|
3
|
-
import { registerFileViewerSearchProvider, registerFileViewerZoomProvider, unregisterFileViewerSearchProvider, unregisterFileViewerZoomProvider, } from '../features/document/dom/index.js';
|
|
4
|
-
import { createFileViewerZoomChangeEmitter } from '../features/document/zoom.js';
|
|
5
|
-
import { buildPrintPageStyle, formatCssPixels, } from '../output/printLayout.js';
|
|
6
|
-
import { DEFAULT_FILE_VIEWER_PDF_WORKER_PATH, resolveFileViewerPdfAssetUrls, } from '../platform/assets.js';
|
|
7
|
-
import { DEFAULT_PDF_RANGE_CHUNK_SIZE } from '../source/loading.js';
|
|
8
|
-
import { pdfViewerStyle } from './pdfStyles.js';
|
|
9
|
-
export const DEFAULT_FILE_VIEWER_PDF_WORKER_URL = DEFAULT_FILE_VIEWER_PDF_WORKER_PATH;
|
|
10
|
-
const MIN_SCALE = 0.2;
|
|
11
|
-
const MAX_SCALE = 3;
|
|
12
|
-
const SCALE_STEP = 0.1;
|
|
13
|
-
const FIT_HORIZONTAL_PADDING = 28;
|
|
14
|
-
const PAGE_BORDER_WIDTH = 18;
|
|
15
|
-
const PDF_EXPORT_MAX_PAGE_PIXELS = 8000000;
|
|
16
|
-
// PDF.js viewer CSS references image assets that are not shipped with the
|
|
17
|
-
// on-demand renderer chunk, so keep the preview self-contained and 404-free.
|
|
18
|
-
const normalizedPdfViewerStyle = pdfViewerStyle
|
|
19
|
-
.replace(/--page-border-image:\s*url\(images\/shadow\.png\)\s*9 9 repeat;/g, '--page-border-image:none;')
|
|
20
|
-
.replace(/background:\s*url\("\.\/images\/loading-icon\.gif"\)\s*center no-repeat;/g, 'background:none;');
|
|
21
|
-
const createStyle = (documentRef) => {
|
|
22
|
-
const style = documentRef.createElement('style');
|
|
23
|
-
style.textContent = `${normalizedPdfViewerStyle}
|
|
24
|
-
.pdf-state[hidden],.pdf-nav-pane[hidden]{display:none!important}
|
|
25
|
-
.pdf-page-button--with-thumbnail{grid-template-columns:52px minmax(0,1fr);min-height:74px}
|
|
26
|
-
.pdf-page-thumb--thumbnail{width:46px;height:60px;overflow:hidden;background:#fff}
|
|
27
|
-
.pdf-page-thumb--thumbnail img{display:block;width:100%;height:100%;object-fit:contain}
|
|
28
|
-
.pdf-page-thumb--thumbnail span{display:inline-flex;align-items:center;justify-content:center;width:100%;height:100%}
|
|
29
|
-
`;
|
|
30
|
-
return style;
|
|
31
|
-
};
|
|
32
|
-
const createElement = (documentRef, tagName, className, text) => {
|
|
33
|
-
const element = documentRef.createElement(tagName);
|
|
34
|
-
if (className) {
|
|
35
|
-
element.className = className;
|
|
36
|
-
}
|
|
37
|
-
if (text !== undefined) {
|
|
38
|
-
element.textContent = text;
|
|
39
|
-
}
|
|
40
|
-
return element;
|
|
41
|
-
};
|
|
42
|
-
const createButton = (documentRef, className, title, label) => {
|
|
43
|
-
const button = createElement(documentRef, 'button', className);
|
|
44
|
-
button.type = 'button';
|
|
45
|
-
button.title = title;
|
|
46
|
-
button.setAttribute('aria-label', title);
|
|
47
|
-
if (label !== undefined) {
|
|
48
|
-
const labelNode = createElement(documentRef, 'span', undefined, label);
|
|
49
|
-
labelNode.setAttribute('aria-hidden', 'true');
|
|
50
|
-
button.append(labelNode);
|
|
51
|
-
}
|
|
52
|
-
return button;
|
|
53
|
-
};
|
|
54
|
-
const normalizeRotation = (rotation) => {
|
|
55
|
-
const normalized = ((Math.round(rotation / 90) * 90) % 360 + 360) % 360;
|
|
56
|
-
return (normalized === 90 || normalized === 180 || normalized === 270 ? normalized : 0);
|
|
57
|
-
};
|
|
58
|
-
const clampScale = (scale) => Number(Math.min(MAX_SCALE, Math.max(MIN_SCALE, scale)).toFixed(2));
|
|
59
|
-
const createPdfSearchState = (query = '') => ({
|
|
60
|
-
query,
|
|
61
|
-
total: 0,
|
|
62
|
-
currentIndex: -1,
|
|
63
|
-
current: null,
|
|
64
|
-
matches: [],
|
|
65
|
-
});
|
|
66
|
-
const escapeAttribute = (value) => value
|
|
67
|
-
.replace(/&/g, '&')
|
|
68
|
-
.replace(/"/g, '"')
|
|
69
|
-
.replace(/</g, '<')
|
|
70
|
-
.replace(/>/g, '>');
|
|
71
|
-
const waitForPaint = (view) => new Promise(resolve => {
|
|
72
|
-
if (view === null || view === void 0 ? void 0 : view.requestAnimationFrame) {
|
|
73
|
-
view.requestAnimationFrame(() => resolve());
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
globalThis.setTimeout(resolve, 0);
|
|
77
|
-
});
|
|
78
|
-
const resolvePdfWorkerUrl = (options, documentUrl) => {
|
|
79
|
-
return resolveFileViewerPdfAssetUrls(options, documentUrl).workerUrl;
|
|
80
|
-
};
|
|
81
|
-
const buildOutlineItems = (items, prefix = 'outline') => items.map((item, index) => {
|
|
82
|
-
const id = `${prefix}-${index}`;
|
|
83
|
-
const children = Array.isArray(item.items)
|
|
84
|
-
? buildOutlineItems(item.items, id)
|
|
85
|
-
: [];
|
|
86
|
-
return {
|
|
87
|
-
id,
|
|
88
|
-
title: item.title || `目录 ${index + 1}`,
|
|
89
|
-
dest: item.dest || null,
|
|
90
|
-
items: children,
|
|
91
|
-
expanded: index < 4,
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
export default async function renderPdf(buffer, target, context) {
|
|
95
|
-
var _a, _b;
|
|
96
|
-
const documentRef = target.ownerDocument || document;
|
|
97
|
-
const targetWindow = documentRef.defaultView || (typeof window !== 'undefined' ? window : null);
|
|
98
|
-
if (!targetWindow) {
|
|
99
|
-
throw new Error('PDF preview requires a browser window');
|
|
100
|
-
}
|
|
101
|
-
const options = (_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.pdf;
|
|
102
|
-
const navigationEnabled = (options === null || options === void 0 ? void 0 : options.navigation) !== false;
|
|
103
|
-
const toolbarVisible = (options === null || options === void 0 ? void 0 : options.toolbar) !== false;
|
|
104
|
-
const thumbnailsEnabled = (options === null || options === void 0 ? void 0 : options.thumbnails) === true;
|
|
105
|
-
const zoomEmitter = createFileViewerZoomChangeEmitter();
|
|
106
|
-
let navVisible = (options === null || options === void 0 ? void 0 : options.navigation) === false ? false : (options === null || options === void 0 ? void 0 : options.defaultNavigationVisible) !== false;
|
|
107
|
-
let navMode = 'pages';
|
|
108
|
-
let loadStatus = 'loading';
|
|
109
|
-
let errorMessage = '';
|
|
110
|
-
let currentPage = 1;
|
|
111
|
-
let pageCount = 0;
|
|
112
|
-
let currentScale = 1;
|
|
113
|
-
let autoFitWidth = true;
|
|
114
|
-
let currentRotation = normalizeRotation((_b = options === null || options === void 0 ? void 0 : options.rotation) !== null && _b !== void 0 ? _b : 0);
|
|
115
|
-
let outlineItems = [];
|
|
116
|
-
let resizeObserver = null;
|
|
117
|
-
let thumbnailObserver = null;
|
|
118
|
-
let fitFrame = 0;
|
|
119
|
-
let pageDimensionFrame = 0;
|
|
120
|
-
let destroyed = false;
|
|
121
|
-
let loadVersion = 0;
|
|
122
|
-
let pdfSearchState = createPdfSearchState();
|
|
123
|
-
let pdfMatchesCount = { current: 0, total: 0 };
|
|
124
|
-
let pdfSearchOptions;
|
|
125
|
-
let pdfSearchWaiters = [];
|
|
126
|
-
const pdfThumbnails = new Map();
|
|
127
|
-
const pendingPdfThumbnails = new Set();
|
|
128
|
-
const pdfContext = {
|
|
129
|
-
viewer: null,
|
|
130
|
-
linkService: null,
|
|
131
|
-
eventBus: null,
|
|
132
|
-
findController: null,
|
|
133
|
-
resource: null,
|
|
134
|
-
document: null,
|
|
135
|
-
search: '',
|
|
136
|
-
};
|
|
137
|
-
const root = createElement(documentRef, 'div', 'pdf-shell');
|
|
138
|
-
root.dataset.viewerSearchProvider = 'pdf';
|
|
139
|
-
root.dataset.viewerZoomProvider = 'pdf';
|
|
140
|
-
const toolbar = createElement(documentRef, 'div', 'pdf-toolbar');
|
|
141
|
-
const navToggleButton = createButton(documentRef, 'pdf-icon-button', '切换导航窗格');
|
|
142
|
-
navToggleButton.setAttribute('aria-pressed', String(navVisible));
|
|
143
|
-
navToggleButton.append(createElement(documentRef, 'span', 'pdf-panel-icon'));
|
|
144
|
-
const pageGroup = createElement(documentRef, 'div', 'pdf-toolbar-group');
|
|
145
|
-
const previousPageButton = createButton(documentRef, 'pdf-icon-button', '上一页', '‹');
|
|
146
|
-
const pageMeter = createElement(documentRef, 'span', 'pdf-page-meter');
|
|
147
|
-
const pageMeterCurrent = createElement(documentRef, 'strong', undefined, '1');
|
|
148
|
-
const pageMeterTotal = createElement(documentRef, 'span', undefined, '/ -');
|
|
149
|
-
pageMeter.append(pageMeterCurrent, pageMeterTotal);
|
|
150
|
-
const nextPageButton = createButton(documentRef, 'pdf-icon-button', '下一页', '›');
|
|
151
|
-
pageGroup.append(previousPageButton, pageMeter, nextPageButton);
|
|
152
|
-
const zoomGroup = createElement(documentRef, 'div', 'pdf-toolbar-group pdf-toolbar-group--zoom');
|
|
153
|
-
const zoomOutButton = createButton(documentRef, 'pdf-icon-button', '缩小', '−');
|
|
154
|
-
const scaleButton = createElement(documentRef, 'button', 'pdf-scale-button', '100%');
|
|
155
|
-
scaleButton.type = 'button';
|
|
156
|
-
scaleButton.title = '适合宽度';
|
|
157
|
-
const zoomInButton = createButton(documentRef, 'pdf-icon-button', '放大', '+');
|
|
158
|
-
zoomGroup.append(zoomOutButton, scaleButton, zoomInButton);
|
|
159
|
-
const rotateGroup = createElement(documentRef, 'div', 'pdf-toolbar-group pdf-toolbar-group--rotate');
|
|
160
|
-
const rotateLeftButton = createButton(documentRef, 'pdf-icon-button', '向左旋转', '↺');
|
|
161
|
-
const rotationMeter = createElement(documentRef, 'span', 'pdf-rotation-meter', `${currentRotation}°`);
|
|
162
|
-
const rotateRightButton = createButton(documentRef, 'pdf-icon-button', '向右旋转', '↻');
|
|
163
|
-
rotateGroup.append(rotateLeftButton, rotationMeter, rotateRightButton);
|
|
164
|
-
if (navigationEnabled) {
|
|
165
|
-
toolbar.append(navToggleButton);
|
|
166
|
-
}
|
|
167
|
-
toolbar.append(pageGroup, zoomGroup, rotateGroup);
|
|
168
|
-
const content = createElement(documentRef, 'div', 'pdf-content');
|
|
169
|
-
const navPane = createElement(documentRef, 'aside', 'pdf-nav-pane');
|
|
170
|
-
const navHead = createElement(documentRef, 'div', 'pdf-nav-head');
|
|
171
|
-
const navTitle = createElement(documentRef, 'span', undefined, '页面导航');
|
|
172
|
-
const navCount = createElement(documentRef, 'strong', undefined, '0 页');
|
|
173
|
-
navHead.append(navTitle, navCount);
|
|
174
|
-
const navTabs = createElement(documentRef, 'div', 'pdf-nav-tabs');
|
|
175
|
-
navTabs.setAttribute('role', 'tablist');
|
|
176
|
-
navTabs.setAttribute('aria-label', 'PDF 导航类型');
|
|
177
|
-
const pagesTab = createButton(documentRef, '', '页面');
|
|
178
|
-
const outlineTab = createButton(documentRef, '', '目录');
|
|
179
|
-
pagesTab.textContent = '页面';
|
|
180
|
-
outlineTab.textContent = '目录';
|
|
181
|
-
pagesTab.setAttribute('role', 'tab');
|
|
182
|
-
outlineTab.setAttribute('role', 'tab');
|
|
183
|
-
navTabs.append(pagesTab, outlineTab);
|
|
184
|
-
const navList = createElement(documentRef, 'div');
|
|
185
|
-
navPane.append(navHead, navTabs, navList);
|
|
186
|
-
const viewport = createElement(documentRef, 'div', 'pdf-viewport');
|
|
187
|
-
const container = createElement(documentRef, 'div', 'pdf-wrapper');
|
|
188
|
-
container.dataset.viewerScrollContainer = 'true';
|
|
189
|
-
const pdfViewerRoot = createElement(documentRef, 'div', 'pdfViewer');
|
|
190
|
-
const stateNode = createElement(documentRef, 'div', 'pdf-state', '正在加载 PDF...');
|
|
191
|
-
container.append(pdfViewerRoot, stateNode);
|
|
192
|
-
viewport.append(container);
|
|
193
|
-
content.append(navPane, viewport);
|
|
194
|
-
root.append(content);
|
|
195
|
-
if (toolbarVisible) {
|
|
196
|
-
root.insertBefore(toolbar, content);
|
|
197
|
-
}
|
|
198
|
-
target.replaceChildren(createStyle(documentRef), root);
|
|
199
|
-
const scaleText = () => `${Math.round(currentScale * 100)}%`;
|
|
200
|
-
const rotationText = () => `${currentRotation}°`;
|
|
201
|
-
const canGoPrevious = () => currentPage > 1;
|
|
202
|
-
const canGoNext = () => currentPage < pageCount;
|
|
203
|
-
const canZoomOut = () => currentScale > MIN_SCALE;
|
|
204
|
-
const canZoomIn = () => currentScale < MAX_SCALE;
|
|
205
|
-
const outlineCount = () => {
|
|
206
|
-
const countItems = (items) => (items.reduce((total, item) => total + 1 + countItems(item.items), 0));
|
|
207
|
-
return countItems(outlineItems);
|
|
208
|
-
};
|
|
209
|
-
const flattenedOutlineItems = () => {
|
|
210
|
-
const result = [];
|
|
211
|
-
const visit = (items, depth) => {
|
|
212
|
-
items.forEach(item => {
|
|
213
|
-
result.push({ item, depth });
|
|
214
|
-
if (item.expanded && item.items.length) {
|
|
215
|
-
visit(item.items, depth + 1);
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
};
|
|
219
|
-
visit(outlineItems, 0);
|
|
220
|
-
return result;
|
|
221
|
-
};
|
|
222
|
-
const renderNavList = () => {
|
|
223
|
-
navList.replaceChildren();
|
|
224
|
-
navList.className = navMode === 'pages' ? 'pdf-page-list' : 'pdf-outline-list';
|
|
225
|
-
if (navMode === 'pages') {
|
|
226
|
-
thumbnailObserver === null || thumbnailObserver === void 0 ? void 0 : thumbnailObserver.disconnect();
|
|
227
|
-
for (let page = 1; page <= pageCount; page += 1) {
|
|
228
|
-
const button = createElement(documentRef, 'button', 'pdf-page-button');
|
|
229
|
-
button.type = 'button';
|
|
230
|
-
button.classList.toggle('pdf-page-button--active', page === currentPage);
|
|
231
|
-
button.classList.toggle('pdf-page-button--with-thumbnail', thumbnailsEnabled);
|
|
232
|
-
const thumb = createElement(documentRef, 'span', 'pdf-page-thumb');
|
|
233
|
-
if (thumbnailsEnabled) {
|
|
234
|
-
thumb.classList.add('pdf-page-thumb--thumbnail');
|
|
235
|
-
queuePdfThumbnail(page, thumb);
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
thumb.textContent = String(page);
|
|
239
|
-
}
|
|
240
|
-
button.append(thumb, createElement(documentRef, 'span', 'pdf-page-label', `第 ${page} 页`));
|
|
241
|
-
button.addEventListener('click', () => goToPage(page));
|
|
242
|
-
navList.append(button);
|
|
243
|
-
}
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
const entries = flattenedOutlineItems();
|
|
247
|
-
entries.forEach(entry => {
|
|
248
|
-
const button = createElement(documentRef, 'button', 'pdf-outline-button');
|
|
249
|
-
button.type = 'button';
|
|
250
|
-
button.style.setProperty('--outline-depth', String(entry.depth));
|
|
251
|
-
const toggle = createElement(documentRef, 'span', 'pdf-outline-toggle');
|
|
252
|
-
toggle.classList.toggle('pdf-outline-toggle--open', entry.item.expanded);
|
|
253
|
-
toggle.classList.toggle('pdf-outline-toggle--empty', !entry.item.items.length);
|
|
254
|
-
toggle.setAttribute('aria-hidden', 'true');
|
|
255
|
-
toggle.addEventListener('click', event => {
|
|
256
|
-
event.stopPropagation();
|
|
257
|
-
toggleOutlineItem(entry.item);
|
|
258
|
-
});
|
|
259
|
-
button.append(toggle, createElement(documentRef, 'span', 'pdf-outline-title', entry.item.title));
|
|
260
|
-
button.addEventListener('click', () => goToOutlineItem(entry.item));
|
|
261
|
-
navList.append(button);
|
|
262
|
-
});
|
|
263
|
-
if (!entries.length) {
|
|
264
|
-
navList.append(createElement(documentRef, 'div', 'pdf-outline-empty', '当前 PDF 没有可用目录'));
|
|
265
|
-
}
|
|
266
|
-
};
|
|
267
|
-
const paintPdfThumbnail = (pageNumber, thumb) => {
|
|
268
|
-
const imageUrl = pdfThumbnails.get(pageNumber);
|
|
269
|
-
thumb.dataset.pdfThumbnailPage = String(pageNumber);
|
|
270
|
-
if (!imageUrl) {
|
|
271
|
-
thumb.replaceChildren(createElement(documentRef, 'span', undefined, String(pageNumber)));
|
|
272
|
-
return false;
|
|
273
|
-
}
|
|
274
|
-
const image = documentRef.createElement('img');
|
|
275
|
-
image.src = imageUrl;
|
|
276
|
-
image.alt = `第 ${pageNumber} 页缩略图`;
|
|
277
|
-
image.loading = 'lazy';
|
|
278
|
-
thumb.replaceChildren(image);
|
|
279
|
-
return true;
|
|
280
|
-
};
|
|
281
|
-
const renderPdfThumbnail = async (pageNumber) => {
|
|
282
|
-
var _a, _b;
|
|
283
|
-
const pdfDocument = pdfContext.document;
|
|
284
|
-
if (!pdfDocument || pdfThumbnails.has(pageNumber) || pendingPdfThumbnails.has(pageNumber)) {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
pendingPdfThumbnails.add(pageNumber);
|
|
288
|
-
try {
|
|
289
|
-
const page = await pdfDocument.getPage(pageNumber);
|
|
290
|
-
if (destroyed || pdfContext.document !== pdfDocument) {
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
const baseViewport = page.getViewport({
|
|
294
|
-
scale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
|
295
|
-
rotation: currentRotation,
|
|
296
|
-
});
|
|
297
|
-
const deviceScale = Math.min(2, Math.max(1, targetWindow.devicePixelRatio || 1));
|
|
298
|
-
const thumbnailWidth = 46;
|
|
299
|
-
const ratio = Math.min(1, thumbnailWidth / Math.max(baseViewport.width, 1));
|
|
300
|
-
const renderViewport = page.getViewport({
|
|
301
|
-
scale: PixelsPerInch.PDF_TO_CSS_UNITS * ratio * deviceScale,
|
|
302
|
-
rotation: currentRotation,
|
|
303
|
-
});
|
|
304
|
-
const canvas = documentRef.createElement('canvas');
|
|
305
|
-
const canvasContext = canvas.getContext('2d');
|
|
306
|
-
if (!canvasContext) {
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
canvas.width = Math.max(1, Math.ceil(renderViewport.width));
|
|
310
|
-
canvas.height = Math.max(1, Math.ceil(renderViewport.height));
|
|
311
|
-
await page.render({ canvas, canvasContext, viewport: renderViewport }).promise;
|
|
312
|
-
if (destroyed || pdfContext.document !== pdfDocument) {
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
pdfThumbnails.set(pageNumber, canvas.toDataURL('image/png'));
|
|
316
|
-
canvas.width = 0;
|
|
317
|
-
canvas.height = 0;
|
|
318
|
-
(_b = (_a = page).cleanup) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
319
|
-
navList
|
|
320
|
-
.querySelectorAll(`.pdf-page-thumb--thumbnail[data-pdf-thumbnail-page="${pageNumber}"]`)
|
|
321
|
-
.forEach(thumb => paintPdfThumbnail(pageNumber, thumb));
|
|
322
|
-
}
|
|
323
|
-
catch (error) {
|
|
324
|
-
console.warn('[file-viewer] PDF 缩略图渲染失败。', error);
|
|
325
|
-
}
|
|
326
|
-
finally {
|
|
327
|
-
pendingPdfThumbnails.delete(pageNumber);
|
|
328
|
-
}
|
|
329
|
-
};
|
|
330
|
-
const ensureThumbnailObserver = () => {
|
|
331
|
-
if (!thumbnailsEnabled || thumbnailObserver || typeof targetWindow.IntersectionObserver !== 'function') {
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
thumbnailObserver = new targetWindow.IntersectionObserver(entries => {
|
|
335
|
-
entries.forEach(entry => {
|
|
336
|
-
if (!entry.isIntersecting) {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
const targetElement = entry.target;
|
|
340
|
-
const pageNumber = Number(targetElement.dataset.pdfThumbnailPage || '0');
|
|
341
|
-
thumbnailObserver === null || thumbnailObserver === void 0 ? void 0 : thumbnailObserver.unobserve(targetElement);
|
|
342
|
-
if (pageNumber > 0) {
|
|
343
|
-
void renderPdfThumbnail(pageNumber);
|
|
344
|
-
}
|
|
345
|
-
});
|
|
346
|
-
}, {
|
|
347
|
-
root: navList,
|
|
348
|
-
rootMargin: '96px 0px',
|
|
349
|
-
});
|
|
350
|
-
};
|
|
351
|
-
const queuePdfThumbnail = (pageNumber, thumb) => {
|
|
352
|
-
if (paintPdfThumbnail(pageNumber, thumb)) {
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
ensureThumbnailObserver();
|
|
356
|
-
if (thumbnailObserver) {
|
|
357
|
-
thumbnailObserver.observe(thumb);
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
void renderPdfThumbnail(pageNumber);
|
|
361
|
-
};
|
|
362
|
-
const syncUi = () => {
|
|
363
|
-
root.classList.toggle('pdf-shell--nav-hidden', !navigationEnabled || !navVisible);
|
|
364
|
-
root.classList.toggle('pdf-shell--toolbar-hidden', !toolbarVisible);
|
|
365
|
-
navToggleButton.classList.toggle('pdf-icon-button--active', navVisible);
|
|
366
|
-
navToggleButton.setAttribute('aria-pressed', String(navVisible));
|
|
367
|
-
navPane.hidden = !navigationEnabled || !navVisible;
|
|
368
|
-
pagesTab.classList.toggle('active', navMode === 'pages');
|
|
369
|
-
outlineTab.classList.toggle('active', navMode === 'outline');
|
|
370
|
-
pagesTab.setAttribute('aria-selected', navMode === 'pages' ? 'true' : 'false');
|
|
371
|
-
outlineTab.setAttribute('aria-selected', navMode === 'outline' ? 'true' : 'false');
|
|
372
|
-
navTitle.textContent = navMode === 'pages' ? '页面导航' : '目录导航';
|
|
373
|
-
navCount.textContent = navMode === 'pages' ? `${pageCount} 页` : `${outlineCount()} 项`;
|
|
374
|
-
pageMeterCurrent.textContent = String(currentPage);
|
|
375
|
-
pageMeterTotal.textContent = `/ ${pageCount || '-'}`;
|
|
376
|
-
scaleButton.textContent = scaleText();
|
|
377
|
-
rotationMeter.textContent = rotationText();
|
|
378
|
-
previousPageButton.disabled = !canGoPrevious();
|
|
379
|
-
nextPageButton.disabled = !canGoNext();
|
|
380
|
-
zoomOutButton.disabled = !canZoomOut();
|
|
381
|
-
zoomInButton.disabled = !canZoomIn();
|
|
382
|
-
stateNode.hidden = loadStatus === 'ready';
|
|
383
|
-
stateNode.classList.toggle('pdf-state--error', loadStatus === 'error');
|
|
384
|
-
stateNode.textContent = loadStatus === 'error' ? errorMessage : '正在加载 PDF...';
|
|
385
|
-
renderNavList();
|
|
386
|
-
};
|
|
387
|
-
const writeLegacyCompatiblePageDimensions = () => {
|
|
388
|
-
var _a, _b;
|
|
389
|
-
const pdfViewer = pdfContext.viewer;
|
|
390
|
-
if (!pdfViewer) {
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
const totalPages = pageCount || pdfViewer.pagesCount || 0;
|
|
394
|
-
for (let index = 0; index < totalPages; index += 1) {
|
|
395
|
-
const pageView = pdfViewer.getPageView(index);
|
|
396
|
-
const pageElement = (pageView === null || pageView === void 0 ? void 0 : pageView.div) ||
|
|
397
|
-
pdfViewerRoot.querySelector(`.page[data-page-number="${index + 1}"]`);
|
|
398
|
-
const width = (_a = pageView === null || pageView === void 0 ? void 0 : pageView.viewport) === null || _a === void 0 ? void 0 : _a.width;
|
|
399
|
-
const height = (_b = pageView === null || pageView === void 0 ? void 0 : pageView.viewport) === null || _b === void 0 ? void 0 : _b.height;
|
|
400
|
-
if (!pageElement || !Number.isFinite(width) || !Number.isFinite(height)) {
|
|
401
|
-
continue;
|
|
402
|
-
}
|
|
403
|
-
pageElement.style.setProperty('width', `${Math.max(1, Math.round(width || 0))}px`, 'important');
|
|
404
|
-
pageElement.style.setProperty('height', `${Math.max(1, Math.round(height || 0))}px`, 'important');
|
|
405
|
-
}
|
|
406
|
-
};
|
|
407
|
-
const scheduleLegacyPageDimensionPatch = () => {
|
|
408
|
-
targetWindow.cancelAnimationFrame(pageDimensionFrame);
|
|
409
|
-
pageDimensionFrame = targetWindow.requestAnimationFrame(() => {
|
|
410
|
-
writeLegacyCompatiblePageDimensions();
|
|
411
|
-
targetWindow.requestAnimationFrame(writeLegacyCompatiblePageDimensions);
|
|
412
|
-
});
|
|
413
|
-
};
|
|
414
|
-
const createPdfWorker = () => {
|
|
415
|
-
if (!(targetWindow === null || targetWindow === void 0 ? void 0 : targetWindow.Worker)) {
|
|
416
|
-
return null;
|
|
417
|
-
}
|
|
418
|
-
GlobalWorkerOptions.workerSrc = resolvePdfWorkerUrl(options, documentRef.URL || undefined);
|
|
419
|
-
try {
|
|
420
|
-
return PdfJsWorker.create({ name: 'file-viewer-pdf-worker' });
|
|
421
|
-
}
|
|
422
|
-
catch (error) {
|
|
423
|
-
console.warn('[file-viewer] PDF Worker 无法创建,回退到 PDF.js 默认加载策略。', error);
|
|
424
|
-
return null;
|
|
425
|
-
}
|
|
426
|
-
};
|
|
427
|
-
const resolvePdfSearchWaiters = (state) => {
|
|
428
|
-
const waiters = pdfSearchWaiters;
|
|
429
|
-
pdfSearchWaiters = [];
|
|
430
|
-
waiters.forEach(waiter => {
|
|
431
|
-
targetWindow.clearTimeout(waiter.timer);
|
|
432
|
-
waiter.resolve(state);
|
|
433
|
-
});
|
|
434
|
-
};
|
|
435
|
-
const readPdfMatchesCount = () => {
|
|
436
|
-
var _a;
|
|
437
|
-
const findController = pdfContext.findController;
|
|
438
|
-
if (!findController) {
|
|
439
|
-
return { current: 0, total: 0 };
|
|
440
|
-
}
|
|
441
|
-
const pageMatches = findController.pageMatches || [];
|
|
442
|
-
const selected = findController.selected;
|
|
443
|
-
const total = pageMatches.reduce((sum, matches) => sum + ((matches === null || matches === void 0 ? void 0 : matches.length) || 0), 0);
|
|
444
|
-
let current = 0;
|
|
445
|
-
if (selected && selected.pageIdx >= 0 && selected.matchIdx >= 0 && total > 0) {
|
|
446
|
-
for (let index = 0; index < selected.pageIdx; index += 1) {
|
|
447
|
-
current += ((_a = pageMatches[index]) === null || _a === void 0 ? void 0 : _a.length) || 0;
|
|
448
|
-
}
|
|
449
|
-
current += selected.matchIdx + 1;
|
|
450
|
-
}
|
|
451
|
-
return { current, total };
|
|
452
|
-
};
|
|
453
|
-
const commitPdfSearchState = (matchesCount = readPdfMatchesCount(), query = pdfContext.search, shouldResolve = false) => {
|
|
454
|
-
var _a;
|
|
455
|
-
pdfMatchesCount = matchesCount;
|
|
456
|
-
const current = Math.max(0, matchesCount.current || 0);
|
|
457
|
-
const total = Math.max(0, matchesCount.total || 0);
|
|
458
|
-
const selected = (_a = pdfContext.findController) === null || _a === void 0 ? void 0 : _a.selected;
|
|
459
|
-
const page = selected && selected.pageIdx >= 0 ? selected.pageIdx + 1 : undefined;
|
|
460
|
-
pdfSearchState = {
|
|
461
|
-
query,
|
|
462
|
-
total,
|
|
463
|
-
currentIndex: current > 0 ? current - 1 : -1,
|
|
464
|
-
current: current > 0
|
|
465
|
-
? {
|
|
466
|
-
id: `pdf-search-match-${current}`,
|
|
467
|
-
index: current - 1,
|
|
468
|
-
text: query,
|
|
469
|
-
anchor: null,
|
|
470
|
-
page,
|
|
471
|
-
}
|
|
472
|
-
: null,
|
|
473
|
-
matches: [],
|
|
474
|
-
};
|
|
475
|
-
if (shouldResolve) {
|
|
476
|
-
resolvePdfSearchWaiters(pdfSearchState);
|
|
477
|
-
}
|
|
478
|
-
return pdfSearchState;
|
|
479
|
-
};
|
|
480
|
-
const waitForPdfSearchState = (query) => new Promise(resolve => {
|
|
481
|
-
const timer = targetWindow.setTimeout(() => {
|
|
482
|
-
const waiterIndex = pdfSearchWaiters.findIndex(waiter => waiter.resolve === resolve);
|
|
483
|
-
if (waiterIndex >= 0) {
|
|
484
|
-
pdfSearchWaiters.splice(waiterIndex, 1);
|
|
485
|
-
}
|
|
486
|
-
resolve(commitPdfSearchState(readPdfMatchesCount(), query));
|
|
487
|
-
}, 1200);
|
|
488
|
-
pdfSearchWaiters.push({ resolve, timer });
|
|
489
|
-
});
|
|
490
|
-
const handlePdfFindMatchesCount = (event) => {
|
|
491
|
-
if (event.matchesCount) {
|
|
492
|
-
commitPdfSearchState(event.matchesCount, pdfContext.search);
|
|
493
|
-
}
|
|
494
|
-
};
|
|
495
|
-
const handlePdfFindControlState = (event) => {
|
|
496
|
-
var _a;
|
|
497
|
-
const query = typeof event.rawQuery === 'string' ? event.rawQuery : pdfContext.search;
|
|
498
|
-
pdfContext.search = query;
|
|
499
|
-
const matchesCount = ((_a = event.matchesCount) === null || _a === void 0 ? void 0 : _a.total) ? event.matchesCount : readPdfMatchesCount();
|
|
500
|
-
const shouldResolve = event.state !== 3 && (matchesCount.total > 0 || event.state === 1);
|
|
501
|
-
commitPdfSearchState(matchesCount, query, shouldResolve);
|
|
502
|
-
};
|
|
503
|
-
const clampHorizontalScroll = (scrollLeft) => {
|
|
504
|
-
const maxScrollLeft = Math.max(0, container.scrollWidth - container.clientWidth);
|
|
505
|
-
return Math.min(Math.max(0, scrollLeft), maxScrollLeft);
|
|
506
|
-
};
|
|
507
|
-
const restoreHorizontalScroll = (scrollLeft) => {
|
|
508
|
-
container.scrollLeft = clampHorizontalScroll(scrollLeft);
|
|
509
|
-
};
|
|
510
|
-
const stabilizeHorizontalScroll = (scrollLeft) => {
|
|
511
|
-
restoreHorizontalScroll(scrollLeft);
|
|
512
|
-
void waitForPaint(targetWindow).then(() => restoreHorizontalScroll(scrollLeft));
|
|
513
|
-
targetWindow.requestAnimationFrame(() => {
|
|
514
|
-
restoreHorizontalScroll(scrollLeft);
|
|
515
|
-
targetWindow.requestAnimationFrame(() => restoreHorizontalScroll(scrollLeft));
|
|
516
|
-
});
|
|
517
|
-
targetWindow.setTimeout(() => restoreHorizontalScroll(scrollLeft), 120);
|
|
518
|
-
};
|
|
519
|
-
const runPdfFind = async (query, searchOptionsInput, type, findPrevious = false) => {
|
|
520
|
-
if (!pdfContext.eventBus) {
|
|
521
|
-
return commitPdfSearchState({ current: 0, total: 0 }, query);
|
|
522
|
-
}
|
|
523
|
-
pdfContext.search = query;
|
|
524
|
-
pdfSearchOptions = searchOptionsInput || pdfSearchOptions;
|
|
525
|
-
const searchOptions = searchOptionsInput || pdfSearchOptions;
|
|
526
|
-
const previousScrollLeft = clampHorizontalScroll(container.scrollLeft || 0);
|
|
527
|
-
pdfContext.eventBus.dispatch('find', {
|
|
528
|
-
source: root,
|
|
529
|
-
type,
|
|
530
|
-
query,
|
|
531
|
-
phraseSearch: true,
|
|
532
|
-
caseSensitive: !!(searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.caseSensitive),
|
|
533
|
-
entireWord: !!(searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.wholeWord),
|
|
534
|
-
highlightAll: true,
|
|
535
|
-
findPrevious,
|
|
536
|
-
matchDiacritics: false,
|
|
537
|
-
});
|
|
538
|
-
try {
|
|
539
|
-
return await waitForPdfSearchState(query);
|
|
540
|
-
}
|
|
541
|
-
finally {
|
|
542
|
-
stabilizeHorizontalScroll(previousScrollLeft);
|
|
543
|
-
}
|
|
544
|
-
};
|
|
545
|
-
const clearPdfFind = () => {
|
|
546
|
-
var _a;
|
|
547
|
-
pdfContext.search = '';
|
|
548
|
-
pdfSearchOptions = undefined;
|
|
549
|
-
pdfMatchesCount = { current: 0, total: 0 };
|
|
550
|
-
(_a = pdfContext.eventBus) === null || _a === void 0 ? void 0 : _a.dispatch('findbarclose', {
|
|
551
|
-
source: root,
|
|
552
|
-
});
|
|
553
|
-
return commitPdfSearchState(pdfMatchesCount, '', true);
|
|
554
|
-
};
|
|
555
|
-
const getPdfZoomState = () => ({
|
|
556
|
-
scale: currentScale,
|
|
557
|
-
label: scaleText(),
|
|
558
|
-
canZoomIn: !!pdfContext.viewer && canZoomIn(),
|
|
559
|
-
canZoomOut: !!pdfContext.viewer && canZoomOut(),
|
|
560
|
-
canReset: !!pdfContext.viewer,
|
|
561
|
-
minScale: MIN_SCALE,
|
|
562
|
-
maxScale: MAX_SCALE,
|
|
563
|
-
});
|
|
564
|
-
const setScale = (scale) => {
|
|
565
|
-
if (!pdfContext.viewer) {
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
const normalizedScale = clampScale(scale);
|
|
569
|
-
pdfContext.viewer.currentScale = normalizedScale;
|
|
570
|
-
currentScale = normalizedScale;
|
|
571
|
-
scheduleLegacyPageDimensionPatch();
|
|
572
|
-
zoomEmitter.emit();
|
|
573
|
-
syncUi();
|
|
574
|
-
};
|
|
575
|
-
const getPageWidthAtScaleOne = (pdfViewer) => {
|
|
576
|
-
var _a;
|
|
577
|
-
const pageView = pdfViewer.getPageView(0);
|
|
578
|
-
const pdfPage = pageView === null || pageView === void 0 ? void 0 : pageView.pdfPage;
|
|
579
|
-
if (pdfPage) {
|
|
580
|
-
return pdfPage.getViewport({
|
|
581
|
-
scale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
|
582
|
-
rotation: currentRotation,
|
|
583
|
-
}).width;
|
|
584
|
-
}
|
|
585
|
-
const viewportWidth = (_a = pageView === null || pageView === void 0 ? void 0 : pageView.viewport) === null || _a === void 0 ? void 0 : _a.width;
|
|
586
|
-
if (viewportWidth && currentScale) {
|
|
587
|
-
return viewportWidth / currentScale;
|
|
588
|
-
}
|
|
589
|
-
return 0;
|
|
590
|
-
};
|
|
591
|
-
const getFitWidthScale = (pdfViewer) => {
|
|
592
|
-
const pageWidth = getPageWidthAtScaleOne(pdfViewer);
|
|
593
|
-
const containerWidth = container.clientWidth || targetWindow.innerWidth;
|
|
594
|
-
const availableWidth = Math.max(containerWidth - FIT_HORIZONTAL_PADDING - PAGE_BORDER_WIDTH, 96);
|
|
595
|
-
return pageWidth ? clampScale(availableWidth / pageWidth) : 1;
|
|
596
|
-
};
|
|
597
|
-
const fitToWidth = () => {
|
|
598
|
-
if (!pdfContext.viewer) {
|
|
599
|
-
return;
|
|
600
|
-
}
|
|
601
|
-
autoFitWidth = true;
|
|
602
|
-
setScale(getFitWidthScale(pdfContext.viewer));
|
|
603
|
-
void waitForPaint(targetWindow).then(() => {
|
|
604
|
-
var _a;
|
|
605
|
-
(_a = pdfContext.viewer) === null || _a === void 0 ? void 0 : _a.update();
|
|
606
|
-
});
|
|
607
|
-
};
|
|
608
|
-
const scheduleFitToWidth = () => {
|
|
609
|
-
if (!autoFitWidth || !pdfContext.viewer) {
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
targetWindow.cancelAnimationFrame(fitFrame);
|
|
613
|
-
fitFrame = targetWindow.requestAnimationFrame(() => fitToWidth());
|
|
614
|
-
};
|
|
615
|
-
const zoomIn = () => {
|
|
616
|
-
autoFitWidth = false;
|
|
617
|
-
setScale(currentScale + SCALE_STEP);
|
|
618
|
-
};
|
|
619
|
-
const zoomOut = () => {
|
|
620
|
-
autoFitWidth = false;
|
|
621
|
-
setScale(currentScale - SCALE_STEP);
|
|
622
|
-
};
|
|
623
|
-
const applyRotation = (rotation) => {
|
|
624
|
-
const normalized = normalizeRotation(rotation);
|
|
625
|
-
currentRotation = normalized;
|
|
626
|
-
pdfThumbnails.clear();
|
|
627
|
-
pendingPdfThumbnails.clear();
|
|
628
|
-
if (!pdfContext.viewer) {
|
|
629
|
-
syncUi();
|
|
630
|
-
return;
|
|
631
|
-
}
|
|
632
|
-
pdfContext.viewer.pagesRotation = normalized;
|
|
633
|
-
void waitForPaint(targetWindow).then(() => {
|
|
634
|
-
var _a;
|
|
635
|
-
if (autoFitWidth) {
|
|
636
|
-
fitToWidth();
|
|
637
|
-
return;
|
|
638
|
-
}
|
|
639
|
-
(_a = pdfContext.viewer) === null || _a === void 0 ? void 0 : _a.update();
|
|
640
|
-
scheduleLegacyPageDimensionPatch();
|
|
641
|
-
syncUi();
|
|
642
|
-
});
|
|
643
|
-
};
|
|
644
|
-
const runWithStableHorizontalScroll = (action) => {
|
|
645
|
-
const previousScrollLeft = clampHorizontalScroll(container.scrollLeft || 0);
|
|
646
|
-
const result = action();
|
|
647
|
-
stabilizeHorizontalScroll(previousScrollLeft);
|
|
648
|
-
if (result && typeof result.then === 'function') {
|
|
649
|
-
void Promise.resolve(result).finally(() => stabilizeHorizontalScroll(previousScrollLeft));
|
|
650
|
-
}
|
|
651
|
-
};
|
|
652
|
-
function goToPage(pageNumber) {
|
|
653
|
-
if (!pdfContext.viewer || !pageCount) {
|
|
654
|
-
return;
|
|
655
|
-
}
|
|
656
|
-
const nextPage = Math.min(pageCount, Math.max(1, pageNumber));
|
|
657
|
-
runWithStableHorizontalScroll(() => {
|
|
658
|
-
pdfContext.viewer.currentPageNumber = nextPage;
|
|
659
|
-
currentPage = nextPage;
|
|
660
|
-
syncUi();
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
const toggleNav = () => {
|
|
664
|
-
if (!navigationEnabled) {
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
navVisible = !navVisible;
|
|
668
|
-
syncUi();
|
|
669
|
-
void waitForPaint(targetWindow).then(() => {
|
|
670
|
-
var _a;
|
|
671
|
-
if (autoFitWidth) {
|
|
672
|
-
fitToWidth();
|
|
673
|
-
return;
|
|
674
|
-
}
|
|
675
|
-
(_a = pdfContext.viewer) === null || _a === void 0 ? void 0 : _a.update();
|
|
676
|
-
});
|
|
677
|
-
};
|
|
678
|
-
const setNavMode = (mode) => {
|
|
679
|
-
navMode = mode;
|
|
680
|
-
syncUi();
|
|
681
|
-
};
|
|
682
|
-
const toggleOutlineItem = (item) => {
|
|
683
|
-
if (!item.items.length) {
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
item.expanded = !item.expanded;
|
|
687
|
-
syncUi();
|
|
688
|
-
};
|
|
689
|
-
const goToOutlineItem = (item) => {
|
|
690
|
-
if (!item.dest || !pdfContext.linkService) {
|
|
691
|
-
return;
|
|
692
|
-
}
|
|
693
|
-
runWithStableHorizontalScroll(() => pdfContext.linkService.goToDestination(item.dest));
|
|
694
|
-
};
|
|
695
|
-
const destroyPdfResource = async (resource) => {
|
|
696
|
-
var _a;
|
|
697
|
-
if (!resource) {
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
try {
|
|
701
|
-
await resource.loadingTask.destroy();
|
|
702
|
-
}
|
|
703
|
-
catch (error) {
|
|
704
|
-
console.warn('PDF 加载任务销毁失败', error);
|
|
705
|
-
}
|
|
706
|
-
finally {
|
|
707
|
-
(_a = resource.worker) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
708
|
-
}
|
|
709
|
-
};
|
|
710
|
-
const loadOutline = async (pdfDocument) => {
|
|
711
|
-
try {
|
|
712
|
-
const outline = await pdfDocument.getOutline();
|
|
713
|
-
if (destroyed || pdfContext.document !== pdfDocument) {
|
|
714
|
-
return;
|
|
715
|
-
}
|
|
716
|
-
outlineItems = Array.isArray(outline)
|
|
717
|
-
? buildOutlineItems(outline)
|
|
718
|
-
: [];
|
|
719
|
-
syncUi();
|
|
720
|
-
}
|
|
721
|
-
catch (error) {
|
|
722
|
-
console.warn('PDF 大纲读取失败', error);
|
|
723
|
-
outlineItems = [];
|
|
724
|
-
syncUi();
|
|
725
|
-
}
|
|
726
|
-
};
|
|
727
|
-
const getPdfExportRatio = (width, height, mode) => {
|
|
728
|
-
const preferredRatio = mode === 'print' ? 1.75 : 1.5;
|
|
729
|
-
const maxRatio = Math.sqrt(PDF_EXPORT_MAX_PAGE_PIXELS / Math.max(width * height, 1));
|
|
730
|
-
return Math.max(0.75, Math.min(preferredRatio, maxRatio));
|
|
731
|
-
};
|
|
732
|
-
const getPdfPrintPageSize = async (pageNumber = 1) => {
|
|
733
|
-
var _a, _b;
|
|
734
|
-
const pdfDocument = pdfContext.document;
|
|
735
|
-
if (!pdfDocument) {
|
|
736
|
-
throw new Error('PDF 尚未加载完成,请稍后再试');
|
|
737
|
-
}
|
|
738
|
-
const page = await pdfDocument.getPage(Math.min(Math.max(pageNumber, 1), pdfDocument.numPages));
|
|
739
|
-
const viewport = page.getViewport({
|
|
740
|
-
scale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
|
741
|
-
rotation: currentRotation,
|
|
742
|
-
});
|
|
743
|
-
(_b = (_a = page).cleanup) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
744
|
-
return {
|
|
745
|
-
width: Math.ceil(viewport.width),
|
|
746
|
-
height: Math.ceil(viewport.height),
|
|
747
|
-
};
|
|
748
|
-
};
|
|
749
|
-
const buildPdfPrintStyle = async () => {
|
|
750
|
-
const size = await getPdfPrintPageSize();
|
|
751
|
-
return buildPrintPageStyle({
|
|
752
|
-
selector: '.viewer-export-content .pdf-export-page',
|
|
753
|
-
width: size.width,
|
|
754
|
-
height: size.height,
|
|
755
|
-
});
|
|
756
|
-
};
|
|
757
|
-
const renderPdfPagesForExport = async (exportOptions) => {
|
|
758
|
-
var _a, _b;
|
|
759
|
-
const pdfDocument = pdfContext.document;
|
|
760
|
-
if (!pdfDocument) {
|
|
761
|
-
throw new Error('PDF 尚未加载完成,请稍后再试');
|
|
762
|
-
}
|
|
763
|
-
const pagesHtml = [];
|
|
764
|
-
for (let pageNumber = 1; pageNumber <= pdfDocument.numPages; pageNumber += 1) {
|
|
765
|
-
if (destroyed) {
|
|
766
|
-
throw new Error('PDF 已卸载,无法继续打印');
|
|
767
|
-
}
|
|
768
|
-
const page = await pdfDocument.getPage(pageNumber);
|
|
769
|
-
const baseViewport = page.getViewport({
|
|
770
|
-
scale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
|
771
|
-
rotation: currentRotation,
|
|
772
|
-
});
|
|
773
|
-
const pageWidth = Math.ceil(baseViewport.width);
|
|
774
|
-
const pageHeight = Math.ceil(baseViewport.height);
|
|
775
|
-
const exportRatio = getPdfExportRatio(baseViewport.width, baseViewport.height, exportOptions.mode);
|
|
776
|
-
const renderViewport = page.getViewport({
|
|
777
|
-
scale: PixelsPerInch.PDF_TO_CSS_UNITS * exportRatio,
|
|
778
|
-
rotation: currentRotation,
|
|
779
|
-
});
|
|
780
|
-
const canvas = documentRef.createElement('canvas');
|
|
781
|
-
const canvasContext = canvas.getContext('2d');
|
|
782
|
-
if (!canvasContext) {
|
|
783
|
-
throw new Error('当前浏览器无法创建 PDF 打印画布');
|
|
784
|
-
}
|
|
785
|
-
canvas.width = Math.ceil(renderViewport.width);
|
|
786
|
-
canvas.height = Math.ceil(renderViewport.height);
|
|
787
|
-
await page.render({ canvas, canvasContext, viewport: renderViewport }).promise;
|
|
788
|
-
const pageTitle = `${exportOptions.title} - 第 ${pageNumber} 页`;
|
|
789
|
-
const pageStyle = [
|
|
790
|
-
`--viewer-print-page-width:${formatCssPixels(pageWidth)}`,
|
|
791
|
-
`--viewer-print-page-height:${formatCssPixels(pageHeight)}`,
|
|
792
|
-
`width:${formatCssPixels(pageWidth)}`,
|
|
793
|
-
`height:${formatCssPixels(pageHeight)}`,
|
|
794
|
-
].join(';');
|
|
795
|
-
pagesHtml.push(`<section class="pdf-export-page viewer-print-page" style="${pageStyle}" aria-label="${escapeAttribute(pageTitle)}"><img src="${canvas.toDataURL('image/png')}" alt="${escapeAttribute(pageTitle)}" /></section>`);
|
|
796
|
-
canvas.width = 0;
|
|
797
|
-
canvas.height = 0;
|
|
798
|
-
(_b = (_a = page).cleanup) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
799
|
-
}
|
|
800
|
-
return `<div class="pdf-export-document">${pagesHtml.join('')}</div>`;
|
|
801
|
-
};
|
|
802
|
-
const loadFile = async () => {
|
|
803
|
-
var _a, _b;
|
|
804
|
-
const requestVersion = ++loadVersion;
|
|
805
|
-
loadStatus = 'loading';
|
|
806
|
-
errorMessage = '';
|
|
807
|
-
pdfContext.document = null;
|
|
808
|
-
outlineItems = [];
|
|
809
|
-
pdfThumbnails.clear();
|
|
810
|
-
pendingPdfThumbnails.clear();
|
|
811
|
-
thumbnailObserver === null || thumbnailObserver === void 0 ? void 0 : thumbnailObserver.disconnect();
|
|
812
|
-
(_a = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _a === void 0 ? void 0 : _a.call(context, null);
|
|
813
|
-
syncUi();
|
|
814
|
-
let resource = null;
|
|
815
|
-
try {
|
|
816
|
-
if (destroyed || requestVersion !== loadVersion) {
|
|
817
|
-
return;
|
|
818
|
-
}
|
|
819
|
-
const eventBus = new EventBus();
|
|
820
|
-
const pdfLinkService = new PDFLinkService({ eventBus });
|
|
821
|
-
const pdfFindController = new PDFFindController({
|
|
822
|
-
eventBus,
|
|
823
|
-
linkService: pdfLinkService,
|
|
824
|
-
updateMatchesCountOnProgress: true,
|
|
825
|
-
});
|
|
826
|
-
const pdfViewer = new PDFViewer({
|
|
827
|
-
container,
|
|
828
|
-
eventBus,
|
|
829
|
-
linkService: pdfLinkService,
|
|
830
|
-
findController: pdfFindController,
|
|
831
|
-
l10n: new GenericL10n('zh-CN'),
|
|
832
|
-
enableAutoLinking: false,
|
|
833
|
-
});
|
|
834
|
-
pdfContext.viewer = pdfViewer;
|
|
835
|
-
pdfContext.linkService = pdfLinkService;
|
|
836
|
-
pdfContext.eventBus = eventBus;
|
|
837
|
-
pdfContext.findController = pdfFindController;
|
|
838
|
-
pdfLinkService.setViewer(pdfViewer);
|
|
839
|
-
eventBus.on('updatefindmatchescount', handlePdfFindMatchesCount);
|
|
840
|
-
eventBus.on('updatefindcontrolstate', handlePdfFindControlState);
|
|
841
|
-
eventBus.on('pagesinit', () => {
|
|
842
|
-
var _a;
|
|
843
|
-
applyRotation(currentRotation);
|
|
844
|
-
fitToWidth();
|
|
845
|
-
scheduleLegacyPageDimensionPatch();
|
|
846
|
-
loadStatus = 'ready';
|
|
847
|
-
syncUi();
|
|
848
|
-
(_a = context === null || context === void 0 ? void 0 : context.onProgressiveRender) === null || _a === void 0 ? void 0 : _a.call(context);
|
|
849
|
-
if (pdfContext.search) {
|
|
850
|
-
eventBus.dispatch('find', { type: '', query: pdfContext.search });
|
|
851
|
-
}
|
|
852
|
-
});
|
|
853
|
-
eventBus.on('pagechanging', ({ pageNumber }) => {
|
|
854
|
-
currentPage = pageNumber;
|
|
855
|
-
syncUi();
|
|
856
|
-
});
|
|
857
|
-
eventBus.on('scalechanging', ({ scale }) => {
|
|
858
|
-
currentScale = clampScale(scale);
|
|
859
|
-
scheduleLegacyPageDimensionPatch();
|
|
860
|
-
zoomEmitter.emit();
|
|
861
|
-
syncUi();
|
|
862
|
-
});
|
|
863
|
-
eventBus.on('pagerendered', scheduleLegacyPageDimensionPatch);
|
|
864
|
-
if (!(context === null || context === void 0 ? void 0 : context.streamUrl) && !buffer.byteLength) {
|
|
865
|
-
throw new Error('PDF 缺少可读取的数据源');
|
|
866
|
-
}
|
|
867
|
-
const worker = createPdfWorker();
|
|
868
|
-
const pdfAssets = resolveFileViewerPdfAssetUrls(options, documentRef.URL || documentRef.baseURI);
|
|
869
|
-
const source = (context === null || context === void 0 ? void 0 : context.streamUrl)
|
|
870
|
-
? {
|
|
871
|
-
url: context.streamUrl,
|
|
872
|
-
rangeChunkSize: (options === null || options === void 0 ? void 0 : options.rangeChunkSize) || DEFAULT_PDF_RANGE_CHUNK_SIZE,
|
|
873
|
-
withCredentials: (options === null || options === void 0 ? void 0 : options.withCredentials) === true,
|
|
874
|
-
}
|
|
875
|
-
: {
|
|
876
|
-
data: buffer,
|
|
877
|
-
};
|
|
878
|
-
const loadingTask = getDocument({
|
|
879
|
-
...source,
|
|
880
|
-
worker: worker || undefined,
|
|
881
|
-
cMapUrl: pdfAssets.cMapUrl,
|
|
882
|
-
wasmUrl: pdfAssets.wasmUrl,
|
|
883
|
-
standardFontDataUrl: pdfAssets.standardFontDataUrl,
|
|
884
|
-
useWorkerFetch: true,
|
|
885
|
-
cMapPacked: true,
|
|
886
|
-
enableXfa: true,
|
|
887
|
-
});
|
|
888
|
-
resource = { loadingTask, worker };
|
|
889
|
-
pdfContext.resource = resource;
|
|
890
|
-
const pdfDocument = await loadingTask.promise;
|
|
891
|
-
if (destroyed || requestVersion !== loadVersion || pdfContext.resource !== resource) {
|
|
892
|
-
if (pdfContext.resource === resource) {
|
|
893
|
-
pdfContext.resource = null;
|
|
894
|
-
await destroyPdfResource(resource);
|
|
895
|
-
}
|
|
896
|
-
return;
|
|
897
|
-
}
|
|
898
|
-
pageCount = pdfDocument.numPages;
|
|
899
|
-
currentPage = 1;
|
|
900
|
-
pdfContext.document = pdfDocument;
|
|
901
|
-
(_b = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _b === void 0 ? void 0 : _b.call(context, {
|
|
902
|
-
includeDocumentStyles: false,
|
|
903
|
-
printStyle: buildPdfPrintStyle,
|
|
904
|
-
toHtml: renderPdfPagesForExport,
|
|
905
|
-
});
|
|
906
|
-
void loadOutline(pdfDocument);
|
|
907
|
-
pdfViewer.setDocument(pdfDocument);
|
|
908
|
-
pdfLinkService.setDocument(pdfDocument, null);
|
|
909
|
-
syncUi();
|
|
910
|
-
}
|
|
911
|
-
catch (error) {
|
|
912
|
-
if (pdfContext.resource === resource) {
|
|
913
|
-
pdfContext.resource = null;
|
|
914
|
-
void destroyPdfResource(resource);
|
|
915
|
-
}
|
|
916
|
-
if (destroyed || requestVersion !== loadVersion) {
|
|
917
|
-
return;
|
|
918
|
-
}
|
|
919
|
-
loadStatus = 'error';
|
|
920
|
-
errorMessage = error instanceof Error ? error.message : 'PDF 加载失败';
|
|
921
|
-
syncUi();
|
|
922
|
-
}
|
|
923
|
-
};
|
|
924
|
-
registerFileViewerSearchProvider(root, {
|
|
925
|
-
search: (query, searchOptions) => runPdfFind(query, searchOptions, '', false),
|
|
926
|
-
next: () => pdfContext.search
|
|
927
|
-
? runPdfFind(pdfContext.search, undefined, 'again', false)
|
|
928
|
-
: pdfSearchState,
|
|
929
|
-
previous: () => pdfContext.search
|
|
930
|
-
? runPdfFind(pdfContext.search, undefined, 'again', true)
|
|
931
|
-
: pdfSearchState,
|
|
932
|
-
clear: clearPdfFind,
|
|
933
|
-
getState: () => pdfSearchState,
|
|
934
|
-
});
|
|
935
|
-
registerFileViewerZoomProvider(root, {
|
|
936
|
-
zoomIn: () => {
|
|
937
|
-
zoomIn();
|
|
938
|
-
return getPdfZoomState();
|
|
939
|
-
},
|
|
940
|
-
zoomOut: () => {
|
|
941
|
-
zoomOut();
|
|
942
|
-
return getPdfZoomState();
|
|
943
|
-
},
|
|
944
|
-
resetZoom: () => {
|
|
945
|
-
fitToWidth();
|
|
946
|
-
return getPdfZoomState();
|
|
947
|
-
},
|
|
948
|
-
setZoom: scale => {
|
|
949
|
-
autoFitWidth = false;
|
|
950
|
-
setScale(scale);
|
|
951
|
-
return getPdfZoomState();
|
|
952
|
-
},
|
|
953
|
-
getState: getPdfZoomState,
|
|
954
|
-
subscribe: zoomEmitter.subscribe,
|
|
955
|
-
});
|
|
956
|
-
navToggleButton.addEventListener('click', toggleNav);
|
|
957
|
-
previousPageButton.addEventListener('click', () => goToPage(currentPage - 1));
|
|
958
|
-
nextPageButton.addEventListener('click', () => goToPage(currentPage + 1));
|
|
959
|
-
zoomOutButton.addEventListener('click', zoomOut);
|
|
960
|
-
zoomInButton.addEventListener('click', zoomIn);
|
|
961
|
-
scaleButton.addEventListener('click', fitToWidth);
|
|
962
|
-
rotateLeftButton.addEventListener('click', () => applyRotation(currentRotation - 90));
|
|
963
|
-
rotateRightButton.addEventListener('click', () => applyRotation(currentRotation + 90));
|
|
964
|
-
pagesTab.addEventListener('click', () => setNavMode('pages'));
|
|
965
|
-
outlineTab.addEventListener('click', () => setNavMode('outline'));
|
|
966
|
-
if (targetWindow.ResizeObserver) {
|
|
967
|
-
resizeObserver = new targetWindow.ResizeObserver(() => scheduleFitToWidth());
|
|
968
|
-
resizeObserver.observe(container);
|
|
969
|
-
}
|
|
970
|
-
syncUi();
|
|
971
|
-
void loadFile();
|
|
972
|
-
return {
|
|
973
|
-
$el: root,
|
|
974
|
-
unmount() {
|
|
975
|
-
var _a;
|
|
976
|
-
destroyed = true;
|
|
977
|
-
loadVersion += 1;
|
|
978
|
-
targetWindow.cancelAnimationFrame(fitFrame);
|
|
979
|
-
targetWindow.cancelAnimationFrame(pageDimensionFrame);
|
|
980
|
-
thumbnailObserver === null || thumbnailObserver === void 0 ? void 0 : thumbnailObserver.disconnect();
|
|
981
|
-
thumbnailObserver = null;
|
|
982
|
-
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.disconnect();
|
|
983
|
-
resizeObserver = null;
|
|
984
|
-
unregisterFileViewerSearchProvider(root);
|
|
985
|
-
unregisterFileViewerZoomProvider(root);
|
|
986
|
-
outlineItems = [];
|
|
987
|
-
(_a = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _a === void 0 ? void 0 : _a.call(context, null);
|
|
988
|
-
const resource = pdfContext.resource;
|
|
989
|
-
pdfContext.viewer = null;
|
|
990
|
-
pdfContext.linkService = null;
|
|
991
|
-
pdfContext.eventBus = null;
|
|
992
|
-
pdfContext.findController = null;
|
|
993
|
-
pdfContext.document = null;
|
|
994
|
-
pdfContext.resource = null;
|
|
995
|
-
pdfSearchWaiters.forEach(waiter => targetWindow.clearTimeout(waiter.timer));
|
|
996
|
-
pdfSearchWaiters = [];
|
|
997
|
-
void destroyPdfResource(resource);
|
|
998
|
-
target.replaceChildren();
|
|
999
|
-
},
|
|
1000
|
-
};
|
|
1001
|
-
}
|