react-pptx-preview-kit 0.1.1

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 ADDED
@@ -0,0 +1,147 @@
1
+ # pptx-preview
2
+
3
+ A pure-frontend `.pptx` renderer for the browser β€” no server, no dependencies on chart libraries.
4
+
5
+ **🎯 Goal: 100% visual fidelity compared to Microsoft PowerPoint.**
6
+
7
+ ## Installation (NPM Package)
8
+
9
+ ```bash
10
+ npm install react-pptx-preview-kit
11
+ ```
12
+
13
+ ## Usage (React)
14
+
15
+ The core component `<PptxPreview />` is designed to be plug-and-play. It takes a single `file` prop of type `ArrayBuffer` and automatically handles slide layout parsing, bounding boxes, text fit, UI zoom controls, and navigation.
16
+
17
+ ```tsx
18
+ import React, { useState } from 'react';
19
+ import { PptxPreview } from 'react-pptx-preview-kit';
20
+
21
+ export default function MyPresentationApp() {
22
+ const [fileBuffer, setFileBuffer] = useState<ArrayBuffer | null>(null);
23
+
24
+ /* Read the file when a user uploads it */
25
+ const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
26
+ const file = e.target.files?.[0];
27
+ if (file) {
28
+ const reader = new FileReader();
29
+ reader.onload = (event) => {
30
+ if (event.target?.result) {
31
+ setFileBuffer(event.target.result as ArrayBuffer);
32
+ }
33
+ };
34
+ reader.readAsArrayBuffer(file);
35
+ }
36
+ };
37
+
38
+ return (
39
+ <div style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
40
+ {/* Header with Upload button */}
41
+ <header style={{ padding: '16px', backgroundColor: '#1e1e2e', color: 'white' }}>
42
+ <h2 style={{ margin: '0 0 10px 0' }}>My PPTX Viewer</h2>
43
+ <input type="file" accept=".pptx" onChange={handleFileUpload} />
44
+ </header>
45
+
46
+ {/* Main Viewer Area */}
47
+ <main style={{ flex: 1, overflow: 'hidden', backgroundColor: '#f5f5f5' }}>
48
+ {fileBuffer ? (
49
+ <PptxPreview file={fileBuffer} />
50
+ ) : (
51
+ <p style={{ textAlign: 'center', marginTop: '50px', color: '#888' }}>
52
+ Please upload a .pptx file to view...
53
+ </p>
54
+ )}
55
+ </main>
56
+ </div>
57
+ );
58
+ }
59
+ ```
60
+
61
+ ### Example 2: Load from Remote URL (with Authentication)
62
+
63
+ Since `<PptxPreview />` only relies on a raw `ArrayBuffer`, it is completely unopinionated about **how** you get the file. You can fetch it from an S3 bucket, a secure internal API, or any remote URLβ€”making it trivial to support Authentication!
64
+
65
+ ```tsx
66
+ import React, { useState, useEffect } from 'react';
67
+ import { PptxPreview } from 'react-pptx-preview-kit';
68
+
69
+ export default function RemoteViewer({ documentId }: { documentId: string }) {
70
+ const [fileBuffer, setFileBuffer] = useState<ArrayBuffer | null>(null);
71
+ const [loading, setLoading] = useState(true);
72
+
73
+ useEffect(() => {
74
+ async function fetchSecureDocument() {
75
+ try {
76
+ const response = await fetch(`https://api.yourcompany.com/files/${documentId}`, {
77
+ headers: {
78
+ // Include your Authentication token here!
79
+ 'Authorization': `Bearer YOUR_ACCESS_TOKEN`
80
+ }
81
+ });
82
+
83
+ if (!response.ok) throw new Error("Failed to fetch document");
84
+
85
+ // Convert the response to ArrayBuffer
86
+ const buffer = await response.arrayBuffer();
87
+ setFileBuffer(buffer);
88
+ } catch (err) {
89
+ console.error("Download error:", err);
90
+ } finally {
91
+ setLoading(false);
92
+ }
93
+ }
94
+
95
+ fetchSecureDocument();
96
+ }, [documentId]);
97
+
98
+ if (loading) return <p style={{ padding: 20 }}>Downloading presentation securely...</p>;
99
+ if (!fileBuffer) return <p style={{ padding: 20 }}>Error loading document.</p>;
100
+
101
+ return (
102
+ <div style={{ height: '800px', border: '1px solid #ccc' }}>
103
+ <PptxPreview file={fileBuffer} />
104
+ </div>
105
+ );
106
+ }
107
+ ```
108
+
109
+ ## Local Development (Contributing)
110
+
111
+ If you are developing inside this repository to improve the library:
112
+
113
+ ```bash
114
+ npm install
115
+ npm run dev # Spin up local demo UI at http://localhost:5173
116
+ npm run build # Build library distributions to dist/
117
+ ```
118
+
119
+ ## Features
120
+
121
+ - βœ… 150+ preset shape geometries + custom freeform shapes (custGeom)
122
+ - βœ… Solid, gradient (SVG defs), and image fills
123
+ - βœ… Shadow effects, borders, image cropping
124
+ - βœ… Tables with merged cells and per-cell styling
125
+ - βœ… Text: fonts, bullets, numbered lists, hyperlinks, super/subscript
126
+ - βœ… Charts: bar, line, area, pie β€” pure SVG
127
+ - βœ… Theme / layout / master inheritance chain
128
+ - βœ… Zoom controls, slide counter, fullscreen, keyboard navigation
129
+
130
+ ## Docs
131
+
132
+ | Document | Contents |
133
+ |---|---|
134
+ | [AGENTS.md](./AGENTS.md) | Guidelines for AI agents contributing to this project |
135
+ | [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) | Codebase structure, data flow, coordinate system |
136
+ | [docs/ROADMAP.md](./docs/ROADMAP.md) | Current completion status and remaining work |
137
+
138
+ ## Tech Stack
139
+
140
+ - **React 18 + Vite + TypeScript**
141
+ - **fast-xml-parser** β€” PPTX XML parsing
142
+ - **JSZip** β€” ZIP extraction
143
+ - No chart libs. No rendering dependencies.
144
+
145
+ ## License
146
+
147
+ MIT
@@ -0,0 +1,99 @@
1
+ import { r as k, b as S, g as t, a as x, c as A } from "./index-BiEAfEne.js";
2
+ async function T(c, n, u, o) {
3
+ var h;
4
+ const b = u[n];
5
+ if (!b) return null;
6
+ const s = k(o, b), a = await c.getXml(s);
7
+ if (!a) return null;
8
+ const p = s.substring(0, s.lastIndexOf("/")), r = s.substring(s.lastIndexOf("/") + 1), m = `${p}/_rels/${r}.rels`, g = await c.getXml(m);
9
+ S(g);
10
+ const C = (h = a == null ? void 0 : a["c:chartSpace"]) == null ? void 0 : h["c:chart"];
11
+ if (!C) return null;
12
+ const P = L(C), R = t(C, "c:plotArea"), d = t(C, "c:legend"), y = !!d;
13
+ let l = "bottom";
14
+ if (y) {
15
+ const f = x(t(d, "c:legendPos"), "val");
16
+ f === "r" ? l = "right" : f === "l" ? l = "left" : f === "t" || f === "tr" ? l = "top" : f === "b" && (l = "bottom");
17
+ } else
18
+ l = "none";
19
+ const i = E(R), e = ["c:barChart", "c:lineChart", "c:pieChart", "c:scatterChart", "c:areaChart", "c:doughnutChart"];
20
+ for (const f of e) {
21
+ const F = t(R, f);
22
+ if (F) {
23
+ const v = V(f, F, P);
24
+ return v.hasLegend = y, v.legendPos = l, v.axes = i, v;
25
+ }
26
+ }
27
+ return { type: "unknown", title: P, categories: [], series: [], hasLegend: y };
28
+ }
29
+ function L(c) {
30
+ const n = t(c, "c:title");
31
+ if (!n) return;
32
+ const u = t(n, "c:tx"), o = t(u, "c:rich");
33
+ return o && A(o, "a:p").map((s) => A(s, "a:r").map((p) => p["a:t"] ?? "").join("")).join(" ").trim() || void 0;
34
+ }
35
+ function V(c, n, u) {
36
+ const b = {
37
+ "c:barChart": "bar",
38
+ "c:lineChart": "line",
39
+ "c:pieChart": "pie",
40
+ "c:scatterChart": "scatter",
41
+ "c:areaChart": "area",
42
+ "c:doughnutChart": "pie"
43
+ }[c] || "unknown", s = [], a = [], p = A(n, "c:ser");
44
+ for (const r of p) {
45
+ const m = t(r, "c:tx");
46
+ let g = "";
47
+ if (m) {
48
+ const i = t(m, "c:strRef");
49
+ if (i) {
50
+ const e = t(i, "c:strCache"), h = e ? t(e, "c:pt") : null;
51
+ g = h && h["c:v"] ? String(h["c:v"]) : "";
52
+ }
53
+ if (!g) {
54
+ const e = t(m, "c:v");
55
+ g = e ? String(e) : x(m, "val") || "";
56
+ }
57
+ }
58
+ let C;
59
+ const P = t(r, "c:spPr"), R = P ? t(P, "a:solidFill") : null, d = R ? R["a:srgbClr"] : null;
60
+ if (d && (C = "#" + (typeof d == "object" ? d["@_val"] : d)), s.length === 0) {
61
+ const i = t(r, "c:cat") || t(r, "c:xVal");
62
+ if (i) {
63
+ const e = t(i, "c:strRef"), h = t(i, "c:numRef"), f = t(e, "c:strCache"), F = t(h, "c:numCache"), v = f || F;
64
+ if (v)
65
+ for (const j of A(v, "c:pt"))
66
+ s.push(j["c:v"] ?? String(x(j, "idx")));
67
+ }
68
+ }
69
+ const y = t(r, "c:val") || t(r, "c:yVal"), l = [];
70
+ if (y) {
71
+ const i = t(y, "c:numRef"), e = t(i, "c:numCache");
72
+ if (e)
73
+ for (const h of A(e, "c:pt"))
74
+ l.push(parseFloat(h["c:v"] ?? "0") || 0);
75
+ }
76
+ l.length > 0 && a.push({ name: g, values: l, color: C });
77
+ }
78
+ return { type: b, title: u, categories: s, series: a };
79
+ }
80
+ function E(c) {
81
+ const n = {}, u = t(c, "c:catAx");
82
+ u && (n.x = w(u));
83
+ const o = t(c, "c:valAx");
84
+ return o && (n.y = w(o)), n;
85
+ }
86
+ function w(c) {
87
+ const n = L(c), o = x(t(c, "c:tickLblPos"), "val") !== "none", s = !!t(c, "c:majorGridlines"), a = t(c, "c:scaling"), p = x(t(a, "c:min"), "val"), r = x(t(a, "c:max"), "val"), m = t(c, "c:numFmt"), g = x(m, "formatCode");
88
+ return {
89
+ title: n,
90
+ showLabels: o,
91
+ showGrid: s,
92
+ min: p !== void 0 ? parseFloat(p) : void 0,
93
+ max: r !== void 0 ? parseFloat(r) : void 0,
94
+ formatCode: g || void 0
95
+ };
96
+ }
97
+ export {
98
+ T as parseChart
99
+ };