@file-viewer/pptx 3.1.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +3 -30
- package/README.md +3 -30
- package/dist/chart.d.ts +1 -0
- package/dist/chart.js +194 -24
- package/dist/index.d.ts +8 -8
- package/dist/index.js +5 -5
- package/dist/options.d.ts +1 -1
- package/dist/presentation.d.ts +1 -20
- package/dist/presentation.js +0 -39
- package/dist/styles.d.ts +0 -5
- package/dist/styles.js +17 -5
- package/dist/types.d.ts +1 -1
- package/dist/viewer.d.ts +2 -13
- package/dist/viewer.js +6 -27
- package/dist/worker/pptx.worker.js +12 -12
- package/dist/worker-location.d.ts +0 -1
- package/dist/worker-location.js +0 -3
- package/dist/worker.THIRD_PARTY_LICENSES.txt +786 -0
- package/dist/worker.d.ts +1 -2
- package/dist/worker.js +1 -13
- package/package.json +34 -22
package/README.en.md
CHANGED
|
@@ -1,34 +1,7 @@
|
|
|
1
1
|
# @file-viewer/pptx
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Runtime package version 3.2.0.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Usage and API documentation: https://doc.file-viewer.app/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- Web Worker based PPTX parsing with progressive slide output.
|
|
9
|
-
- Preserves the latest native Flyfish rendering path for themes, images, shapes, tables, text, and basic charts.
|
|
10
|
-
- Supports `fitMode`, `zoomPercent`, custom Worker URLs, and Worker factories for private deployment and strict CSP environments.
|
|
11
|
-
|
|
12
|
-
`resolvePptxPackageWorkerUrl()` lets integrations identify an emitted or directly served package Worker URL. It returns `undefined` when the location is not recognized, not as proof that a file is missing; integrations can then use their asset-copy strategy or explicit Worker configuration.
|
|
13
|
-
|
|
14
|
-
When `PptxViewer` is used directly, Vite or Webpack can serve a recognized package Worker; an unrecognized build never guesses an application-relative path. Angular, IIFE, offline static directories, and strict CSP deployments should use `file-viewer-copy-assets` or an explicit `workerUrl` so the final static origin serves the Worker reliably.
|
|
15
|
-
|
|
16
|
-
## Usage
|
|
17
|
-
|
|
18
|
-
```ts
|
|
19
|
-
import { PptxViewer } from '@file-viewer/pptx'
|
|
20
|
-
|
|
21
|
-
const buffer = await file.arrayBuffer()
|
|
22
|
-
const viewer = await PptxViewer.open(buffer, document.querySelector('#app')!, {
|
|
23
|
-
fitMode: 'contain',
|
|
24
|
-
zoomPercent: 100,
|
|
25
|
-
onSlideRendered(index) {
|
|
26
|
-
console.log('slide rendered', index)
|
|
27
|
-
},
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
await viewer.setZoom(125)
|
|
31
|
-
viewer.destroy()
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Most users should consume PPTX through `@file-viewer/core` or a standard framework package. The core package lazy-loads this renderer only when a presentation is opened.
|
|
7
|
+
The package's own license is Apache-2.0. Included third-party notices apply to their respective components.
|
package/README.md
CHANGED
|
@@ -1,34 +1,7 @@
|
|
|
1
1
|
# @file-viewer/pptx
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Runtime package version 3.2.0.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Usage and API documentation: https://doc.file-viewer.app/
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
- 使用 Web Worker 解析 PPTX,按页渐进输出,避免阻塞主线程。
|
|
9
|
-
- 保留历史原生解析链路中的主题、图片、形状、表格、文本和基础图表处理。
|
|
10
|
-
- 支持 `fitMode`、`zoomPercent`、自定义 Worker URL / Worker factory,方便内网、离线和 CSP 场景。
|
|
11
|
-
|
|
12
|
-
`resolvePptxPackageWorkerUrl()` 可供集成层识别打包器已输出或包目录直接提供的 Worker URL。未识别时返回 `undefined`,不代表文件不存在;集成层仍可使用自己的资产复制策略或显式 Worker 配置。
|
|
13
|
-
|
|
14
|
-
直接使用 `PptxViewer` 时,Vite 或 Webpack 可直接提供已识别的包内 Worker;无法识别的构建不会猜测应用相对路径。Angular、IIFE、离线静态目录或严格 CSP 部署应使用 `file-viewer-copy-assets` 或显式 `workerUrl`,让 Worker 由最终静态域名稳定提供。
|
|
15
|
-
|
|
16
|
-
## 使用
|
|
17
|
-
|
|
18
|
-
```ts
|
|
19
|
-
import { PptxViewer } from '@file-viewer/pptx'
|
|
20
|
-
|
|
21
|
-
const buffer = await file.arrayBuffer()
|
|
22
|
-
const viewer = await PptxViewer.open(buffer, document.querySelector('#app')!, {
|
|
23
|
-
fitMode: 'contain',
|
|
24
|
-
zoomPercent: 100,
|
|
25
|
-
onSlideRendered(index) {
|
|
26
|
-
console.log('slide rendered', index)
|
|
27
|
-
},
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
await viewer.setZoom(125)
|
|
31
|
-
viewer.destroy()
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
在 Flyfish File Viewer 中通常无需直接使用本包,`@file-viewer/core` 会按 PPTX 类型自动按需加载。
|
|
7
|
+
The package's own license is Apache-2.0. Included third-party notices apply to their respective components.
|
package/dist/chart.d.ts
CHANGED
|
@@ -8,4 +8,5 @@ export type PptxPostProcessingHandle = {
|
|
|
8
8
|
destroy: () => void;
|
|
9
9
|
};
|
|
10
10
|
export declare const findPptxChartTarget: (root: ParentNode, chartID: string) => HTMLElement | null;
|
|
11
|
+
export declare const getNumericBulletText: (type: string, index: number) => string;
|
|
11
12
|
export declare const renderPptxPostProcessing: (charts: unknown, root: ParentNode) => Promise<PptxPostProcessingHandle>;
|
package/dist/chart.js
CHANGED
|
@@ -13,22 +13,69 @@ export const findPptxChartTarget = (root, chartID) => {
|
|
|
13
13
|
return Array.from(root.querySelectorAll('[id]'))
|
|
14
14
|
.find(element => element.id === chartID) || null;
|
|
15
15
|
};
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
16
|
+
const formatEastAsianNumber = (index) => {
|
|
17
|
+
const digits = '零一二三四五六七八九';
|
|
18
|
+
const units = [[1000, '千'], [100, '百'], [10, '十'], [1, '']];
|
|
19
|
+
const group = (value, omitLeadingOne = true) => {
|
|
20
|
+
let label = '';
|
|
21
|
+
let gap = false;
|
|
22
|
+
for (const [place, unit] of units) {
|
|
23
|
+
const digit = Math.floor(value / place);
|
|
24
|
+
value %= place;
|
|
25
|
+
if (digit) {
|
|
26
|
+
if (gap)
|
|
27
|
+
label += digits[0];
|
|
28
|
+
label += (place === 10 && digit === 1 && !label && omitLeadingOne ? '' : digits[digit]) + unit;
|
|
29
|
+
gap = false;
|
|
30
|
+
}
|
|
31
|
+
else if (label && value)
|
|
32
|
+
gap = true;
|
|
33
|
+
}
|
|
34
|
+
return label;
|
|
35
|
+
};
|
|
36
|
+
if (index < 10000)
|
|
37
|
+
return group(index);
|
|
38
|
+
const remainder = index % 10000;
|
|
39
|
+
return digits[Math.floor(index / 10000)] + '万' +
|
|
40
|
+
(remainder ? (remainder < 1000 ? digits[0] : '') + group(remainder, false) : '');
|
|
41
|
+
};
|
|
42
|
+
export const getNumericBulletText = (type, index) => {
|
|
43
|
+
if (!Number.isInteger(index) || index < 1 || index > 32767) {
|
|
44
|
+
return String(index);
|
|
45
|
+
}
|
|
46
|
+
if (type === 'ea1JpnChsDbPeriod') {
|
|
47
|
+
return `${formatEastAsianNumber(index)}. `;
|
|
48
|
+
}
|
|
49
|
+
const match = /^(arabic|alphaLc|alphaUc|romanLc|romanUc)(Period|ParenR|ParenBoth|Plain)$/.exec(type);
|
|
50
|
+
if (!match) {
|
|
51
|
+
return String(index);
|
|
52
|
+
}
|
|
53
|
+
let label = String(index);
|
|
54
|
+
if (match[1].startsWith('alpha')) {
|
|
55
|
+
label = '';
|
|
56
|
+
for (let remaining = index; remaining > 0; remaining = Math.floor((remaining - 1) / 26)) {
|
|
57
|
+
label = String.fromCharCode(65 + (remaining - 1) % 26) + label;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else if (match[1].startsWith('roman')) {
|
|
61
|
+
label = '';
|
|
62
|
+
let remaining = index;
|
|
63
|
+
const digits = [
|
|
64
|
+
[1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'], [100, 'C'], [90, 'XC'],
|
|
65
|
+
[50, 'L'], [40, 'XL'], [10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I'],
|
|
66
|
+
];
|
|
67
|
+
for (const [value, symbol] of digits) {
|
|
68
|
+
label += symbol.repeat(Math.floor(remaining / value));
|
|
69
|
+
remaining %= value;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (match[1].endsWith('Lc'))
|
|
73
|
+
label = label.toLowerCase();
|
|
74
|
+
switch (match[2]) {
|
|
75
|
+
case 'Period': return `${label}. `;
|
|
76
|
+
case 'ParenR': return `${label}) `;
|
|
77
|
+
case 'ParenBoth': return `(${label}) `;
|
|
78
|
+
default: return label;
|
|
32
79
|
}
|
|
33
80
|
};
|
|
34
81
|
const restoreNumericBullets = (root) => {
|
|
@@ -43,7 +90,10 @@ const restoreNumericBullets = (root) => {
|
|
|
43
90
|
const type = String(bullet.dataset.bulltname || 'arabicPeriod');
|
|
44
91
|
const level = String(bullet.dataset.bulltlvl || '0');
|
|
45
92
|
const key = `${level}:${type}`;
|
|
46
|
-
const
|
|
93
|
+
const startAt = Number(bullet.dataset.bulltstartat);
|
|
94
|
+
const nextIndex = Number.isInteger(startAt) && startAt >= 1 && startAt <= 32767
|
|
95
|
+
? startAt
|
|
96
|
+
: (counters.get(key) || 0) + 1;
|
|
47
97
|
counters.set(key, nextIndex);
|
|
48
98
|
bullet.textContent = getNumericBulletText(type, nextIndex);
|
|
49
99
|
}
|
|
@@ -120,6 +170,11 @@ const applyTextFitScale = (block, scale) => {
|
|
|
120
170
|
const hasTextOverflow = (block, fitWidth = true) => block.scrollHeight > block.clientHeight + TEXT_FIT_TOLERANCE ||
|
|
121
171
|
(fitWidth && block.scrollWidth > block.clientWidth + TEXT_FIT_TOLERANCE);
|
|
122
172
|
const fitOverflowingTextBlock = (block, fitWidth = true) => {
|
|
173
|
+
const mode = block.dataset.pptxAutofit;
|
|
174
|
+
if (mode && mode !== 'normal')
|
|
175
|
+
return;
|
|
176
|
+
if (block.dataset.pptxWrap === 'none')
|
|
177
|
+
fitWidth = false;
|
|
123
178
|
if (!block.querySelector('.text-block') || block.clientWidth <= 0 || block.clientHeight <= 0) {
|
|
124
179
|
return;
|
|
125
180
|
}
|
|
@@ -157,7 +212,7 @@ const fitOverflowingTextBlocks = (root) => {
|
|
|
157
212
|
.forEach(block => fitOverflowingTextBlock(block, false));
|
|
158
213
|
};
|
|
159
214
|
const renderChart = async (message, root) => {
|
|
160
|
-
var _a;
|
|
215
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
161
216
|
const payload = message.data;
|
|
162
217
|
if (!(payload === null || payload === void 0 ? void 0 : payload.chartID) || !payload.chartType || !payload.chartData) {
|
|
163
218
|
return;
|
|
@@ -174,10 +229,11 @@ const renderChart = async (message, root) => {
|
|
|
174
229
|
}
|
|
175
230
|
const { billboard, d3Format } = await chartLibraryLoader();
|
|
176
231
|
const bb = billboard.default || billboard;
|
|
177
|
-
const { area, bar, line, pie, scatter } = billboard;
|
|
232
|
+
const { area, bar, line, pie, donut, scatter } = billboard;
|
|
233
|
+
if (typeof billboard.grid === 'function') {
|
|
234
|
+
billboard.grid();
|
|
235
|
+
}
|
|
178
236
|
const chart = {
|
|
179
|
-
// A selector makes Billboard query the main document. That misses chart
|
|
180
|
-
// placeholders inside the viewer Shadow DOM and makes it fall back to body.
|
|
181
237
|
bindto: chartTarget,
|
|
182
238
|
};
|
|
183
239
|
const chartData = payload.chartData;
|
|
@@ -192,6 +248,58 @@ const renderChart = async (message, root) => {
|
|
|
192
248
|
},
|
|
193
249
|
};
|
|
194
250
|
switch (payload.chartType) {
|
|
251
|
+
case 'doughnutChart': {
|
|
252
|
+
const series = chartData[0];
|
|
253
|
+
const columns = [];
|
|
254
|
+
const names = {};
|
|
255
|
+
const colors = {};
|
|
256
|
+
for (const [index, point] of ((series === null || series === void 0 ? void 0 : series.values) || []).entries()) {
|
|
257
|
+
if (typeof point.y !== 'number' || !Number.isFinite(point.y) || point.y === 0)
|
|
258
|
+
continue;
|
|
259
|
+
const id = `point-${index}`;
|
|
260
|
+
columns.push([id, Math.abs(point.y)]);
|
|
261
|
+
names[id] = String((_c = (_b = (_a = series.xlabels) === null || _a === void 0 ? void 0 : _a[index]) !== null && _b !== void 0 ? _b : point.x) !== null && _c !== void 0 ? _c : index);
|
|
262
|
+
const fill = (_h = (_g = (_f = (_e = (_d = payload.chartOptions) === null || _d === void 0 ? void 0 : _d.series) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.points) === null || _g === void 0 ? void 0 : _g[point.x]) !== null && _h !== void 0 ? _h : (_l = (_k = (_j = payload.chartOptions) === null || _j === void 0 ? void 0 : _j.series) === null || _k === void 0 ? void 0 : _k[0]) === null || _l === void 0 ? void 0 : _l.color;
|
|
263
|
+
if (fill && /^(?:#[\da-f]{6}(?:[\da-f]{2})?|transparent)$/i.test(fill))
|
|
264
|
+
colors[id] = fill;
|
|
265
|
+
}
|
|
266
|
+
const hole = (_m = payload.chartOptions) === null || _m === void 0 ? void 0 : _m.holeSize;
|
|
267
|
+
const holeRatio = (typeof hole === 'number' && Number.isFinite(hole) && hole >= 10 && hole <= 90 ? hole : 50) / 100;
|
|
268
|
+
const angle = (_o = payload.chartOptions) === null || _o === void 0 ? void 0 : _o.firstSliceAngle;
|
|
269
|
+
let adjustingWidth = false;
|
|
270
|
+
Object.assign(chart, {
|
|
271
|
+
data: { columns, names, colors, type: donut(), order: null },
|
|
272
|
+
donut: {
|
|
273
|
+
startingAngle: typeof angle === 'number' && Number.isFinite(angle) ? angle * Math.PI / 180 : 0,
|
|
274
|
+
expand: false,
|
|
275
|
+
label: { show: false },
|
|
276
|
+
},
|
|
277
|
+
transition: { duration: 0 },
|
|
278
|
+
onrendered() {
|
|
279
|
+
if (adjustingWidth || typeof this.config !== 'function')
|
|
280
|
+
return;
|
|
281
|
+
let radius = 0;
|
|
282
|
+
for (const arc of chartTarget.querySelectorAll('.bb-arc')) {
|
|
283
|
+
const match = /[Aa]([\d.+eE-]+)[ ,]+([\d.+eE-]+)/.exec(arc.getAttribute('d') || '');
|
|
284
|
+
if (match)
|
|
285
|
+
radius = Math.max(radius, Number(match[1]));
|
|
286
|
+
}
|
|
287
|
+
if (!(radius > 0) || !Number.isFinite(radius))
|
|
288
|
+
return;
|
|
289
|
+
const width = radius * (1 - holeRatio);
|
|
290
|
+
if (Math.abs(Number(this.config('donut.width')) - width) <= 0.01)
|
|
291
|
+
return;
|
|
292
|
+
adjustingWidth = true;
|
|
293
|
+
try {
|
|
294
|
+
this.config('donut.width', width, true);
|
|
295
|
+
}
|
|
296
|
+
finally {
|
|
297
|
+
adjustingWidth = false;
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
195
303
|
case 'lineChart':
|
|
196
304
|
Object.assign(chart, {
|
|
197
305
|
data: {
|
|
@@ -226,7 +334,7 @@ const renderChart = async (message, root) => {
|
|
|
226
334
|
case 'pie3DChart':
|
|
227
335
|
Object.assign(chart, {
|
|
228
336
|
data: {
|
|
229
|
-
columns: Object.values(((
|
|
337
|
+
columns: Object.values(((_p = chartData[0]) === null || _p === void 0 ? void 0 : _p.xlabels) || {}).map((value, index) => {
|
|
230
338
|
var _a, _b, _c;
|
|
231
339
|
return [
|
|
232
340
|
value,
|
|
@@ -276,7 +384,70 @@ const renderChart = async (message, root) => {
|
|
|
276
384
|
return;
|
|
277
385
|
}
|
|
278
386
|
if (chart.data) {
|
|
279
|
-
|
|
387
|
+
const options = payload.chartOptions;
|
|
388
|
+
if (options === null || options === void 0 ? void 0 : options.legend) {
|
|
389
|
+
chart.legend = {
|
|
390
|
+
show: options.legend.show !== false,
|
|
391
|
+
position: options.legend.position === 'b' ? 'bottom' : 'right',
|
|
392
|
+
format: (label) => label === '' ? '\u200b' : label,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
if (['barChart', 'areaChart', 'lineChart'].includes(payload.chartType)) {
|
|
396
|
+
const used = new Set();
|
|
397
|
+
const reserved = new Set(chart.data.columns.map((column) => String(column[0])));
|
|
398
|
+
const names = {};
|
|
399
|
+
chart.data.columns.forEach((column, index) => {
|
|
400
|
+
const label = String(column[0]);
|
|
401
|
+
let id = label;
|
|
402
|
+
if (used.has(id)) {
|
|
403
|
+
id = `pptx-series-${index}`;
|
|
404
|
+
while (reserved.has(id) || used.has(id))
|
|
405
|
+
id += '-';
|
|
406
|
+
column[0] = id;
|
|
407
|
+
}
|
|
408
|
+
used.add(id);
|
|
409
|
+
Object.defineProperty(names, id, { value: label, enumerable: true });
|
|
410
|
+
});
|
|
411
|
+
chart.data.names = names;
|
|
412
|
+
if ((options === null || options === void 0 ? void 0 : options.grouping) === 'stacked' || (options === null || options === void 0 ? void 0 : options.grouping) === 'percentStacked') {
|
|
413
|
+
chart.data.groups = [chart.data.columns.map((column) => column[0])];
|
|
414
|
+
if (options.grouping === 'percentStacked')
|
|
415
|
+
chart.data.stack = { normalize: true };
|
|
416
|
+
chart.data.order = null;
|
|
417
|
+
}
|
|
418
|
+
const colors = {};
|
|
419
|
+
chartData.forEach((item, index) => {
|
|
420
|
+
var _a, _b;
|
|
421
|
+
const fill = (_b = (_a = options === null || options === void 0 ? void 0 : options.series) === null || _a === void 0 ? void 0 : _a[index]) === null || _b === void 0 ? void 0 : _b.color;
|
|
422
|
+
if (fill && /^(?:#[\da-f]{6}(?:[\da-f]{2})?|transparent)$/i.test(fill))
|
|
423
|
+
Object.defineProperty(colors, String(chart.data.columns[index][0]), { value: fill, enumerable: true });
|
|
424
|
+
});
|
|
425
|
+
if (Object.keys(colors).length)
|
|
426
|
+
chart.data.colors = colors;
|
|
427
|
+
}
|
|
428
|
+
const surface = chartTarget.ownerDocument.createElement('div');
|
|
429
|
+
surface.className = 'pptx-chart-surface';
|
|
430
|
+
surface.style.cssText = 'width:100%;height:100%;min-width:0;min-height:0;flex:1 1 auto';
|
|
431
|
+
chartTarget.replaceChildren(surface);
|
|
432
|
+
chart.bindto = surface;
|
|
433
|
+
try {
|
|
434
|
+
const instance = bb.generate(chart);
|
|
435
|
+
return {
|
|
436
|
+
destroy() {
|
|
437
|
+
var _a;
|
|
438
|
+
try {
|
|
439
|
+
(_a = instance.destroy) === null || _a === void 0 ? void 0 : _a.call(instance);
|
|
440
|
+
}
|
|
441
|
+
finally {
|
|
442
|
+
surface.remove();
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
surface.remove();
|
|
449
|
+
throw error;
|
|
450
|
+
}
|
|
280
451
|
}
|
|
281
452
|
};
|
|
282
453
|
export const renderPptxPostProcessing = async (charts, root) => {
|
|
@@ -310,7 +481,6 @@ export const renderPptxPostProcessing = async (charts, root) => {
|
|
|
310
481
|
(_a = chart.destroy) === null || _a === void 0 ? void 0 : _a.call(chart);
|
|
311
482
|
}
|
|
312
483
|
catch {
|
|
313
|
-
// The DOM may already be detached during framework unmount.
|
|
314
484
|
}
|
|
315
485
|
}
|
|
316
486
|
chartInstances.length = 0;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { PptxViewer } from
|
|
2
|
-
export { PptxPresentation } from
|
|
3
|
-
export type { PptxPresentationLabels, PptxPresentationState } from
|
|
4
|
-
export { createPptxWorker, resolvePptxPackageWorkerUrl } from
|
|
5
|
-
export { registerPptxChartLibraryLoader } from
|
|
6
|
-
export type { PptxChartLibraries, PptxChartLibraryLoader } from
|
|
7
|
-
export { RECOMMENDED_ZIP_LIMITS, createDefaultPptxOptions, resolvePptxEngineOptions, } from
|
|
8
|
-
export type { NativePptxEngineOptions, PptxDiagnosticError, PptxDiagnosticErrorCode, PptxFitMode, PptxListOptions, PptxSlideSize, PptxViewerOptions, PptxWorkerFactoryOptions, PptxWorkerMessage, PptxZipLimits, } from
|
|
1
|
+
export { PptxViewer } from "./viewer.js";
|
|
2
|
+
export { PptxPresentation } from "./presentation.js";
|
|
3
|
+
export type { PptxPresentationLabels, PptxPresentationState } from "./presentation.js";
|
|
4
|
+
export { createPptxWorker, resolvePptxPackageWorkerUrl } from "./worker.js";
|
|
5
|
+
export { registerPptxChartLibraryLoader } from "./chart.js";
|
|
6
|
+
export type { PptxChartLibraries, PptxChartLibraryLoader } from "./chart.js";
|
|
7
|
+
export { RECOMMENDED_ZIP_LIMITS, createDefaultPptxOptions, resolvePptxEngineOptions, } from "./options.js";
|
|
8
|
+
export type { NativePptxEngineOptions, PptxDiagnosticError, PptxDiagnosticErrorCode, PptxFitMode, PptxListOptions, PptxSlideSize, PptxViewerOptions, PptxWorkerFactoryOptions, PptxWorkerMessage, PptxZipLimits, } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { PptxViewer } from
|
|
2
|
-
export { PptxPresentation } from
|
|
3
|
-
export { createPptxWorker, resolvePptxPackageWorkerUrl } from
|
|
4
|
-
export { registerPptxChartLibraryLoader } from
|
|
5
|
-
export { RECOMMENDED_ZIP_LIMITS, createDefaultPptxOptions, resolvePptxEngineOptions, } from
|
|
1
|
+
export { PptxViewer } from "./viewer.js";
|
|
2
|
+
export { PptxPresentation } from "./presentation.js";
|
|
3
|
+
export { createPptxWorker, resolvePptxPackageWorkerUrl } from "./worker.js";
|
|
4
|
+
export { registerPptxChartLibraryLoader } from "./chart.js";
|
|
5
|
+
export { RECOMMENDED_ZIP_LIMITS, createDefaultPptxOptions, resolvePptxEngineOptions, } from "./options.js";
|
package/dist/options.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NativePptxEngineOptions, PptxZipLimits } from
|
|
1
|
+
import type { NativePptxEngineOptions, PptxZipLimits } from "./types.js";
|
|
2
2
|
export declare const RECOMMENDED_ZIP_LIMITS: Required<PptxZipLimits>;
|
|
3
3
|
export declare const createDefaultPptxOptions: () => NativePptxEngineOptions;
|
|
4
4
|
export declare const resolvePptxEngineOptions: (options?: Partial<NativePptxEngineOptions>) => NativePptxEngineOptions;
|
package/dist/presentation.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PptxViewer } from
|
|
1
|
+
import type { PptxViewer } from "./viewer.js";
|
|
2
2
|
export interface PptxPresentationState {
|
|
3
3
|
active: boolean;
|
|
4
4
|
slideNumber: number;
|
|
@@ -10,14 +10,6 @@ export interface PptxPresentationLabels {
|
|
|
10
10
|
next?: string;
|
|
11
11
|
previous?: string;
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
14
|
-
* Full-screen slideshow for a rendered PPTX deck.
|
|
15
|
-
*
|
|
16
|
-
* The slides stay where the viewer put them: the whole scale box is moved into an overlay and the
|
|
17
|
-
* inactive slots are hidden with CSS, so the engine's scoped `.flyfish-pptx-content .slide` rules
|
|
18
|
-
* keep applying and no node is cloned. A placeholder marks the original position so exiting puts
|
|
19
|
-
* everything back exactly where it was.
|
|
20
|
-
*/
|
|
21
13
|
export declare class PptxPresentation {
|
|
22
14
|
private readonly viewer;
|
|
23
15
|
private readonly labels;
|
|
@@ -40,21 +32,10 @@ export declare class PptxPresentation {
|
|
|
40
32
|
goTo(slideNumber: number): void;
|
|
41
33
|
next(): void;
|
|
42
34
|
previous(): void;
|
|
43
|
-
/** Scale the active slide to fit the overlay, letterboxing whichever axis has slack. */
|
|
44
35
|
layout(): void;
|
|
45
36
|
destroy(): void;
|
|
46
37
|
private slots;
|
|
47
|
-
/**
|
|
48
|
-
* The nodes the slideshow toggles between. Windowed decks wrap each slide in a
|
|
49
|
-
* slot; the default (non-windowed) deck appends slides directly to the content
|
|
50
|
-
* node, so those are the containers there.
|
|
51
|
-
*/
|
|
52
38
|
private slideContainers;
|
|
53
|
-
/**
|
|
54
|
-
* Chromium reports the fullscreen element of a shadow root on the shadow root
|
|
55
|
-
* itself while document.fullscreenElement stays on the host, so both have to
|
|
56
|
-
* be checked before deciding who owns fullscreen.
|
|
57
|
-
*/
|
|
58
39
|
private isFullscreenElement;
|
|
59
40
|
private scheduleLayout;
|
|
60
41
|
private notify;
|
package/dist/presentation.js
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
2
|
-
/**
|
|
3
|
-
* Full-screen slideshow for a rendered PPTX deck.
|
|
4
|
-
*
|
|
5
|
-
* The slides stay where the viewer put them: the whole scale box is moved into an overlay and the
|
|
6
|
-
* inactive slots are hidden with CSS, so the engine's scoped `.flyfish-pptx-content .slide` rules
|
|
7
|
-
* keep applying and no node is cloned. A placeholder marks the original position so exiting puts
|
|
8
|
-
* everything back exactly where it was.
|
|
9
|
-
*/
|
|
10
2
|
export class PptxPresentation {
|
|
11
3
|
constructor(viewer, labels = {}, fullscreen = true) {
|
|
12
4
|
this.overlay = null;
|
|
@@ -62,11 +54,8 @@ export class PptxPresentation {
|
|
|
62
54
|
this.overlay = overlay;
|
|
63
55
|
this.stage = stage;
|
|
64
56
|
this.counter = counter;
|
|
65
|
-
// Keep the overlay inside whichever root holds the slide styles, or the engine's scoped CSS
|
|
66
|
-
// would not reach the slides once they move.
|
|
67
57
|
const styleRoot = this.viewer.presentationRoot;
|
|
68
58
|
styleRoot.appendChild(overlay);
|
|
69
|
-
// Park a comment where the scale box lived so exit() can restore the exact position.
|
|
70
59
|
this.placeholder = documentRef.createComment('flyfish-pptx-presentation');
|
|
71
60
|
this.viewer.saveScrollPosition();
|
|
72
61
|
this.viewer.scaleBox.replaceWith(this.placeholder);
|
|
@@ -80,13 +69,9 @@ export class PptxPresentation {
|
|
|
80
69
|
this.ownsFullscreen = this.isFullscreenElement(overlay);
|
|
81
70
|
}
|
|
82
71
|
catch {
|
|
83
|
-
// Fullscreen needs a user gesture and can be blocked by permissions policy. The overlay is
|
|
84
|
-
// position:fixed, so the slideshow still fills the viewport without it.
|
|
85
72
|
this.ownsFullscreen = false;
|
|
86
73
|
}
|
|
87
74
|
}
|
|
88
|
-
// exit() may have run while fullscreen was being requested. Leave the
|
|
89
|
-
// browser out of fullscreen rather than stranded with no overlay.
|
|
90
75
|
if (this.overlay !== overlay) {
|
|
91
76
|
if (this.ownsFullscreen) {
|
|
92
77
|
void ((_b = documentRef.exitFullscreen) === null || _b === void 0 ? void 0 : _b.call(documentRef).catch(() => undefined));
|
|
@@ -105,8 +90,6 @@ export class PptxPresentation {
|
|
|
105
90
|
return;
|
|
106
91
|
}
|
|
107
92
|
const documentRef = this.viewer.target.ownerDocument || document;
|
|
108
|
-
// Capture before the overlay leaves the DOM: a detached node's getRootNode()
|
|
109
|
-
// is the document, so the fullscreen check would fail after removal.
|
|
110
93
|
const shouldExitFullscreen = this.ownsFullscreen && this.isFullscreenElement(overlay);
|
|
111
94
|
this.detach();
|
|
112
95
|
if (this.layoutFrame) {
|
|
@@ -149,11 +132,8 @@ export class PptxPresentation {
|
|
|
149
132
|
}
|
|
150
133
|
this.current = clamp(Math.round(slideNumber) || 1, 1, total);
|
|
151
134
|
this.viewer.ensureSlideRendered(this.current);
|
|
152
|
-
// Rendering the neighbour keeps the next step instant without defeating virtualization.
|
|
153
135
|
this.viewer.ensureSlideRendered(this.current + 1);
|
|
154
136
|
const containers = this.slideContainers();
|
|
155
|
-
// Windowed decks tag each slot with its slide number; non-windowed decks
|
|
156
|
-
// append slides in order, so the active one is found by index.
|
|
157
137
|
const byNumber = containers.some(container => Boolean(container.dataset.slideNumber));
|
|
158
138
|
containers.forEach((container, index) => {
|
|
159
139
|
const isActive = byNumber
|
|
@@ -179,7 +159,6 @@ export class PptxPresentation {
|
|
|
179
159
|
this.goTo(this.current - 1);
|
|
180
160
|
}
|
|
181
161
|
}
|
|
182
|
-
/** Scale the active slide to fit the overlay, letterboxing whichever axis has slack. */
|
|
183
162
|
layout() {
|
|
184
163
|
const overlay = this.overlay;
|
|
185
164
|
if (!overlay) {
|
|
@@ -191,7 +170,6 @@ export class PptxPresentation {
|
|
|
191
170
|
}
|
|
192
171
|
return index === this.current - 1;
|
|
193
172
|
});
|
|
194
|
-
// Windowed decks wrap the slide in a slot; non-windowed decks are the slide.
|
|
195
173
|
const slide = container && container.classList.contains('flyfish-pptx-slide-slot')
|
|
196
174
|
? container.firstElementChild
|
|
197
175
|
: container;
|
|
@@ -220,11 +198,6 @@ export class PptxPresentation {
|
|
|
220
198
|
slots() {
|
|
221
199
|
return Array.from(this.viewer.content.querySelectorAll(':scope > .flyfish-pptx-slide-slot'));
|
|
222
200
|
}
|
|
223
|
-
/**
|
|
224
|
-
* The nodes the slideshow toggles between. Windowed decks wrap each slide in a
|
|
225
|
-
* slot; the default (non-windowed) deck appends slides directly to the content
|
|
226
|
-
* node, so those are the containers there.
|
|
227
|
-
*/
|
|
228
201
|
slideContainers() {
|
|
229
202
|
const slots = this.slots();
|
|
230
203
|
if (slots.length > 0) {
|
|
@@ -232,11 +205,6 @@ export class PptxPresentation {
|
|
|
232
205
|
}
|
|
233
206
|
return Array.from(this.viewer.content.querySelectorAll(':scope > .slide'));
|
|
234
207
|
}
|
|
235
|
-
/**
|
|
236
|
-
* Chromium reports the fullscreen element of a shadow root on the shadow root
|
|
237
|
-
* itself while document.fullscreenElement stays on the host, so both have to
|
|
238
|
-
* be checked before deciding who owns fullscreen.
|
|
239
|
-
*/
|
|
240
208
|
isFullscreenElement(overlay) {
|
|
241
209
|
const documentRef = this.viewer.target.ownerDocument || document;
|
|
242
210
|
if (documentRef.fullscreenElement === overlay) {
|
|
@@ -275,8 +243,6 @@ export class PptxPresentation {
|
|
|
275
243
|
});
|
|
276
244
|
this.on(view, 'resize', () => this.scheduleLayout());
|
|
277
245
|
this.on(documentRef, 'fullscreenchange', () => {
|
|
278
|
-
// Leaving fullscreen with the browser's own Esc must close the slideshow too, otherwise the
|
|
279
|
-
// overlay would stay up as a plain fixed layer.
|
|
280
246
|
if (this.ownsFullscreen && !this.isFullscreenElement(overlay)) {
|
|
281
247
|
this.ownsFullscreen = false;
|
|
282
248
|
this.exit();
|
|
@@ -295,7 +261,6 @@ export class PptxPresentation {
|
|
|
295
261
|
if (!overlay || ((_a = event.target) === null || _a === void 0 ? void 0 : _a.closest('.flyfish-pptx-presentation-exit'))) {
|
|
296
262
|
return;
|
|
297
263
|
}
|
|
298
|
-
// PowerPoint advances on click; the left edge is the only place that goes back.
|
|
299
264
|
const bounds = overlay.getBoundingClientRect();
|
|
300
265
|
const ratio = (event.clientX - bounds.left) / (bounds.width || 1);
|
|
301
266
|
if (ratio < 0.2) {
|
|
@@ -309,16 +274,12 @@ export class PptxPresentation {
|
|
|
309
274
|
if (!this.active || event.altKey || event.metaKey || event.ctrlKey) {
|
|
310
275
|
return;
|
|
311
276
|
}
|
|
312
|
-
// Enter/Space on a focused control (the exit button) must activate that
|
|
313
|
-
// control, not advance the slide. Let the browser's default run instead.
|
|
314
277
|
const target = event.target;
|
|
315
278
|
if (target &&
|
|
316
279
|
typeof target.closest === 'function' &&
|
|
317
280
|
target.closest('button, a, input, textarea, select, [contenteditable="true"], [contenteditable=""]')) {
|
|
318
281
|
return;
|
|
319
282
|
}
|
|
320
|
-
// With two slideshows open, a key pressed inside the other overlay belongs
|
|
321
|
-
// to that overlay; only the focused one advances.
|
|
322
283
|
if (target && typeof target.closest === 'function') {
|
|
323
284
|
const otherOverlay = target.closest('.flyfish-pptx-presentation');
|
|
324
285
|
if (otherOverlay && otherOverlay !== this.overlay) {
|
package/dist/styles.d.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The legacy engine emits class-only selector lists. Prefix each top-level
|
|
3
|
-
* class rule so light-DOM consumers do not inherit generic `.slide` or
|
|
4
|
-
* generated `._css_*` rules from a renderer-local style element.
|
|
5
|
-
*/
|
|
6
1
|
export declare const scopePptxContentStyleText: (cssText: string) => string;
|
|
7
2
|
export declare const pptxViewerCss: string;
|
|
8
3
|
export declare const ensurePptxViewerStyles: (documentRef: Document, root?: Document | ShadowRoot) => void;
|
package/dist/styles.js
CHANGED
|
@@ -32,13 +32,25 @@ const legacyPptxCss = `
|
|
|
32
32
|
.slide table tr,.slide table td{box-sizing:border-box;min-width:0;overflow:hidden}
|
|
33
33
|
.slide .pptx-table-cell-content{box-sizing:border-box;min-height:0;overflow:hidden}
|
|
34
34
|
.slide svg.drawing{position:absolute;overflow:visible}
|
|
35
|
+
/* Chart shapes need renderer-owned styling even in offline / Shadow DOM hosts.
|
|
36
|
+
Without it SVG's default black fill closes line and axis paths into polygons. */
|
|
37
|
+
.slide .pptx-chart-surface svg{font:10px sans-serif;overflow:hidden}
|
|
38
|
+
.slide .pptx-chart-surface .bb-axis path,.slide .pptx-chart-surface .bb-axis line{fill:none;stroke:currentColor}
|
|
39
|
+
.slide .pptx-chart-surface .bb-line{fill:none;stroke-width:1px}
|
|
40
|
+
.slide .pptx-chart-surface .bb-bar{stroke-width:0}
|
|
41
|
+
.slide .pptx-chart-surface .bb-area{stroke-width:0;opacity:.2}
|
|
42
|
+
.slide .pptx-chart-surface .bb-chart-arc path{stroke:#fff}
|
|
43
|
+
.slide .pptx-chart-surface .bb-chart-arc text{fill:#fff;font-size:13px}
|
|
44
|
+
.slide .pptx-chart-surface .bb-grid line{fill:none;stroke:#aaa}
|
|
45
|
+
.slide .pptx-chart-surface .bb-xgrid,.slide .pptx-chart-surface .bb-ygrid{stroke-dasharray:3 3}
|
|
46
|
+
.slide .pptx-chart-surface .bb-xgrid-focus,.slide .pptx-chart-surface .bb-ygrid-focus{pointer-events:none}
|
|
47
|
+
.slide .pptx-chart-surface .bb-legend-item{font-size:12px}
|
|
48
|
+
.slide .pptx-chart-surface .bb-tooltip-container{z-index:10;pointer-events:none}
|
|
49
|
+
.slide .pptx-chart-surface table.bb-tooltip{position:static;width:auto;table-layout:auto;background:#fff;color:#172033;border:1px solid #ccc;opacity:.95;font-size:12px}
|
|
50
|
+
.slide .pptx-chart-surface .bb-tooltip td,.slide .pptx-chart-surface .bb-tooltip th{padding:4px 6px}
|
|
51
|
+
|
|
35
52
|
`;
|
|
36
53
|
const PPTX_CONTENT_STYLE_SCOPE = '.flyfish-pptx-content';
|
|
37
|
-
/**
|
|
38
|
-
* The legacy engine emits class-only selector lists. Prefix each top-level
|
|
39
|
-
* class rule so light-DOM consumers do not inherit generic `.slide` or
|
|
40
|
-
* generated `._css_*` rules from a renderer-local style element.
|
|
41
|
-
*/
|
|
42
54
|
export const scopePptxContentStyleText = (cssText) => cssText.replace(/(^|})\s*(\.[^{}]+)\{/g, (_match, boundary, selectorList) => {
|
|
43
55
|
const scopedSelectors = selectorList
|
|
44
56
|
.split(',')
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PptxPresentationLabels, PptxPresentationState } from
|
|
1
|
+
import type { PptxPresentationLabels, PptxPresentationState } from "./presentation.js";
|
|
2
2
|
export type PptxFitMode = 'contain' | 'none';
|
|
3
3
|
export interface PptxZipLimits {
|
|
4
4
|
maxFileBytes?: number;
|