@silurus/ooxml 0.57.0 → 0.59.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
@@ -30,7 +30,7 @@ pnpm add @silurus/ooxml
30
30
 
31
31
  > **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/).
32
32
 
33
- > **Bundle size note**: the package is ESM-only (`.mjs`). npm's *Unpacked Size* sums all four entry bundles, including the **opt-in** math engine (MathJax + STIX Two Math, ~3 MB). What actually lands in your app is much smaller — import only the format you need (e.g. `@silurus/ooxml/pptx`). The math engine is a **separate entry** (`@silurus/ooxml/math`): it is bundled **only if you import it and pass it to a viewer** (see [Rendering equations](#rendering-equations)). Viewers that never receive a `math` engine tree-shake the ~3 MB away entirely.
33
+ > **Bundle size note**: the package is ESM-only (`.mjs`). npm's *Unpacked Size* sums all four entry bundles, including the **opt-in** math engine (MathJax + STIX Two Math, ~4 MB). What actually lands in your app is much smaller — import only the format you need (e.g. `@silurus/ooxml/pptx`). The math engine is a **separate entry** (`@silurus/ooxml/math`): it is bundled **only if you import it and pass it to a viewer** (see [Rendering equations](#rendering-equations)). Viewers that never receive a `math` engine tree-shake the ~4 MB away entirely.
34
34
 
35
35
  ---
36
36
 
@@ -63,9 +63,9 @@ pptx.nextSlide();
63
63
 
64
64
  OMML equations (`m:oMath` / `m:oMathPara`) in `.docx`, `.pptx` and `.xlsx` are rendered with
65
65
  [MathJax](https://www.mathjax.org/) + [STIX Two Math](https://github.com/stipub/stixfonts).
66
- That engine is ~3 MB, so it is **opt-in**: import the `math` engine from the separate
66
+ That engine is ~4 MB, so it is **opt-in**: import the `math` engine from the separate
67
67
  `@silurus/ooxml/math` entry and pass it to the viewer. Pass it and equations render;
68
- omit it and the engine is referenced nowhere, so a bundler **tree-shakes the ~3 MB
68
+ omit it and the engine is referenced nowhere, so a bundler **tree-shakes the ~4 MB
69
69
  away entirely** (equations are simply skipped). It is fully self-contained: no
70
70
  network, no cross-origin requests.
71
71
 
@@ -86,6 +86,46 @@ per-render argument. (Excel stores "Insert > Equation" as OMML inside the shared
86
86
  DrawingML `<xdr:txBody>` grammar, so `XlsxViewer` renders equations embedded in
87
87
  shapes / text boxes the same way.)
88
88
 
89
+ ### Off-main-thread rendering
90
+
91
+ By default the headless engines parse in a worker but render on the main thread.
92
+ Pass `mode: 'worker'` to `.load()` to parse **and** render entirely inside a Web
93
+ Worker — the main thread only paints the returned `ImageBitmap` via a
94
+ `bitmaprenderer` context, keeping it free for scrolling and input. It requires
95
+ `Worker` + `OffscreenCanvas`.
96
+
97
+ ```typescript
98
+ import { PptxPresentation } from '@silurus/ooxml/pptx';
99
+
100
+ // Render entirely inside a Web Worker — the main thread only paints bitmaps.
101
+ const pres = await PptxPresentation.load('/deck.pptx', { mode: 'worker' });
102
+ const canvas = document.getElementById('pptx-canvas') as HTMLCanvasElement;
103
+ const bitmap = await pres.renderSlideToBitmap(0, { width: 960, dpr: window.devicePixelRatio });
104
+ const ctx = canvas.getContext('bitmaprenderer') as ImageBitmapRenderingContext;
105
+ ctx.transferFromImageBitmap(bitmap); // consumes the bitmap
106
+ ```
107
+
108
+ The `*ToBitmap` method exists on all three engines —
109
+ `PptxPresentation.renderSlideToBitmap(slideIndex, opts)`,
110
+ `DocxDocument.renderPageToBitmap(pageIndex, opts)`, and
111
+ `XlsxWorkbook.renderViewportToBitmap(sheetIndex, viewport, opts)` (the xlsx
112
+ variant **requires** `opts.width` and `opts.height`, since a worker has no DOM
113
+ element to measure). They work in **both** modes — in main mode they render to
114
+ an internal `OffscreenCanvas` — so you can write mode-agnostic code.
115
+
116
+ Notes:
117
+
118
+ - The returned `ImageBitmap` is owned by the caller: `transferFromImageBitmap`
119
+ consumes it, or call `bitmap.close()` when done.
120
+ - The canvas-target methods (`renderSlide(canvas)`, `renderPage(canvas)`,
121
+ `renderViewport(canvas)`) are unavailable in worker mode — use the `*ToBitmap`
122
+ variants instead.
123
+ - OMML equations require `mode: 'main'`; in worker mode they are skipped (with a
124
+ console warning).
125
+ - Trade-off: worker mode keeps the main thread responsive, but each frame is
126
+ transferred back as an `ImageBitmap`, so a single render can be marginally
127
+ slower than `mode: 'main'`. Choose it for non-blocking UI, not raw speed.
128
+
89
129
  ---
90
130
 
91
131
  <details>
@@ -420,9 +460,11 @@ export const PptxViewerComponent = component$<{ src: string }>(({ src }) => {
420
460
  | | Images (inline and anchored, with text wrap) | ✅ |
421
461
  | | Text boxes / drawing shapes | ✅ |
422
462
  | | WMF / EMF metafile images (legacy vector) | ❌ Not planned |
423
- | **Advanced** | Footnote / endnote reference markers | ✅ |
463
+ | **Advanced** | Footnotes reference markers + bottom-of-page bodies with separator rule, numbered (`w:footnoteReference` / `w:footnoteRef`, §17.11) | ✅ |
464
+ | | Endnotes — reference markers + bodies at document end (`w:endnoteReference`, §17.11) | ✅ |
465
+ | | `w:snapToGrid` opt-out of the document grid (§17.3.1.32) | ✅ |
424
466
  | | Track changes (`w:ins` / `w:del` — author-coloured underline / strikethrough) | ✅ |
425
- | | Comments / footnote bodies (parsed, not yet rendered inline) | ⚠️ |
467
+ | | Comments — author / date / text via the document model (`doc.comments`, §17.13.4; not drawn on the page) | |
426
468
  | | Mail merge fields | ❌ Not planned |
427
469
  | **Interaction** | Text selection (transparent overlay, native copy) | ✅ |
428
470
 
@@ -456,7 +498,7 @@ export const PptxViewerComponent = component$<{ src: string }>(({ src }) => {
456
498
  | **Elements** | Images (`<xdr:twoCellAnchor>`) | ✅ |
457
499
  | | Drawing shapes / text boxes (`xdr:sp`, `xdr:txBody` — 186 preset geometries via the shared engine, with `avLst` adjust handles) | ✅ |
458
500
  | | Math equations in shapes (OMML `m:oMath` / `m:oMathPara` in `xdr:txBody`, incl. `a14:m` / `mc:AlternateContent`; rendered via MathJax — opt-in `@silurus/ooxml/math`) | ✅ |
459
- | | Charts (bar, line, area, radar, scatter / bubble) | ✅ |
501
+ | | Charts (bar, line, area, pie, doughnut, radar, scatter / bubble) | ✅ |
460
502
  | | Chart markers (circle / square / diamond / triangle / x / plus / star / dot / dash, per-point `<c:dPt>` overrides) | ✅ |
461
503
  | | Chart data labels (`<c:dLbl>` per-point with CELLRANGE / VALUE / SERIESNAME / CATEGORYNAME field references, position `l`/`r`/`t`/`b`/`ctr`/`outEnd`) | ✅ |
462
504
  | | Chart error bars (`<c:errBars>` X/Y direction, `cust` / `fixedVal` / `stdErr` / `stdDev` / `percentage`, dashed/styled lines) | ✅ |
@@ -465,7 +507,8 @@ export const PptxViewerComponent = component$<{ src: string }>(({ src }) => {
465
507
  | **Advanced** | Conditional formatting (`cellIs`, `colorScale`, `dataBar`, `iconSet`, `top10`, `aboveAverage`) | ✅ |
466
508
  | | Slicers (static, Office 2010 extension) | ✅ |
467
509
  | | Pivot tables | ❌ Not planned |
468
- | | Data validation / comments | Not planned |
510
+ | | Cell comments / notes (classic `xl/commentsN.xml` + Office-365 threaded comments red triangle indicator + author / text via the worksheet model, shown in an Excel-style hover popup) | ✅ |
511
+ | | Data validation (rules via the worksheet model; `list`-type dropdown arrow on the selected cell whose click opens a panel showing the allowed values — read-only) | ✅ |
469
512
  | **Interaction** | Cell selection (single / range / row / column / all) | ✅ |
470
513
  | | Excel-style row / column header highlight on selection | ✅ |
471
514
  | | Shift+click to extend, Ctrl+C to copy as TSV | ✅ |
@@ -484,7 +527,7 @@ export const PptxViewerComponent = component$<{ src: string }>(({ src }) => {
484
527
  | | Slide size (custom dimensions) | ✅ |
485
528
  | | Slide background (solid, gradient, image) | ✅ |
486
529
  | | Slide numbers | ✅ |
487
- | | Notes pages | |
530
+ | | Speaker notes (plain text via `getNotes()`) | |
488
531
  | | Animations / transitions | ❌ Not planned |
489
532
  | **Element types** | Shapes (`sp`) | ✅ |
490
533
  | | Pictures (`pic`) | ✅ |
@@ -495,14 +538,13 @@ export const PptxViewerComponent = component$<{ src: string }>(({ src }) => {
495
538
  | | Charts (pie, doughnut) | ✅ |
496
539
  | | Charts (scatter — `scatterStyle` marker / line / smooth variants) | ✅ |
497
540
  | | Charts (bubble — `bubbleSize` per-point area scaling) | ✅ |
498
- | | SmartArt | |
499
- | | OLE objects | ❌ |
541
+ | | SmartArt (renders the PowerPoint-saved drawing layout `dsp:drawing`; no native diagram layout engine) | |
542
+ | | OLE embedded objects (`p:oleObj` — preview/icon rendering) | ❌ Not planned |
500
543
  | | Video / audio (poster + interactive playback) | ✅ |
501
544
  | | Ink / handwriting (`p:contentPart`, raster fallback) | ✅ |
502
- | **Shape geometry** | 130+ preset shapes (`prstGeom`) | ✅ |
545
+ | **Shape geometry** | 186 preset shapes (`prstGeom` — incl. 3D presets cube / can / bevel / frame) | ✅ |
503
546
  | | Custom geometry (`custGeom`) on shapes and pictures (clipping) | ✅ |
504
547
  | | Rotation and flip (flipH / flipV) | ✅ |
505
- | | 3D preset shapes | ❌ |
506
548
  | **Fills** | Solid fill (`solidFill`) | ✅ |
507
549
  | | Linear / radial gradient (`gradFill`) | ✅ |
508
550
  | | No fill (`noFill`) | ✅ |
@@ -518,9 +560,10 @@ export const PptxViewerComponent = component$<{ src: string }>(({ src }) => {
518
560
  | | Inner shadow (`innerShdw`) | ✅ |
519
561
  | | Soft edge (`softEdge`) | ✅ |
520
562
  | | Reflection (`reflection`) | ✅ |
521
- | | 3D camera / perspective projection (`scene3d` camera + `rot`) on pictures | ✅ |
522
- | | 3D contour edge (`sp3d` `contourW` / `contourClr`) on pictures — flat approximation, no bevel shading | ⚠️ |
523
- | | Bevel / 3D extrusion (`sp3d` `bevelT` / `bevelB` / `extrusionH`) | ⚠️ parsed; rendering planned |
563
+ | | 3D camera / perspective projection (`scene3d` camera + `rot`) on pictures and shapes — projected shape text is drawn but not selectable | ✅ |
564
+ | | 3D contour edge (`sp3d` `contourW` / `contourClr`) — flat approximation | ⚠️ |
565
+ | | Bevel shading (`sp3d` `bevelT` / `bevelB`) distance-field lip lit by `lightRig`, `matte`/`plastic` materials | ✅ |
566
+ | | 3D extrusion (`sp3d` `extrusionH` / `extrusionClr`) — swept side-wall approximation (visible only under a tilted camera) | ⚠️ |
524
567
  | **Text — characters** | Bold, italic, strikethrough (incl. `dblStrike`) | ✅ |
525
568
  | | Underline styles (`sng` / `dbl` / `dotted` / `dash` / `dashLong` / `dotDash` / `dotDotDash` / `wavy` / `wavyDbl` and `*Heavy` variants) | ✅ |
526
569
  | | Per-run underline colour (`uFill` / `uFillTx`) | ✅ |
@@ -569,7 +612,7 @@ export const PptxViewerComponent = component$<{ src: string }>(({ src }) => {
569
612
 
570
613
  - **[`packages/markdown/`](packages/markdown/)** — `@silurus/ooxml-markdown` and the `ooxml-md` CLI convert `.pptx` / `.docx` / `.xlsx` to GitHub-flavoured markdown via the workspace WASM parsers. Same projection used by the MCP server (~21× smaller than the raw XML on the demo deck, ~8% bigger than a flat-text extractor). Includes a node20-based GitHub Action for bulk repo-wide conversion.
571
614
  - **[`packages/node/`](packages/node/)** — Node-side parsers (`@silurus/ooxml-node`) exposing `parsePptx` / `parseDocx` / `parseXlsx` / `parseXlsxAllSheets` against the workspace WASM artifacts, with no DOM or Web Worker dependency. Useful for CI checks, headless rendering pipelines, and CLI tools. Includes an `ooxml-thumbnail` CLI (pptx-only first pass; requires `skia-canvas`).
572
- - **[`packages/vscode-extension/`](packages/vscode-extension/)** — VS Code extension (`ooxml-viewer`) that registers `CustomEditorProvider`s for `.docx`, `.xlsx`, and `.pptx`, and (opt-in) auto-installs and registers the `ooxml-mcp-server` so AI coding agents in the same window (Copilot Agent mode, Claude, …) can read those files via dedicated tools.
615
+ - **[`packages/vscode-extension/`](packages/vscode-extension/)** — VS Code extension (`ooxml-viewer`) that registers `CustomEditorProvider`s for `.docx`, `.xlsx`, and `.pptx`, and (opt-in) auto-installs and registers the `ooxml-mcp-server` so AI coding agents in the same window (Copilot Agent mode, Claude, …) can read those files via dedicated tools. The preview is offline by default; an opt-in `ooxmlViewer.useGoogleFonts` setting (off, and force-disabled in untrusted workspaces) surfaces the library's metric-compatible font substitution, widening the webview CSP to the Google Fonts CDN only while enabled.
573
616
  - **[`packages/mcp-server/`](packages/mcp-server/)** — Rust MCP server (`ooxml-mcp-server`) exposing the parsers as tools for AI agents (Claude, Copilot, Codex, etc.). Provides structured queries (`docx_get_structure`, `xlsx_get_cell_range`, `pptx_get_slide_structure`, …) so agents can inspect OOXML files without shelling out to `unzip`. Prebuilt binaries are attached to each [GitHub Release](https://github.com/yukiyokotani/office-open-xml-viewer/releases) for macOS / Linux / Windows; the VS Code extension downloads them on demand.
574
617
 
575
618
  ---
@@ -614,7 +657,7 @@ cd packages/pptx/parser && wasm-pack build --target web && cp pkg/pptx_parser_bg
614
657
  new PptxViewer(canvas, { maxZipEntryBytes: 64 * 1024 * 1024 }); // 64 MiB
615
658
  ```
616
659
  Supported uniformly by `DocxViewer`, `PptxViewer`, and `XlsxViewer`. Zero / negative values fall back to the default.
617
- - **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.
660
+ - **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, Office font metric substitutes (Carlito/Caladea), and the script fallback fonts are **not** loaded from Google Fonts unless you pass `useGoogleFonts: true` to the relevant `Viewer` / `load(...)` options — supported uniformly by `DocxViewer`, `PptxViewer`, and `XlsxViewer`. When enabled, fonts for non-Latin scripts are supplied on demand from Noto families so text does not fall back to tofu: Arabic (Noto Naskh/Sans Arabic), CJK (Noto Sans/Serif KR · SC · TC · JP, picked per document language so shared Han glyphs take the right shapes), Cyrillic (Noto Sans/Serif), Hebrew (Noto Sans/Serif Hebrew, RTL), Thai (Noto Sans Thai) and Devanagari (Noto Sans Devanagari). No font binaries ship in the bundle. Enabling this 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.
618
661
  - **XML parsing.** Uses `roxmltree`, which does not resolve external entities (XXE-safe by default).
619
662
 
620
663
  ## License