@file-viewer/pptx 2.2.3 → 2.2.5

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/chart.d.ts CHANGED
@@ -1 +1,5 @@
1
- export declare const renderPptxPostProcessing: (charts: unknown, root: ParentNode) => Promise<void>;
1
+ export type PptxPostProcessingHandle = {
2
+ destroy: () => void;
3
+ };
4
+ export declare const findPptxChartTarget: (root: ParentNode, chartID: string) => HTMLElement | null;
5
+ export declare const renderPptxPostProcessing: (charts: unknown, root: ParentNode) => Promise<PptxPostProcessingHandle>;
package/dist/chart.js CHANGED
@@ -1,6 +1,14 @@
1
1
  const asChartQueue = (charts) => {
2
2
  return Array.isArray(charts === null || charts === void 0 ? void 0 : charts.MsgQueue) ? charts.MsgQueue : [];
3
3
  };
4
+ export const findPptxChartTarget = (root, chartID) => {
5
+ const rootElement = root;
6
+ if (rootElement.id === chartID) {
7
+ return rootElement;
8
+ }
9
+ return Array.from(root.querySelectorAll('[id]'))
10
+ .find(element => element.id === chartID) || null;
11
+ };
4
12
  const getNumericBulletText = (type, index) => {
5
13
  switch (type) {
6
14
  case 'arabicPeriod':
@@ -141,18 +149,24 @@ const fitOverflowingTextBlocks = (root) => {
141
149
  .querySelectorAll('.slide div.content, .slide div.content-rtl')
142
150
  .forEach(fitOverflowingTextBlock);
143
151
  };
144
- const renderChart = async (message) => {
152
+ const renderChart = async (message, root) => {
145
153
  var _a;
146
154
  const payload = message.data;
147
155
  if (!(payload === null || payload === void 0 ? void 0 : payload.chartID) || !payload.chartType || !payload.chartData) {
148
156
  return;
149
157
  }
158
+ const chartTarget = findPptxChartTarget(root, payload.chartID);
159
+ if (!chartTarget) {
160
+ return;
161
+ }
150
162
  const billboard = await import('billboard.js');
151
163
  const d3Format = await import('d3-format');
152
164
  const bb = billboard.default || billboard;
153
165
  const { area, bar, line, pie, scatter } = billboard;
154
166
  const chart = {
155
- bindto: `#${payload.chartID}`,
167
+ // A selector makes Billboard query the main document. That misses chart
168
+ // placeholders inside the viewer Shadow DOM and makes it fall back to body.
169
+ bindto: chartTarget,
156
170
  };
157
171
  const chartData = payload.chartData;
158
172
  const axis = {
@@ -249,20 +263,44 @@ const renderChart = async (message) => {
249
263
  return;
250
264
  }
251
265
  if (chart.data) {
252
- bb.generate(chart);
266
+ return bb.generate(chart);
253
267
  }
254
268
  };
255
269
  export const renderPptxPostProcessing = async (charts, root) => {
256
270
  restoreNumericBullets(root);
257
271
  fitOverflowingTextBlocks(root);
258
272
  const queue = asChartQueue(charts);
259
- if (!queue.length) {
260
- return;
261
- }
262
- try {
263
- await Promise.all(queue.map(renderChart));
264
- }
265
- catch (error) {
266
- console.warn('PPTX chart rendering skipped:', error);
273
+ const chartInstances = [];
274
+ if (queue.length) {
275
+ const results = await Promise.allSettled(queue.map(message => renderChart(message, root)));
276
+ for (const result of results) {
277
+ if (result.status === 'fulfilled') {
278
+ if (result.value) {
279
+ chartInstances.push(result.value);
280
+ }
281
+ }
282
+ else {
283
+ console.warn('PPTX chart rendering skipped:', result.reason);
284
+ }
285
+ }
267
286
  }
287
+ let destroyed = false;
288
+ return {
289
+ destroy() {
290
+ var _a;
291
+ if (destroyed) {
292
+ return;
293
+ }
294
+ destroyed = true;
295
+ for (const chart of chartInstances) {
296
+ try {
297
+ (_a = chart.destroy) === null || _a === void 0 ? void 0 : _a.call(chart);
298
+ }
299
+ catch {
300
+ // The DOM may already be detached during framework unmount.
301
+ }
302
+ }
303
+ chartInstances.length = 0;
304
+ },
305
+ };
268
306
  };
package/dist/viewer.d.ts CHANGED
@@ -20,6 +20,8 @@ export declare class PptxViewer {
20
20
  private slideWindowListeners;
21
21
  private slideWindowFrame;
22
22
  private charts;
23
+ private readonly chartHandles;
24
+ private readonly mediaRecords;
23
25
  private disposed;
24
26
  private completed;
25
27
  private readonly handleSlideWindowChange;
@@ -59,6 +61,12 @@ export declare class PptxViewer {
59
61
  cloneForExport(): Promise<HTMLElement>;
60
62
  private complete;
61
63
  private fail;
64
+ private trackChartHandle;
65
+ private releaseChartHandle;
66
+ private releaseCharts;
67
+ private storeMedia;
68
+ private hydrateMedia;
69
+ private releaseMedia;
62
70
  private attachResizeObserver;
63
71
  private scheduleResize;
64
72
  private resize;
package/dist/viewer.js CHANGED
@@ -100,6 +100,8 @@ export class PptxViewer {
100
100
  this.slideWindowListeners = [];
101
101
  this.slideWindowFrame = 0;
102
102
  this.charts = null;
103
+ this.chartHandles = new Set();
104
+ this.mediaRecords = new Map();
103
105
  this.disposed = false;
104
106
  this.completed = false;
105
107
  this.handleSlideWindowChange = () => this.scheduleSlideWindowUpdate();
@@ -139,6 +141,8 @@ export class PptxViewer {
139
141
  var _a, _b, _c, _d;
140
142
  this.disposed = true;
141
143
  this.previewThumbnailDataUrl = null;
144
+ this.releaseCharts();
145
+ this.releaseMedia();
142
146
  (_a = this.worker) === null || _a === void 0 ? void 0 : _a.terminate();
143
147
  this.worker = null;
144
148
  (_b = this.resizeObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
@@ -155,6 +159,8 @@ export class PptxViewer {
155
159
  startWorker() {
156
160
  var _a;
157
161
  (_a = this.worker) === null || _a === void 0 ? void 0 : _a.terminate();
162
+ this.releaseCharts();
163
+ this.releaseMedia();
158
164
  this.completed = false;
159
165
  this.charts = null;
160
166
  this.previewThumbnailDataUrl = null;
@@ -195,11 +201,15 @@ export class PptxViewer {
195
201
  }
196
202
  else {
197
203
  const element = appendHtml(this.content, String(message.data || ''));
204
+ this.hydrateMedia(element);
198
205
  this.scheduleResize();
199
206
  (_b = (_a = this.options).onSlideRendered) === null || _b === void 0 ? void 0 : _b.call(_a, Number(message.slide_num || 0), element);
200
207
  }
201
208
  break;
202
209
  }
210
+ case 'media':
211
+ this.storeMedia(message.data);
212
+ break;
203
213
  case 'slide-error': {
204
214
  const slideNumber = Number(message.slide_num || 0);
205
215
  const error = message.data;
@@ -320,6 +330,8 @@ export class PptxViewer {
320
330
  notified: false,
321
331
  postProcessed: false,
322
332
  postProcessPromise: null,
333
+ postProcessVersion: 0,
334
+ chartHandle: null,
323
335
  };
324
336
  }
325
337
  getDesignerCanvases(record) {
@@ -333,9 +345,11 @@ export class PptxViewer {
333
345
  const designerCanvases = this.getDesignerCanvases(record);
334
346
  record.slot.replaceChildren();
335
347
  record.element = appendHtml(record.slot, record.html);
348
+ this.hydrateMedia(record.element);
336
349
  record.slot.append(...designerCanvases);
337
350
  record.rendered = true;
338
351
  record.postProcessed = false;
352
+ record.postProcessVersion += 1;
339
353
  this.updateMeasuredSlideHeight(record);
340
354
  if (!record.notified) {
341
355
  record.notified = true;
@@ -351,6 +365,9 @@ export class PptxViewer {
351
365
  return;
352
366
  }
353
367
  this.updateMeasuredSlideHeight(record);
368
+ this.releaseChartHandle(record.chartHandle);
369
+ record.chartHandle = null;
370
+ record.postProcessVersion += 1;
354
371
  record.slot.replaceChildren();
355
372
  record.element = null;
356
373
  record.rendered = false;
@@ -530,7 +547,8 @@ export class PptxViewer {
530
547
  }
531
548
  async postProcessRenderedContent(charts) {
532
549
  if (!this.shouldWindowSlides()) {
533
- await renderPptxPostProcessing(charts, this.content);
550
+ const handle = await renderPptxPostProcessing(charts, this.content);
551
+ this.trackChartHandle(handle);
534
552
  return;
535
553
  }
536
554
  await Promise.all(this.slideRecords
@@ -542,16 +560,24 @@ export class PptxViewer {
542
560
  return;
543
561
  }
544
562
  if (!record.postProcessPromise) {
545
- record.postProcessPromise = (async () => {
546
- await renderPptxPostProcessing(this.charts, record.slot);
547
- if (this.disposed) {
563
+ const version = record.postProcessVersion;
564
+ const postProcessPromise = (async () => {
565
+ const handle = await renderPptxPostProcessing(this.charts, record.slot);
566
+ if (this.disposed || !record.rendered || record.postProcessVersion !== version) {
567
+ handle === null || handle === void 0 ? void 0 : handle.destroy();
548
568
  return;
549
569
  }
570
+ this.releaseChartHandle(record.chartHandle);
571
+ record.chartHandle = handle;
572
+ this.trackChartHandle(handle);
550
573
  record.postProcessed = true;
551
574
  this.updateMeasuredSlideHeight(record);
552
575
  })().finally(() => {
553
- record.postProcessPromise = null;
576
+ if (record.postProcessPromise === postProcessPromise) {
577
+ record.postProcessPromise = null;
578
+ }
554
579
  });
580
+ record.postProcessPromise = postProcessPromise;
555
581
  }
556
582
  await record.postProcessPromise;
557
583
  }
@@ -603,9 +629,103 @@ export class PptxViewer {
603
629
  this.completed = true;
604
630
  (_a = this.worker) === null || _a === void 0 ? void 0 : _a.terminate();
605
631
  this.worker = null;
632
+ this.releaseCharts();
633
+ this.releaseMedia();
606
634
  this.content.dataset.renderState = 'error';
607
635
  (_c = (_b = this.options).onError) === null || _c === void 0 ? void 0 : _c.call(_b, error);
608
636
  }
637
+ trackChartHandle(handle) {
638
+ if (!handle) {
639
+ return;
640
+ }
641
+ if (this.disposed) {
642
+ handle.destroy();
643
+ return;
644
+ }
645
+ this.chartHandles.add(handle);
646
+ }
647
+ releaseChartHandle(handle) {
648
+ if (!handle) {
649
+ return;
650
+ }
651
+ this.chartHandles.delete(handle);
652
+ handle.destroy();
653
+ }
654
+ releaseCharts() {
655
+ for (const handle of this.chartHandles) {
656
+ handle.destroy();
657
+ }
658
+ this.chartHandles.clear();
659
+ for (const record of this.slideRecords) {
660
+ record.chartHandle = null;
661
+ }
662
+ }
663
+ storeMedia(data) {
664
+ if (!data || typeof data !== 'object') {
665
+ return;
666
+ }
667
+ const media = data;
668
+ if (typeof media.id !== 'string' ||
669
+ !media.id ||
670
+ typeof media.mimeType !== 'string' ||
671
+ !(media.buffer instanceof ArrayBuffer)) {
672
+ return;
673
+ }
674
+ this.releaseMedia(media.id);
675
+ this.mediaRecords.set(media.id, {
676
+ buffer: media.buffer,
677
+ mimeType: media.mimeType,
678
+ objectUrl: null,
679
+ revokeObjectUrl: null,
680
+ });
681
+ this.hydrateMedia(this.content);
682
+ }
683
+ hydrateMedia(root) {
684
+ var _a;
685
+ if (!root) {
686
+ return;
687
+ }
688
+ const elements = Array.from(root.querySelectorAll('[data-pptx-media-id]'));
689
+ if (root.matches('[data-pptx-media-id]')) {
690
+ elements.unshift(root);
691
+ }
692
+ for (const element of elements) {
693
+ const mediaId = element.dataset.pptxMediaId;
694
+ const record = mediaId ? this.mediaRecords.get(mediaId) : undefined;
695
+ if (!record) {
696
+ continue;
697
+ }
698
+ if (!record.objectUrl) {
699
+ const view = this.target.ownerDocument.defaultView;
700
+ const BlobCtor = (view === null || view === void 0 ? void 0 : view.Blob) || globalThis.Blob;
701
+ const UrlApi = (view === null || view === void 0 ? void 0 : view.URL) || globalThis.URL;
702
+ if (typeof BlobCtor !== 'function' ||
703
+ typeof (UrlApi === null || UrlApi === void 0 ? void 0 : UrlApi.createObjectURL) !== 'function') {
704
+ continue;
705
+ }
706
+ const objectUrl = UrlApi.createObjectURL(new BlobCtor([record.buffer], { type: record.mimeType }));
707
+ record.objectUrl = objectUrl;
708
+ record.revokeObjectUrl = () => UrlApi.revokeObjectURL(objectUrl);
709
+ }
710
+ if (element.src !== record.objectUrl) {
711
+ element.src = record.objectUrl;
712
+ (_a = element.load) === null || _a === void 0 ? void 0 : _a.call(element);
713
+ }
714
+ }
715
+ }
716
+ releaseMedia(mediaId) {
717
+ var _a, _b;
718
+ if (mediaId) {
719
+ const record = this.mediaRecords.get(mediaId);
720
+ (_a = record === null || record === void 0 ? void 0 : record.revokeObjectUrl) === null || _a === void 0 ? void 0 : _a.call(record);
721
+ this.mediaRecords.delete(mediaId);
722
+ return;
723
+ }
724
+ for (const record of this.mediaRecords.values()) {
725
+ (_b = record.revokeObjectUrl) === null || _b === void 0 ? void 0 : _b.call(record);
726
+ }
727
+ this.mediaRecords.clear();
728
+ }
609
729
  attachResizeObserver() {
610
730
  if (typeof ResizeObserver === 'undefined') {
611
731
  return;