@file-viewer/renderer-word 2.1.9 → 2.1.11

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 CHANGED
@@ -32,7 +32,7 @@ Use `@file-viewer/preset-all` when you want the same complete matrix as the offi
32
32
  ## Capabilities
33
33
 
34
34
  - DOCX / DOCM / DOTX / DOTM use the self-maintained `@file-viewer/docx` engine with Worker parsing, continuous reading layout, cached TOC fields, and async batched rendering.
35
- - DOC / DOT use `msdoc-viewer` with a Word-like paper surface, zoom, print, and HTML export adapters.
35
+ - DOC / DOT use `@file-viewer/doc` with a Word-like paper surface, zoom, print, and HTML export adapters.
36
36
  - RTF uses `rtf.js`; ODT / ODP read `content.xml` from OpenDocument packages for safe structure previews.
37
37
  - The renderer reuses core search, zoom, print, export, lifecycle, and operation APIs.
38
38
 
@@ -56,4 +56,4 @@ const options = {
56
56
 
57
57
  ## Migration
58
58
 
59
- `@file-viewer/core` no longer depends on `@file-viewer/docx`, `msdoc-viewer`, `rtf.js`, `linkedom`, or `@xmldom/xmldom` directly. Install and pass this renderer for full Word preview, or use `@file-viewer/preset-all`.
59
+ `@file-viewer/core` no longer depends on `@file-viewer/docx`, `@file-viewer/doc`, `rtf.js`, `linkedom`, or `@xmldom/xmldom` directly. Install and pass this renderer for full Word preview, or use `@file-viewer/preset-all`.
package/README.md CHANGED
@@ -32,7 +32,7 @@ const options = {
32
32
  ## 能力边界
33
33
 
34
34
  - DOCX / DOCM / DOTX / DOTM 使用自研 `@file-viewer/docx`,默认 Worker 解析、连续流式阅读、目录字段缓存和异步分批渲染。
35
- - DOC / DOT 使用 `msdoc-viewer`,并套用 Word 风格纸张阅读面、缩放、打印和 HTML 导出适配。
35
+ - DOC / DOT 使用 `@file-viewer/doc`,并套用 Word 风格纸张阅读面、缩放、打印和 HTML 导出适配。
36
36
  - RTF 使用 `rtf.js`,ODT / ODP 读取 OpenDocument 包内 `content.xml` 做安全结构预览。
37
37
  - 继续复用 core 的统一搜索、缩放、打印、导出、生命周期和操作能力。
38
38
 
@@ -56,4 +56,4 @@ const options = {
56
56
 
57
57
  ## 迁移说明
58
58
 
59
- `@file-viewer/core` 已不再直接依赖 `@file-viewer/docx`、`msdoc-viewer`、`rtf.js`、`linkedom` 或 `@xmldom/xmldom`。需要 Word 完整预览时,请安装本包并通过 `renderers` 传入,或使用 `@file-viewer/preset-all`。
59
+ `@file-viewer/core` 已不再直接依赖 `@file-viewer/docx`、`@file-viewer/doc`、`rtf.js`、`linkedom` 或 `@xmldom/xmldom`。需要 Word 完整预览时,请安装本包并通过 `renderers` 传入,或使用 `@file-viewer/preset-all`。
package/dist/wordDoc.js CHANGED
@@ -1,4 +1,4 @@
1
- import { defaultMsDocCss, parseMsDocToHtml } from 'msdoc-viewer';
1
+ import { defaultMsDocCss, parseMsDocToHtml } from '@file-viewer/doc';
2
2
  import { applyPrintPageSize, buildPrintPageStyle, createFileViewerZoomChangeEmitter as createZoomChangeEmitter, formatCssPixels, registerFileViewerZoomProvider, unregisterFileViewerZoomProvider, } from '@file-viewer/core';
3
3
  const PAGE_BREAK_MARKER = '<span class="msdoc-page-break"></span>';
4
4
  const EMPTY_PAGE_HTML = '<p class="msdoc-paragraph"><br></p>';
package/dist/wordDocx.js CHANGED
@@ -4,13 +4,11 @@ const DOCX_DEFAULT_PAGE_SIZE = {
4
4
  width: 794,
5
5
  height: 1123
6
6
  };
7
- const DOCX_PROGRESSIVE_BATCH_SIZE = 2;
8
- const DOCX_WORKER_TIMEOUT = 5000;
9
7
  const DOCX_WORKER_UNSAFE_PROTOCOLS = new Set(['file:', 'about:', 'data:']);
10
8
  const DOCX_MIN_SCALE = 0.24;
11
9
  const DOCX_MAX_SCALE = 3;
12
10
  const DOCX_ZOOM_STEP = 0.15;
13
- const DOCX_EMU_PER_CSS_PIXEL = 9525;
11
+ const DOCX_VENDOR_ASSET_VERSION = '0.3.15';
14
12
  const ZIP_SIGNATURE_PK = 0x504b;
15
13
  const loadLibrary = (() => {
16
14
  const loader = {
@@ -72,8 +70,14 @@ const shouldUseDocxWorker = (target, docxOptions) => {
72
70
  }
73
71
  return !DOCX_WORKER_UNSAFE_PROTOCOLS.has(getTargetProtocol(target));
74
72
  };
73
+ const appendDocxVendorAssetVersion = (url, explicitUrl) => {
74
+ if (!url || explicitUrl) {
75
+ return url;
76
+ }
77
+ return `${url}${url.includes('?') ? '&' : '?'}file-viewer-docx=${DOCX_VENDOR_ASSET_VERSION}`;
78
+ };
75
79
  const createDocxOptions = (target, context, notifyProgressiveRender) => {
76
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
80
+ var _a, _b;
77
81
  const docxOptions = (_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.docx;
78
82
  const documentBaseUrl = target.ownerDocument.URL || undefined;
79
83
  const useWorker = shouldUseDocxWorker(target, docxOptions);
@@ -83,44 +87,56 @@ const createDocxOptions = (target, context, notifyProgressiveRender) => {
83
87
  notifyProgressiveRender();
84
88
  }
85
89
  };
86
- return {
87
- // Word 会写入 autoSpaceDN/autoSpaceDE 等兼容标签;生产预览保持静默,避免 DOCX 调试告警刷屏。
88
- debug: false,
89
- experimental: false,
90
+ const options = {
90
91
  useWorker,
91
- workerUrl: useWorker
92
- ? resolveFileViewerDocxWorkerUrl(docxOptions, documentBaseUrl)
93
- : undefined,
94
- workerJsZipUrl: useWorker
95
- ? resolveFileViewerDocxWorkerJsZipUrl(docxOptions, documentBaseUrl)
96
- : undefined,
97
- workerFallback: true,
98
- workerTimeout: (_b = docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.workerTimeout) !== null && _b !== void 0 ? _b : DOCX_WORKER_TIMEOUT,
99
- renderPageBatchSize: (_c = docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.renderPageBatchSize) !== null && _c !== void 0 ? _c : ((docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.progressive) === false ? Number.MAX_SAFE_INTEGER : DOCX_PROGRESSIVE_BATCH_SIZE),
100
- renderYieldEveryMs: (_d = docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.renderYieldEveryMs) !== null && _d !== void 0 ? _d : 16,
101
- strictWordCompatibility: (_e = docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.strictWordCompatibility) !== null && _e !== void 0 ? _e : true,
102
- paginationTolerance: (_f = docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.paginationTolerance) !== null && _f !== void 0 ? _f : 2,
103
92
  breakPages: usePagedLayout,
104
- maxDynamicPaginationPasses: usePagedLayout
105
- ? (_g = docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.maxDynamicPaginationPasses) !== null && _g !== void 0 ? _g : 1000
106
- : 0,
107
- awaitLayout: (_h = docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.awaitLayout) !== null && _h !== void 0 ? _h : usePagedLayout,
108
- preserveComplexFieldResults: (_j = docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.preserveComplexFieldResults) !== null && _j !== void 0 ? _j : true,
109
- updatePageReferences: (_k = docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.updatePageReferences) !== null && _k !== void 0 ? _k : false,
110
- hideWebHiddenContent: (_l = docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.hideWebHiddenContent) !== null && _l !== void 0 ? _l : false,
111
- ignoreLastRenderedPageBreak: (_m = docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.ignoreLastRenderedPageBreak) !== null && _m !== void 0 ? _m : !usePagedLayout,
93
+ ignoreLastRenderedPageBreak: (_b = docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.ignoreLastRenderedPageBreak) !== null && _b !== void 0 ? _b : !usePagedLayout,
112
94
  progress
113
95
  };
96
+ if (useWorker) {
97
+ options.workerUrl = appendDocxVendorAssetVersion(resolveFileViewerDocxWorkerUrl(docxOptions, documentBaseUrl), !!(docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.workerUrl));
98
+ options.workerJsZipUrl = appendDocxVendorAssetVersion(resolveFileViewerDocxWorkerJsZipUrl(docxOptions, documentBaseUrl), !!(docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.workerJsZipUrl));
99
+ }
100
+ if ((docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.workerTimeout) !== undefined) {
101
+ options.workerTimeout = docxOptions.workerTimeout;
102
+ }
103
+ if ((docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.renderPageBatchSize) !== undefined) {
104
+ options.renderPageBatchSize = docxOptions.renderPageBatchSize;
105
+ }
106
+ else if ((docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.progressive) === false) {
107
+ options.renderPageBatchSize = Number.MAX_SAFE_INTEGER;
108
+ }
109
+ if ((docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.renderYieldEveryMs) !== undefined) {
110
+ options.renderYieldEveryMs = docxOptions.renderYieldEveryMs;
111
+ }
112
+ if ((docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.strictWordCompatibility) !== undefined) {
113
+ options.strictWordCompatibility = docxOptions.strictWordCompatibility;
114
+ }
115
+ if ((docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.paginationTolerance) !== undefined) {
116
+ options.paginationTolerance = docxOptions.paginationTolerance;
117
+ }
118
+ if ((docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.maxDynamicPaginationPasses) !== undefined) {
119
+ options.maxDynamicPaginationPasses = docxOptions.maxDynamicPaginationPasses;
120
+ }
121
+ if ((docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.awaitLayout) !== undefined) {
122
+ options.awaitLayout = docxOptions.awaitLayout;
123
+ }
124
+ if ((docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.preserveComplexFieldResults) !== undefined) {
125
+ options.preserveComplexFieldResults = docxOptions.preserveComplexFieldResults;
126
+ }
127
+ if ((docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.updatePageReferences) !== undefined) {
128
+ options.updatePageReferences = docxOptions.updatePageReferences;
129
+ }
130
+ if ((docxOptions === null || docxOptions === void 0 ? void 0 : docxOptions.hideWebHiddenContent) !== undefined) {
131
+ options.hideWebHiddenContent = docxOptions.hideWebHiddenContent;
132
+ }
133
+ return options;
114
134
  };
115
135
  const isTargetHTMLElement = (value, target) => {
116
136
  var _a;
117
137
  const HTMLElementCtor = (_a = getTargetWindow(target)) === null || _a === void 0 ? void 0 : _a.HTMLElement;
118
138
  return HTMLElementCtor ? value instanceof HTMLElementCtor : value instanceof HTMLElement;
119
139
  };
120
- const shouldPaginateOversizedDocxSections = (context) => {
121
- var _a, _b;
122
- 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;
123
- };
124
140
  const DOCX_RESPONSIVE_CSS = `
125
141
  .docx-fit-viewer {
126
142
  box-sizing: border-box;
@@ -172,616 +188,13 @@ const DOCX_RESPONSIVE_CSS = `
172
188
  position: relative;
173
189
  z-index: 1;
174
190
  }
175
- .docx-fit-viewer .docx-vml-watermark {
176
- position: absolute;
177
- inset: 0;
178
- z-index: 0;
179
- width: 100%;
180
- height: 100%;
181
- object-fit: cover;
182
- opacity: 0.28;
183
- filter: saturate(0.72) brightness(1.24);
184
- pointer-events: none;
185
- user-select: none;
186
- }
187
- .docx-fit-viewer .docx-vml-fallback,
188
- .docx-fit-viewer .docx-chart-fallback {
189
- display: block;
190
- max-width: 100%;
191
- margin: 12px auto;
192
- break-inside: avoid;
193
- page-break-inside: avoid;
194
- }
195
- .docx-fit-viewer .docx-vml-fallback {
196
- text-align: center;
197
- }
198
- .docx-fit-viewer .docx-vml-fallback img {
199
- display: block;
200
- max-width: 100%;
201
- height: auto;
202
- margin: 0 auto;
203
- }
204
- .docx-fit-viewer .docx-chart-fallback {
205
- box-sizing: border-box;
206
- overflow: hidden;
207
- border: 1px solid #d7dee8;
208
- border-radius: 8px;
209
- background: #ffffff;
210
- box-shadow: 0 1px 6px rgba(15, 23, 42, 0.08);
211
- }
212
- .docx-fit-viewer .docx-chart-fallback svg {
213
- display: block;
214
- width: 100%;
215
- height: auto;
216
- }
217
191
  `;
218
- function getXmlLocalName(node) {
219
- return node.localName || node.tagName.split(':').pop() || node.tagName;
220
- }
221
- function getXmlElements(root, localName) {
222
- return Array.from(root.querySelectorAll('*')).filter(element => getXmlLocalName(element) === localName);
223
- }
224
- function getFirstXmlElement(root, localName) {
225
- return getXmlElements(root, localName)[0];
226
- }
227
- function getXmlAttribute(element, name, namespace) {
228
- return (namespace ? element.getAttributeNS(namespace, name) : null)
229
- || element.getAttribute(name)
230
- || element.getAttribute(`r:${name}`);
231
- }
232
- function getXmlText(element) {
233
- if (!element) {
234
- return '';
235
- }
236
- return (element.textContent || '').replace(/\s+/g, ' ').trim();
237
- }
238
- function getXmlAncestor(element, localName) {
239
- let current = (element === null || element === void 0 ? void 0 : element.parentElement) || null;
240
- while (current) {
241
- if (getXmlLocalName(current) === localName) {
242
- return current;
243
- }
244
- current = current.parentElement;
245
- }
246
- return null;
247
- }
248
- function getParagraphIndex(element) {
249
- const paragraph = getXmlLocalName(element) === 'p' ? element : getXmlAncestor(element, 'p');
250
- if (!paragraph) {
251
- return undefined;
252
- }
253
- let index = 0;
254
- let sibling = paragraph.previousElementSibling;
255
- while (sibling) {
256
- if (getXmlLocalName(sibling) === 'p') {
257
- index += 1;
258
- }
259
- sibling = sibling.previousElementSibling;
260
- }
261
- return index;
262
- }
263
- function parseXml(xml, target) {
264
- var _a;
265
- const Parser = ((_a = getTargetWindow(target)) === null || _a === void 0 ? void 0 : _a.DOMParser) || globalThis.DOMParser;
266
- if (!Parser) {
267
- return null;
268
- }
269
- const doc = new Parser().parseFromString(xml, 'application/xml');
270
- if (doc.querySelector('parsererror')) {
271
- return null;
272
- }
273
- return doc;
274
- }
275
- function normalizeZipPath(path) {
276
- const segments = [];
277
- path.split('/').forEach(segment => {
278
- if (!segment || segment === '.') {
279
- return;
280
- }
281
- if (segment === '..') {
282
- segments.pop();
283
- return;
284
- }
285
- segments.push(segment);
286
- });
287
- return segments.join('/');
288
- }
289
- function getDirectoryName(path) {
290
- const index = path.lastIndexOf('/');
291
- return index >= 0 ? path.slice(0, index) : '';
292
- }
293
- function getRelationshipPath(partPath) {
294
- const directory = getDirectoryName(partPath);
295
- const name = partPath.slice(directory ? directory.length + 1 : 0);
296
- return normalizeZipPath(`${directory}/_rels/${name}.rels`);
297
- }
298
- function resolveRelationshipTarget(partPath, target) {
299
- if (target.startsWith('/')) {
300
- return normalizeZipPath(target.slice(1));
301
- }
302
- return normalizeZipPath(`${getDirectoryName(partPath)}/${target}`);
303
- }
304
- function getMimeType(path) {
305
- var _a;
306
- const extension = (_a = path.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
307
- switch (extension) {
308
- case 'png':
309
- return 'image/png';
310
- case 'jpg':
311
- case 'jpeg':
312
- return 'image/jpeg';
313
- case 'gif':
314
- return 'image/gif';
315
- case 'bmp':
316
- return 'image/bmp';
317
- case 'webp':
318
- return 'image/webp';
319
- case 'svg':
320
- return 'image/svg+xml';
321
- default:
322
- return 'application/octet-stream';
323
- }
324
- }
325
- function parseCssLengthToPixels(value) {
326
- if (!value) {
327
- return undefined;
328
- }
329
- const match = value.trim().match(/^(-?\d+(?:\.\d+)?)(px|pt|in|cm|mm)?$/i);
330
- if (!match) {
331
- return undefined;
332
- }
333
- const amount = Number(match[1]);
334
- if (!Number.isFinite(amount) || amount <= 0) {
335
- return undefined;
336
- }
337
- const unit = (match[2] || 'px').toLowerCase();
338
- switch (unit) {
339
- case 'pt':
340
- return amount * 96 / 72;
341
- case 'in':
342
- return amount * 96;
343
- case 'cm':
344
- return amount * 96 / 2.54;
345
- case 'mm':
346
- return amount * 96 / 25.4;
347
- default:
348
- return amount;
349
- }
350
- }
351
- function parseStyleDeclaration(style) {
352
- const declarations = new Map();
353
- if (!style) {
354
- return declarations;
355
- }
356
- style.split(';').forEach(declaration => {
357
- const separator = declaration.indexOf(':');
358
- if (separator <= 0) {
359
- return;
360
- }
361
- declarations.set(declaration.slice(0, separator).trim().toLowerCase(), declaration.slice(separator + 1).trim());
362
- });
363
- return declarations;
364
- }
365
- function parseShapeSize(style) {
366
- const declarations = parseStyleDeclaration(style);
367
- return {
368
- width: parseCssLengthToPixels(declarations.get('width')),
369
- height: parseCssLengthToPixels(declarations.get('height'))
370
- };
371
- }
372
- function getExtentSize(element) {
373
- const inline = getXmlAncestor(element, 'inline') || getXmlAncestor(element, 'anchor');
374
- const extent = inline ? getFirstXmlElement(inline, 'extent') : undefined;
375
- const width = Number(extent === null || extent === void 0 ? void 0 : extent.getAttribute('cx'));
376
- const height = Number(extent === null || extent === void 0 ? void 0 : extent.getAttribute('cy'));
377
- return {
378
- width: Number.isFinite(width) && width > 0 ? width / DOCX_EMU_PER_CSS_PIXEL : undefined,
379
- height: Number.isFinite(height) && height > 0 ? height / DOCX_EMU_PER_CSS_PIXEL : undefined
380
- };
381
- }
382
- async function readDocxRelationships(zip, partPath, target) {
383
- const relFile = zip.file(getRelationshipPath(partPath));
384
- const relationships = new Map();
385
- if (!relFile) {
386
- return relationships;
387
- }
388
- const relDoc = parseXml(await relFile.async('text'), target);
389
- if (!relDoc) {
390
- return relationships;
391
- }
392
- getXmlElements(relDoc, 'Relationship').forEach(element => {
393
- const id = element.getAttribute('Id');
394
- const relationshipTarget = element.getAttribute('Target');
395
- if (!id || !relationshipTarget) {
396
- return;
397
- }
398
- relationships.set(id, {
399
- id,
400
- type: element.getAttribute('Type') || '',
401
- target: relationshipTarget,
402
- targetMode: element.getAttribute('TargetMode') || undefined
403
- });
404
- });
405
- return relationships;
406
- }
407
- async function getDataUrlFromZip(zip, sourcePath) {
408
- const file = zip.file(sourcePath);
409
- if (!file) {
410
- return undefined;
411
- }
412
- const base64 = await file.async('base64');
413
- return `data:${getMimeType(sourcePath)};base64,${base64}`;
414
- }
415
- function getDocxPartPaths(zip) {
416
- return Object.keys(zip.files)
417
- .filter(path => path === 'word/document.xml'
418
- || /^word\/header\d+\.xml$/i.test(path)
419
- || /^word\/footer\d+\.xml$/i.test(path))
420
- .sort((left, right) => {
421
- if (left === 'word/document.xml') {
422
- return -1;
423
- }
424
- if (right === 'word/document.xml') {
425
- return 1;
426
- }
427
- return left.localeCompare(right);
428
- });
429
- }
430
- function inferVmlFallbackRole(partPath, shape, style) {
431
- const shapeId = `${(shape === null || shape === void 0 ? void 0 : shape.getAttribute('id')) || ''} ${(shape === null || shape === void 0 ? void 0 : shape.getAttribute('o:spid')) || ''}`.toLowerCase();
432
- const normalizedStyle = (style || '').toLowerCase();
433
- if (partPath.includes('/header')
434
- && (shapeId.includes('watermark') || normalizedStyle.includes('z-index:-') || normalizedStyle.includes('mso-position-horizontal:center'))) {
435
- return 'watermark';
436
- }
437
- if ((shape === null || shape === void 0 ? void 0 : shape.getAttribute('o:ole')) === 't' || (shape === null || shape === void 0 ? void 0 : shape.getAttribute('ole')) === 't') {
438
- return 'ole-preview';
439
- }
440
- return 'vml-image';
441
- }
442
- async function collectDocxVmlFallbacks(zip, target) {
443
- var _a;
444
- const fallbacks = [];
445
- const seen = new Set();
446
- for (const partPath of getDocxPartPaths(zip)) {
447
- const partFile = zip.file(partPath);
448
- if (!partFile) {
449
- continue;
450
- }
451
- const doc = parseXml(await partFile.async('text'), target);
452
- if (!doc) {
453
- continue;
454
- }
455
- const relationships = await readDocxRelationships(zip, partPath, target);
456
- for (const imagedata of getXmlElements(doc, 'imagedata')) {
457
- const relationshipId = getXmlAttribute(imagedata, 'id', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
458
- const relationship = relationshipId ? relationships.get(relationshipId) : undefined;
459
- if (!relationship || relationship.targetMode === 'External' || !relationship.type.includes('/image')) {
460
- continue;
461
- }
462
- const sourcePath = resolveRelationshipTarget(partPath, relationship.target);
463
- const dataUrl = await getDataUrlFromZip(zip, sourcePath);
464
- if (!dataUrl) {
465
- continue;
466
- }
467
- const shape = getXmlAncestor(imagedata, 'shape');
468
- const style = (shape === null || shape === void 0 ? void 0 : shape.getAttribute('style')) || undefined;
469
- const role = inferVmlFallbackRole(partPath, shape, style);
470
- const key = role === 'watermark'
471
- ? `${role}:${sourcePath}`
472
- : `${role}:${partPath}:${sourcePath}:${(_a = getParagraphIndex(imagedata)) !== null && _a !== void 0 ? _a : 'end'}`;
473
- if (seen.has(key)) {
474
- continue;
475
- }
476
- seen.add(key);
477
- fallbacks.push({
478
- role,
479
- key,
480
- dataUrl,
481
- sourcePath,
482
- partPath,
483
- title: imagedata.getAttribute('o:title') || imagedata.getAttribute('title') || undefined,
484
- style,
485
- ...parseShapeSize(style),
486
- paragraphIndex: partPath === 'word/document.xml' ? getParagraphIndex(imagedata) : undefined
487
- });
488
- }
489
- }
490
- return fallbacks;
491
- }
492
- function parseCacheValues(element) {
493
- if (!element) {
494
- return [];
495
- }
496
- return getXmlElements(element, 'pt')
497
- .sort((left, right) => Number(left.getAttribute('idx') || 0) - Number(right.getAttribute('idx') || 0))
498
- .map(point => getXmlText(getFirstXmlElement(point, 'v')))
499
- .filter(Boolean);
500
- }
501
- function parseChartSeries(series) {
502
- const name = parseCacheValues(getFirstXmlElement(getFirstXmlElement(series, 'tx') || series, 'strCache'))[0]
503
- || parseCacheValues(getFirstXmlElement(getFirstXmlElement(series, 'tx') || series, 'numCache'))[0]
504
- || 'Series';
505
- const categories = parseCacheValues(getFirstXmlElement(getFirstXmlElement(series, 'cat') || series, 'strCache'));
506
- const numericCategories = parseCacheValues(getFirstXmlElement(getFirstXmlElement(series, 'cat') || series, 'numCache'));
507
- const values = parseCacheValues(getFirstXmlElement(getFirstXmlElement(series, 'val') || series, 'numCache'))
508
- .map(value => Number(value))
509
- .filter(value => Number.isFinite(value));
510
- return {
511
- name,
512
- categories: categories.length ? categories : numericCategories,
513
- values
514
- };
515
- }
516
- function parseChartFallback(chartDoc, sourcePath, chartElement, paragraphIndex) {
517
- const chartTypeElement = ['lineChart', 'barChart', 'pieChart', 'areaChart', 'scatterChart']
518
- .map(type => getXmlElements(chartDoc, type)[0])
519
- .find(Boolean);
520
- const chartType = chartTypeElement ? getXmlLocalName(chartTypeElement) : 'chart';
521
- const title = getXmlText(getFirstXmlElement(getFirstXmlElement(chartDoc, 'title') || chartDoc, 't'))
522
- || sourcePath.split('/').pop() || 'Chart';
523
- const series = (chartTypeElement ? getXmlElements(chartTypeElement, 'ser') : getXmlElements(chartDoc, 'ser'))
524
- .map(parseChartSeries)
525
- .filter(item => item.values.length);
526
- const { width, height } = getExtentSize(chartElement);
527
- if (!series.length) {
528
- return undefined;
529
- }
530
- return {
531
- key: `chart:${sourcePath}:${paragraphIndex !== null && paragraphIndex !== void 0 ? paragraphIndex : 'end'}`,
532
- title,
533
- type: chartType,
534
- sourcePath,
535
- series,
536
- width,
537
- height,
538
- paragraphIndex
539
- };
540
- }
541
- async function collectDocxChartFallbacks(zip, target) {
542
- const partPath = 'word/document.xml';
543
- const documentFile = zip.file(partPath);
544
- if (!documentFile) {
545
- return [];
546
- }
547
- const documentDoc = parseXml(await documentFile.async('text'), target);
548
- if (!documentDoc) {
549
- return [];
550
- }
551
- const relationships = await readDocxRelationships(zip, partPath, target);
552
- const fallbacks = [];
553
- const seen = new Set();
554
- for (const chart of getXmlElements(documentDoc, 'chart')) {
555
- const relationshipId = getXmlAttribute(chart, 'id', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
556
- const relationship = relationshipId ? relationships.get(relationshipId) : undefined;
557
- if (!relationship || relationship.targetMode === 'External' || !relationship.type.includes('/chart')) {
558
- continue;
559
- }
560
- const sourcePath = resolveRelationshipTarget(partPath, relationship.target);
561
- const chartFile = zip.file(sourcePath);
562
- if (!chartFile) {
563
- continue;
564
- }
565
- const chartDoc = parseXml(await chartFile.async('text'), target);
566
- const paragraphIndex = getParagraphIndex(chart);
567
- const fallback = chartDoc ? parseChartFallback(chartDoc, sourcePath, chart, paragraphIndex) : undefined;
568
- if (!fallback || seen.has(fallback.key)) {
569
- continue;
570
- }
571
- seen.add(fallback.key);
572
- fallbacks.push(fallback);
573
- }
574
- return fallbacks;
575
- }
576
- function escapeHtml(value) {
577
- return value
578
- .replace(/&/g, '&amp;')
579
- .replace(/</g, '&lt;')
580
- .replace(/>/g, '&gt;')
581
- .replace(/"/g, '&quot;');
582
- }
583
- function buildChartSvg(chart) {
584
- var _a;
585
- const width = Math.max(360, Math.round(chart.width || 520));
586
- const height = Math.max(220, Math.round(chart.height || 320));
587
- const padding = { top: 52, right: 30, bottom: 56, left: 54 };
588
- const plotWidth = width - padding.left - padding.right;
589
- const plotHeight = height - padding.top - padding.bottom;
590
- const allValues = chart.series.flatMap(item => item.values);
591
- const maxValue = Math.max(...allValues, 1);
592
- const minValue = Math.min(...allValues, 0);
593
- const valueSpan = Math.max(maxValue - minValue, 1);
594
- const colors = ['#2563eb', '#10b981', '#f97316', '#8b5cf6', '#ef4444'];
595
- const maxPoints = Math.max(...chart.series.map(item => item.values.length), 1);
596
- const pointX = (index) => padding.left + (maxPoints === 1 ? plotWidth / 2 : index * plotWidth / (maxPoints - 1));
597
- const pointY = (value) => padding.top + plotHeight - ((value - minValue) / valueSpan) * plotHeight;
598
- const seriesPaths = chart.series.map((series, seriesIndex) => {
599
- const color = colors[seriesIndex % colors.length];
600
- const points = series.values.map((value, index) => `${pointX(index).toFixed(1)},${pointY(value).toFixed(1)}`).join(' ');
601
- const circles = series.values.map((value, index) => {
602
- const x = pointX(index).toFixed(1);
603
- const y = pointY(value).toFixed(1);
604
- return `<circle cx="${x}" cy="${y}" r="3.5" fill="${color}"><title>${escapeHtml(series.name)}: ${escapeHtml(String(value))}</title></circle>`;
605
- }).join('');
606
- return `<polyline points="${points}" fill="none" stroke="${color}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>${circles}`;
607
- }).join('');
608
- const categories = ((_a = chart.series.find(item => item.categories.length)) === null || _a === void 0 ? void 0 : _a.categories) || [];
609
- const labels = categories.slice(0, maxPoints).map((label, index) => {
610
- const x = pointX(index);
611
- const shown = label.length > 14 ? `${label.slice(0, 13)}...` : label;
612
- return `<text x="${x.toFixed(1)}" y="${height - 22}" text-anchor="middle" fill="#64748b" font-size="11">${escapeHtml(shown)}</text>`;
613
- }).join('');
614
- const legend = chart.series.slice(0, 5).map((series, index) => {
615
- const x = padding.left + index * 98;
616
- const y = 30;
617
- const color = colors[index % colors.length];
618
- return `<g transform="translate(${x} ${y})"><rect width="10" height="10" rx="2" fill="${color}"/><text x="15" y="9" fill="#475569" font-size="11">${escapeHtml(series.name)}</text></g>`;
619
- }).join('');
620
- return `<svg viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeHtml(chart.title)}">
621
- <rect x="0" y="0" width="${width}" height="${height}" rx="8" fill="#ffffff"/>
622
- <text x="${padding.left}" y="22" fill="#0f172a" font-size="15" font-weight="700">${escapeHtml(chart.title)}</text>
623
- ${legend}
624
- <line x1="${padding.left}" y1="${padding.top + plotHeight}" x2="${padding.left + plotWidth}" y2="${padding.top + plotHeight}" stroke="#cbd5e1"/>
625
- <line x1="${padding.left}" y1="${padding.top}" x2="${padding.left}" y2="${padding.top + plotHeight}" stroke="#cbd5e1"/>
626
- <text x="${padding.left - 8}" y="${padding.top + 4}" text-anchor="end" fill="#64748b" font-size="11">${escapeHtml(maxValue.toFixed(1))}</text>
627
- <text x="${padding.left - 8}" y="${padding.top + plotHeight}" text-anchor="end" fill="#64748b" font-size="11">${escapeHtml(minValue.toFixed(1))}</text>
628
- ${seriesPaths}
629
- ${labels}
630
- </svg>`;
631
- }
632
- function getDocxSections(target) {
633
- return Array.from(target.querySelectorAll('section.docx'));
634
- }
635
- function getDocxArticles(target) {
636
- return Array.from(target.querySelectorAll('section.docx > article'));
637
- }
638
- function getDocxParagraphAnchor(target, paragraphIndex) {
639
- if (paragraphIndex === undefined) {
640
- return undefined;
641
- }
642
- const paragraphs = Array.from(target.querySelectorAll('section.docx > article p'));
643
- return paragraphs[Math.min(Math.max(paragraphIndex, 0), Math.max(paragraphs.length - 1, 0))];
644
- }
645
- function insertDocxFallback(target, node, paragraphIndex) {
646
- const anchor = getDocxParagraphAnchor(target, paragraphIndex);
647
- if (anchor) {
648
- anchor.after(node);
649
- return;
650
- }
651
- const article = getDocxArticles(target)[0];
652
- if (article) {
653
- article.appendChild(node);
654
- }
655
- }
656
- function injectDocxImageFallbacks(target, fallbacks) {
657
- const sections = getDocxSections(target);
658
- if (!sections.length) {
659
- return;
660
- }
661
- fallbacks.forEach(fallback => {
662
- if (fallback.role === 'watermark') {
663
- sections.forEach(section => {
664
- const image = target.ownerDocument.createElement('img');
665
- image.className = 'docx-vml-watermark';
666
- image.src = fallback.dataUrl;
667
- image.alt = fallback.title || '';
668
- image.dataset.docxFallback = fallback.key;
669
- section.prepend(image);
670
- });
671
- return;
672
- }
673
- const figure = target.ownerDocument.createElement('figure');
674
- figure.className = 'docx-vml-fallback';
675
- figure.dataset.docxFallback = fallback.key;
676
- if (fallback.width) {
677
- figure.style.width = `${Math.round(fallback.width)}px`;
678
- }
679
- const image = target.ownerDocument.createElement('img');
680
- image.src = fallback.dataUrl;
681
- image.alt = fallback.title || (fallback.role === 'ole-preview' ? 'Embedded object preview' : 'Document image');
682
- if (fallback.width) {
683
- image.style.width = `${Math.round(fallback.width)}px`;
684
- }
685
- if (fallback.height) {
686
- image.style.height = `${Math.round(fallback.height)}px`;
687
- }
688
- figure.appendChild(image);
689
- insertDocxFallback(target, figure, fallback.paragraphIndex);
690
- });
691
- }
692
- function injectDocxChartFallbacks(target, fallbacks) {
693
- fallbacks.forEach(fallback => {
694
- const figure = target.ownerDocument.createElement('figure');
695
- figure.className = 'docx-chart-fallback';
696
- figure.dataset.docxFallback = fallback.key;
697
- if (fallback.width) {
698
- figure.style.width = `${Math.round(fallback.width)}px`;
699
- }
700
- figure.innerHTML = buildChartSvg(fallback);
701
- insertDocxFallback(target, figure, fallback.paragraphIndex);
702
- });
703
- }
704
- async function enhanceDocxFallbacks(buffer, target) {
705
- try {
706
- const { default: JSZip } = await import('jszip');
707
- const zip = await JSZip.loadAsync(buffer.slice(0));
708
- const [imageFallbacks, chartFallbacks] = await Promise.all([
709
- collectDocxVmlFallbacks(zip, target),
710
- collectDocxChartFallbacks(zip, target)
711
- ]);
712
- if (imageFallbacks.length) {
713
- injectDocxImageFallbacks(target, imageFallbacks);
714
- }
715
- if (chartFallbacks.length) {
716
- injectDocxChartFallbacks(target, chartFallbacks);
717
- }
718
- }
719
- catch (error) {
720
- console.warn('[file-viewer] DOCX 兼容增强解析失败,已保留 @file-viewer/docx 原始渲染结果。', error);
721
- }
722
- }
723
192
  function installResponsiveStyle(target) {
724
193
  const style = target.ownerDocument.createElement('style');
725
194
  style.textContent = DOCX_RESPONSIVE_CSS;
726
195
  target.prepend(style);
727
196
  return style;
728
197
  }
729
- function clonePageShell(section, article, pageHeight) {
730
- const nextPage = section.cloneNode(false);
731
- nextPage.innerHTML = '';
732
- nextPage.dataset.docxPaginated = 'true';
733
- nextPage.style.minHeight = `${pageHeight}px`;
734
- nextPage.style.height = `${pageHeight}px`;
735
- nextPage.style.overflow = 'hidden';
736
- const nextArticle = article.cloneNode(false);
737
- nextPage.appendChild(nextArticle);
738
- Array.from(section.children).forEach(child => {
739
- if (child !== article) {
740
- nextPage.appendChild(child.cloneNode(true));
741
- }
742
- });
743
- return { page: nextPage, article: nextArticle };
744
- }
745
- function getDocxPageHeight(section) {
746
- var _a;
747
- const style = (_a = section.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.getComputedStyle(section);
748
- const minHeight = style ? parseFloat(style.minHeight) : 0;
749
- return Number.isFinite(minHeight) && minHeight > 0 ? minHeight : section.offsetHeight;
750
- }
751
- function paginateOversizedSections(target) {
752
- const wrapper = target.querySelector('.docx-wrapper');
753
- if (!wrapper) {
754
- return;
755
- }
756
- Array.from(wrapper.children).forEach(child => {
757
- if (!isTargetHTMLElement(child, target) || !child.matches('section.docx')) {
758
- return;
759
- }
760
- const article = child.querySelector(':scope > article');
761
- if (!isTargetHTMLElement(article, target)) {
762
- return;
763
- }
764
- const pageHeight = getDocxPageHeight(child);
765
- const originalNodes = Array.from(article.childNodes);
766
- if (!pageHeight || originalNodes.length < 2 || child.scrollHeight <= pageHeight * 1.15) {
767
- return;
768
- }
769
- // 新 DOCX 引擎会优先使用 Word 保存的分页;这里保留预览层兜底分页,覆盖缺少分页标记的旧文件。
770
- let current = clonePageShell(child, article, pageHeight);
771
- child.before(current.page);
772
- originalNodes.forEach(node => {
773
- current.article.appendChild(node);
774
- if (current.page.scrollHeight <= pageHeight + 1 || current.article.childNodes.length === 1) {
775
- return;
776
- }
777
- current.article.removeChild(node);
778
- current = clonePageShell(child, article, pageHeight);
779
- child.before(current.page);
780
- current.article.appendChild(node);
781
- });
782
- child.remove();
783
- });
784
- }
785
198
  function wrapDocxSections(target, pagedLayout) {
786
199
  const wrapper = target.querySelector('.docx-wrapper');
787
200
  if (!wrapper) {
@@ -799,12 +212,10 @@ function wrapDocxSections(target, pagedLayout) {
799
212
  });
800
213
  }
801
214
  function makeDocxResponsive(target, context) {
215
+ var _a, _b;
802
216
  target.classList.add('docx-fit-viewer');
803
217
  const style = installResponsiveStyle(target);
804
- const pagedLayout = shouldPaginateOversizedDocxSections(context);
805
- if (pagedLayout) {
806
- paginateOversizedSections(target);
807
- }
218
+ const pagedLayout = ((_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;
808
219
  const frames = wrapDocxSections(target, pagedLayout);
809
220
  const view = getTargetWindow(target);
810
221
  const ResizeObserverCtor = view === null || view === void 0 ? void 0 : view.ResizeObserver;
@@ -989,7 +400,6 @@ export default async function (buffer, target, context) {
989
400
  ...docxOptions
990
401
  });
991
402
  notifyProgressiveRender();
992
- await enhanceDocxFallbacks(buffer, target);
993
403
  const disposeResponsive = makeDocxResponsive(target, context);
994
404
  (_a = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _a === void 0 ? void 0 : _a.call(context, {
995
405
  includeDocumentStyles: false,
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@file-viewer/renderer-word",
3
- "version": "2.1.9",
3
+ "version": "2.1.11",
4
4
  "private": false,
5
5
  "type": "module",
6
- "description": "Standalone Word and compatible document renderer plugin for Flyfish File Viewer powered by @file-viewer/docx, msdoc-viewer, and RTF/ODF parsing.",
6
+ "description": "Standalone Word and compatible document renderer plugin for Flyfish File Viewer powered by @file-viewer/docx, @file-viewer/doc, and RTF/ODF parsing.",
7
7
  "keywords": [
8
8
  "file-viewer",
9
9
  "renderer",
@@ -57,10 +57,10 @@
57
57
  "LICENSE"
58
58
  ],
59
59
  "dependencies": {
60
- "@file-viewer/core": "^2.1.9",
61
- "@file-viewer/docx": "^0.3.12",
60
+ "@file-viewer/core": "^2.1.11",
61
+ "@file-viewer/doc": "^2.1.11",
62
+ "@file-viewer/docx": "^0.3.15",
62
63
  "jszip": "^3.10.1",
63
- "msdoc-viewer": "^0.2.0",
64
64
  "rtf.js": "^3.0.9"
65
65
  },
66
66
  "devDependencies": {