@silurus/ooxml 0.1.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/LICENSE +21 -0
- package/README.md +499 -0
- package/dist/chunk-BwIEoMh7.cjs +1 -0
- package/dist/chunk-DmhlhrBa.js +11 -0
- package/dist/docx-Bpx5ZIHv.js +721 -0
- package/dist/docx-CVRWUA32.cjs +1 -0
- package/dist/docx.cjs +1 -0
- package/dist/docx.mjs +2 -0
- package/dist/index.cjs +1 -0
- package/dist/index.mjs +4 -0
- package/dist/pptx-D3vSvVQ6.js +1475 -0
- package/dist/pptx-DrZBtOP1.cjs +1 -0
- package/dist/pptx.cjs +1 -0
- package/dist/pptx.mjs +2 -0
- package/dist/renderer-B5mmiYKF.cjs +1 -0
- package/dist/renderer-BlgQO3S_.js +473 -0
- package/dist/types/docx.d.ts +286 -0
- package/dist/types/index.d.ts +1172 -0
- package/dist/types/pptx.d.ts +450 -0
- package/dist/types/xlsx.d.ts +375 -0
- package/dist/xlsx-B-yZ85zA.js +1068 -0
- package/dist/xlsx-BhLRc4om.cjs +4 -0
- package/dist/xlsx.cjs +1 -0
- package/dist/xlsx.mjs +2 -0
- package/package.json +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yuki Yokotani
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
> **This entire codebase — Rust parsers, TypeScript renderers, tests, and tooling — was implemented by [Claude](https://claude.ai)** (Anthropic's AI assistant) through iterative prompting. No human-written application code exists in this repository.
|
|
2
|
+
|
|
3
|
+
# office-open-xml-viewer
|
|
4
|
+
|
|
5
|
+
**[Demo (Storybook)](https://yukiyokotani.github.io/office-open-xml-viewer/)**
|
|
6
|
+
|
|
7
|
+
A browser-based viewer for Office Open XML documents that renders to an HTML Canvas element.
|
|
8
|
+
The parsers are written in Rust and compiled to WebAssembly; the renderers use the Canvas 2D API.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @silurus/ooxml
|
|
12
|
+
# or
|
|
13
|
+
pnpm add @silurus/ooxml
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
> **Bundler note**: this package embeds `.wasm` files. With Vite add [`vite-plugin-wasm`](https://github.com/Menci/vite-plugin-wasm); with webpack use [`experiments.asyncWebAssembly`](https://webpack.js.org/configuration/experiments/).
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { PptxViewer } from '@silurus/ooxml/pptx';
|
|
24
|
+
import { XlsxViewer } from '@silurus/ooxml/xlsx';
|
|
25
|
+
import { DocxViewer } from '@silurus/ooxml/docx';
|
|
26
|
+
|
|
27
|
+
// PPTX — viewer manages its own <canvas>
|
|
28
|
+
const pptx = new PptxViewer(document.getElementById('pptx-container')!);
|
|
29
|
+
const buf = await fetch('/deck.pptx').then(r => r.arrayBuffer());
|
|
30
|
+
await pptx.load(buf);
|
|
31
|
+
pptx.nextSlide();
|
|
32
|
+
|
|
33
|
+
// XLSX — viewer manages its own <canvas> + tab bar
|
|
34
|
+
const xlsx = new XlsxViewer(document.getElementById('xlsx-container')!);
|
|
35
|
+
await xlsx.load('/workbook.xlsx');
|
|
36
|
+
|
|
37
|
+
// DOCX — caller provides the <canvas>
|
|
38
|
+
const canvas = document.getElementById('docx-canvas') as HTMLCanvasElement;
|
|
39
|
+
const docx = new DocxViewer(canvas);
|
|
40
|
+
await docx.load('/document.docx');
|
|
41
|
+
docx.nextPage();
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
<details>
|
|
47
|
+
<summary><strong>Architecture diagram</strong></summary>
|
|
48
|
+
|
|
49
|
+
```mermaid
|
|
50
|
+
flowchart TB
|
|
51
|
+
subgraph build["🦀 Build-time (Rust → WebAssembly)"]
|
|
52
|
+
direction LR
|
|
53
|
+
pptx_rs["packages/pptx/parser/src/lib.rs"]
|
|
54
|
+
xlsx_rs["packages/xlsx/parser/src/lib.rs"]
|
|
55
|
+
docx_rs["packages/docx/parser/src/lib.rs"]
|
|
56
|
+
pptx_rs -- wasm-pack --> pptx_wasm["pptx_parser.wasm"]
|
|
57
|
+
xlsx_rs -- wasm-pack --> xlsx_wasm["xlsx_parser.wasm"]
|
|
58
|
+
docx_rs -- wasm-pack --> docx_wasm["docx_parser.wasm"]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
subgraph browser["🌐 Runtime (Browser)"]
|
|
62
|
+
subgraph pptx_pkg["@silurus/ooxml · pptx"]
|
|
63
|
+
PV["PptxViewer"] --> PP["PptxPresentation"]
|
|
64
|
+
PP --> PW["worker.ts\n〈Web Worker — parse only〉"]
|
|
65
|
+
PP --> PR["renderer.ts\n〈Canvas 2D — main thread〉"]
|
|
66
|
+
end
|
|
67
|
+
subgraph xlsx_pkg["@silurus/ooxml · xlsx"]
|
|
68
|
+
XV["XlsxViewer"] --> XR["renderer.ts\n〈Canvas 2D〉"]
|
|
69
|
+
end
|
|
70
|
+
subgraph docx_pkg["@silurus/ooxml · docx"]
|
|
71
|
+
DV["DocxViewer"] --> DD["DocxDocument"]
|
|
72
|
+
DD --> DR["renderer.ts\n〈Canvas 2D〉"]
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
pptx_wasm --> PW
|
|
77
|
+
xlsx_wasm --> XV
|
|
78
|
+
docx_wasm --> DD
|
|
79
|
+
PR --> canvas["<canvas>"]
|
|
80
|
+
XR --> canvas
|
|
81
|
+
DR --> canvas
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The pptx worker parses the `.pptx` archive via WASM and returns a JSON model to the main thread. Rendering runs on the main thread so the canvas shares the document's `FontFaceSet` — an `OffscreenCanvas` in a worker has its own font registry and would silently fall back to a system font, producing subtly different text measurements (and wrap positions) from the installed theme webfonts.
|
|
85
|
+
|
|
86
|
+
### Key files
|
|
87
|
+
|
|
88
|
+
| File | Role |
|
|
89
|
+
|------|------|
|
|
90
|
+
| `packages/pptx/parser/src/lib.rs` | Rust WASM parser — PPTX ZIP → `Presentation` JSON |
|
|
91
|
+
| `packages/xlsx/parser/src/lib.rs` | Rust WASM parser — XLSX ZIP → `Workbook` JSON |
|
|
92
|
+
| `packages/docx/parser/src/lib.rs` | Rust WASM parser — DOCX ZIP → `Document` JSON |
|
|
93
|
+
| `packages/pptx/src/renderer.ts` | Canvas 2D rendering engine (runs on main thread) |
|
|
94
|
+
| `packages/xlsx/src/renderer.ts` | Canvas 2D rendering engine with virtual scroll |
|
|
95
|
+
| `packages/docx/src/renderer.ts` | Canvas 2D rendering engine with text layout |
|
|
96
|
+
| `packages/pptx/src/worker.ts` | Web Worker: WASM init and parsing only |
|
|
97
|
+
| `packages/*/src/viewer.ts` | Public Viewer API — canvas lifecycle, navigation |
|
|
98
|
+
|
|
99
|
+
</details>
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Framework Examples
|
|
104
|
+
|
|
105
|
+
<details>
|
|
106
|
+
<summary><strong>React 19</strong></summary>
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
// React 19.1 — vite-plugin-wasm required in vite.config.ts
|
|
110
|
+
import { useEffect, useRef, useState } from 'react';
|
|
111
|
+
import { PptxViewer } from '@silurus/ooxml/pptx';
|
|
112
|
+
|
|
113
|
+
export function PptxViewerComponent({ src }: { src: string }) {
|
|
114
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
115
|
+
const viewerRef = useRef<PptxViewer | null>(null);
|
|
116
|
+
const [slide, setSlide] = useState({ current: 0, total: 0 });
|
|
117
|
+
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
const container = containerRef.current;
|
|
120
|
+
if (!container) return;
|
|
121
|
+
|
|
122
|
+
const viewer = new PptxViewer(container, {
|
|
123
|
+
onSlideChange: (i, total) => setSlide({ current: i, total }),
|
|
124
|
+
});
|
|
125
|
+
viewerRef.current = viewer;
|
|
126
|
+
|
|
127
|
+
let cancelled = false;
|
|
128
|
+
fetch(src)
|
|
129
|
+
.then(r => r.arrayBuffer())
|
|
130
|
+
.then(buf => { if (!cancelled) viewer.load(buf); });
|
|
131
|
+
|
|
132
|
+
return () => { cancelled = true; };
|
|
133
|
+
}, [src]);
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<div>
|
|
137
|
+
<div ref={containerRef} style={{ width: 800 }} />
|
|
138
|
+
<button onClick={() => viewerRef.current?.prevSlide()}>‹ Prev</button>
|
|
139
|
+
<span> {slide.current + 1} / {slide.total} </span>
|
|
140
|
+
<button onClick={() => viewerRef.current?.nextSlide()}>Next ›</button>
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
</details>
|
|
147
|
+
|
|
148
|
+
<details>
|
|
149
|
+
<summary><strong>Vue 3.5</strong></summary>
|
|
150
|
+
|
|
151
|
+
```vue
|
|
152
|
+
<!-- Vue 3.5 — useTemplateRef is a 3.5+ feature -->
|
|
153
|
+
<script setup lang="ts">
|
|
154
|
+
import { useTemplateRef, onMounted, ref } from 'vue';
|
|
155
|
+
import { PptxViewer } from '@silurus/ooxml/pptx';
|
|
156
|
+
|
|
157
|
+
const props = defineProps<{ src: string }>();
|
|
158
|
+
|
|
159
|
+
const container = useTemplateRef<HTMLDivElement>('container');
|
|
160
|
+
let viewer: PptxViewer | null = null;
|
|
161
|
+
const current = ref(0);
|
|
162
|
+
const total = ref(0);
|
|
163
|
+
|
|
164
|
+
onMounted(async () => {
|
|
165
|
+
viewer = new PptxViewer(container.value!, {
|
|
166
|
+
onSlideChange: (i, t) => { current.value = i; total.value = t; },
|
|
167
|
+
});
|
|
168
|
+
const buf = await fetch(props.src).then(r => r.arrayBuffer());
|
|
169
|
+
await viewer.load(buf);
|
|
170
|
+
});
|
|
171
|
+
</script>
|
|
172
|
+
|
|
173
|
+
<template>
|
|
174
|
+
<div>
|
|
175
|
+
<div ref="container" style="width: 800px" />
|
|
176
|
+
<button @click="viewer?.prevSlide()">‹ Prev</button>
|
|
177
|
+
<span> {{ current + 1 }} / {{ total }} </span>
|
|
178
|
+
<button @click="viewer?.nextSlide()">Next ›</button>
|
|
179
|
+
</div>
|
|
180
|
+
</template>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
</details>
|
|
184
|
+
|
|
185
|
+
<details>
|
|
186
|
+
<summary><strong>Angular 19</strong></summary>
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// Angular 19 — standalone component with signal-based state
|
|
190
|
+
import {
|
|
191
|
+
Component, ElementRef, viewChild,
|
|
192
|
+
signal, AfterViewInit,
|
|
193
|
+
} from '@angular/core';
|
|
194
|
+
import { PptxViewer } from '@silurus/ooxml/pptx';
|
|
195
|
+
|
|
196
|
+
@Component({
|
|
197
|
+
selector: 'app-pptx-viewer',
|
|
198
|
+
standalone: true,
|
|
199
|
+
template: `
|
|
200
|
+
<div>
|
|
201
|
+
<div #container style="width: 800px"></div>
|
|
202
|
+
<button (click)="prev()">‹ Prev</button>
|
|
203
|
+
<span> {{ current() + 1 }} / {{ total() }} </span>
|
|
204
|
+
<button (click)="next()">Next ›</button>
|
|
205
|
+
</div>
|
|
206
|
+
`,
|
|
207
|
+
})
|
|
208
|
+
export class PptxViewerComponent implements AfterViewInit {
|
|
209
|
+
containerEl = viewChild.required<ElementRef<HTMLDivElement>>('container');
|
|
210
|
+
current = signal(0);
|
|
211
|
+
total = signal(0);
|
|
212
|
+
private viewer?: PptxViewer;
|
|
213
|
+
|
|
214
|
+
ngAfterViewInit(): void {
|
|
215
|
+
this.viewer = new PptxViewer(this.containerEl().nativeElement, {
|
|
216
|
+
onSlideChange: (i, t) => { this.current.set(i); this.total.set(t); },
|
|
217
|
+
});
|
|
218
|
+
fetch('/deck.pptx')
|
|
219
|
+
.then(r => r.arrayBuffer())
|
|
220
|
+
.then(buf => this.viewer!.load(buf));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
prev(): void { this.viewer?.prevSlide(); }
|
|
224
|
+
next(): void { this.viewer?.nextSlide(); }
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
> Add `"allowSyntheticDefaultImports": true` and configure `@angular-builders/custom-webpack` (or use `esbuild` builder) with WASM support in your Angular workspace.
|
|
229
|
+
|
|
230
|
+
</details>
|
|
231
|
+
|
|
232
|
+
<details>
|
|
233
|
+
<summary><strong>Svelte 5</strong></summary>
|
|
234
|
+
|
|
235
|
+
```svelte
|
|
236
|
+
<!-- Svelte 5 — runes syntax ($props, $state) -->
|
|
237
|
+
<script lang="ts">
|
|
238
|
+
import { onMount } from 'svelte';
|
|
239
|
+
import { PptxViewer } from '@silurus/ooxml/pptx';
|
|
240
|
+
|
|
241
|
+
let { src }: { src: string } = $props();
|
|
242
|
+
|
|
243
|
+
let container: HTMLDivElement;
|
|
244
|
+
let viewer: PptxViewer;
|
|
245
|
+
let current = $state(0);
|
|
246
|
+
let total = $state(0);
|
|
247
|
+
|
|
248
|
+
onMount(async () => {
|
|
249
|
+
viewer = new PptxViewer(container, {
|
|
250
|
+
onSlideChange: (i, t) => { current = i; total = t; },
|
|
251
|
+
});
|
|
252
|
+
const buf = await fetch(src).then(r => r.arrayBuffer());
|
|
253
|
+
await viewer.load(buf);
|
|
254
|
+
});
|
|
255
|
+
</script>
|
|
256
|
+
|
|
257
|
+
<div>
|
|
258
|
+
<div bind:this={container} style="width: 800px"></div>
|
|
259
|
+
<button onclick={() => viewer?.prevSlide()}>‹ Prev</button>
|
|
260
|
+
<span> {current + 1} / {total} </span>
|
|
261
|
+
<button onclick={() => viewer?.nextSlide()}>Next ›</button>
|
|
262
|
+
</div>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
</details>
|
|
266
|
+
|
|
267
|
+
<details>
|
|
268
|
+
<summary><strong>SolidJS 1.9</strong></summary>
|
|
269
|
+
|
|
270
|
+
```tsx
|
|
271
|
+
// SolidJS 1.9
|
|
272
|
+
import { createSignal, onMount, onCleanup } from 'solid-js';
|
|
273
|
+
import { PptxViewer } from '@silurus/ooxml/pptx';
|
|
274
|
+
|
|
275
|
+
export function PptxViewerComponent(props: { src: string }) {
|
|
276
|
+
let containerEl!: HTMLDivElement;
|
|
277
|
+
let viewer: PptxViewer | undefined;
|
|
278
|
+
const [current, setCurrent] = createSignal(0);
|
|
279
|
+
const [total, setTotal ] = createSignal(0);
|
|
280
|
+
|
|
281
|
+
onMount(async () => {
|
|
282
|
+
viewer = new PptxViewer(containerEl, {
|
|
283
|
+
onSlideChange: (i, t) => { setCurrent(i); setTotal(t); },
|
|
284
|
+
});
|
|
285
|
+
const buf = await fetch(props.src).then(r => r.arrayBuffer());
|
|
286
|
+
await viewer.load(buf);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
onCleanup(() => { /* viewer?.destroy?.() */ });
|
|
290
|
+
|
|
291
|
+
return (
|
|
292
|
+
<div>
|
|
293
|
+
<div ref={containerEl} style={{ width: '800px' }} />
|
|
294
|
+
<button onClick={() => viewer?.prevSlide()}>‹ Prev</button>
|
|
295
|
+
<span> {current() + 1} / {total()} </span>
|
|
296
|
+
<button onClick={() => viewer?.nextSlide()}>Next ›</button>
|
|
297
|
+
</div>
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
</details>
|
|
303
|
+
|
|
304
|
+
<details>
|
|
305
|
+
<summary><strong>Qwik 2</strong></summary>
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
// Qwik 2.0 — dynamic import to keep WASM out of SSR bundle
|
|
309
|
+
import { component$, useSignal, useVisibleTask$ } from '@builder.io/qwik';
|
|
310
|
+
import type { PptxViewer as PptxViewerType } from '@silurus/ooxml/pptx';
|
|
311
|
+
|
|
312
|
+
export const PptxViewerComponent = component$<{ src: string }>(({ src }) => {
|
|
313
|
+
const containerRef = useSignal<HTMLDivElement>();
|
|
314
|
+
const current = useSignal(0);
|
|
315
|
+
const total = useSignal(0);
|
|
316
|
+
let viewer: PptxViewerType | undefined;
|
|
317
|
+
|
|
318
|
+
// useVisibleTask$ runs only in the browser, never during SSR
|
|
319
|
+
useVisibleTask$(async () => {
|
|
320
|
+
if (!containerRef.value) return;
|
|
321
|
+
const { PptxViewer } = await import('@silurus/ooxml/pptx');
|
|
322
|
+
viewer = new PptxViewer(containerRef.value, {
|
|
323
|
+
onSlideChange: (i, t) => { current.value = i; total.value = t; },
|
|
324
|
+
});
|
|
325
|
+
const buf = await fetch(src).then(r => r.arrayBuffer());
|
|
326
|
+
await viewer.load(buf);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
return (
|
|
330
|
+
<div>
|
|
331
|
+
<div ref={containerRef} style={{ width: '800px' }} />
|
|
332
|
+
<button onClick$={() => viewer?.prevSlide()}>‹ Prev</button>
|
|
333
|
+
<span> {current.value + 1} / {total.value} </span>
|
|
334
|
+
<button onClick$={() => viewer?.nextSlide()}>Next ›</button>
|
|
335
|
+
</div>
|
|
336
|
+
);
|
|
337
|
+
});
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
</details>
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Feature Support
|
|
345
|
+
|
|
346
|
+
### PowerPoint (.pptx)
|
|
347
|
+
|
|
348
|
+
| Category | Feature | Status |
|
|
349
|
+
|----------|---------|--------|
|
|
350
|
+
| **Slides** | Slide rendering | ✅ |
|
|
351
|
+
| | Slide layout / master inheritance | ✅ |
|
|
352
|
+
| | Slide size (custom dimensions) | ✅ |
|
|
353
|
+
| | Slide background (solid, gradient, image) | ✅ |
|
|
354
|
+
| | Slide numbers | ✅ |
|
|
355
|
+
| | Notes pages | ❌ |
|
|
356
|
+
| | Animations / transitions | ❌ |
|
|
357
|
+
| **Element types** | Shapes (`sp`) | ✅ |
|
|
358
|
+
| | Pictures (`pic`) | ✅ |
|
|
359
|
+
| | Groups (`grpSp`) with nested transforms | ✅ |
|
|
360
|
+
| | Connectors (`cxnSp`) | ✅ |
|
|
361
|
+
| | Tables (`tbl` in `graphicFrame`) | ✅ |
|
|
362
|
+
| | Charts (bar, line, area, radar, waterfall) | ✅ |
|
|
363
|
+
| | Charts (pie, scatter, bubble) | ❌ |
|
|
364
|
+
| | SmartArt | ❌ |
|
|
365
|
+
| | OLE objects | ❌ |
|
|
366
|
+
| | Video / audio | ❌ |
|
|
367
|
+
| **Shape geometry** | 130+ preset shapes (`prstGeom`) | ✅ |
|
|
368
|
+
| | Custom geometry (`custGeom`) | ✅ |
|
|
369
|
+
| | Rotation and flip (flipH / flipV) | ✅ |
|
|
370
|
+
| | 3D preset shapes | ❌ |
|
|
371
|
+
| **Fills** | Solid fill (`solidFill`) | ✅ |
|
|
372
|
+
| | Linear / radial gradient (`gradFill`) | ✅ |
|
|
373
|
+
| | No fill (`noFill`) | ✅ |
|
|
374
|
+
| | Pattern fill (`pattFill`) | ❌ |
|
|
375
|
+
| | Image fill on shapes (`blipFill` in `sp`) | ✅ |
|
|
376
|
+
| **Strokes** | Solid line color and width | ✅ |
|
|
377
|
+
| | Dash / dot styles | ✅ |
|
|
378
|
+
| | Arrow heads (`headEnd` / `tailEnd`) | ✅ |
|
|
379
|
+
| | Compound / double lines | ❌ |
|
|
380
|
+
| **Shape effects** | Drop shadow (`outerShdw`) | ✅ |
|
|
381
|
+
| | Inner shadow / glow / reflection | ❌ |
|
|
382
|
+
| | Bevel / 3D extrusion | ❌ |
|
|
383
|
+
| **Text — characters** | Bold, italic, underline, strikethrough | ✅ |
|
|
384
|
+
| | Font family, size, color | ✅ |
|
|
385
|
+
| | Superscript / subscript | ✅ |
|
|
386
|
+
| | Hyperlinks | ❌ |
|
|
387
|
+
| | Text shadow / outline effects | ❌ |
|
|
388
|
+
| **Text — paragraphs** | Horizontal alignment (left / center / right / justify) | ✅ |
|
|
389
|
+
| | Vertical anchor (top / center / bottom) | ✅ |
|
|
390
|
+
| | Line spacing (`spcPct`, `spcPts`) | ✅ |
|
|
391
|
+
| | Space before / after paragraph | ✅ |
|
|
392
|
+
| | Bullet points (character and auto-numbered) | ✅ |
|
|
393
|
+
| | Tab stops | ✅ |
|
|
394
|
+
| | Indent / margin | ✅ |
|
|
395
|
+
| | Vertical / RTL text | ❌ |
|
|
396
|
+
| **Text — body** | Text padding (insets) | ✅ |
|
|
397
|
+
| | normAutoFit (shrink to fit) | ✅ |
|
|
398
|
+
| | spAutoFit (expand box) | ✅ |
|
|
399
|
+
| | Word wrap / no wrap | ✅ |
|
|
400
|
+
| **Tables** | Cells, rows, columns | ✅ |
|
|
401
|
+
| | Cell merges (horizontal / vertical) | ✅ |
|
|
402
|
+
| | Cell borders | ✅ |
|
|
403
|
+
| | Cell fills (solid / gradient) | ✅ |
|
|
404
|
+
| | Cell diagonal lines (`lnTlToBr` / `lnBlToTr`) | ✅ |
|
|
405
|
+
| | Table theme styles | ❌ |
|
|
406
|
+
| **Theme** | Scheme colors (dk1/lt1/accent1–6) | ✅ |
|
|
407
|
+
| | Font scheme (`+mj-lt`, `+mn-lt`) | ✅ |
|
|
408
|
+
| | lumMod / lumOff / alpha transforms | ✅ |
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
### Word (.docx)
|
|
413
|
+
|
|
414
|
+
| Category | Feature | Status |
|
|
415
|
+
|----------|---------|--------|
|
|
416
|
+
| **Document** | Page rendering | ✅ |
|
|
417
|
+
| | Page size and margins | ✅ |
|
|
418
|
+
| | Headers / footers (default / first / even) | ✅ |
|
|
419
|
+
| | Section breaks | ❌ |
|
|
420
|
+
| **Text** | Paragraphs | ✅ |
|
|
421
|
+
| | Bold, italic, underline, strikethrough | ✅ |
|
|
422
|
+
| | Font family, size, color | ✅ |
|
|
423
|
+
| | Hyperlinks | ✅ |
|
|
424
|
+
| | Superscript / subscript | ❌ |
|
|
425
|
+
| **Formatting** | Paragraph alignment | ✅ |
|
|
426
|
+
| | Line spacing | ✅ |
|
|
427
|
+
| | Indents and tab stops | ✅ |
|
|
428
|
+
| | Lists (bullet and numbered) | ✅ |
|
|
429
|
+
| | Paragraph styles (Heading 1–6, Normal) | 🔜 Planned |
|
|
430
|
+
| **Elements** | Tables (with borders, fills, merges) | ✅ |
|
|
431
|
+
| | Images (inline and anchored) | ✅ |
|
|
432
|
+
| | Text boxes / drawing shapes | ❌ |
|
|
433
|
+
| **Advanced** | Track changes / comments / footnotes | ❌ |
|
|
434
|
+
| | Mail merge fields | ❌ Not planned |
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
### Excel (.xlsx)
|
|
439
|
+
|
|
440
|
+
| Category | Feature | Status |
|
|
441
|
+
|----------|---------|--------|
|
|
442
|
+
| **Workbook** | Multiple sheets, sheet names | ✅ |
|
|
443
|
+
| **Cells** | Text, number, boolean, error values | ✅ |
|
|
444
|
+
| | Formula results (from cached `<v>`) | ✅ |
|
|
445
|
+
| | Dates (ECMA-376 date format codes) | ✅ |
|
|
446
|
+
| | Rich text (per-run formatting) | ✅ |
|
|
447
|
+
| **Formatting** | Bold, italic, underline, strikethrough | ✅ |
|
|
448
|
+
| | Font family, size, color | ✅ |
|
|
449
|
+
| | Cell background color | ✅ |
|
|
450
|
+
| | Borders | ✅ |
|
|
451
|
+
| | Horizontal / vertical alignment | ✅ |
|
|
452
|
+
| | Text wrapping | ✅ |
|
|
453
|
+
| | Number formats (`0.00`, `%`, `#,##0`, custom date/time) | ✅ |
|
|
454
|
+
| **Structure** | Merged cells | ✅ |
|
|
455
|
+
| | Frozen panes | ✅ |
|
|
456
|
+
| | Row / column sizing (custom widths and heights) | ✅ |
|
|
457
|
+
| | Hidden rows / columns | ✅ |
|
|
458
|
+
| **Elements** | Images (`<xdr:twoCellAnchor>`) | ✅ |
|
|
459
|
+
| | Charts (bar, line, area, radar) | ✅ |
|
|
460
|
+
| | Sparklines | ❌ Not planned |
|
|
461
|
+
| **Advanced** | Conditional formatting (`cellIs`, `colorScale`, `dataBar`, `iconSet`, `top10`, `aboveAverage`) | ✅ |
|
|
462
|
+
| | Pivot tables | ❌ Not planned |
|
|
463
|
+
| | Data validation / comments | ❌ Not planned |
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
## Development
|
|
468
|
+
|
|
469
|
+
```bash
|
|
470
|
+
# Install dependencies
|
|
471
|
+
pnpm install
|
|
472
|
+
|
|
473
|
+
# Build all WASM parsers (requires Rust + wasm-pack)
|
|
474
|
+
pnpm build:wasm
|
|
475
|
+
|
|
476
|
+
# Start Storybook dev server (port 6006)
|
|
477
|
+
pnpm storybook
|
|
478
|
+
|
|
479
|
+
# Type-check all packages
|
|
480
|
+
pnpm typecheck
|
|
481
|
+
|
|
482
|
+
# Run visual regression tests
|
|
483
|
+
pnpm vrt
|
|
484
|
+
|
|
485
|
+
# Build the library
|
|
486
|
+
pnpm build
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### WASM build (individual packages)
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
cd packages/pptx/parser && wasm-pack build --target web && cp pkg/pptx_parser_bg.wasm pkg/pptx_parser.js ../src/wasm/
|
|
493
|
+
cd packages/xlsx/parser && wasm-pack build --target web && cp pkg/xlsx_parser_bg.wasm pkg/xlsx_parser.js ../src/wasm/
|
|
494
|
+
cd packages/docx/parser && wasm-pack build --target web && cp pkg/docx_parser_bg.wasm pkg/docx_parser.js ../src/wasm/
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
## License
|
|
498
|
+
|
|
499
|
+
MIT
|
|
@@ -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};Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return t}});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var e = Object.defineProperty, t = (t, n) => {
|
|
3
|
+
let r = {};
|
|
4
|
+
for (var i in t) e(r, i, {
|
|
5
|
+
get: t[i],
|
|
6
|
+
enumerable: !0
|
|
7
|
+
});
|
|
8
|
+
return n || e(r, Symbol.toStringTag, { value: "Module" }), r;
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { t };
|