quicklook-pptx-renderer 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 +266 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +175 -0
- package/dist/diff/compare.d.ts +17 -0
- package/dist/diff/compare.js +71 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +72 -0
- package/dist/lint.d.ts +27 -0
- package/dist/lint.js +328 -0
- package/dist/mapper/bleed-map.d.ts +6 -0
- package/dist/mapper/bleed-map.js +1 -0
- package/dist/mapper/constants.d.ts +2 -0
- package/dist/mapper/constants.js +4 -0
- package/dist/mapper/drawable-mapper.d.ts +16 -0
- package/dist/mapper/drawable-mapper.js +1464 -0
- package/dist/mapper/html-generator.d.ts +13 -0
- package/dist/mapper/html-generator.js +539 -0
- package/dist/mapper/image-mapper.d.ts +14 -0
- package/dist/mapper/image-mapper.js +70 -0
- package/dist/mapper/nano-malloc.d.ts +130 -0
- package/dist/mapper/nano-malloc.js +197 -0
- package/dist/mapper/ql-bleed.d.ts +35 -0
- package/dist/mapper/ql-bleed.js +254 -0
- package/dist/mapper/shape-mapper.d.ts +41 -0
- package/dist/mapper/shape-mapper.js +2384 -0
- package/dist/mapper/slide-mapper.d.ts +4 -0
- package/dist/mapper/slide-mapper.js +112 -0
- package/dist/mapper/style-builder.d.ts +12 -0
- package/dist/mapper/style-builder.js +30 -0
- package/dist/mapper/text-mapper.d.ts +14 -0
- package/dist/mapper/text-mapper.js +302 -0
- package/dist/model/enums.d.ts +25 -0
- package/dist/model/enums.js +2 -0
- package/dist/model/types.d.ts +482 -0
- package/dist/model/types.js +7 -0
- package/dist/package/content-types.d.ts +1 -0
- package/dist/package/content-types.js +4 -0
- package/dist/package/package.d.ts +10 -0
- package/dist/package/package.js +52 -0
- package/dist/package/relationships.d.ts +6 -0
- package/dist/package/relationships.js +25 -0
- package/dist/package/zip.d.ts +6 -0
- package/dist/package/zip.js +17 -0
- package/dist/reader/color.d.ts +3 -0
- package/dist/reader/color.js +79 -0
- package/dist/reader/drawing.d.ts +17 -0
- package/dist/reader/drawing.js +403 -0
- package/dist/reader/effects.d.ts +2 -0
- package/dist/reader/effects.js +83 -0
- package/dist/reader/fill.d.ts +2 -0
- package/dist/reader/fill.js +94 -0
- package/dist/reader/presentation.d.ts +5 -0
- package/dist/reader/presentation.js +127 -0
- package/dist/reader/slide-layout.d.ts +2 -0
- package/dist/reader/slide-layout.js +28 -0
- package/dist/reader/slide-master.d.ts +4 -0
- package/dist/reader/slide-master.js +49 -0
- package/dist/reader/slide.d.ts +2 -0
- package/dist/reader/slide.js +26 -0
- package/dist/reader/text-list-style.d.ts +2 -0
- package/dist/reader/text-list-style.js +9 -0
- package/dist/reader/text.d.ts +5 -0
- package/dist/reader/text.js +295 -0
- package/dist/reader/theme.d.ts +2 -0
- package/dist/reader/theme.js +109 -0
- package/dist/reader/transform.d.ts +2 -0
- package/dist/reader/transform.js +21 -0
- package/dist/render/image-renderer.d.ts +3 -0
- package/dist/render/image-renderer.js +33 -0
- package/dist/render/renderer.d.ts +9 -0
- package/dist/render/renderer.js +178 -0
- package/dist/render/shape-renderer.d.ts +3 -0
- package/dist/render/shape-renderer.js +175 -0
- package/dist/render/text-renderer.d.ts +3 -0
- package/dist/render/text-renderer.js +152 -0
- package/dist/resolve/color-resolver.d.ts +18 -0
- package/dist/resolve/color-resolver.js +321 -0
- package/dist/resolve/font-map.d.ts +2 -0
- package/dist/resolve/font-map.js +66 -0
- package/dist/resolve/inheritance.d.ts +5 -0
- package/dist/resolve/inheritance.js +106 -0
- package/package.json +74 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Drawable, Shape, Picture, Group, Connector, GraphicFrame } from "../model/types.js";
|
|
2
|
+
export declare function readShape(sp: any): Shape;
|
|
3
|
+
export declare function readPicture(pic: any): Picture;
|
|
4
|
+
export declare function readGroup(grpSp: any): Group;
|
|
5
|
+
export declare function readConnector(cxnSp: any): Connector;
|
|
6
|
+
export declare function readGraphicFrame(gf: any): GraphicFrame;
|
|
7
|
+
/**
|
|
8
|
+
* Recover XML document order for spTree children.
|
|
9
|
+
*
|
|
10
|
+
* fast-xml-parser groups elements by tag name, losing the interleaving order.
|
|
11
|
+
* OfficeImport uses XML document order for z-ordering. We recover it by scanning
|
|
12
|
+
* the raw XML with a regex to extract the sequence of child tag names, then
|
|
13
|
+
* pick from each tag's parsed array in that order.
|
|
14
|
+
*
|
|
15
|
+
* If rawXml is not provided, falls back to ID sort (less accurate for non-sequential IDs).
|
|
16
|
+
*/
|
|
17
|
+
export declare function readDrawables(spTree: any, rawXml?: string): Drawable[];
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { readColor } from "./color.js";
|
|
2
|
+
import { readFill } from "./fill.js";
|
|
3
|
+
import { readTransform } from "./transform.js";
|
|
4
|
+
import { readEffects } from "./effects.js";
|
|
5
|
+
import { readTextBody } from "./text.js";
|
|
6
|
+
function toArray(val) {
|
|
7
|
+
return val == null ? [] : Array.isArray(val) ? val : [val];
|
|
8
|
+
}
|
|
9
|
+
function toNum(val) {
|
|
10
|
+
return val == null ? undefined : Number(val);
|
|
11
|
+
}
|
|
12
|
+
// ── Common helpers ─────────────────────────────────
|
|
13
|
+
function readNonVisual(nvPr) {
|
|
14
|
+
const cNvPr = nvPr?.cNvPr ?? {};
|
|
15
|
+
const base = {
|
|
16
|
+
id: Number(cNvPr["@_id"] ?? 0),
|
|
17
|
+
name: cNvPr["@_name"] ?? "",
|
|
18
|
+
};
|
|
19
|
+
if (cNvPr["@_descr"])
|
|
20
|
+
base.description = cNvPr["@_descr"];
|
|
21
|
+
if (cNvPr["@_hidden"] === "1" || cNvPr["@_hidden"] === "true")
|
|
22
|
+
base.hidden = true;
|
|
23
|
+
if (cNvPr.hlinkClick?.["@_id"])
|
|
24
|
+
base.hyperlink = cNvPr.hlinkClick["@_id"];
|
|
25
|
+
const ph = nvPr?.nvPr?.ph;
|
|
26
|
+
if (ph != null) {
|
|
27
|
+
const info = {};
|
|
28
|
+
if (ph["@_type"])
|
|
29
|
+
info.type = ph["@_type"];
|
|
30
|
+
if (ph["@_idx"] != null)
|
|
31
|
+
info.idx = Number(ph["@_idx"]);
|
|
32
|
+
base.placeholder = info;
|
|
33
|
+
}
|
|
34
|
+
return base;
|
|
35
|
+
}
|
|
36
|
+
function readLineEnd(node) {
|
|
37
|
+
if (node == null)
|
|
38
|
+
return undefined;
|
|
39
|
+
const type = node["@_type"];
|
|
40
|
+
if (!type)
|
|
41
|
+
return undefined;
|
|
42
|
+
const end = { type };
|
|
43
|
+
if (node["@_w"])
|
|
44
|
+
end.width = node["@_w"];
|
|
45
|
+
if (node["@_len"])
|
|
46
|
+
end.length = node["@_len"];
|
|
47
|
+
return end;
|
|
48
|
+
}
|
|
49
|
+
function readStroke(node) {
|
|
50
|
+
if (node == null)
|
|
51
|
+
return undefined;
|
|
52
|
+
const s = {};
|
|
53
|
+
if (node["@_w"] != null)
|
|
54
|
+
s.width = Number(node["@_w"]);
|
|
55
|
+
if (node["@_cap"])
|
|
56
|
+
s.cap = node["@_cap"];
|
|
57
|
+
if (node["@_cmpd"])
|
|
58
|
+
s.compound = node["@_cmpd"];
|
|
59
|
+
const fill = readFill(node);
|
|
60
|
+
if (fill)
|
|
61
|
+
s.fill = fill;
|
|
62
|
+
if (node.prstDash?.["@_val"])
|
|
63
|
+
s.dash = node.prstDash["@_val"];
|
|
64
|
+
if (node.round != null)
|
|
65
|
+
s.join = "round";
|
|
66
|
+
else if (node.bevel != null)
|
|
67
|
+
s.join = "bevel";
|
|
68
|
+
else if (node.miter != null) {
|
|
69
|
+
s.join = "miter";
|
|
70
|
+
if (node.miter["@_lim"] != null)
|
|
71
|
+
s.miterLimit = Number(node.miter["@_lim"]);
|
|
72
|
+
}
|
|
73
|
+
const head = readLineEnd(node.headEnd);
|
|
74
|
+
const tail = readLineEnd(node.tailEnd);
|
|
75
|
+
if (head)
|
|
76
|
+
s.headEnd = head;
|
|
77
|
+
if (tail)
|
|
78
|
+
s.tailEnd = tail;
|
|
79
|
+
return s;
|
|
80
|
+
}
|
|
81
|
+
function readGeometry(spPr) {
|
|
82
|
+
const prstGeom = spPr.prstGeom;
|
|
83
|
+
if (prstGeom == null)
|
|
84
|
+
return undefined;
|
|
85
|
+
const geom = {};
|
|
86
|
+
if (prstGeom["@_prst"])
|
|
87
|
+
geom.preset = prstGeom["@_prst"];
|
|
88
|
+
const gds = toArray(prstGeom.avLst?.gd);
|
|
89
|
+
if (gds.length) {
|
|
90
|
+
const adj = {};
|
|
91
|
+
for (const gd of gds) {
|
|
92
|
+
if (gd["@_name"] && gd["@_fmla"])
|
|
93
|
+
adj[gd["@_name"]] = gd["@_fmla"];
|
|
94
|
+
}
|
|
95
|
+
if (Object.keys(adj).length)
|
|
96
|
+
geom.adjustValues = adj;
|
|
97
|
+
}
|
|
98
|
+
return geom;
|
|
99
|
+
}
|
|
100
|
+
function readStyleRef(node) {
|
|
101
|
+
if (node == null)
|
|
102
|
+
return undefined;
|
|
103
|
+
const ref = { idx: Number(node["@_idx"] ?? 0) };
|
|
104
|
+
const color = readColor(node);
|
|
105
|
+
if (color)
|
|
106
|
+
ref.color = color;
|
|
107
|
+
return ref;
|
|
108
|
+
}
|
|
109
|
+
function readShapeStyle(style) {
|
|
110
|
+
if (style == null)
|
|
111
|
+
return undefined;
|
|
112
|
+
const ss = {};
|
|
113
|
+
const lnRef = readStyleRef(style.lnRef);
|
|
114
|
+
if (lnRef)
|
|
115
|
+
ss.lnRef = lnRef;
|
|
116
|
+
const fillRef = readStyleRef(style.fillRef);
|
|
117
|
+
if (fillRef)
|
|
118
|
+
ss.fillRef = fillRef;
|
|
119
|
+
const effectRef = readStyleRef(style.effectRef);
|
|
120
|
+
if (effectRef)
|
|
121
|
+
ss.effectRef = effectRef;
|
|
122
|
+
if (style.fontRef) {
|
|
123
|
+
const fr = {
|
|
124
|
+
idx: (style.fontRef["@_idx"] ?? "none"),
|
|
125
|
+
};
|
|
126
|
+
const color = readColor(style.fontRef);
|
|
127
|
+
if (color)
|
|
128
|
+
fr.color = color;
|
|
129
|
+
ss.fontRef = fr;
|
|
130
|
+
}
|
|
131
|
+
return ss;
|
|
132
|
+
}
|
|
133
|
+
// ── Shape readers ──────────────────────────────────
|
|
134
|
+
export function readShape(sp) {
|
|
135
|
+
const base = readNonVisual(sp.nvSpPr);
|
|
136
|
+
const spPr = sp.spPr ?? {};
|
|
137
|
+
const shape = { ...base, drawableType: "sp" };
|
|
138
|
+
shape.bounds = readTransform(spPr.xfrm);
|
|
139
|
+
shape.geometry = readGeometry(spPr);
|
|
140
|
+
const fill = readFill(spPr);
|
|
141
|
+
if (fill)
|
|
142
|
+
shape.fill = fill;
|
|
143
|
+
const stroke = readStroke(spPr.ln);
|
|
144
|
+
if (stroke)
|
|
145
|
+
shape.stroke = stroke;
|
|
146
|
+
const effects = readEffects(spPr.effectLst);
|
|
147
|
+
if (effects.length)
|
|
148
|
+
shape.effects = effects;
|
|
149
|
+
if (sp.txBody)
|
|
150
|
+
shape.textBody = readTextBody(sp.txBody);
|
|
151
|
+
const style = readShapeStyle(sp.style);
|
|
152
|
+
if (style)
|
|
153
|
+
shape.shapeStyle = style;
|
|
154
|
+
return shape;
|
|
155
|
+
}
|
|
156
|
+
export function readPicture(pic) {
|
|
157
|
+
const base = readNonVisual(pic.nvPicPr);
|
|
158
|
+
const spPr = pic.spPr ?? {};
|
|
159
|
+
const p = { ...base, drawableType: "pic" };
|
|
160
|
+
p.bounds = readTransform(spPr.xfrm);
|
|
161
|
+
// blipFill
|
|
162
|
+
const bf = pic.blipFill;
|
|
163
|
+
if (bf) {
|
|
164
|
+
if (bf.blip?.["@_embed"])
|
|
165
|
+
p.blipRId = bf.blip["@_embed"];
|
|
166
|
+
const imgFill = readFill({ blipFill: bf });
|
|
167
|
+
if (imgFill)
|
|
168
|
+
p.fill = imgFill;
|
|
169
|
+
}
|
|
170
|
+
p.geometry = readGeometry(spPr);
|
|
171
|
+
// shape-level properties
|
|
172
|
+
const shapeFill = readFill(spPr);
|
|
173
|
+
const shapeStroke = readStroke(spPr.ln);
|
|
174
|
+
const shapeEffects = readEffects(spPr.effectLst);
|
|
175
|
+
if (shapeFill || shapeStroke || shapeEffects.length) {
|
|
176
|
+
p.shapeProperties = {};
|
|
177
|
+
if (shapeFill)
|
|
178
|
+
p.shapeProperties.fill = shapeFill;
|
|
179
|
+
if (shapeStroke)
|
|
180
|
+
p.shapeProperties.stroke = shapeStroke;
|
|
181
|
+
if (shapeEffects.length)
|
|
182
|
+
p.shapeProperties.effects = shapeEffects;
|
|
183
|
+
}
|
|
184
|
+
return p;
|
|
185
|
+
}
|
|
186
|
+
export function readGroup(grpSp) {
|
|
187
|
+
const base = readNonVisual(grpSp.nvGrpSpPr);
|
|
188
|
+
const grpSpPr = grpSp.grpSpPr ?? {};
|
|
189
|
+
const g = { ...base, drawableType: "grpSp", children: [] };
|
|
190
|
+
const xfrm = grpSpPr.xfrm;
|
|
191
|
+
if (xfrm) {
|
|
192
|
+
g.bounds = readTransform(xfrm);
|
|
193
|
+
// child offset/extent
|
|
194
|
+
const chOff = xfrm.chOff;
|
|
195
|
+
const chExt = xfrm.chExt;
|
|
196
|
+
if (chOff && chExt) {
|
|
197
|
+
g.childBounds = {
|
|
198
|
+
x: Number(chOff["@_x"] ?? 0),
|
|
199
|
+
y: Number(chOff["@_y"] ?? 0),
|
|
200
|
+
cx: Number(chExt["@_cx"] ?? 0),
|
|
201
|
+
cy: Number(chExt["@_cy"] ?? 0),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
g.children = readChildDrawables(grpSp);
|
|
206
|
+
return g;
|
|
207
|
+
}
|
|
208
|
+
export function readConnector(cxnSp) {
|
|
209
|
+
const base = readNonVisual(cxnSp.nvCxnSpPr);
|
|
210
|
+
const spPr = cxnSp.spPr ?? {};
|
|
211
|
+
const c = { ...base, drawableType: "cxnSp" };
|
|
212
|
+
c.bounds = readTransform(spPr.xfrm);
|
|
213
|
+
c.geometry = readGeometry(spPr);
|
|
214
|
+
const fill = readFill(spPr);
|
|
215
|
+
if (fill)
|
|
216
|
+
c.fill = fill;
|
|
217
|
+
const stroke = readStroke(spPr.ln);
|
|
218
|
+
if (stroke)
|
|
219
|
+
c.stroke = stroke;
|
|
220
|
+
return c;
|
|
221
|
+
}
|
|
222
|
+
export function readGraphicFrame(gf) {
|
|
223
|
+
const base = readNonVisual(gf.nvGraphicFramePr);
|
|
224
|
+
const f = { ...base, drawableType: "graphicFrame" };
|
|
225
|
+
f.bounds = readTransform(gf.xfrm);
|
|
226
|
+
const graphicData = gf.graphic?.graphicData;
|
|
227
|
+
// chart relationship
|
|
228
|
+
const chart = graphicData?.chart;
|
|
229
|
+
if (chart?.["@_id"])
|
|
230
|
+
f.chartRId = chart["@_id"];
|
|
231
|
+
// SmartArt/diagram relationship
|
|
232
|
+
const relIds = graphicData?.relIds;
|
|
233
|
+
if (relIds?.["@_dm"])
|
|
234
|
+
f.smartArtRId = relIds["@_dm"];
|
|
235
|
+
// OLE object
|
|
236
|
+
const oleObj = graphicData?.oleObj;
|
|
237
|
+
if (oleObj?.["@_id"])
|
|
238
|
+
f.oleRId = oleObj["@_id"];
|
|
239
|
+
// table
|
|
240
|
+
const tbl = graphicData?.tbl;
|
|
241
|
+
if (tbl)
|
|
242
|
+
f.tableData = readTable(tbl);
|
|
243
|
+
return f;
|
|
244
|
+
}
|
|
245
|
+
function readTable(tbl) {
|
|
246
|
+
const gridCols = toArray(tbl.tblGrid?.gridCol).map((gc) => Number(gc["@_w"] ?? 0));
|
|
247
|
+
const tblPr = tbl.tblPr;
|
|
248
|
+
const props = tblPr ? {
|
|
249
|
+
firstRow: tblPr["@_firstRow"] === "1",
|
|
250
|
+
lastRow: tblPr["@_lastRow"] === "1",
|
|
251
|
+
bandRow: tblPr["@_bandRow"] === "1",
|
|
252
|
+
bandCol: tblPr["@_bandCol"] === "1",
|
|
253
|
+
} : undefined;
|
|
254
|
+
const rows = toArray(tbl.tr).map((tr) => {
|
|
255
|
+
const height = Number(tr["@_h"] ?? 0);
|
|
256
|
+
const cells = toArray(tr.tc).map((tc) => {
|
|
257
|
+
const cell = {};
|
|
258
|
+
if (tc.txBody)
|
|
259
|
+
cell.textBody = readTextBody(tc.txBody);
|
|
260
|
+
// gridSpan, hMerge, vMerge are attributes of <a:tc>, not <a:tcPr>
|
|
261
|
+
if (tc["@_gridSpan"] != null)
|
|
262
|
+
cell.gridSpan = Number(tc["@_gridSpan"]);
|
|
263
|
+
if (tc["@_rowSpan"] != null)
|
|
264
|
+
cell.rowSpan = Number(tc["@_rowSpan"]);
|
|
265
|
+
if (tc["@_hMerge"] === "1")
|
|
266
|
+
cell.hMerge = true;
|
|
267
|
+
if (tc["@_vMerge"] === "1")
|
|
268
|
+
cell.vMerge = true;
|
|
269
|
+
const tcPr = tc.tcPr;
|
|
270
|
+
if (tcPr) {
|
|
271
|
+
const fill = readFill(tcPr);
|
|
272
|
+
if (fill)
|
|
273
|
+
cell.fill = fill;
|
|
274
|
+
const borders = {};
|
|
275
|
+
let hasBorder = false;
|
|
276
|
+
for (const side of ["left", "right", "top", "bottom"]) {
|
|
277
|
+
const tag = side === "left" ? "lnL" : side === "right" ? "lnR" : side === "top" ? "lnT" : "lnB";
|
|
278
|
+
const ln = tcPr[tag];
|
|
279
|
+
if (ln) {
|
|
280
|
+
borders[side] = readStroke(ln);
|
|
281
|
+
hasBorder = true;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (hasBorder)
|
|
285
|
+
cell.borders = borders;
|
|
286
|
+
if (tcPr["@_anchor"])
|
|
287
|
+
cell.anchor = tcPr["@_anchor"];
|
|
288
|
+
}
|
|
289
|
+
return cell;
|
|
290
|
+
});
|
|
291
|
+
return { height, cells };
|
|
292
|
+
});
|
|
293
|
+
const td = { rows, gridCols };
|
|
294
|
+
if (props)
|
|
295
|
+
td.properties = props;
|
|
296
|
+
const styleId = tblPr?.tableStyleId;
|
|
297
|
+
if (styleId)
|
|
298
|
+
td.tableStyleId = String(styleId);
|
|
299
|
+
return td;
|
|
300
|
+
}
|
|
301
|
+
// ── Tree reader ────────────────────────────────────
|
|
302
|
+
function readChildDrawables(parent) {
|
|
303
|
+
const drawables = [];
|
|
304
|
+
for (const sp of toArray(parent.sp))
|
|
305
|
+
drawables.push(readShape(sp));
|
|
306
|
+
for (const pic of toArray(parent.pic))
|
|
307
|
+
drawables.push(readPicture(pic));
|
|
308
|
+
for (const grp of toArray(parent.grpSp))
|
|
309
|
+
drawables.push(readGroup(grp));
|
|
310
|
+
for (const cxn of toArray(parent.cxnSp))
|
|
311
|
+
drawables.push(readConnector(cxn));
|
|
312
|
+
for (const gf of toArray(parent.graphicFrame))
|
|
313
|
+
drawables.push(readGraphicFrame(gf));
|
|
314
|
+
// mc:AlternateContent: extract drawables from Fallback child (Choice requires features
|
|
315
|
+
// the reader may not support, e.g. chart rendering). OfficeImport uses the fallback
|
|
316
|
+
// which typically contains a pre-rendered image (<p:pic>) or simplified shape.
|
|
317
|
+
for (const ac of toArray(parent.AlternateContent)) {
|
|
318
|
+
const fallback = ac.Fallback ?? ac.Choice;
|
|
319
|
+
if (fallback) {
|
|
320
|
+
const fallbackDrawables = readChildDrawables(fallback);
|
|
321
|
+
drawables.push(...fallbackDrawables);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return drawables;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Recover XML document order for spTree children.
|
|
328
|
+
*
|
|
329
|
+
* fast-xml-parser groups elements by tag name, losing the interleaving order.
|
|
330
|
+
* OfficeImport uses XML document order for z-ordering. We recover it by scanning
|
|
331
|
+
* the raw XML with a regex to extract the sequence of child tag names, then
|
|
332
|
+
* pick from each tag's parsed array in that order.
|
|
333
|
+
*
|
|
334
|
+
* If rawXml is not provided, falls back to ID sort (less accurate for non-sequential IDs).
|
|
335
|
+
*/
|
|
336
|
+
export function readDrawables(spTree, rawXml) {
|
|
337
|
+
if (spTree == null)
|
|
338
|
+
return [];
|
|
339
|
+
const drawables = readChildDrawables(spTree);
|
|
340
|
+
if (rawXml) {
|
|
341
|
+
return reorderByXml(drawables, rawXml);
|
|
342
|
+
}
|
|
343
|
+
// Fallback: sort by ID (works when IDs are sequential)
|
|
344
|
+
drawables.sort((a, b) => a.id - b.id);
|
|
345
|
+
return drawables;
|
|
346
|
+
}
|
|
347
|
+
const DRAWABLE_TAGS = new Set(["sp", "pic", "grpSp", "cxnSp", "graphicFrame"]);
|
|
348
|
+
function reorderByXml(drawables, rawXml) {
|
|
349
|
+
// Extract the tag order from spTree children in the raw XML.
|
|
350
|
+
// Match opening tags: <p:sp, <p:pic, <p:grpSp, <p:cxnSp, <p:graphicFrame
|
|
351
|
+
// Also match without namespace prefix (after removeNSPrefix processing, but raw XML has them).
|
|
352
|
+
const tagOrder = [];
|
|
353
|
+
const tagRegex = /<(?:p:|wsp:)?(?:mc:)?(?:AlternateContent|sp|pic|grpSp|cxnSp|graphicFrame)\b/g;
|
|
354
|
+
let m;
|
|
355
|
+
// Find the spTree region in the raw XML
|
|
356
|
+
const spTreeStart = rawXml.indexOf("<p:spTree");
|
|
357
|
+
const spTreeEnd = rawXml.indexOf("</p:spTree>");
|
|
358
|
+
if (spTreeStart === -1 || spTreeEnd === -1) {
|
|
359
|
+
drawables.sort((a, b) => a.id - b.id);
|
|
360
|
+
return drawables;
|
|
361
|
+
}
|
|
362
|
+
const spTreeXml = rawXml.substring(spTreeStart, spTreeEnd);
|
|
363
|
+
// Track nesting depth to only capture direct children of spTree
|
|
364
|
+
// Simple approach: just capture all tag names in order and skip non-drawable tags
|
|
365
|
+
while ((m = tagRegex.exec(spTreeXml)) !== null) {
|
|
366
|
+
const tag = m[0];
|
|
367
|
+
// Extract tag name after prefix
|
|
368
|
+
const nameMatch = tag.match(/(?:p:|wsp:)?(sp|pic|grpSp|cxnSp|graphicFrame|AlternateContent)$/);
|
|
369
|
+
if (nameMatch && DRAWABLE_TAGS.has(nameMatch[1])) {
|
|
370
|
+
tagOrder.push(nameMatch[1]);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// Build per-tag queues from the drawables array
|
|
374
|
+
const queues = new Map();
|
|
375
|
+
for (const d of drawables) {
|
|
376
|
+
const tag = d.drawableType === "grpSp" ? "grpSp" : d.drawableType;
|
|
377
|
+
const q = queues.get(tag) ?? [];
|
|
378
|
+
q.push(d);
|
|
379
|
+
queues.set(tag, q);
|
|
380
|
+
}
|
|
381
|
+
// Per-tag index pointers
|
|
382
|
+
const ptrs = new Map();
|
|
383
|
+
for (const [tag] of queues)
|
|
384
|
+
ptrs.set(tag, 0);
|
|
385
|
+
// Rebuild in XML document order
|
|
386
|
+
const ordered = [];
|
|
387
|
+
for (const tag of tagOrder) {
|
|
388
|
+
const q = queues.get(tag);
|
|
389
|
+
const ptr = ptrs.get(tag) ?? 0;
|
|
390
|
+
if (q && ptr < q.length) {
|
|
391
|
+
ordered.push(q[ptr]);
|
|
392
|
+
ptrs.set(tag, ptr + 1);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// Append any remaining drawables not matched (safety net)
|
|
396
|
+
for (const [tag, q] of queues) {
|
|
397
|
+
const ptr = ptrs.get(tag) ?? 0;
|
|
398
|
+
for (let i = ptr; i < q.length; i++) {
|
|
399
|
+
ordered.push(q[i]);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return ordered;
|
|
403
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { readColor } from "./color.js";
|
|
2
|
+
function toBool(val) {
|
|
3
|
+
if (val == null)
|
|
4
|
+
return undefined;
|
|
5
|
+
return val === "1" || val === "true";
|
|
6
|
+
}
|
|
7
|
+
function toNum(val) {
|
|
8
|
+
return val == null ? undefined : Number(val);
|
|
9
|
+
}
|
|
10
|
+
function readOuterShadow(node) {
|
|
11
|
+
const e = { type: "outerShdw" };
|
|
12
|
+
e.blurRad = toNum(node["@_blurRad"]);
|
|
13
|
+
e.dist = toNum(node["@_dist"]);
|
|
14
|
+
e.dir = toNum(node["@_dir"]);
|
|
15
|
+
e.sx = toNum(node["@_sx"]);
|
|
16
|
+
e.sy = toNum(node["@_sy"]);
|
|
17
|
+
e.kx = toNum(node["@_kx"]);
|
|
18
|
+
e.ky = toNum(node["@_ky"]);
|
|
19
|
+
if (node["@_algn"])
|
|
20
|
+
e.algn = node["@_algn"];
|
|
21
|
+
e.rotWithShape = toBool(node["@_rotWithShape"]);
|
|
22
|
+
const color = readColor(node);
|
|
23
|
+
if (color)
|
|
24
|
+
e.color = color;
|
|
25
|
+
return e;
|
|
26
|
+
}
|
|
27
|
+
function readInnerShadow(node) {
|
|
28
|
+
const e = { type: "innerShdw" };
|
|
29
|
+
e.blurRad = toNum(node["@_blurRad"]);
|
|
30
|
+
e.dist = toNum(node["@_dist"]);
|
|
31
|
+
e.dir = toNum(node["@_dir"]);
|
|
32
|
+
const color = readColor(node);
|
|
33
|
+
if (color)
|
|
34
|
+
e.color = color;
|
|
35
|
+
return e;
|
|
36
|
+
}
|
|
37
|
+
function readGlow(node) {
|
|
38
|
+
const e = { type: "glow" };
|
|
39
|
+
e.rad = toNum(node["@_rad"]);
|
|
40
|
+
const color = readColor(node);
|
|
41
|
+
if (color)
|
|
42
|
+
e.color = color;
|
|
43
|
+
return e;
|
|
44
|
+
}
|
|
45
|
+
function readReflection(node) {
|
|
46
|
+
const e = { type: "reflection" };
|
|
47
|
+
e.blurRad = toNum(node["@_blurRad"]);
|
|
48
|
+
e.stA = toNum(node["@_stA"]);
|
|
49
|
+
e.endA = toNum(node["@_endA"]);
|
|
50
|
+
e.dist = toNum(node["@_dist"]);
|
|
51
|
+
e.dir = toNum(node["@_dir"]);
|
|
52
|
+
e.fadeDir = toNum(node["@_fadeDir"]);
|
|
53
|
+
e.sx = toNum(node["@_sx"]);
|
|
54
|
+
e.sy = toNum(node["@_sy"]);
|
|
55
|
+
e.kx = toNum(node["@_kx"]);
|
|
56
|
+
e.ky = toNum(node["@_ky"]);
|
|
57
|
+
if (node["@_algn"])
|
|
58
|
+
e.algn = node["@_algn"];
|
|
59
|
+
e.rotWithShape = toBool(node["@_rotWithShape"]);
|
|
60
|
+
return e;
|
|
61
|
+
}
|
|
62
|
+
function readBlur(node) {
|
|
63
|
+
const e = { type: "blur" };
|
|
64
|
+
e.rad = toNum(node["@_rad"]);
|
|
65
|
+
e.grow = toBool(node["@_grow"]);
|
|
66
|
+
return e;
|
|
67
|
+
}
|
|
68
|
+
export function readEffects(effectLst) {
|
|
69
|
+
if (effectLst == null)
|
|
70
|
+
return [];
|
|
71
|
+
const effects = [];
|
|
72
|
+
if (effectLst.outerShdw)
|
|
73
|
+
effects.push(readOuterShadow(effectLst.outerShdw));
|
|
74
|
+
if (effectLst.innerShdw)
|
|
75
|
+
effects.push(readInnerShadow(effectLst.innerShdw));
|
|
76
|
+
if (effectLst.glow)
|
|
77
|
+
effects.push(readGlow(effectLst.glow));
|
|
78
|
+
if (effectLst.reflection)
|
|
79
|
+
effects.push(readReflection(effectLst.reflection));
|
|
80
|
+
if (effectLst.blur)
|
|
81
|
+
effects.push(readBlur(effectLst.blur));
|
|
82
|
+
return effects;
|
|
83
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { readColor } from "./color.js";
|
|
2
|
+
function toArray(val) {
|
|
3
|
+
return val == null ? [] : Array.isArray(val) ? val : [val];
|
|
4
|
+
}
|
|
5
|
+
function readRelativeRect(node) {
|
|
6
|
+
if (node == null)
|
|
7
|
+
return undefined;
|
|
8
|
+
const r = {};
|
|
9
|
+
if (node["@_l"] != null)
|
|
10
|
+
r.l = Number(node["@_l"]);
|
|
11
|
+
if (node["@_t"] != null)
|
|
12
|
+
r.t = Number(node["@_t"]);
|
|
13
|
+
if (node["@_r"] != null)
|
|
14
|
+
r.r = Number(node["@_r"]);
|
|
15
|
+
if (node["@_b"] != null)
|
|
16
|
+
r.b = Number(node["@_b"]);
|
|
17
|
+
return r;
|
|
18
|
+
}
|
|
19
|
+
function readSolidFill(node) {
|
|
20
|
+
const color = readColor(node);
|
|
21
|
+
return color ? { type: "solid", color } : undefined;
|
|
22
|
+
}
|
|
23
|
+
function readGradientFill(node) {
|
|
24
|
+
const stops = toArray(node.gsLst?.gs).map((gs) => ({
|
|
25
|
+
position: Number(gs["@_pos"] ?? 0),
|
|
26
|
+
color: readColor(gs),
|
|
27
|
+
})).filter((s) => s.color);
|
|
28
|
+
const fill = { type: "gradient", stops };
|
|
29
|
+
if (node.lin) {
|
|
30
|
+
fill.linear = {
|
|
31
|
+
angle: Number(node.lin["@_ang"] ?? 5400000),
|
|
32
|
+
scaled: node.lin["@_scaled"] === "1" || node.lin["@_scaled"] === "true",
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (node.path) {
|
|
36
|
+
fill.path = {
|
|
37
|
+
type: node.path["@_path"] ?? "shape",
|
|
38
|
+
fillToRect: readRelativeRect(node.path.fillToRect),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (node["@_rotWithShape"] != null) {
|
|
42
|
+
fill.rotateWithShape = node["@_rotWithShape"] === "1" || node["@_rotWithShape"] === "true";
|
|
43
|
+
}
|
|
44
|
+
return fill;
|
|
45
|
+
}
|
|
46
|
+
function readImageFill(node) {
|
|
47
|
+
const fill = { type: "image" };
|
|
48
|
+
if (node.blip?.["@_embed"])
|
|
49
|
+
fill.blipRId = node.blip["@_embed"];
|
|
50
|
+
if (node.stretch) {
|
|
51
|
+
fill.stretch = { fillRect: readRelativeRect(node.stretch.fillRect) };
|
|
52
|
+
}
|
|
53
|
+
if (node.tile) {
|
|
54
|
+
const t = node.tile;
|
|
55
|
+
fill.tile = {
|
|
56
|
+
tx: Number(t["@_tx"] ?? 0),
|
|
57
|
+
ty: Number(t["@_ty"] ?? 0),
|
|
58
|
+
sx: Number(t["@_sx"] ?? 100000),
|
|
59
|
+
sy: Number(t["@_sy"] ?? 100000),
|
|
60
|
+
};
|
|
61
|
+
if (t["@_flip"])
|
|
62
|
+
fill.tile.flip = t["@_flip"];
|
|
63
|
+
if (t["@_algn"])
|
|
64
|
+
fill.tile.algn = t["@_algn"];
|
|
65
|
+
}
|
|
66
|
+
return fill;
|
|
67
|
+
}
|
|
68
|
+
function readPatternFill(node) {
|
|
69
|
+
const fill = { type: "pattern", preset: node["@_prst"] ?? "solid" };
|
|
70
|
+
const fg = readColor(node.fgClr);
|
|
71
|
+
const bg = readColor(node.bgClr);
|
|
72
|
+
if (fg)
|
|
73
|
+
fill.foreground = fg;
|
|
74
|
+
if (bg)
|
|
75
|
+
fill.background = bg;
|
|
76
|
+
return fill;
|
|
77
|
+
}
|
|
78
|
+
export function readFill(node) {
|
|
79
|
+
if (node == null)
|
|
80
|
+
return undefined;
|
|
81
|
+
if (node.solidFill)
|
|
82
|
+
return readSolidFill(node.solidFill);
|
|
83
|
+
if (node.gradFill)
|
|
84
|
+
return readGradientFill(node.gradFill);
|
|
85
|
+
if (node.blipFill)
|
|
86
|
+
return readImageFill(node.blipFill);
|
|
87
|
+
if (node.pattFill)
|
|
88
|
+
return readPatternFill(node.pattFill);
|
|
89
|
+
if (node.noFill != null)
|
|
90
|
+
return { type: "noFill" };
|
|
91
|
+
if (node.grpFill != null)
|
|
92
|
+
return { type: "grpFill" };
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Presentation } from "../model/types.js";
|
|
2
|
+
import type { PptxPackage } from "../package/package.js";
|
|
3
|
+
import { readTextListStyle } from "./text-list-style.js";
|
|
4
|
+
export { readTextListStyle };
|
|
5
|
+
export declare function readPresentation(pkg: PptxPackage): Promise<Presentation>;
|