@file-viewer/renderer-word 2.2.9 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docxChart.d.ts +4 -0
- package/dist/docxChart.js +149 -0
- package/dist/openDocument.js +14 -33
- package/dist/wordDocx.js +4 -1
- package/package.json +4 -4
|
@@ -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/openDocument.js
CHANGED
|
@@ -2,19 +2,14 @@ import JSZip from 'jszip';
|
|
|
2
2
|
import { createFileViewerTranslator, } from '@file-viewer/core';
|
|
3
3
|
const openDocumentStyle = `
|
|
4
4
|
.odf-viewer{min-height:100%;padding:28px;overflow:auto;background:var(--file-viewer-render-surface-background,#dfe5eb);box-sizing:border-box}
|
|
5
|
-
.odf-shell{width:min(100%,980px);margin:0 auto}
|
|
6
|
-
.odf-shell>header span{color:#0f766e;font-size:12px;font-weight:800}.odf-shell>header h2{margin:6px 0 0;color:#132235;font-size:24px}
|
|
5
|
+
.odf-shell{width:min(100%,980px);margin:0 auto}
|
|
7
6
|
.odf-page{min-height:360px;margin:0 auto 18px;padding:42px 48px;border-radius:4px;background:#fff;box-shadow:0 16px 38px rgba(15,23,42,.12);color:#1f2937;box-sizing:border-box}
|
|
8
|
-
.odf-page
|
|
7
|
+
.odf-page p{margin:0 0 12px;font-size:15px;line-height:1.85;white-space:pre-wrap}
|
|
9
8
|
.flyfish-rtf-viewer{min-height:100%;padding:28px;overflow:auto;background:var(--file-viewer-render-surface-background,#dfe5eb);color:#1f2937;box-sizing:border-box}
|
|
10
|
-
.flyfish-rtf-header{width:min(100%,900px);margin:0 auto 18px;padding:18px 22px;border-radius:8px;background:#fff;box-shadow:0 10px 26px rgba(15,23,42,.1);box-sizing:border-box}
|
|
11
|
-
.flyfish-rtf-header span{display:block;color:#0f766e;font-size:12px;font-weight:800}.flyfish-rtf-header strong{display:block;margin-top:6px;color:#132235;font-size:24px}
|
|
12
9
|
.flyfish-rtf-paper{width:min(100%,900px);min-height:980px;margin:0 auto;padding:54px 62px;background:#fff;box-shadow:0 16px 38px rgba(15,23,42,.12);line-height:1.75;box-sizing:border-box}.flyfish-rtf-paper p{margin:0 0 12px}
|
|
13
10
|
[data-viewer-theme='dark'] .odf-viewer,[data-viewer-theme='dark'] .flyfish-rtf-viewer{color-scheme:dark;background:var(--file-viewer-render-surface-background,#0d1117);color:#e6edf3}
|
|
14
|
-
[data-viewer-theme='dark'] .odf-
|
|
15
|
-
[data-viewer-theme='
|
|
16
|
-
[data-viewer-theme='dark'] .odf-shell>header span,[data-viewer-theme='dark'] .flyfish-rtf-header span{color:#6ee7b7}
|
|
17
|
-
@media (prefers-color-scheme:dark){[data-viewer-theme='system'] .odf-viewer,[data-viewer-theme='system'] .flyfish-rtf-viewer{color-scheme:dark;background:var(--file-viewer-render-surface-background,#0d1117);color:#e6edf3}[data-viewer-theme='system'] .odf-shell>header,[data-viewer-theme='system'] .odf-page,[data-viewer-theme='system'] .flyfish-rtf-header,[data-viewer-theme='system'] .flyfish-rtf-paper{border:1px solid rgba(139,148,158,.24);background:#161b22;color:#e6edf3;box-shadow:0 18px 44px rgba(0,0,0,.34)}[data-viewer-theme='system'] .odf-shell>header h2,[data-viewer-theme='system'] .odf-page h3,[data-viewer-theme='system'] .flyfish-rtf-header strong{color:#f0f6fc}[data-viewer-theme='system'] .odf-shell>header span,[data-viewer-theme='system'] .flyfish-rtf-header span{color:#6ee7b7}}
|
|
11
|
+
[data-viewer-theme='dark'] .odf-page,[data-viewer-theme='dark'] .flyfish-rtf-paper{border:1px solid rgba(139,148,158,.24);background:#161b22;color:#e6edf3;box-shadow:0 18px 44px rgba(0,0,0,.34)}
|
|
12
|
+
@media (prefers-color-scheme:dark){[data-viewer-theme='system'] .odf-viewer,[data-viewer-theme='system'] .flyfish-rtf-viewer{color-scheme:dark;background:var(--file-viewer-render-surface-background,#0d1117);color:#e6edf3}[data-viewer-theme='system'] .odf-page,[data-viewer-theme='system'] .flyfish-rtf-paper{border:1px solid rgba(139,148,158,.24);background:#161b22;color:#e6edf3;box-shadow:0 18px 44px rgba(0,0,0,.34)}}
|
|
18
13
|
@media (max-width:720px){.odf-viewer,.flyfish-rtf-viewer{padding:14px}.odf-page{padding:28px 24px}.flyfish-rtf-paper{padding:36px 28px}}
|
|
19
14
|
`;
|
|
20
15
|
const createStyle = () => {
|
|
@@ -48,13 +43,12 @@ const parseOdf = async (buffer, type, t) => {
|
|
|
48
43
|
}
|
|
49
44
|
if (type === 'odp') {
|
|
50
45
|
const slides = Array.from(doc.getElementsByTagName('draw:page'));
|
|
51
|
-
return slides.map(
|
|
46
|
+
return slides.map(slide => {
|
|
52
47
|
const blocks = Array.from(slide.getElementsByTagName('text:p'))
|
|
53
48
|
.map(nodeText)
|
|
54
49
|
.filter(Boolean);
|
|
55
50
|
return {
|
|
56
|
-
|
|
57
|
-
blocks: blocks.length ? blocks : [t('word.page.empty')],
|
|
51
|
+
blocks,
|
|
58
52
|
};
|
|
59
53
|
});
|
|
60
54
|
}
|
|
@@ -62,21 +56,16 @@ const parseOdf = async (buffer, type, t) => {
|
|
|
62
56
|
...Array.from(doc.getElementsByTagName('text:h')).map(nodeText),
|
|
63
57
|
...Array.from(doc.getElementsByTagName('text:p')).map(nodeText),
|
|
64
58
|
].filter(Boolean);
|
|
65
|
-
return [{
|
|
59
|
+
return [{ blocks }];
|
|
66
60
|
};
|
|
67
|
-
const renderOdfPages = (
|
|
61
|
+
const renderOdfPages = (pages) => {
|
|
68
62
|
const root = document.createElement('div');
|
|
69
63
|
root.className = 'odf-viewer';
|
|
70
64
|
const shell = document.createElement('section');
|
|
71
65
|
shell.className = 'odf-shell';
|
|
72
|
-
const header = document.createElement('header');
|
|
73
|
-
appendText(header, 'span', type.toUpperCase());
|
|
74
|
-
appendText(header, 'h2', title);
|
|
75
|
-
shell.appendChild(header);
|
|
76
66
|
pages.forEach(page => {
|
|
77
67
|
const article = document.createElement('article');
|
|
78
68
|
article.className = 'odf-page';
|
|
79
|
-
appendText(article, 'h3', page.title);
|
|
80
69
|
page.blocks.forEach(block => appendText(article, 'p', block));
|
|
81
70
|
shell.appendChild(article);
|
|
82
71
|
});
|
|
@@ -87,25 +76,20 @@ const resolveRtfJs = async () => {
|
|
|
87
76
|
const rtfModule = await import('rtf.js/dist/RTFJS.bundle.js');
|
|
88
77
|
return rtfModule.RTFJS || rtfModule.default || rtfModule;
|
|
89
78
|
};
|
|
90
|
-
const renderRtf = async (buffer, target,
|
|
91
|
-
var _a, _b
|
|
79
|
+
const renderRtf = async (buffer, target, context) => {
|
|
80
|
+
var _a, _b;
|
|
92
81
|
const RTFJS = await resolveRtfJs();
|
|
93
82
|
(_a = RTFJS.loggingEnabled) === null || _a === void 0 ? void 0 : _a.call(RTFJS, false);
|
|
94
83
|
const doc = new RTFJS.Document(buffer, {});
|
|
95
|
-
const meta = ((_b = doc.metadata) === null || _b === void 0 ? void 0 : _b.call(doc)) || {};
|
|
96
84
|
const elements = await doc.render();
|
|
97
85
|
const stage = document.createElement('div');
|
|
98
86
|
stage.className = 'flyfish-rtf-viewer';
|
|
99
|
-
const header = document.createElement('div');
|
|
100
|
-
header.className = 'flyfish-rtf-header';
|
|
101
|
-
appendText(header, 'span', 'RTF');
|
|
102
|
-
appendText(header, 'strong', meta.title || t('word.title.rtf'));
|
|
103
87
|
const paper = document.createElement('article');
|
|
104
88
|
paper.className = 'flyfish-rtf-paper';
|
|
105
89
|
elements.forEach((element) => paper.appendChild(element));
|
|
106
|
-
stage.
|
|
90
|
+
stage.appendChild(paper);
|
|
107
91
|
target.replaceChildren(createStyle(), stage);
|
|
108
|
-
(
|
|
92
|
+
(_b = context === null || context === void 0 ? void 0 : context.registerThumbnailAdapter) === null || _b === void 0 ? void 0 : _b.call(context, { getTarget: () => paper });
|
|
109
93
|
return {
|
|
110
94
|
$el: target,
|
|
111
95
|
unmount() {
|
|
@@ -120,13 +104,10 @@ export default async function renderOpenDocument(buffer, target, type, context)
|
|
|
120
104
|
const t = createFileViewerTranslator(context === null || context === void 0 ? void 0 : context.options);
|
|
121
105
|
const normalizedType = (type || 'odt').toLowerCase();
|
|
122
106
|
if (normalizedType === 'rtf') {
|
|
123
|
-
return renderRtf(buffer, target,
|
|
107
|
+
return renderRtf(buffer, target, context);
|
|
124
108
|
}
|
|
125
109
|
const pages = await parseOdf(buffer, normalizedType, t);
|
|
126
|
-
|
|
127
|
-
? t('word.title.openDocumentPresentation')
|
|
128
|
-
: t('word.title.openDocumentText');
|
|
129
|
-
target.replaceChildren(createStyle(), renderOdfPages(normalizedType, title, pages));
|
|
110
|
+
target.replaceChildren(createStyle(), renderOdfPages(pages));
|
|
130
111
|
(_a = context === null || context === void 0 ? void 0 : context.registerThumbnailAdapter) === null || _a === void 0 ? void 0 : _a.call(context, {
|
|
131
112
|
getTarget: () => target.querySelector('.odf-page') || target
|
|
132
113
|
});
|
package/dist/wordDocx.js
CHANGED
|
@@ -9,7 +9,7 @@ const DOCX_WORKER_UNSAFE_PROTOCOLS = new Set(['file:', 'about:', 'data:']);
|
|
|
9
9
|
const DOCX_MIN_SCALE = 0.24;
|
|
10
10
|
const DOCX_MAX_SCALE = 3;
|
|
11
11
|
const DOCX_ZOOM_STEP = 0.15;
|
|
12
|
-
const DOCX_VENDOR_ASSET_VERSION = '0.3.
|
|
12
|
+
const DOCX_VENDOR_ASSET_VERSION = '0.3.27';
|
|
13
13
|
const ZIP_SIGNATURE_PK = 0x504b;
|
|
14
14
|
const WORDPROCESSINGML_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
|
|
15
15
|
const OFFICE_RELATIONSHIP_NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
|
|
@@ -258,6 +258,9 @@ const appendDocxVendorAssetVersion = (url, explicitUrl) => {
|
|
|
258
258
|
if (!url || explicitUrl) {
|
|
259
259
|
return url;
|
|
260
260
|
}
|
|
261
|
+
if (/[?&]file-viewer-docx=[^&#]*/.test(url)) {
|
|
262
|
+
return url.replace(/([?&])file-viewer-docx=[^&#]*/, `$1file-viewer-docx=${DOCX_VENDOR_ASSET_VERSION}`);
|
|
263
|
+
}
|
|
261
264
|
return `${url}${url.includes('?') ? '&' : '?'}file-viewer-docx=${DOCX_VENDOR_ASSET_VERSION}`;
|
|
262
265
|
};
|
|
263
266
|
export const applyDocxExternalLinkPolicy = (target, policy) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@file-viewer/renderer-word",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Standalone Word and compatible document renderer plugin for File Viewer powered by @file-viewer/docx, @file-viewer/doc, and RTF/ODF parsing.",
|
|
@@ -57,9 +57,9 @@
|
|
|
57
57
|
"LICENSE"
|
|
58
58
|
],
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@file-viewer/core": "2.
|
|
61
|
-
"@file-viewer/doc": "2.
|
|
62
|
-
"@file-viewer/docx": "^0.3.
|
|
60
|
+
"@file-viewer/core": "2.3.0",
|
|
61
|
+
"@file-viewer/doc": "2.3.0",
|
|
62
|
+
"@file-viewer/docx": "^0.3.27",
|
|
63
63
|
"jszip": "^3.10.1",
|
|
64
64
|
"rtf.js": "^3.0.9"
|
|
65
65
|
},
|