@ganttloom/gantt-export 0.2.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/pptx.cjs ADDED
@@ -0,0 +1,827 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/pptx.ts
21
+ var pptx_exports = {};
22
+ __export(pptx_exports, {
23
+ computePagination: () => computePagination,
24
+ renderToPPTX: () => renderToPPTX
25
+ });
26
+ module.exports = __toCommonJS(pptx_exports);
27
+
28
+ // src/paginate.ts
29
+ var PAGE_SIZES_PT = {
30
+ // Values are the portrait (short-edge width x long-edge height) dimensions
31
+ // in PDF/typographic points (72pt = 1in).
32
+ A4: { width: 595.28, height: 841.89 },
33
+ Letter: { width: 612, height: 792 },
34
+ A3: { width: 841.89, height: 1190.55 }
35
+ };
36
+ var DEFAULT_MARGIN_PT = 36;
37
+ var DEFAULT_SCALE = 0.75;
38
+ var MIN_TIMELINE_WIDTH_PT = 50;
39
+ var MIN_ROWS_HEIGHT_PT = 20;
40
+ function resolvePageSize(opt) {
41
+ if (opt?.custom) {
42
+ return { width: opt.custom.widthPt, height: opt.custom.heightPt };
43
+ }
44
+ const name = opt?.name ?? "A4";
45
+ const base = PAGE_SIZES_PT[name];
46
+ const orientation = opt?.orientation ?? "landscape";
47
+ const short = Math.min(base.width, base.height);
48
+ const long = Math.max(base.width, base.height);
49
+ return orientation === "landscape" ? { width: long, height: short } : { width: short, height: long };
50
+ }
51
+ function defaultGridPanelWidthPx(model) {
52
+ const cols = model.columns ?? [];
53
+ if (cols.length === 0) return 160;
54
+ return cols.reduce((sum, c) => sum + (c.width ?? 120), 0);
55
+ }
56
+ function computePagination(model, options = {}) {
57
+ const { width: pageWidthPt, height: pageHeightPt } = resolvePageSize(options.pageSize);
58
+ const margins = {
59
+ top: options.margins?.top ?? DEFAULT_MARGIN_PT,
60
+ right: options.margins?.right ?? DEFAULT_MARGIN_PT,
61
+ bottom: options.margins?.bottom ?? DEFAULT_MARGIN_PT,
62
+ left: options.margins?.left ?? DEFAULT_MARGIN_PT
63
+ };
64
+ const scale = options.scale ?? DEFAULT_SCALE;
65
+ const gridPanelWidthPx = options.gridPanelWidthPx ?? defaultGridPanelWidthPx(model);
66
+ const gridPanelWidthPt = gridPanelWidthPx * scale;
67
+ const headerHeightPx = model.headerHeight;
68
+ const headerHeightPt = headerHeightPx * scale;
69
+ const contentWidthPt = pageWidthPt - margins.left - margins.right;
70
+ const contentHeightPt = pageHeightPt - margins.top - margins.bottom;
71
+ const timelineWidthPtPerPage = Math.max(contentWidthPt - gridPanelWidthPt, MIN_TIMELINE_WIDTH_PT);
72
+ const rowsHeightPtPerPage = Math.max(contentHeightPt - headerHeightPt, MIN_ROWS_HEIGHT_PT);
73
+ const timelineWidthPxPerPage = timelineWidthPtPerPage / scale;
74
+ const rowsHeightPxPerPage = rowsHeightPtPerPage / scale;
75
+ const totalHeightPx = Math.max(model.height, 1);
76
+ const totalWidthPx = Math.max(model.width, 1);
77
+ const rowCount = Math.max(1, Math.ceil(totalHeightPx / rowsHeightPxPerPage));
78
+ const colCount = Math.max(1, Math.ceil(totalWidthPx / timelineWidthPxPerPage));
79
+ const rowBands = [];
80
+ for (let i = 0; i < rowCount; i++) {
81
+ rowBands.push({
82
+ start: i * rowsHeightPxPerPage,
83
+ end: Math.min(totalHeightPx, (i + 1) * rowsHeightPxPerPage)
84
+ });
85
+ }
86
+ const colBands = [];
87
+ for (let i = 0; i < colCount; i++) {
88
+ colBands.push({
89
+ start: i * timelineWidthPxPerPage,
90
+ end: Math.min(totalWidthPx, (i + 1) * timelineWidthPxPerPage)
91
+ });
92
+ }
93
+ const pages = [];
94
+ for (let r = 0; r < rowCount; r++) {
95
+ const rowBand = rowBands[r];
96
+ for (let c = 0; c < colCount; c++) {
97
+ const colBand = colBands[c];
98
+ pages.push({
99
+ rowBand: r,
100
+ colBand: c,
101
+ rowStartY: rowBand.start,
102
+ rowEndY: rowBand.end,
103
+ colStartX: colBand.start,
104
+ colEndX: colBand.end
105
+ });
106
+ }
107
+ }
108
+ return {
109
+ pageWidthPt,
110
+ pageHeightPt,
111
+ margins,
112
+ scale,
113
+ gridPanelWidthPx,
114
+ gridPanelWidthPt,
115
+ headerHeightPx,
116
+ headerHeightPt,
117
+ timelineWidthPxPerPage,
118
+ rowsHeightPxPerPage,
119
+ rowBands,
120
+ colBands,
121
+ pages,
122
+ rowCount,
123
+ colCount
124
+ };
125
+ }
126
+ function clipSegment(x0, y0, x1, y1, rect) {
127
+ let t0 = 0;
128
+ let t1 = 1;
129
+ const dx = x1 - x0;
130
+ const dy = y1 - y0;
131
+ const checks = [
132
+ [-dx, x0 - rect.x0],
133
+ [dx, rect.x1 - x0],
134
+ [-dy, y0 - rect.y0],
135
+ [dy, rect.y1 - y0]
136
+ ];
137
+ for (const [p, q] of checks) {
138
+ if (p === 0) {
139
+ if (q < 0) return null;
140
+ } else {
141
+ const r = q / p;
142
+ if (p < 0) {
143
+ if (r > t1) return null;
144
+ if (r > t0) t0 = r;
145
+ } else {
146
+ if (r < t0) return null;
147
+ if (r < t1) t1 = r;
148
+ }
149
+ }
150
+ }
151
+ return { x0: x0 + t0 * dx, y0: y0 + t0 * dy, x1: x0 + t1 * dx, y1: y0 + t1 * dy };
152
+ }
153
+ function clipRect(x, y, w, h, rect) {
154
+ const x0 = Math.max(x, rect.x0);
155
+ const y0 = Math.max(y, rect.y0);
156
+ const x1 = Math.min(x + w, rect.x1);
157
+ const y1 = Math.min(y + h, rect.y1);
158
+ if (x1 <= x0 || y1 <= y0) return null;
159
+ return { x: x0, y: y0, w: x1 - x0, h: y1 - y0 };
160
+ }
161
+
162
+ // src/geom.ts
163
+ function computeLinkPolyline(fromBar, toBar) {
164
+ const x0 = fromBar.x + fromBar.width;
165
+ const y0 = fromBar.y + fromBar.height / 2;
166
+ const x1 = toBar.x;
167
+ const y1 = toBar.y + toBar.height / 2;
168
+ if (Math.abs(y1 - y0) < 0.01) {
169
+ return [{ x: x0, y: y0 }, { x: x1, y: y1 }];
170
+ }
171
+ const midX = x0 + (x1 - x0) / 2;
172
+ return [
173
+ { x: x0, y: y0 },
174
+ { x: midX, y: y0 },
175
+ { x: midX, y: y1 },
176
+ { x: x1, y: y1 }
177
+ ];
178
+ }
179
+ function clipPolylineToRect(points, rect) {
180
+ const segments = [];
181
+ let current = [];
182
+ for (let i = 0; i < points.length - 1; i++) {
183
+ const a = points[i];
184
+ const b = points[i + 1];
185
+ const clipped = clipSegment(a.x, a.y, b.x, b.y, rect);
186
+ if (!clipped) {
187
+ if (current.length > 1) segments.push(current);
188
+ current = [];
189
+ continue;
190
+ }
191
+ if (current.length === 0) {
192
+ current.push({ x: clipped.x0, y: clipped.y0 });
193
+ } else {
194
+ const last = current[current.length - 1];
195
+ if (Math.abs(last.x - clipped.x0) > 0.01 || Math.abs(last.y - clipped.y0) > 0.01) {
196
+ if (current.length > 1) segments.push(current);
197
+ current = [{ x: clipped.x0, y: clipped.y0 }];
198
+ }
199
+ }
200
+ current.push({ x: clipped.x1, y: clipped.y1 });
201
+ }
202
+ if (current.length > 1) segments.push(current);
203
+ return segments;
204
+ }
205
+
206
+ // src/layout.ts
207
+ function columnWidthsPt(model, layout) {
208
+ const columns = model.columns.length > 0 ? model.columns : [{ id: "name", title: "Task" }];
209
+ const naturalTotal = columns.reduce((sum, c) => sum + (c.width ?? 120), 0) || 1;
210
+ return columns.map((column) => ({
211
+ column,
212
+ widthPt: (column.width ?? 120) / naturalTotal * layout.gridPanelWidthPt
213
+ }));
214
+ }
215
+ function cellText(column, task, depth) {
216
+ if (!task) return "";
217
+ if (column.accessor) {
218
+ const v = column.accessor(task);
219
+ return v === null || v === void 0 ? "" : String(v);
220
+ }
221
+ const indent = " ".repeat(Math.max(0, depth));
222
+ return `${indent}${task.name}`;
223
+ }
224
+ function buildPageContent(model, tasks, layout, page) {
225
+ const { scale, gridPanelWidthPt, headerHeightPt, pageWidthPt, pageHeightPt, margins } = layout;
226
+ const theme = model.theme;
227
+ const tasksById = new Map(tasks.map((t) => [t.id, t]));
228
+ const barsByTaskId = new Map(model.bars.map((b) => [b.taskId, b]));
229
+ const rowsByTaskId = new Map(model.rows.map((r) => [r.taskId, r]));
230
+ const commands = [];
231
+ const contentX0 = margins.left;
232
+ const contentY0 = margins.top;
233
+ const timelineRect = {
234
+ x0: page.colStartX,
235
+ y0: page.rowStartY,
236
+ x1: page.colEndX,
237
+ y1: page.rowEndY
238
+ };
239
+ commands.push({ kind: "rect", x: 0, y: 0, w: pageWidthPt, h: pageHeightPt, fill: theme.backgroundColor });
240
+ const cols = columnWidthsPt(model, layout);
241
+ let colCursor = contentX0;
242
+ for (const { column, widthPt } of cols) {
243
+ commands.push({
244
+ kind: "text",
245
+ x: colCursor + 4,
246
+ y: contentY0 + headerHeightPt / 2 - 5,
247
+ w: widthPt - 8,
248
+ text: column.title,
249
+ size: 9,
250
+ color: theme.textColor,
251
+ bold: true,
252
+ align: column.align ?? "left"
253
+ });
254
+ colCursor += widthPt;
255
+ }
256
+ commands.push({
257
+ kind: "rect",
258
+ x: contentX0,
259
+ y: contentY0,
260
+ w: gridPanelWidthPt + (page.colEndX - page.colStartX) * scale,
261
+ h: headerHeightPt,
262
+ stroke: theme.gridColor
263
+ });
264
+ const timelineOriginX = contentX0 + gridPanelWidthPt;
265
+ for (const tick of model.ticks) {
266
+ if (tick.x < page.colStartX || tick.x > page.colEndX) continue;
267
+ const px = timelineOriginX + (tick.x - page.colStartX) * scale;
268
+ if (tick.isWeekend) {
269
+ }
270
+ commands.push({
271
+ kind: "line",
272
+ points: [
273
+ { x: px, y: contentY0 },
274
+ { x: px, y: pageHeightPt - margins.bottom }
275
+ ],
276
+ stroke: tick.isToday ? theme.todayColor : theme.gridColor
277
+ });
278
+ commands.push({
279
+ kind: "text",
280
+ x: px + 2,
281
+ y: contentY0 + headerHeightPt / 2 - 5,
282
+ text: tick.label,
283
+ size: 7,
284
+ color: theme.textColor,
285
+ align: "left"
286
+ });
287
+ }
288
+ const rowTop = (y) => contentY0 + headerHeightPt + (y - page.rowStartY) * scale;
289
+ for (const row of model.rows) {
290
+ if (row.y + row.height <= page.rowStartY || row.y >= page.rowEndY) continue;
291
+ const clippedRowY0 = Math.max(row.y, page.rowStartY);
292
+ const clippedRowY1 = Math.min(row.y + row.height, page.rowEndY);
293
+ const rowYTop = rowTop(clippedRowY0);
294
+ const rowHPt = (clippedRowY1 - clippedRowY0) * scale;
295
+ const task = tasksById.get(row.taskId);
296
+ let cx = contentX0;
297
+ for (const { column, widthPt } of cols) {
298
+ commands.push({
299
+ kind: "text",
300
+ x: cx + 4,
301
+ y: rowYTop + rowHPt / 2 - 4,
302
+ w: widthPt - 8,
303
+ text: cellText(column, task, row.depth),
304
+ size: 8,
305
+ color: theme.textColor,
306
+ align: column.align ?? "left"
307
+ });
308
+ cx += widthPt;
309
+ }
310
+ commands.push({
311
+ kind: "rect",
312
+ x: contentX0,
313
+ y: rowYTop,
314
+ w: gridPanelWidthPt + (page.colEndX - page.colStartX) * scale,
315
+ h: rowHPt,
316
+ stroke: theme.gridColor
317
+ });
318
+ const bar = barsByTaskId.get(row.taskId);
319
+ if (bar) {
320
+ const clippedBar = clipRect(bar.x, bar.y, bar.width, bar.height, timelineRect);
321
+ if (clippedBar) {
322
+ const bx = timelineOriginX + (clippedBar.x - page.colStartX) * scale;
323
+ const by = rowTop(clippedBar.y);
324
+ const bw = clippedBar.w * scale;
325
+ const bh = clippedBar.h * scale;
326
+ if (bar.baseline) {
327
+ const clippedBaseline = clipRect(bar.baseline.x, bar.y, bar.baseline.width, bar.height, timelineRect);
328
+ if (clippedBaseline) {
329
+ commands.push({
330
+ kind: "rect",
331
+ x: timelineOriginX + (clippedBaseline.x - page.colStartX) * scale,
332
+ y: rowTop(clippedBaseline.y),
333
+ w: clippedBaseline.w * scale,
334
+ h: clippedBaseline.h * scale,
335
+ fill: theme.baselineColor
336
+ });
337
+ }
338
+ }
339
+ if (bar.isMilestone) {
340
+ const r = bh / 2;
341
+ commands.push({
342
+ kind: "diamond",
343
+ cx: bx + r,
344
+ cy: by + r,
345
+ r,
346
+ fill: bar.isCritical ? theme.criticalColor : bar.color,
347
+ stroke: theme.textColor
348
+ });
349
+ if (bar.label) {
350
+ commands.push({
351
+ kind: "text",
352
+ x: bx + bh + 4,
353
+ y: by + bh / 2 - 4,
354
+ text: bar.label,
355
+ size: 8,
356
+ color: theme.textColor
357
+ });
358
+ }
359
+ } else {
360
+ commands.push({
361
+ kind: "rect",
362
+ x: bx,
363
+ y: by,
364
+ w: bw,
365
+ h: bh,
366
+ fill: bar.isCritical ? theme.criticalColor : bar.color
367
+ });
368
+ if (bar.progressWidth > 0) {
369
+ const clippedProgress = clipRect(bar.x, bar.y, bar.progressWidth, bar.height, timelineRect);
370
+ if (clippedProgress) {
371
+ commands.push({
372
+ kind: "rect",
373
+ x: timelineOriginX + (clippedProgress.x - page.colStartX) * scale,
374
+ y: rowTop(clippedProgress.y),
375
+ w: clippedProgress.w * scale,
376
+ h: clippedProgress.h * scale,
377
+ fill: bar.progressColor
378
+ });
379
+ }
380
+ }
381
+ commands.push({
382
+ kind: "text",
383
+ x: bx + 3,
384
+ y: by + bh / 2 - 4,
385
+ text: bar.label,
386
+ size: 8,
387
+ color: theme.textColor
388
+ });
389
+ }
390
+ }
391
+ }
392
+ }
393
+ for (const link of model.links) {
394
+ const fromBar = barsByTaskId.get(link.fromId);
395
+ const toBar = barsByTaskId.get(link.toId);
396
+ if (!fromBar || !toBar) continue;
397
+ const fromRow = rowsByTaskId.get(link.fromId);
398
+ const toRow = rowsByTaskId.get(link.toId);
399
+ if (!fromRow || !toRow) continue;
400
+ const polyline = computeLinkPolyline(fromBar, toBar);
401
+ const pieces = clipPolylineToRect(polyline, timelineRect);
402
+ for (const piece of pieces) {
403
+ commands.push({
404
+ kind: "line",
405
+ points: piece.map((p) => ({
406
+ x: timelineOriginX + (p.x - page.colStartX) * scale,
407
+ y: rowTop(p.y)
408
+ })),
409
+ stroke: link.isCritical ? theme.criticalColor : theme.linkColor
410
+ });
411
+ }
412
+ }
413
+ return { widthPt: pageWidthPt, heightPt: pageHeightPt, commands };
414
+ }
415
+
416
+ // src/zip.ts
417
+ var encoder = new TextEncoder();
418
+ var CRC_TABLE = (() => {
419
+ const table = new Uint32Array(256);
420
+ for (let n = 0; n < 256; n++) {
421
+ let c = n;
422
+ for (let k = 0; k < 8; k++) {
423
+ c = c & 1 ? (3988292384 ^ c >>> 1) >>> 0 : c >>> 1;
424
+ }
425
+ table[n] = c >>> 0;
426
+ }
427
+ return table;
428
+ })();
429
+ function crc32(data) {
430
+ let crc = 4294967295;
431
+ for (let i = 0; i < data.length; i++) {
432
+ crc = (CRC_TABLE[(crc ^ data[i]) & 255] ^ crc >>> 8) >>> 0;
433
+ }
434
+ return (crc ^ 4294967295) >>> 0;
435
+ }
436
+ var ByteWriter = class {
437
+ constructor() {
438
+ this.chunks = [];
439
+ this.len = 0;
440
+ }
441
+ get length() {
442
+ return this.len;
443
+ }
444
+ push(bytes) {
445
+ this.chunks.push(bytes);
446
+ this.len += bytes.length;
447
+ }
448
+ pushU16(v) {
449
+ this.push(new Uint8Array([v & 255, v >>> 8 & 255]));
450
+ }
451
+ pushU32(v) {
452
+ this.push(new Uint8Array([v & 255, v >>> 8 & 255, v >>> 16 & 255, v >>> 24 & 255]));
453
+ }
454
+ pushStr(s) {
455
+ this.push(encoder.encode(s));
456
+ }
457
+ toUint8Array() {
458
+ const out = new Uint8Array(this.len);
459
+ let o = 0;
460
+ for (const c of this.chunks) {
461
+ out.set(c, o);
462
+ o += c.length;
463
+ }
464
+ return out;
465
+ }
466
+ };
467
+ var DOS_TIME = 0;
468
+ var DOS_DATE = 1980 - 1980 << 9;
469
+ function buildZip(entries) {
470
+ const w = new ByteWriter();
471
+ const central = [];
472
+ for (const entry of entries) {
473
+ const nameBytes = encoder.encode(entry.name);
474
+ const crc = crc32(entry.data);
475
+ const offset = w.length;
476
+ w.pushU32(67324752);
477
+ w.pushU16(20);
478
+ w.pushU16(0);
479
+ w.pushU16(0);
480
+ w.pushU16(DOS_TIME);
481
+ w.pushU16(DOS_DATE);
482
+ w.pushU32(crc);
483
+ w.pushU32(entry.data.length);
484
+ w.pushU32(entry.data.length);
485
+ w.pushU16(nameBytes.length);
486
+ w.pushU16(0);
487
+ w.push(nameBytes);
488
+ w.push(entry.data);
489
+ central.push({ offset, entry, crc });
490
+ }
491
+ const centralStart = w.length;
492
+ for (const { offset, entry, crc } of central) {
493
+ const nameBytes = encoder.encode(entry.name);
494
+ w.pushU32(33639248);
495
+ w.pushU16(20);
496
+ w.pushU16(20);
497
+ w.pushU16(0);
498
+ w.pushU16(0);
499
+ w.pushU16(DOS_TIME);
500
+ w.pushU16(DOS_DATE);
501
+ w.pushU32(crc);
502
+ w.pushU32(entry.data.length);
503
+ w.pushU32(entry.data.length);
504
+ w.pushU16(nameBytes.length);
505
+ w.pushU16(0);
506
+ w.pushU16(0);
507
+ w.pushU16(0);
508
+ w.pushU16(0);
509
+ w.pushU32(0);
510
+ w.pushU32(offset);
511
+ w.push(nameBytes);
512
+ }
513
+ const centralSize = w.length - centralStart;
514
+ w.pushU32(101010256);
515
+ w.pushU16(0);
516
+ w.pushU16(0);
517
+ w.pushU16(central.length);
518
+ w.pushU16(central.length);
519
+ w.pushU32(centralSize);
520
+ w.pushU32(centralStart);
521
+ w.pushU16(0);
522
+ return w.toUint8Array();
523
+ }
524
+
525
+ // src/color.ts
526
+ function parseColor(input) {
527
+ if (!input) return [0, 0, 0];
528
+ const s = input.trim();
529
+ const hex = s.match(/^#?([0-9a-fA-F]{6})$/) ?? s.match(/^#?([0-9a-fA-F]{3})$/);
530
+ if (hex) {
531
+ let h = hex[1];
532
+ if (h.length === 3) {
533
+ h = h.split("").map((c) => c + c).join("");
534
+ }
535
+ const r = parseInt(h.slice(0, 2), 16) / 255;
536
+ const g = parseInt(h.slice(2, 4), 16) / 255;
537
+ const b = parseInt(h.slice(4, 6), 16) / 255;
538
+ return [r, g, b];
539
+ }
540
+ const rgb = s.match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i);
541
+ if (rgb) {
542
+ return [Number(rgb[1]) / 255, Number(rgb[2]) / 255, Number(rgb[3]) / 255];
543
+ }
544
+ const named = {
545
+ black: [0, 0, 0],
546
+ white: [1, 1, 1],
547
+ red: [1, 0, 0],
548
+ green: [0, 0.5, 0],
549
+ blue: [0, 0, 1],
550
+ gray: [0.5, 0.5, 0.5],
551
+ grey: [0.5, 0.5, 0.5],
552
+ transparent: [1, 1, 1]
553
+ };
554
+ return named[s.toLowerCase()] ?? [0, 0, 0];
555
+ }
556
+ function toHexByte(v) {
557
+ return Math.round(Math.max(0, Math.min(1, v)) * 255).toString(16).padStart(2, "0");
558
+ }
559
+ function toHexRRGGBB(input) {
560
+ const [r, g, b] = parseColor(input);
561
+ return `${toHexByte(r)}${toHexByte(g)}${toHexByte(b)}`.toUpperCase();
562
+ }
563
+
564
+ // src/pptx/xml.ts
565
+ function xmlEscape(input) {
566
+ return input.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
567
+ }
568
+
569
+ // src/pptx/shapes.ts
570
+ var EMU_PER_PT = 12700;
571
+ var ptToEmu = (pt) => Math.round(pt * EMU_PER_PT);
572
+ function alignAttr(align) {
573
+ if (align === "center") return ' algn="ctr"';
574
+ if (align === "right") return ' algn="r"';
575
+ return "";
576
+ }
577
+ function rectShape(id, cmd) {
578
+ const x = ptToEmu(cmd.x);
579
+ const y = ptToEmu(cmd.y);
580
+ const cx = Math.max(ptToEmu(cmd.w), 1);
581
+ const cy = Math.max(ptToEmu(cmd.h), 1);
582
+ const fill = cmd.fill ? `<a:solidFill><a:srgbClr val="${toHexRRGGBB(cmd.fill)}"/></a:solidFill>` : `<a:noFill/>`;
583
+ const line = cmd.stroke ? `<a:ln w="3175"><a:solidFill><a:srgbClr val="${toHexRRGGBB(cmd.stroke)}"/></a:solidFill></a:ln>` : `<a:ln><a:noFill/></a:ln>`;
584
+ return `<p:sp><p:nvSpPr><p:cNvPr id="${id}" name="Rect${id}"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="${x}" y="${y}"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom>${fill}${line}</p:spPr></p:sp>`;
585
+ }
586
+ function diamondShape(id, cmd) {
587
+ const x = ptToEmu(cmd.cx - cmd.r);
588
+ const y = ptToEmu(cmd.cy - cmd.r);
589
+ const size = Math.max(ptToEmu(cmd.r * 2), 1);
590
+ const fill = cmd.fill ? `<a:solidFill><a:srgbClr val="${toHexRRGGBB(cmd.fill)}"/></a:solidFill>` : `<a:noFill/>`;
591
+ const line = cmd.stroke ? `<a:ln w="3175"><a:solidFill><a:srgbClr val="${toHexRRGGBB(cmd.stroke)}"/></a:solidFill></a:ln>` : `<a:ln><a:noFill/></a:ln>`;
592
+ return `<p:sp><p:nvSpPr><p:cNvPr id="${id}" name="Milestone${id}"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="${x}" y="${y}"/><a:ext cx="${size}" cy="${size}"/></a:xfrm><a:prstGeom prst="diamond"><a:avLst/></a:prstGeom>${fill}${line}</p:spPr></p:sp>`;
593
+ }
594
+ function textShape(id, cmd) {
595
+ const x = ptToEmu(cmd.x);
596
+ const y = ptToEmu(Math.max(cmd.y - 2, 0));
597
+ const cx = Math.max(ptToEmu(cmd.w ?? 300), 1);
598
+ const cy = Math.max(ptToEmu(cmd.size * 1.6), 1);
599
+ const sizeHundredths = Math.round(cmd.size * 100);
600
+ const color = toHexRRGGBB(cmd.color);
601
+ const bold = cmd.bold ? ' b="1"' : "";
602
+ return `<p:sp><p:nvSpPr><p:cNvPr id="${id}" name="Text${id}"/><p:cNvSpPr txBox="1"/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="${x}" y="${y}"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/></p:spPr><p:txBody><a:bodyPr wrap="none" lIns="0" tIns="0" rIns="0" bIns="0" anchor="t"/><a:lstStyle/><a:p><a:pPr${alignAttr(cmd.align)}/><a:r><a:rPr lang="en-US" sz="${sizeHundredths}"${bold} dirty="0"><a:solidFill><a:srgbClr val="${color}"/></a:solidFill></a:rPr><a:t>${xmlEscape(cmd.text)}</a:t></a:r></a:p></p:txBody></p:sp>`;
603
+ }
604
+ function lineShape(id, cmd) {
605
+ if (cmd.points.length < 2) return null;
606
+ const xs = cmd.points.map((p) => ptToEmu(p.x));
607
+ const ys = cmd.points.map((p) => ptToEmu(p.y));
608
+ const minX = Math.min(...xs);
609
+ const minY = Math.min(...ys);
610
+ const w = Math.max(Math.max(...xs) - minX, 1);
611
+ const h = Math.max(Math.max(...ys) - minY, 1);
612
+ const pathPts = cmd.points.map((p, i) => {
613
+ const px = ptToEmu(p.x) - minX;
614
+ const py = ptToEmu(p.y) - minY;
615
+ return i === 0 ? `<a:moveTo><a:pt x="${px}" y="${py}"/></a:moveTo>` : `<a:lnTo><a:pt x="${px}" y="${py}"/></a:lnTo>`;
616
+ }).join("");
617
+ const color = toHexRRGGBB(cmd.stroke);
618
+ return `<p:sp><p:nvSpPr><p:cNvPr id="${id}" name="Link${id}"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="${minX}" y="${minY}"/><a:ext cx="${w}" cy="${h}"/></a:xfrm><a:custGeom><a:avLst/><a:gdLst/><a:ahLst/><a:cxnLst/><a:rect l="0" t="0" r="0" b="0"/><a:pathLst><a:path w="${w}" h="${h}">${pathPts}</a:path></a:pathLst></a:custGeom><a:noFill/><a:ln w="9525"><a:solidFill><a:srgbClr val="${color}"/></a:solidFill></a:ln></p:spPr></p:sp>`;
619
+ }
620
+ function buildSlideShapesXml(commands) {
621
+ let id = 2;
622
+ const shapes = [];
623
+ for (const cmd of commands) {
624
+ if (cmd.kind === "rect") shapes.push(rectShape(id++, cmd));
625
+ else if (cmd.kind === "diamond") shapes.push(diamondShape(id++, cmd));
626
+ else if (cmd.kind === "text") shapes.push(textShape(id++, cmd));
627
+ else if (cmd.kind === "line") {
628
+ const s = lineShape(id++, cmd);
629
+ if (s) shapes.push(s);
630
+ }
631
+ }
632
+ return shapes.join("");
633
+ }
634
+
635
+ // src/pptx/render.ts
636
+ function buildSlideXml(commands) {
637
+ const shapesXml = buildSlideShapesXml(commands);
638
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
639
+ <p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
640
+ <p:cSld>
641
+ <p:spTree>
642
+ <p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
643
+ <p:grpSpPr/>
644
+ ${shapesXml}
645
+ </p:spTree>
646
+ </p:cSld>
647
+ <p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr>
648
+ </p:sld>`;
649
+ }
650
+
651
+ // src/pptx/parts.ts
652
+ function contentTypesXml(slideCount) {
653
+ const slideOverrides = Array.from(
654
+ { length: slideCount },
655
+ (_, i) => `<Override PartName="/ppt/slides/slide${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>`
656
+ ).join("");
657
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
658
+ <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
659
+ <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
660
+ <Default Extension="xml" ContentType="application/xml"/>
661
+ <Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>
662
+ <Override PartName="/ppt/slideMasters/slideMaster1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"/>
663
+ <Override PartName="/ppt/slideLayouts/slideLayout1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"/>
664
+ <Override PartName="/ppt/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>
665
+ ${slideOverrides}
666
+ </Types>`;
667
+ }
668
+ function rootRelsXml() {
669
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
670
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
671
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/>
672
+ </Relationships>`;
673
+ }
674
+ function presentationXml(slideCount, widthEmu, heightEmu) {
675
+ const sldIds = Array.from(
676
+ { length: slideCount },
677
+ (_, i) => `<p:sldId id="${256 + i}" r:id="rId${i + 2}"/>`
678
+ ).join("");
679
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
680
+ <p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
681
+ <p:sldMasterIdLst><p:sldMasterId id="2147483648" r:id="rId1"/></p:sldMasterIdLst>
682
+ <p:sldIdLst>${sldIds}</p:sldIdLst>
683
+ <p:sldSz cx="${Math.round(widthEmu)}" cy="${Math.round(heightEmu)}"/>
684
+ <p:notesSz cx="${Math.round(heightEmu)}" cy="${Math.round(widthEmu)}"/>
685
+ </p:presentation>`;
686
+ }
687
+ function presentationRelsXml(slideCount) {
688
+ const slideRels = Array.from(
689
+ { length: slideCount },
690
+ (_, i) => `<Relationship Id="rId${i + 2}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide${i + 1}.xml"/>`
691
+ ).join("");
692
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
693
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
694
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="slideMasters/slideMaster1.xml"/>
695
+ ${slideRels}
696
+ </Relationships>`;
697
+ }
698
+ function slideMasterXml() {
699
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
700
+ <p:sldMaster xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
701
+ <p:cSld>
702
+ <p:bg><p:bgRef idx="1001"><a:schemeClr val="bg1"/></p:bgRef></p:bg>
703
+ <p:spTree>
704
+ <p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
705
+ <p:grpSpPr/>
706
+ </p:spTree>
707
+ </p:cSld>
708
+ <p:clrMap bg1="lt1" tx1="dk1" bg2="lt2" tx2="dk2" accent1="accent1" accent2="accent2" accent3="accent3" accent4="accent4" accent5="accent5" accent6="accent6" hlink="hlink" folHlink="folHlink"/>
709
+ <p:sldLayoutIdLst><p:sldLayoutId id="2147483649" r:id="rId1"/></p:sldLayoutIdLst>
710
+ </p:sldMaster>`;
711
+ }
712
+ function slideMasterRelsXml() {
713
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
714
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
715
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
716
+ <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="../theme/theme1.xml"/>
717
+ </Relationships>`;
718
+ }
719
+ function slideLayoutXml() {
720
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
721
+ <p:sldLayout xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" type="blank" preserve="1">
722
+ <p:cSld name="Blank">
723
+ <p:spTree>
724
+ <p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
725
+ <p:grpSpPr/>
726
+ </p:spTree>
727
+ </p:cSld>
728
+ <p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr>
729
+ </p:sldLayout>`;
730
+ }
731
+ function slideLayoutRelsXml() {
732
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
733
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
734
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="../slideMasters/slideMaster1.xml"/>
735
+ </Relationships>`;
736
+ }
737
+ function themeXml() {
738
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
739
+ <a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="GanttloomExportTheme">
740
+ <a:themeElements>
741
+ <a:clrScheme name="Gantt">
742
+ <a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1>
743
+ <a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1>
744
+ <a:dk2><a:srgbClr val="44546A"/></a:dk2>
745
+ <a:lt2><a:srgbClr val="E7E6E6"/></a:lt2>
746
+ <a:accent1><a:srgbClr val="4472C4"/></a:accent1>
747
+ <a:accent2><a:srgbClr val="ED7D31"/></a:accent2>
748
+ <a:accent3><a:srgbClr val="A5A5A5"/></a:accent3>
749
+ <a:accent4><a:srgbClr val="FFC000"/></a:accent4>
750
+ <a:accent5><a:srgbClr val="5B9BD5"/></a:accent5>
751
+ <a:accent6><a:srgbClr val="70AD47"/></a:accent6>
752
+ <a:hlink><a:srgbClr val="0563C1"/></a:hlink>
753
+ <a:folHlink><a:srgbClr val="954F72"/></a:folHlink>
754
+ </a:clrScheme>
755
+ <a:fontScheme name="Gantt">
756
+ <a:majorFont><a:latin typeface="Calibri"/><a:ea typeface=""/><a:cs typeface=""/></a:majorFont>
757
+ <a:minorFont><a:latin typeface="Calibri"/><a:ea typeface=""/><a:cs typeface=""/></a:minorFont>
758
+ </a:fontScheme>
759
+ <a:fmtScheme name="Gantt">
760
+ <a:fillStyleLst>
761
+ <a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
762
+ <a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
763
+ <a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
764
+ </a:fillStyleLst>
765
+ <a:lnStyleLst>
766
+ <a:ln w="6350"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:ln>
767
+ <a:ln w="12700"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:ln>
768
+ <a:ln w="19050"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:ln>
769
+ </a:lnStyleLst>
770
+ <a:effectStyleLst>
771
+ <a:effectStyle><a:effectLst/></a:effectStyle>
772
+ <a:effectStyle><a:effectLst/></a:effectStyle>
773
+ <a:effectStyle><a:effectLst/></a:effectStyle>
774
+ </a:effectStyleLst>
775
+ <a:bgFillStyleLst>
776
+ <a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
777
+ <a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
778
+ <a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
779
+ </a:bgFillStyleLst>
780
+ </a:fmtScheme>
781
+ </a:themeElements>
782
+ </a:theme>`;
783
+ }
784
+ function slideRelsXml() {
785
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
786
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
787
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
788
+ </Relationships>`;
789
+ }
790
+
791
+ // src/pptx/index.ts
792
+ var EMU_PER_PT2 = 12700;
793
+ var encoder2 = new TextEncoder();
794
+ function textEntry(name, xml) {
795
+ return { name, data: encoder2.encode(xml) };
796
+ }
797
+ function renderToPPTX(model, tasks, options = {}) {
798
+ const layout = computePagination(model, options);
799
+ const widthEmu = layout.pageWidthPt * EMU_PER_PT2;
800
+ const heightEmu = layout.pageHeightPt * EMU_PER_PT2;
801
+ const slideXmls = layout.pages.map((page) => {
802
+ const content = buildPageContent(model, tasks, layout, page);
803
+ return buildSlideXml(content.commands);
804
+ });
805
+ const entries = [
806
+ textEntry("[Content_Types].xml", contentTypesXml(slideXmls.length)),
807
+ textEntry("_rels/.rels", rootRelsXml()),
808
+ textEntry("ppt/presentation.xml", presentationXml(slideXmls.length, widthEmu, heightEmu)),
809
+ textEntry("ppt/_rels/presentation.xml.rels", presentationRelsXml(slideXmls.length)),
810
+ textEntry("ppt/slideMasters/slideMaster1.xml", slideMasterXml()),
811
+ textEntry("ppt/slideMasters/_rels/slideMaster1.xml.rels", slideMasterRelsXml()),
812
+ textEntry("ppt/slideLayouts/slideLayout1.xml", slideLayoutXml()),
813
+ textEntry("ppt/slideLayouts/_rels/slideLayout1.xml.rels", slideLayoutRelsXml()),
814
+ textEntry("ppt/theme/theme1.xml", themeXml())
815
+ ];
816
+ slideXmls.forEach((xml, i) => {
817
+ entries.push(textEntry(`ppt/slides/slide${i + 1}.xml`, xml));
818
+ entries.push(textEntry(`ppt/slides/_rels/slide${i + 1}.xml.rels`, slideRelsXml()));
819
+ });
820
+ return buildZip(entries);
821
+ }
822
+ // Annotate the CommonJS export names for ESM import in node:
823
+ 0 && (module.exports = {
824
+ computePagination,
825
+ renderToPPTX
826
+ });
827
+ //# sourceMappingURL=pptx.cjs.map