pptx-craft 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/dist/index.cjs ADDED
@@ -0,0 +1,3933 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ChartElement: () => ChartElement,
34
+ ConnectorElement: () => ConnectorElement,
35
+ GroupElement: () => GroupElement,
36
+ HandoutMaster: () => HandoutMaster,
37
+ ImageElement: () => ImageElement,
38
+ MediaElement: () => MediaElement,
39
+ NotesMaster: () => NotesMaster,
40
+ Presentation: () => Presentation,
41
+ Registry: () => Registry,
42
+ ShapeElement: () => ShapeElement,
43
+ SlideBuilder: () => SlideBuilder,
44
+ SlideLayout: () => SlideLayout,
45
+ SlideMaster: () => SlideMaster,
46
+ TableElement: () => TableElement,
47
+ TextElement: () => TextElement,
48
+ builtinLayouts: () => builtinLayouts,
49
+ defaultColorScheme: () => defaultColorScheme,
50
+ defaultFontScheme: () => defaultFontScheme,
51
+ defaultTheme: () => defaultTheme
52
+ });
53
+ module.exports = __toCommonJS(index_exports);
54
+
55
+ // src/core/registry.ts
56
+ var Registry = class {
57
+ serializers = /* @__PURE__ */ new Map();
58
+ registerElement(type, serializer) {
59
+ this.serializers.set(type, serializer);
60
+ }
61
+ getSerializer(type) {
62
+ return this.serializers.get(type);
63
+ }
64
+ };
65
+
66
+ // src/core/theme.ts
67
+ var defaultColorScheme = {
68
+ dk1: "000000",
69
+ lt1: "FFFFFF",
70
+ dk2: "44546A",
71
+ lt2: "E7E6E6",
72
+ accent1: "4472C4",
73
+ accent2: "ED7D31",
74
+ accent3: "A5A5A5",
75
+ accent4: "FFC000",
76
+ accent5: "5B9BD5",
77
+ accent6: "70AD47",
78
+ hlink: "0563C1",
79
+ folHlink: "954F72"
80
+ };
81
+ var defaultFontScheme = {
82
+ majorFont: "Calibri Light",
83
+ minorFont: "Calibri"
84
+ };
85
+ var defaultTheme = {
86
+ name: "Office Theme",
87
+ colors: defaultColorScheme,
88
+ fonts: defaultFontScheme
89
+ };
90
+
91
+ // src/core/slide-master.ts
92
+ var SlideMaster = class {
93
+ name;
94
+ background;
95
+ elements;
96
+ constructor(def) {
97
+ this.name = def.name;
98
+ this.background = def.background;
99
+ this.elements = def.elements ?? [];
100
+ }
101
+ };
102
+
103
+ // src/core/slide-layout.ts
104
+ var SlideLayout = class {
105
+ name;
106
+ type;
107
+ placeholders;
108
+ constructor(def) {
109
+ this.name = def.name;
110
+ this.type = def.type ?? "blank";
111
+ this.placeholders = def.placeholders ?? [];
112
+ }
113
+ };
114
+ var builtinLayouts = {
115
+ blank: {
116
+ name: "Blank",
117
+ type: "blank",
118
+ placeholders: []
119
+ },
120
+ title: {
121
+ name: "Title Slide",
122
+ type: "title",
123
+ placeholders: [
124
+ { type: "ctrTitle", placement: { x: 0.69, y: 1.65, w: 8.63, h: 2.13 }, idx: 0 },
125
+ { type: "subTitle", placement: { x: 1.36, y: 4.08, w: 7.29, h: 1.68 }, idx: 1 }
126
+ ]
127
+ },
128
+ titleAndContent: {
129
+ name: "Title and Content",
130
+ type: "titleAndContent",
131
+ placeholders: [
132
+ { type: "title", placement: { x: 0.5, y: 0.28, w: 9, h: 1 }, idx: 0 },
133
+ { type: "body", placement: { x: 0.5, y: 1.53, w: 9, h: 5.47 }, idx: 1 }
134
+ ]
135
+ },
136
+ sectionHeader: {
137
+ name: "Section Header",
138
+ type: "sectionHeader",
139
+ placeholders: [
140
+ { type: "title", placement: { x: 0.69, y: 1.65, w: 8.63, h: 2.13 }, idx: 0 },
141
+ { type: "body", placement: { x: 0.69, y: 4.08, w: 8.63, h: 1.68 }, idx: 1 }
142
+ ]
143
+ },
144
+ twoContent: {
145
+ name: "Two Content",
146
+ type: "twoContent",
147
+ placeholders: [
148
+ { type: "title", placement: { x: 0.5, y: 0.28, w: 9, h: 1 }, idx: 0 },
149
+ { type: "body", placement: { x: 0.5, y: 1.53, w: 4.25, h: 5.47 }, idx: 1 },
150
+ { type: "body", placement: { x: 5.25, y: 1.53, w: 4.25, h: 5.47 }, idx: 2 }
151
+ ]
152
+ }
153
+ };
154
+
155
+ // src/core/notes-master.ts
156
+ var NotesMaster = class {
157
+ name;
158
+ constructor(def) {
159
+ this.name = def?.name ?? "Default Notes Master";
160
+ }
161
+ };
162
+
163
+ // src/core/handout-master.ts
164
+ var HandoutMaster = class {
165
+ name;
166
+ constructor(def) {
167
+ this.name = def?.name ?? "Default Handout Master";
168
+ }
169
+ };
170
+
171
+ // src/ooxml/xml.ts
172
+ var AMP = /&/g;
173
+ var LT = /</g;
174
+ var GT = />/g;
175
+ var QUOT = /"/g;
176
+ var APOS = /'/g;
177
+ function escapeXml(s) {
178
+ return s.replace(AMP, "&amp;").replace(LT, "&lt;").replace(GT, "&gt;").replace(QUOT, "&quot;").replace(APOS, "&apos;");
179
+ }
180
+ function tag(name, attrs, children) {
181
+ let s = `<${name}`;
182
+ if (attrs) {
183
+ for (const [k, v] of Object.entries(attrs)) {
184
+ if (v !== void 0) {
185
+ s += ` ${k}="${escapeXml(String(v))}"`;
186
+ }
187
+ }
188
+ }
189
+ if (children !== void 0 && children !== "") {
190
+ s += `>${children}</${name}>`;
191
+ } else {
192
+ s += "/>";
193
+ }
194
+ return s;
195
+ }
196
+ function openTag(name, attrs) {
197
+ let s = `<${name}`;
198
+ if (attrs) {
199
+ for (const [k, v] of Object.entries(attrs)) {
200
+ if (v !== void 0) {
201
+ s += ` ${k}="${escapeXml(String(v))}"`;
202
+ }
203
+ }
204
+ }
205
+ s += ">";
206
+ return s;
207
+ }
208
+ function closeTag(name) {
209
+ return `</${name}>`;
210
+ }
211
+
212
+ // src/ooxml/units.ts
213
+ var EMU_PER_INCH = 914400;
214
+ var EMU_PER_PT = 12700;
215
+ function inchesToEmu(inches) {
216
+ return Math.round(inches * EMU_PER_INCH);
217
+ }
218
+ function ptToEmu(pt) {
219
+ return Math.round(pt * EMU_PER_PT);
220
+ }
221
+ function ptToHundredths(pt) {
222
+ return Math.round(pt * 100);
223
+ }
224
+
225
+ // src/ooxml/constants.ts
226
+ var NAMESPACE = {
227
+ a: "http://schemas.openxmlformats.org/drawingml/2006/main",
228
+ r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
229
+ p: "http://schemas.openxmlformats.org/presentationml/2006/main"
230
+ };
231
+ var CONTENT_TYPE = {
232
+ presentation: "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml",
233
+ slide: "application/vnd.openxmlformats-officedocument.presentationml.slide+xml",
234
+ slideLayout: "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml",
235
+ slideMaster: "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml",
236
+ theme: "application/vnd.openxmlformats-officedocument.theme+xml",
237
+ rels: "application/vnd.openxmlformats-package.relationships+xml",
238
+ coreProps: "application/vnd.openxmlformats-package.core-properties+xml",
239
+ appProps: "application/vnd.openxmlformats-officedocument.extended-properties+xml",
240
+ presProps: "application/vnd.openxmlformats-officedocument.presentationml.presProps+xml",
241
+ viewProps: "application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml",
242
+ notesMaster: "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml",
243
+ notesSlide: "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml",
244
+ commentAuthors: "application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml",
245
+ comments: "application/vnd.openxmlformats-officedocument.presentationml.comments+xml",
246
+ handoutMaster: "application/vnd.openxmlformats-officedocument.presentationml.handoutMaster+xml",
247
+ tags: "application/vnd.openxmlformats-officedocument.presentationml.tags+xml",
248
+ slideUpdateInfo: "application/vnd.openxmlformats-officedocument.presentationml.slideUpdateInfo+xml",
249
+ png: "image/png",
250
+ jpeg: "image/jpeg",
251
+ gif: "image/gif",
252
+ svg: "image/svg+xml"
253
+ };
254
+ var REL_TYPE = {
255
+ slide: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide",
256
+ slideMaster: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster",
257
+ slideLayout: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout",
258
+ theme: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme",
259
+ image: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
260
+ officeDocument: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
261
+ coreProperties: "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
262
+ extendedProperties: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties",
263
+ presProps: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps",
264
+ viewProps: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps",
265
+ notesMaster: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster",
266
+ notesSlide: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide",
267
+ commentAuthors: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/commentAuthors",
268
+ comments: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
269
+ handoutMaster: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/handoutMaster",
270
+ tags: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/tags",
271
+ slideUpdateInfo: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideUpdateInfo",
272
+ htmlPubSaveAs: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/htmlPubSaveAs",
273
+ slideUpdateUrl: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideUpdateUrl"
274
+ };
275
+ var DEFAULT_SLIDE_WIDTH = 10;
276
+ var DEFAULT_SLIDE_HEIGHT = 7.5;
277
+
278
+ // src/ooxml/serializers/shared.ts
279
+ var import_node_fs = require("fs");
280
+
281
+ // src/ooxml/serializers/color.ts
282
+ function isSchemeColor(c) {
283
+ return typeof c === "object" && "scheme" in c;
284
+ }
285
+ function isSystemColor(c) {
286
+ return typeof c === "object" && "sys" in c;
287
+ }
288
+ function isPresetColor(c) {
289
+ return typeof c === "object" && "preset" in c;
290
+ }
291
+ function isHslColor(c) {
292
+ return typeof c === "object" && "hue" in c && "sat" in c && "lum" in c;
293
+ }
294
+ function isSrgbColor(c) {
295
+ return typeof c === "object" && "hex" in c;
296
+ }
297
+ function serializeTransforms(t) {
298
+ let xml = "";
299
+ if (t.tint !== void 0) xml += tag("a:tint", { val: t.tint });
300
+ if (t.shade !== void 0) xml += tag("a:shade", { val: t.shade });
301
+ if (t.satMod !== void 0) xml += tag("a:satMod", { val: t.satMod });
302
+ if (t.satOff !== void 0) xml += tag("a:satOff", { val: t.satOff });
303
+ if (t.lumMod !== void 0) xml += tag("a:lumMod", { val: t.lumMod });
304
+ if (t.lumOff !== void 0) xml += tag("a:lumOff", { val: t.lumOff });
305
+ if (t.hueMod !== void 0) xml += tag("a:hueMod", { val: t.hueMod });
306
+ if (t.hueOff !== void 0) xml += tag("a:hueOff", { val: t.hueOff });
307
+ if (t.alpha !== void 0) xml += tag("a:alpha", { val: Math.round(t.alpha * 1e3) });
308
+ if (t.alphaOff !== void 0) xml += tag("a:alphaOff", { val: Math.round(t.alphaOff * 1e3) });
309
+ return xml;
310
+ }
311
+ function serializeColor(color) {
312
+ if (typeof color === "string") {
313
+ return tag("a:srgbClr", { val: color });
314
+ }
315
+ if (isSchemeColor(color)) {
316
+ const children = color.transforms ? serializeTransforms(color.transforms) : void 0;
317
+ return tag("a:schemeClr", { val: color.scheme }, children);
318
+ }
319
+ if (isSystemColor(color)) {
320
+ const attrs = { val: color.sys };
321
+ if (color.lastClr) attrs.lastClr = color.lastClr;
322
+ const children = color.transforms ? serializeTransforms(color.transforms) : void 0;
323
+ return tag("a:sysClr", attrs, children);
324
+ }
325
+ if (isPresetColor(color)) {
326
+ const children = color.transforms ? serializeTransforms(color.transforms) : void 0;
327
+ return tag("a:prstClr", { val: color.preset }, children);
328
+ }
329
+ if (isHslColor(color)) {
330
+ const children = color.transforms ? serializeTransforms(color.transforms) : void 0;
331
+ return tag(
332
+ "a:hslClr",
333
+ {
334
+ hue: Math.round(color.hue * 6e4),
335
+ sat: Math.round(color.sat * 1e3),
336
+ lum: Math.round(color.lum * 1e3)
337
+ },
338
+ children
339
+ );
340
+ }
341
+ if (isSrgbColor(color)) {
342
+ const children = color.transforms ? serializeTransforms(color.transforms) : void 0;
343
+ return tag("a:srgbClr", { val: color.hex }, children);
344
+ }
345
+ return tag("a:srgbClr", { val: "000000" });
346
+ }
347
+ function serializeSolidFill(color) {
348
+ return tag("a:solidFill", void 0, serializeColor(color));
349
+ }
350
+ function serializeColorWithAlpha(color, alpha) {
351
+ if (alpha === void 0 || alpha >= 100) return serializeColor(color);
352
+ const alphaChild = tag("a:alpha", { val: Math.round(alpha * 1e3) });
353
+ if (typeof color === "string") {
354
+ return tag("a:srgbClr", { val: color }, alphaChild);
355
+ }
356
+ if (isSrgbColor(color)) {
357
+ let children = color.transforms ? serializeTransforms(color.transforms) : "";
358
+ children += alphaChild;
359
+ return tag("a:srgbClr", { val: color.hex }, children);
360
+ }
361
+ if (isSchemeColor(color)) {
362
+ let children = color.transforms ? serializeTransforms(color.transforms) : "";
363
+ children += alphaChild;
364
+ return tag("a:schemeClr", { val: color.scheme }, children);
365
+ }
366
+ if (isSystemColor(color)) {
367
+ const attrs = { val: color.sys };
368
+ if (color.lastClr) attrs.lastClr = color.lastClr;
369
+ let children = color.transforms ? serializeTransforms(color.transforms) : "";
370
+ children += alphaChild;
371
+ return tag("a:sysClr", attrs, children);
372
+ }
373
+ if (isPresetColor(color)) {
374
+ let children = color.transforms ? serializeTransforms(color.transforms) : "";
375
+ children += alphaChild;
376
+ return tag("a:prstClr", { val: color.preset }, children);
377
+ }
378
+ if (isHslColor(color)) {
379
+ let children = color.transforms ? serializeTransforms(color.transforms) : "";
380
+ children += alphaChild;
381
+ return tag(
382
+ "a:hslClr",
383
+ {
384
+ hue: Math.round(color.hue * 6e4),
385
+ sat: Math.round(color.sat * 1e3),
386
+ lum: Math.round(color.lum * 1e3)
387
+ },
388
+ children
389
+ );
390
+ }
391
+ return tag("a:srgbClr", { val: "000000" }, alphaChild);
392
+ }
393
+
394
+ // src/ooxml/serializers/shared.ts
395
+ function isGradientFill(fill) {
396
+ return typeof fill === "object" && "stops" in fill && "type" in fill;
397
+ }
398
+ function isPatternFill(fill) {
399
+ return typeof fill === "object" && "pattern" in fill;
400
+ }
401
+ function isPictureFill(fill) {
402
+ return typeof fill === "object" && !isGradientFill(fill) && !isPatternFill(fill) && ("path" in fill || "data" in fill);
403
+ }
404
+ function serializeFill(fill, ctx) {
405
+ if (typeof fill === "string" || typeof fill === "object" && ("scheme" in fill || "sys" in fill || "preset" in fill || "hex" in fill || "hue" in fill && "sat" in fill && "lum" in fill)) {
406
+ return serializeSolidFill(fill);
407
+ }
408
+ if (isGradientFill(fill)) {
409
+ let stopsXml = "";
410
+ for (const stop of fill.stops) {
411
+ const pos = Math.round(stop.position * 1e3);
412
+ const clrXml = serializeColorWithAlpha(stop.color, stop.alpha);
413
+ stopsXml += tag("a:gs", { pos }, clrXml);
414
+ }
415
+ let dirChild = "";
416
+ if (fill.type === "linear") {
417
+ dirChild = tag("a:lin", { ang: Math.round((fill.angle ?? 0) * 6e4), scaled: "1" });
418
+ } else if (fill.type === "radial" || fill.type === "path") {
419
+ dirChild = tag(
420
+ "a:path",
421
+ { path: fill.type === "radial" ? "circle" : "rect" },
422
+ tag("a:fillToRect", { l: "50000", t: "50000", r: "50000", b: "50000" })
423
+ );
424
+ }
425
+ return tag("a:gradFill", void 0, tag("a:gsLst", void 0, stopsXml) + dirChild);
426
+ }
427
+ if (isPatternFill(fill)) {
428
+ return tag(
429
+ "a:pattFill",
430
+ { prst: fill.pattern },
431
+ tag("a:fgClr", void 0, serializeColor(fill.foreground)) + tag("a:bgClr", void 0, serializeColor(fill.background))
432
+ );
433
+ }
434
+ if (isPictureFill(fill) && ctx) {
435
+ let data = fill.data;
436
+ if (!data && fill.path) {
437
+ data = (0, import_node_fs.readFileSync)(fill.path);
438
+ }
439
+ if (!data) return "";
440
+ const ct = fill.contentType ?? "image/png";
441
+ const ext = ct.split("/")[1] ?? "png";
442
+ const fileName = `fill_${Date.now()}.${ext}`;
443
+ const target = `../media/${fileName}`;
444
+ const rId = ctx.addRelationship(REL_TYPE.image, target, data);
445
+ let content = tag("a:blip", { "r:embed": rId });
446
+ if (fill.tile) {
447
+ content += tag("a:tile", {
448
+ sx: fill.tile.sx,
449
+ sy: fill.tile.sy,
450
+ tx: fill.tile.tx,
451
+ ty: fill.tile.ty
452
+ });
453
+ } else {
454
+ content += tag("a:stretch", void 0, tag("a:fillRect"));
455
+ }
456
+ return tag("a:blipFill", void 0, content);
457
+ }
458
+ return "";
459
+ }
460
+ function serializeLine(line) {
461
+ const attrs = {};
462
+ if (line.width) attrs.w = ptToEmu(line.width);
463
+ if (line.cap) attrs.cap = line.cap === "square" ? "sq" : line.cap;
464
+ if (line.compound) attrs.cmpd = line.compound;
465
+ if (line.alignment) attrs.algn = line.alignment;
466
+ let children = "";
467
+ if (line.fill) {
468
+ children += serializeFill(line.fill);
469
+ } else if (line.color) {
470
+ children += tag("a:solidFill", void 0, serializeColorWithAlpha(line.color, line.opacity));
471
+ }
472
+ if (line.customDash && line.customDash.length > 0) {
473
+ let dashChildren = "";
474
+ for (const seg of line.customDash) {
475
+ dashChildren += tag("a:ds", { d: seg.d, sp: seg.sp });
476
+ }
477
+ children += tag("a:custDash", void 0, dashChildren);
478
+ } else if (line.dash && line.dash !== "solid") {
479
+ children += tag("a:prstDash", { val: line.dash });
480
+ }
481
+ if (line.join) {
482
+ if (line.join === "round") children += tag("a:round");
483
+ else if (line.join === "bevel") children += tag("a:bevel");
484
+ else children += tag("a:miter", { lim: "800000" });
485
+ }
486
+ if (line.headEnd) {
487
+ const hAttrs = {};
488
+ if (line.headEnd.type) hAttrs.type = line.headEnd.type;
489
+ if (line.headEnd.width) hAttrs.w = line.headEnd.width;
490
+ if (line.headEnd.length) hAttrs.len = line.headEnd.length;
491
+ children += tag("a:headEnd", hAttrs);
492
+ }
493
+ if (line.tailEnd) {
494
+ const tAttrs = {};
495
+ if (line.tailEnd.type) tAttrs.type = line.tailEnd.type;
496
+ if (line.tailEnd.width) tAttrs.w = line.tailEnd.width;
497
+ if (line.tailEnd.length) tAttrs.len = line.tailEnd.length;
498
+ children += tag("a:tailEnd", tAttrs);
499
+ }
500
+ return tag("a:ln", attrs, children || void 0);
501
+ }
502
+ function serializeShadow(shadow) {
503
+ const isInner = shadow.type === "inner";
504
+ const elName = isInner ? "a:innerShdw" : "a:outerShdw";
505
+ const attrs = {};
506
+ if (shadow.blur) attrs.blurRad = ptToEmu(shadow.blur);
507
+ if (shadow.offset) attrs.dist = ptToEmu(shadow.offset);
508
+ if (shadow.angle !== void 0) attrs.dir = Math.round(shadow.angle * 6e4);
509
+ const color = shadow.color ?? "000000";
510
+ return tag(elName, attrs, serializeColorWithAlpha(color, shadow.alpha));
511
+ }
512
+ function serializeGlow(glow) {
513
+ const attrs = {};
514
+ if (glow.radius) attrs.rad = ptToEmu(glow.radius);
515
+ const color = glow.color ?? "FFC000";
516
+ return tag("a:glow", attrs, serializeColorWithAlpha(color, glow.alpha));
517
+ }
518
+ function serializeReflection(refl) {
519
+ const attrs = {};
520
+ if (refl.blur) attrs.blurRad = ptToEmu(refl.blur);
521
+ if (refl.startAlpha !== void 0) attrs.stA = Math.round(refl.startAlpha * 1e3);
522
+ if (refl.endAlpha !== void 0) attrs.endA = Math.round(refl.endAlpha * 1e3);
523
+ if (refl.distance) attrs.dist = ptToEmu(refl.distance);
524
+ if (refl.direction !== void 0) attrs.dir = Math.round(refl.direction * 6e4);
525
+ if (refl.fadeDirection !== void 0) attrs.fadeDir = Math.round(refl.fadeDirection * 6e4);
526
+ return tag("a:reflection", attrs);
527
+ }
528
+ function serializeSoftEdge(se) {
529
+ const attrs = {};
530
+ if (se.radius) attrs.rad = ptToEmu(se.radius);
531
+ return tag("a:softEdge", attrs);
532
+ }
533
+ function serializeEffects(effects) {
534
+ let children = "";
535
+ if (effects.glow) children += serializeGlow(effects.glow);
536
+ if (effects.shadow) children += serializeShadow(effects.shadow);
537
+ if (effects.reflection) children += serializeReflection(effects.reflection);
538
+ if (effects.softEdge) children += serializeSoftEdge(effects.softEdge);
539
+ if (!children) return "";
540
+ return tag("a:effectLst", void 0, children);
541
+ }
542
+ function serializeTransform(placement, transform) {
543
+ const xfrmAttrs = {};
544
+ const rot = placement.rotation ?? transform?.rotation;
545
+ if (rot) xfrmAttrs.rot = Math.round(rot * 6e4);
546
+ if (transform?.flipH) xfrmAttrs.flipH = "1";
547
+ if (transform?.flipV) xfrmAttrs.flipV = "1";
548
+ return tag(
549
+ "a:xfrm",
550
+ Object.keys(xfrmAttrs).length > 0 ? xfrmAttrs : void 0,
551
+ tag("a:off", { x: inchesToEmu(placement.x), y: inchesToEmu(placement.y) }) + tag("a:ext", { cx: inchesToEmu(placement.w), cy: inchesToEmu(placement.h) })
552
+ );
553
+ }
554
+ var HYPERLINK_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
555
+ function serializeHyperlink(link, ctx) {
556
+ if (link.url) {
557
+ const rId = ctx.addRelationship(HYPERLINK_REL_TYPE, link.url);
558
+ const attrs = { "r:id": rId };
559
+ if (link.tooltip) attrs.tooltip = link.tooltip;
560
+ return tag("a:hlinkClick", attrs);
561
+ }
562
+ if (link.email) {
563
+ const rId = ctx.addRelationship(HYPERLINK_REL_TYPE, `mailto:${link.email}`);
564
+ const attrs = { "r:id": rId };
565
+ if (link.tooltip) attrs.tooltip = link.tooltip;
566
+ return tag("a:hlinkClick", attrs);
567
+ }
568
+ if (link.file) {
569
+ const rId = ctx.addRelationship(HYPERLINK_REL_TYPE, link.file);
570
+ const attrs = { "r:id": rId };
571
+ if (link.tooltip) attrs.tooltip = link.tooltip;
572
+ return tag("a:hlinkClick", attrs);
573
+ }
574
+ if (link.action) {
575
+ const attrs = { action: link.action };
576
+ if (link.tooltip) attrs.tooltip = link.tooltip;
577
+ return tag("a:hlinkClick", attrs);
578
+ }
579
+ if (link.slide !== void 0) {
580
+ const attrs = { action: "ppaction://hlinksldjump" };
581
+ if (link.tooltip) attrs.tooltip = link.tooltip;
582
+ return tag("a:hlinkClick", attrs);
583
+ }
584
+ return "";
585
+ }
586
+
587
+ // src/ooxml/serializers/scene3d.ts
588
+ function serializeScene3D(scene) {
589
+ let children = "";
590
+ if (scene.camera) {
591
+ const attrs = {
592
+ prst: scene.camera.preset ?? "orthographicFront"
593
+ };
594
+ if (scene.camera.fov !== void 0) attrs.fov = Math.round(scene.camera.fov * 6e4);
595
+ children += tag("a:camera", attrs);
596
+ } else {
597
+ children += tag("a:camera", { prst: "orthographicFront" });
598
+ }
599
+ if (scene.lightRig) {
600
+ const attrs = {
601
+ rig: scene.lightRig.type ?? "threePt",
602
+ dir: scene.lightRig.direction ?? "t"
603
+ };
604
+ children += tag("a:lightRig", attrs);
605
+ } else {
606
+ children += tag("a:lightRig", { rig: "threePt", dir: "t" });
607
+ }
608
+ return tag("a:scene3d", void 0, children);
609
+ }
610
+ function serializeShape3D(sp3d) {
611
+ const attrs = {};
612
+ if (sp3d.extrusionHeight !== void 0)
613
+ attrs.extrusionH = Math.round(sp3d.extrusionHeight * 12700);
614
+ if (sp3d.contourWidth !== void 0) attrs.contourW = Math.round(sp3d.contourWidth * 12700);
615
+ if (sp3d.material) attrs.prstMaterial = sp3d.material;
616
+ let children = "";
617
+ if (sp3d.bevelTop) {
618
+ const bAttrs = {};
619
+ if (sp3d.bevelTop.width !== void 0) bAttrs.w = Math.round(sp3d.bevelTop.width * 12700);
620
+ if (sp3d.bevelTop.height !== void 0) bAttrs.h = Math.round(sp3d.bevelTop.height * 12700);
621
+ if (sp3d.bevelTop.preset) bAttrs.prst = sp3d.bevelTop.preset;
622
+ children += tag("a:bevelT", bAttrs);
623
+ }
624
+ if (sp3d.bevelBottom) {
625
+ const bAttrs = {};
626
+ if (sp3d.bevelBottom.width !== void 0) bAttrs.w = Math.round(sp3d.bevelBottom.width * 12700);
627
+ if (sp3d.bevelBottom.height !== void 0)
628
+ bAttrs.h = Math.round(sp3d.bevelBottom.height * 12700);
629
+ if (sp3d.bevelBottom.preset) bAttrs.prst = sp3d.bevelBottom.preset;
630
+ children += tag("a:bevelB", bAttrs);
631
+ }
632
+ if (sp3d.extrusionColor) {
633
+ children += tag("a:extrusionClr", void 0, serializeColor(sp3d.extrusionColor));
634
+ }
635
+ if (sp3d.contourColor) {
636
+ children += tag("a:contourClr", void 0, serializeColor(sp3d.contourColor));
637
+ }
638
+ return tag("a:sp3d", Object.keys(attrs).length > 0 ? attrs : void 0, children || void 0);
639
+ }
640
+
641
+ // src/ooxml/serializers/text.ts
642
+ function serializeSpacing(val) {
643
+ if (typeof val === "number") {
644
+ return tag("a:spcPts", { val: ptToHundredths(val) });
645
+ }
646
+ if (val.unit === "pct") {
647
+ return tag("a:spcPct", { val: Math.round(val.value * 1e3) });
648
+ }
649
+ return tag("a:spcPts", { val: ptToHundredths(val.value) });
650
+ }
651
+ function serializeRunProps(run, ctx) {
652
+ const o = run.options;
653
+ if (!o) return "";
654
+ const attrs = {};
655
+ if (o.fontSize) attrs.sz = ptToHundredths(o.fontSize);
656
+ if (o.bold) attrs.b = "1";
657
+ if (o.italic) attrs.i = "1";
658
+ if (o.underline) attrs.u = "sng";
659
+ if (o.strikethrough) {
660
+ attrs.strike = o.strikethrough === "double" ? "dblStrike" : "sngStrike";
661
+ }
662
+ if (o.caps === "small") attrs.cap = "small";
663
+ else if (o.caps === "all") attrs.cap = "all";
664
+ if (o.letterSpacing) attrs.spc = Math.round(o.letterSpacing * 100);
665
+ if (o.lang) attrs.lang = o.lang;
666
+ if (o.altLang) attrs.altLang = o.altLang;
667
+ if (o.subscript) attrs.baseline = "-40000";
668
+ else if (o.superscript) attrs.baseline = "30000";
669
+ else if (o.baseline !== void 0) attrs.baseline = String(Math.round(o.baseline * 1e3));
670
+ let children = "";
671
+ if (o.color) children += serializeSolidFill(o.color);
672
+ if (o.highlight) children += tag("a:highlight", void 0, serializeColor(o.highlight));
673
+ if (o.fontFace) children += tag("a:latin", { typeface: o.fontFace });
674
+ if (o.eastAsianFont) children += tag("a:ea", { typeface: o.eastAsianFont });
675
+ if (o.complexScriptFont) children += tag("a:cs", { typeface: o.complexScriptFont });
676
+ if (o.outline) children += serializeLine(o.outline);
677
+ if (o.shadow) {
678
+ const sAttrs = {};
679
+ if (o.shadow.blur) sAttrs.blurRad = ptToEmu(o.shadow.blur);
680
+ if (o.shadow.offset) sAttrs.dist = ptToEmu(o.shadow.offset);
681
+ if (o.shadow.angle !== void 0) sAttrs.dir = Math.round(o.shadow.angle * 6e4);
682
+ const sc = o.shadow.color ?? "000000";
683
+ children += tag(
684
+ "a:effectLst",
685
+ void 0,
686
+ tag("a:outerShdw", sAttrs, serializeColorWithAlpha(sc, o.shadow.alpha))
687
+ );
688
+ }
689
+ if (o.hyperlink) children += serializeHyperlink(o.hyperlink, ctx);
690
+ if (children) return tag("a:rPr", attrs, children);
691
+ if (Object.keys(attrs).length > 0) return tag("a:rPr", attrs);
692
+ return "";
693
+ }
694
+ function serializeDefRunProps(o) {
695
+ const attrs = {};
696
+ if (o.fontSize) attrs.sz = ptToHundredths(o.fontSize);
697
+ if (o.bold) attrs.b = "1";
698
+ if (o.italic) attrs.i = "1";
699
+ let children = "";
700
+ if (o.color) children += serializeSolidFill(o.color);
701
+ if (o.fontFace) children += tag("a:latin", { typeface: o.fontFace });
702
+ return tag("a:defRPr", attrs, children || void 0);
703
+ }
704
+ function serializeParagraph(para, ctx) {
705
+ let xml = openTag("a:p");
706
+ const pOpts = para.options;
707
+ if (pOpts) {
708
+ const attrs = {};
709
+ if (pOpts.align) {
710
+ const map = { left: "l", center: "ctr", right: "r", justify: "just" };
711
+ attrs.algn = map[pOpts.align] ?? "l";
712
+ }
713
+ if (pOpts.level !== void 0) attrs.lvl = pOpts.level;
714
+ if (pOpts.indent) attrs.indent = ptToEmu(pOpts.indent);
715
+ if (pOpts.marginLeft) attrs.marL = ptToEmu(pOpts.marginLeft);
716
+ if (pOpts.rtl) attrs.rtl = "1";
717
+ let pPrChildren = "";
718
+ if (pOpts.lineSpacing) {
719
+ pPrChildren += tag("a:lnSpc", void 0, serializeSpacing(pOpts.lineSpacing));
720
+ }
721
+ if (pOpts.spaceBefore) {
722
+ pPrChildren += tag("a:spcBef", void 0, serializeSpacing(pOpts.spaceBefore));
723
+ }
724
+ if (pOpts.spaceAfter) {
725
+ pPrChildren += tag("a:spcAft", void 0, serializeSpacing(pOpts.spaceAfter));
726
+ }
727
+ if (pOpts.bullet) {
728
+ if (pOpts.bullet === true) {
729
+ pPrChildren += tag("a:buChar", { char: "\u2022" });
730
+ } else {
731
+ if (pOpts.bullet.color) {
732
+ pPrChildren += tag("a:buClr", void 0, serializeColor(pOpts.bullet.color));
733
+ }
734
+ if (pOpts.bullet.size) {
735
+ pPrChildren += tag("a:buSzPct", { val: Math.round(pOpts.bullet.size * 1e3) });
736
+ }
737
+ if (pOpts.bullet.font) {
738
+ pPrChildren += tag("a:buFont", { typeface: pOpts.bullet.font });
739
+ }
740
+ if (pOpts.bullet.type === "auto") {
741
+ pPrChildren += tag("a:buAutoNum", { type: "arabicPeriod" });
742
+ } else {
743
+ pPrChildren += tag("a:buChar", { char: pOpts.bullet.char ?? "\u2022" });
744
+ }
745
+ }
746
+ } else if (pOpts.numbering) {
747
+ if (pOpts.numbering.color) {
748
+ pPrChildren += tag("a:buClr", void 0, serializeColor(pOpts.numbering.color));
749
+ }
750
+ if (pOpts.numbering.size) {
751
+ pPrChildren += tag("a:buSzPct", { val: Math.round(pOpts.numbering.size * 1e3) });
752
+ }
753
+ if (pOpts.numbering.font) {
754
+ pPrChildren += tag("a:buFont", { typeface: pOpts.numbering.font });
755
+ }
756
+ const numAttrs = {
757
+ type: pOpts.numbering.type ?? "arabicPeriod"
758
+ };
759
+ if (pOpts.numbering.startAt) numAttrs.startAt = pOpts.numbering.startAt;
760
+ pPrChildren += tag("a:buAutoNum", numAttrs);
761
+ }
762
+ if (pOpts.tabStops) {
763
+ let tabsXml = "";
764
+ for (const ts of pOpts.tabStops) {
765
+ const tsAttrs = { pos: ptToEmu(ts.position) };
766
+ if (ts.alignment) tsAttrs.algn = ts.alignment;
767
+ tabsXml += tag("a:tab", tsAttrs);
768
+ }
769
+ pPrChildren += tag("a:tabLst", void 0, tabsXml);
770
+ }
771
+ if (pOpts.defaultRunOptions) {
772
+ pPrChildren += serializeDefRunProps(pOpts.defaultRunOptions);
773
+ }
774
+ xml += tag("a:pPr", attrs, pPrChildren || void 0);
775
+ }
776
+ for (const run of para.runs) {
777
+ xml += openTag("a:r");
778
+ xml += serializeRunProps(run, ctx);
779
+ xml += tag("a:t", void 0, run.text);
780
+ xml += closeTag("a:r");
781
+ }
782
+ xml += closeTag("a:p");
783
+ return xml;
784
+ }
785
+ function serializeBodyPr(bodyOpts) {
786
+ const attrs = { wrap: "square", rtlCol: "0" };
787
+ if (bodyOpts) {
788
+ if (bodyOpts.verticalAlign) {
789
+ const map = { top: "t", middle: "ctr", bottom: "b" };
790
+ attrs.anchor = map[bodyOpts.verticalAlign] ?? "t";
791
+ }
792
+ if (bodyOpts.textDirection) {
793
+ const map = {
794
+ horz: "horz",
795
+ vert: "vert",
796
+ vert270: "vert270",
797
+ wordArtVert: "wordArtVert"
798
+ };
799
+ if (map[bodyOpts.textDirection]) attrs.vert = map[bodyOpts.textDirection];
800
+ }
801
+ if (bodyOpts.wordWrap === false) attrs.wrap = "none";
802
+ if (bodyOpts.columns && bodyOpts.columns > 1) attrs.numCol = bodyOpts.columns;
803
+ if (bodyOpts.columnSpacing) attrs.spcCol = ptToEmu(bodyOpts.columnSpacing);
804
+ if (bodyOpts.margin) {
805
+ if (bodyOpts.margin.top !== void 0) attrs.tIns = ptToEmu(bodyOpts.margin.top);
806
+ if (bodyOpts.margin.bottom !== void 0) attrs.bIns = ptToEmu(bodyOpts.margin.bottom);
807
+ if (bodyOpts.margin.left !== void 0) attrs.lIns = ptToEmu(bodyOpts.margin.left);
808
+ if (bodyOpts.margin.right !== void 0) attrs.rIns = ptToEmu(bodyOpts.margin.right);
809
+ }
810
+ }
811
+ let children = "";
812
+ if (bodyOpts?.autoFit === true || bodyOpts?.autoFit === "shrink") {
813
+ children += tag("a:normAutofit");
814
+ } else if (bodyOpts?.autoFit === "none") {
815
+ children += tag("a:noAutofit");
816
+ }
817
+ if (bodyOpts?.textPath) {
818
+ children += tag("a:prstTxWarp", { prst: bodyOpts.textPath.preset }, tag("a:avLst"));
819
+ }
820
+ return tag("a:bodyPr", attrs, children || void 0);
821
+ }
822
+ var textSerializer = {
823
+ type: "text",
824
+ serialize(element, ctx) {
825
+ const p = element.placement;
826
+ let xml = openTag("p:sp");
827
+ xml += tag(
828
+ "p:nvSpPr",
829
+ void 0,
830
+ tag("p:cNvPr", { id: "0", name: element.name ?? "" }) + tag("p:cNvSpPr", { txBox: "1" }) + tag("p:nvPr")
831
+ );
832
+ xml += openTag("p:spPr");
833
+ xml += serializeTransform(p, element.transform);
834
+ xml += tag("a:prstGeom", { prst: "rect" }, tag("a:avLst"));
835
+ if (element.fill) xml += serializeFill(element.fill, ctx);
836
+ if (element.line) xml += serializeLine(element.line);
837
+ if (element.effects) xml += serializeEffects(element.effects);
838
+ if (element.scene3d) xml += serializeScene3D(element.scene3d);
839
+ if (element.shape3d) xml += serializeShape3D(element.shape3d);
840
+ xml += closeTag("p:spPr");
841
+ xml += openTag("p:txBody");
842
+ xml += serializeBodyPr(element.bodyOptions);
843
+ xml += tag("a:lstStyle");
844
+ for (const para of element.paragraphs) {
845
+ xml += serializeParagraph(para, ctx);
846
+ }
847
+ xml += closeTag("p:txBody");
848
+ xml += closeTag("p:sp");
849
+ return xml;
850
+ }
851
+ };
852
+
853
+ // src/ooxml/serializers/image.ts
854
+ var import_node_fs2 = require("fs");
855
+ var imageCounter = 0;
856
+ var imageSerializer = {
857
+ type: "image",
858
+ serialize(element, ctx) {
859
+ imageCounter++;
860
+ const ext = element.contentType.split("/")[1] ?? "png";
861
+ const fileName = `image${imageCounter}.${ext}`;
862
+ const target = `../media/${fileName}`;
863
+ let data = element.data;
864
+ if (!data && element.path) {
865
+ data = (0, import_node_fs2.readFileSync)(element.path);
866
+ }
867
+ if (!data) throw new Error("Image element must have data or path");
868
+ const rId = ctx.addRelationship(REL_TYPE.image, target, data);
869
+ const p = element.placement;
870
+ let xml = openTag("p:pic");
871
+ let cNvPrChildren = "";
872
+ if (element.hyperlink) {
873
+ cNvPrChildren = serializeHyperlink(element.hyperlink, ctx);
874
+ }
875
+ const lockAspect = element.lockAspectRatio !== false;
876
+ xml += tag(
877
+ "p:nvPicPr",
878
+ void 0,
879
+ tag("p:cNvPr", { id: "0", name: element.name ?? fileName }, cNvPrChildren || void 0) + tag(
880
+ "p:cNvPicPr",
881
+ void 0,
882
+ tag("a:picLocks", { noChangeAspect: lockAspect ? "1" : "0" })
883
+ ) + tag("p:nvPr")
884
+ );
885
+ let blipChildren = "";
886
+ if (element.opacity !== void 0 && element.opacity < 100) {
887
+ blipChildren += tag("a:alphaModFix", { amt: Math.round(element.opacity * 1e3) });
888
+ }
889
+ if (element.brightness !== void 0 || element.contrast !== void 0) {
890
+ const lumAttrs = {};
891
+ if (element.brightness !== void 0) lumAttrs.bright = element.brightness * 1e3;
892
+ if (element.contrast !== void 0) lumAttrs.contrast = element.contrast * 1e3;
893
+ blipChildren += tag("a:lum", lumAttrs);
894
+ }
895
+ if (element.saturation !== void 0) {
896
+ blipChildren += tag("a:saturation", { sat: Math.round(element.saturation * 1e3) });
897
+ }
898
+ if (element.duotone) {
899
+ blipChildren += tag(
900
+ "a:duotone",
901
+ void 0,
902
+ serializeColor(element.duotone.color1) + serializeColor(element.duotone.color2)
903
+ );
904
+ }
905
+ let fillContent = tag("a:blip", { "r:embed": rId }, blipChildren || void 0);
906
+ if (element.crop) {
907
+ const cropAttrs = {};
908
+ if (element.crop.top) cropAttrs.t = Math.round(element.crop.top * 1e3);
909
+ if (element.crop.bottom) cropAttrs.b = Math.round(element.crop.bottom * 1e3);
910
+ if (element.crop.left) cropAttrs.l = Math.round(element.crop.left * 1e3);
911
+ if (element.crop.right) cropAttrs.r = Math.round(element.crop.right * 1e3);
912
+ fillContent += tag("a:srcRect", cropAttrs);
913
+ }
914
+ fillContent += tag("a:stretch", void 0, tag("a:fillRect"));
915
+ xml += tag("p:blipFill", void 0, fillContent);
916
+ xml += openTag("p:spPr");
917
+ xml += serializeTransform(p, element.transform);
918
+ xml += tag("a:prstGeom", { prst: element.cropShape ?? "rect" }, tag("a:avLst"));
919
+ if (element.border) xml += serializeLine(element.border);
920
+ if (element.effects) xml += serializeEffects(element.effects);
921
+ if (element.scene3d) xml += serializeScene3D(element.scene3d);
922
+ if (element.shape3d) xml += serializeShape3D(element.shape3d);
923
+ xml += closeTag("p:spPr");
924
+ xml += closeTag("p:pic");
925
+ return xml;
926
+ }
927
+ };
928
+
929
+ // src/ooxml/serializers/shape.ts
930
+ var GEOMETRY_MAP = {
931
+ rect: "rect",
932
+ roundRect: "roundRect",
933
+ ellipse: "ellipse",
934
+ triangle: "triangle",
935
+ diamond: "diamond",
936
+ line: "line",
937
+ arrow: "rightArrow",
938
+ star5: "star5",
939
+ heart: "heart"
940
+ };
941
+ function serializeCustomGeometry(geom) {
942
+ let pathLst = "";
943
+ for (const path of geom.paths) {
944
+ const pathAttrs = {};
945
+ if (path.w !== void 0) pathAttrs.w = path.w;
946
+ if (path.h !== void 0) pathAttrs.h = path.h;
947
+ let cmds = "";
948
+ for (const cmd of path.commands) {
949
+ switch (cmd.type) {
950
+ case "moveTo":
951
+ cmds += tag("a:moveTo", void 0, tag("a:pt", { x: cmd.x, y: cmd.y }));
952
+ break;
953
+ case "lineTo":
954
+ cmds += tag("a:lnTo", void 0, tag("a:pt", { x: cmd.x, y: cmd.y }));
955
+ break;
956
+ case "arcTo":
957
+ cmds += tag("a:arcTo", { wR: cmd.wR, hR: cmd.hR, stAng: cmd.stAng, swAng: cmd.swAng });
958
+ break;
959
+ case "cubicBezTo":
960
+ cmds += tag(
961
+ "a:cubicBezTo",
962
+ void 0,
963
+ tag("a:pt", { x: cmd.x1, y: cmd.y1 }) + tag("a:pt", { x: cmd.x2, y: cmd.y2 }) + tag("a:pt", { x: cmd.x, y: cmd.y })
964
+ );
965
+ break;
966
+ case "close":
967
+ cmds += tag("a:close");
968
+ break;
969
+ }
970
+ }
971
+ pathLst += tag("a:path", Object.keys(pathAttrs).length > 0 ? pathAttrs : void 0, cmds);
972
+ }
973
+ return tag("a:custGeom", void 0, tag("a:avLst") + tag("a:pathLst", void 0, pathLst));
974
+ }
975
+ function serializeShapeLocks(locks) {
976
+ const attrs = {};
977
+ if (locks.noGroup) attrs.noGrp = "1";
978
+ if (locks.noRotation) attrs.noRot = "1";
979
+ if (locks.noMove) attrs.noMove = "1";
980
+ if (locks.noResize) attrs.noResize = "1";
981
+ if (locks.noSelect) attrs.noSelect = "1";
982
+ if (locks.noTextEdit) attrs.noTextEdit = "1";
983
+ return tag("a:spLocks", attrs);
984
+ }
985
+ var shapeSerializer = {
986
+ type: "shape",
987
+ serialize(element, ctx) {
988
+ const p = element.placement;
989
+ const prst = GEOMETRY_MAP[element.geometry] ?? element.geometry;
990
+ let xml = openTag("p:sp");
991
+ let cNvPrChildren = "";
992
+ if (element.hyperlink) {
993
+ cNvPrChildren = serializeHyperlink(element.hyperlink, ctx);
994
+ }
995
+ let cNvSpPrChildren = "";
996
+ if (element.locks) {
997
+ cNvSpPrChildren = serializeShapeLocks(element.locks);
998
+ }
999
+ xml += tag(
1000
+ "p:nvSpPr",
1001
+ void 0,
1002
+ tag("p:cNvPr", { id: "0", name: element.name ?? "" }, cNvPrChildren || void 0) + tag("p:cNvSpPr", void 0, cNvSpPrChildren || void 0) + tag("p:nvPr")
1003
+ );
1004
+ xml += openTag("p:spPr");
1005
+ xml += serializeTransform(p, element.transform);
1006
+ if (element.customGeometry) {
1007
+ xml += serializeCustomGeometry(element.customGeometry);
1008
+ } else {
1009
+ let avLstXml = "";
1010
+ if (element.adjustValues) {
1011
+ for (const [name, val] of Object.entries(element.adjustValues)) {
1012
+ avLstXml += tag("a:gd", { name, fmla: `val ${val}` });
1013
+ }
1014
+ }
1015
+ xml += tag(
1016
+ "a:prstGeom",
1017
+ { prst },
1018
+ avLstXml ? tag("a:avLst", void 0, avLstXml) : tag("a:avLst")
1019
+ );
1020
+ }
1021
+ if (element.fill) {
1022
+ xml += serializeFill(element.fill, ctx);
1023
+ }
1024
+ if (element.line) {
1025
+ xml += serializeLine(element.line);
1026
+ }
1027
+ if (element.effects) {
1028
+ xml += serializeEffects(element.effects);
1029
+ }
1030
+ if (element.scene3d) {
1031
+ xml += serializeScene3D(element.scene3d);
1032
+ }
1033
+ if (element.shape3d) {
1034
+ xml += serializeShape3D(element.shape3d);
1035
+ }
1036
+ xml += closeTag("p:spPr");
1037
+ if (element.text) {
1038
+ xml += openTag("p:txBody");
1039
+ const bodyAttrs = { wrap: "square", rtlCol: "0" };
1040
+ if (element.textOptions) {
1041
+ if (element.textOptions.verticalAlign) {
1042
+ const map = { top: "t", middle: "ctr", bottom: "b" };
1043
+ bodyAttrs.anchor = map[element.textOptions.verticalAlign] ?? "t";
1044
+ }
1045
+ if (element.textOptions.wordWrap === false) bodyAttrs.wrap = "none";
1046
+ if (element.textOptions.margin) {
1047
+ if (element.textOptions.margin.top !== void 0)
1048
+ bodyAttrs.tIns = ptToEmu(element.textOptions.margin.top);
1049
+ if (element.textOptions.margin.bottom !== void 0)
1050
+ bodyAttrs.bIns = ptToEmu(element.textOptions.margin.bottom);
1051
+ if (element.textOptions.margin.left !== void 0)
1052
+ bodyAttrs.lIns = ptToEmu(element.textOptions.margin.left);
1053
+ if (element.textOptions.margin.right !== void 0)
1054
+ bodyAttrs.rIns = ptToEmu(element.textOptions.margin.right);
1055
+ }
1056
+ }
1057
+ let bodyChildren = "";
1058
+ if (element.textOptions?.autoFit === true || element.textOptions?.autoFit === "shrink") {
1059
+ bodyChildren = tag("a:normAutofit");
1060
+ } else if (element.textOptions?.autoFit === "none") {
1061
+ bodyChildren = tag("a:noAutofit");
1062
+ }
1063
+ xml += tag("a:bodyPr", bodyAttrs, bodyChildren || void 0);
1064
+ xml += tag("a:lstStyle");
1065
+ const lines = element.text.split("\n");
1066
+ for (const line of lines) {
1067
+ xml += serializeParagraph(
1068
+ {
1069
+ runs: [{ text: line, options: element.textRunOptions }],
1070
+ options: element.textParagraphOptions
1071
+ },
1072
+ ctx
1073
+ );
1074
+ }
1075
+ xml += closeTag("p:txBody");
1076
+ }
1077
+ xml += closeTag("p:sp");
1078
+ return xml;
1079
+ }
1080
+ };
1081
+
1082
+ // src/ooxml/serializers/table.ts
1083
+ function serializeBorderLine(b, defaultWidth) {
1084
+ const bw = b.width ? ptToEmu(b.width) : defaultWidth ?? 12700;
1085
+ const bc = b.color ?? "000000";
1086
+ return tag("a:ln", { w: bw }, serializeSolidFill(bc));
1087
+ }
1088
+ function serializeCellBorders(cell, _tableBorder) {
1089
+ const o = cell.options;
1090
+ let xml = "";
1091
+ const sides = [
1092
+ { cellKey: "borderLeft", tblKey: "left", elName: "a:lnL" },
1093
+ { cellKey: "borderRight", tblKey: "right", elName: "a:lnR" },
1094
+ { cellKey: "borderTop", tblKey: "top", elName: "a:lnT" },
1095
+ { cellKey: "borderBottom", tblKey: "bottom", elName: "a:lnB" }
1096
+ ];
1097
+ for (const side of sides) {
1098
+ const cellBorder = o?.[side.cellKey];
1099
+ if (cellBorder) {
1100
+ xml += tag(side.elName, void 0, serializeBorderLine(cellBorder));
1101
+ }
1102
+ }
1103
+ return xml;
1104
+ }
1105
+ function serializeCell(cell, tableBorder, ctx) {
1106
+ const o = cell.options;
1107
+ const tcAttrs = {};
1108
+ if (o?.colSpan && o.colSpan > 1) tcAttrs.gridSpan = o.colSpan;
1109
+ if (o?.rowSpan && o.rowSpan > 1) tcAttrs.rowSpan = o.rowSpan;
1110
+ let xml = openTag("a:tc", Object.keys(tcAttrs).length > 0 ? tcAttrs : void 0);
1111
+ xml += openTag("a:txBody");
1112
+ {
1113
+ const bodyAttrs = {};
1114
+ if (o?.textBody) {
1115
+ if (o.textBody.verticalAlign) {
1116
+ const m = { top: "t", middle: "ctr", bottom: "b" };
1117
+ bodyAttrs.anchor = m[o.textBody.verticalAlign] ?? "t";
1118
+ }
1119
+ if (o.textBody.wordWrap === false) bodyAttrs.wrap = "none";
1120
+ if (o.textBody.margin) {
1121
+ if (o.textBody.margin.top !== void 0) bodyAttrs.tIns = ptToEmu(o.textBody.margin.top);
1122
+ if (o.textBody.margin.bottom !== void 0)
1123
+ bodyAttrs.bIns = ptToEmu(o.textBody.margin.bottom);
1124
+ if (o.textBody.margin.left !== void 0) bodyAttrs.lIns = ptToEmu(o.textBody.margin.left);
1125
+ if (o.textBody.margin.right !== void 0)
1126
+ bodyAttrs.rIns = ptToEmu(o.textBody.margin.right);
1127
+ }
1128
+ }
1129
+ xml += tag("a:bodyPr", Object.keys(bodyAttrs).length > 0 ? bodyAttrs : void 0);
1130
+ }
1131
+ xml += tag("a:lstStyle");
1132
+ xml += openTag("a:p");
1133
+ if (o?.align) {
1134
+ const map = { left: "l", center: "ctr", right: "r", justify: "just" };
1135
+ xml += tag("a:pPr", { algn: map[o.align] ?? "l" });
1136
+ }
1137
+ xml += openTag("a:r");
1138
+ const rAttrs = {};
1139
+ if (o?.fontSize) rAttrs.sz = ptToHundredths(o.fontSize);
1140
+ if (o?.bold) rAttrs.b = "1";
1141
+ if (o?.italic) rAttrs.i = "1";
1142
+ if (o?.underline) rAttrs.u = "sng";
1143
+ if (o?.strikethrough) {
1144
+ rAttrs.strike = o.strikethrough === "double" ? "dblStrike" : "sngStrike";
1145
+ }
1146
+ let rPrChildren = "";
1147
+ if (o?.color) rPrChildren += serializeSolidFill(o.color);
1148
+ if (o?.fontFace) rPrChildren += tag("a:latin", { typeface: o.fontFace });
1149
+ xml += tag("a:rPr", { lang: "en-US", ...rAttrs }, rPrChildren || void 0);
1150
+ xml += tag("a:t", void 0, cell.text);
1151
+ xml += closeTag("a:r");
1152
+ xml += closeTag("a:p");
1153
+ xml += closeTag("a:txBody");
1154
+ let tcPr = "";
1155
+ const tcPrAttrs = {};
1156
+ if (o?.verticalAlign) {
1157
+ const map = {
1158
+ top: "t",
1159
+ middle: "ctr",
1160
+ bottom: "b",
1161
+ distributed: "dist"
1162
+ };
1163
+ tcPrAttrs.anchor = map[o.verticalAlign] ?? "t";
1164
+ }
1165
+ tcPr += serializeCellBorders(cell, tableBorder);
1166
+ if (o?.fill) {
1167
+ tcPr += serializeFill(o.fill, ctx);
1168
+ }
1169
+ if (o?.margin) {
1170
+ if (o.margin.top !== void 0) tcPrAttrs.marT = String(ptToEmu(o.margin.top));
1171
+ if (o.margin.bottom !== void 0) tcPrAttrs.marB = String(ptToEmu(o.margin.bottom));
1172
+ if (o.margin.left !== void 0) tcPrAttrs.marL = String(ptToEmu(o.margin.left));
1173
+ if (o.margin.right !== void 0) tcPrAttrs.marR = String(ptToEmu(o.margin.right));
1174
+ }
1175
+ xml += tag(
1176
+ "a:tcPr",
1177
+ Object.keys(tcPrAttrs).length > 0 ? tcPrAttrs : void 0,
1178
+ tcPr || void 0
1179
+ );
1180
+ xml += closeTag("a:tc");
1181
+ return xml;
1182
+ }
1183
+ function serializeTableBorders(border) {
1184
+ const bw = border.width ? ptToEmu(border.width) : 12700;
1185
+ const bc = border.color ?? "000000";
1186
+ const defaultLine = tag("a:ln", { w: bw }, serializeSolidFill(bc));
1187
+ const sides = [
1188
+ { key: "left", elName: "a:left" },
1189
+ { key: "right", elName: "a:right" },
1190
+ { key: "top", elName: "a:top" },
1191
+ { key: "bottom", elName: "a:bottom" },
1192
+ { key: "insideH", elName: "a:insideH" },
1193
+ { key: "insideV", elName: "a:insideV" }
1194
+ ];
1195
+ let xml = "";
1196
+ for (const side of sides) {
1197
+ const override = border[side.key];
1198
+ if (typeof override === "object" && override !== null && "color" in override) {
1199
+ xml += tag(side.elName, void 0, serializeBorderLine(override));
1200
+ } else {
1201
+ xml += tag(side.elName, void 0, defaultLine);
1202
+ }
1203
+ }
1204
+ return xml;
1205
+ }
1206
+ var tableSerializer = {
1207
+ type: "table",
1208
+ serialize(element, ctx) {
1209
+ const p = element.placement;
1210
+ const numCols = element.rows[0]?.length ?? 0;
1211
+ let xml = openTag("p:graphicFrame");
1212
+ xml += tag(
1213
+ "p:nvGraphicFramePr",
1214
+ void 0,
1215
+ tag("p:cNvPr", { id: "0", name: element.name ?? "Table" }) + tag("p:cNvGraphicFramePr", void 0, tag("a:graphicFrameLocks", { noGrp: "1" })) + tag("p:nvPr")
1216
+ );
1217
+ xml += tag(
1218
+ "p:xfrm",
1219
+ void 0,
1220
+ tag("a:off", { x: inchesToEmu(p.x), y: inchesToEmu(p.y) }) + tag("a:ext", { cx: inchesToEmu(p.w), cy: inchesToEmu(p.h) })
1221
+ );
1222
+ xml += openTag("a:graphic");
1223
+ xml += openTag("a:graphicData", {
1224
+ uri: "http://schemas.openxmlformats.org/drawingml/2006/table"
1225
+ });
1226
+ xml += openTag("a:tbl");
1227
+ const tblPrAttrs = {
1228
+ firstRow: element.firstRow ? "1" : "0",
1229
+ lastRow: element.lastRow ? "1" : "0",
1230
+ firstCol: element.firstCol ? "1" : "0",
1231
+ lastCol: element.lastCol ? "1" : "0",
1232
+ bandRow: element.bandRow ? "1" : "0",
1233
+ bandCol: element.bandCol ? "1" : "0"
1234
+ };
1235
+ let tblPrChildren = "";
1236
+ if (element.fill) {
1237
+ tblPrChildren += serializeFill(element.fill, ctx);
1238
+ }
1239
+ if (element.border) {
1240
+ tblPrChildren += serializeTableBorders(element.border);
1241
+ }
1242
+ if (element.tableStyleId) {
1243
+ tblPrChildren += tag("a:tblStyle", { val: element.tableStyleId });
1244
+ }
1245
+ xml += tag("a:tblPr", tblPrAttrs, tblPrChildren || void 0);
1246
+ xml += openTag("a:tblGrid");
1247
+ const defaultColWidth = p.w / numCols;
1248
+ for (let i = 0; i < numCols; i++) {
1249
+ const w = element.colWidths?.[i] ?? defaultColWidth;
1250
+ xml += tag("a:gridCol", { w: inchesToEmu(w) });
1251
+ }
1252
+ xml += closeTag("a:tblGrid");
1253
+ const defaultRowHeight = p.h / element.rows.length;
1254
+ for (let r = 0; r < element.rows.length; r++) {
1255
+ const h = element.rowHeights?.[r] ?? defaultRowHeight;
1256
+ xml += openTag("a:tr", { h: inchesToEmu(h) });
1257
+ for (const cell of element.rows[r]) {
1258
+ xml += serializeCell(cell, element.border, ctx);
1259
+ }
1260
+ xml += closeTag("a:tr");
1261
+ }
1262
+ xml += closeTag("a:tbl");
1263
+ xml += closeTag("a:graphicData");
1264
+ xml += closeTag("a:graphic");
1265
+ xml += closeTag("p:graphicFrame");
1266
+ return xml;
1267
+ }
1268
+ };
1269
+
1270
+ // src/ooxml/serializers/chart.ts
1271
+ function serializeTrendline(t) {
1272
+ let xml = openTag("c:trendline");
1273
+ xml += tag("c:trendlineType", { val: t.type ?? "linear" });
1274
+ if (t.order !== void 0) xml += tag("c:order", { val: t.order });
1275
+ if (t.period !== void 0) xml += tag("c:period", { val: t.period });
1276
+ if (t.forward !== void 0) xml += tag("c:forward", { val: t.forward });
1277
+ if (t.backward !== void 0) xml += tag("c:backward", { val: t.backward });
1278
+ if (t.intercept !== void 0) xml += tag("c:intercept", { val: t.intercept });
1279
+ if (t.displayEquation) xml += tag("c:dispEq", { val: "1" });
1280
+ if (t.displayRSquared) xml += tag("c:dispRSqr", { val: "1" });
1281
+ if (t.color || t.width || t.dash) {
1282
+ let spPr = "";
1283
+ const lnAttrs = {};
1284
+ if (t.width) lnAttrs.w = ptToEmu(t.width);
1285
+ let lnChildren = "";
1286
+ if (t.color) lnChildren += serializeSolidFill(t.color);
1287
+ if (t.dash && t.dash !== "solid") lnChildren += tag("a:prstDash", { val: t.dash });
1288
+ spPr += tag(
1289
+ "a:ln",
1290
+ Object.keys(lnAttrs).length > 0 ? lnAttrs : void 0,
1291
+ lnChildren || void 0
1292
+ );
1293
+ xml += tag("c:spPr", void 0, spPr);
1294
+ }
1295
+ xml += closeTag("c:trendline");
1296
+ return xml;
1297
+ }
1298
+ function serializeErrorBars(e) {
1299
+ let xml = openTag("c:errBars");
1300
+ xml += tag("c:errDir", { val: e.direction ?? "y" });
1301
+ xml += tag("c:errBarType", { val: e.type ?? "both" });
1302
+ xml += tag("c:errValType", { val: e.valueType ?? "fixedVal" });
1303
+ if (e.noEndCap) xml += tag("c:noEndCap", { val: "1" });
1304
+ if (e.value !== void 0) xml += tag("c:val", { val: e.value });
1305
+ if (e.color || e.width) {
1306
+ let spPr = "";
1307
+ const lnAttrs = {};
1308
+ if (e.width) lnAttrs.w = ptToEmu(e.width);
1309
+ let lnChildren = "";
1310
+ if (e.color) lnChildren += serializeSolidFill(e.color);
1311
+ spPr += tag(
1312
+ "a:ln",
1313
+ Object.keys(lnAttrs).length > 0 ? lnAttrs : void 0,
1314
+ lnChildren || void 0
1315
+ );
1316
+ xml += tag("c:spPr", void 0, spPr);
1317
+ }
1318
+ xml += closeTag("c:errBars");
1319
+ return xml;
1320
+ }
1321
+ function serializeDataLabels(dl) {
1322
+ let xml = openTag("c:dLbls");
1323
+ if (dl.position) xml += tag("c:dLblPos", { val: dl.position });
1324
+ if (dl.numberFormat) {
1325
+ xml += tag("c:numFmt", { formatCode: dl.numberFormat, sourceLinked: "0" });
1326
+ }
1327
+ if (dl.fontSize || dl.fontFace || dl.color) {
1328
+ let rPrChildren = "";
1329
+ if (dl.color) rPrChildren += serializeSolidFill(dl.color);
1330
+ if (dl.fontFace) rPrChildren += tag("a:latin", { typeface: dl.fontFace });
1331
+ const rPrAttrs = {};
1332
+ if (dl.fontSize) rPrAttrs.sz = ptToHundredths(dl.fontSize);
1333
+ xml += tag(
1334
+ "c:txPr",
1335
+ void 0,
1336
+ tag("a:bodyPr") + tag("a:lstStyle") + tag(
1337
+ "a:p",
1338
+ void 0,
1339
+ tag(
1340
+ "a:pPr",
1341
+ void 0,
1342
+ tag(
1343
+ "a:defRPr",
1344
+ Object.keys(rPrAttrs).length > 0 ? rPrAttrs : void 0,
1345
+ rPrChildren || void 0
1346
+ )
1347
+ )
1348
+ )
1349
+ );
1350
+ }
1351
+ if (dl.separator) xml += tag("c:separator", void 0, dl.separator);
1352
+ xml += tag("c:showVal", { val: dl.showValue !== false ? "1" : "0" });
1353
+ xml += tag("c:showCatName", { val: dl.showCategory ? "1" : "0" });
1354
+ xml += tag("c:showSerName", { val: dl.showSeriesName ? "1" : "0" });
1355
+ xml += tag("c:showPercent", { val: dl.showPercent ? "1" : "0" });
1356
+ xml += closeTag("c:dLbls");
1357
+ return xml;
1358
+ }
1359
+ var chartCounter = 0;
1360
+ var CHART_NS = "http://schemas.openxmlformats.org/drawingml/2006/chart";
1361
+ var CHART_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart";
1362
+ var DEFAULT_COLORS = [
1363
+ "4472C4",
1364
+ "ED7D31",
1365
+ "A5A5A5",
1366
+ "FFC000",
1367
+ "5B9BD5",
1368
+ "70AD47",
1369
+ "264478",
1370
+ "9B57A2",
1371
+ "636363",
1372
+ "EB6B0A"
1373
+ ];
1374
+ function serializeAxis(axisTag, axId, crossAxId, position, opts, showGridLines) {
1375
+ let xml = openTag(axisTag);
1376
+ xml += tag("c:axId", void 0, axId);
1377
+ let scalingChildren = tag("c:orientation", { val: opts?.orientation ?? "minMax" });
1378
+ if (opts?.min !== void 0) scalingChildren += tag("c:min", { val: opts.min });
1379
+ if (opts?.max !== void 0) scalingChildren += tag("c:max", { val: opts.max });
1380
+ if (opts?.logBase !== void 0) scalingChildren += tag("c:logBase", { val: opts.logBase });
1381
+ xml += tag("c:scaling", void 0, scalingChildren);
1382
+ xml += tag("c:delete", { val: opts?.delete ? "1" : "0" });
1383
+ xml += tag("c:axPos", { val: opts?.position ?? position });
1384
+ if (opts?.format) {
1385
+ xml += tag("c:numFmt", { formatCode: opts.format, sourceLinked: "0" });
1386
+ }
1387
+ const showMajor = opts?.majorGridlines ?? (axisTag === "c:valAx" && showGridLines);
1388
+ if (showMajor) {
1389
+ if (typeof showMajor === "object") {
1390
+ const glAttrs = {};
1391
+ if (showMajor.width) glAttrs.w = ptToEmu(showMajor.width);
1392
+ let glChildren = "";
1393
+ if (showMajor.color) {
1394
+ glChildren = serializeSolidFill(showMajor.color);
1395
+ }
1396
+ xml += tag(
1397
+ "c:majorGridlines",
1398
+ void 0,
1399
+ tag("c:spPr", void 0, tag("a:ln", glAttrs, glChildren || void 0))
1400
+ );
1401
+ } else {
1402
+ xml += tag("c:majorGridlines");
1403
+ }
1404
+ }
1405
+ if (opts?.minorGridlines) {
1406
+ if (typeof opts.minorGridlines === "object") {
1407
+ const glAttrs = {};
1408
+ if (opts.minorGridlines.width) glAttrs.w = ptToEmu(opts.minorGridlines.width);
1409
+ let glChildren = "";
1410
+ if (opts.minorGridlines.color) {
1411
+ glChildren = serializeSolidFill(opts.minorGridlines.color);
1412
+ }
1413
+ xml += tag(
1414
+ "c:minorGridlines",
1415
+ void 0,
1416
+ tag("c:spPr", void 0, tag("a:ln", glAttrs, glChildren || void 0))
1417
+ );
1418
+ } else {
1419
+ xml += tag("c:minorGridlines");
1420
+ }
1421
+ }
1422
+ if (opts?.title) {
1423
+ const titleRPrAttrs = { lang: "en-US" };
1424
+ if (opts.titleFontSize) titleRPrAttrs.sz = ptToHundredths(opts.titleFontSize);
1425
+ xml += tag(
1426
+ "c:title",
1427
+ void 0,
1428
+ tag(
1429
+ "c:tx",
1430
+ void 0,
1431
+ tag(
1432
+ "c:rich",
1433
+ void 0,
1434
+ tag("a:bodyPr") + tag("a:lstStyle") + tag(
1435
+ "a:p",
1436
+ void 0,
1437
+ tag("a:pPr", void 0, tag("a:defRPr", titleRPrAttrs)) + tag(
1438
+ "a:r",
1439
+ void 0,
1440
+ tag("a:rPr", titleRPrAttrs) + tag("a:t", void 0, escapeXml(opts.title))
1441
+ )
1442
+ )
1443
+ )
1444
+ ) + tag("c:overlay", { val: "0" })
1445
+ );
1446
+ }
1447
+ if (opts?.majorUnit) xml += tag("c:majorUnit", { val: opts.majorUnit });
1448
+ if (opts?.minorUnit) xml += tag("c:minorUnit", { val: opts.minorUnit });
1449
+ if (opts?.labelFontSize || opts?.labelColor || opts?.labelRotation !== void 0) {
1450
+ const bodyAttrs = {};
1451
+ if (opts?.labelRotation !== void 0) bodyAttrs.rot = Math.round(opts.labelRotation * 6e4);
1452
+ const rPrAttrs = {};
1453
+ if (opts?.labelFontSize) rPrAttrs.sz = ptToHundredths(opts.labelFontSize);
1454
+ let rPrChildren = "";
1455
+ if (opts?.labelColor) rPrChildren = serializeSolidFill(opts.labelColor);
1456
+ xml += tag(
1457
+ "c:txPr",
1458
+ void 0,
1459
+ tag("a:bodyPr", bodyAttrs) + tag("a:lstStyle") + tag(
1460
+ "a:p",
1461
+ void 0,
1462
+ tag("a:pPr", void 0, tag("a:defRPr", rPrAttrs, rPrChildren || void 0))
1463
+ )
1464
+ );
1465
+ }
1466
+ xml += tag("c:crossAx", void 0, crossAxId);
1467
+ if (opts?.crossesAt !== void 0) {
1468
+ if (opts.crossesAt === "min") xml += tag("c:crosses", { val: "min" });
1469
+ else if (opts.crossesAt === "max") xml += tag("c:crosses", { val: "max" });
1470
+ else if (opts.crossesAt === "auto") xml += tag("c:crosses", { val: "autoZero" });
1471
+ else xml += tag("c:crossesAt", { val: opts.crossesAt });
1472
+ }
1473
+ xml += closeTag(axisTag);
1474
+ return xml;
1475
+ }
1476
+ function serializeSeriesData(data, chartType, showDataLabels) {
1477
+ let xml = "";
1478
+ const isScatter = chartType === "scatter";
1479
+ for (let i = 0; i < data.series.length; i++) {
1480
+ const s = data.series[i];
1481
+ const color = s.color ?? DEFAULT_COLORS[i % DEFAULT_COLORS.length];
1482
+ xml += openTag("c:ser");
1483
+ xml += tag("c:idx", { val: i });
1484
+ xml += tag("c:order", { val: i });
1485
+ xml += tag(
1486
+ "c:tx",
1487
+ void 0,
1488
+ tag(
1489
+ "c:strRef",
1490
+ void 0,
1491
+ tag("c:f", void 0, `Sheet1!$B$1`) + tag(
1492
+ "c:strCache",
1493
+ void 0,
1494
+ tag("c:ptCount", { val: "1" }) + tag("c:pt", { idx: "0" }, tag("c:v", void 0, escapeXml(s.name)))
1495
+ )
1496
+ )
1497
+ );
1498
+ let spPrChildren = serializeSolidFill(color);
1499
+ if (s.lineWidth || s.lineDash) {
1500
+ const lnAttrs = {};
1501
+ if (s.lineWidth) lnAttrs.w = ptToEmu(s.lineWidth);
1502
+ let lnChildren = serializeSolidFill(color);
1503
+ if (s.lineDash && s.lineDash !== "solid") {
1504
+ lnChildren += tag("a:prstDash", { val: s.lineDash });
1505
+ }
1506
+ spPrChildren += tag("a:ln", lnAttrs, lnChildren);
1507
+ }
1508
+ if (s.opacity !== void 0 && s.opacity < 100) {
1509
+ spPrChildren = tag(
1510
+ "a:solidFill",
1511
+ void 0,
1512
+ serializeColorWithAlpha(s.fill ?? color, s.opacity)
1513
+ );
1514
+ }
1515
+ xml += tag("c:spPr", void 0, spPrChildren);
1516
+ if (s.marker) {
1517
+ let markerXml = "";
1518
+ markerXml += tag("c:symbol", { val: s.marker.type ?? "circle" });
1519
+ if (s.marker.size) markerXml += tag("c:size", { val: s.marker.size });
1520
+ let markerSpPr = "";
1521
+ if (s.marker.fill) {
1522
+ markerSpPr += serializeSolidFill(s.marker.fill);
1523
+ }
1524
+ if (s.marker.border) {
1525
+ let lnChildren = "";
1526
+ if (s.marker.border.color) {
1527
+ lnChildren = serializeSolidFill(s.marker.border.color);
1528
+ }
1529
+ const lnAttrs = {};
1530
+ if (s.marker.border.width) lnAttrs.w = ptToEmu(s.marker.border.width);
1531
+ markerSpPr += tag("a:ln", lnAttrs, lnChildren || void 0);
1532
+ }
1533
+ if (markerSpPr) markerXml += tag("c:spPr", void 0, markerSpPr);
1534
+ xml += tag("c:marker", void 0, markerXml);
1535
+ }
1536
+ if (s.colors && s.colors.length > 0) {
1537
+ for (let j = 0; j < s.colors.length; j++) {
1538
+ xml += tag(
1539
+ "c:dPt",
1540
+ void 0,
1541
+ tag("c:idx", { val: j }) + tag("c:spPr", void 0, serializeSolidFill(s.colors[j]))
1542
+ );
1543
+ }
1544
+ }
1545
+ if (showDataLabels) {
1546
+ xml += tag(
1547
+ "c:dLbls",
1548
+ void 0,
1549
+ tag("c:showVal", { val: "1" }) + tag("c:showCatName", { val: "0" }) + tag("c:showSerName", { val: "0" })
1550
+ );
1551
+ } else if (s.dataLabels) {
1552
+ xml += serializeDataLabels(s.dataLabels);
1553
+ }
1554
+ if (s.trendline) {
1555
+ xml += serializeTrendline(s.trendline);
1556
+ }
1557
+ if (s.errorBars) {
1558
+ xml += serializeErrorBars(s.errorBars);
1559
+ }
1560
+ if (s.explosion !== void 0) {
1561
+ xml += tag("c:explosion", { val: s.explosion });
1562
+ }
1563
+ if (s.smooth) {
1564
+ xml += tag("c:smooth", { val: "1" });
1565
+ }
1566
+ if (data.categories && !isScatter) {
1567
+ xml += openTag("c:cat");
1568
+ xml += openTag("c:strRef");
1569
+ xml += tag("c:f", void 0, "Sheet1!$A$2:$A$" + (data.categories.length + 1));
1570
+ xml += openTag("c:strCache");
1571
+ xml += tag("c:ptCount", { val: data.categories.length });
1572
+ for (let j = 0; j < data.categories.length; j++) {
1573
+ xml += tag("c:pt", { idx: j }, tag("c:v", void 0, escapeXml(data.categories[j])));
1574
+ }
1575
+ xml += closeTag("c:strCache");
1576
+ xml += closeTag("c:strRef");
1577
+ xml += closeTag("c:cat");
1578
+ }
1579
+ if (isScatter && data.xValues) {
1580
+ xml += openTag("c:xVal");
1581
+ xml += openTag("c:numRef");
1582
+ xml += tag("c:f", void 0, `Sheet1!$A$2:$A$${data.xValues.length + 1}`);
1583
+ xml += openTag("c:numCache");
1584
+ xml += tag("c:formatCode", void 0, "General");
1585
+ xml += tag("c:ptCount", { val: data.xValues.length });
1586
+ for (let j = 0; j < data.xValues.length; j++) {
1587
+ xml += tag("c:pt", { idx: j }, tag("c:v", void 0, String(data.xValues[j])));
1588
+ }
1589
+ xml += closeTag("c:numCache");
1590
+ xml += closeTag("c:numRef");
1591
+ xml += closeTag("c:xVal");
1592
+ xml += openTag("c:yVal");
1593
+ } else {
1594
+ xml += openTag("c:val");
1595
+ }
1596
+ xml += openTag("c:numRef");
1597
+ xml += tag("c:f", void 0, `Sheet1!$B$2:$B$${s.values.length + 1}`);
1598
+ xml += openTag("c:numCache");
1599
+ xml += tag("c:formatCode", void 0, "General");
1600
+ xml += tag("c:ptCount", { val: s.values.length });
1601
+ for (let j = 0; j < s.values.length; j++) {
1602
+ xml += tag("c:pt", { idx: j }, tag("c:v", void 0, String(s.values[j])));
1603
+ }
1604
+ xml += closeTag("c:numCache");
1605
+ xml += closeTag("c:numRef");
1606
+ xml += closeTag(isScatter ? "c:yVal" : "c:val");
1607
+ if (chartType === "bubble" && data.bubbleSizes) {
1608
+ xml += openTag("c:bubbleSize");
1609
+ xml += openTag("c:numRef");
1610
+ xml += tag("c:f", void 0, `Sheet1!$C$2:$C$${data.bubbleSizes.length + 1}`);
1611
+ xml += openTag("c:numCache");
1612
+ xml += tag("c:formatCode", void 0, "General");
1613
+ xml += tag("c:ptCount", { val: data.bubbleSizes.length });
1614
+ for (let j = 0; j < data.bubbleSizes.length; j++) {
1615
+ xml += tag("c:pt", { idx: j }, tag("c:v", void 0, String(data.bubbleSizes[j])));
1616
+ }
1617
+ xml += closeTag("c:numCache");
1618
+ xml += closeTag("c:numRef");
1619
+ xml += closeTag("c:bubbleSize");
1620
+ }
1621
+ xml += closeTag("c:ser");
1622
+ }
1623
+ return xml;
1624
+ }
1625
+ function getOoxmlChartTag(chartType) {
1626
+ const map = {
1627
+ bar: "c:barChart",
1628
+ col: "c:barChart",
1629
+ line: "c:lineChart",
1630
+ pie: "c:pieChart",
1631
+ area: "c:areaChart",
1632
+ scatter: "c:scatterChart",
1633
+ doughnut: "c:doughnutChart",
1634
+ radar: "c:radarChart",
1635
+ bubble: "c:bubbleChart",
1636
+ stock: "c:stockChart",
1637
+ surface: "c:surfaceChart",
1638
+ bar3D: "c:bar3DChart",
1639
+ col3D: "c:bar3DChart",
1640
+ line3D: "c:line3DChart",
1641
+ pie3D: "c:pie3DChart",
1642
+ area3D: "c:area3DChart"
1643
+ };
1644
+ return map[chartType];
1645
+ }
1646
+ function serializeChartXml(element) {
1647
+ const { chartType, data, title, showLegend, showDataLabels, showGridLines } = element;
1648
+ const chartTag = getOoxmlChartTag(chartType);
1649
+ const noAxesTypes = ["pie", "doughnut", "pie3D"];
1650
+ const hasAxes = !noAxesTypes.includes(chartType);
1651
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
1652
+ xml += openTag("c:chartSpace", {
1653
+ "xmlns:c": CHART_NS,
1654
+ "xmlns:a": NAMESPACE.a,
1655
+ "xmlns:r": NAMESPACE.r
1656
+ });
1657
+ xml += openTag("c:chart");
1658
+ if (title) {
1659
+ const titleRPrAttrs = { lang: "en-US" };
1660
+ if (element.titleFontSize) titleRPrAttrs.sz = ptToHundredths(element.titleFontSize);
1661
+ let titleRPrChildren = "";
1662
+ if (element.titleColor) {
1663
+ titleRPrChildren = serializeSolidFill(element.titleColor);
1664
+ }
1665
+ xml += tag(
1666
+ "c:title",
1667
+ void 0,
1668
+ tag(
1669
+ "c:tx",
1670
+ void 0,
1671
+ tag(
1672
+ "c:rich",
1673
+ void 0,
1674
+ tag("a:bodyPr") + tag("a:lstStyle") + tag(
1675
+ "a:p",
1676
+ void 0,
1677
+ tag(
1678
+ "a:pPr",
1679
+ void 0,
1680
+ tag("a:defRPr", titleRPrAttrs, titleRPrChildren || void 0)
1681
+ ) + tag(
1682
+ "a:r",
1683
+ void 0,
1684
+ tag("a:rPr", titleRPrAttrs, titleRPrChildren || void 0) + tag("a:t", void 0, escapeXml(title))
1685
+ )
1686
+ )
1687
+ )
1688
+ ) + tag("c:overlay", { val: "0" })
1689
+ );
1690
+ } else {
1691
+ xml += tag("c:autoTitleDeleted", { val: "1" });
1692
+ }
1693
+ if (element.view3D) {
1694
+ let v3d = "";
1695
+ if (element.view3D.rotX !== void 0) v3d += tag("c:rotX", { val: element.view3D.rotX });
1696
+ if (element.view3D.rotY !== void 0) v3d += tag("c:rotY", { val: element.view3D.rotY });
1697
+ if (element.view3D.depthPercent !== void 0)
1698
+ v3d += tag("c:depthPercent", { val: element.view3D.depthPercent });
1699
+ if (element.view3D.rAngAx !== void 0)
1700
+ v3d += tag("c:rAngAx", { val: element.view3D.rAngAx ? "1" : "0" });
1701
+ if (element.view3D.perspective !== void 0)
1702
+ v3d += tag("c:perspective", { val: element.view3D.perspective });
1703
+ xml += tag("c:view3D", void 0, v3d);
1704
+ }
1705
+ xml += openTag("c:plotArea");
1706
+ xml += tag("c:layout");
1707
+ xml += openTag(chartTag);
1708
+ if (chartType === "bar" || chartType === "bar3D") {
1709
+ xml += tag("c:barDir", { val: "bar" });
1710
+ xml += tag("c:grouping", { val: element.barGrouping ?? "clustered" });
1711
+ } else if (chartType === "col" || chartType === "col3D") {
1712
+ xml += tag("c:barDir", { val: "col" });
1713
+ xml += tag("c:grouping", { val: element.barGrouping ?? "clustered" });
1714
+ } else if (chartType === "line" || chartType === "line3D") {
1715
+ xml += tag("c:grouping", { val: element.lineGrouping ?? "standard" });
1716
+ } else if (chartType === "area" || chartType === "area3D") {
1717
+ xml += tag("c:grouping", { val: element.areaGrouping ?? "standard" });
1718
+ } else if (chartType === "scatter") {
1719
+ xml += tag("c:scatterStyle", { val: element.scatterStyle ?? "lineMarker" });
1720
+ } else if (chartType === "radar") {
1721
+ xml += tag("c:radarStyle", { val: element.radarStyle ?? "marker" });
1722
+ }
1723
+ xml += serializeSeriesData(data, chartType, showDataLabels);
1724
+ if (chartType === "pie" || chartType === "pie3D") {
1725
+ if (element.pieExplosion) {
1726
+ for (let i = 0; i < data.series[0]?.values.length; i++) {
1727
+ xml += tag(
1728
+ "c:dPt",
1729
+ void 0,
1730
+ tag("c:idx", { val: i }) + tag("c:explosion", { val: element.pieExplosion })
1731
+ );
1732
+ }
1733
+ }
1734
+ }
1735
+ if (chartType === "doughnut") {
1736
+ xml += tag("c:holeSize", { val: element.doughnutHoleSize ?? 50 });
1737
+ }
1738
+ if ((chartType === "bar" || chartType === "col" || chartType === "bar3D" || chartType === "col3D") && element.barGapWidth !== void 0) {
1739
+ xml += tag("c:gapWidth", { val: element.barGapWidth });
1740
+ }
1741
+ if ((chartType === "bar" || chartType === "col" || chartType === "bar3D" || chartType === "col3D") && element.barOverlap !== void 0) {
1742
+ xml += tag("c:overlap", { val: element.barOverlap });
1743
+ }
1744
+ if (hasAxes) {
1745
+ xml += tag("c:axId", { val: "1" });
1746
+ xml += tag("c:axId", { val: "2" });
1747
+ }
1748
+ xml += closeTag(chartTag);
1749
+ if (hasAxes) {
1750
+ xml += serializeAxis("c:catAx", "1", "2", "b", element.catAxis, showGridLines);
1751
+ xml += serializeAxis("c:valAx", "2", "1", "l", element.valAxis, showGridLines);
1752
+ if (element.secondaryValAxis) {
1753
+ xml += serializeAxis("c:valAx", "3", "1", "r", element.secondaryValAxis, false);
1754
+ }
1755
+ }
1756
+ if (element.plotArea) {
1757
+ let plotSpPr = "";
1758
+ if (element.plotArea.fill) {
1759
+ plotSpPr += serializeSolidFill(element.plotArea.fill);
1760
+ }
1761
+ if (plotSpPr) {
1762
+ xml += tag("c:spPr", void 0, plotSpPr);
1763
+ }
1764
+ }
1765
+ xml += closeTag("c:plotArea");
1766
+ if (showLegend) {
1767
+ xml += tag(
1768
+ "c:legend",
1769
+ void 0,
1770
+ tag("c:legendPos", { val: element.legendPosition ?? "b" }) + tag("c:overlay", { val: "0" })
1771
+ );
1772
+ }
1773
+ xml += tag("c:plotVisOnly", { val: "1" });
1774
+ xml += closeTag("c:chart");
1775
+ if (element.fill) {
1776
+ xml += tag("c:spPr", void 0, serializeSolidFill(element.fill));
1777
+ }
1778
+ xml += closeTag("c:chartSpace");
1779
+ return xml;
1780
+ }
1781
+ var chartSerializer = {
1782
+ type: "chart",
1783
+ serialize(element, ctx) {
1784
+ chartCounter++;
1785
+ const chartFileName = `chart${chartCounter}.xml`;
1786
+ const target = `../charts/${chartFileName}`;
1787
+ const chartXml = serializeChartXml(element);
1788
+ const encoder2 = new TextEncoder();
1789
+ const rId = ctx.addRelationship(CHART_REL_TYPE, target, encoder2.encode(chartXml));
1790
+ const p = element.placement;
1791
+ let xml = openTag("p:graphicFrame");
1792
+ xml += tag(
1793
+ "p:nvGraphicFramePr",
1794
+ void 0,
1795
+ tag("p:cNvPr", { id: "0", name: element.name ?? "Chart" }) + tag("p:cNvGraphicFramePr") + tag("p:nvPr")
1796
+ );
1797
+ xml += tag(
1798
+ "p:xfrm",
1799
+ void 0,
1800
+ tag("a:off", { x: inchesToEmu(p.x), y: inchesToEmu(p.y) }) + tag("a:ext", { cx: inchesToEmu(p.w), cy: inchesToEmu(p.h) })
1801
+ );
1802
+ xml += tag(
1803
+ "a:graphic",
1804
+ void 0,
1805
+ tag("a:graphicData", { uri: CHART_NS }, tag("c:chart", { "xmlns:c": CHART_NS, "r:id": rId }))
1806
+ );
1807
+ xml += closeTag("p:graphicFrame");
1808
+ return xml;
1809
+ }
1810
+ };
1811
+
1812
+ // src/ooxml/serializers/media.ts
1813
+ var import_node_fs3 = require("fs");
1814
+ var mediaCounter = 0;
1815
+ var MEDIA_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/video";
1816
+ var AUDIO_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/audio";
1817
+ function extFromContentType(ct) {
1818
+ const map = {
1819
+ "video/mp4": "mp4",
1820
+ "video/webm": "webm",
1821
+ "video/avi": "avi",
1822
+ "audio/mpeg": "mp3",
1823
+ "audio/mp3": "mp3",
1824
+ "audio/wav": "wav",
1825
+ "audio/ogg": "ogg"
1826
+ };
1827
+ return map[ct] ?? ct.split("/")[1] ?? "bin";
1828
+ }
1829
+ var mediaSerializer = {
1830
+ type: "media",
1831
+ serialize(element, ctx) {
1832
+ mediaCounter++;
1833
+ const ext = extFromContentType(element.contentType);
1834
+ const fileName = `media${mediaCounter}.${ext}`;
1835
+ const target = `../media/${fileName}`;
1836
+ const relType = element.mediaType === "audio" ? AUDIO_REL_TYPE : MEDIA_REL_TYPE;
1837
+ let data = element.data;
1838
+ if (!data && element.path) {
1839
+ data = (0, import_node_fs3.readFileSync)(element.path);
1840
+ }
1841
+ if (!data) throw new Error("Media element must have data or path");
1842
+ const rId = ctx.addRelationship(relType, target, data);
1843
+ const p = element.placement;
1844
+ if (element.mediaType === "audio") {
1845
+ let xml2 = openTag("p:sp");
1846
+ xml2 += tag(
1847
+ "p:nvSpPr",
1848
+ void 0,
1849
+ tag("p:cNvPr", { id: "0", name: element.name ?? "Audio" }) + tag("p:cNvSpPr") + tag("p:nvPr", void 0, tag("a:audioFile", { "r:link": rId }))
1850
+ );
1851
+ xml2 += openTag("p:spPr");
1852
+ xml2 += serializeTransform(p, element.transform);
1853
+ xml2 += tag("a:prstGeom", { prst: "rect" }, tag("a:avLst"));
1854
+ if (element.effects) xml2 += serializeEffects(element.effects);
1855
+ xml2 += closeTag("p:spPr");
1856
+ xml2 += closeTag("p:sp");
1857
+ return xml2;
1858
+ }
1859
+ let posterRId;
1860
+ if (element.poster) {
1861
+ let posterData = element.poster.data;
1862
+ if (!posterData && element.poster.path) {
1863
+ posterData = (0, import_node_fs3.readFileSync)(element.poster.path);
1864
+ }
1865
+ if (posterData) {
1866
+ const pCt = element.poster.contentType ?? "image/png";
1867
+ const pExt = pCt.split("/")[1] ?? "png";
1868
+ const posterFile = `poster${mediaCounter}.${pExt}`;
1869
+ posterRId = ctx.addRelationship(REL_TYPE.image, `../media/${posterFile}`, posterData);
1870
+ }
1871
+ }
1872
+ let xml = openTag("p:pic");
1873
+ xml += tag(
1874
+ "p:nvPicPr",
1875
+ void 0,
1876
+ tag("p:cNvPr", { id: "0", name: element.name ?? "Video" }) + tag("p:cNvPicPr", void 0, tag("a:picLocks", { noChangeAspect: "1" })) + tag("p:nvPr", void 0, tag("a:videoFile", { "r:link": rId }))
1877
+ );
1878
+ xml += tag(
1879
+ "p:blipFill",
1880
+ void 0,
1881
+ tag("a:blip", posterRId ? { "r:embed": posterRId } : void 0) + tag("a:stretch", void 0, tag("a:fillRect"))
1882
+ );
1883
+ xml += openTag("p:spPr");
1884
+ xml += serializeTransform(p, element.transform);
1885
+ xml += tag("a:prstGeom", { prst: "rect" }, tag("a:avLst"));
1886
+ if (element.effects) xml += serializeEffects(element.effects);
1887
+ xml += closeTag("p:spPr");
1888
+ xml += closeTag("p:pic");
1889
+ return xml;
1890
+ }
1891
+ };
1892
+
1893
+ // src/ooxml/serializers/group.ts
1894
+ function createGroupSerializer(registry) {
1895
+ return {
1896
+ type: "group",
1897
+ serialize(element, ctx) {
1898
+ const p = element.placement;
1899
+ let xml = openTag("p:grpSp");
1900
+ xml += tag(
1901
+ "p:nvGrpSpPr",
1902
+ void 0,
1903
+ tag("p:cNvPr", { id: "0", name: element.name ?? "" }) + tag("p:cNvGrpSpPr") + tag("p:nvPr")
1904
+ );
1905
+ xml += tag(
1906
+ "p:grpSpPr",
1907
+ void 0,
1908
+ tag(
1909
+ "a:xfrm",
1910
+ void 0,
1911
+ tag("a:off", { x: inchesToEmu(p.x), y: inchesToEmu(p.y) }) + tag("a:ext", { cx: inchesToEmu(p.w), cy: inchesToEmu(p.h) }) + tag("a:chOff", { x: inchesToEmu(p.x), y: inchesToEmu(p.y) }) + tag("a:chExt", { cx: inchesToEmu(p.w), cy: inchesToEmu(p.h) })
1912
+ )
1913
+ );
1914
+ for (const child of element.children) {
1915
+ const serializer = registry.getSerializer(child.type);
1916
+ if (serializer) {
1917
+ xml += serializer.serialize(child, ctx);
1918
+ }
1919
+ }
1920
+ xml += closeTag("p:grpSp");
1921
+ return xml;
1922
+ }
1923
+ };
1924
+ }
1925
+
1926
+ // src/ooxml/serializers/connector.ts
1927
+ var CONNECTOR_GEOM = {
1928
+ straight: "straightConnector1",
1929
+ elbow: "bentConnector3",
1930
+ curved: "curvedConnector3"
1931
+ };
1932
+ var connectorSerializer = {
1933
+ type: "connector",
1934
+ serialize(element, _ctx) {
1935
+ const p = element.placement;
1936
+ const prst = CONNECTOR_GEOM[element.connectorType] ?? "straightConnector1";
1937
+ let xml = openTag("p:cxnSp");
1938
+ xml += tag(
1939
+ "p:nvCxnSpPr",
1940
+ void 0,
1941
+ tag("p:cNvPr", { id: "0", name: element.name ?? "" }) + tag("p:cNvCxnSpPr") + tag("p:nvPr")
1942
+ );
1943
+ xml += openTag("p:spPr");
1944
+ xml += serializeTransform(p);
1945
+ xml += tag("a:prstGeom", { prst }, tag("a:avLst"));
1946
+ if (element.line) {
1947
+ xml += serializeLine(element.line);
1948
+ } else {
1949
+ let defaultLn = serializeSolidFill("000000");
1950
+ if (element.startArrow) {
1951
+ const hAttrs = {};
1952
+ if (element.startArrow.type) hAttrs.type = element.startArrow.type;
1953
+ if (element.startArrow.width) hAttrs.w = element.startArrow.width;
1954
+ if (element.startArrow.length) hAttrs.len = element.startArrow.length;
1955
+ defaultLn += tag("a:headEnd", hAttrs);
1956
+ }
1957
+ if (element.endArrow) {
1958
+ const tAttrs = {};
1959
+ if (element.endArrow.type) tAttrs.type = element.endArrow.type;
1960
+ if (element.endArrow.width) tAttrs.w = element.endArrow.width;
1961
+ if (element.endArrow.length) tAttrs.len = element.endArrow.length;
1962
+ defaultLn += tag("a:tailEnd", tAttrs);
1963
+ }
1964
+ xml += tag("a:ln", { w: "12700" }, defaultLn);
1965
+ }
1966
+ xml += closeTag("p:spPr");
1967
+ xml += closeTag("p:cxnSp");
1968
+ return xml;
1969
+ }
1970
+ };
1971
+
1972
+ // src/io/writer.ts
1973
+ var import_fflate = require("fflate");
1974
+
1975
+ // src/core/relationships.ts
1976
+ var RelationshipManager = class {
1977
+ counter = 0;
1978
+ rels = [];
1979
+ add(type, target, data, external) {
1980
+ this.counter++;
1981
+ const id = `rId${this.counter}`;
1982
+ this.rels.push({ id, type, target, data, external });
1983
+ return id;
1984
+ }
1985
+ getAll() {
1986
+ return this.rels;
1987
+ }
1988
+ toXml() {
1989
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
1990
+ xml += '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
1991
+ for (const rel of this.rels) {
1992
+ const attrs = {
1993
+ Id: rel.id,
1994
+ Type: rel.type,
1995
+ Target: rel.target
1996
+ };
1997
+ if (rel.external) {
1998
+ attrs.TargetMode = "External";
1999
+ }
2000
+ xml += tag("Relationship", attrs);
2001
+ }
2002
+ xml += "</Relationships>";
2003
+ return xml;
2004
+ }
2005
+ };
2006
+
2007
+ // src/ooxml/serializers/slide.ts
2008
+ function serializeTransition(t) {
2009
+ const attrs = {};
2010
+ if (t.durationMs !== void 0) {
2011
+ attrs.dur = t.durationMs;
2012
+ } else if (t.duration) {
2013
+ attrs.spd = t.duration <= 500 ? "fast" : t.duration <= 1500 ? "med" : "slow";
2014
+ }
2015
+ if (t.advanceOnClick === false) attrs.advClick = "0";
2016
+ if (t.advanceAfter !== void 0) {
2017
+ attrs.advTm = t.advanceAfter;
2018
+ }
2019
+ const type = t.type ?? "fade";
2020
+ let child = "";
2021
+ const transMap = {
2022
+ fade: "p:fade",
2023
+ push: "p:push",
2024
+ wipe: "p:wipe",
2025
+ split: "p:split",
2026
+ cover: "p:cover",
2027
+ uncover: "p:uncover",
2028
+ strips: "p:strips",
2029
+ blinds: "p:blinds",
2030
+ checker: "p:checker",
2031
+ dissolve: "p:dissolve",
2032
+ comb: "p:comb",
2033
+ newsflash: "p:newsflash",
2034
+ randomBars: "p:randomBar",
2035
+ circle: "p:circle",
2036
+ diamond: "p:diamond",
2037
+ plus: "p:plus",
2038
+ wedge: "p:wedge",
2039
+ wheel: "p:wheel",
2040
+ zoom: "p:zoom",
2041
+ cut: "p:cut",
2042
+ random: "p:random"
2043
+ };
2044
+ const elName = transMap[type];
2045
+ if (elName && type !== "none") {
2046
+ const dirAttrs = {};
2047
+ if (t.direction) dirAttrs.dir = t.direction;
2048
+ child = tag(elName, Object.keys(dirAttrs).length > 0 ? dirAttrs : void 0);
2049
+ }
2050
+ return tag("p:transition", attrs, child || void 0);
2051
+ }
2052
+ function serializeAnimations(animations) {
2053
+ let tnLstXml = "";
2054
+ for (let i = 0; i < animations.length; i++) {
2055
+ const anim = animations[i];
2056
+ const dur = anim.duration ?? 500;
2057
+ const delay = anim.delay ?? 0;
2058
+ const spTgtId = String(anim.elementIndex + 2);
2059
+ let nodeType;
2060
+ if (anim.trigger === "withPrevious") nodeType = "withEffect";
2061
+ else if (anim.trigger === "afterPrevious") nodeType = "afterEffect";
2062
+ else nodeType = "clickEffect";
2063
+ const cTnAttrs = {
2064
+ id: String((i + 1) * 10 + 1),
2065
+ dur: String(dur),
2066
+ fill: "hold"
2067
+ };
2068
+ if (anim.acceleration !== void 0) cTnAttrs.accel = Math.round(anim.acceleration * 1e3);
2069
+ if (anim.deceleration !== void 0) cTnAttrs.decel = Math.round(anim.deceleration * 1e3);
2070
+ const tgtEl = tag("p:tgtEl", void 0, tag("p:spTgt", { spid: spTgtId }));
2071
+ const stCond = tag("p:stCondLst", void 0, tag("p:cond", { delay: String(delay) }));
2072
+ let animEffect;
2073
+ if (anim.motionPath) {
2074
+ animEffect = tag(
2075
+ "p:animMotion",
2076
+ { origin: "layout", path: anim.motionPath, pathEditMode: "relative" },
2077
+ tag("p:cBhvr", void 0, tag("p:cTn", cTnAttrs, stCond) + tgtEl)
2078
+ );
2079
+ } else if (anim.effect === "appear") {
2080
+ const setCTn = { ...cTnAttrs, dur: "1" };
2081
+ animEffect = tag(
2082
+ "p:set",
2083
+ void 0,
2084
+ tag("p:cBhvr", void 0, tag("p:cTn", setCTn, stCond) + tgtEl) + tag("p:to", void 0, tag("p:strVal", { val: "visible" }))
2085
+ );
2086
+ } else {
2087
+ animEffect = tag(
2088
+ "p:animEffect",
2089
+ { transition: anim.category === "exit" ? "out" : "in", filter: anim.effect },
2090
+ tag("p:cBhvr", void 0, tag("p:cTn", cTnAttrs, stCond) + tgtEl)
2091
+ );
2092
+ }
2093
+ const childTnXml = tag(
2094
+ "p:par",
2095
+ void 0,
2096
+ tag(
2097
+ "p:cTn",
2098
+ { id: String((i + 1) * 10), fill: "hold", nodeType },
2099
+ tag("p:stCondLst", void 0, tag("p:cond", { delay: "0" })) + tag("p:childTnLst", void 0, animEffect)
2100
+ )
2101
+ );
2102
+ tnLstXml += childTnXml;
2103
+ }
2104
+ const mainSeq = tag(
2105
+ "p:seq",
2106
+ { concurrent: "1", nextAc: "seek" },
2107
+ tag(
2108
+ "p:cTn",
2109
+ { id: "1", dur: "indefinite", nodeType: "mainSeq" },
2110
+ tag("p:childTnLst", void 0, tnLstXml)
2111
+ ) + tag(
2112
+ "p:prevCondLst",
2113
+ void 0,
2114
+ tag("p:cond", { evt: "onPrev", delay: "0" }, tag("p:tgtEl", void 0, tag("p:sldTgt")))
2115
+ ) + tag(
2116
+ "p:nextCondLst",
2117
+ void 0,
2118
+ tag("p:cond", { evt: "onNext", delay: "0" }, tag("p:tgtEl", void 0, tag("p:sldTgt")))
2119
+ )
2120
+ );
2121
+ return tag("p:timing", void 0, tag("p:tnLst", void 0, mainSeq));
2122
+ }
2123
+ function serializeSlide(elements, slideIndex, theme, registry, _layoutRId, transition, animations, background) {
2124
+ const relManager = new RelationshipManager();
2125
+ const media = /* @__PURE__ */ new Map();
2126
+ const mediaRels = [];
2127
+ relManager.add(
2128
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout",
2129
+ "../slideLayouts/slideLayout1.xml"
2130
+ );
2131
+ const ctx = {
2132
+ addRelationship(type, target, data) {
2133
+ const rId = relManager.add(type, target);
2134
+ mediaRels.push({ type, target });
2135
+ if (data) {
2136
+ const fileName = target.split("/").pop() ?? "file";
2137
+ if (target.includes("/charts/")) {
2138
+ media.set(`ppt/charts/${fileName}`, data);
2139
+ } else {
2140
+ media.set(`ppt/media/${fileName}`, data);
2141
+ }
2142
+ }
2143
+ return rId;
2144
+ },
2145
+ theme,
2146
+ slideIndex
2147
+ };
2148
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2149
+ xml += openTag("p:sld", {
2150
+ "xmlns:a": NAMESPACE.a,
2151
+ "xmlns:r": NAMESPACE.r,
2152
+ "xmlns:p": NAMESPACE.p
2153
+ });
2154
+ xml += openTag("p:cSld");
2155
+ if (background) {
2156
+ let bgXml = "";
2157
+ if (background.color) {
2158
+ bgXml = tag("p:bgPr", void 0, serializeSolidFill(background.color) + tag("a:effectLst"));
2159
+ } else if (background.fill) {
2160
+ bgXml = tag("p:bgPr", void 0, serializeFill(background.fill, ctx) + tag("a:effectLst"));
2161
+ }
2162
+ if (bgXml) xml += tag("p:bg", void 0, bgXml);
2163
+ }
2164
+ xml += openTag("p:spTree");
2165
+ xml += tag(
2166
+ "p:nvGrpSpPr",
2167
+ void 0,
2168
+ tag("p:cNvPr", { id: "1", name: "" }) + tag("p:cNvGrpSpPr") + tag("p:nvPr")
2169
+ );
2170
+ xml += tag(
2171
+ "p:grpSpPr",
2172
+ void 0,
2173
+ tag(
2174
+ "a:xfrm",
2175
+ void 0,
2176
+ tag("a:off", { x: "0", y: "0" }) + tag("a:ext", { cx: "0", cy: "0" }) + tag("a:chOff", { x: "0", y: "0" }) + tag("a:chExt", { cx: "0", cy: "0" })
2177
+ )
2178
+ );
2179
+ for (const el of elements) {
2180
+ const serializer = registry.getSerializer(el.type);
2181
+ if (!serializer) {
2182
+ throw new Error(`No serializer registered for element type: ${el.type}`);
2183
+ }
2184
+ xml += serializer.serialize(el, ctx);
2185
+ }
2186
+ xml += closeTag("p:spTree");
2187
+ xml += closeTag("p:cSld");
2188
+ if (transition) {
2189
+ xml += serializeTransition(transition);
2190
+ }
2191
+ if (animations && animations.length > 0) {
2192
+ xml += serializeAnimations(animations);
2193
+ }
2194
+ xml += closeTag("p:sld");
2195
+ return {
2196
+ slideXml: xml,
2197
+ relsXml: relManager.toXml(),
2198
+ media,
2199
+ mediaRels
2200
+ };
2201
+ }
2202
+
2203
+ // src/ooxml/serializers/presentation.ts
2204
+ function serializePresentation(slideCount, slideRIds, slideSize) {
2205
+ const sldW = slideSize?.width ?? DEFAULT_SLIDE_WIDTH;
2206
+ const sldH = slideSize?.height ?? DEFAULT_SLIDE_HEIGHT;
2207
+ const sldType = slideSize?.type ?? "custom";
2208
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2209
+ xml += openTag("p:presentation", {
2210
+ "xmlns:a": NAMESPACE.a,
2211
+ "xmlns:r": NAMESPACE.r,
2212
+ "xmlns:p": NAMESPACE.p,
2213
+ saveSubsetFonts: "1"
2214
+ });
2215
+ xml += tag(
2216
+ "p:sldMasterIdLst",
2217
+ void 0,
2218
+ tag("p:sldMasterId", { id: "2147483648", "r:id": "rId1" })
2219
+ );
2220
+ xml += openTag("p:sldIdLst");
2221
+ for (let i = 0; i < slideCount; i++) {
2222
+ xml += tag("p:sldId", { id: String(256 + i), "r:id": slideRIds[i] });
2223
+ }
2224
+ xml += closeTag("p:sldIdLst");
2225
+ xml += tag("p:sldSz", {
2226
+ cx: inchesToEmu(sldW),
2227
+ cy: inchesToEmu(sldH),
2228
+ type: sldType
2229
+ });
2230
+ xml += tag("p:notesSz", {
2231
+ cx: inchesToEmu(sldW),
2232
+ cy: inchesToEmu(sldH)
2233
+ });
2234
+ xml += closeTag("p:presentation");
2235
+ return xml;
2236
+ }
2237
+
2238
+ // src/ooxml/serializers/theme.ts
2239
+ function serializeTheme(theme) {
2240
+ const c = theme.colors;
2241
+ const f = theme.fonts;
2242
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2243
+ xml += openTag("a:theme", {
2244
+ "xmlns:a": NAMESPACE.a,
2245
+ name: theme.name
2246
+ });
2247
+ xml += openTag("a:themeElements");
2248
+ xml += openTag("a:clrScheme", { name: theme.name });
2249
+ const colorEntries = [
2250
+ ["a:dk1", c.dk1],
2251
+ ["a:lt1", c.lt1],
2252
+ ["a:dk2", c.dk2],
2253
+ ["a:lt2", c.lt2],
2254
+ ["a:accent1", c.accent1],
2255
+ ["a:accent2", c.accent2],
2256
+ ["a:accent3", c.accent3],
2257
+ ["a:accent4", c.accent4],
2258
+ ["a:accent5", c.accent5],
2259
+ ["a:accent6", c.accent6],
2260
+ ["a:hlink", c.hlink],
2261
+ ["a:folHlink", c.folHlink]
2262
+ ];
2263
+ for (const [tagName, val] of colorEntries) {
2264
+ xml += tag(tagName, void 0, serializeColor(val));
2265
+ }
2266
+ xml += closeTag("a:clrScheme");
2267
+ xml += openTag("a:fontScheme", { name: theme.name });
2268
+ xml += tag(
2269
+ "a:majorFont",
2270
+ void 0,
2271
+ tag("a:latin", { typeface: f.majorFont }) + tag("a:ea", { typeface: f.majorEastAsian ?? "" }) + tag("a:cs", { typeface: f.majorComplexScript ?? "" })
2272
+ );
2273
+ xml += tag(
2274
+ "a:minorFont",
2275
+ void 0,
2276
+ tag("a:latin", { typeface: f.minorFont }) + tag("a:ea", { typeface: f.minorEastAsian ?? "" }) + tag("a:cs", { typeface: f.minorComplexScript ?? "" })
2277
+ );
2278
+ xml += closeTag("a:fontScheme");
2279
+ xml += openTag("a:fmtScheme", { name: theme.name });
2280
+ xml += tag(
2281
+ "a:fillStyleLst",
2282
+ void 0,
2283
+ tag("a:solidFill", void 0, tag("a:schemeClr", { val: "phClr" })) + tag("a:solidFill", void 0, tag("a:schemeClr", { val: "phClr" })) + tag("a:solidFill", void 0, tag("a:schemeClr", { val: "phClr" }))
2284
+ );
2285
+ xml += tag(
2286
+ "a:lnStyleLst",
2287
+ void 0,
2288
+ tag(
2289
+ "a:ln",
2290
+ { w: "6350", cap: "flat", cmpd: "sng", algn: "ctr" },
2291
+ tag("a:solidFill", void 0, tag("a:schemeClr", { val: "phClr" }))
2292
+ ) + tag(
2293
+ "a:ln",
2294
+ { w: "12700", cap: "flat", cmpd: "sng", algn: "ctr" },
2295
+ tag("a:solidFill", void 0, tag("a:schemeClr", { val: "phClr" }))
2296
+ ) + tag(
2297
+ "a:ln",
2298
+ { w: "19050", cap: "flat", cmpd: "sng", algn: "ctr" },
2299
+ tag("a:solidFill", void 0, tag("a:schemeClr", { val: "phClr" }))
2300
+ )
2301
+ );
2302
+ xml += tag(
2303
+ "a:effectStyleLst",
2304
+ void 0,
2305
+ tag("a:effectStyle", void 0, tag("a:effectLst")) + tag("a:effectStyle", void 0, tag("a:effectLst")) + tag("a:effectStyle", void 0, tag("a:effectLst"))
2306
+ );
2307
+ xml += tag(
2308
+ "a:bgFillStyleLst",
2309
+ void 0,
2310
+ tag("a:solidFill", void 0, tag("a:schemeClr", { val: "phClr" })) + tag("a:solidFill", void 0, tag("a:schemeClr", { val: "phClr" })) + tag("a:solidFill", void 0, tag("a:schemeClr", { val: "phClr" }))
2311
+ );
2312
+ xml += closeTag("a:fmtScheme");
2313
+ xml += closeTag("a:themeElements");
2314
+ xml += tag("a:objectDefaults");
2315
+ if (theme.extraColorSchemes && theme.extraColorSchemes.length > 0) {
2316
+ let extraXml = "";
2317
+ for (let i = 0; i < theme.extraColorSchemes.length; i++) {
2318
+ const ec = theme.extraColorSchemes[i];
2319
+ let schemeXml = "";
2320
+ const extraEntries = [
2321
+ ["a:dk1", ec.dk1],
2322
+ ["a:lt1", ec.lt1],
2323
+ ["a:dk2", ec.dk2],
2324
+ ["a:lt2", ec.lt2],
2325
+ ["a:accent1", ec.accent1],
2326
+ ["a:accent2", ec.accent2],
2327
+ ["a:accent3", ec.accent3],
2328
+ ["a:accent4", ec.accent4],
2329
+ ["a:accent5", ec.accent5],
2330
+ ["a:accent6", ec.accent6],
2331
+ ["a:hlink", ec.hlink],
2332
+ ["a:folHlink", ec.folHlink]
2333
+ ];
2334
+ for (const [tagName, val] of extraEntries) {
2335
+ schemeXml += tag(tagName, void 0, serializeColor(val));
2336
+ }
2337
+ extraXml += tag(
2338
+ "a:extraClrScheme",
2339
+ void 0,
2340
+ tag("a:clrScheme", { name: `Extra ${i + 1}` }, schemeXml)
2341
+ );
2342
+ }
2343
+ xml += tag("a:extraClrSchemeLst", void 0, extraXml);
2344
+ } else {
2345
+ xml += tag("a:extraClrSchemeLst");
2346
+ }
2347
+ xml += closeTag("a:theme");
2348
+ return xml;
2349
+ }
2350
+
2351
+ // src/ooxml/serializers/slide-master.ts
2352
+ function serializeSlideMaster(master, layoutCount) {
2353
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2354
+ xml += openTag("p:sldMaster", {
2355
+ "xmlns:a": NAMESPACE.a,
2356
+ "xmlns:r": NAMESPACE.r,
2357
+ "xmlns:p": NAMESPACE.p
2358
+ });
2359
+ xml += openTag("p:cSld");
2360
+ if (master.background) {
2361
+ xml += tag(
2362
+ "p:bg",
2363
+ void 0,
2364
+ tag("p:bgPr", void 0, serializeSolidFill(master.background) + tag("a:effectLst"))
2365
+ );
2366
+ } else {
2367
+ xml += tag(
2368
+ "p:bg",
2369
+ void 0,
2370
+ tag("p:bgRef", { idx: "1001" }, tag("a:schemeClr", { val: "bg1" }))
2371
+ );
2372
+ }
2373
+ xml += openTag("p:spTree");
2374
+ xml += tag(
2375
+ "p:nvGrpSpPr",
2376
+ void 0,
2377
+ tag("p:cNvPr", { id: "1", name: "" }) + tag("p:cNvGrpSpPr") + tag("p:nvPr")
2378
+ );
2379
+ xml += tag(
2380
+ "p:grpSpPr",
2381
+ void 0,
2382
+ tag(
2383
+ "a:xfrm",
2384
+ void 0,
2385
+ tag("a:off", { x: "0", y: "0" }) + tag("a:ext", { cx: "0", cy: "0" }) + tag("a:chOff", { x: "0", y: "0" }) + tag("a:chExt", { cx: "0", cy: "0" })
2386
+ )
2387
+ );
2388
+ xml += closeTag("p:spTree");
2389
+ xml += closeTag("p:cSld");
2390
+ xml += openTag("p:clrMap", {
2391
+ bg1: "lt1",
2392
+ tx1: "dk1",
2393
+ bg2: "lt2",
2394
+ tx2: "dk2",
2395
+ accent1: "accent1",
2396
+ accent2: "accent2",
2397
+ accent3: "accent3",
2398
+ accent4: "accent4",
2399
+ accent5: "accent5",
2400
+ accent6: "accent6",
2401
+ hlink: "hlink",
2402
+ folHlink: "folHlink"
2403
+ });
2404
+ xml += closeTag("p:clrMap");
2405
+ xml += openTag("p:sldLayoutIdLst");
2406
+ for (let i = 0; i < layoutCount; i++) {
2407
+ xml += tag("p:sldLayoutId", { id: String(2147483649 + i), "r:id": `rId${i + 1}` });
2408
+ }
2409
+ xml += closeTag("p:sldLayoutIdLst");
2410
+ xml += closeTag("p:sldMaster");
2411
+ return xml;
2412
+ }
2413
+
2414
+ // src/ooxml/serializers/slide-layout.ts
2415
+ function serializeSlideLayout(layout) {
2416
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2417
+ xml += openTag("p:sldLayout", {
2418
+ "xmlns:a": NAMESPACE.a,
2419
+ "xmlns:r": NAMESPACE.r,
2420
+ "xmlns:p": NAMESPACE.p,
2421
+ type: layout.type === "title" ? "title" : layout.type === "blank" ? "blank" : "obj",
2422
+ preserve: "1"
2423
+ });
2424
+ xml += openTag("p:cSld", { name: layout.name });
2425
+ xml += openTag("p:spTree");
2426
+ xml += tag(
2427
+ "p:nvGrpSpPr",
2428
+ void 0,
2429
+ tag("p:cNvPr", { id: "1", name: "" }) + tag("p:cNvGrpSpPr") + tag("p:nvPr")
2430
+ );
2431
+ xml += tag(
2432
+ "p:grpSpPr",
2433
+ void 0,
2434
+ tag(
2435
+ "a:xfrm",
2436
+ void 0,
2437
+ tag("a:off", { x: "0", y: "0" }) + tag("a:ext", { cx: "0", cy: "0" }) + tag("a:chOff", { x: "0", y: "0" }) + tag("a:chExt", { cx: "0", cy: "0" })
2438
+ )
2439
+ );
2440
+ let spId = 2;
2441
+ for (const ph of layout.placeholders) {
2442
+ const p = ph.placement;
2443
+ xml += openTag("p:sp");
2444
+ xml += tag(
2445
+ "p:nvSpPr",
2446
+ void 0,
2447
+ tag("p:cNvPr", { id: String(spId), name: `${ph.type} ${ph.idx ?? spId}` }) + tag("p:cNvSpPr", void 0, tag("a:spLocks", { noGrp: "1" })) + tag(
2448
+ "p:nvPr",
2449
+ void 0,
2450
+ tag("p:ph", {
2451
+ type: ph.type,
2452
+ ...ph.idx !== void 0 ? { idx: String(ph.idx) } : {}
2453
+ })
2454
+ )
2455
+ );
2456
+ xml += tag(
2457
+ "p:spPr",
2458
+ void 0,
2459
+ tag(
2460
+ "a:xfrm",
2461
+ void 0,
2462
+ tag("a:off", { x: inchesToEmu(p.x), y: inchesToEmu(p.y) }) + tag("a:ext", { cx: inchesToEmu(p.w), cy: inchesToEmu(p.h) })
2463
+ )
2464
+ );
2465
+ xml += closeTag("p:sp");
2466
+ spId++;
2467
+ }
2468
+ xml += closeTag("p:spTree");
2469
+ xml += closeTag("p:cSld");
2470
+ xml += closeTag("p:sldLayout");
2471
+ return xml;
2472
+ }
2473
+
2474
+ // src/core/content-types.ts
2475
+ var ContentTypesBuilder = class {
2476
+ defaults = /* @__PURE__ */ new Map();
2477
+ overrides = /* @__PURE__ */ new Map();
2478
+ addDefault(extension, contentType) {
2479
+ this.defaults.set(extension, contentType);
2480
+ }
2481
+ addOverride(partName, contentType) {
2482
+ this.overrides.set(partName, contentType);
2483
+ }
2484
+ toXml() {
2485
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2486
+ xml += '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">';
2487
+ for (const [ext, ct] of this.defaults) {
2488
+ xml += tag("Default", { Extension: ext, ContentType: ct });
2489
+ }
2490
+ for (const [pn, ct] of this.overrides) {
2491
+ xml += tag("Override", { PartName: pn, ContentType: ct });
2492
+ }
2493
+ xml += "</Types>";
2494
+ return xml;
2495
+ }
2496
+ };
2497
+
2498
+ // src/ooxml/serializers/content-types.ts
2499
+ function buildContentTypes(opts) {
2500
+ const { slideCount, imageExtensions, chartCount, notesCount, layoutCount } = opts;
2501
+ const ct = new ContentTypesBuilder();
2502
+ ct.addDefault("rels", CONTENT_TYPE.rels);
2503
+ ct.addDefault("xml", "application/xml");
2504
+ for (const ext of imageExtensions) {
2505
+ const map = {
2506
+ png: CONTENT_TYPE.png,
2507
+ jpeg: CONTENT_TYPE.jpeg,
2508
+ jpg: CONTENT_TYPE.jpeg,
2509
+ gif: CONTENT_TYPE.gif,
2510
+ svg: CONTENT_TYPE.svg
2511
+ };
2512
+ if (map[ext]) ct.addDefault(ext, map[ext]);
2513
+ }
2514
+ ct.addOverride("/ppt/presentation.xml", CONTENT_TYPE.presentation);
2515
+ ct.addOverride("/ppt/theme/theme1.xml", CONTENT_TYPE.theme);
2516
+ ct.addOverride("/ppt/slideMasters/slideMaster1.xml", CONTENT_TYPE.slideMaster);
2517
+ ct.addOverride("/ppt/presProps.xml", CONTENT_TYPE.presProps);
2518
+ ct.addOverride("/ppt/viewProps.xml", CONTENT_TYPE.viewProps);
2519
+ for (let i = 1; i <= layoutCount; i++) {
2520
+ ct.addOverride(`/ppt/slideLayouts/slideLayout${i}.xml`, CONTENT_TYPE.slideLayout);
2521
+ }
2522
+ for (let i = 1; i <= slideCount; i++) {
2523
+ ct.addOverride(`/ppt/slides/slide${i}.xml`, CONTENT_TYPE.slide);
2524
+ }
2525
+ for (let i = 1; i <= chartCount; i++) {
2526
+ ct.addOverride(
2527
+ `/ppt/charts/chart${i}.xml`,
2528
+ "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
2529
+ );
2530
+ }
2531
+ for (let i = 1; i <= notesCount; i++) {
2532
+ ct.addOverride(`/ppt/notesSlides/notesSlide${i}.xml`, CONTENT_TYPE.notesSlide);
2533
+ }
2534
+ if (opts.hasNotesMaster) {
2535
+ ct.addOverride("/ppt/notesMasters/notesMaster1.xml", CONTENT_TYPE.notesMaster);
2536
+ }
2537
+ if (opts.hasHandoutMaster) {
2538
+ ct.addOverride("/ppt/handoutMasters/handoutMaster1.xml", CONTENT_TYPE.handoutMaster);
2539
+ }
2540
+ if (opts.hasCommentAuthors) {
2541
+ ct.addOverride("/ppt/commentAuthors.xml", CONTENT_TYPE.commentAuthors);
2542
+ }
2543
+ const commentCount = opts.commentCount ?? 0;
2544
+ for (let i = 1; i <= commentCount; i++) {
2545
+ ct.addOverride(`/ppt/comments/comment${i}.xml`, CONTENT_TYPE.comments);
2546
+ }
2547
+ const tagCount = opts.tagCount ?? 0;
2548
+ for (let i = 1; i <= tagCount; i++) {
2549
+ ct.addOverride(`/ppt/tags/tag${i}.xml`, CONTENT_TYPE.tags);
2550
+ }
2551
+ const syncDataCount = opts.syncDataCount ?? 0;
2552
+ for (let i = 1; i <= syncDataCount; i++) {
2553
+ ct.addOverride(`/ppt/slideUpdateInfo/slideUpdateInfo${i}.xml`, CONTENT_TYPE.slideUpdateInfo);
2554
+ }
2555
+ return ct.toXml();
2556
+ }
2557
+
2558
+ // src/ooxml/serializers/notes.ts
2559
+ function serializeNotesSlide(notes) {
2560
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2561
+ xml += openTag("p:notes", {
2562
+ "xmlns:a": NAMESPACE.a,
2563
+ "xmlns:r": NAMESPACE.r,
2564
+ "xmlns:p": NAMESPACE.p
2565
+ });
2566
+ xml += openTag("p:cSld");
2567
+ xml += openTag("p:spTree");
2568
+ xml += tag(
2569
+ "p:nvGrpSpPr",
2570
+ void 0,
2571
+ tag("p:cNvPr", { id: "1", name: "" }) + tag("p:cNvGrpSpPr") + tag("p:nvPr")
2572
+ );
2573
+ xml += tag(
2574
+ "p:grpSpPr",
2575
+ void 0,
2576
+ tag(
2577
+ "a:xfrm",
2578
+ void 0,
2579
+ tag("a:off", { x: "0", y: "0" }) + tag("a:ext", { cx: "0", cy: "0" }) + tag("a:chOff", { x: "0", y: "0" }) + tag("a:chExt", { cx: "0", cy: "0" })
2580
+ )
2581
+ );
2582
+ xml += openTag("p:sp");
2583
+ xml += tag(
2584
+ "p:nvSpPr",
2585
+ void 0,
2586
+ tag("p:cNvPr", { id: "2", name: "Slide Image Placeholder" }) + tag(
2587
+ "p:cNvSpPr",
2588
+ void 0,
2589
+ tag("a:spLocks", { noGrp: "1", noRot: "1", noChangeAspect: "1" })
2590
+ ) + tag("p:nvPr", void 0, tag("p:ph", { type: "sldImg" }))
2591
+ );
2592
+ xml += tag("p:spPr");
2593
+ xml += closeTag("p:sp");
2594
+ xml += openTag("p:sp");
2595
+ xml += tag(
2596
+ "p:nvSpPr",
2597
+ void 0,
2598
+ tag("p:cNvPr", { id: "3", name: "Notes Placeholder" }) + tag("p:cNvSpPr", void 0, tag("a:spLocks", { noGrp: "1" })) + tag("p:nvPr", void 0, tag("p:ph", { type: "body", idx: "1" }))
2599
+ );
2600
+ xml += tag("p:spPr");
2601
+ xml += openTag("p:txBody");
2602
+ xml += tag("a:bodyPr");
2603
+ xml += tag("a:lstStyle");
2604
+ const lines = notes.split("\n");
2605
+ for (const line of lines) {
2606
+ xml += tag(
2607
+ "a:p",
2608
+ void 0,
2609
+ tag(
2610
+ "a:r",
2611
+ void 0,
2612
+ tag("a:rPr", { lang: "en-US" }) + tag("a:t", void 0, escapeXml(line))
2613
+ )
2614
+ );
2615
+ }
2616
+ xml += closeTag("p:txBody");
2617
+ xml += closeTag("p:sp");
2618
+ xml += closeTag("p:spTree");
2619
+ xml += closeTag("p:cSld");
2620
+ xml += closeTag("p:notes");
2621
+ return xml;
2622
+ }
2623
+
2624
+ // src/ooxml/serializers/pres-props.ts
2625
+ function serializePresProps(props) {
2626
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2627
+ xml += openTag("p:presentationPr", {
2628
+ "xmlns:a": NAMESPACE.a,
2629
+ "xmlns:r": NAMESPACE.r,
2630
+ "xmlns:p": NAMESPACE.p
2631
+ });
2632
+ if (props?.slideShow) {
2633
+ const s = props.slideShow;
2634
+ const attrs = {};
2635
+ if (s.loop != null) attrs.loop = s.loop ? "1" : "0";
2636
+ if (s.showNarration != null) attrs.showNarration = s.showNarration ? "1" : "0";
2637
+ if (s.showAnimation != null) attrs.showAnimation = s.showAnimation ? "1" : "0";
2638
+ if (s.useTimings != null) attrs.useTimings = s.useTimings ? "1" : "0";
2639
+ xml += tag("p:showPr", attrs);
2640
+ }
2641
+ if (props?.print) {
2642
+ const p = props.print;
2643
+ const attrs = {};
2644
+ if (p.frameSlides != null) attrs.frameSlides = p.frameSlides ? "1" : "0";
2645
+ if (p.hiddenSlides != null) attrs.prnWhat = p.hiddenSlides ? "slides" : "slides";
2646
+ xml += tag("p:prnPr", attrs);
2647
+ }
2648
+ xml += closeTag("p:presentationPr");
2649
+ return xml;
2650
+ }
2651
+
2652
+ // src/ooxml/serializers/view-props.ts
2653
+ function serializeViewProps(props) {
2654
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2655
+ const attrs = {
2656
+ "xmlns:a": NAMESPACE.a,
2657
+ "xmlns:r": NAMESPACE.r,
2658
+ "xmlns:p": NAMESPACE.p
2659
+ };
2660
+ if (props?.lastView) {
2661
+ attrs.lastView = props.lastView;
2662
+ }
2663
+ xml += openTag("p:viewPr", attrs);
2664
+ xml += closeTag("p:viewPr");
2665
+ return xml;
2666
+ }
2667
+
2668
+ // src/ooxml/serializers/notes-master.ts
2669
+ function serializeNotesMaster() {
2670
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2671
+ xml += openTag("p:notesMaster", {
2672
+ "xmlns:a": NAMESPACE.a,
2673
+ "xmlns:r": NAMESPACE.r,
2674
+ "xmlns:p": NAMESPACE.p
2675
+ });
2676
+ xml += openTag("p:cSld");
2677
+ xml += openTag("p:spTree");
2678
+ xml += tag(
2679
+ "p:nvGrpSpPr",
2680
+ void 0,
2681
+ tag("p:cNvPr", { id: "1", name: "" }) + tag("p:cNvGrpSpPr") + tag("p:nvPr")
2682
+ );
2683
+ xml += tag(
2684
+ "p:grpSpPr",
2685
+ void 0,
2686
+ tag(
2687
+ "a:xfrm",
2688
+ void 0,
2689
+ tag("a:off", { x: "0", y: "0" }) + tag("a:ext", { cx: "0", cy: "0" }) + tag("a:chOff", { x: "0", y: "0" }) + tag("a:chExt", { cx: "0", cy: "0" })
2690
+ )
2691
+ );
2692
+ xml += closeTag("p:spTree");
2693
+ xml += closeTag("p:cSld");
2694
+ xml += tag("p:clrMap", {
2695
+ bg1: "lt1",
2696
+ tx1: "dk1",
2697
+ bg2: "lt2",
2698
+ tx2: "dk2",
2699
+ accent1: "accent1",
2700
+ accent2: "accent2",
2701
+ accent3: "accent3",
2702
+ accent4: "accent4",
2703
+ accent5: "accent5",
2704
+ accent6: "accent6",
2705
+ hlink: "hlink",
2706
+ folHlink: "folHlink"
2707
+ });
2708
+ xml += closeTag("p:notesMaster");
2709
+ return xml;
2710
+ }
2711
+
2712
+ // src/ooxml/serializers/comment-authors.ts
2713
+ function serializeCommentAuthors(authors) {
2714
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2715
+ xml += openTag("p:cmAuthorLst", {
2716
+ "xmlns:a": NAMESPACE.a,
2717
+ "xmlns:r": NAMESPACE.r,
2718
+ "xmlns:p": NAMESPACE.p
2719
+ });
2720
+ for (const author of authors) {
2721
+ xml += tag("p:cmAuthor", {
2722
+ id: String(author.id),
2723
+ name: author.name,
2724
+ initials: author.initials,
2725
+ lastIdx: String(author.lastIdx),
2726
+ clrIdx: String(author.clrIdx)
2727
+ });
2728
+ }
2729
+ xml += closeTag("p:cmAuthorLst");
2730
+ return xml;
2731
+ }
2732
+
2733
+ // src/ooxml/serializers/comments.ts
2734
+ function serializeComments(comments) {
2735
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2736
+ xml += openTag("p:cmLst", {
2737
+ "xmlns:a": NAMESPACE.a,
2738
+ "xmlns:r": NAMESPACE.r,
2739
+ "xmlns:p": NAMESPACE.p
2740
+ });
2741
+ for (const comment of comments) {
2742
+ const attrs = {
2743
+ authorId: String(comment.authorId),
2744
+ idx: String(comment.index ?? 1)
2745
+ };
2746
+ if (comment.date) {
2747
+ attrs.dt = comment.date;
2748
+ }
2749
+ let body = "";
2750
+ if (comment.position) {
2751
+ body += tag("p:pos", {
2752
+ x: String(inchesToEmu(comment.position.x)),
2753
+ y: String(inchesToEmu(comment.position.y))
2754
+ });
2755
+ } else {
2756
+ body += tag("p:pos", { x: "0", y: "0" });
2757
+ }
2758
+ body += tag("p:text", void 0, comment.text);
2759
+ xml += tag("p:cm", attrs, body);
2760
+ }
2761
+ xml += closeTag("p:cmLst");
2762
+ return xml;
2763
+ }
2764
+
2765
+ // src/ooxml/serializers/handout-master.ts
2766
+ function serializeHandoutMaster() {
2767
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2768
+ xml += openTag("p:handoutMaster", {
2769
+ "xmlns:a": NAMESPACE.a,
2770
+ "xmlns:r": NAMESPACE.r,
2771
+ "xmlns:p": NAMESPACE.p
2772
+ });
2773
+ xml += openTag("p:cSld");
2774
+ xml += openTag("p:spTree");
2775
+ xml += tag(
2776
+ "p:nvGrpSpPr",
2777
+ void 0,
2778
+ tag("p:cNvPr", { id: "1", name: "" }) + tag("p:cNvGrpSpPr") + tag("p:nvPr")
2779
+ );
2780
+ xml += tag(
2781
+ "p:grpSpPr",
2782
+ void 0,
2783
+ tag(
2784
+ "a:xfrm",
2785
+ void 0,
2786
+ tag("a:off", { x: "0", y: "0" }) + tag("a:ext", { cx: "0", cy: "0" }) + tag("a:chOff", { x: "0", y: "0" }) + tag("a:chExt", { cx: "0", cy: "0" })
2787
+ )
2788
+ );
2789
+ xml += closeTag("p:spTree");
2790
+ xml += closeTag("p:cSld");
2791
+ xml += tag("p:clrMap", {
2792
+ bg1: "lt1",
2793
+ tx1: "dk1",
2794
+ bg2: "lt2",
2795
+ tx2: "dk2",
2796
+ accent1: "accent1",
2797
+ accent2: "accent2",
2798
+ accent3: "accent3",
2799
+ accent4: "accent4",
2800
+ accent5: "accent5",
2801
+ accent6: "accent6",
2802
+ hlink: "hlink",
2803
+ folHlink: "folHlink"
2804
+ });
2805
+ xml += closeTag("p:handoutMaster");
2806
+ return xml;
2807
+ }
2808
+
2809
+ // src/ooxml/serializers/tags.ts
2810
+ function serializeTags(tags) {
2811
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2812
+ xml += openTag("p:tagLst", {
2813
+ "xmlns:a": NAMESPACE.a,
2814
+ "xmlns:r": NAMESPACE.r,
2815
+ "xmlns:p": NAMESPACE.p
2816
+ });
2817
+ for (const t of tags) {
2818
+ xml += tag("p:tag", { name: t.name, val: t.value });
2819
+ }
2820
+ xml += closeTag("p:tagLst");
2821
+ return xml;
2822
+ }
2823
+
2824
+ // src/ooxml/serializers/slide-update-info.ts
2825
+ function serializeSlideUpdateInfo(data) {
2826
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2827
+ xml += openTag("p:sldSyncPr", {
2828
+ "xmlns:a": NAMESPACE.a,
2829
+ "xmlns:r": NAMESPACE.r,
2830
+ "xmlns:p": NAMESPACE.p,
2831
+ serverSldId: data.serverSldId,
2832
+ serverSldModifiedTime: data.serverSldModifiedTime,
2833
+ clientInsertedTime: data.clientInsertedTime
2834
+ });
2835
+ xml += closeTag("p:sldSyncPr");
2836
+ return xml;
2837
+ }
2838
+
2839
+ // src/ooxml/serializers/core-props.ts
2840
+ function serializeCoreProperties(props) {
2841
+ let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
2842
+ xml += openTag("cp:coreProperties", {
2843
+ "xmlns:cp": "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
2844
+ "xmlns:dc": "http://purl.org/dc/elements/1.1/",
2845
+ "xmlns:dcterms": "http://purl.org/dc/terms/",
2846
+ "xmlns:dcmitype": "http://purl.org/dc/dcmitype/",
2847
+ "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance"
2848
+ });
2849
+ if (props.title) xml += tag("dc:title", void 0, props.title);
2850
+ if (props.creator) xml += tag("dc:creator", void 0, props.creator);
2851
+ if (props.subject) xml += tag("dc:subject", void 0, props.subject);
2852
+ if (props.description) xml += tag("dc:description", void 0, props.description);
2853
+ if (props.keywords) xml += tag("cp:keywords", void 0, props.keywords);
2854
+ if (props.category) xml += tag("cp:category", void 0, props.category);
2855
+ xml += closeTag("cp:coreProperties");
2856
+ return xml;
2857
+ }
2858
+
2859
+ // src/io/writer.ts
2860
+ var encoder = new TextEncoder();
2861
+ function writePresentation(input) {
2862
+ const { slides, theme, registry, masters, layouts } = input;
2863
+ const files = {};
2864
+ const allMedia = /* @__PURE__ */ new Map();
2865
+ const imageExtensions = /* @__PURE__ */ new Set();
2866
+ const presRels = new RelationshipManager();
2867
+ presRels.add(REL_TYPE.slideMaster, "slideMasters/slideMaster1.xml");
2868
+ presRels.add(REL_TYPE.theme, "theme/theme1.xml");
2869
+ const slideRIds = [];
2870
+ for (let i = 0; i < slides.length; i++) {
2871
+ const rId = presRels.add(REL_TYPE.slide, `slides/slide${i + 1}.xml`);
2872
+ slideRIds.push(rId);
2873
+ }
2874
+ presRels.add(REL_TYPE.presProps, "presProps.xml");
2875
+ files["ppt/presProps.xml"] = encoder.encode(serializePresProps(input.presProps));
2876
+ presRels.add(REL_TYPE.viewProps, "viewProps.xml");
2877
+ files["ppt/viewProps.xml"] = encoder.encode(serializeViewProps(input.viewProps));
2878
+ let chartCount = 0;
2879
+ let notesCount = 0;
2880
+ let commentCount = 0;
2881
+ let tagCount = 0;
2882
+ const hasNotes = slides.some((s) => s.notes);
2883
+ const hasComments = input.commentAuthors && input.commentAuthors.length > 0 && slides.some((s) => s.comments?.length);
2884
+ if (hasNotes) {
2885
+ presRels.add(REL_TYPE.notesMaster, "notesMasters/notesMaster1.xml");
2886
+ files["ppt/notesMasters/notesMaster1.xml"] = encoder.encode(serializeNotesMaster());
2887
+ const notesMasterRels = new RelationshipManager();
2888
+ notesMasterRels.add(REL_TYPE.theme, "../theme/theme1.xml");
2889
+ files["ppt/notesMasters/_rels/notesMaster1.xml.rels"] = encoder.encode(notesMasterRels.toXml());
2890
+ }
2891
+ if (input.handoutMaster) {
2892
+ presRels.add(REL_TYPE.handoutMaster, "handoutMasters/handoutMaster1.xml");
2893
+ files["ppt/handoutMasters/handoutMaster1.xml"] = encoder.encode(serializeHandoutMaster());
2894
+ const handoutMasterRels = new RelationshipManager();
2895
+ handoutMasterRels.add(REL_TYPE.theme, "../theme/theme1.xml");
2896
+ files["ppt/handoutMasters/_rels/handoutMaster1.xml.rels"] = encoder.encode(
2897
+ handoutMasterRels.toXml()
2898
+ );
2899
+ }
2900
+ if (hasComments && input.commentAuthors) {
2901
+ presRels.add(REL_TYPE.commentAuthors, "commentAuthors.xml");
2902
+ files["ppt/commentAuthors.xml"] = encoder.encode(serializeCommentAuthors(input.commentAuthors));
2903
+ }
2904
+ if (input.tags && input.tags.length > 0) {
2905
+ tagCount++;
2906
+ presRels.add(REL_TYPE.tags, `tags/tag${tagCount}.xml`);
2907
+ files[`ppt/tags/tag${tagCount}.xml`] = encoder.encode(serializeTags(input.tags));
2908
+ }
2909
+ if (input.htmlPublishUri) {
2910
+ presRels.add(REL_TYPE.htmlPubSaveAs, input.htmlPublishUri, void 0, true);
2911
+ }
2912
+ for (let i = 0; i < slides.length; i++) {
2913
+ const layoutIdx = slides[i].layoutIndex ?? 0;
2914
+ const result = serializeSlide(
2915
+ slides[i].elements,
2916
+ i,
2917
+ theme,
2918
+ registry,
2919
+ `rId1`,
2920
+ slides[i].transition,
2921
+ slides[i].animations,
2922
+ slides[i].background
2923
+ );
2924
+ files[`ppt/slides/slide${i + 1}.xml`] = encoder.encode(result.slideXml);
2925
+ for (const [path, data] of result.media) {
2926
+ allMedia.set(path, data);
2927
+ if (path.startsWith("ppt/charts/")) {
2928
+ chartCount++;
2929
+ } else {
2930
+ const ext = path.split(".").pop() ?? "bin";
2931
+ imageExtensions.add(ext);
2932
+ }
2933
+ }
2934
+ const slideRelManager = new RelationshipManager();
2935
+ slideRelManager.add(REL_TYPE.slideLayout, `../slideLayouts/slideLayout${layoutIdx + 1}.xml`);
2936
+ if (slides[i].notes) {
2937
+ notesCount++;
2938
+ const notesRel = new RelationshipManager();
2939
+ notesRel.add(REL_TYPE.slide, `../slides/slide${i + 1}.xml`);
2940
+ notesRel.add(REL_TYPE.notesMaster, "../notesMasters/notesMaster1.xml");
2941
+ const notesXml = serializeNotesSlide(slides[i].notes ?? "");
2942
+ files[`ppt/notesSlides/notesSlide${notesCount}.xml`] = encoder.encode(notesXml);
2943
+ files[`ppt/notesSlides/_rels/notesSlide${notesCount}.xml.rels`] = encoder.encode(
2944
+ notesRel.toXml()
2945
+ );
2946
+ slideRelManager.add(REL_TYPE.notesSlide, `../notesSlides/notesSlide${notesCount}.xml`);
2947
+ }
2948
+ const slideComments = slides[i].comments;
2949
+ if (slideComments && slideComments.length > 0) {
2950
+ commentCount++;
2951
+ slideRelManager.add(REL_TYPE.comments, `../comments/comment${commentCount}.xml`);
2952
+ files[`ppt/comments/comment${commentCount}.xml`] = encoder.encode(
2953
+ serializeComments(slideComments)
2954
+ );
2955
+ }
2956
+ const slideTags = slides[i].tags;
2957
+ if (slideTags && slideTags.length > 0) {
2958
+ tagCount++;
2959
+ slideRelManager.add(REL_TYPE.tags, `../tags/tag${tagCount}.xml`);
2960
+ files[`ppt/tags/tag${tagCount}.xml`] = encoder.encode(serializeTags(slideTags));
2961
+ }
2962
+ const syncData = slides[i].syncData;
2963
+ if (syncData) {
2964
+ slideRelManager.add(
2965
+ REL_TYPE.slideUpdateInfo,
2966
+ `../slideUpdateInfo/slideUpdateInfo${i + 1}.xml`
2967
+ );
2968
+ files[`ppt/slideUpdateInfo/slideUpdateInfo${i + 1}.xml`] = encoder.encode(
2969
+ serializeSlideUpdateInfo(syncData)
2970
+ );
2971
+ if (input.slideUpdateUrl) {
2972
+ const syncRels = new RelationshipManager();
2973
+ syncRels.add(REL_TYPE.slideUpdateUrl, input.slideUpdateUrl, void 0, true);
2974
+ files[`ppt/slideUpdateInfo/_rels/slideUpdateInfo${i + 1}.xml.rels`] = encoder.encode(
2975
+ syncRels.toXml()
2976
+ );
2977
+ }
2978
+ }
2979
+ for (const rel of result.mediaRels) {
2980
+ slideRelManager.add(rel.type, rel.target);
2981
+ }
2982
+ files[`ppt/slides/_rels/slide${i + 1}.xml.rels`] = encoder.encode(slideRelManager.toXml());
2983
+ }
2984
+ files["ppt/theme/theme1.xml"] = encoder.encode(serializeTheme(theme));
2985
+ for (let i = 0; i < masters.length; i++) {
2986
+ files[`ppt/slideMasters/slideMaster${i + 1}.xml`] = encoder.encode(
2987
+ serializeSlideMaster(masters[i], layouts.length)
2988
+ );
2989
+ const masterRels = new RelationshipManager();
2990
+ for (let j = 0; j < layouts.length; j++) {
2991
+ masterRels.add(REL_TYPE.slideLayout, `../slideLayouts/slideLayout${j + 1}.xml`);
2992
+ }
2993
+ masterRels.add(REL_TYPE.theme, "../theme/theme1.xml");
2994
+ files[`ppt/slideMasters/_rels/slideMaster${i + 1}.xml.rels`] = encoder.encode(
2995
+ masterRels.toXml()
2996
+ );
2997
+ }
2998
+ for (let i = 0; i < layouts.length; i++) {
2999
+ files[`ppt/slideLayouts/slideLayout${i + 1}.xml`] = encoder.encode(
3000
+ serializeSlideLayout(layouts[i])
3001
+ );
3002
+ const layoutRels = new RelationshipManager();
3003
+ layoutRels.add(REL_TYPE.slideMaster, "../slideMasters/slideMaster1.xml");
3004
+ files[`ppt/slideLayouts/_rels/slideLayout${i + 1}.xml.rels`] = encoder.encode(
3005
+ layoutRels.toXml()
3006
+ );
3007
+ }
3008
+ files["[Content_Types].xml"] = encoder.encode(
3009
+ buildContentTypes({
3010
+ slideCount: slides.length,
3011
+ imageExtensions,
3012
+ chartCount,
3013
+ notesCount,
3014
+ layoutCount: layouts.length,
3015
+ hasNotesMaster: hasNotes,
3016
+ hasHandoutMaster: !!input.handoutMaster,
3017
+ commentCount,
3018
+ hasCommentAuthors: !!hasComments,
3019
+ tagCount,
3020
+ syncDataCount: slides.filter((s) => s.syncData).length
3021
+ })
3022
+ );
3023
+ files["ppt/presentation.xml"] = encoder.encode(
3024
+ serializePresentation(slides.length, slideRIds, input.slideSize)
3025
+ );
3026
+ files["ppt/_rels/presentation.xml.rels"] = encoder.encode(presRels.toXml());
3027
+ if (input.coreProperties) {
3028
+ files["docProps/core.xml"] = encoder.encode(serializeCoreProperties(input.coreProperties));
3029
+ }
3030
+ const rootRels = new RelationshipManager();
3031
+ rootRels.add(REL_TYPE.officeDocument, "ppt/presentation.xml");
3032
+ if (input.coreProperties) {
3033
+ rootRels.add(
3034
+ "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
3035
+ "docProps/core.xml"
3036
+ );
3037
+ }
3038
+ files["_rels/.rels"] = encoder.encode(rootRels.toXml());
3039
+ for (const [path, data] of allMedia) {
3040
+ files[path] = data;
3041
+ }
3042
+ return (0, import_fflate.zipSync)(files);
3043
+ }
3044
+
3045
+ // src/elements/base.ts
3046
+ var BaseElement = class {
3047
+ /** Position and size of the element on the slide. */
3048
+ placement;
3049
+ /** Optional human-readable name for the element. */
3050
+ name;
3051
+ constructor(placement, name) {
3052
+ this.placement = placement;
3053
+ this.name = name;
3054
+ }
3055
+ };
3056
+
3057
+ // src/elements/text.ts
3058
+ var TextElement = class _TextElement extends BaseElement {
3059
+ /** Element type discriminator. */
3060
+ type = "text";
3061
+ /** Paragraphs that make up the text content. */
3062
+ paragraphs;
3063
+ /** Options controlling the text body (margins, wrapping, auto-fit, etc.). */
3064
+ bodyOptions;
3065
+ /** Background fill for the text box. */
3066
+ fill;
3067
+ /** Outline/border properties for the text box. */
3068
+ line;
3069
+ /** Visual effects (shadow, glow, etc.). */
3070
+ effects;
3071
+ /** 2-D transform (rotation, flip). */
3072
+ transform;
3073
+ /** 3D scene (camera + lighting). */
3074
+ scene3d;
3075
+ /** 3D shape (extrusion, bevels, material). */
3076
+ shape3d;
3077
+ constructor(paragraphs, placement, name, opts) {
3078
+ super(placement, name);
3079
+ this.paragraphs = paragraphs;
3080
+ this.bodyOptions = opts?.bodyOptions;
3081
+ this.fill = opts?.fill;
3082
+ this.line = opts?.line;
3083
+ this.effects = opts?.effects;
3084
+ this.transform = opts?.transform;
3085
+ this.scene3d = opts?.scene3d;
3086
+ this.shape3d = opts?.shape3d;
3087
+ }
3088
+ /**
3089
+ * Creates a TextElement from a plain string, splitting on newlines into paragraphs.
3090
+ * @param text - Plain text content (newlines become paragraph breaks).
3091
+ * @param placement - Position and size on the slide.
3092
+ * @param runOptions - Optional formatting for all text runs.
3093
+ * @param paraOptions - Optional paragraph-level formatting.
3094
+ * @param bodyOptions - Optional text body options.
3095
+ * @returns A new TextElement instance.
3096
+ */
3097
+ static fromString(text, placement, runOptions, paraOptions, bodyOptions) {
3098
+ const lines = text.split("\n");
3099
+ const paragraphs = lines.map((line) => ({
3100
+ runs: [{ text: line, options: runOptions }],
3101
+ options: paraOptions
3102
+ }));
3103
+ return new _TextElement(paragraphs, placement, void 0, { bodyOptions });
3104
+ }
3105
+ };
3106
+
3107
+ // src/elements/image.ts
3108
+ var ImageElement = class extends BaseElement {
3109
+ /** Element type discriminator. */
3110
+ type = "image";
3111
+ /** File system path to the image (mutually exclusive with `data`). */
3112
+ path;
3113
+ /** Raw image bytes (mutually exclusive with `path`). */
3114
+ data;
3115
+ /** MIME type of the image (defaults to 'image/png'). */
3116
+ contentType;
3117
+ /** Border/outline properties for the image. */
3118
+ border;
3119
+ /** Visual effects (shadow, glow, etc.). */
3120
+ effects;
3121
+ /** 2-D transform (rotation, flip). */
3122
+ transform;
3123
+ /** Image opacity, 0-100. */
3124
+ opacity;
3125
+ /** Crop offsets as fractions (0-1) from each edge. */
3126
+ crop;
3127
+ /** Hyperlink attached to the image. */
3128
+ hyperlink;
3129
+ /** Whether to lock the aspect ratio when resizing. */
3130
+ lockAspectRatio;
3131
+ /** 3D scene (camera + lighting). */
3132
+ scene3d;
3133
+ /** 3D shape (extrusion, bevels, material). */
3134
+ shape3d;
3135
+ brightness;
3136
+ contrast;
3137
+ saturation;
3138
+ duotone;
3139
+ cropShape;
3140
+ constructor(placement, opts) {
3141
+ super(placement);
3142
+ this.path = opts.path;
3143
+ this.data = opts.data;
3144
+ this.contentType = opts.contentType ?? "image/png";
3145
+ this.border = opts.border;
3146
+ this.effects = opts.effects;
3147
+ this.transform = opts.transform;
3148
+ this.opacity = opts.opacity;
3149
+ this.crop = opts.crop;
3150
+ this.hyperlink = opts.hyperlink;
3151
+ this.lockAspectRatio = opts.lockAspectRatio;
3152
+ this.scene3d = opts.scene3d;
3153
+ this.shape3d = opts.shape3d;
3154
+ this.brightness = opts.brightness;
3155
+ this.contrast = opts.contrast;
3156
+ this.saturation = opts.saturation;
3157
+ this.duotone = opts.duotone;
3158
+ this.cropShape = opts.cropShape;
3159
+ }
3160
+ };
3161
+
3162
+ // src/elements/shape.ts
3163
+ var ShapeElement = class extends BaseElement {
3164
+ /** Element type discriminator. */
3165
+ type = "shape";
3166
+ /** Preset geometry type (e.g. 'rect', 'ellipse', 'roundRect'). */
3167
+ geometry;
3168
+ /** Background fill for the shape. */
3169
+ fill;
3170
+ /** Outline/border properties. */
3171
+ line;
3172
+ /** Visual effects (shadow, glow, etc.). */
3173
+ effects;
3174
+ /** 2-D transform (rotation, flip). */
3175
+ transform;
3176
+ /** Shape opacity, 0-100. */
3177
+ opacity;
3178
+ /** Plain text content rendered inside the shape. */
3179
+ text;
3180
+ /** Text body options for text rendered inside the shape. */
3181
+ textOptions;
3182
+ /** Run-level formatting for the shape's text. */
3183
+ textRunOptions;
3184
+ /** Paragraph-level formatting for the shape's text. */
3185
+ textParagraphOptions;
3186
+ /** Geometry adjustment values keyed by handle name. */
3187
+ adjustValues;
3188
+ /** Hyperlink attached to the shape. */
3189
+ hyperlink;
3190
+ /** 3D scene (camera + lighting). */
3191
+ scene3d;
3192
+ /** 3D shape (extrusion, bevels, material). */
3193
+ shape3d;
3194
+ /** Custom geometry (overrides preset). */
3195
+ customGeometry;
3196
+ /** Shape protection locks. */
3197
+ locks;
3198
+ constructor(geometry, placement, opts) {
3199
+ super(placement);
3200
+ this.geometry = geometry;
3201
+ this.fill = opts?.fill;
3202
+ this.line = opts?.line;
3203
+ this.effects = opts?.effects;
3204
+ this.transform = opts?.transform;
3205
+ this.opacity = opts?.opacity;
3206
+ this.text = opts?.text;
3207
+ this.textOptions = opts?.textOptions;
3208
+ this.textRunOptions = opts?.textRunOptions;
3209
+ this.textParagraphOptions = opts?.textParagraphOptions;
3210
+ this.adjustValues = opts?.adjustValues;
3211
+ this.hyperlink = opts?.hyperlink;
3212
+ this.scene3d = opts?.scene3d;
3213
+ this.shape3d = opts?.shape3d;
3214
+ this.customGeometry = opts?.customGeometry;
3215
+ this.locks = opts?.locks;
3216
+ }
3217
+ };
3218
+
3219
+ // src/elements/table.ts
3220
+ var TableElement = class extends BaseElement {
3221
+ /** Element type discriminator. */
3222
+ type = "table";
3223
+ /** Row data including cells and per-row options. */
3224
+ rows;
3225
+ /** Column widths in EMUs. */
3226
+ colWidths;
3227
+ /** Row heights in EMUs. */
3228
+ rowHeights;
3229
+ /** Border configuration for the table. */
3230
+ border;
3231
+ /** Default background fill for cells. */
3232
+ fill;
3233
+ /** Apply special formatting to the first row. */
3234
+ firstRow;
3235
+ /** Apply special formatting to the last row. */
3236
+ lastRow;
3237
+ /** Apply special formatting to the first column. */
3238
+ firstCol;
3239
+ /** Apply special formatting to the last column. */
3240
+ lastCol;
3241
+ /** Enable row banding (alternating row colors). */
3242
+ bandRow;
3243
+ /** Enable column banding (alternating column colors). */
3244
+ bandCol;
3245
+ /** Pair of colors for alternating row bands. */
3246
+ bandRowColors;
3247
+ /** Visual effects (shadow, glow, etc.). */
3248
+ effects;
3249
+ /** 2-D transform (rotation, flip). */
3250
+ transform;
3251
+ /** Built-in table style GUID. */
3252
+ tableStyleId;
3253
+ constructor(rows, placement, opts) {
3254
+ super(placement);
3255
+ this.rows = rows;
3256
+ this.colWidths = opts?.colWidths;
3257
+ this.rowHeights = opts?.rowHeights;
3258
+ this.border = opts?.border;
3259
+ this.fill = opts?.fill;
3260
+ this.firstRow = opts?.firstRow;
3261
+ this.lastRow = opts?.lastRow;
3262
+ this.firstCol = opts?.firstCol;
3263
+ this.lastCol = opts?.lastCol;
3264
+ this.bandRow = opts?.bandRow;
3265
+ this.bandCol = opts?.bandCol;
3266
+ this.bandRowColors = opts?.bandRowColors;
3267
+ this.effects = opts?.effects;
3268
+ this.transform = opts?.transform;
3269
+ this.tableStyleId = opts?.tableStyleId;
3270
+ }
3271
+ };
3272
+
3273
+ // src/elements/chart.ts
3274
+ var ChartElement = class extends BaseElement {
3275
+ /** Element type discriminator. */
3276
+ type = "chart";
3277
+ /** The kind of chart to render (e.g. 'bar', 'line', 'pie'). */
3278
+ chartType;
3279
+ /** Series and category data for the chart. */
3280
+ data;
3281
+ /** Optional chart title text. */
3282
+ title;
3283
+ /** Whether to display the legend (defaults to true). */
3284
+ showLegend;
3285
+ /** Whether to show data labels on data points (defaults to false). */
3286
+ showDataLabels;
3287
+ /** Whether to display grid lines (defaults to true). */
3288
+ showGridLines;
3289
+ /** Font size for the chart title, in points. */
3290
+ titleFontSize;
3291
+ /** Color for the chart title. */
3292
+ titleColor;
3293
+ /** Position of the legend relative to the chart. */
3294
+ legendPosition;
3295
+ /** Category (horizontal) axis options. */
3296
+ catAxis;
3297
+ /** Primary value (vertical) axis options. */
3298
+ valAxis;
3299
+ /** Secondary value axis options (for combo charts). */
3300
+ secondaryValAxis;
3301
+ /** Plot area background fill and border. */
3302
+ plotArea;
3303
+ /** Gap width between bar groups, as a percentage. */
3304
+ barGapWidth;
3305
+ /** Overlap between bars in a group, as a percentage (-100 to 100). */
3306
+ barOverlap;
3307
+ /** Grouping mode for bar/column charts. */
3308
+ barGrouping;
3309
+ /** Explosion distance for pie slices, as a percentage. */
3310
+ pieExplosion;
3311
+ /** Hole size for doughnut charts, as a percentage (0-90). */
3312
+ doughnutHoleSize;
3313
+ /** Grouping mode for line charts. */
3314
+ lineGrouping;
3315
+ /** Grouping mode for area charts. */
3316
+ areaGrouping;
3317
+ /** Rendering style for scatter charts. */
3318
+ scatterStyle;
3319
+ /** Rendering style for radar charts. */
3320
+ radarStyle;
3321
+ /** Chart border/outline properties. */
3322
+ border;
3323
+ /** Chart background fill color. */
3324
+ fill;
3325
+ /** Chart-level data label configuration. */
3326
+ dataLabels;
3327
+ /** 3-D view settings for the chart. */
3328
+ view3D;
3329
+ constructor(chartType, data, placement, opts) {
3330
+ super(placement);
3331
+ this.chartType = chartType;
3332
+ this.data = data;
3333
+ this.title = opts?.title;
3334
+ this.showLegend = opts?.showLegend ?? true;
3335
+ this.showDataLabels = opts?.showDataLabels ?? false;
3336
+ this.showGridLines = opts?.showGridLines ?? true;
3337
+ this.titleFontSize = opts?.titleFontSize;
3338
+ this.titleColor = opts?.titleColor;
3339
+ this.legendPosition = opts?.legendPosition;
3340
+ this.catAxis = opts?.catAxis;
3341
+ this.valAxis = opts?.valAxis;
3342
+ this.secondaryValAxis = opts?.secondaryValAxis;
3343
+ this.plotArea = opts?.plotArea;
3344
+ this.barGapWidth = opts?.barGapWidth;
3345
+ this.barOverlap = opts?.barOverlap;
3346
+ this.barGrouping = opts?.barGrouping;
3347
+ this.pieExplosion = opts?.pieExplosion;
3348
+ this.doughnutHoleSize = opts?.doughnutHoleSize;
3349
+ this.lineGrouping = opts?.lineGrouping;
3350
+ this.areaGrouping = opts?.areaGrouping;
3351
+ this.scatterStyle = opts?.scatterStyle;
3352
+ this.radarStyle = opts?.radarStyle;
3353
+ this.border = opts?.border;
3354
+ this.fill = opts?.fill;
3355
+ this.dataLabels = opts?.dataLabels;
3356
+ this.view3D = opts?.view3D;
3357
+ }
3358
+ };
3359
+
3360
+ // src/elements/media.ts
3361
+ var MediaElement = class extends BaseElement {
3362
+ /** Element type discriminator. */
3363
+ type = "media";
3364
+ /** Whether this is a video or audio element. */
3365
+ mediaType;
3366
+ /** File system path to the media file (mutually exclusive with `data`). */
3367
+ path;
3368
+ /** Raw media bytes (mutually exclusive with `path`). */
3369
+ data;
3370
+ /** MIME type (defaults to 'video/mp4' or 'audio/mpeg' based on mediaType). */
3371
+ contentType;
3372
+ /** Poster/thumbnail image displayed before playback. */
3373
+ poster;
3374
+ /** Playback settings (autoplay, loop, etc.). */
3375
+ playback;
3376
+ /** Visual effects (shadow, glow, etc.). */
3377
+ effects;
3378
+ /** 2-D transform (rotation, flip). */
3379
+ transform;
3380
+ constructor(mediaType, placement, opts) {
3381
+ super(placement);
3382
+ this.mediaType = mediaType;
3383
+ this.path = opts.path;
3384
+ this.data = opts.data;
3385
+ this.contentType = opts.contentType ?? (mediaType === "video" ? "video/mp4" : "audio/mpeg");
3386
+ this.poster = opts.poster;
3387
+ this.playback = opts.playback;
3388
+ this.effects = opts.effects;
3389
+ this.transform = opts.transform;
3390
+ }
3391
+ };
3392
+
3393
+ // src/elements/group.ts
3394
+ var GroupElement = class extends BaseElement {
3395
+ /** Element type discriminator. */
3396
+ type = "group";
3397
+ /** Child elements contained within this group. */
3398
+ children;
3399
+ constructor(children, placement, name) {
3400
+ super(placement, name);
3401
+ this.children = children;
3402
+ }
3403
+ };
3404
+
3405
+ // src/elements/connector.ts
3406
+ var ConnectorElement = class extends BaseElement {
3407
+ /** Element type discriminator. */
3408
+ type = "connector";
3409
+ /** The connector routing style (defaults to 'straight'). */
3410
+ connectorType;
3411
+ /** Line formatting (color, width, dash style, etc.). */
3412
+ line;
3413
+ /** Arrowhead properties for the start of the connector. */
3414
+ startArrow;
3415
+ /** Arrowhead properties for the end of the connector. */
3416
+ endArrow;
3417
+ constructor(placement, opts) {
3418
+ super(placement);
3419
+ this.connectorType = opts?.type ?? "straight";
3420
+ this.line = opts?.line;
3421
+ this.startArrow = opts?.startArrow;
3422
+ this.endArrow = opts?.endArrow;
3423
+ }
3424
+ };
3425
+
3426
+ // src/builder/slide-builder.ts
3427
+ function placementFromOpts(opts) {
3428
+ return {
3429
+ x: opts.x ?? 0,
3430
+ y: opts.y ?? 0,
3431
+ w: opts.w ?? 5,
3432
+ h: opts.h ?? 1
3433
+ };
3434
+ }
3435
+ var SlideBuilder = class {
3436
+ data;
3437
+ commentAuthors;
3438
+ constructor(data, commentAuthors) {
3439
+ this.data = data;
3440
+ this.commentAuthors = commentAuthors;
3441
+ }
3442
+ /** Adds a text box to the slide. Placement dimensions are in inches. */
3443
+ addText(text, opts = {}) {
3444
+ const placement = placementFromOpts(opts);
3445
+ const element = TextElement.fromString(
3446
+ text,
3447
+ placement,
3448
+ {
3449
+ bold: opts.bold,
3450
+ italic: opts.italic,
3451
+ underline: opts.underline,
3452
+ strikethrough: opts.strikethrough,
3453
+ fontSize: opts.fontSize,
3454
+ fontFace: opts.fontFace,
3455
+ color: opts.color,
3456
+ subscript: opts.subscript,
3457
+ superscript: opts.superscript,
3458
+ highlight: opts.highlight,
3459
+ letterSpacing: opts.letterSpacing,
3460
+ baseline: opts.baseline,
3461
+ hyperlink: opts.hyperlink,
3462
+ outline: opts.outline,
3463
+ shadow: opts.shadow,
3464
+ caps: opts.caps,
3465
+ lang: opts.lang
3466
+ },
3467
+ {
3468
+ align: opts.align,
3469
+ lineSpacing: opts.lineSpacing,
3470
+ spaceBefore: opts.spaceBefore,
3471
+ spaceAfter: opts.spaceAfter,
3472
+ indent: opts.indent,
3473
+ marginLeft: opts.marginLeft,
3474
+ level: opts.level,
3475
+ rtl: opts.rtl,
3476
+ bullet: opts.bullet,
3477
+ numbering: opts.numbering,
3478
+ tabStops: opts.tabStops,
3479
+ defaultRunOptions: opts.defaultRunOptions
3480
+ },
3481
+ {
3482
+ verticalAlign: opts.verticalAlign,
3483
+ textDirection: opts.textDirection,
3484
+ wordWrap: opts.wordWrap,
3485
+ autoFit: opts.autoFit,
3486
+ margin: opts.margin,
3487
+ columns: opts.columns,
3488
+ columnSpacing: opts.columnSpacing
3489
+ }
3490
+ );
3491
+ if (opts.fill) element.fill = opts.fill;
3492
+ if (opts.line) element.line = opts.line;
3493
+ if (opts.effects) element.effects = opts.effects;
3494
+ if (opts.transform) element.transform = opts.transform;
3495
+ if (opts.scene3d) element.scene3d = opts.scene3d;
3496
+ if (opts.shape3d) element.shape3d = opts.shape3d;
3497
+ this.data.elements.push(element);
3498
+ return this;
3499
+ }
3500
+ /** Adds an image to the slide from a file path or binary data. */
3501
+ addImage(opts) {
3502
+ const placement = placementFromOpts(opts);
3503
+ const element = new ImageElement(placement, {
3504
+ path: opts.path,
3505
+ data: opts.data,
3506
+ contentType: opts.contentType,
3507
+ border: opts.border,
3508
+ effects: opts.effects,
3509
+ transform: opts.transform,
3510
+ opacity: opts.opacity,
3511
+ crop: opts.crop,
3512
+ hyperlink: opts.hyperlink,
3513
+ lockAspectRatio: opts.lockAspectRatio,
3514
+ scene3d: opts.scene3d,
3515
+ shape3d: opts.shape3d,
3516
+ brightness: opts.brightness,
3517
+ contrast: opts.contrast,
3518
+ saturation: opts.saturation,
3519
+ duotone: opts.duotone,
3520
+ cropShape: opts.cropShape
3521
+ });
3522
+ this.data.elements.push(element);
3523
+ return this;
3524
+ }
3525
+ /** Adds a preset geometry shape to the slide. */
3526
+ addShape(geometry, opts = {}) {
3527
+ const placement = placementFromOpts(opts);
3528
+ const element = new ShapeElement(geometry, placement, {
3529
+ fill: opts.fill,
3530
+ line: opts.line,
3531
+ effects: opts.effects,
3532
+ transform: opts.transform,
3533
+ opacity: opts.opacity,
3534
+ text: opts.text,
3535
+ textOptions: opts.textOptions,
3536
+ textRunOptions: opts.textRunOptions,
3537
+ textParagraphOptions: opts.textParagraphOptions,
3538
+ adjustValues: opts.adjustValues,
3539
+ hyperlink: opts.hyperlink,
3540
+ scene3d: opts.scene3d,
3541
+ shape3d: opts.shape3d,
3542
+ customGeometry: opts.customGeometry,
3543
+ locks: opts.locks
3544
+ });
3545
+ this.data.elements.push(element);
3546
+ return this;
3547
+ }
3548
+ /** Adds a table to the slide. */
3549
+ addTable(rows, opts = {}) {
3550
+ const placement = placementFromOpts(opts);
3551
+ const element = new TableElement(rows, placement, {
3552
+ colWidths: opts.colWidths,
3553
+ rowHeights: opts.rowHeights,
3554
+ border: opts.border,
3555
+ fill: opts.fill,
3556
+ firstRow: opts.firstRow,
3557
+ lastRow: opts.lastRow,
3558
+ firstCol: opts.firstCol,
3559
+ lastCol: opts.lastCol,
3560
+ bandRow: opts.bandRow,
3561
+ bandCol: opts.bandCol,
3562
+ bandRowColors: opts.bandRowColors,
3563
+ effects: opts.effects,
3564
+ transform: opts.transform,
3565
+ tableStyleId: opts.tableStyleId
3566
+ });
3567
+ this.data.elements.push(element);
3568
+ return this;
3569
+ }
3570
+ /** Adds a chart to the slide. Defaults to 6x4 inches if width/height are not specified. */
3571
+ addChart(type, data, opts = {}) {
3572
+ const placement = placementFromOpts({ ...opts, w: opts.w ?? 6, h: opts.h ?? 4 });
3573
+ const element = new ChartElement(type, data, placement, {
3574
+ title: opts.title,
3575
+ showLegend: opts.showLegend,
3576
+ showDataLabels: opts.showDataLabels,
3577
+ showGridLines: opts.showGridLines,
3578
+ titleFontSize: opts.titleFontSize,
3579
+ titleColor: opts.titleColor,
3580
+ legendPosition: opts.legendPosition,
3581
+ catAxis: opts.catAxis,
3582
+ valAxis: opts.valAxis,
3583
+ secondaryValAxis: opts.secondaryValAxis,
3584
+ plotArea: opts.plotArea,
3585
+ barGapWidth: opts.barGapWidth,
3586
+ barOverlap: opts.barOverlap,
3587
+ barGrouping: opts.barGrouping,
3588
+ pieExplosion: opts.pieExplosion,
3589
+ doughnutHoleSize: opts.doughnutHoleSize,
3590
+ lineGrouping: opts.lineGrouping,
3591
+ areaGrouping: opts.areaGrouping,
3592
+ scatterStyle: opts.scatterStyle,
3593
+ radarStyle: opts.radarStyle,
3594
+ border: opts.border,
3595
+ fill: opts.fill,
3596
+ dataLabels: opts.dataLabels,
3597
+ view3D: opts.view3D
3598
+ });
3599
+ this.data.elements.push(element);
3600
+ return this;
3601
+ }
3602
+ /** Sets speaker notes text for this slide. */
3603
+ setNotes(notes) {
3604
+ this.data.notes = notes;
3605
+ return this;
3606
+ }
3607
+ /** Adds a video element to the slide. Defaults to 6x4 inches. */
3608
+ addVideo(opts) {
3609
+ const placement = placementFromOpts({ ...opts, w: opts.w ?? 6, h: opts.h ?? 4 });
3610
+ const element = new MediaElement("video", placement, {
3611
+ path: opts.path,
3612
+ data: opts.data,
3613
+ contentType: opts.contentType,
3614
+ poster: opts.poster,
3615
+ playback: opts.playback,
3616
+ effects: opts.effects,
3617
+ transform: opts.transform
3618
+ });
3619
+ this.data.elements.push(element);
3620
+ return this;
3621
+ }
3622
+ /** Adds an audio element to the slide. Defaults to 1x1 inch. */
3623
+ addAudio(opts) {
3624
+ const placement = placementFromOpts({ ...opts, w: opts.w ?? 1, h: opts.h ?? 1 });
3625
+ const element = new MediaElement("audio", placement, {
3626
+ path: opts.path,
3627
+ data: opts.data,
3628
+ contentType: opts.contentType,
3629
+ poster: opts.poster,
3630
+ playback: opts.playback,
3631
+ effects: opts.effects,
3632
+ transform: opts.transform
3633
+ });
3634
+ this.data.elements.push(element);
3635
+ return this;
3636
+ }
3637
+ /** Adds a pre-built element directly to the slide. */
3638
+ addElement(element) {
3639
+ this.data.elements.push(element);
3640
+ return this;
3641
+ }
3642
+ /**
3643
+ * Adds a comment to the slide.
3644
+ * @param authorId - ID returned by {@link Presentation.defineCommentAuthor}.
3645
+ */
3646
+ addComment(text, authorId, opts) {
3647
+ if (!this.data.comments) this.data.comments = [];
3648
+ if (this.commentAuthors) {
3649
+ const author = this.commentAuthors.find((a) => a.id === authorId);
3650
+ if (author) author.lastIdx++;
3651
+ }
3652
+ this.data.comments.push({
3653
+ authorId,
3654
+ text,
3655
+ date: opts?.date,
3656
+ position: opts?.position,
3657
+ index: this.data.comments.length + 1
3658
+ });
3659
+ return this;
3660
+ }
3661
+ /** Sets user-defined tags on this slide as key-value pairs. */
3662
+ setTags(tags) {
3663
+ this.data.tags = Object.entries(tags).map(([name, value]) => ({ name, value }));
3664
+ return this;
3665
+ }
3666
+ /** Sets synchronization data for collaborative slide updates. */
3667
+ setSyncData(data) {
3668
+ this.data.syncData = data;
3669
+ return this;
3670
+ }
3671
+ /** Sets the transition effect for this slide. */
3672
+ setTransition(transition) {
3673
+ this.data.transition = transition;
3674
+ return this;
3675
+ }
3676
+ /** Adds an animation to the slide's animation sequence. */
3677
+ addAnimation(anim) {
3678
+ if (!this.data.animations) this.data.animations = [];
3679
+ this.data.animations.push(anim);
3680
+ return this;
3681
+ }
3682
+ /** Sets the background fill or image for this slide. */
3683
+ setBackground(bg) {
3684
+ this.data.background = bg;
3685
+ return this;
3686
+ }
3687
+ /**
3688
+ * Adds a group of elements that are positioned and transformed together.
3689
+ * @param buildFn - Callback receiving a {@link GroupBuilder} to populate the group.
3690
+ */
3691
+ addGroup(buildFn, opts = {}) {
3692
+ const placement = placementFromOpts(opts);
3693
+ const children = [];
3694
+ const groupBuilder = new GroupBuilder(children);
3695
+ buildFn(groupBuilder);
3696
+ const element = new GroupElement(children, placement);
3697
+ this.data.elements.push(element);
3698
+ return this;
3699
+ }
3700
+ /** Adds a connector line between points on the slide. */
3701
+ addConnector(opts) {
3702
+ const placement = placementFromOpts(opts);
3703
+ const element = new ConnectorElement(placement, {
3704
+ type: opts.type,
3705
+ line: opts.line,
3706
+ startArrow: opts.startArrow,
3707
+ endArrow: opts.endArrow
3708
+ });
3709
+ this.data.elements.push(element);
3710
+ return this;
3711
+ }
3712
+ };
3713
+ var GroupBuilder = class {
3714
+ children;
3715
+ constructor(children) {
3716
+ this.children = children;
3717
+ }
3718
+ /** Adds a shape to the group. */
3719
+ addShape(geometry, opts = {}) {
3720
+ const placement = placementFromOpts(opts);
3721
+ this.children.push(
3722
+ new ShapeElement(geometry, placement, {
3723
+ fill: opts.fill,
3724
+ line: opts.line,
3725
+ effects: opts.effects,
3726
+ transform: opts.transform,
3727
+ text: opts.text,
3728
+ textOptions: opts.textOptions,
3729
+ textRunOptions: opts.textRunOptions,
3730
+ textParagraphOptions: opts.textParagraphOptions,
3731
+ adjustValues: opts.adjustValues,
3732
+ hyperlink: opts.hyperlink
3733
+ })
3734
+ );
3735
+ return this;
3736
+ }
3737
+ /** Adds a text box to the group. */
3738
+ addText(text, opts = {}) {
3739
+ const placement = placementFromOpts(opts);
3740
+ this.children.push(
3741
+ TextElement.fromString(
3742
+ text,
3743
+ placement,
3744
+ {
3745
+ bold: opts.bold,
3746
+ italic: opts.italic,
3747
+ underline: opts.underline,
3748
+ fontSize: opts.fontSize,
3749
+ fontFace: opts.fontFace,
3750
+ color: opts.color
3751
+ },
3752
+ { align: opts.align }
3753
+ )
3754
+ );
3755
+ return this;
3756
+ }
3757
+ /** Adds an image to the group. */
3758
+ addImage(opts) {
3759
+ const placement = placementFromOpts(opts);
3760
+ this.children.push(
3761
+ new ImageElement(placement, {
3762
+ path: opts.path,
3763
+ data: opts.data,
3764
+ contentType: opts.contentType
3765
+ })
3766
+ );
3767
+ return this;
3768
+ }
3769
+ /** Adds a pre-built element directly to the group. */
3770
+ addElement(element) {
3771
+ this.children.push(element);
3772
+ return this;
3773
+ }
3774
+ };
3775
+
3776
+ // src/builder/presentation-builder.ts
3777
+ var Presentation = class {
3778
+ slides = [];
3779
+ registry;
3780
+ theme;
3781
+ masters;
3782
+ layouts;
3783
+ layoutNameMap = /* @__PURE__ */ new Map();
3784
+ presProps = {};
3785
+ viewProps = {};
3786
+ notesMaster;
3787
+ handoutMaster;
3788
+ commentAuthors = [];
3789
+ authorNameMap = /* @__PURE__ */ new Map();
3790
+ presTags = [];
3791
+ opts;
3792
+ /** Creates a new presentation with optional configuration. */
3793
+ constructor(opts) {
3794
+ this.opts = opts ?? {};
3795
+ this.theme = {
3796
+ ...defaultTheme,
3797
+ ...opts?.theme,
3798
+ colors: { ...defaultTheme.colors, ...opts?.theme?.colors },
3799
+ fonts: { ...defaultTheme.fonts, ...opts?.theme?.fonts }
3800
+ };
3801
+ this.registry = new Registry();
3802
+ this.registry.registerElement("text", textSerializer);
3803
+ this.registry.registerElement("image", imageSerializer);
3804
+ this.registry.registerElement("shape", shapeSerializer);
3805
+ this.registry.registerElement("table", tableSerializer);
3806
+ this.registry.registerElement("chart", chartSerializer);
3807
+ this.registry.registerElement("media", mediaSerializer);
3808
+ this.registry.registerElement("group", createGroupSerializer(this.registry));
3809
+ this.registry.registerElement("connector", connectorSerializer);
3810
+ this.masters = [new SlideMaster({ name: "Default Master" })];
3811
+ this.layouts = [new SlideLayout(builtinLayouts.blank)];
3812
+ this.layoutNameMap.set("blank", 0);
3813
+ }
3814
+ /** Registers a plugin to extend serialization capabilities. */
3815
+ use(plugin) {
3816
+ plugin.install(this.registry);
3817
+ return this;
3818
+ }
3819
+ /** Defines or replaces the default slide master. */
3820
+ defineSlideMaster(def) {
3821
+ this.masters[0] = new SlideMaster(def);
3822
+ return this;
3823
+ }
3824
+ /** Registers a new slide layout that can be referenced by name when adding slides. */
3825
+ defineLayout(def) {
3826
+ const idx = this.layouts.length;
3827
+ this.layouts.push(new SlideLayout(def));
3828
+ this.layoutNameMap.set(def.name, idx);
3829
+ return this;
3830
+ }
3831
+ /** Defines the notes master for speaker notes formatting. */
3832
+ defineNotesMaster(def) {
3833
+ this.notesMaster = new NotesMaster(def);
3834
+ return this;
3835
+ }
3836
+ /** Defines the handout master for printed handout formatting. */
3837
+ defineHandoutMaster(def) {
3838
+ this.handoutMaster = new HandoutMaster(def);
3839
+ return this;
3840
+ }
3841
+ /** Configures slideshow playback properties (e.g., loop, timing). */
3842
+ setShowProperties(props) {
3843
+ this.presProps.slideShow = props;
3844
+ return this;
3845
+ }
3846
+ /** Configures print-related properties. */
3847
+ setPrintProperties(props) {
3848
+ this.presProps.print = props;
3849
+ return this;
3850
+ }
3851
+ /** Sets the default view properties (e.g., normal, slide sorter). */
3852
+ setViewProperties(props) {
3853
+ this.viewProps = props;
3854
+ return this;
3855
+ }
3856
+ /**
3857
+ * Registers a comment author. Returns the author ID, reusing an existing one if the name matches.
3858
+ * @param name - Display name of the author.
3859
+ * @param initials - Author initials shown in comment badges.
3860
+ */
3861
+ defineCommentAuthor(name, initials) {
3862
+ const existing = this.authorNameMap.get(name);
3863
+ if (existing != null) return existing;
3864
+ const id = this.commentAuthors.length;
3865
+ this.commentAuthors.push({ id, name, initials, lastIdx: 0, clrIdx: id });
3866
+ this.authorNameMap.set(name, id);
3867
+ return id;
3868
+ }
3869
+ /** Sets user-defined tags on the presentation as key-value pairs. */
3870
+ setTags(tags) {
3871
+ this.presTags = Object.entries(tags).map(([name, value]) => ({ name, value }));
3872
+ return this;
3873
+ }
3874
+ /**
3875
+ * Adds a new slide and returns a builder for populating it.
3876
+ * @param layoutName - Name of a previously defined layout; defaults to "blank".
3877
+ */
3878
+ addSlide(layoutName) {
3879
+ const layoutIndex = layoutName ? this.layoutNameMap.get(layoutName) ?? 0 : 0;
3880
+ const slideData = { elements: [], layoutIndex };
3881
+ this.slides.push(slideData);
3882
+ return new SlideBuilder(slideData, this.commentAuthors);
3883
+ }
3884
+ /** Serializes the presentation to a PPTX file in memory and returns the raw bytes. */
3885
+ toBuffer() {
3886
+ return writePresentation({
3887
+ slides: this.slides,
3888
+ theme: this.theme,
3889
+ registry: this.registry,
3890
+ masters: this.masters,
3891
+ layouts: this.layouts,
3892
+ presProps: this.presProps,
3893
+ viewProps: this.viewProps,
3894
+ notesMaster: this.notesMaster,
3895
+ commentAuthors: this.commentAuthors.length > 0 ? this.commentAuthors : void 0,
3896
+ handoutMaster: this.handoutMaster,
3897
+ tags: this.presTags.length > 0 ? this.presTags : void 0,
3898
+ htmlPublishUri: this.opts.htmlPublishUri,
3899
+ slideUpdateUrl: this.opts.slideUpdateUrl,
3900
+ slideSize: this.opts.slideSize,
3901
+ coreProperties: this.opts.coreProperties
3902
+ });
3903
+ }
3904
+ /** Writes the presentation as a .pptx file to disk (Node.js only). */
3905
+ async toFile(path) {
3906
+ const { writeFile } = await import("fs/promises");
3907
+ const buf = this.toBuffer();
3908
+ await writeFile(path, buf);
3909
+ }
3910
+ };
3911
+ // Annotate the CommonJS export names for ESM import in node:
3912
+ 0 && (module.exports = {
3913
+ ChartElement,
3914
+ ConnectorElement,
3915
+ GroupElement,
3916
+ HandoutMaster,
3917
+ ImageElement,
3918
+ MediaElement,
3919
+ NotesMaster,
3920
+ Presentation,
3921
+ Registry,
3922
+ ShapeElement,
3923
+ SlideBuilder,
3924
+ SlideLayout,
3925
+ SlideMaster,
3926
+ TableElement,
3927
+ TextElement,
3928
+ builtinLayouts,
3929
+ defaultColorScheme,
3930
+ defaultFontScheme,
3931
+ defaultTheme
3932
+ });
3933
+ //# sourceMappingURL=index.cjs.map