pptx-angular-viewer 1.1.22 → 1.1.29
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/CHANGELOG.md +86 -0
- package/NOTICE +1 -1
- package/README.md +48 -74
- package/fesm2022/pptx-angular-viewer-index-BfKeZbQy.mjs +358 -0
- package/fesm2022/pptx-angular-viewer-index-BfKeZbQy.mjs.map +1 -0
- package/fesm2022/pptx-angular-viewer.mjs +43551 -26305
- package/fesm2022/pptx-angular-viewer.mjs.map +1 -1
- package/package.json +17 -5
- package/pptx-angular-viewer.css +2 -471
- package/types/pptx-angular-viewer.d.ts +4822 -4188
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
This file is generated from [Conventional Commits](https://www.conventionalcommits.org)
|
|
5
|
+
by [git-cliff](https://git-cliff.org); do not edit it by hand.
|
|
6
|
+
|
|
7
|
+
## [1.1.29](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@1.1.29) - 2026-06-21
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- **shared:** Add Three.js SmartArt 3D model + scene runtime (by @ChristopherVR) ([f949213](https://github.com/ChristopherVR/pptx-viewer/commit/f949213b33ed0dca4c52d5d1ab414c3dba67efe7))
|
|
12
|
+
- **angular:** Opt-in Three.js SmartArt renderer (by @ChristopherVR) ([be6d858](https://github.com/ChristopherVR/pptx-viewer/commit/be6d85818b4a2f70cf644ee91467fd44dc4506de))
|
|
13
|
+
|
|
14
|
+
### Documentation
|
|
15
|
+
|
|
16
|
+
- Sharpen npm descriptions and keywords for discoverability (by @ChristopherVR) ([8fea56d](https://github.com/ChristopherVR/pptx-viewer/commit/8fea56d7650f7dc2f3167dea97b94b612a03a4e7))
|
|
17
|
+
- **angular:** Reword README in plain language (by @ChristopherVR) ([ba72266](https://github.com/ChristopherVR/pptx-viewer/commit/ba722668b0c4846e86837b2cf255198231ab2631))
|
|
18
|
+
|
|
19
|
+
## [1.1.24](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@1.1.24) - 2026-06-20
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
- **vue:** Add pptx-vue-viewer package + bundled pptx-viewer-shared (by @ChristopherVR) ([1b7a958](https://github.com/ChristopherVR/pptx-viewer/commit/1b7a958ce91792a6d174f174932800bc8ff40ef9))
|
|
24
|
+
- **angular:** Add pptx-angular-viewer package + demo (by @ChristopherVR) ([81255a9](https://github.com/ChristopherVR/pptx-viewer/commit/81255a9251e855bc51b97c8dc68b55e71e206882))
|
|
25
|
+
- **angular:** Image & gradient fills in element-style (by @ChristopherVR) ([2457aa2](https://github.com/ChristopherVR/pptx-viewer/commit/2457aa2163e6e6504aa36d464d3686d58f625338))
|
|
26
|
+
- **angular:** Connector, table & clip-path renderers (by @ChristopherVR) ([12bb060](https://github.com/ChristopherVR/pptx-viewer/commit/12bb060841d9cdc2d473c5d3794f37502b6047eb))
|
|
27
|
+
- **angular:** PNG + PDF export (html2canvas-pro + jspdf) (by @ChristopherVR) ([e5aec3d](https://github.com/ChristopherVR/pptx-viewer/commit/e5aec3d58b84407629ca84292fe7c3407bd9d87e))
|
|
28
|
+
- **angular:** Wire signatures panel (parts-reading) into the viewer (by @ChristopherVR) ([d11afb9](https://github.com/ChristopherVR/pptx-viewer/commit/d11afb96195c998f9a56a218fa641b8adbf62fb6))
|
|
29
|
+
|
|
30
|
+
### Bug Fixes
|
|
31
|
+
|
|
32
|
+
- **build:** Make all packages build + publish cleanly; align Vue README (by @ChristopherVR) ([7db5de6](https://github.com/ChristopherVR/pptx-viewer/commit/7db5de6a343887fc1a32dd526ae1ab68e1e3e6e0))
|
|
33
|
+
|
|
34
|
+
### Other
|
|
35
|
+
|
|
36
|
+
- **angular:** Tailwind 4 Office ribbon + pt→px font fix (by @ChristopherVR) ([ad5da60](https://github.com/ChristopherVR/pptx-viewer/commit/ad5da60e73c4a6ea780cda94773b4a74dcea9786))
|
|
37
|
+
|
|
38
|
+
### Refactor
|
|
39
|
+
|
|
40
|
+
- **react:** Consume theme + loader from pptx-viewer-shared (by @ChristopherVR) ([1b93d1f](https://github.com/ChristopherVR/pptx-viewer/commit/1b93d1fccff378b0ac402810a0cbddea46add29c))
|
|
41
|
+
- **angular:** Keep core peer as workspace:\*, resolve at build time (by @ChristopherVR) ([b123ac9](https://github.com/ChristopherVR/pptx-viewer/commit/b123ac99e9611b7f585197d827ba2ac35217997e))
|
|
42
|
+
- **angular:** Remove em-dashes from code comments and prose (by @ChristopherVR) ([0166321](https://github.com/ChristopherVR/pptx-viewer/commit/01663210fd84f60b29c7c6176def02951e3903f3))
|
|
43
|
+
|
|
44
|
+
### Documentation
|
|
45
|
+
|
|
46
|
+
- **angular:** Update PORTING for charts, table rich text, connector routing (by @ChristopherVR) ([3baddb5](https://github.com/ChristopherVR/pptx-viewer/commit/3baddb5363294aa2bcbe08c18f51a7b8a0be4f1d))
|
|
47
|
+
- **angular:** Update PORTING for SmartArt/ink/OLE/3D/zoom + effects (by @ChristopherVR) ([d5393c6](https://github.com/ChristopherVR/pptx-viewer/commit/d5393c6269812941f2314f03e33432c076f39c79))
|
|
48
|
+
- **angular:** Record slide background + hyperlink rendering (by @ChristopherVR) ([0c21fe3](https://github.com/ChristopherVR/pptx-viewer/commit/0c21fe31ad18006c97807b0ea3c3e39bb950d163))
|
|
49
|
+
- **angular:** Update PORTING for parity waves 1-3 (by @ChristopherVR) ([e51e7b9](https://github.com/ChristopherVR/pptx-viewer/commit/e51e7b98e4ee833a28f4f2e2ba1cc0e8b8af881b))
|
|
50
|
+
- **angular:** Record export + editor foundation in PORTING (by @ChristopherVR) ([d6f494d](https://github.com/ChristopherVR/pptx-viewer/commit/d6f494df7c59b0f88c9b4a66bce5705c43c4603c))
|
|
51
|
+
- **angular:** Mark editor interaction UI + save-back done (by @ChristopherVR) ([ce0fddf](https://github.com/ChristopherVR/pptx-viewer/commit/ce0fddff33728155cee86930c5f530c66ed400d3))
|
|
52
|
+
- **angular:** Record drag/resize + inspector panel (by @ChristopherVR) ([04dd906](https://github.com/ChristopherVR/pptx-viewer/commit/04dd9069dec0a384bda83d7d1a8262eb39fb5eba))
|
|
53
|
+
- **angular:** Record editor chrome (panels, toolbar, clipboard, align) (by @ChristopherVR) ([69ba935](https://github.com/ChristopherVR/pptx-viewer/commit/69ba9354218ea9ce3066ea84fa4fa60659ff85d8))
|
|
54
|
+
- **angular:** Record rotation/marquee/group + direct-manipulation complete (by @ChristopherVR) ([bb45ccd](https://github.com/ChristopherVR/pptx-viewer/commit/bb45ccd938b83881d735f205a245c1eb1df3d8c3))
|
|
55
|
+
- **angular:** Record snap guides + slide props; parity summary (by @ChristopherVR) ([f748b18](https://github.com/ChristopherVR/pptx-viewer/commit/f748b1871307c97e2c81b2fa64cc9611f2b559d1))
|
|
56
|
+
- **angular:** Record advanced-subsystem waves 1-2 in PORTING.md (by @ChristopherVR) ([63c78ff](https://github.com/ChristopherVR/pptx-viewer/commit/63c78ff996efe208540a1825d9c301feebb36956))
|
|
57
|
+
- **angular:** Record advanced-subsystem wiring in PORTING.md (by @ChristopherVR) ([ebc72a7](https://github.com/ChristopherVR/pptx-viewer/commit/ebc72a79bf5edb1c13d58a70b7b96a3cecc8a810))
|
|
58
|
+
- **angular:** Record bubble & radar chart kinds in PORTING.md (by @ChristopherVR) ([ea8dd22](https://github.com/ChristopherVR/pptx-viewer/commit/ea8dd226cca868fec18e1c6a2375d25ad942a03f))
|
|
59
|
+
- **angular:** Record parity push (charts, connectors, duotone, editor, export, subsystem wiring) (by @ChristopherVR) ([127a233](https://github.com/ChristopherVR/pptx-viewer/commit/127a2333f84eeb0ffb0956dd2a2d15518f18269e))
|
|
60
|
+
- **angular:** Record full feature parity (animation playback/authoring, mobile chrome) (by @ChristopherVR) ([e48a258](https://github.com/ChristopherVR/pptx-viewer/commit/e48a258e0e2a873f75121aaeff906d56de5b43c6))
|
|
61
|
+
- **angular:** Record depth batch (chart overlays, text warp, annotations, transitions) (by @ChristopherVR) ([e68f07e](https://github.com/ChristopherVR/pptx-viewer/commit/e68f07e3a56ba881bbd0f178e518695baf34d139))
|
|
62
|
+
- **angular:** Record framework-neutral e2e contract parity (by @ChristopherVR) ([6200fc9](https://github.com/ChristopherVR/pptx-viewer/commit/6200fc9cdf38f2c1623b3b519d1499cdc98515b3))
|
|
63
|
+
- **angular:** Record visual-parity audit vs React + remaining chrome gap (by @ChristopherVR) ([f3ae199](https://github.com/ChristopherVR/pptx-viewer/commit/f3ae19991d6208a2654a7137aff08dd3ee43b22e))
|
|
64
|
+
- Streamline npm READMEs and add badges, screenshots, demo links (by @ChristopherVR) ([92e980d](https://github.com/ChristopherVR/pptx-viewer/commit/92e980d434900abd223c4d70c6cae19a623f9ca8))
|
|
65
|
+
- **vue,angular:** Point Try-demo links at per-framework demos (by @ChristopherVR) ([b5e6915](https://github.com/ChristopherVR/pptx-viewer/commit/b5e6915c416075f4f50630d76dfedbc324cde03e))
|
|
66
|
+
- **angular:** Log ribbon-port kickoff + Tailwind foundation status (by @ChristopherVR) ([40e0408](https://github.com/ChristopherVR/pptx-viewer/commit/40e04083c02b2f59f77253743df74218c4bca5b3))
|
|
67
|
+
- **angular:** Log ribbon shell + status bar landed, preflight verified (by @ChristopherVR) ([2b10e74](https://github.com/ChristopherVR/pptx-viewer/commit/2b10e74db1ae065785c8ac8ec50d4f46d3635ad7))
|
|
68
|
+
- **angular:** Log ribbon e2e status (8/10) + known mobile gaps + pt→px note (by @ChristopherVR) ([e0cb539](https://github.com/ChristopherVR/pptx-viewer/commit/e0cb5394473b96c453fce71e9b83205976ebd803))
|
|
69
|
+
- **angular:** Mark ribbon merged to main (by @ChristopherVR) ([9226bdc](https://github.com/ChristopherVR/pptx-viewer/commit/9226bdc08a3e87dc2d2322fbe06c3491d6476c35))
|
|
70
|
+
- **angular:** Log advanced ribbon tabs landed (Transitions/Animations wired, Design partial, Draw UI-only) (by @ChristopherVR) ([deeb6c1](https://github.com/ChristopherVR/pptx-viewer/commit/deeb6c1adfd25516b3d3effe19b136721c9ee8a1))
|
|
71
|
+
- **angular:** Mobile e2e now 10/10 (notes-sheet flow fix); log trunk spec fix (by @ChristopherVR) ([92f4c44](https://github.com/ChristopherVR/pptx-viewer/commit/92f4c44a931233258981e452ecaccd5fe25f1a39))
|
|
72
|
+
- **angular:** Log Insert/View/Design/Draw depth landed (2148 tests, e2e 10/10) (by @ChristopherVR) ([cbe9dc7](https://github.com/ChristopherVR/pptx-viewer/commit/cbe9dc7cb1998d766def97fbb20db4169c869680))
|
|
73
|
+
- **angular:** Log niche wave (snap-to-grid/guides/eyedropper/selection-pane/custom-shows); 2161 tests, e2e 10/10 (by @ChristopherVR) ([fa77b49](https://github.com/ChristopherVR/pptx-viewer/commit/fa77b497938ea9bae60aa3794902f16f710efe54))
|
|
74
|
+
- **angular:** Niche list complete — custom-show present + equation OMML; functional parity reached (by @ChristopherVR) ([ac719c7](https://github.com/ChristopherVR/pptx-viewer/commit/ac719c79af0a78d3d98902577c7c35a38c42423e))
|
|
75
|
+
- **angular:** Trim PORTING.md to status + what's-missing (drop session log) (by @ChristopherVR) ([393d5e2](https://github.com/ChristopherVR/pptx-viewer/commit/393d5e2e10361cd9158cf502c2f15e67bbe9e09c))
|
|
76
|
+
- **angular:** E2e now 28/0 (no skips); remaining = refactor/cosmetic debts only (by @ChristopherVR) ([7817092](https://github.com/ChristopherVR/pptx-viewer/commit/7817092780173c1a288029addfadc6c6e571e871))
|
|
77
|
+
- Remove em-dashes and clarify demo link in viewer packages (by @ChristopherVR) ([f52afff](https://github.com/ChristopherVR/pptx-viewer/commit/f52afffd935016b747116a9909c523021b492225))
|
|
78
|
+
|
|
79
|
+
### Build & CI
|
|
80
|
+
|
|
81
|
+
- **angular:** Adopt Tailwind 4 pipeline for ribbon chrome parity (by @ChristopherVR) ([65cf58f](https://github.com/ChristopherVR/pptx-viewer/commit/65cf58fbcce1fbf3ac0c3ce0f3b49b3c9604d1b1))
|
|
82
|
+
|
|
83
|
+
### Dependencies
|
|
84
|
+
|
|
85
|
+
- **deps:** Update dependencies and CI actions to latest (by @ChristopherVR) ([b1a84a2](https://github.com/ChristopherVR/pptx-viewer/commit/b1a84a26814bfdb9b5d5ef7dd87aeabc4fa82c04))
|
|
86
|
+
- **deps:** Bump all workspace manifest floors to latest (by @ChristopherVR) ([890c33d](https://github.com/ChristopherVR/pptx-viewer/commit/890c33d667a39480a69e6a3da893964382993b29))
|
package/NOTICE
CHANGED
|
@@ -7,7 +7,7 @@ attribution it contains) in any derivative works you distribute.
|
|
|
7
7
|
|
|
8
8
|
This package bundles the following component under a different license:
|
|
9
9
|
|
|
10
|
-
mtx-decompressor
|
|
10
|
+
mtx-decompressor - MicroType Express (MTX) font decompressor
|
|
11
11
|
License: Mozilla Public License 2.0 (MPL-2.0)
|
|
12
12
|
Source: https://github.com/ChristopherVR/pptx-viewer/tree/main/packages/mtx-decompressor
|
|
13
13
|
Origin: Ported from libeot (https://github.com/nicowilliams/libeot)
|
package/README.md
CHANGED
|
@@ -1,26 +1,37 @@
|
|
|
1
1
|
# pptx-angular-viewer
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
component, hand it the file bytes, and it parses and displays the slides as
|
|
6
|
-
scalable HTML/CSS with slide navigation and zoom.
|
|
3
|
+
[](https://www.npmjs.com/package/pptx-angular-viewer)
|
|
4
|
+
[](https://github.com/ChristopherVR/pptx-viewer/blob/main/LICENSE)
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
Show Microsoft PowerPoint (`.pptx`) presentations directly in an Angular app:
|
|
7
|
+
no server, no conversion step, no PowerPoint install required. Drop in a
|
|
8
|
+
`<pptx-viewer>` component, hand it the file's bytes, and it reads and displays
|
|
9
|
+
the slides as real HTML and CSS, with slide navigation and zoom.
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
> The screenshot shows the full-featured **React** editor. This Angular package
|
|
14
|
+
> is at a **read-only viewer** milestone today; see [Limitations](#limitations).
|
|
15
|
+
|
|
16
|
+
The reading is done by the framework-agnostic `pptx-viewer-core` engine, which
|
|
17
|
+
turns a `.pptx` file into a structured slide model. This package is the Angular
|
|
18
|
+
layer that draws that model on screen.
|
|
19
|
+
|
|
20
|
+
<samp>**[▶️ Try the live demo](https://christophervr.github.io/pptx-viewer/demo-angular/)** · **[📦 npm](https://www.npmjs.com/package/pptx-angular-viewer)** · **[📖 Full docs](https://christophervr.github.io/pptx-viewer/)**</samp>
|
|
21
|
+
|
|
22
|
+
> **[▶️ Try the live demo](https://christophervr.github.io/pptx-viewer/demo-angular/)**: open a `.pptx` and render it in your browser, no install required.
|
|
11
23
|
|
|
12
24
|
## Features
|
|
13
25
|
|
|
14
|
-
- **Standalone component
|
|
15
|
-
- **
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
|
|
23
|
-
`fetch`, drag-and-drop, etc.
|
|
26
|
+
- **Standalone component**: `<pptx-viewer>`, no NgModule needed.
|
|
27
|
+
- **Modern Angular**: built on signals and `OnPush`, and works without Zone.js.
|
|
28
|
+
- **Real HTML rendering**: slides are drawn as ordinary HTML and SVG, not as a
|
|
29
|
+
picture, so text stays sharp at any zoom and is selectable and accessible.
|
|
30
|
+
- **Slide navigation**: a thumbnail rail, previous/next, and a slide counter.
|
|
31
|
+
- **Zoom**: in, out, and reset.
|
|
32
|
+
- **Themeable**: change colours through CSS custom properties.
|
|
33
|
+
- **Loads from anywhere**: an `ArrayBuffer` or `Uint8Array` from a file input,
|
|
34
|
+
a `fetch`, drag-and-drop, and so on.
|
|
24
35
|
|
|
25
36
|
## Installation
|
|
26
37
|
|
|
@@ -28,7 +39,7 @@ layer on top of it.
|
|
|
28
39
|
npm install pptx-angular-viewer pptx-viewer-core
|
|
29
40
|
```
|
|
30
41
|
|
|
31
|
-
**Peer requirements:** Angular
|
|
42
|
+
**Peer requirements:** Angular 22+ (`@angular/core`, `@angular/common`),
|
|
32
43
|
`rxjs`, and `pptx-viewer-core`.
|
|
33
44
|
|
|
34
45
|
## Usage
|
|
@@ -98,7 +109,7 @@ bootstrapApplication(AppComponent, {
|
|
|
98
109
|
|
|
99
110
|
### Reading the current presentation back
|
|
100
111
|
|
|
101
|
-
`getContent()`
|
|
112
|
+
`getContent()` turns the current presentation back into `.pptx` bytes. Reach it
|
|
102
113
|
via a template reference or `viewChild`:
|
|
103
114
|
|
|
104
115
|
```ts
|
|
@@ -117,10 +128,10 @@ async save() {
|
|
|
117
128
|
| Input | Type | Default | Description |
|
|
118
129
|
| --------------- | ----------------------------------- | ------- | ----------------------------------------------------------- |
|
|
119
130
|
| `content` | `Uint8Array \| ArrayBuffer \| null` | `null` | The `.pptx` bytes to render. |
|
|
120
|
-
| `theme` | `ViewerTheme` |
|
|
131
|
+
| `theme` | `ViewerTheme` | n/a | Color/radius overrides applied as CSS custom properties. |
|
|
121
132
|
| `class` | `string` | `''` | Class applied to the root element. |
|
|
122
133
|
| `canEdit` | `boolean` | `false` | Reserved for the editor (not yet implemented). |
|
|
123
|
-
| `collaboration` | `CollaborationConfig` |
|
|
134
|
+
| `collaboration` | `CollaborationConfig` | n/a | Reserved for real-time collaboration (not yet implemented). |
|
|
124
135
|
|
|
125
136
|
### Outputs
|
|
126
137
|
|
|
@@ -144,59 +155,22 @@ async save() {
|
|
|
144
155
|
|
|
145
156
|
## Limitations
|
|
146
157
|
|
|
147
|
-
This package is at a **read-only viewer** milestone
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
- No custom-geometry clip-paths; only a few common preset shapes get rounded/
|
|
164
|
-
elliptical corners.
|
|
165
|
-
- No shadows, glow, reflection, soft-edge, 3D bevels, or image effects.
|
|
166
|
-
- No text warp / WordArt, and equations (OMML) are not rendered.
|
|
167
|
-
- Slide background images render; advanced background fills are simplified.
|
|
168
|
-
- Fonts use whatever is available in the browser; embedded-font injection is not
|
|
169
|
-
yet wired up, which can affect text metrics.
|
|
170
|
-
|
|
171
|
-
**Playback & interaction**
|
|
172
|
-
|
|
173
|
-
- Media shows the poster frame only — audio/video playback is not implemented.
|
|
174
|
-
- No animations, slide transitions, or presentation mode yet.
|
|
175
|
-
- The viewer is read-only: no selection, editing, toolbar, or inspector.
|
|
176
|
-
|
|
177
|
-
**Export**
|
|
178
|
-
|
|
179
|
-
- No image/PDF/GIF/video export yet.
|
|
180
|
-
|
|
181
|
-
If you need any of the above today, the underlying `pptx-viewer-core` engine
|
|
182
|
-
already parses most of this data — you can read it from the parsed model even
|
|
183
|
-
where this UI layer doesn't render it yet.
|
|
184
|
-
|
|
185
|
-
## Roadmap
|
|
186
|
-
|
|
187
|
-
Actively being worked on, roughly in priority order:
|
|
188
|
-
|
|
189
|
-
1. **Richer rendering** — gradient/pattern fills, preset-geometry clip-paths,
|
|
190
|
-
then tables, connectors, and charts.
|
|
191
|
-
2. **Effects** — shadows, glow, image effects, and 3D styling.
|
|
192
|
-
3. **Editing** — selection, transform, and an editor chrome (toolbar/inspector),
|
|
193
|
-
unlocking `canEdit`, `dirtyChange`, and `contentChange`.
|
|
194
|
-
4. **Animations, transitions, and presentation mode.**
|
|
195
|
-
5. **Export** — PNG/PDF/GIF/video.
|
|
196
|
-
6. **Real-time collaboration** (the `collaboration` input).
|
|
197
|
-
7. **Font embedding/injection** for higher text fidelity.
|
|
198
|
-
|
|
199
|
-
Progress and design notes live in [`PORTING.md`](./PORTING.md).
|
|
158
|
+
This package is at a **read-only viewer** milestone: it shows the content of a
|
|
159
|
+
slide, but richer visual effects and editing are not built yet.
|
|
160
|
+
|
|
161
|
+
- **Shown today:** text (with mixed formatting), shapes (solid fill, outline, and
|
|
162
|
+
basic rounded corners), pictures and images, the still frame of a video, and
|
|
163
|
+
nested groups.
|
|
164
|
+
- **Shown as placeholders:** tables, charts, SmartArt, connectors, ink, OLE
|
|
165
|
+
objects, 3D models, and zoom links appear as labelled boxes for now.
|
|
166
|
+
- **Not built yet:** gradient, pattern, and image fills; custom shape outlines;
|
|
167
|
+
effects (shadows, glow, 3D, image filters); warped text and equations;
|
|
168
|
+
embedded fonts; video and audio playback; animations, transitions, and
|
|
169
|
+
presentation mode; editing (selecting, the toolbar, the inspector); and export.
|
|
170
|
+
|
|
171
|
+
The `pptx-viewer-core` engine already reads most of this data, so you can get it
|
|
172
|
+
from the parsed model even where this UI does not draw it yet. Progress, the
|
|
173
|
+
roadmap, and design notes live in [`PORTING.md`](./PORTING.md).
|
|
200
174
|
|
|
201
175
|
## Build (contributing)
|
|
202
176
|
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { CanvasTexture, SRGBColorSpace, LinearFilter, Shape, ExtrudeGeometry, MeshStandardMaterial, Color, Mesh, EdgesGeometry, LineBasicMaterial, LineSegments, PlaneGeometry, MeshBasicMaterial, Euler, Vector3, BufferGeometry, Line, Group, WebGLRenderer, Scene, PerspectiveCamera, AmbientLight, DirectionalLight } from 'three';
|
|
2
|
+
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Three.js SmartArt renderer - front-face text textures.
|
|
6
|
+
*
|
|
7
|
+
* Renders a node label onto an offscreen 2D canvas and wraps it as a
|
|
8
|
+
* `THREE.CanvasTexture`, sized to sit on the extruded block's front face. Text
|
|
9
|
+
* is word-wrapped and auto-shrunk to fit the node footprint. Returns `null` in
|
|
10
|
+
* non-DOM environments (SSR / unit tests).
|
|
11
|
+
*/
|
|
12
|
+
/** Supersampling factor for crisp text at oblique camera angles. */
|
|
13
|
+
const SUPERSAMPLE = 4;
|
|
14
|
+
/** Fraction of the footprint the text plane occupies (inset padding). */
|
|
15
|
+
const FILL = 0.86;
|
|
16
|
+
/** Greedily word-wrap `text` to lines that fit `maxWidth` at the given font. */
|
|
17
|
+
function wrapLines(ctx, text, maxWidth) {
|
|
18
|
+
const words = text.split(/\s+/u).filter(Boolean);
|
|
19
|
+
if (words.length === 0) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
const lines = [];
|
|
23
|
+
let line = words[0];
|
|
24
|
+
for (let i = 1; i < words.length; i++) {
|
|
25
|
+
const candidate = `${line} ${words[i]}`;
|
|
26
|
+
if (ctx.measureText(candidate).width <= maxWidth) {
|
|
27
|
+
line = candidate;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
lines.push(line);
|
|
31
|
+
line = words[i];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
lines.push(line);
|
|
35
|
+
return lines;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build a label texture for a node's front face.
|
|
39
|
+
*
|
|
40
|
+
* @param text Label text.
|
|
41
|
+
* @param color CSS colour for the text.
|
|
42
|
+
* @param fontSize Requested font size in layout pixels.
|
|
43
|
+
* @param footW Node footprint width (layout pixels).
|
|
44
|
+
* @param footH Node footprint height (layout pixels).
|
|
45
|
+
* @returns The texture + plane size, or `null` when no DOM / empty text.
|
|
46
|
+
*/
|
|
47
|
+
function makeTextTexture(text, color, fontSize, footW, footH) {
|
|
48
|
+
if (typeof document === 'undefined' || !text.trim() || footW <= 0 || footH <= 0) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const worldWidth = footW * FILL;
|
|
52
|
+
const worldHeight = footH * FILL;
|
|
53
|
+
const canvas = document.createElement('canvas');
|
|
54
|
+
canvas.width = Math.max(8, Math.round(worldWidth * SUPERSAMPLE));
|
|
55
|
+
canvas.height = Math.max(8, Math.round(worldHeight * SUPERSAMPLE));
|
|
56
|
+
const ctx = canvas.getContext('2d');
|
|
57
|
+
if (!ctx) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const family = 'system-ui, -apple-system, Segoe UI, Roboto, sans-serif';
|
|
61
|
+
const maxTextWidth = canvas.width * 0.94;
|
|
62
|
+
// Shrink the font until the wrapped block fits the canvas height.
|
|
63
|
+
let px = Math.max(6, fontSize) * SUPERSAMPLE;
|
|
64
|
+
let lines = [];
|
|
65
|
+
for (let attempt = 0; attempt < 12; attempt++) {
|
|
66
|
+
ctx.font = `600 ${px}px ${family}`;
|
|
67
|
+
lines = wrapLines(ctx, text, maxTextWidth);
|
|
68
|
+
const lineHeight = px * 1.2;
|
|
69
|
+
if (lines.length * lineHeight <= canvas.height * 0.96 || px <= 6 * SUPERSAMPLE) {
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
px *= 0.88;
|
|
73
|
+
}
|
|
74
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
75
|
+
ctx.fillStyle = color;
|
|
76
|
+
ctx.textAlign = 'center';
|
|
77
|
+
ctx.textBaseline = 'middle';
|
|
78
|
+
ctx.font = `600 ${px}px ${family}`;
|
|
79
|
+
const lineHeight = px * 1.2;
|
|
80
|
+
const blockHeight = lines.length * lineHeight;
|
|
81
|
+
const startY = canvas.height / 2 - blockHeight / 2 + lineHeight / 2;
|
|
82
|
+
for (let i = 0; i < lines.length; i++) {
|
|
83
|
+
ctx.fillText(lines[i], canvas.width / 2, startY + i * lineHeight);
|
|
84
|
+
}
|
|
85
|
+
const texture = new CanvasTexture(canvas);
|
|
86
|
+
texture.colorSpace = SRGBColorSpace;
|
|
87
|
+
texture.minFilter = LinearFilter;
|
|
88
|
+
texture.magFilter = LinearFilter;
|
|
89
|
+
texture.needsUpdate = true;
|
|
90
|
+
return { texture, worldWidth, worldHeight };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Three.js SmartArt renderer - mesh-group construction.
|
|
95
|
+
*
|
|
96
|
+
* Turns a pure {@link SmartArt3DModel} into a `THREE.Group` of extruded blocks
|
|
97
|
+
* (with bevels, edge outlines, and front-face text planes) plus connector
|
|
98
|
+
* lines. All allocated GPU resources are tracked so the caller can dispose them
|
|
99
|
+
* deterministically.
|
|
100
|
+
*/
|
|
101
|
+
/** Build the extruded geometry for one node. */
|
|
102
|
+
function extrudeGeometry(m) {
|
|
103
|
+
const shape = new Shape();
|
|
104
|
+
m.outline.forEach((p, i) => {
|
|
105
|
+
if (i === 0) {
|
|
106
|
+
shape.moveTo(p.x, p.y);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
shape.lineTo(p.x, p.y);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
shape.closePath();
|
|
113
|
+
const bevelEnabled = m.bevel > 0;
|
|
114
|
+
return new ExtrudeGeometry(shape, {
|
|
115
|
+
depth: m.depth,
|
|
116
|
+
bevelEnabled,
|
|
117
|
+
bevelThickness: m.bevel,
|
|
118
|
+
bevelSize: m.bevel,
|
|
119
|
+
bevelSegments: 2,
|
|
120
|
+
curveSegments: m.rounded ? 24 : 1,
|
|
121
|
+
steps: 1,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/** Add the extruded block + edge outline for one node to the group. */
|
|
125
|
+
function addBlock(group, disposables, m) {
|
|
126
|
+
const geo = extrudeGeometry(m);
|
|
127
|
+
const material = new MeshStandardMaterial({
|
|
128
|
+
color: new Color(m.fill),
|
|
129
|
+
metalness: 0.12,
|
|
130
|
+
roughness: 0.52,
|
|
131
|
+
transparent: m.opacity < 1,
|
|
132
|
+
opacity: m.opacity,
|
|
133
|
+
});
|
|
134
|
+
const mesh = new Mesh(geo, material);
|
|
135
|
+
mesh.position.set(m.position.x, m.position.y, m.position.z);
|
|
136
|
+
mesh.rotation.set(m.rotation.x, m.rotation.y, m.rotation.z);
|
|
137
|
+
group.add(mesh);
|
|
138
|
+
disposables.push(geo, material);
|
|
139
|
+
if (m.strokeWidth > 0) {
|
|
140
|
+
const edges = new EdgesGeometry(geo, 30);
|
|
141
|
+
const lineMaterial = new LineBasicMaterial({ color: new Color(m.stroke) });
|
|
142
|
+
const line = new LineSegments(edges, lineMaterial);
|
|
143
|
+
line.position.copy(mesh.position);
|
|
144
|
+
line.rotation.copy(mesh.rotation);
|
|
145
|
+
group.add(line);
|
|
146
|
+
disposables.push(edges, lineMaterial);
|
|
147
|
+
}
|
|
148
|
+
return geo;
|
|
149
|
+
}
|
|
150
|
+
/** Add a front-face text plane for one node, if it has a label. */
|
|
151
|
+
function addLabel(group, disposables, m) {
|
|
152
|
+
if (!m.text) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const tex = makeTextTexture(m.text, m.textColor, m.fontSize, m.halfWidth * 2, m.halfHeight * 2);
|
|
156
|
+
if (!tex) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const planeGeo = new PlaneGeometry(tex.worldWidth, tex.worldHeight);
|
|
160
|
+
const planeMaterial = new MeshBasicMaterial({
|
|
161
|
+
map: tex.texture,
|
|
162
|
+
transparent: true,
|
|
163
|
+
depthWrite: false,
|
|
164
|
+
});
|
|
165
|
+
const plane = new Mesh(planeGeo, planeMaterial);
|
|
166
|
+
// Float just past the front (+z) face, clearing any bevel, following the
|
|
167
|
+
// mesh's rotation so the label sits flat on the (possibly rotated) face.
|
|
168
|
+
const euler = new Euler(m.rotation.x, m.rotation.y, m.rotation.z);
|
|
169
|
+
const offset = new Vector3(0, 0, m.depth + m.bevel + 0.4).applyEuler(euler);
|
|
170
|
+
plane.position.set(m.position.x + offset.x, m.position.y + offset.y, m.position.z + offset.z);
|
|
171
|
+
plane.rotation.copy(euler);
|
|
172
|
+
group.add(plane);
|
|
173
|
+
disposables.push(planeGeo, planeMaterial, tex.texture);
|
|
174
|
+
}
|
|
175
|
+
/** Add a connector poly-line on the base plane. */
|
|
176
|
+
function addConnectors(group, disposables, model) {
|
|
177
|
+
for (const c of model.connectors) {
|
|
178
|
+
if (c.points.length < 2) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const geo = new BufferGeometry().setFromPoints(c.points.map((p) => new Vector3(p.x, p.y, p.z)));
|
|
182
|
+
const material = new LineBasicMaterial({
|
|
183
|
+
color: new Color(c.color),
|
|
184
|
+
transparent: true,
|
|
185
|
+
opacity: 0.7,
|
|
186
|
+
});
|
|
187
|
+
group.add(new Line(geo, material));
|
|
188
|
+
disposables.push(geo, material);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Build a `THREE.Group` for a SmartArt 3D model. The group is centred on the
|
|
193
|
+
* origin (the model positions already are); callers add it to the scene.
|
|
194
|
+
*/
|
|
195
|
+
function buildMeshGroup(model) {
|
|
196
|
+
const group = new Group();
|
|
197
|
+
const disposables = [];
|
|
198
|
+
for (const m of model.meshes) {
|
|
199
|
+
addBlock(group, disposables, m);
|
|
200
|
+
addLabel(group, disposables, m);
|
|
201
|
+
}
|
|
202
|
+
addConnectors(group, disposables, model);
|
|
203
|
+
return {
|
|
204
|
+
group,
|
|
205
|
+
dispose() {
|
|
206
|
+
for (const d of disposables) {
|
|
207
|
+
d.dispose();
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Three.js SmartArt renderer - vanilla scene runtime.
|
|
215
|
+
*
|
|
216
|
+
* Frames a {@link SmartArt3DModel} in a WebGL scene (lights, perspective
|
|
217
|
+
* camera, optional OrbitControls, render loop) on a caller-provided canvas.
|
|
218
|
+
* Pure vanilla three.js - no framework code - so the React, Vue, and Angular
|
|
219
|
+
* bindings all mount it through a thin canvas wrapper. `three` is imported here
|
|
220
|
+
* only; this module lives behind the `pptx-viewer-shared/smartart-3d` subpath so
|
|
221
|
+
* it is lazily loaded and `three` stays an optional dependency.
|
|
222
|
+
*/
|
|
223
|
+
const FOV = 42;
|
|
224
|
+
/** Bounding sphere of all meshes (expanded by footprint/depth) + connectors. */
|
|
225
|
+
function contentSphere(model) {
|
|
226
|
+
let minX = Infinity;
|
|
227
|
+
let minY = Infinity;
|
|
228
|
+
let minZ = Infinity;
|
|
229
|
+
let maxX = -Infinity;
|
|
230
|
+
let maxY = -Infinity;
|
|
231
|
+
let maxZ = -Infinity;
|
|
232
|
+
const expand = (x, y, z, r) => {
|
|
233
|
+
minX = Math.min(minX, x - r);
|
|
234
|
+
minY = Math.min(minY, y - r);
|
|
235
|
+
minZ = Math.min(minZ, z - r);
|
|
236
|
+
maxX = Math.max(maxX, x + r);
|
|
237
|
+
maxY = Math.max(maxY, y + r);
|
|
238
|
+
maxZ = Math.max(maxZ, z + r);
|
|
239
|
+
};
|
|
240
|
+
for (const m of model.meshes) {
|
|
241
|
+
const r = Math.max(m.halfWidth, m.halfHeight) + m.depth + m.bevel;
|
|
242
|
+
expand(m.position.x, m.position.y, m.position.z, r);
|
|
243
|
+
}
|
|
244
|
+
for (const c of model.connectors) {
|
|
245
|
+
for (const p of c.points) {
|
|
246
|
+
expand(p.x, p.y, p.z, 1);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (!Number.isFinite(minX)) {
|
|
250
|
+
const fallback = Math.max(model.bounds.width, model.bounds.height) / 2 || 1;
|
|
251
|
+
return { cx: 0, cy: 0, cz: 0, radius: fallback };
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
cx: (minX + maxX) / 2,
|
|
255
|
+
cy: (minY + maxY) / 2,
|
|
256
|
+
cz: (minZ + maxZ) / 2,
|
|
257
|
+
radius: 0.5 * Math.hypot(maxX - minX, maxY - minY, maxZ - minZ) || 1,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
/** Camera distance that frames a bounding sphere of `radius` at the given FOV. */
|
|
261
|
+
function frameDistance(radius, aspect) {
|
|
262
|
+
const vFov = (FOV * Math.PI) / 180;
|
|
263
|
+
const hFov = 2 * Math.atan(Math.tan(vFov / 2) * aspect);
|
|
264
|
+
const minFov = Math.min(vFov, hFov);
|
|
265
|
+
return (radius / Math.sin(minFov / 2)) * 1.1;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Mount a SmartArt 3D model onto a canvas and start rendering.
|
|
269
|
+
*
|
|
270
|
+
* @returns a handle for resizing, toggling interactivity, and disposal.
|
|
271
|
+
*/
|
|
272
|
+
function mountSmartArt3D(canvas, model, width, height, options = {}) {
|
|
273
|
+
const renderer = new WebGLRenderer({ canvas, antialias: true, alpha: !options.background });
|
|
274
|
+
renderer.setPixelRatio(Math.min(typeof window === 'undefined' ? 1 : window.devicePixelRatio || 1, options.maxPixelRatio ?? 2));
|
|
275
|
+
renderer.setSize(width, height, false);
|
|
276
|
+
const scene = new Scene();
|
|
277
|
+
if (options.background) {
|
|
278
|
+
scene.background = new Color(options.background);
|
|
279
|
+
}
|
|
280
|
+
const { cx, cy, cz, radius } = contentSphere(model);
|
|
281
|
+
const aspect = width / Math.max(1, height);
|
|
282
|
+
const dist = frameDistance(radius, aspect);
|
|
283
|
+
const camera = new PerspectiveCamera(FOV, aspect, 0.1, dist * 8 + radius * 4);
|
|
284
|
+
// A slight elevation + offset gives the extrusion/spatial depth a readable
|
|
285
|
+
// three-quarter presence, framing the content's own centroid.
|
|
286
|
+
camera.position.set(cx + radius * 0.25, cy + radius * 0.3, cz + dist);
|
|
287
|
+
camera.lookAt(cx, cy, cz);
|
|
288
|
+
scene.add(new AmbientLight(0xffffff, 0.62));
|
|
289
|
+
const key = new DirectionalLight(0xffffff, 0.95);
|
|
290
|
+
key.position.set(cx + radius, cy + radius * 1.4, cz + dist);
|
|
291
|
+
scene.add(key);
|
|
292
|
+
const fill = new DirectionalLight(0xffffff, 0.3);
|
|
293
|
+
fill.position.set(cx - radius, cy - radius * 0.6, cz + dist * 0.6);
|
|
294
|
+
scene.add(fill);
|
|
295
|
+
const built = buildMeshGroup(model);
|
|
296
|
+
scene.add(built.group);
|
|
297
|
+
let controls = null;
|
|
298
|
+
const enableControls = (on) => {
|
|
299
|
+
if (on && !controls) {
|
|
300
|
+
controls = new OrbitControls(camera, canvas);
|
|
301
|
+
controls.enablePan = false;
|
|
302
|
+
controls.target.set(cx, cy, cz);
|
|
303
|
+
controls.minDistance = dist * 0.4;
|
|
304
|
+
controls.maxDistance = dist * 3;
|
|
305
|
+
controls.update();
|
|
306
|
+
}
|
|
307
|
+
else if (!on && controls) {
|
|
308
|
+
controls.dispose();
|
|
309
|
+
controls = null;
|
|
310
|
+
}
|
|
311
|
+
if (controls) {
|
|
312
|
+
controls.autoRotate = Boolean(options.autoRotate);
|
|
313
|
+
controls.autoRotateSpeed = 1.2;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
enableControls(Boolean(options.interactive));
|
|
317
|
+
let frame = 0;
|
|
318
|
+
let disposed = false;
|
|
319
|
+
const renderLoop = () => {
|
|
320
|
+
if (disposed) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
frame = requestAnimationFrame(renderLoop);
|
|
324
|
+
controls?.update();
|
|
325
|
+
renderer.render(scene, camera);
|
|
326
|
+
};
|
|
327
|
+
frame = requestAnimationFrame(renderLoop);
|
|
328
|
+
return {
|
|
329
|
+
resize(w, h) {
|
|
330
|
+
camera.aspect = w / Math.max(1, h);
|
|
331
|
+
camera.updateProjectionMatrix();
|
|
332
|
+
renderer.setSize(w, h, false);
|
|
333
|
+
},
|
|
334
|
+
setInteractive(on) {
|
|
335
|
+
enableControls(on);
|
|
336
|
+
},
|
|
337
|
+
dispose() {
|
|
338
|
+
disposed = true;
|
|
339
|
+
cancelAnimationFrame(frame);
|
|
340
|
+
controls?.dispose();
|
|
341
|
+
built.dispose();
|
|
342
|
+
renderer.dispose();
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* `pptx-viewer-shared/smartart-3d` - vanilla three.js SmartArt scene runtime.
|
|
349
|
+
*
|
|
350
|
+
* Lazily imported by each binding's SmartArt 3D wrapper so `three` stays an
|
|
351
|
+
* optional dependency: when it is not installed the dynamic import rejects and
|
|
352
|
+
* the binding falls back to the SVG `SmartArtRenderer`. The pure model builder
|
|
353
|
+
* (`buildSmartArt3DModel`) and its types live in the main barrel
|
|
354
|
+
* (`pptx-viewer-shared`) and should be imported from there directly.
|
|
355
|
+
*/
|
|
356
|
+
|
|
357
|
+
export { mountSmartArt3D };
|
|
358
|
+
//# sourceMappingURL=pptx-angular-viewer-index-BfKeZbQy.mjs.map
|