@file-viewer/core 2.0.0 → 2.0.2

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.
Files changed (111) hide show
  1. package/README.en.md +3 -3
  2. package/README.md +3 -3
  3. package/dist/assets.d.ts +2 -62
  4. package/dist/assets.js +5 -260
  5. package/dist/{options.d.ts → config/options.d.ts} +1 -1
  6. package/dist/{documentDom → features/document/dom}/anchors.d.ts +1 -1
  7. package/dist/{documentDom → features/document/dom}/providers.d.ts +1 -1
  8. package/dist/{documentEvents.d.ts → features/document/events.d.ts} +2 -2
  9. package/dist/{documentEvents.js → features/document/events.js} +3 -3
  10. package/dist/{document.d.ts → features/document/model.d.ts} +1 -1
  11. package/dist/{documentSearch.d.ts → features/document/search.d.ts} +1 -1
  12. package/dist/{documentSearch.js → features/document/search.js} +2 -2
  13. package/dist/{documentZoom.d.ts → features/document/zoom.d.ts} +1 -1
  14. package/dist/{documentZoom.js → features/document/zoom.js} +2 -2
  15. package/dist/{watermark.d.ts → features/watermark.d.ts} +1 -1
  16. package/dist/headless.d.ts +21 -21
  17. package/dist/headless.js +18 -13
  18. package/dist/index.d.ts +48 -48
  19. package/dist/index.js +23 -23
  20. package/dist/{lifecycleFacade.d.ts → lifecycle/facade.d.ts} +3 -3
  21. package/dist/{lifecycleFacade.js → lifecycle/facade.js} +1 -1
  22. package/dist/{operations.d.ts → lifecycle/operations.d.ts} +3 -3
  23. package/dist/{operations.js → lifecycle/operations.js} +8 -3
  24. package/dist/{export.d.ts → output/export.d.ts} +1 -1
  25. package/dist/platform/assets.d.ts +62 -0
  26. package/dist/platform/assets.js +260 -0
  27. package/dist/{presentation.d.ts → presentation/state.d.ts} +1 -1
  28. package/dist/{presentation.js → presentation/state.js} +3 -3
  29. package/dist/{capabilities.d.ts → registry/capabilities.d.ts} +1 -1
  30. package/dist/{capabilities.js → registry/capabilities.js} +1 -1
  31. package/dist/{registry.d.ts → registry/registry.d.ts} +1 -1
  32. package/dist/{registry.js → registry/registry.js} +1 -1
  33. package/dist/renderers/archive.d.ts +1 -1
  34. package/dist/renderers/archive.js +2 -2
  35. package/dist/renderers/archiveShared.js +1 -1
  36. package/dist/renderers/audio.d.ts +1 -1
  37. package/dist/renderers/cad.d.ts +1 -1
  38. package/dist/renderers/cad.js +3 -3
  39. package/dist/renderers/code.d.ts +1 -1
  40. package/dist/renderers/code.js +3 -3
  41. package/dist/renderers/data.d.ts +1 -1
  42. package/dist/renderers/data.js +1 -1
  43. package/dist/renderers/drawing.d.ts +1 -1
  44. package/dist/renderers/drawing.js +4 -4
  45. package/dist/renderers/eda.d.ts +1 -1
  46. package/dist/renderers/email.d.ts +1 -1
  47. package/dist/renderers/email.js +1 -1
  48. package/dist/renderers/epub.d.ts +1 -1
  49. package/dist/renderers/geo.d.ts +1 -1
  50. package/dist/renderers/geo.js +1 -1
  51. package/dist/renderers/image.d.ts +1 -1
  52. package/dist/renderers/image.js +2 -2
  53. package/dist/renderers/index.d.ts +1 -1
  54. package/dist/renderers/index.js +9 -4
  55. package/dist/renderers/markdown.d.ts +1 -1
  56. package/dist/renderers/markdown.js +3 -3
  57. package/dist/renderers/model.d.ts +1 -1
  58. package/dist/renderers/ofd.d.ts +1 -1
  59. package/dist/renderers/ofd.js +2 -2
  60. package/dist/renderers/openDocument.d.ts +1 -1
  61. package/dist/renderers/pdf.d.ts +1 -1
  62. package/dist/renderers/pdf.js +7 -5
  63. package/dist/renderers/pptx.d.ts +1 -1
  64. package/dist/renderers/pptx.js +19 -4
  65. package/dist/renderers/spreadsheet.d.ts +1 -1
  66. package/dist/renderers/spreadsheet.js +4 -4
  67. package/dist/renderers/typst.d.ts +1 -1
  68. package/dist/renderers/typst.js +5 -5
  69. package/dist/renderers/umd.d.ts +1 -1
  70. package/dist/renderers/video.d.ts +1 -1
  71. package/dist/renderers/wordDoc.d.ts +1 -1
  72. package/dist/renderers/wordDoc.js +3 -3
  73. package/dist/renderers/wordDocx.d.ts +1 -1
  74. package/dist/renderers/wordDocx.js +557 -4
  75. package/dist/{rendererDispatcher.d.ts → rendering/dispatcher.d.ts} +1 -1
  76. package/dist/{rendererDispatcher.js → rendering/dispatcher.js} +3 -3
  77. package/dist/{rendererHandler.d.ts → rendering/handler.d.ts} +3 -3
  78. package/dist/{rendererHandler.js → rendering/handler.js} +11 -6
  79. package/dist/{source.d.ts → source/index.d.ts} +1 -1
  80. package/dist/{sourceLoading.d.ts → source/loading.d.ts} +3 -3
  81. package/dist/{sourceLoading.js → source/loading.js} +3 -3
  82. package/dist/{viewer.d.ts → viewer/createViewer.d.ts} +1 -1
  83. package/dist/{viewer.js → viewer/createViewer.js} +17 -11
  84. package/dist/{loading.d.ts → viewer/loading.d.ts} +1 -1
  85. package/dist/{viewerOperations.d.ts → viewer/operations.d.ts} +1 -1
  86. package/dist/{viewerOperations.js → viewer/operations.js} +2 -2
  87. package/dist/{state.d.ts → viewer/state.d.ts} +1 -1
  88. package/dist/{state.js → viewer/state.js} +1 -1
  89. package/package.json +3 -3
  90. package/dist/documentDom.d.ts +0 -5
  91. package/dist/documentDom.js +0 -3
  92. /package/dist/{options.js → config/options.js} +0 -0
  93. /package/dist/{types.d.ts → contracts/types.d.ts} +0 -0
  94. /package/dist/{types.js → contracts/types.js} +0 -0
  95. /package/dist/{documentDom → features/document/dom}/anchors.js +0 -0
  96. /package/dist/{documentDom → features/document/dom}/index.d.ts +0 -0
  97. /package/dist/{documentDom → features/document/dom}/index.js +0 -0
  98. /package/dist/{documentDom → features/document/dom}/providers.js +0 -0
  99. /package/dist/{documentDom → features/document/dom}/scroll.d.ts +0 -0
  100. /package/dist/{documentDom → features/document/dom}/scroll.js +0 -0
  101. /package/dist/{document.js → features/document/model.js} +0 -0
  102. /package/dist/{watermark.js → features/watermark.js} +0 -0
  103. /package/dist/{export.js → output/export.js} +0 -0
  104. /package/dist/{printLayout.d.ts → output/printLayout.d.ts} +0 -0
  105. /package/dist/{printLayout.js → output/printLayout.js} +0 -0
  106. /package/dist/{worker.d.ts → platform/worker.d.ts} +0 -0
  107. /package/dist/{worker.js → platform/worker.js} +0 -0
  108. /package/dist/{formats.d.ts → registry/formats.d.ts} +0 -0
  109. /package/dist/{formats.js → registry/formats.js} +0 -0
  110. /package/dist/{source.js → source/index.js} +0 -0
  111. /package/dist/{loading.js → viewer/loading.js} +0 -0
@@ -1,7 +1,7 @@
1
- import { resolveFileViewerSpreadsheetWorkerUrl } from '../assets.js';
2
- import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider, } from '../documentDom.js';
3
- import { createFileViewerZoomChangeEmitter as createZoomChangeEmitter } from '../documentZoom.js';
4
- import { createFileViewerWorkerController, } from '../worker.js';
1
+ import { resolveFileViewerSpreadsheetWorkerUrl } from '../platform/assets.js';
2
+ import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider, } from '../features/document/dom';
3
+ import { createFileViewerZoomChangeEmitter as createZoomChangeEmitter } from '../features/document/zoom.js';
4
+ import { createFileViewerWorkerController, } from '../platform/worker.js';
5
5
  import { buildRows, clampWindowStart, collectWindowStarts, createEmptyVirtualState, DEFAULT_SHEET_DEFAULTS, displayCellKey, getDataKey, markWindowState, ROW_STATE_FIELD, RowState, WINDOW_SIZE, } from './spreadsheet/state.js';
6
6
  import { buildColumns, createTableConfig, detectIndexOffset, getDisplayColumns, getRowHeight, HEADER_HEIGHT, INDEX_COLUMN_WIDTH, normalizeCellStyle, normalizeRowHeight, } from './spreadsheet/view.js';
7
7
  const spreadsheetStyle = `
@@ -1,4 +1,4 @@
1
- import type { FileRenderContext, FileViewerRenderedInstance } from '../types';
1
+ import type { FileRenderContext, FileViewerRenderedInstance } from '../contracts/types';
2
2
  declare global {
3
3
  interface Window {
4
4
  __FLYFISH_TYPST_COMPILER_WASM_URL__?: string;
@@ -1,9 +1,9 @@
1
1
  import { $typst } from '@myriaddreamin/typst.ts';
2
- import { resolveFileViewerTypstCompilerWasmUrl, resolveFileViewerTypstRendererWasmUrl, } from '../assets.js';
3
- import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider, } from '../documentDom.js';
4
- import { createFileViewerZoomChangeEmitter } from '../documentZoom.js';
5
- import { formatCssPixels } from '../printLayout.js';
6
- import { readFileViewerText } from '../source.js';
2
+ import { resolveFileViewerTypstCompilerWasmUrl, resolveFileViewerTypstRendererWasmUrl, } from '../platform/assets.js';
3
+ import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider, } from '../features/document/dom';
4
+ import { createFileViewerZoomChangeEmitter } from '../features/document/zoom.js';
5
+ import { formatCssPixels } from '../output/printLayout.js';
6
+ import { readFileViewerText } from '../source';
7
7
  const typstStyle = `
8
8
  .typst-viewer{min-height:100%;overflow:auto;background:#eef1f4;color:#172033}
9
9
  .typst-toolbar{position:sticky;top:0;z-index:2;display:flex;min-height:52px;align-items:center;justify-content:space-between;gap:16px;padding:10px 18px;border-bottom:1px solid rgba(120,134,155,.18);background:rgba(248,250,252,.92);backdrop-filter:blur(16px)}
@@ -1,2 +1,2 @@
1
- import type { FileViewerRenderedInstance } from '../types';
1
+ import type { FileViewerRenderedInstance } from '../contracts/types';
2
2
  export default function renderUmd(buffer: ArrayBuffer, target: HTMLDivElement): Promise<FileViewerRenderedInstance>;
@@ -1,4 +1,4 @@
1
- import type { FileRenderContext, FileViewerRenderedInstance } from '../types';
1
+ import type { FileRenderContext, FileViewerRenderedInstance } from '../contracts/types';
2
2
  /**
3
3
  * Pure TypeScript video renderer.
4
4
  *
@@ -1,4 +1,4 @@
1
- import type { FileRenderContext, FileViewerRenderedInstance as AppWrapper } from '../types';
1
+ import type { FileRenderContext, FileViewerRenderedInstance as AppWrapper } from '../contracts/types';
2
2
  /**
3
3
  * 渲染 doc 文件
4
4
  */
@@ -1,7 +1,7 @@
1
1
  import { defaultMsDocCss, parseMsDocToHtml } from 'msdoc-viewer';
2
- import { applyPrintPageSize, buildPrintPageStyle, formatCssPixels } from '../printLayout.js';
3
- import { createFileViewerZoomChangeEmitter as createZoomChangeEmitter, } from '../documentZoom.js';
4
- import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider } from '../documentDom.js';
2
+ import { applyPrintPageSize, buildPrintPageStyle, formatCssPixels } from '../output/printLayout.js';
3
+ import { createFileViewerZoomChangeEmitter as createZoomChangeEmitter, } from '../features/document/zoom.js';
4
+ import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider } from '../features/document/dom';
5
5
  const PAGE_BREAK_MARKER = '<span class="msdoc-page-break"></span>';
6
6
  const EMPTY_PAGE_HTML = '<p class="msdoc-paragraph"><br></p>';
7
7
  const MSDOC_PAGE_SIZE = {
@@ -1,4 +1,4 @@
1
- import type { FileRenderContext, FileViewerRenderedInstance as AppWrapper } from '../types';
1
+ import type { FileRenderContext, FileViewerRenderedInstance as AppWrapper } from '../contracts/types';
2
2
  /**
3
3
  * 渲染docx文件
4
4
  */
@@ -1,7 +1,7 @@
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';
1
+ import { resolveFileViewerDocxWorkerUrl } from '../platform/assets.js';
2
+ import { applyPrintPageSize, buildPrintPageStyle, formatCssPixels, getElementPrintPageSize } from '../output/printLayout.js';
3
+ import { createFileViewerZoomChangeEmitter as createZoomChangeEmitter, } from '../features/document/zoom.js';
4
+ import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider } from '../features/document/dom';
5
5
  const DOCX_DEFAULT_PAGE_SIZE = {
6
6
  width: 794,
7
7
  height: 1123
@@ -11,6 +11,7 @@ const DOCX_WORKER_TIMEOUT = 15000;
11
11
  const DOCX_MIN_SCALE = 0.24;
12
12
  const DOCX_MAX_SCALE = 3;
13
13
  const DOCX_ZOOM_STEP = 0.15;
14
+ const DOCX_EMU_PER_CSS_PIXEL = 9525;
14
15
  let docxWorkerRequestId = 0;
15
16
  const loadLibrary = (() => {
16
17
  const loader = {
@@ -237,7 +238,558 @@ const DOCX_RESPONSIVE_CSS = `
237
238
  overflow: hidden;
238
239
  transform-origin: top center;
239
240
  }
241
+ .docx-fit-viewer .docx-page-frame > section.docx > article {
242
+ position: relative;
243
+ z-index: 1;
244
+ }
245
+ .docx-fit-viewer .docx-vml-watermark {
246
+ position: absolute;
247
+ inset: 0;
248
+ z-index: 0;
249
+ width: 100%;
250
+ height: 100%;
251
+ object-fit: cover;
252
+ opacity: 0.28;
253
+ filter: saturate(0.72) brightness(1.24);
254
+ pointer-events: none;
255
+ user-select: none;
256
+ }
257
+ .docx-fit-viewer .docx-vml-fallback,
258
+ .docx-fit-viewer .docx-chart-fallback {
259
+ display: block;
260
+ max-width: 100%;
261
+ margin: 12px auto;
262
+ break-inside: avoid;
263
+ page-break-inside: avoid;
264
+ }
265
+ .docx-fit-viewer .docx-vml-fallback {
266
+ text-align: center;
267
+ }
268
+ .docx-fit-viewer .docx-vml-fallback img {
269
+ display: block;
270
+ max-width: 100%;
271
+ height: auto;
272
+ margin: 0 auto;
273
+ }
274
+ .docx-fit-viewer .docx-chart-fallback {
275
+ box-sizing: border-box;
276
+ overflow: hidden;
277
+ border: 1px solid #d7dee8;
278
+ border-radius: 8px;
279
+ background: #ffffff;
280
+ box-shadow: 0 1px 6px rgba(15, 23, 42, 0.08);
281
+ }
282
+ .docx-fit-viewer .docx-chart-fallback svg {
283
+ display: block;
284
+ width: 100%;
285
+ height: auto;
286
+ }
240
287
  `;
288
+ function getXmlLocalName(node) {
289
+ return node.localName || node.tagName.split(':').pop() || node.tagName;
290
+ }
291
+ function getXmlElements(root, localName) {
292
+ return Array.from(root.querySelectorAll('*')).filter(element => getXmlLocalName(element) === localName);
293
+ }
294
+ function getFirstXmlElement(root, localName) {
295
+ return getXmlElements(root, localName)[0];
296
+ }
297
+ function getXmlAttribute(element, name, namespace) {
298
+ return (namespace ? element.getAttributeNS(namespace, name) : null)
299
+ || element.getAttribute(name)
300
+ || element.getAttribute(`r:${name}`);
301
+ }
302
+ function getXmlText(element) {
303
+ if (!element) {
304
+ return '';
305
+ }
306
+ return (element.textContent || '').replace(/\s+/g, ' ').trim();
307
+ }
308
+ function getXmlAncestor(element, localName) {
309
+ let current = (element === null || element === void 0 ? void 0 : element.parentElement) || null;
310
+ while (current) {
311
+ if (getXmlLocalName(current) === localName) {
312
+ return current;
313
+ }
314
+ current = current.parentElement;
315
+ }
316
+ return null;
317
+ }
318
+ function getParagraphIndex(element) {
319
+ const paragraph = getXmlLocalName(element) === 'p' ? element : getXmlAncestor(element, 'p');
320
+ if (!paragraph) {
321
+ return undefined;
322
+ }
323
+ let index = 0;
324
+ let sibling = paragraph.previousElementSibling;
325
+ while (sibling) {
326
+ if (getXmlLocalName(sibling) === 'p') {
327
+ index += 1;
328
+ }
329
+ sibling = sibling.previousElementSibling;
330
+ }
331
+ return index;
332
+ }
333
+ function parseXml(xml, target) {
334
+ var _a;
335
+ const Parser = ((_a = getTargetWindow(target)) === null || _a === void 0 ? void 0 : _a.DOMParser) || globalThis.DOMParser;
336
+ if (!Parser) {
337
+ return null;
338
+ }
339
+ const doc = new Parser().parseFromString(xml, 'application/xml');
340
+ if (doc.querySelector('parsererror')) {
341
+ return null;
342
+ }
343
+ return doc;
344
+ }
345
+ function normalizeZipPath(path) {
346
+ const segments = [];
347
+ path.split('/').forEach(segment => {
348
+ if (!segment || segment === '.') {
349
+ return;
350
+ }
351
+ if (segment === '..') {
352
+ segments.pop();
353
+ return;
354
+ }
355
+ segments.push(segment);
356
+ });
357
+ return segments.join('/');
358
+ }
359
+ function getDirectoryName(path) {
360
+ const index = path.lastIndexOf('/');
361
+ return index >= 0 ? path.slice(0, index) : '';
362
+ }
363
+ function getRelationshipPath(partPath) {
364
+ const directory = getDirectoryName(partPath);
365
+ const name = partPath.slice(directory ? directory.length + 1 : 0);
366
+ return normalizeZipPath(`${directory}/_rels/${name}.rels`);
367
+ }
368
+ function resolveRelationshipTarget(partPath, target) {
369
+ if (target.startsWith('/')) {
370
+ return normalizeZipPath(target.slice(1));
371
+ }
372
+ return normalizeZipPath(`${getDirectoryName(partPath)}/${target}`);
373
+ }
374
+ function getMimeType(path) {
375
+ var _a;
376
+ const extension = (_a = path.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
377
+ switch (extension) {
378
+ case 'png':
379
+ return 'image/png';
380
+ case 'jpg':
381
+ case 'jpeg':
382
+ return 'image/jpeg';
383
+ case 'gif':
384
+ return 'image/gif';
385
+ case 'bmp':
386
+ return 'image/bmp';
387
+ case 'webp':
388
+ return 'image/webp';
389
+ case 'svg':
390
+ return 'image/svg+xml';
391
+ default:
392
+ return 'application/octet-stream';
393
+ }
394
+ }
395
+ function parseCssLengthToPixels(value) {
396
+ if (!value) {
397
+ return undefined;
398
+ }
399
+ const match = value.trim().match(/^(-?\d+(?:\.\d+)?)(px|pt|in|cm|mm)?$/i);
400
+ if (!match) {
401
+ return undefined;
402
+ }
403
+ const amount = Number(match[1]);
404
+ if (!Number.isFinite(amount) || amount <= 0) {
405
+ return undefined;
406
+ }
407
+ const unit = (match[2] || 'px').toLowerCase();
408
+ switch (unit) {
409
+ case 'pt':
410
+ return amount * 96 / 72;
411
+ case 'in':
412
+ return amount * 96;
413
+ case 'cm':
414
+ return amount * 96 / 2.54;
415
+ case 'mm':
416
+ return amount * 96 / 25.4;
417
+ default:
418
+ return amount;
419
+ }
420
+ }
421
+ function parseStyleDeclaration(style) {
422
+ const declarations = new Map();
423
+ if (!style) {
424
+ return declarations;
425
+ }
426
+ style.split(';').forEach(declaration => {
427
+ const separator = declaration.indexOf(':');
428
+ if (separator <= 0) {
429
+ return;
430
+ }
431
+ declarations.set(declaration.slice(0, separator).trim().toLowerCase(), declaration.slice(separator + 1).trim());
432
+ });
433
+ return declarations;
434
+ }
435
+ function parseShapeSize(style) {
436
+ const declarations = parseStyleDeclaration(style);
437
+ return {
438
+ width: parseCssLengthToPixels(declarations.get('width')),
439
+ height: parseCssLengthToPixels(declarations.get('height'))
440
+ };
441
+ }
442
+ function getExtentSize(element) {
443
+ const inline = getXmlAncestor(element, 'inline') || getXmlAncestor(element, 'anchor');
444
+ const extent = inline ? getFirstXmlElement(inline, 'extent') : undefined;
445
+ const width = Number(extent === null || extent === void 0 ? void 0 : extent.getAttribute('cx'));
446
+ const height = Number(extent === null || extent === void 0 ? void 0 : extent.getAttribute('cy'));
447
+ return {
448
+ width: Number.isFinite(width) && width > 0 ? width / DOCX_EMU_PER_CSS_PIXEL : undefined,
449
+ height: Number.isFinite(height) && height > 0 ? height / DOCX_EMU_PER_CSS_PIXEL : undefined
450
+ };
451
+ }
452
+ async function readDocxRelationships(zip, partPath, target) {
453
+ const relFile = zip.file(getRelationshipPath(partPath));
454
+ const relationships = new Map();
455
+ if (!relFile) {
456
+ return relationships;
457
+ }
458
+ const relDoc = parseXml(await relFile.async('text'), target);
459
+ if (!relDoc) {
460
+ return relationships;
461
+ }
462
+ getXmlElements(relDoc, 'Relationship').forEach(element => {
463
+ const id = element.getAttribute('Id');
464
+ const relationshipTarget = element.getAttribute('Target');
465
+ if (!id || !relationshipTarget) {
466
+ return;
467
+ }
468
+ relationships.set(id, {
469
+ id,
470
+ type: element.getAttribute('Type') || '',
471
+ target: relationshipTarget,
472
+ targetMode: element.getAttribute('TargetMode') || undefined
473
+ });
474
+ });
475
+ return relationships;
476
+ }
477
+ async function getDataUrlFromZip(zip, sourcePath) {
478
+ const file = zip.file(sourcePath);
479
+ if (!file) {
480
+ return undefined;
481
+ }
482
+ const base64 = await file.async('base64');
483
+ return `data:${getMimeType(sourcePath)};base64,${base64}`;
484
+ }
485
+ function getDocxPartPaths(zip) {
486
+ return Object.keys(zip.files)
487
+ .filter(path => path === 'word/document.xml'
488
+ || /^word\/header\d+\.xml$/i.test(path)
489
+ || /^word\/footer\d+\.xml$/i.test(path))
490
+ .sort((left, right) => {
491
+ if (left === 'word/document.xml') {
492
+ return -1;
493
+ }
494
+ if (right === 'word/document.xml') {
495
+ return 1;
496
+ }
497
+ return left.localeCompare(right);
498
+ });
499
+ }
500
+ function inferVmlFallbackRole(partPath, shape, style) {
501
+ const shapeId = `${(shape === null || shape === void 0 ? void 0 : shape.getAttribute('id')) || ''} ${(shape === null || shape === void 0 ? void 0 : shape.getAttribute('o:spid')) || ''}`.toLowerCase();
502
+ const normalizedStyle = (style || '').toLowerCase();
503
+ if (partPath.includes('/header')
504
+ && (shapeId.includes('watermark') || normalizedStyle.includes('z-index:-') || normalizedStyle.includes('mso-position-horizontal:center'))) {
505
+ return 'watermark';
506
+ }
507
+ if ((shape === null || shape === void 0 ? void 0 : shape.getAttribute('o:ole')) === 't' || (shape === null || shape === void 0 ? void 0 : shape.getAttribute('ole')) === 't') {
508
+ return 'ole-preview';
509
+ }
510
+ return 'vml-image';
511
+ }
512
+ async function collectDocxVmlFallbacks(zip, target) {
513
+ var _a;
514
+ const fallbacks = [];
515
+ const seen = new Set();
516
+ for (const partPath of getDocxPartPaths(zip)) {
517
+ const partFile = zip.file(partPath);
518
+ if (!partFile) {
519
+ continue;
520
+ }
521
+ const doc = parseXml(await partFile.async('text'), target);
522
+ if (!doc) {
523
+ continue;
524
+ }
525
+ const relationships = await readDocxRelationships(zip, partPath, target);
526
+ for (const imagedata of getXmlElements(doc, 'imagedata')) {
527
+ const relationshipId = getXmlAttribute(imagedata, 'id', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
528
+ const relationship = relationshipId ? relationships.get(relationshipId) : undefined;
529
+ if (!relationship || relationship.targetMode === 'External' || !relationship.type.includes('/image')) {
530
+ continue;
531
+ }
532
+ const sourcePath = resolveRelationshipTarget(partPath, relationship.target);
533
+ const dataUrl = await getDataUrlFromZip(zip, sourcePath);
534
+ if (!dataUrl) {
535
+ continue;
536
+ }
537
+ const shape = getXmlAncestor(imagedata, 'shape');
538
+ const style = (shape === null || shape === void 0 ? void 0 : shape.getAttribute('style')) || undefined;
539
+ const role = inferVmlFallbackRole(partPath, shape, style);
540
+ const key = role === 'watermark'
541
+ ? `${role}:${sourcePath}`
542
+ : `${role}:${partPath}:${sourcePath}:${(_a = getParagraphIndex(imagedata)) !== null && _a !== void 0 ? _a : 'end'}`;
543
+ if (seen.has(key)) {
544
+ continue;
545
+ }
546
+ seen.add(key);
547
+ fallbacks.push({
548
+ role,
549
+ key,
550
+ dataUrl,
551
+ sourcePath,
552
+ partPath,
553
+ title: imagedata.getAttribute('o:title') || imagedata.getAttribute('title') || undefined,
554
+ style,
555
+ ...parseShapeSize(style),
556
+ paragraphIndex: partPath === 'word/document.xml' ? getParagraphIndex(imagedata) : undefined
557
+ });
558
+ }
559
+ }
560
+ return fallbacks;
561
+ }
562
+ function parseCacheValues(element) {
563
+ if (!element) {
564
+ return [];
565
+ }
566
+ return getXmlElements(element, 'pt')
567
+ .sort((left, right) => Number(left.getAttribute('idx') || 0) - Number(right.getAttribute('idx') || 0))
568
+ .map(point => getXmlText(getFirstXmlElement(point, 'v')))
569
+ .filter(Boolean);
570
+ }
571
+ function parseChartSeries(series) {
572
+ const name = parseCacheValues(getFirstXmlElement(getFirstXmlElement(series, 'tx') || series, 'strCache'))[0]
573
+ || parseCacheValues(getFirstXmlElement(getFirstXmlElement(series, 'tx') || series, 'numCache'))[0]
574
+ || 'Series';
575
+ const categories = parseCacheValues(getFirstXmlElement(getFirstXmlElement(series, 'cat') || series, 'strCache'));
576
+ const numericCategories = parseCacheValues(getFirstXmlElement(getFirstXmlElement(series, 'cat') || series, 'numCache'));
577
+ const values = parseCacheValues(getFirstXmlElement(getFirstXmlElement(series, 'val') || series, 'numCache'))
578
+ .map(value => Number(value))
579
+ .filter(value => Number.isFinite(value));
580
+ return {
581
+ name,
582
+ categories: categories.length ? categories : numericCategories,
583
+ values
584
+ };
585
+ }
586
+ function parseChartFallback(chartDoc, sourcePath, chartElement, paragraphIndex) {
587
+ const chartTypeElement = ['lineChart', 'barChart', 'pieChart', 'areaChart', 'scatterChart']
588
+ .map(type => getXmlElements(chartDoc, type)[0])
589
+ .find(Boolean);
590
+ const chartType = chartTypeElement ? getXmlLocalName(chartTypeElement) : 'chart';
591
+ const title = getXmlText(getFirstXmlElement(getFirstXmlElement(chartDoc, 'title') || chartDoc, 't'))
592
+ || sourcePath.split('/').pop() || 'Chart';
593
+ const series = (chartTypeElement ? getXmlElements(chartTypeElement, 'ser') : getXmlElements(chartDoc, 'ser'))
594
+ .map(parseChartSeries)
595
+ .filter(item => item.values.length);
596
+ const { width, height } = getExtentSize(chartElement);
597
+ if (!series.length) {
598
+ return undefined;
599
+ }
600
+ return {
601
+ key: `chart:${sourcePath}:${paragraphIndex !== null && paragraphIndex !== void 0 ? paragraphIndex : 'end'}`,
602
+ title,
603
+ type: chartType,
604
+ sourcePath,
605
+ series,
606
+ width,
607
+ height,
608
+ paragraphIndex
609
+ };
610
+ }
611
+ async function collectDocxChartFallbacks(zip, target) {
612
+ const partPath = 'word/document.xml';
613
+ const documentFile = zip.file(partPath);
614
+ if (!documentFile) {
615
+ return [];
616
+ }
617
+ const documentDoc = parseXml(await documentFile.async('text'), target);
618
+ if (!documentDoc) {
619
+ return [];
620
+ }
621
+ const relationships = await readDocxRelationships(zip, partPath, target);
622
+ const fallbacks = [];
623
+ const seen = new Set();
624
+ for (const chart of getXmlElements(documentDoc, 'chart')) {
625
+ const relationshipId = getXmlAttribute(chart, 'id', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
626
+ const relationship = relationshipId ? relationships.get(relationshipId) : undefined;
627
+ if (!relationship || relationship.targetMode === 'External' || !relationship.type.includes('/chart')) {
628
+ continue;
629
+ }
630
+ const sourcePath = resolveRelationshipTarget(partPath, relationship.target);
631
+ const chartFile = zip.file(sourcePath);
632
+ if (!chartFile) {
633
+ continue;
634
+ }
635
+ const chartDoc = parseXml(await chartFile.async('text'), target);
636
+ const paragraphIndex = getParagraphIndex(chart);
637
+ const fallback = chartDoc ? parseChartFallback(chartDoc, sourcePath, chart, paragraphIndex) : undefined;
638
+ if (!fallback || seen.has(fallback.key)) {
639
+ continue;
640
+ }
641
+ seen.add(fallback.key);
642
+ fallbacks.push(fallback);
643
+ }
644
+ return fallbacks;
645
+ }
646
+ function escapeHtml(value) {
647
+ return value
648
+ .replace(/&/g, '&amp;')
649
+ .replace(/</g, '&lt;')
650
+ .replace(/>/g, '&gt;')
651
+ .replace(/"/g, '&quot;');
652
+ }
653
+ function buildChartSvg(chart) {
654
+ var _a;
655
+ const width = Math.max(360, Math.round(chart.width || 520));
656
+ const height = Math.max(220, Math.round(chart.height || 320));
657
+ const padding = { top: 52, right: 30, bottom: 56, left: 54 };
658
+ const plotWidth = width - padding.left - padding.right;
659
+ const plotHeight = height - padding.top - padding.bottom;
660
+ const allValues = chart.series.flatMap(item => item.values);
661
+ const maxValue = Math.max(...allValues, 1);
662
+ const minValue = Math.min(...allValues, 0);
663
+ const valueSpan = Math.max(maxValue - minValue, 1);
664
+ const colors = ['#2563eb', '#10b981', '#f97316', '#8b5cf6', '#ef4444'];
665
+ const maxPoints = Math.max(...chart.series.map(item => item.values.length), 1);
666
+ const pointX = (index) => padding.left + (maxPoints === 1 ? plotWidth / 2 : index * plotWidth / (maxPoints - 1));
667
+ const pointY = (value) => padding.top + plotHeight - ((value - minValue) / valueSpan) * plotHeight;
668
+ const seriesPaths = chart.series.map((series, seriesIndex) => {
669
+ const color = colors[seriesIndex % colors.length];
670
+ const points = series.values.map((value, index) => `${pointX(index).toFixed(1)},${pointY(value).toFixed(1)}`).join(' ');
671
+ const circles = series.values.map((value, index) => {
672
+ const x = pointX(index).toFixed(1);
673
+ const y = pointY(value).toFixed(1);
674
+ return `<circle cx="${x}" cy="${y}" r="3.5" fill="${color}"><title>${escapeHtml(series.name)}: ${escapeHtml(String(value))}</title></circle>`;
675
+ }).join('');
676
+ return `<polyline points="${points}" fill="none" stroke="${color}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>${circles}`;
677
+ }).join('');
678
+ const categories = ((_a = chart.series.find(item => item.categories.length)) === null || _a === void 0 ? void 0 : _a.categories) || [];
679
+ const labels = categories.slice(0, maxPoints).map((label, index) => {
680
+ const x = pointX(index);
681
+ const shown = label.length > 14 ? `${label.slice(0, 13)}...` : label;
682
+ return `<text x="${x.toFixed(1)}" y="${height - 22}" text-anchor="middle" fill="#64748b" font-size="11">${escapeHtml(shown)}</text>`;
683
+ }).join('');
684
+ const legend = chart.series.slice(0, 5).map((series, index) => {
685
+ const x = padding.left + index * 98;
686
+ const y = 30;
687
+ const color = colors[index % colors.length];
688
+ 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>`;
689
+ }).join('');
690
+ return `<svg viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeHtml(chart.title)}">
691
+ <rect x="0" y="0" width="${width}" height="${height}" rx="8" fill="#ffffff"/>
692
+ <text x="${padding.left}" y="22" fill="#0f172a" font-size="15" font-weight="700">${escapeHtml(chart.title)}</text>
693
+ ${legend}
694
+ <line x1="${padding.left}" y1="${padding.top + plotHeight}" x2="${padding.left + plotWidth}" y2="${padding.top + plotHeight}" stroke="#cbd5e1"/>
695
+ <line x1="${padding.left}" y1="${padding.top}" x2="${padding.left}" y2="${padding.top + plotHeight}" stroke="#cbd5e1"/>
696
+ <text x="${padding.left - 8}" y="${padding.top + 4}" text-anchor="end" fill="#64748b" font-size="11">${escapeHtml(maxValue.toFixed(1))}</text>
697
+ <text x="${padding.left - 8}" y="${padding.top + plotHeight}" text-anchor="end" fill="#64748b" font-size="11">${escapeHtml(minValue.toFixed(1))}</text>
698
+ ${seriesPaths}
699
+ ${labels}
700
+ </svg>`;
701
+ }
702
+ function getDocxSections(target) {
703
+ return Array.from(target.querySelectorAll('section.docx'));
704
+ }
705
+ function getDocxArticles(target) {
706
+ return Array.from(target.querySelectorAll('section.docx > article'));
707
+ }
708
+ function getDocxParagraphAnchor(target, paragraphIndex) {
709
+ if (paragraphIndex === undefined) {
710
+ return undefined;
711
+ }
712
+ const paragraphs = Array.from(target.querySelectorAll('section.docx > article p'));
713
+ return paragraphs[Math.min(Math.max(paragraphIndex, 0), Math.max(paragraphs.length - 1, 0))];
714
+ }
715
+ function insertDocxFallback(target, node, paragraphIndex) {
716
+ const anchor = getDocxParagraphAnchor(target, paragraphIndex);
717
+ if (anchor) {
718
+ anchor.after(node);
719
+ return;
720
+ }
721
+ const article = getDocxArticles(target)[0];
722
+ if (article) {
723
+ article.appendChild(node);
724
+ }
725
+ }
726
+ function injectDocxImageFallbacks(target, fallbacks) {
727
+ const sections = getDocxSections(target);
728
+ if (!sections.length) {
729
+ return;
730
+ }
731
+ fallbacks.forEach(fallback => {
732
+ if (fallback.role === 'watermark') {
733
+ sections.forEach(section => {
734
+ const image = target.ownerDocument.createElement('img');
735
+ image.className = 'docx-vml-watermark';
736
+ image.src = fallback.dataUrl;
737
+ image.alt = fallback.title || '';
738
+ image.dataset.docxFallback = fallback.key;
739
+ section.prepend(image);
740
+ });
741
+ return;
742
+ }
743
+ const figure = target.ownerDocument.createElement('figure');
744
+ figure.className = 'docx-vml-fallback';
745
+ figure.dataset.docxFallback = fallback.key;
746
+ if (fallback.width) {
747
+ figure.style.width = `${Math.round(fallback.width)}px`;
748
+ }
749
+ const image = target.ownerDocument.createElement('img');
750
+ image.src = fallback.dataUrl;
751
+ image.alt = fallback.title || (fallback.role === 'ole-preview' ? 'Embedded object preview' : 'Document image');
752
+ if (fallback.width) {
753
+ image.style.width = `${Math.round(fallback.width)}px`;
754
+ }
755
+ if (fallback.height) {
756
+ image.style.height = `${Math.round(fallback.height)}px`;
757
+ }
758
+ figure.appendChild(image);
759
+ insertDocxFallback(target, figure, fallback.paragraphIndex);
760
+ });
761
+ }
762
+ function injectDocxChartFallbacks(target, fallbacks) {
763
+ fallbacks.forEach(fallback => {
764
+ const figure = target.ownerDocument.createElement('figure');
765
+ figure.className = 'docx-chart-fallback';
766
+ figure.dataset.docxFallback = fallback.key;
767
+ if (fallback.width) {
768
+ figure.style.width = `${Math.round(fallback.width)}px`;
769
+ }
770
+ figure.innerHTML = buildChartSvg(fallback);
771
+ insertDocxFallback(target, figure, fallback.paragraphIndex);
772
+ });
773
+ }
774
+ async function enhanceDocxFallbacks(buffer, target) {
775
+ try {
776
+ const { default: JSZip } = await import('jszip');
777
+ const zip = await JSZip.loadAsync(buffer.slice(0));
778
+ const [imageFallbacks, chartFallbacks] = await Promise.all([
779
+ collectDocxVmlFallbacks(zip, target),
780
+ collectDocxChartFallbacks(zip, target)
781
+ ]);
782
+ if (imageFallbacks.length) {
783
+ injectDocxImageFallbacks(target, imageFallbacks);
784
+ }
785
+ if (chartFallbacks.length) {
786
+ injectDocxChartFallbacks(target, chartFallbacks);
787
+ }
788
+ }
789
+ catch (error) {
790
+ console.warn('[file-viewer] DOCX 兼容增强解析失败,已保留 docx-preview 原始渲染结果。', error);
791
+ }
792
+ }
241
793
  function installResponsiveStyle(target) {
242
794
  const style = target.ownerDocument.createElement('style');
243
795
  style.textContent = DOCX_RESPONSIVE_CSS;
@@ -476,6 +1028,7 @@ export default async function (buffer, target, context) {
476
1028
  ...docxOptions
477
1029
  });
478
1030
  }
1031
+ await enhanceDocxFallbacks(buffer, target);
479
1032
  const disposeResponsive = makeDocxResponsive(target, context);
480
1033
  (_a = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _a === void 0 ? void 0 : _a.call(context, {
481
1034
  includeDocumentStyles: false,
@@ -1,4 +1,4 @@
1
- import type { RendererRegistry } from './types';
1
+ import type { RendererRegistry } from '../contracts/types';
2
2
  export interface FileViewerRendererHandlerEntry<Handler> {
3
3
  rendererId: string;
4
4
  handler: Handler;
@@ -1,6 +1,6 @@
1
- import { DEFAULT_RENDERER_DEFINITIONS } from './formats.js';
2
- import { createRendererRegistry } from './registry.js';
3
- import { normalizeFileExtension } from './source.js';
1
+ import { DEFAULT_RENDERER_DEFINITIONS } from '../registry/formats.js';
2
+ import { createRendererRegistry } from '../registry/registry.js';
3
+ import { normalizeFileExtension } from '../source';
4
4
  export const createFileViewerRendererDispatcher = ({ registry = createRendererRegistry(DEFAULT_RENDERER_DEFINITIONS), handlers, fallbackHandler, fallbackKey = 'error', }) => {
5
5
  const handlersByRendererId = Array.from(handlers).reduce((result, entry) => {
6
6
  result.set(entry.rendererId, entry.handler);