@file-viewer/renderer-word 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ type DOMParserConstructor = new () => DOMParser;
2
+ export declare const collectDocxChartSeriesNames: (buffer: ArrayBuffer, DOMParserCtor: DOMParserConstructor) => Promise<string[][]>;
3
+ export declare const applyDocxChartSeriesNames: (buffer: ArrayBuffer, target: HTMLDivElement) => Promise<void>;
4
+ export {};
@@ -0,0 +1,149 @@
1
+ import JSZip from 'jszip';
2
+ const localName = (node) => {
3
+ const name = node.localName || node.nodeName;
4
+ return name.split(':').pop() || name;
5
+ };
6
+ const elementsByLocal = (node, name) => {
7
+ const result = [];
8
+ const visit = (current) => {
9
+ childElements(current).forEach((child) => {
10
+ if (localName(child) === name) {
11
+ result.push(child);
12
+ }
13
+ visit(child);
14
+ });
15
+ };
16
+ if (node) {
17
+ visit(node);
18
+ }
19
+ return result;
20
+ };
21
+ const childElements = (node) => {
22
+ return node
23
+ ? Array.from(node.childNodes).filter((child) => child.nodeType === 1)
24
+ : [];
25
+ };
26
+ const firstChildByLocal = (node, name) => {
27
+ return childElements(node).find((child) => localName(child) === name);
28
+ };
29
+ const relationshipId = (element) => {
30
+ return (element.getAttribute('r:id') ||
31
+ element.getAttributeNS('http://schemas.openxmlformats.org/officeDocument/2006/relationships', 'id') ||
32
+ '');
33
+ };
34
+ const resolvePartPath = (sourcePart, target) => {
35
+ const sourceDirectory = sourcePart.slice(0, Math.max(0, sourcePart.lastIndexOf('/')));
36
+ const parts = (target.startsWith('/') ? target.slice(1) : `${sourceDirectory}/${target}`).split('/');
37
+ const normalized = [];
38
+ for (const part of parts) {
39
+ if (!part || part === '.') {
40
+ continue;
41
+ }
42
+ if (part === '..') {
43
+ normalized.pop();
44
+ }
45
+ else {
46
+ normalized.push(part);
47
+ }
48
+ }
49
+ return normalized.join('/');
50
+ };
51
+ const readXml = async (zip, path, DOMParserCtor) => {
52
+ const file = zip.file(path);
53
+ if (!file) {
54
+ return null;
55
+ }
56
+ return new DOMParserCtor().parseFromString(await file.async('text'), 'application/xml');
57
+ };
58
+ const extractSeriesName = (series) => {
59
+ var _a, _b;
60
+ const tx = firstChildByLocal(series, 'tx');
61
+ if (!tx) {
62
+ return '';
63
+ }
64
+ const points = elementsByLocal(tx, 'pt')
65
+ .map((point) => {
66
+ var _a, _b;
67
+ return ({
68
+ index: Number(point.getAttribute('idx')) || 0,
69
+ value: ((_b = (_a = firstChildByLocal(point, 'v')) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim()) || ''
70
+ });
71
+ })
72
+ .sort((left, right) => left.index - right.index)
73
+ .map((point) => point.value)
74
+ .filter(Boolean);
75
+ if (points.length) {
76
+ return points.join(' ');
77
+ }
78
+ const richText = elementsByLocal(tx, 't')
79
+ .map((text) => { var _a; return ((_a = text.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || ''; })
80
+ .filter(Boolean)
81
+ .join(' ');
82
+ if (richText) {
83
+ return richText;
84
+ }
85
+ return ((_b = (_a = elementsByLocal(tx, 'v')[0]) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim()) || '';
86
+ };
87
+ export const collectDocxChartSeriesNames = async (buffer, DOMParserCtor) => {
88
+ const zip = await JSZip.loadAsync(buffer);
89
+ const documentPart = 'word/document.xml';
90
+ const [documentXml, relationshipsXml] = await Promise.all([
91
+ readXml(zip, documentPart, DOMParserCtor),
92
+ readXml(zip, 'word/_rels/document.xml.rels', DOMParserCtor)
93
+ ]);
94
+ if (!documentXml || !relationshipsXml) {
95
+ return [];
96
+ }
97
+ const relationships = new Map(elementsByLocal(relationshipsXml.documentElement, 'Relationship').map((relationship) => [
98
+ relationship.getAttribute('Id') || '',
99
+ {
100
+ target: relationship.getAttribute('Target') || '',
101
+ type: relationship.getAttribute('Type') || ''
102
+ }
103
+ ]));
104
+ const chartParts = elementsByLocal(documentXml.documentElement, 'chart').flatMap((chart) => {
105
+ const relationship = relationships.get(relationshipId(chart));
106
+ if (!(relationship === null || relationship === void 0 ? void 0 : relationship.target) || !relationship.type.endsWith('/chart')) {
107
+ return [];
108
+ }
109
+ return [resolvePartPath(documentPart, relationship.target)];
110
+ });
111
+ return Promise.all(chartParts.map(async (chartPart) => {
112
+ const chartXml = await readXml(zip, chartPart, DOMParserCtor);
113
+ return chartXml ? elementsByLocal(chartXml.documentElement, 'ser').map(extractSeriesName) : [];
114
+ }));
115
+ };
116
+ export const applyDocxChartSeriesNames = async (buffer, target) => {
117
+ var _a;
118
+ const charts = Array.from(target.querySelectorAll('.docx-chart'));
119
+ const hasFallbackLegend = charts.some((chart) => /Series\s+\d+/.test(chart.textContent || ''));
120
+ if (!hasFallbackLegend) {
121
+ return;
122
+ }
123
+ const DOMParserCtor = ((_a = target.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.DOMParser) || globalThis.DOMParser;
124
+ if (!DOMParserCtor) {
125
+ return;
126
+ }
127
+ try {
128
+ const namesByChart = await collectDocxChartSeriesNames(buffer, DOMParserCtor);
129
+ charts.forEach((chart, chartIndex) => {
130
+ var _a;
131
+ const names = ((_a = namesByChart[chartIndex]) === null || _a === void 0 ? void 0 : _a.filter(Boolean)) || [];
132
+ if (!names.length) {
133
+ return;
134
+ }
135
+ chart.dataset.chartSeriesNames = JSON.stringify(names);
136
+ const textElements = Array.from(chart.querySelectorAll('text'));
137
+ names.forEach((name, seriesIndex) => {
138
+ const fallback = `Series ${seriesIndex + 1}`;
139
+ const legend = textElements.find((text) => { var _a; return ((_a = text.textContent) === null || _a === void 0 ? void 0 : _a.trim()) === fallback; });
140
+ if (legend) {
141
+ legend.textContent = name;
142
+ }
143
+ });
144
+ });
145
+ }
146
+ catch (error) {
147
+ console.warn('[file-viewer] DOCX 图表系列名修复失败,保留基础图表结果。', error);
148
+ }
149
+ };
package/dist/wordDocx.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import JSZip from 'jszip';
2
2
  import { resolveFileViewerDocxWorkerJsZipUrl, resolveFileViewerDocxWorkerUrl, resolveFileViewerRuntimeAssetBaseUrl, } from '@file-viewer/core/assets';
3
- import { applyPrintPageSize, buildPrintPageStyle, createFileViewerTranslator, createFileViewerZoomChangeEmitter as createZoomChangeEmitter, formatCssPixels, getElementPrintPageSize, normalizeFileViewerTheme, registerFileViewerZoomProvider, resolveFileViewerFitScale, unregisterFileViewerZoomProvider, } from '@file-viewer/core';
3
+ import { applyPrintPageSize, buildPrintPageStyle, createFileViewerTranslator, createFileViewerZoomChangeEmitter as createZoomChangeEmitter, formatCssPixels, getElementPrintPageSize, normalizeFileViewerTheme, replaceFileViewerCanvasWithImages, registerFileViewerZoomProvider, resolveFileViewerFitScale, unregisterFileViewerZoomProvider, waitForFileViewerNextPaint, } from '@file-viewer/core';
4
4
  const DOCX_DEFAULT_PAGE_SIZE = {
5
5
  width: 794,
6
6
  height: 1123
@@ -628,6 +628,9 @@ function getDocxPageElement(frame) {
628
628
  function isDocxFlowFrame(frame) {
629
629
  return !!(frame === null || frame === void 0 ? void 0 : frame.classList.contains('docx-flow-frame'));
630
630
  }
631
+ function isDocxCanvasSheet(frame) {
632
+ return !!(frame === null || frame === void 0 ? void 0 : frame.classList.contains('docx-canvas-sheet'));
633
+ }
631
634
  function getDocxFramePrintSize(frame) {
632
635
  const page = frame ? getDocxPageElement(frame) : null;
633
636
  if (!page) {
@@ -643,6 +646,17 @@ function getDocxFramePrintSize(frame) {
643
646
  };
644
647
  }
645
648
  function normalizeDocxPageForPrint(frame, pageSize) {
649
+ var _a;
650
+ if (isDocxCanvasSheet(frame)) {
651
+ applyPrintPageSize(frame, pageSize);
652
+ (_a = frame.dataset).viewerPrintPageIndex || (_a.viewerPrintPageIndex = '0');
653
+ frame.classList.remove('docx-canvas-sheet-pending', 'docx-canvas-sheet-virtualized');
654
+ frame.style.position = 'relative';
655
+ frame.style.contain = 'none';
656
+ frame.style.margin = '0 auto 18px';
657
+ frame.style.boxShadow = 'none';
658
+ return;
659
+ }
646
660
  const flowLayout = isDocxFlowFrame(frame);
647
661
  const pageWidth = formatCssPixels(pageSize.width);
648
662
  const pageHeight = formatCssPixels(pageSize.height);
@@ -666,11 +680,13 @@ function normalizeDocxPageForPrint(frame, pageSize) {
666
680
  page.style.boxShadow = 'none';
667
681
  }
668
682
  function buildDocxPrintStyle(target) {
669
- const firstFrame = target.querySelector('.docx-page-frame, .docx-flow-frame');
683
+ const firstFrame = target.querySelector('.docx-page-frame, .docx-flow-frame, .docx-canvas-sheet');
670
684
  const pageSize = getDocxFramePrintSize(firstFrame || undefined);
671
- const selector = (firstFrame === null || firstFrame === void 0 ? void 0 : firstFrame.classList.contains('docx-flow-frame'))
672
- ? '.viewer-export-content .docx-flow-frame'
673
- : '.viewer-export-content .docx-page-frame';
685
+ const selector = isDocxCanvasSheet(firstFrame || undefined)
686
+ ? '.viewer-export-content .docx-canvas-sheet'
687
+ : (firstFrame === null || firstFrame === void 0 ? void 0 : firstFrame.classList.contains('docx-flow-frame'))
688
+ ? '.viewer-export-content .docx-flow-frame'
689
+ : '.viewer-export-content .docx-page-frame';
674
690
  return buildPrintPageStyle({
675
691
  selector,
676
692
  width: pageSize.width,
@@ -680,21 +696,41 @@ function buildDocxPrintStyle(target) {
680
696
  heightMode: (firstFrame === null || firstFrame === void 0 ? void 0 : firstFrame.classList.contains('docx-flow-frame')) ? 'min' : 'fixed'
681
697
  });
682
698
  }
683
- function prepareDocxCloneForExport(target) {
684
- const liveFrames = Array.from(target.querySelectorAll('.docx-page-frame, .docx-flow-frame'));
685
- const clone = target.cloneNode(true);
686
- const printDocument = target.ownerDocument.createElement('div');
687
- printDocument.className = 'docx-print-document';
688
- const scopedStyles = Array.from(clone.querySelectorAll('style'))
689
- .filter(style => { var _a; return !((_a = style.textContent) === null || _a === void 0 ? void 0 : _a.includes('.docx-fit-viewer')); })
690
- .map(style => style.outerHTML)
691
- .join('');
692
- clone.querySelectorAll('.docx-page-frame, .docx-flow-frame').forEach((frame, index) => {
693
- frame.dataset.viewerPrintPageIndex = String(index);
694
- normalizeDocxPageForPrint(frame, getDocxFramePrintSize(liveFrames[index]));
695
- printDocument.appendChild(frame.cloneNode(true));
696
- });
697
- return printDocument.childElementCount ? `${scopedStyles}${printDocument.outerHTML}` : clone.innerHTML;
699
+ async function prepareDocxCloneForExport(target) {
700
+ const view = getTargetWindow(target);
701
+ const usesVirtualCanvas = !!target.querySelector('.docx-canvas-sheet');
702
+ const materializeAllPages = view === null || view === void 0 ? void 0 : view.__docxCanvasMaterializeAllPages;
703
+ let materializedForSnapshot = false;
704
+ if (usesVirtualCanvas && typeof materializeAllPages === 'function') {
705
+ await materializeAllPages.call(view, 'file-viewer-print-snapshot');
706
+ await waitForFileViewerNextPaint(view);
707
+ materializedForSnapshot = true;
708
+ }
709
+ try {
710
+ const selector = '.docx-page-frame, .docx-flow-frame, .docx-canvas-sheet';
711
+ const liveFrames = Array.from(target.querySelectorAll(selector));
712
+ const clone = target.cloneNode(true);
713
+ replaceFileViewerCanvasWithImages(target, clone);
714
+ const printDocument = target.ownerDocument.createElement('div');
715
+ printDocument.className = 'docx-print-document';
716
+ const scopedStyles = Array.from(clone.querySelectorAll('style'))
717
+ .filter(style => { var _a; return !((_a = style.textContent) === null || _a === void 0 ? void 0 : _a.includes('.docx-fit-viewer')); })
718
+ .map(style => style.outerHTML)
719
+ .join('');
720
+ clone.querySelectorAll(selector).forEach((frame, index) => {
721
+ frame.dataset.viewerPrintPageIndex = String(index);
722
+ normalizeDocxPageForPrint(frame, getDocxFramePrintSize(liveFrames[index]));
723
+ printDocument.appendChild(frame.cloneNode(true));
724
+ });
725
+ return printDocument.childElementCount ? `${scopedStyles}${printDocument.outerHTML}` : clone.innerHTML;
726
+ }
727
+ finally {
728
+ if (materializedForSnapshot && view) {
729
+ const afterPrint = target.ownerDocument.createEvent('Event');
730
+ afterPrint.initEvent('afterprint', false, false);
731
+ view.dispatchEvent(afterPrint);
732
+ }
733
+ }
698
734
  }
699
735
  /**
700
736
  * 渲染docx文件
@@ -731,7 +767,7 @@ export default async function (buffer, target, context) {
731
767
  const disposeResponsive = makeDocxResponsive(target, context);
732
768
  (_a = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _a === void 0 ? void 0 : _a.call(context, {
733
769
  includeDocumentStyles: false,
734
- getPrintMaskPages: () => Array.from(target.querySelectorAll('.docx-page-frame, .docx-flow-frame')),
770
+ getPrintMaskPages: () => Array.from(target.querySelectorAll('.docx-page-frame, .docx-flow-frame, .docx-canvas-sheet')),
735
771
  beforeSnapshot: () => {
736
772
  const view = getTargetWindow(target);
737
773
  if (view) {
@@ -742,7 +778,7 @@ export default async function (buffer, target, context) {
742
778
  toHtml: () => prepareDocxCloneForExport(target)
743
779
  });
744
780
  (_b = context === null || context === void 0 ? void 0 : context.registerThumbnailAdapter) === null || _b === void 0 ? void 0 : _b.call(context, {
745
- getTarget: () => target.querySelector('.docx-page-frame, .docx-flow-frame') || target
781
+ getTarget: () => target.querySelector('.docx-page-frame, .docx-flow-frame, .docx-canvas-sheet') || target
746
782
  });
747
783
  return {
748
784
  $el: target,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@file-viewer/renderer-word",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Standalone DOCX, DOC, and OpenDocument renderer for File Viewer; RTF parsing is an explicit opt-in capability.",
@@ -58,8 +58,8 @@
58
58
  "LICENSE"
59
59
  ],
60
60
  "dependencies": {
61
- "@file-viewer/core": "3.0.0",
62
- "@file-viewer/doc": "3.0.0",
61
+ "@file-viewer/core": "3.0.1",
62
+ "@file-viewer/doc": "3.0.1",
63
63
  "@file-viewer/docx": "0.3.28",
64
64
  "jszip": "^3.10.1"
65
65
  },