@file-viewer/core 2.0.0
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/LICENSE +160 -0
- package/README.en.md +78 -0
- package/README.md +83 -0
- package/dist/assets.d.ts +62 -0
- package/dist/assets.js +260 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +1 -0
- package/dist/capabilities.d.ts +24 -0
- package/dist/capabilities.js +95 -0
- package/dist/document.d.ts +9 -0
- package/dist/document.js +86 -0
- package/dist/documentDom/anchors.d.ts +7 -0
- package/dist/documentDom/anchors.js +196 -0
- package/dist/documentDom/index.d.ts +5 -0
- package/dist/documentDom/index.js +3 -0
- package/dist/documentDom/providers.d.ts +13 -0
- package/dist/documentDom/providers.js +52 -0
- package/dist/documentDom/scroll.d.ts +12 -0
- package/dist/documentDom/scroll.js +42 -0
- package/dist/documentDom.d.ts +5 -0
- package/dist/documentDom.js +3 -0
- package/dist/documentEvents.d.ts +73 -0
- package/dist/documentEvents.js +150 -0
- package/dist/documentSearch.d.ts +50 -0
- package/dist/documentSearch.js +459 -0
- package/dist/documentZoom.d.ts +47 -0
- package/dist/documentZoom.js +184 -0
- package/dist/export.d.ts +26 -0
- package/dist/export.js +466 -0
- package/dist/formats.d.ts +305 -0
- package/dist/formats.js +207 -0
- package/dist/headless.d.ts +25 -0
- package/dist/headless.js +14 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.js +118 -0
- package/dist/lifecycleFacade.d.ts +46 -0
- package/dist/lifecycleFacade.js +68 -0
- package/dist/loading.d.ts +59 -0
- package/dist/loading.js +489 -0
- package/dist/operations.d.ts +287 -0
- package/dist/operations.js +485 -0
- package/dist/options.d.ts +16 -0
- package/dist/options.js +92 -0
- package/dist/presentation.d.ts +14 -0
- package/dist/presentation.js +16 -0
- package/dist/printLayout.d.ts +19 -0
- package/dist/printLayout.js +83 -0
- package/dist/registry.d.ts +2 -0
- package/dist/registry.js +63 -0
- package/dist/rendererDispatcher.d.ts +21 -0
- package/dist/rendererDispatcher.js +42 -0
- package/dist/rendererHandler.d.ts +165 -0
- package/dist/rendererHandler.js +354 -0
- package/dist/renderers/archive.d.ts +2 -0
- package/dist/renderers/archive.js +547 -0
- package/dist/renderers/archiveCache.d.ts +10 -0
- package/dist/renderers/archiveCache.js +96 -0
- package/dist/renderers/archiveFallback.d.ts +7 -0
- package/dist/renderers/archiveFallback.js +166 -0
- package/dist/renderers/archiveShared.d.ts +23 -0
- package/dist/renderers/archiveShared.js +71 -0
- package/dist/renderers/audio.d.ts +8 -0
- package/dist/renderers/audio.js +219 -0
- package/dist/renderers/cad.d.ts +2 -0
- package/dist/renderers/cad.js +445 -0
- package/dist/renderers/code.d.ts +11 -0
- package/dist/renderers/code.js +233 -0
- package/dist/renderers/data.d.ts +7 -0
- package/dist/renderers/data.js +370 -0
- package/dist/renderers/drawing.d.ts +10 -0
- package/dist/renderers/drawing.js +537 -0
- package/dist/renderers/eda.d.ts +2 -0
- package/dist/renderers/eda.js +434 -0
- package/dist/renderers/edaParser.d.ts +77 -0
- package/dist/renderers/edaParser.js +569 -0
- package/dist/renderers/email.d.ts +2 -0
- package/dist/renderers/email.js +463 -0
- package/dist/renderers/epub.d.ts +2 -0
- package/dist/renderers/epub.js +330 -0
- package/dist/renderers/geo.d.ts +2 -0
- package/dist/renderers/geo.js +284 -0
- package/dist/renderers/image.d.ts +2 -0
- package/dist/renderers/image.js +179 -0
- package/dist/renderers/index.d.ts +21 -0
- package/dist/renderers/index.js +207 -0
- package/dist/renderers/markdown.d.ts +2 -0
- package/dist/renderers/markdown.js +83 -0
- package/dist/renderers/model.d.ts +2 -0
- package/dist/renderers/model.js +566 -0
- package/dist/renderers/ofd.d.ts +2 -0
- package/dist/renderers/ofd.js +255 -0
- package/dist/renderers/openDocument.d.ts +2 -0
- package/dist/renderers/openDocument.js +122 -0
- package/dist/renderers/pdf.d.ts +3 -0
- package/dist/renderers/pdf.js +846 -0
- package/dist/renderers/pdfStyles.d.ts +1 -0
- package/dist/renderers/pdfStyles.js +1 -0
- package/dist/renderers/pptx.d.ts +2 -0
- package/dist/renderers/pptx.js +202 -0
- package/dist/renderers/spreadsheet/state.d.ts +80 -0
- package/dist/renderers/spreadsheet/state.js +96 -0
- package/dist/renderers/spreadsheet/view.d.ts +25 -0
- package/dist/renderers/spreadsheet/view.js +833 -0
- package/dist/renderers/spreadsheet/worker/index.d.ts +2 -0
- package/dist/renderers/spreadsheet/worker/index.js +1 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/SheetJsModel.d.ts +73 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/SheetJsModel.js +623 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/color.d.ts +2 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/color.js +73 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/index.d.ts +1 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/index.js +1 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/parser.d.ts +18 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/parser.js +106 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/sheet.worker.d.ts +1 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/sheet.worker.js +11 -0
- package/dist/renderers/spreadsheet/worker/type.d.ts +57 -0
- package/dist/renderers/spreadsheet/worker/type.js +1 -0
- package/dist/renderers/spreadsheet.d.ts +3 -0
- package/dist/renderers/spreadsheet.js +929 -0
- package/dist/renderers/typst.d.ts +8 -0
- package/dist/renderers/typst.js +415 -0
- package/dist/renderers/umd/parser.d.ts +30 -0
- package/dist/renderers/umd/parser.js +408 -0
- package/dist/renderers/umd.d.ts +2 -0
- package/dist/renderers/umd.js +297 -0
- package/dist/renderers/video.d.ts +8 -0
- package/dist/renderers/video.js +108 -0
- package/dist/renderers/wordDoc.d.ts +5 -0
- package/dist/renderers/wordDoc.js +284 -0
- package/dist/renderers/wordDocx.d.ts +5 -0
- package/dist/renderers/wordDocx.js +501 -0
- package/dist/renderers/wordDocx.worker.d.ts +1 -0
- package/dist/renderers/wordDocx.worker.js +96 -0
- package/dist/source.d.ts +18 -0
- package/dist/source.js +152 -0
- package/dist/sourceLoading.d.ts +566 -0
- package/dist/sourceLoading.js +918 -0
- package/dist/state.d.ts +16 -0
- package/dist/state.js +81 -0
- package/dist/types.d.ts +446 -0
- package/dist/types.js +1 -0
- package/dist/viewer.d.ts +8 -0
- package/dist/viewer.js +285 -0
- package/dist/viewerOperations.d.ts +88 -0
- package/dist/viewerOperations.js +242 -0
- package/dist/watermark.d.ts +15 -0
- package/dist/watermark.js +81 -0
- package/dist/worker.d.ts +34 -0
- package/dist/worker.js +101 -0
- package/package.json +109 -0
- package/vendor/ofd/dltech/jbig2/arithmetic_decoder.js +183 -0
- package/vendor/ofd/dltech/jbig2/ccitt.js +1070 -0
- package/vendor/ofd/dltech/jbig2/compatibility.js +12 -0
- package/vendor/ofd/dltech/jbig2/core_utils.js +180 -0
- package/vendor/ofd/dltech/jbig2/is_node.js +27 -0
- package/vendor/ofd/dltech/jbig2/jbig2.js +2589 -0
- package/vendor/ofd/dltech/jbig2/jbig2_stream.js +81 -0
- package/vendor/ofd/dltech/jbig2/primitives.js +387 -0
- package/vendor/ofd/dltech/jbig2/stream.js +1348 -0
- package/vendor/ofd/dltech/jbig2/util.js +972 -0
- package/vendor/ofd/dltech/ofd/ofd.d.ts +11 -0
- package/vendor/ofd/dltech/ofd/ofd.js +100 -0
- package/vendor/ofd/dltech/ofd/ofd_parser.js +395 -0
- package/vendor/ofd/dltech/ofd/ofd_render.js +473 -0
- package/vendor/ofd/dltech/ofd/ofd_util.js +350 -0
- package/vendor/ofd/dltech/ofd/pipeline.js +26 -0
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
import { resolveFileViewerDocxWorkerUrl } from '../assets.js';
|
|
2
|
+
import { applyPrintPageSize, buildPrintPageStyle, formatCssPixels, getElementPrintPageSize } from '../printLayout.js';
|
|
3
|
+
import { createFileViewerZoomChangeEmitter as createZoomChangeEmitter, } from '../documentZoom.js';
|
|
4
|
+
import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider } from '../documentDom.js';
|
|
5
|
+
const DOCX_DEFAULT_PAGE_SIZE = {
|
|
6
|
+
width: 794,
|
|
7
|
+
height: 1123
|
|
8
|
+
};
|
|
9
|
+
const DOCX_PROGRESSIVE_BATCH_SIZE = 2;
|
|
10
|
+
const DOCX_WORKER_TIMEOUT = 15000;
|
|
11
|
+
const DOCX_MIN_SCALE = 0.24;
|
|
12
|
+
const DOCX_MAX_SCALE = 3;
|
|
13
|
+
const DOCX_ZOOM_STEP = 0.15;
|
|
14
|
+
let docxWorkerRequestId = 0;
|
|
15
|
+
const loadLibrary = (() => {
|
|
16
|
+
const loader = {
|
|
17
|
+
module: null,
|
|
18
|
+
async load() {
|
|
19
|
+
if (!this.module) {
|
|
20
|
+
this.module = import('docx-preview');
|
|
21
|
+
}
|
|
22
|
+
return this.module;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
return async () => {
|
|
26
|
+
return await loader.load();
|
|
27
|
+
};
|
|
28
|
+
})();
|
|
29
|
+
const createDocxOptions = (experimental = true) => ({
|
|
30
|
+
// Word 会写入 autoSpaceDN/autoSpaceDE 等兼容标签;生产预览保持静默,避免 docx-preview 调试告警刷屏。
|
|
31
|
+
debug: false,
|
|
32
|
+
experimental
|
|
33
|
+
});
|
|
34
|
+
const getTargetWindow = (target) => {
|
|
35
|
+
return target.ownerDocument.defaultView;
|
|
36
|
+
};
|
|
37
|
+
const isTargetHTMLElement = (value, target) => {
|
|
38
|
+
var _a;
|
|
39
|
+
const HTMLElementCtor = (_a = getTargetWindow(target)) === null || _a === void 0 ? void 0 : _a.HTMLElement;
|
|
40
|
+
return HTMLElementCtor ? value instanceof HTMLElementCtor : value instanceof HTMLElement;
|
|
41
|
+
};
|
|
42
|
+
const shouldUseDocxWorker = (target, context) => {
|
|
43
|
+
var _a, _b;
|
|
44
|
+
const view = getTargetWindow(target);
|
|
45
|
+
return ((_b = (_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.docx) === null || _b === void 0 ? void 0 : _b.worker) === true && typeof (view === null || view === void 0 ? void 0 : view.Worker) === 'function';
|
|
46
|
+
};
|
|
47
|
+
const shouldMountDocxProgressively = (context) => {
|
|
48
|
+
var _a, _b;
|
|
49
|
+
return ((_b = (_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.docx) === null || _b === void 0 ? void 0 : _b.progressive) === true;
|
|
50
|
+
};
|
|
51
|
+
const shouldPaginateOversizedDocxSections = (context) => {
|
|
52
|
+
var _a, _b;
|
|
53
|
+
return ((_b = (_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.docx) === null || _b === void 0 ? void 0 : _b.visualPagination) === true;
|
|
54
|
+
};
|
|
55
|
+
const waitDocxMountFrame = (target) => {
|
|
56
|
+
return new Promise(resolve => {
|
|
57
|
+
const view = getTargetWindow(target);
|
|
58
|
+
if (!view || typeof view.requestAnimationFrame !== 'function') {
|
|
59
|
+
globalThis.setTimeout(resolve, 0);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
view.requestAnimationFrame(() => resolve());
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
async function mountDocxPreviewHtml(html, target, context) {
|
|
66
|
+
var _a, _b, _c;
|
|
67
|
+
if (!shouldMountDocxProgressively(context)) {
|
|
68
|
+
target.innerHTML = html;
|
|
69
|
+
(_a = context === null || context === void 0 ? void 0 : context.onProgressiveRender) === null || _a === void 0 ? void 0 : _a.call(context);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const template = target.ownerDocument.createElement('template');
|
|
73
|
+
template.innerHTML = html;
|
|
74
|
+
const sourceWrapper = template.content.querySelector('.docx-wrapper');
|
|
75
|
+
if (!sourceWrapper) {
|
|
76
|
+
target.innerHTML = html;
|
|
77
|
+
(_b = context === null || context === void 0 ? void 0 : context.onProgressiveRender) === null || _b === void 0 ? void 0 : _b.call(context);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const pages = Array.from(sourceWrapper.children);
|
|
81
|
+
const liveWrapper = sourceWrapper.cloneNode(false);
|
|
82
|
+
let hasNotifiedFirstPaint = false;
|
|
83
|
+
target.innerHTML = '';
|
|
84
|
+
Array.from(template.content.childNodes).forEach(node => {
|
|
85
|
+
if (node === sourceWrapper) {
|
|
86
|
+
target.appendChild(liveWrapper);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
target.appendChild(node);
|
|
90
|
+
});
|
|
91
|
+
for (let index = 0; index < pages.length; index += DOCX_PROGRESSIVE_BATCH_SIZE) {
|
|
92
|
+
pages.slice(index, index + DOCX_PROGRESSIVE_BATCH_SIZE).forEach(page => {
|
|
93
|
+
liveWrapper.appendChild(page);
|
|
94
|
+
});
|
|
95
|
+
if (!hasNotifiedFirstPaint) {
|
|
96
|
+
hasNotifiedFirstPaint = true;
|
|
97
|
+
(_c = context === null || context === void 0 ? void 0 : context.onProgressiveRender) === null || _c === void 0 ? void 0 : _c.call(context);
|
|
98
|
+
}
|
|
99
|
+
if (index + DOCX_PROGRESSIVE_BATCH_SIZE < pages.length) {
|
|
100
|
+
await waitDocxMountFrame(target);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function createDocxWorker(target, context) {
|
|
105
|
+
var _a;
|
|
106
|
+
const view = getTargetWindow(target);
|
|
107
|
+
if (!(view === null || view === void 0 ? void 0 : view.Worker)) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const workerUrl = resolveFileViewerDocxWorkerUrl((_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.docx, target.ownerDocument.URL || undefined);
|
|
111
|
+
try {
|
|
112
|
+
return new view.Worker(workerUrl, { type: 'module' });
|
|
113
|
+
}
|
|
114
|
+
catch (moduleWorkerError) {
|
|
115
|
+
try {
|
|
116
|
+
return new view.Worker(workerUrl);
|
|
117
|
+
}
|
|
118
|
+
catch (classicWorkerError) {
|
|
119
|
+
console.warn('[file-viewer] DOCX Worker 无法创建,回退到 docx-preview 主线程渲染。', classicWorkerError || moduleWorkerError);
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function renderDocxWithWorker(buffer, target, options, context) {
|
|
125
|
+
var _a, _b, _c;
|
|
126
|
+
if (!shouldUseDocxWorker(target, context)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
const worker = createDocxWorker(target, context);
|
|
130
|
+
if (!worker) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
const view = getTargetWindow(target);
|
|
134
|
+
const id = ++docxWorkerRequestId;
|
|
135
|
+
const timeout = (_c = (_b = (_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.docx) === null || _b === void 0 ? void 0 : _b.workerTimeout) !== null && _c !== void 0 ? _c : DOCX_WORKER_TIMEOUT;
|
|
136
|
+
return await new Promise(resolve => {
|
|
137
|
+
let settled = false;
|
|
138
|
+
let timeoutId;
|
|
139
|
+
const cleanup = () => {
|
|
140
|
+
if (timeoutId !== undefined) {
|
|
141
|
+
view === null || view === void 0 ? void 0 : view.clearTimeout(timeoutId);
|
|
142
|
+
}
|
|
143
|
+
worker.removeEventListener('message', handleMessage);
|
|
144
|
+
worker.removeEventListener('error', handleError);
|
|
145
|
+
worker.removeEventListener('messageerror', handleMessageError);
|
|
146
|
+
worker.terminate();
|
|
147
|
+
};
|
|
148
|
+
const fallback = (reason) => {
|
|
149
|
+
if (settled) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
settled = true;
|
|
153
|
+
cleanup();
|
|
154
|
+
console.warn('[file-viewer] DOCX Worker 渲染失败,回退到 docx-preview 主线程渲染。', reason);
|
|
155
|
+
resolve(false);
|
|
156
|
+
};
|
|
157
|
+
const handleMessage = (event) => {
|
|
158
|
+
var _a;
|
|
159
|
+
if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.id) !== id) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (settled) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
settled = true;
|
|
166
|
+
cleanup();
|
|
167
|
+
if (event.data.ok) {
|
|
168
|
+
void mountDocxPreviewHtml(event.data.html, target, context)
|
|
169
|
+
.then(() => resolve(true))
|
|
170
|
+
.catch(reason => {
|
|
171
|
+
console.warn('[file-viewer] DOCX 渐进挂载失败,回退到 docx-preview 主线程渲染。', reason);
|
|
172
|
+
resolve(false);
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
console.warn('[file-viewer] DOCX Worker 渲染失败,回退到 docx-preview 主线程渲染。', event.data.message);
|
|
177
|
+
resolve(false);
|
|
178
|
+
};
|
|
179
|
+
const handleError = (event) => {
|
|
180
|
+
fallback(event.error || event.message);
|
|
181
|
+
};
|
|
182
|
+
const handleMessageError = () => {
|
|
183
|
+
fallback('DOCX Worker 消息无法结构化传输');
|
|
184
|
+
};
|
|
185
|
+
worker.addEventListener('message', handleMessage);
|
|
186
|
+
worker.addEventListener('error', handleError);
|
|
187
|
+
worker.addEventListener('messageerror', handleMessageError);
|
|
188
|
+
if (Number.isFinite(timeout) && timeout > 0) {
|
|
189
|
+
timeoutId = view === null || view === void 0 ? void 0 : view.setTimeout(() => {
|
|
190
|
+
fallback(`DOCX Worker 超过 ${timeout}ms 未返回结果`);
|
|
191
|
+
}, timeout);
|
|
192
|
+
}
|
|
193
|
+
const workerBuffer = buffer.slice(0);
|
|
194
|
+
// Worker 内输出 HTML,图片和字体使用 data URL,避免 Worker 生命周期结束后 Blob URL 失效。
|
|
195
|
+
worker.postMessage({
|
|
196
|
+
id,
|
|
197
|
+
buffer: workerBuffer,
|
|
198
|
+
options: {
|
|
199
|
+
...options,
|
|
200
|
+
// docx-preview 的 experimental tab stop 需要真实布局 API,Worker 内无法可靠计算。
|
|
201
|
+
experimental: false,
|
|
202
|
+
useBase64URL: true
|
|
203
|
+
}
|
|
204
|
+
}, [workerBuffer]);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
const DOCX_RESPONSIVE_CSS = `
|
|
208
|
+
.docx-fit-viewer {
|
|
209
|
+
box-sizing: border-box;
|
|
210
|
+
height: 100%;
|
|
211
|
+
overflow: auto;
|
|
212
|
+
background: #ececec;
|
|
213
|
+
}
|
|
214
|
+
.docx-fit-viewer .docx-wrapper {
|
|
215
|
+
box-sizing: border-box;
|
|
216
|
+
min-width: 0 !important;
|
|
217
|
+
width: 100% !important;
|
|
218
|
+
padding: 24px 14px 40px !important;
|
|
219
|
+
background: #e7e9ec !important;
|
|
220
|
+
}
|
|
221
|
+
.docx-fit-viewer .docx-page-frame {
|
|
222
|
+
position: relative;
|
|
223
|
+
width: 100%;
|
|
224
|
+
min-width: 0;
|
|
225
|
+
margin: 0 auto 24px;
|
|
226
|
+
overflow: visible;
|
|
227
|
+
}
|
|
228
|
+
.docx-fit-viewer .docx-page-frame > section.docx {
|
|
229
|
+
position: absolute;
|
|
230
|
+
top: 0;
|
|
231
|
+
left: 50%;
|
|
232
|
+
margin: 0 !important;
|
|
233
|
+
background: #ffffff !important;
|
|
234
|
+
box-shadow: 0 2px 14px rgba(25, 35, 48, 0.18);
|
|
235
|
+
box-sizing: border-box;
|
|
236
|
+
color: #111827;
|
|
237
|
+
overflow: hidden;
|
|
238
|
+
transform-origin: top center;
|
|
239
|
+
}
|
|
240
|
+
`;
|
|
241
|
+
function installResponsiveStyle(target) {
|
|
242
|
+
const style = target.ownerDocument.createElement('style');
|
|
243
|
+
style.textContent = DOCX_RESPONSIVE_CSS;
|
|
244
|
+
target.prepend(style);
|
|
245
|
+
return style;
|
|
246
|
+
}
|
|
247
|
+
function clonePageShell(section, article, pageHeight) {
|
|
248
|
+
const nextPage = section.cloneNode(false);
|
|
249
|
+
nextPage.innerHTML = '';
|
|
250
|
+
nextPage.dataset.docxPaginated = 'true';
|
|
251
|
+
nextPage.style.minHeight = `${pageHeight}px`;
|
|
252
|
+
nextPage.style.height = `${pageHeight}px`;
|
|
253
|
+
nextPage.style.overflow = 'hidden';
|
|
254
|
+
const nextArticle = article.cloneNode(false);
|
|
255
|
+
nextPage.appendChild(nextArticle);
|
|
256
|
+
Array.from(section.children).forEach(child => {
|
|
257
|
+
if (child !== article) {
|
|
258
|
+
nextPage.appendChild(child.cloneNode(true));
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
return { page: nextPage, article: nextArticle };
|
|
262
|
+
}
|
|
263
|
+
function getDocxPageHeight(section) {
|
|
264
|
+
var _a;
|
|
265
|
+
const style = (_a = section.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.getComputedStyle(section);
|
|
266
|
+
const minHeight = style ? parseFloat(style.minHeight) : 0;
|
|
267
|
+
return Number.isFinite(minHeight) && minHeight > 0 ? minHeight : section.offsetHeight;
|
|
268
|
+
}
|
|
269
|
+
function paginateOversizedSections(target) {
|
|
270
|
+
const wrapper = target.querySelector('.docx-wrapper');
|
|
271
|
+
if (!wrapper) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
Array.from(wrapper.children).forEach(child => {
|
|
275
|
+
if (!isTargetHTMLElement(child, target) || !child.matches('section.docx')) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const article = child.querySelector(':scope > article');
|
|
279
|
+
if (!isTargetHTMLElement(article, target)) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const pageHeight = getDocxPageHeight(child);
|
|
283
|
+
const originalNodes = Array.from(article.childNodes);
|
|
284
|
+
if (!pageHeight || originalNodes.length < 2 || child.scrollHeight <= pageHeight * 1.15) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
// docx-preview 只能按已有分页符拆页;没有分页符的长文档需要在预览层补一层视觉分页。
|
|
288
|
+
let current = clonePageShell(child, article, pageHeight);
|
|
289
|
+
child.before(current.page);
|
|
290
|
+
originalNodes.forEach(node => {
|
|
291
|
+
current.article.appendChild(node);
|
|
292
|
+
if (current.page.scrollHeight <= pageHeight + 1 || current.article.childNodes.length === 1) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
current.article.removeChild(node);
|
|
296
|
+
current = clonePageShell(child, article, pageHeight);
|
|
297
|
+
child.before(current.page);
|
|
298
|
+
current.article.appendChild(node);
|
|
299
|
+
});
|
|
300
|
+
child.remove();
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
function wrapDocxPages(target) {
|
|
304
|
+
const wrapper = target.querySelector('.docx-wrapper');
|
|
305
|
+
if (!wrapper) {
|
|
306
|
+
return [];
|
|
307
|
+
}
|
|
308
|
+
return Array.from(wrapper.children).flatMap(child => {
|
|
309
|
+
if (!isTargetHTMLElement(child, target) || !child.matches('section.docx')) {
|
|
310
|
+
return [];
|
|
311
|
+
}
|
|
312
|
+
const frame = target.ownerDocument.createElement('div');
|
|
313
|
+
frame.className = 'docx-page-frame';
|
|
314
|
+
child.before(frame);
|
|
315
|
+
frame.appendChild(child);
|
|
316
|
+
return [frame];
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
function makeDocxResponsive(target, context) {
|
|
320
|
+
target.classList.add('docx-fit-viewer');
|
|
321
|
+
const style = installResponsiveStyle(target);
|
|
322
|
+
if (shouldPaginateOversizedDocxSections(context)) {
|
|
323
|
+
paginateOversizedSections(target);
|
|
324
|
+
}
|
|
325
|
+
const frames = wrapDocxPages(target);
|
|
326
|
+
const view = getTargetWindow(target);
|
|
327
|
+
const ResizeObserverCtor = view === null || view === void 0 ? void 0 : view.ResizeObserver;
|
|
328
|
+
let resizeFrame = 0;
|
|
329
|
+
let userZoom = 1;
|
|
330
|
+
let currentScale = 1;
|
|
331
|
+
let currentFitScale = 1;
|
|
332
|
+
const zoomEmitter = createZoomChangeEmitter();
|
|
333
|
+
const clampScale = (scale) => {
|
|
334
|
+
return Math.min(DOCX_MAX_SCALE, Math.max(DOCX_MIN_SCALE, Number(scale.toFixed(2))));
|
|
335
|
+
};
|
|
336
|
+
const resize = () => {
|
|
337
|
+
if (!view) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
view.cancelAnimationFrame(resizeFrame);
|
|
341
|
+
resizeFrame = view.requestAnimationFrame(() => {
|
|
342
|
+
let firstScale = 1;
|
|
343
|
+
frames.forEach(frame => {
|
|
344
|
+
const page = frame.firstElementChild;
|
|
345
|
+
if (!isTargetHTMLElement(page, target)) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
page.style.transform = 'translateX(-50%)';
|
|
349
|
+
const pageWidth = page.offsetWidth;
|
|
350
|
+
const pageHeight = page.offsetHeight;
|
|
351
|
+
if (!pageWidth || !pageHeight) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const availableWidth = Math.max(target.clientWidth - 28, 120);
|
|
355
|
+
const fitScale = Math.min(1, Math.max(DOCX_MIN_SCALE, availableWidth / pageWidth));
|
|
356
|
+
const scale = clampScale(fitScale * userZoom);
|
|
357
|
+
firstScale = scale;
|
|
358
|
+
currentFitScale = fitScale;
|
|
359
|
+
page.style.transform = `translateX(-50%) scale(${scale})`;
|
|
360
|
+
frame.style.width = `${Math.ceil(Math.max(pageWidth * scale, target.clientWidth - 28, 120))}px`;
|
|
361
|
+
frame.style.maxWidth = 'none';
|
|
362
|
+
frame.style.height = `${Math.ceil(pageHeight * scale)}px`;
|
|
363
|
+
});
|
|
364
|
+
currentScale = firstScale;
|
|
365
|
+
zoomEmitter.emit();
|
|
366
|
+
});
|
|
367
|
+
};
|
|
368
|
+
const getZoomState = () => ({
|
|
369
|
+
scale: currentScale,
|
|
370
|
+
label: `${Math.round(currentScale * 100)}%`,
|
|
371
|
+
canZoomIn: currentScale < DOCX_MAX_SCALE,
|
|
372
|
+
canZoomOut: currentScale > DOCX_MIN_SCALE,
|
|
373
|
+
canReset: userZoom !== 1,
|
|
374
|
+
minScale: DOCX_MIN_SCALE,
|
|
375
|
+
maxScale: DOCX_MAX_SCALE
|
|
376
|
+
});
|
|
377
|
+
const setUserZoom = (nextZoom) => {
|
|
378
|
+
userZoom = Math.min(6, Math.max(0.2, Number(nextZoom.toFixed(2))));
|
|
379
|
+
resize();
|
|
380
|
+
return getZoomState();
|
|
381
|
+
};
|
|
382
|
+
target.dataset.viewerZoomProvider = 'docx';
|
|
383
|
+
registerFileViewerZoomProvider(target, {
|
|
384
|
+
zoomIn: () => setUserZoom(userZoom + DOCX_ZOOM_STEP),
|
|
385
|
+
zoomOut: () => setUserZoom(userZoom - DOCX_ZOOM_STEP),
|
|
386
|
+
resetZoom: () => setUserZoom(1),
|
|
387
|
+
setZoom: scale => setUserZoom(scale / Math.max(currentFitScale, 0.01)),
|
|
388
|
+
getState: getZoomState,
|
|
389
|
+
subscribe: zoomEmitter.subscribe
|
|
390
|
+
});
|
|
391
|
+
const observer = ResizeObserverCtor ? new ResizeObserverCtor(resize) : null;
|
|
392
|
+
observer === null || observer === void 0 ? void 0 : observer.observe(target);
|
|
393
|
+
frames.forEach(frame => {
|
|
394
|
+
const page = getDocxPageElement(frame);
|
|
395
|
+
if (page) {
|
|
396
|
+
observer === null || observer === void 0 ? void 0 : observer.observe(page);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
resize();
|
|
400
|
+
return () => {
|
|
401
|
+
view === null || view === void 0 ? void 0 : view.cancelAnimationFrame(resizeFrame);
|
|
402
|
+
observer === null || observer === void 0 ? void 0 : observer.disconnect();
|
|
403
|
+
unregisterFileViewerZoomProvider(target);
|
|
404
|
+
style.remove();
|
|
405
|
+
target.classList.remove('docx-fit-viewer');
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function getDocxPageElement(frame) {
|
|
409
|
+
var _a;
|
|
410
|
+
const page = frame.firstElementChild;
|
|
411
|
+
const HTMLElementCtor = (_a = frame.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.HTMLElement;
|
|
412
|
+
return HTMLElementCtor && page instanceof HTMLElementCtor ? page : null;
|
|
413
|
+
}
|
|
414
|
+
function getDocxFramePrintSize(frame) {
|
|
415
|
+
const page = frame ? getDocxPageElement(frame) : null;
|
|
416
|
+
return page ? getElementPrintPageSize(page, DOCX_DEFAULT_PAGE_SIZE) : DOCX_DEFAULT_PAGE_SIZE;
|
|
417
|
+
}
|
|
418
|
+
function normalizeDocxPageForPrint(frame, pageSize) {
|
|
419
|
+
const pageWidth = formatCssPixels(pageSize.width);
|
|
420
|
+
const pageHeight = formatCssPixels(pageSize.height);
|
|
421
|
+
applyPrintPageSize(frame, pageSize);
|
|
422
|
+
frame.style.margin = '0 auto 18px';
|
|
423
|
+
const page = getDocxPageElement(frame);
|
|
424
|
+
if (!page) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
page.style.position = 'relative';
|
|
428
|
+
page.style.top = 'auto';
|
|
429
|
+
page.style.left = 'auto';
|
|
430
|
+
page.style.width = pageWidth;
|
|
431
|
+
page.style.maxWidth = 'none';
|
|
432
|
+
page.style.minHeight = pageHeight;
|
|
433
|
+
page.style.height = pageHeight;
|
|
434
|
+
page.style.margin = '0 auto';
|
|
435
|
+
page.style.transform = 'none';
|
|
436
|
+
page.style.transformOrigin = 'top left';
|
|
437
|
+
page.style.overflow = 'hidden';
|
|
438
|
+
page.style.boxShadow = 'none';
|
|
439
|
+
}
|
|
440
|
+
function buildDocxPrintStyle(target) {
|
|
441
|
+
const firstFrame = target.querySelector('.docx-page-frame');
|
|
442
|
+
const pageSize = getDocxFramePrintSize(firstFrame || undefined);
|
|
443
|
+
return buildPrintPageStyle({
|
|
444
|
+
selector: '.viewer-export-content .docx-page-frame',
|
|
445
|
+
width: pageSize.width,
|
|
446
|
+
height: pageSize.height
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
function prepareDocxCloneForExport(target) {
|
|
450
|
+
const liveFrames = Array.from(target.querySelectorAll('.docx-page-frame'));
|
|
451
|
+
const clone = target.cloneNode(true);
|
|
452
|
+
const printDocument = target.ownerDocument.createElement('div');
|
|
453
|
+
printDocument.className = 'docx-print-document';
|
|
454
|
+
const scopedStyles = Array.from(clone.querySelectorAll('style'))
|
|
455
|
+
.filter(style => { var _a; return !((_a = style.textContent) === null || _a === void 0 ? void 0 : _a.includes('.docx-fit-viewer')); })
|
|
456
|
+
.map(style => style.outerHTML)
|
|
457
|
+
.join('');
|
|
458
|
+
clone.querySelectorAll('.docx-page-frame').forEach((frame, index) => {
|
|
459
|
+
normalizeDocxPageForPrint(frame, getDocxFramePrintSize(liveFrames[index]));
|
|
460
|
+
printDocument.appendChild(frame.cloneNode(true));
|
|
461
|
+
});
|
|
462
|
+
return printDocument.childElementCount ? `${scopedStyles}${printDocument.outerHTML}` : clone.innerHTML;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* 渲染docx文件
|
|
466
|
+
*/
|
|
467
|
+
export default async function (buffer, target, context) {
|
|
468
|
+
var _a;
|
|
469
|
+
const docxOptions = createDocxOptions();
|
|
470
|
+
const workerRendered = await renderDocxWithWorker(buffer, target, docxOptions, context);
|
|
471
|
+
target.dataset.docxWorker = workerRendered ? 'true' : 'false';
|
|
472
|
+
if (!workerRendered) {
|
|
473
|
+
const { defaultOptions, renderAsync } = await loadLibrary();
|
|
474
|
+
await renderAsync(buffer, target, undefined, {
|
|
475
|
+
...defaultOptions,
|
|
476
|
+
...docxOptions
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
const disposeResponsive = makeDocxResponsive(target, context);
|
|
480
|
+
(_a = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _a === void 0 ? void 0 : _a.call(context, {
|
|
481
|
+
includeDocumentStyles: false,
|
|
482
|
+
beforeSnapshot: () => {
|
|
483
|
+
const view = getTargetWindow(target);
|
|
484
|
+
if (view) {
|
|
485
|
+
view.dispatchEvent(new view.Event('resize'));
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
printStyle: () => buildDocxPrintStyle(target),
|
|
489
|
+
toHtml: () => prepareDocxCloneForExport(target)
|
|
490
|
+
});
|
|
491
|
+
return {
|
|
492
|
+
$el: target,
|
|
493
|
+
unmount() {
|
|
494
|
+
var _a;
|
|
495
|
+
(_a = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _a === void 0 ? void 0 : _a.call(context, null);
|
|
496
|
+
disposeResponsive();
|
|
497
|
+
delete target.dataset.docxWorker;
|
|
498
|
+
target.innerHTML = '';
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { renderAsync } from 'docx-preview';
|
|
2
|
+
import { DOMParser, XMLSerializer } from '@xmldom/xmldom';
|
|
3
|
+
import { parseHTML } from 'linkedom';
|
|
4
|
+
const ctx = self;
|
|
5
|
+
const getFirstElementChild = function () {
|
|
6
|
+
const nodes = this.childNodes;
|
|
7
|
+
if (!nodes) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
for (let index = 0; index < nodes.length; index += 1) {
|
|
11
|
+
const child = typeof nodes.item === 'function' ? nodes.item(index) : nodes[index];
|
|
12
|
+
if ((child === null || child === void 0 ? void 0 : child.nodeType) === 1) {
|
|
13
|
+
return child;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
};
|
|
18
|
+
const ensureFirstElementChild = (node) => {
|
|
19
|
+
if (!node || typeof node !== 'object') {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const prototype = Object.getPrototypeOf(node);
|
|
23
|
+
if (!prototype || Object.prototype.hasOwnProperty.call(prototype, 'firstElementChild')) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
Object.defineProperty(prototype, 'firstElementChild', {
|
|
27
|
+
configurable: true,
|
|
28
|
+
get: getFirstElementChild,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
class BrowserLikeXmlDomParser extends DOMParser {
|
|
32
|
+
parseFromString(source, mimeType) {
|
|
33
|
+
const xmlDocument = super.parseFromString(source, mimeType);
|
|
34
|
+
ensureFirstElementChild(xmlDocument);
|
|
35
|
+
ensureFirstElementChild(xmlDocument.documentElement);
|
|
36
|
+
return xmlDocument;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const toErrorPayload = (id, error) => {
|
|
40
|
+
if (error instanceof Error) {
|
|
41
|
+
return {
|
|
42
|
+
id,
|
|
43
|
+
ok: false,
|
|
44
|
+
message: error.message,
|
|
45
|
+
stack: error.stack,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
id,
|
|
50
|
+
ok: false,
|
|
51
|
+
message: String(error),
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
const installDomRuntime = () => {
|
|
55
|
+
const { window } = parseHTML('<!doctype html><html><head></head><body></body></html>');
|
|
56
|
+
const runtime = globalThis;
|
|
57
|
+
runtime.window = window;
|
|
58
|
+
runtime.document = window.document;
|
|
59
|
+
// docx-preview expects browser XML namespace behavior where `<w:body>` has
|
|
60
|
+
// localName === "body"; linkedom keeps prefixes, so XML parsing uses xmldom.
|
|
61
|
+
runtime.DOMParser = BrowserLikeXmlDomParser;
|
|
62
|
+
runtime.Node = window.Node;
|
|
63
|
+
runtime.Element = window.Element;
|
|
64
|
+
runtime.HTMLElement = window.HTMLElement;
|
|
65
|
+
runtime.DocumentFragment = window.DocumentFragment;
|
|
66
|
+
runtime.XMLSerializer = XMLSerializer;
|
|
67
|
+
return window.document;
|
|
68
|
+
};
|
|
69
|
+
const renderDocxToHtml = async (request) => {
|
|
70
|
+
const document = installDomRuntime();
|
|
71
|
+
const styleContainer = document.createElement('div');
|
|
72
|
+
const bodyContainer = document.createElement('div');
|
|
73
|
+
document.body.append(styleContainer, bodyContainer);
|
|
74
|
+
await renderAsync(request.buffer, bodyContainer, styleContainer, {
|
|
75
|
+
...request.options,
|
|
76
|
+
// Tab stop calculation depends on browser layout APIs and cannot be trusted in Worker DOM.
|
|
77
|
+
experimental: false,
|
|
78
|
+
// Worker output must inline binary assets because the worker is terminated immediately.
|
|
79
|
+
useBase64URL: true,
|
|
80
|
+
});
|
|
81
|
+
return `${styleContainer.innerHTML}${bodyContainer.innerHTML}`;
|
|
82
|
+
};
|
|
83
|
+
ctx.addEventListener('message', async (event) => {
|
|
84
|
+
const request = event.data;
|
|
85
|
+
try {
|
|
86
|
+
const html = await renderDocxToHtml(request);
|
|
87
|
+
ctx.postMessage({
|
|
88
|
+
id: request.id,
|
|
89
|
+
ok: true,
|
|
90
|
+
html,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
ctx.postMessage(toErrorPayload(request.id, error));
|
|
95
|
+
}
|
|
96
|
+
});
|
package/dist/source.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { FileViewerFileRef, FileViewerSource, NormalizedFileViewerSource } from './types';
|
|
2
|
+
export type FileViewerReadResult = string | ArrayBuffer | undefined | null;
|
|
3
|
+
export declare const DEFAULT_FILE_VIEWER_SOURCE_FILENAME = "preview.bin";
|
|
4
|
+
export declare const normalizeFileExtension: (extension: string) => string;
|
|
5
|
+
export declare const decodeFilename: (name: string) => string;
|
|
6
|
+
export declare const getExtension: (name: string) => string;
|
|
7
|
+
export declare const normalizeFilename: (value: string | undefined, fallback?: string) => string;
|
|
8
|
+
export declare const resolveFileViewerSourceFilename: ({ filename, file, url, fallback, }: {
|
|
9
|
+
filename?: string;
|
|
10
|
+
file?: FileViewerFileRef;
|
|
11
|
+
url?: string;
|
|
12
|
+
fallback?: string;
|
|
13
|
+
}) => string;
|
|
14
|
+
export declare const normalizeSource: (source: FileViewerSource) => NormalizedFileViewerSource;
|
|
15
|
+
export declare const wrapFileViewerFileRef: (data: FileViewerFileRef, filename?: string) => File;
|
|
16
|
+
export declare const readFileViewerBuffer: (file: Blob) => Promise<ArrayBuffer>;
|
|
17
|
+
export declare const readFileViewerDataUrl: (source: Blob | ArrayBuffer) => Promise<string>;
|
|
18
|
+
export declare const readFileViewerText: (source: Blob | ArrayBuffer, encoding?: string) => Promise<string>;
|