@silurus/ooxml 0.24.3 → 0.26.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.md CHANGED
@@ -48,11 +48,13 @@ await docx.load('/document.docx');
48
48
  docx.nextPage();
49
49
 
50
50
  // XLSX — viewer manages its own <canvas> + tab bar
51
- const xlsx = new XlsxViewer(document.getElementById('xlsx-container')!);
51
+ const container = document.getElementById('xlsx-container') as HTMLElement;
52
+ const xlsx = new XlsxViewer(container);
52
53
  await xlsx.load('/workbook.xlsx');
53
54
 
54
- // PPTX — viewer manages its own <canvas>
55
- const pptx = new PptxViewer(document.getElementById('pptx-container')!);
55
+ // PPTX — caller provides the <canvas>
56
+ const canvas = document.getElementById('pptx-canvas') as HTMLCanvasElement;
57
+ const pptx = new PptxViewer(canvas);
56
58
  await pptx.load('/deck.pptx');
57
59
  pptx.nextSlide();
58
60
  ```
@@ -137,15 +139,15 @@ import { useEffect, useRef, useState } from 'react';
137
139
  import { PptxViewer } from '@silurus/ooxml/pptx';
138
140
 
139
141
  export function PptxViewerComponent({ src }: { src: string }) {
140
- const containerRef = useRef<HTMLDivElement>(null);
141
- const viewerRef = useRef<PptxViewer | null>(null);
142
+ const canvasRef = useRef<HTMLCanvasElement>(null);
143
+ const viewerRef = useRef<PptxViewer | null>(null);
142
144
  const [slide, setSlide] = useState({ current: 0, total: 0 });
143
145
 
144
146
  useEffect(() => {
145
- const container = containerRef.current;
146
- if (!container) return;
147
+ const canvas = canvasRef.current;
148
+ if (!canvas) return;
147
149
 
148
- const viewer = new PptxViewer(container, {
150
+ const viewer = new PptxViewer(canvas, {
149
151
  onSlideChange: (i, total) => setSlide({ current: i, total }),
150
152
  });
151
153
  viewerRef.current = viewer;
@@ -154,7 +156,7 @@ export function PptxViewerComponent({ src }: { src: string }) {
154
156
 
155
157
  return (
156
158
  <div>
157
- <div ref={containerRef} style={{ width: 800 }} />
159
+ <canvas ref={canvasRef} style={{ width: 800 }} />
158
160
  <button onClick={() => viewerRef.current?.prevSlide()}>‹ Prev</button>
159
161
  <span> {slide.current + 1} / {slide.total} </span>
160
162
  <button onClick={() => viewerRef.current?.nextSlide()}>Next ›</button>
@@ -176,13 +178,13 @@ import { PptxViewer } from '@silurus/ooxml/pptx';
176
178
 
177
179
  const props = defineProps<{ src: string }>();
178
180
 
179
- const container = useTemplateRef<HTMLDivElement>('container');
181
+ const canvas = useTemplateRef<HTMLCanvasElement>('canvas');
180
182
  let viewer: PptxViewer | null = null;
181
183
  const current = ref(0);
182
184
  const total = ref(0);
183
185
 
184
186
  onMounted(async () => {
185
- viewer = new PptxViewer(container.value!, {
187
+ viewer = new PptxViewer(canvas.value!, {
186
188
  onSlideChange: (i, t) => { current.value = i; total.value = t; },
187
189
  });
188
190
  await viewer.load(props.src);
@@ -191,7 +193,7 @@ onMounted(async () => {
191
193
 
192
194
  <template>
193
195
  <div>
194
- <div ref="container" style="width: 800px" />
196
+ <canvas ref="canvas" style="width: 800px" />
195
197
  <button @click="viewer?.prevSlide()">‹ Prev</button>
196
198
  <span> {{ current + 1 }} / {{ total }} </span>
197
199
  <button @click="viewer?.nextSlide()">Next ›</button>
@@ -217,7 +219,7 @@ import { PptxViewer } from '@silurus/ooxml/pptx';
217
219
  standalone: true,
218
220
  template: `
219
221
  <div>
220
- <div #container style="width: 800px"></div>
222
+ <canvas #canvas style="width: 800px"></canvas>
221
223
  <button (click)="prev()">‹ Prev</button>
222
224
  <span> {{ current() + 1 }} / {{ total() }} </span>
223
225
  <button (click)="next()">Next ›</button>
@@ -225,13 +227,13 @@ import { PptxViewer } from '@silurus/ooxml/pptx';
225
227
  `,
226
228
  })
227
229
  export class PptxViewerComponent implements AfterViewInit {
228
- containerEl = viewChild.required<ElementRef<HTMLDivElement>>('container');
230
+ canvasEl = viewChild.required<ElementRef<HTMLCanvasElement>>('canvas');
229
231
  current = signal(0);
230
232
  total = signal(0);
231
233
  private viewer?: PptxViewer;
232
234
 
233
235
  ngAfterViewInit(): void {
234
- this.viewer = new PptxViewer(this.containerEl().nativeElement, {
236
+ this.viewer = new PptxViewer(this.canvasEl().nativeElement, {
235
237
  onSlideChange: (i, t) => { this.current.set(i); this.total.set(t); },
236
238
  });
237
239
  this.viewer.load('/deck.pptx');
@@ -257,13 +259,13 @@ export class PptxViewerComponent implements AfterViewInit {
257
259
 
258
260
  let { src }: { src: string } = $props();
259
261
 
260
- let container: HTMLDivElement;
262
+ let canvas: HTMLCanvasElement;
261
263
  let viewer: PptxViewer;
262
264
  let current = $state(0);
263
265
  let total = $state(0);
264
266
 
265
267
  onMount(async () => {
266
- viewer = new PptxViewer(container, {
268
+ viewer = new PptxViewer(canvas, {
267
269
  onSlideChange: (i, t) => { current = i; total = t; },
268
270
  });
269
271
  await viewer.load(src);
@@ -271,7 +273,7 @@ export class PptxViewerComponent implements AfterViewInit {
271
273
  </script>
272
274
 
273
275
  <div>
274
- <div bind:this={container} style="width: 800px"></div>
276
+ <canvas bind:this={canvas} style="width: 800px"></canvas>
275
277
  <button onclick={() => viewer?.prevSlide()}>‹ Prev</button>
276
278
  <span> {current + 1} / {total} </span>
277
279
  <button onclick={() => viewer?.nextSlide()}>Next ›</button>
@@ -289,13 +291,13 @@ import { createSignal, onMount, onCleanup } from 'solid-js';
289
291
  import { PptxViewer } from '@silurus/ooxml/pptx';
290
292
 
291
293
  export function PptxViewerComponent(props: { src: string }) {
292
- let containerEl!: HTMLDivElement;
294
+ let canvasEl!: HTMLCanvasElement;
293
295
  let viewer: PptxViewer | undefined;
294
296
  const [current, setCurrent] = createSignal(0);
295
297
  const [total, setTotal ] = createSignal(0);
296
298
 
297
299
  onMount(async () => {
298
- viewer = new PptxViewer(containerEl, {
300
+ viewer = new PptxViewer(canvasEl, {
299
301
  onSlideChange: (i, t) => { setCurrent(i); setTotal(t); },
300
302
  });
301
303
  await viewer.load(props.src);
@@ -305,7 +307,7 @@ export function PptxViewerComponent(props: { src: string }) {
305
307
 
306
308
  return (
307
309
  <div>
308
- <div ref={containerEl} style={{ width: '800px' }} />
310
+ <canvas ref={canvasEl} style={{ width: '800px' }} />
309
311
  <button onClick={() => viewer?.prevSlide()}>‹ Prev</button>
310
312
  <span> {current() + 1} / {total()} </span>
311
313
  <button onClick={() => viewer?.nextSlide()}>Next ›</button>
@@ -325,16 +327,16 @@ import { component$, useSignal, useVisibleTask$ } from '@builder.io/qwik';
325
327
  import type { PptxViewer as PptxViewerType } from '@silurus/ooxml/pptx';
326
328
 
327
329
  export const PptxViewerComponent = component$<{ src: string }>(({ src }) => {
328
- const containerRef = useSignal<HTMLDivElement>();
330
+ const canvasRef = useSignal<HTMLCanvasElement>();
329
331
  const current = useSignal(0);
330
332
  const total = useSignal(0);
331
333
  let viewer: PptxViewerType | undefined;
332
334
 
333
335
  // useVisibleTask$ runs only in the browser, never during SSR
334
336
  useVisibleTask$(async () => {
335
- if (!containerRef.value) return;
337
+ if (!canvasRef.value) return;
336
338
  const { PptxViewer } = await import('@silurus/ooxml/pptx');
337
- viewer = new PptxViewer(containerRef.value, {
339
+ viewer = new PptxViewer(canvasRef.value, {
338
340
  onSlideChange: (i, t) => { current.value = i; total.value = t; },
339
341
  });
340
342
  await viewer.load(src);
@@ -342,7 +344,7 @@ export const PptxViewerComponent = component$<{ src: string }>(({ src }) => {
342
344
 
343
345
  return (
344
346
  <div>
345
- <div ref={containerRef} style={{ width: '800px' }} />
347
+ <canvas ref={canvasRef} style={{ width: '800px' }} />
346
348
  <button onClick$={() => viewer?.prevSlide()}>‹ Prev</button>
347
349
  <span> {current.value + 1} / {total.value} </span>
348
350
  <button onClick$={() => viewer?.nextSlide()}>Next ›</button>
@@ -522,8 +524,10 @@ pnpm storybook
522
524
  # Type-check all packages
523
525
  pnpm typecheck
524
526
 
525
- # Run visual regression tests
527
+ # Run visual regression tests (local only — not run in CI)
526
528
  pnpm vrt
529
+ # Adopt the current rendering as the new reference baseline
530
+ UPDATE_REFS=1 pnpm vrt
527
531
 
528
532
  # Build the library
529
533
  pnpm build
@@ -541,7 +545,7 @@ cd packages/pptx/parser && wasm-pack build --target web && cp pkg/pptx_parser_bg
541
545
 
542
546
  - **Canvas-only rendering.** Documents are decoded and drawn to an `HTMLCanvasElement`. No script, link, form, or other active content from the source file is executed or injected into the DOM.
543
547
  - **ZIP decompression cap.** Each entry in the source archive is limited to 512 MiB of uncompressed output to block zip-bomb DoS.
544
- - **No network by default.** The library does not send telemetry or analytics, and does not contact third-party services unless you ask it to. In particular, PPTX theme webfonts are **not** loaded from Google Fonts unless you pass `useGoogleFonts: true` to `PptxPresentation.load()` / `new PptxViewer(...)`. Enabling that option causes the end-user's browser to send an HTTP request (IP and User-Agent) to `fonts.googleapis.com`, which may have GDPR implications for your application — consider self-hosting the required fonts via `@font-face` instead.
548
+ - **No network by default.** The library does not send telemetry or analytics, and does not contact third-party services unless you ask it to. In particular, theme webfonts (and Office font metric substitutes for XLSX) are **not** loaded from Google Fonts unless you pass `useGoogleFonts: true` to the relevant `Viewer` / `load(...)` options — supported uniformly by `DocxViewer`, `PptxViewer`, and `XlsxViewer`. Enabling that option causes the end-user's browser to send an HTTP request (IP and User-Agent) to `fonts.googleapis.com`, which may have GDPR implications for your application — consider self-hosting the required fonts via `@font-face` instead.
545
549
  - **XML parsing.** Uses `roxmltree`, which does not resolve external entities (XXE-safe by default).
546
550
 
547
551
  ## License
@@ -0,0 +1 @@
1
+ var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r},n=3e3,r=20,i=25;async function a(e,t){if(typeof document>`u`)return;let a=new Set,o=new Set;for(let n of e){if(!n)continue;let e=n.toLowerCase();if(a.has(e))continue;a.add(e);let r=t[e];if(r){if(!document.querySelector(`link[href="${r.url}"]`))try{let e=document.createElement(`link`);e.rel=`stylesheet`,e.href=r.url,document.head.appendChild(e)}catch{}o.add((r.loadFamily??n).toLowerCase())}}if(o.size===0)return;let s=[];for(let e=0;e<i&&(s=[...document.fonts].filter(e=>o.has(e.family.toLowerCase())),!(s.length>0));e++)await new Promise(e=>setTimeout(e,r));await Promise.race([Promise.allSettled(s.map(e=>e.load())).then(()=>document.fonts.ready),new Promise(e=>setTimeout(e,n))])}function o(e,t,n={}){let r=n.pauseWhenHidden??!0,i=null,a=0,o=0,s=null,c=!1,l=!1,u=()=>{if(!l&&!(r&&typeof document<`u`&&document.hidden)){if(s){c=!0;return}i===null&&(i=requestAnimationFrame(d))}},d=async()=>{if(i=null,l)return;let t=a,n=o;try{let r=e(t,n);s=r instanceof Promise?r:Promise.resolve(),await s}catch(e){console.error(`[autoResize] render failed:`,e)}finally{s=null,c&&!l&&(c=!1,u())}},f=new ResizeObserver(e=>{for(let t of e){let e=t.contentRect;a=e.width,o=e.height}u()});f.observe(t);let p=()=>{typeof document<`u`&&!document.hidden&&u()};return r&&typeof document<`u`&&document.addEventListener(`visibilitychange`,p),()=>{l=!0,f.disconnect(),i!==null&&(cancelAnimationFrame(i),i=null),r&&typeof document<`u`&&document.removeEventListener(`visibilitychange`,p)}}Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return o}});
@@ -6,10 +6,32 @@ var e = Object.defineProperty, t = (t, n) => {
6
6
  enumerable: !0
7
7
  });
8
8
  return n || e(r, Symbol.toStringTag, { value: "Module" }), r;
9
- };
9
+ }, n = 3e3, r = 20, i = 25;
10
+ async function a(e, t) {
11
+ if (typeof document > "u") return;
12
+ let a = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Set();
13
+ for (let n of e) {
14
+ if (!n) continue;
15
+ let e = n.toLowerCase();
16
+ if (a.has(e)) continue;
17
+ a.add(e);
18
+ let r = t[e];
19
+ if (r) {
20
+ if (!document.querySelector(`link[href="${r.url}"]`)) try {
21
+ let e = document.createElement("link");
22
+ e.rel = "stylesheet", e.href = r.url, document.head.appendChild(e);
23
+ } catch {}
24
+ o.add((r.loadFamily ?? n).toLowerCase());
25
+ }
26
+ }
27
+ if (o.size === 0) return;
28
+ let s = [];
29
+ for (let e = 0; e < i && (s = [...document.fonts].filter((e) => o.has(e.family.toLowerCase())), !(s.length > 0)); e++) await new Promise((e) => setTimeout(e, r));
30
+ await Promise.race([Promise.allSettled(s.map((e) => e.load())).then(() => document.fonts.ready), new Promise((e) => setTimeout(e, n))]);
31
+ }
10
32
  //#endregion
11
33
  //#region packages/core/src/autoResize.ts
12
- function n(e, t, n = {}) {
34
+ function o(e, t, n = {}) {
13
35
  let r = n.pauseWhenHidden ?? !0, i = null, a = 0, o = 0, s = null, c = !1, l = !1, u = () => {
14
36
  if (!l && !(r && typeof document < "u" && document.hidden)) {
15
37
  if (s) {
@@ -45,4 +67,4 @@ function n(e, t, n = {}) {
45
67
  };
46
68
  }
47
69
  //#endregion
48
- export { t as n, n as t };
70
+ export { a as n, t as r, o as t };