@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/LICENSE +21 -0
- package/README.md +109 -0
- package/dist/chunk-JB7HLLAI.js +113 -0
- package/dist/chunk-JB7HLLAI.js.map +1 -0
- package/dist/chunk-PXURNZ6Y.js +272 -0
- package/dist/chunk-PXURNZ6Y.js.map +1 -0
- package/dist/chunk-QPE3ABUG.js +229 -0
- package/dist/chunk-QPE3ABUG.js.map +1 -0
- package/dist/chunk-TYNGUB4V.js +434 -0
- package/dist/chunk-TYNGUB4V.js.map +1 -0
- package/dist/chunk-X5RULSWH.js +125 -0
- package/dist/chunk-X5RULSWH.js.map +1 -0
- package/dist/index.cjs +1167 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/paginate-Z2RycLzz.d.cts +73 -0
- package/dist/paginate-Z2RycLzz.d.ts +73 -0
- package/dist/pdf.cjs +671 -0
- package/dist/pdf.cjs.map +1 -0
- package/dist/pdf.d.cts +13 -0
- package/dist/pdf.d.ts +13 -0
- package/dist/pdf.js +11 -0
- package/dist/pdf.js.map +1 -0
- package/dist/pptx.cjs +827 -0
- package/dist/pptx.cjs.map +1 -0
- package/dist/pptx.d.cts +15 -0
- package/dist/pptx.d.ts +15 -0
- package/dist/pptx.js +12 -0
- package/dist/pptx.js.map +1 -0
- package/dist/xlsx.cjs +256 -0
- package/dist/xlsx.cjs.map +1 -0
- package/dist/xlsx.d.cts +22 -0
- package/dist/xlsx.d.ts +22 -0
- package/dist/xlsx.js +8 -0
- package/dist/xlsx.js.map +1 -0
- package/package.json +69 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1167 @@
|
|
|
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/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
computePagination: () => computePagination,
|
|
24
|
+
renderToPDF: () => renderToPDF,
|
|
25
|
+
renderToPPTX: () => renderToPPTX,
|
|
26
|
+
renderToXLSX: () => renderToXLSX
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/paginate.ts
|
|
31
|
+
var PAGE_SIZES_PT = {
|
|
32
|
+
// Values are the portrait (short-edge width x long-edge height) dimensions
|
|
33
|
+
// in PDF/typographic points (72pt = 1in).
|
|
34
|
+
A4: { width: 595.28, height: 841.89 },
|
|
35
|
+
Letter: { width: 612, height: 792 },
|
|
36
|
+
A3: { width: 841.89, height: 1190.55 }
|
|
37
|
+
};
|
|
38
|
+
var DEFAULT_MARGIN_PT = 36;
|
|
39
|
+
var DEFAULT_SCALE = 0.75;
|
|
40
|
+
var MIN_TIMELINE_WIDTH_PT = 50;
|
|
41
|
+
var MIN_ROWS_HEIGHT_PT = 20;
|
|
42
|
+
function resolvePageSize(opt) {
|
|
43
|
+
if (opt?.custom) {
|
|
44
|
+
return { width: opt.custom.widthPt, height: opt.custom.heightPt };
|
|
45
|
+
}
|
|
46
|
+
const name = opt?.name ?? "A4";
|
|
47
|
+
const base = PAGE_SIZES_PT[name];
|
|
48
|
+
const orientation = opt?.orientation ?? "landscape";
|
|
49
|
+
const short = Math.min(base.width, base.height);
|
|
50
|
+
const long = Math.max(base.width, base.height);
|
|
51
|
+
return orientation === "landscape" ? { width: long, height: short } : { width: short, height: long };
|
|
52
|
+
}
|
|
53
|
+
function defaultGridPanelWidthPx(model) {
|
|
54
|
+
const cols = model.columns ?? [];
|
|
55
|
+
if (cols.length === 0) return 160;
|
|
56
|
+
return cols.reduce((sum, c) => sum + (c.width ?? 120), 0);
|
|
57
|
+
}
|
|
58
|
+
function computePagination(model, options = {}) {
|
|
59
|
+
const { width: pageWidthPt, height: pageHeightPt } = resolvePageSize(options.pageSize);
|
|
60
|
+
const margins = {
|
|
61
|
+
top: options.margins?.top ?? DEFAULT_MARGIN_PT,
|
|
62
|
+
right: options.margins?.right ?? DEFAULT_MARGIN_PT,
|
|
63
|
+
bottom: options.margins?.bottom ?? DEFAULT_MARGIN_PT,
|
|
64
|
+
left: options.margins?.left ?? DEFAULT_MARGIN_PT
|
|
65
|
+
};
|
|
66
|
+
const scale = options.scale ?? DEFAULT_SCALE;
|
|
67
|
+
const gridPanelWidthPx = options.gridPanelWidthPx ?? defaultGridPanelWidthPx(model);
|
|
68
|
+
const gridPanelWidthPt = gridPanelWidthPx * scale;
|
|
69
|
+
const headerHeightPx = model.headerHeight;
|
|
70
|
+
const headerHeightPt = headerHeightPx * scale;
|
|
71
|
+
const contentWidthPt = pageWidthPt - margins.left - margins.right;
|
|
72
|
+
const contentHeightPt = pageHeightPt - margins.top - margins.bottom;
|
|
73
|
+
const timelineWidthPtPerPage = Math.max(contentWidthPt - gridPanelWidthPt, MIN_TIMELINE_WIDTH_PT);
|
|
74
|
+
const rowsHeightPtPerPage = Math.max(contentHeightPt - headerHeightPt, MIN_ROWS_HEIGHT_PT);
|
|
75
|
+
const timelineWidthPxPerPage = timelineWidthPtPerPage / scale;
|
|
76
|
+
const rowsHeightPxPerPage = rowsHeightPtPerPage / scale;
|
|
77
|
+
const totalHeightPx = Math.max(model.height, 1);
|
|
78
|
+
const totalWidthPx = Math.max(model.width, 1);
|
|
79
|
+
const rowCount = Math.max(1, Math.ceil(totalHeightPx / rowsHeightPxPerPage));
|
|
80
|
+
const colCount = Math.max(1, Math.ceil(totalWidthPx / timelineWidthPxPerPage));
|
|
81
|
+
const rowBands = [];
|
|
82
|
+
for (let i = 0; i < rowCount; i++) {
|
|
83
|
+
rowBands.push({
|
|
84
|
+
start: i * rowsHeightPxPerPage,
|
|
85
|
+
end: Math.min(totalHeightPx, (i + 1) * rowsHeightPxPerPage)
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const colBands = [];
|
|
89
|
+
for (let i = 0; i < colCount; i++) {
|
|
90
|
+
colBands.push({
|
|
91
|
+
start: i * timelineWidthPxPerPage,
|
|
92
|
+
end: Math.min(totalWidthPx, (i + 1) * timelineWidthPxPerPage)
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const pages = [];
|
|
96
|
+
for (let r = 0; r < rowCount; r++) {
|
|
97
|
+
const rowBand = rowBands[r];
|
|
98
|
+
for (let c = 0; c < colCount; c++) {
|
|
99
|
+
const colBand = colBands[c];
|
|
100
|
+
pages.push({
|
|
101
|
+
rowBand: r,
|
|
102
|
+
colBand: c,
|
|
103
|
+
rowStartY: rowBand.start,
|
|
104
|
+
rowEndY: rowBand.end,
|
|
105
|
+
colStartX: colBand.start,
|
|
106
|
+
colEndX: colBand.end
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
pageWidthPt,
|
|
112
|
+
pageHeightPt,
|
|
113
|
+
margins,
|
|
114
|
+
scale,
|
|
115
|
+
gridPanelWidthPx,
|
|
116
|
+
gridPanelWidthPt,
|
|
117
|
+
headerHeightPx,
|
|
118
|
+
headerHeightPt,
|
|
119
|
+
timelineWidthPxPerPage,
|
|
120
|
+
rowsHeightPxPerPage,
|
|
121
|
+
rowBands,
|
|
122
|
+
colBands,
|
|
123
|
+
pages,
|
|
124
|
+
rowCount,
|
|
125
|
+
colCount
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function clipSegment(x0, y0, x1, y1, rect) {
|
|
129
|
+
let t0 = 0;
|
|
130
|
+
let t1 = 1;
|
|
131
|
+
const dx = x1 - x0;
|
|
132
|
+
const dy = y1 - y0;
|
|
133
|
+
const checks = [
|
|
134
|
+
[-dx, x0 - rect.x0],
|
|
135
|
+
[dx, rect.x1 - x0],
|
|
136
|
+
[-dy, y0 - rect.y0],
|
|
137
|
+
[dy, rect.y1 - y0]
|
|
138
|
+
];
|
|
139
|
+
for (const [p, q] of checks) {
|
|
140
|
+
if (p === 0) {
|
|
141
|
+
if (q < 0) return null;
|
|
142
|
+
} else {
|
|
143
|
+
const r = q / p;
|
|
144
|
+
if (p < 0) {
|
|
145
|
+
if (r > t1) return null;
|
|
146
|
+
if (r > t0) t0 = r;
|
|
147
|
+
} else {
|
|
148
|
+
if (r < t0) return null;
|
|
149
|
+
if (r < t1) t1 = r;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return { x0: x0 + t0 * dx, y0: y0 + t0 * dy, x1: x0 + t1 * dx, y1: y0 + t1 * dy };
|
|
154
|
+
}
|
|
155
|
+
function clipRect(x, y, w, h, rect) {
|
|
156
|
+
const x0 = Math.max(x, rect.x0);
|
|
157
|
+
const y0 = Math.max(y, rect.y0);
|
|
158
|
+
const x1 = Math.min(x + w, rect.x1);
|
|
159
|
+
const y1 = Math.min(y + h, rect.y1);
|
|
160
|
+
if (x1 <= x0 || y1 <= y0) return null;
|
|
161
|
+
return { x: x0, y: y0, w: x1 - x0, h: y1 - y0 };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/geom.ts
|
|
165
|
+
function computeLinkPolyline(fromBar, toBar) {
|
|
166
|
+
const x0 = fromBar.x + fromBar.width;
|
|
167
|
+
const y0 = fromBar.y + fromBar.height / 2;
|
|
168
|
+
const x1 = toBar.x;
|
|
169
|
+
const y1 = toBar.y + toBar.height / 2;
|
|
170
|
+
if (Math.abs(y1 - y0) < 0.01) {
|
|
171
|
+
return [{ x: x0, y: y0 }, { x: x1, y: y1 }];
|
|
172
|
+
}
|
|
173
|
+
const midX = x0 + (x1 - x0) / 2;
|
|
174
|
+
return [
|
|
175
|
+
{ x: x0, y: y0 },
|
|
176
|
+
{ x: midX, y: y0 },
|
|
177
|
+
{ x: midX, y: y1 },
|
|
178
|
+
{ x: x1, y: y1 }
|
|
179
|
+
];
|
|
180
|
+
}
|
|
181
|
+
function clipPolylineToRect(points, rect) {
|
|
182
|
+
const segments = [];
|
|
183
|
+
let current = [];
|
|
184
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
185
|
+
const a = points[i];
|
|
186
|
+
const b = points[i + 1];
|
|
187
|
+
const clipped = clipSegment(a.x, a.y, b.x, b.y, rect);
|
|
188
|
+
if (!clipped) {
|
|
189
|
+
if (current.length > 1) segments.push(current);
|
|
190
|
+
current = [];
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (current.length === 0) {
|
|
194
|
+
current.push({ x: clipped.x0, y: clipped.y0 });
|
|
195
|
+
} else {
|
|
196
|
+
const last = current[current.length - 1];
|
|
197
|
+
if (Math.abs(last.x - clipped.x0) > 0.01 || Math.abs(last.y - clipped.y0) > 0.01) {
|
|
198
|
+
if (current.length > 1) segments.push(current);
|
|
199
|
+
current = [{ x: clipped.x0, y: clipped.y0 }];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
current.push({ x: clipped.x1, y: clipped.y1 });
|
|
203
|
+
}
|
|
204
|
+
if (current.length > 1) segments.push(current);
|
|
205
|
+
return segments;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/layout.ts
|
|
209
|
+
function columnWidthsPt(model, layout) {
|
|
210
|
+
const columns = model.columns.length > 0 ? model.columns : [{ id: "name", title: "Task" }];
|
|
211
|
+
const naturalTotal = columns.reduce((sum, c) => sum + (c.width ?? 120), 0) || 1;
|
|
212
|
+
return columns.map((column) => ({
|
|
213
|
+
column,
|
|
214
|
+
widthPt: (column.width ?? 120) / naturalTotal * layout.gridPanelWidthPt
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
function cellText(column, task, depth) {
|
|
218
|
+
if (!task) return "";
|
|
219
|
+
if (column.accessor) {
|
|
220
|
+
const v = column.accessor(task);
|
|
221
|
+
return v === null || v === void 0 ? "" : String(v);
|
|
222
|
+
}
|
|
223
|
+
const indent = " ".repeat(Math.max(0, depth));
|
|
224
|
+
return `${indent}${task.name}`;
|
|
225
|
+
}
|
|
226
|
+
function buildPageContent(model, tasks, layout, page) {
|
|
227
|
+
const { scale, gridPanelWidthPt, headerHeightPt, pageWidthPt, pageHeightPt, margins } = layout;
|
|
228
|
+
const theme = model.theme;
|
|
229
|
+
const tasksById = new Map(tasks.map((t) => [t.id, t]));
|
|
230
|
+
const barsByTaskId = new Map(model.bars.map((b) => [b.taskId, b]));
|
|
231
|
+
const rowsByTaskId = new Map(model.rows.map((r) => [r.taskId, r]));
|
|
232
|
+
const commands = [];
|
|
233
|
+
const contentX0 = margins.left;
|
|
234
|
+
const contentY0 = margins.top;
|
|
235
|
+
const timelineRect = {
|
|
236
|
+
x0: page.colStartX,
|
|
237
|
+
y0: page.rowStartY,
|
|
238
|
+
x1: page.colEndX,
|
|
239
|
+
y1: page.rowEndY
|
|
240
|
+
};
|
|
241
|
+
commands.push({ kind: "rect", x: 0, y: 0, w: pageWidthPt, h: pageHeightPt, fill: theme.backgroundColor });
|
|
242
|
+
const cols = columnWidthsPt(model, layout);
|
|
243
|
+
let colCursor = contentX0;
|
|
244
|
+
for (const { column, widthPt } of cols) {
|
|
245
|
+
commands.push({
|
|
246
|
+
kind: "text",
|
|
247
|
+
x: colCursor + 4,
|
|
248
|
+
y: contentY0 + headerHeightPt / 2 - 5,
|
|
249
|
+
w: widthPt - 8,
|
|
250
|
+
text: column.title,
|
|
251
|
+
size: 9,
|
|
252
|
+
color: theme.textColor,
|
|
253
|
+
bold: true,
|
|
254
|
+
align: column.align ?? "left"
|
|
255
|
+
});
|
|
256
|
+
colCursor += widthPt;
|
|
257
|
+
}
|
|
258
|
+
commands.push({
|
|
259
|
+
kind: "rect",
|
|
260
|
+
x: contentX0,
|
|
261
|
+
y: contentY0,
|
|
262
|
+
w: gridPanelWidthPt + (page.colEndX - page.colStartX) * scale,
|
|
263
|
+
h: headerHeightPt,
|
|
264
|
+
stroke: theme.gridColor
|
|
265
|
+
});
|
|
266
|
+
const timelineOriginX = contentX0 + gridPanelWidthPt;
|
|
267
|
+
for (const tick of model.ticks) {
|
|
268
|
+
if (tick.x < page.colStartX || tick.x > page.colEndX) continue;
|
|
269
|
+
const px = timelineOriginX + (tick.x - page.colStartX) * scale;
|
|
270
|
+
if (tick.isWeekend) {
|
|
271
|
+
}
|
|
272
|
+
commands.push({
|
|
273
|
+
kind: "line",
|
|
274
|
+
points: [
|
|
275
|
+
{ x: px, y: contentY0 },
|
|
276
|
+
{ x: px, y: pageHeightPt - margins.bottom }
|
|
277
|
+
],
|
|
278
|
+
stroke: tick.isToday ? theme.todayColor : theme.gridColor
|
|
279
|
+
});
|
|
280
|
+
commands.push({
|
|
281
|
+
kind: "text",
|
|
282
|
+
x: px + 2,
|
|
283
|
+
y: contentY0 + headerHeightPt / 2 - 5,
|
|
284
|
+
text: tick.label,
|
|
285
|
+
size: 7,
|
|
286
|
+
color: theme.textColor,
|
|
287
|
+
align: "left"
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
const rowTop = (y) => contentY0 + headerHeightPt + (y - page.rowStartY) * scale;
|
|
291
|
+
for (const row of model.rows) {
|
|
292
|
+
if (row.y + row.height <= page.rowStartY || row.y >= page.rowEndY) continue;
|
|
293
|
+
const clippedRowY0 = Math.max(row.y, page.rowStartY);
|
|
294
|
+
const clippedRowY1 = Math.min(row.y + row.height, page.rowEndY);
|
|
295
|
+
const rowYTop = rowTop(clippedRowY0);
|
|
296
|
+
const rowHPt = (clippedRowY1 - clippedRowY0) * scale;
|
|
297
|
+
const task = tasksById.get(row.taskId);
|
|
298
|
+
let cx = contentX0;
|
|
299
|
+
for (const { column, widthPt } of cols) {
|
|
300
|
+
commands.push({
|
|
301
|
+
kind: "text",
|
|
302
|
+
x: cx + 4,
|
|
303
|
+
y: rowYTop + rowHPt / 2 - 4,
|
|
304
|
+
w: widthPt - 8,
|
|
305
|
+
text: cellText(column, task, row.depth),
|
|
306
|
+
size: 8,
|
|
307
|
+
color: theme.textColor,
|
|
308
|
+
align: column.align ?? "left"
|
|
309
|
+
});
|
|
310
|
+
cx += widthPt;
|
|
311
|
+
}
|
|
312
|
+
commands.push({
|
|
313
|
+
kind: "rect",
|
|
314
|
+
x: contentX0,
|
|
315
|
+
y: rowYTop,
|
|
316
|
+
w: gridPanelWidthPt + (page.colEndX - page.colStartX) * scale,
|
|
317
|
+
h: rowHPt,
|
|
318
|
+
stroke: theme.gridColor
|
|
319
|
+
});
|
|
320
|
+
const bar = barsByTaskId.get(row.taskId);
|
|
321
|
+
if (bar) {
|
|
322
|
+
const clippedBar = clipRect(bar.x, bar.y, bar.width, bar.height, timelineRect);
|
|
323
|
+
if (clippedBar) {
|
|
324
|
+
const bx = timelineOriginX + (clippedBar.x - page.colStartX) * scale;
|
|
325
|
+
const by = rowTop(clippedBar.y);
|
|
326
|
+
const bw = clippedBar.w * scale;
|
|
327
|
+
const bh = clippedBar.h * scale;
|
|
328
|
+
if (bar.baseline) {
|
|
329
|
+
const clippedBaseline = clipRect(bar.baseline.x, bar.y, bar.baseline.width, bar.height, timelineRect);
|
|
330
|
+
if (clippedBaseline) {
|
|
331
|
+
commands.push({
|
|
332
|
+
kind: "rect",
|
|
333
|
+
x: timelineOriginX + (clippedBaseline.x - page.colStartX) * scale,
|
|
334
|
+
y: rowTop(clippedBaseline.y),
|
|
335
|
+
w: clippedBaseline.w * scale,
|
|
336
|
+
h: clippedBaseline.h * scale,
|
|
337
|
+
fill: theme.baselineColor
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (bar.isMilestone) {
|
|
342
|
+
const r = bh / 2;
|
|
343
|
+
commands.push({
|
|
344
|
+
kind: "diamond",
|
|
345
|
+
cx: bx + r,
|
|
346
|
+
cy: by + r,
|
|
347
|
+
r,
|
|
348
|
+
fill: bar.isCritical ? theme.criticalColor : bar.color,
|
|
349
|
+
stroke: theme.textColor
|
|
350
|
+
});
|
|
351
|
+
if (bar.label) {
|
|
352
|
+
commands.push({
|
|
353
|
+
kind: "text",
|
|
354
|
+
x: bx + bh + 4,
|
|
355
|
+
y: by + bh / 2 - 4,
|
|
356
|
+
text: bar.label,
|
|
357
|
+
size: 8,
|
|
358
|
+
color: theme.textColor
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
} else {
|
|
362
|
+
commands.push({
|
|
363
|
+
kind: "rect",
|
|
364
|
+
x: bx,
|
|
365
|
+
y: by,
|
|
366
|
+
w: bw,
|
|
367
|
+
h: bh,
|
|
368
|
+
fill: bar.isCritical ? theme.criticalColor : bar.color
|
|
369
|
+
});
|
|
370
|
+
if (bar.progressWidth > 0) {
|
|
371
|
+
const clippedProgress = clipRect(bar.x, bar.y, bar.progressWidth, bar.height, timelineRect);
|
|
372
|
+
if (clippedProgress) {
|
|
373
|
+
commands.push({
|
|
374
|
+
kind: "rect",
|
|
375
|
+
x: timelineOriginX + (clippedProgress.x - page.colStartX) * scale,
|
|
376
|
+
y: rowTop(clippedProgress.y),
|
|
377
|
+
w: clippedProgress.w * scale,
|
|
378
|
+
h: clippedProgress.h * scale,
|
|
379
|
+
fill: bar.progressColor
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
commands.push({
|
|
384
|
+
kind: "text",
|
|
385
|
+
x: bx + 3,
|
|
386
|
+
y: by + bh / 2 - 4,
|
|
387
|
+
text: bar.label,
|
|
388
|
+
size: 8,
|
|
389
|
+
color: theme.textColor
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
for (const link of model.links) {
|
|
396
|
+
const fromBar = barsByTaskId.get(link.fromId);
|
|
397
|
+
const toBar = barsByTaskId.get(link.toId);
|
|
398
|
+
if (!fromBar || !toBar) continue;
|
|
399
|
+
const fromRow = rowsByTaskId.get(link.fromId);
|
|
400
|
+
const toRow = rowsByTaskId.get(link.toId);
|
|
401
|
+
if (!fromRow || !toRow) continue;
|
|
402
|
+
const polyline = computeLinkPolyline(fromBar, toBar);
|
|
403
|
+
const pieces = clipPolylineToRect(polyline, timelineRect);
|
|
404
|
+
for (const piece of pieces) {
|
|
405
|
+
commands.push({
|
|
406
|
+
kind: "line",
|
|
407
|
+
points: piece.map((p) => ({
|
|
408
|
+
x: timelineOriginX + (p.x - page.colStartX) * scale,
|
|
409
|
+
y: rowTop(p.y)
|
|
410
|
+
})),
|
|
411
|
+
stroke: link.isCritical ? theme.criticalColor : theme.linkColor
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return { widthPt: pageWidthPt, heightPt: pageHeightPt, commands };
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// src/pdf/writer.ts
|
|
419
|
+
var encoder = new TextEncoder();
|
|
420
|
+
function encodeLatin1(str) {
|
|
421
|
+
const out = new Uint8Array(str.length);
|
|
422
|
+
for (let i = 0; i < str.length; i++) {
|
|
423
|
+
const code = str.charCodeAt(i);
|
|
424
|
+
out[i] = code <= 255 ? code : 63;
|
|
425
|
+
}
|
|
426
|
+
return out;
|
|
427
|
+
}
|
|
428
|
+
function escapePdfText(str) {
|
|
429
|
+
return str.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
|
|
430
|
+
}
|
|
431
|
+
function xrefLine(offset, generation, type) {
|
|
432
|
+
const line = `${String(offset).padStart(10, "0")} ${String(generation).padStart(5, "0")} ${type}
|
|
433
|
+
`;
|
|
434
|
+
return line;
|
|
435
|
+
}
|
|
436
|
+
var PdfBuilder = class {
|
|
437
|
+
constructor() {
|
|
438
|
+
this.parts = [];
|
|
439
|
+
this.length = 0;
|
|
440
|
+
this.offsetsById = /* @__PURE__ */ new Map();
|
|
441
|
+
this.nextId = 1;
|
|
442
|
+
}
|
|
443
|
+
reserveId() {
|
|
444
|
+
return this.nextId++;
|
|
445
|
+
}
|
|
446
|
+
raw(bytes) {
|
|
447
|
+
this.parts.push(bytes);
|
|
448
|
+
this.length += bytes.length;
|
|
449
|
+
}
|
|
450
|
+
text(s) {
|
|
451
|
+
this.raw(encoder.encode(s));
|
|
452
|
+
}
|
|
453
|
+
writeHeader() {
|
|
454
|
+
this.text("%PDF-1.4\n");
|
|
455
|
+
this.raw(new Uint8Array([37, 226, 227, 207, 211, 10]));
|
|
456
|
+
}
|
|
457
|
+
/** Append a simple (non-stream) indirect object, e.g. a dictionary or array. */
|
|
458
|
+
addObject(id, body) {
|
|
459
|
+
this.offsetsById.set(id, this.length);
|
|
460
|
+
this.text(`${id} 0 obj
|
|
461
|
+
${body}
|
|
462
|
+
endobj
|
|
463
|
+
`);
|
|
464
|
+
}
|
|
465
|
+
/** Append a stream object; `dictExtra` is the dictionary content besides `/Length`. */
|
|
466
|
+
addStreamObject(id, dictExtra, data) {
|
|
467
|
+
this.offsetsById.set(id, this.length);
|
|
468
|
+
this.text(`${id} 0 obj
|
|
469
|
+
<< ${dictExtra} /Length ${data.length} >>
|
|
470
|
+
stream
|
|
471
|
+
`);
|
|
472
|
+
this.raw(data);
|
|
473
|
+
this.text("\nendstream\nendobj\n");
|
|
474
|
+
}
|
|
475
|
+
/** Finalize the file: write the xref table, trailer, and startxref/%%EOF footer. */
|
|
476
|
+
build(rootId) {
|
|
477
|
+
const xrefOffset = this.length;
|
|
478
|
+
const maxId = this.nextId - 1;
|
|
479
|
+
let xref = `xref
|
|
480
|
+
0 ${maxId + 1}
|
|
481
|
+
`;
|
|
482
|
+
xref += xrefLine(0, 65535, "f");
|
|
483
|
+
for (let id = 1; id <= maxId; id++) {
|
|
484
|
+
const off = this.offsetsById.get(id);
|
|
485
|
+
if (off === void 0) {
|
|
486
|
+
throw new Error(`PdfBuilder: object ${id} was reserved but never written`);
|
|
487
|
+
}
|
|
488
|
+
xref += xrefLine(off, 0, "n");
|
|
489
|
+
}
|
|
490
|
+
this.text(xref);
|
|
491
|
+
this.text(`trailer
|
|
492
|
+
<< /Size ${maxId + 1} /Root ${rootId} 0 R >>
|
|
493
|
+
startxref
|
|
494
|
+
${xrefOffset}
|
|
495
|
+
%%EOF`);
|
|
496
|
+
const total = new Uint8Array(this.length);
|
|
497
|
+
let o = 0;
|
|
498
|
+
for (const p of this.parts) {
|
|
499
|
+
total.set(p, o);
|
|
500
|
+
o += p.length;
|
|
501
|
+
}
|
|
502
|
+
return total;
|
|
503
|
+
}
|
|
504
|
+
/** Byte offset an object was (or will be) written at — exposed for tests. */
|
|
505
|
+
getOffset(id) {
|
|
506
|
+
return this.offsetsById.get(id);
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
// src/color.ts
|
|
511
|
+
function parseColor(input) {
|
|
512
|
+
if (!input) return [0, 0, 0];
|
|
513
|
+
const s = input.trim();
|
|
514
|
+
const hex = s.match(/^#?([0-9a-fA-F]{6})$/) ?? s.match(/^#?([0-9a-fA-F]{3})$/);
|
|
515
|
+
if (hex) {
|
|
516
|
+
let h = hex[1];
|
|
517
|
+
if (h.length === 3) {
|
|
518
|
+
h = h.split("").map((c) => c + c).join("");
|
|
519
|
+
}
|
|
520
|
+
const r = parseInt(h.slice(0, 2), 16) / 255;
|
|
521
|
+
const g = parseInt(h.slice(2, 4), 16) / 255;
|
|
522
|
+
const b = parseInt(h.slice(4, 6), 16) / 255;
|
|
523
|
+
return [r, g, b];
|
|
524
|
+
}
|
|
525
|
+
const rgb = s.match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i);
|
|
526
|
+
if (rgb) {
|
|
527
|
+
return [Number(rgb[1]) / 255, Number(rgb[2]) / 255, Number(rgb[3]) / 255];
|
|
528
|
+
}
|
|
529
|
+
const named = {
|
|
530
|
+
black: [0, 0, 0],
|
|
531
|
+
white: [1, 1, 1],
|
|
532
|
+
red: [1, 0, 0],
|
|
533
|
+
green: [0, 0.5, 0],
|
|
534
|
+
blue: [0, 0, 1],
|
|
535
|
+
gray: [0.5, 0.5, 0.5],
|
|
536
|
+
grey: [0.5, 0.5, 0.5],
|
|
537
|
+
transparent: [1, 1, 1]
|
|
538
|
+
};
|
|
539
|
+
return named[s.toLowerCase()] ?? [0, 0, 0];
|
|
540
|
+
}
|
|
541
|
+
function toHexByte(v) {
|
|
542
|
+
return Math.round(Math.max(0, Math.min(1, v)) * 255).toString(16).padStart(2, "0");
|
|
543
|
+
}
|
|
544
|
+
function toHexRRGGBB(input) {
|
|
545
|
+
const [r, g, b] = parseColor(input);
|
|
546
|
+
return `${toHexByte(r)}${toHexByte(g)}${toHexByte(b)}`.toUpperCase();
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// src/pdf/content.ts
|
|
550
|
+
function num(n) {
|
|
551
|
+
const r = Math.round(n * 1e3) / 1e3;
|
|
552
|
+
return Object.is(r, -0) ? "0" : String(r);
|
|
553
|
+
}
|
|
554
|
+
function colorOp(color, op) {
|
|
555
|
+
const [r, g, b] = parseColor(color);
|
|
556
|
+
return `${num(r)} ${num(g)} ${num(b)} ${op}
|
|
557
|
+
`;
|
|
558
|
+
}
|
|
559
|
+
function buildContentStream(commands, pageHeightPt) {
|
|
560
|
+
let s = "q\n";
|
|
561
|
+
for (const cmd of commands) {
|
|
562
|
+
switch (cmd.kind) {
|
|
563
|
+
case "rect": {
|
|
564
|
+
const x = cmd.x;
|
|
565
|
+
const yTop = pageHeightPt - (cmd.y + cmd.h);
|
|
566
|
+
if (cmd.fill) s += colorOp(cmd.fill, "rg");
|
|
567
|
+
if (cmd.stroke) s += colorOp(cmd.stroke, "RG");
|
|
568
|
+
s += `${num(x)} ${num(yTop)} ${num(cmd.w)} ${num(cmd.h)} re
|
|
569
|
+
`;
|
|
570
|
+
if (cmd.fill && cmd.stroke) s += "B\n";
|
|
571
|
+
else if (cmd.fill) s += "f\n";
|
|
572
|
+
else if (cmd.stroke) s += "S\n";
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
case "diamond": {
|
|
576
|
+
const pts = [
|
|
577
|
+
{ x: cmd.cx, y: cmd.cy - cmd.r },
|
|
578
|
+
{ x: cmd.cx + cmd.r, y: cmd.cy },
|
|
579
|
+
{ x: cmd.cx, y: cmd.cy + cmd.r },
|
|
580
|
+
{ x: cmd.cx - cmd.r, y: cmd.cy }
|
|
581
|
+
].map((p) => ({ x: p.x, y: pageHeightPt - p.y }));
|
|
582
|
+
if (cmd.fill) s += colorOp(cmd.fill, "rg");
|
|
583
|
+
if (cmd.stroke) s += colorOp(cmd.stroke, "RG");
|
|
584
|
+
s += `${num(pts[0].x)} ${num(pts[0].y)} m
|
|
585
|
+
`;
|
|
586
|
+
for (let i = 1; i < pts.length; i++) s += `${num(pts[i].x)} ${num(pts[i].y)} l
|
|
587
|
+
`;
|
|
588
|
+
s += "h\n";
|
|
589
|
+
if (cmd.fill && cmd.stroke) s += "B\n";
|
|
590
|
+
else if (cmd.fill) s += "f\n";
|
|
591
|
+
else if (cmd.stroke) s += "S\n";
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
case "line": {
|
|
595
|
+
if (cmd.points.length < 2) break;
|
|
596
|
+
s += colorOp(cmd.stroke, "RG");
|
|
597
|
+
const p0 = cmd.points[0];
|
|
598
|
+
s += `${num(p0.x)} ${num(pageHeightPt - p0.y)} m
|
|
599
|
+
`;
|
|
600
|
+
for (let i = 1; i < cmd.points.length; i++) {
|
|
601
|
+
const p = cmd.points[i];
|
|
602
|
+
s += `${num(p.x)} ${num(pageHeightPt - p.y)} l
|
|
603
|
+
`;
|
|
604
|
+
}
|
|
605
|
+
s += "S\n";
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
case "text": {
|
|
609
|
+
if (!cmd.text) break;
|
|
610
|
+
const font = cmd.bold ? "/F2" : "/F1";
|
|
611
|
+
const baselineY = pageHeightPt - (cmd.y + cmd.size * 0.8);
|
|
612
|
+
s += colorOp(cmd.color, "rg");
|
|
613
|
+
s += "BT\n";
|
|
614
|
+
s += `${font} ${num(cmd.size)} Tf
|
|
615
|
+
`;
|
|
616
|
+
s += `${num(cmd.x)} ${num(baselineY)} Td
|
|
617
|
+
`;
|
|
618
|
+
s += `(${escapePdfText(latin1Safe(cmd.text))}) Tj
|
|
619
|
+
`;
|
|
620
|
+
s += "ET\n";
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
s += "Q\n";
|
|
626
|
+
return encodeLatin1(s);
|
|
627
|
+
}
|
|
628
|
+
function latin1Safe(s) {
|
|
629
|
+
let out = "";
|
|
630
|
+
for (const ch of s) {
|
|
631
|
+
const code = ch.codePointAt(0);
|
|
632
|
+
out += code <= 255 ? ch : "?";
|
|
633
|
+
}
|
|
634
|
+
return out;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// src/pdf/index.ts
|
|
638
|
+
function renderToPDF(model, tasks, options = {}) {
|
|
639
|
+
const layout = computePagination(model, options);
|
|
640
|
+
const pdf = new PdfBuilder();
|
|
641
|
+
pdf.writeHeader();
|
|
642
|
+
const catalogId = pdf.reserveId();
|
|
643
|
+
const pagesId = pdf.reserveId();
|
|
644
|
+
const helveticaId = pdf.reserveId();
|
|
645
|
+
const helveticaBoldId = pdf.reserveId();
|
|
646
|
+
pdf.addObject(helveticaId, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
|
|
647
|
+
pdf.addObject(
|
|
648
|
+
helveticaBoldId,
|
|
649
|
+
"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>"
|
|
650
|
+
);
|
|
651
|
+
const pageIds = [];
|
|
652
|
+
for (const page of layout.pages) {
|
|
653
|
+
const content = buildPageContent(model, tasks, layout, page);
|
|
654
|
+
const streamBytes = buildContentStream(content.commands, layout.pageHeightPt);
|
|
655
|
+
const pageId = pdf.reserveId();
|
|
656
|
+
const contentId = pdf.reserveId();
|
|
657
|
+
pdf.addStreamObject(contentId, "", streamBytes);
|
|
658
|
+
pdf.addObject(
|
|
659
|
+
pageId,
|
|
660
|
+
`<< /Type /Page /Parent ${pagesId} 0 R /MediaBox [0 0 ${num2(layout.pageWidthPt)} ${num2(layout.pageHeightPt)}] /Resources << /Font << /F1 ${helveticaId} 0 R /F2 ${helveticaBoldId} 0 R >> >> /Contents ${contentId} 0 R >>`
|
|
661
|
+
);
|
|
662
|
+
pageIds.push(pageId);
|
|
663
|
+
}
|
|
664
|
+
pdf.addObject(
|
|
665
|
+
pagesId,
|
|
666
|
+
`<< /Type /Pages /Kids [${pageIds.map((id) => `${id} 0 R`).join(" ")}] /Count ${pageIds.length} >>`
|
|
667
|
+
);
|
|
668
|
+
pdf.addObject(catalogId, `<< /Type /Catalog /Pages ${pagesId} 0 R >>`);
|
|
669
|
+
return pdf.build(catalogId);
|
|
670
|
+
}
|
|
671
|
+
function num2(n) {
|
|
672
|
+
const r = Math.round(n * 1e3) / 1e3;
|
|
673
|
+
return Object.is(r, -0) ? "0" : String(r);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// src/zip.ts
|
|
677
|
+
var encoder2 = new TextEncoder();
|
|
678
|
+
var CRC_TABLE = (() => {
|
|
679
|
+
const table = new Uint32Array(256);
|
|
680
|
+
for (let n = 0; n < 256; n++) {
|
|
681
|
+
let c = n;
|
|
682
|
+
for (let k = 0; k < 8; k++) {
|
|
683
|
+
c = c & 1 ? (3988292384 ^ c >>> 1) >>> 0 : c >>> 1;
|
|
684
|
+
}
|
|
685
|
+
table[n] = c >>> 0;
|
|
686
|
+
}
|
|
687
|
+
return table;
|
|
688
|
+
})();
|
|
689
|
+
function crc32(data) {
|
|
690
|
+
let crc = 4294967295;
|
|
691
|
+
for (let i = 0; i < data.length; i++) {
|
|
692
|
+
crc = (CRC_TABLE[(crc ^ data[i]) & 255] ^ crc >>> 8) >>> 0;
|
|
693
|
+
}
|
|
694
|
+
return (crc ^ 4294967295) >>> 0;
|
|
695
|
+
}
|
|
696
|
+
var ByteWriter = class {
|
|
697
|
+
constructor() {
|
|
698
|
+
this.chunks = [];
|
|
699
|
+
this.len = 0;
|
|
700
|
+
}
|
|
701
|
+
get length() {
|
|
702
|
+
return this.len;
|
|
703
|
+
}
|
|
704
|
+
push(bytes) {
|
|
705
|
+
this.chunks.push(bytes);
|
|
706
|
+
this.len += bytes.length;
|
|
707
|
+
}
|
|
708
|
+
pushU16(v) {
|
|
709
|
+
this.push(new Uint8Array([v & 255, v >>> 8 & 255]));
|
|
710
|
+
}
|
|
711
|
+
pushU32(v) {
|
|
712
|
+
this.push(new Uint8Array([v & 255, v >>> 8 & 255, v >>> 16 & 255, v >>> 24 & 255]));
|
|
713
|
+
}
|
|
714
|
+
pushStr(s) {
|
|
715
|
+
this.push(encoder2.encode(s));
|
|
716
|
+
}
|
|
717
|
+
toUint8Array() {
|
|
718
|
+
const out = new Uint8Array(this.len);
|
|
719
|
+
let o = 0;
|
|
720
|
+
for (const c of this.chunks) {
|
|
721
|
+
out.set(c, o);
|
|
722
|
+
o += c.length;
|
|
723
|
+
}
|
|
724
|
+
return out;
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
var DOS_TIME = 0;
|
|
728
|
+
var DOS_DATE = 1980 - 1980 << 9;
|
|
729
|
+
function buildZip(entries) {
|
|
730
|
+
const w = new ByteWriter();
|
|
731
|
+
const central = [];
|
|
732
|
+
for (const entry of entries) {
|
|
733
|
+
const nameBytes = encoder2.encode(entry.name);
|
|
734
|
+
const crc = crc32(entry.data);
|
|
735
|
+
const offset = w.length;
|
|
736
|
+
w.pushU32(67324752);
|
|
737
|
+
w.pushU16(20);
|
|
738
|
+
w.pushU16(0);
|
|
739
|
+
w.pushU16(0);
|
|
740
|
+
w.pushU16(DOS_TIME);
|
|
741
|
+
w.pushU16(DOS_DATE);
|
|
742
|
+
w.pushU32(crc);
|
|
743
|
+
w.pushU32(entry.data.length);
|
|
744
|
+
w.pushU32(entry.data.length);
|
|
745
|
+
w.pushU16(nameBytes.length);
|
|
746
|
+
w.pushU16(0);
|
|
747
|
+
w.push(nameBytes);
|
|
748
|
+
w.push(entry.data);
|
|
749
|
+
central.push({ offset, entry, crc });
|
|
750
|
+
}
|
|
751
|
+
const centralStart = w.length;
|
|
752
|
+
for (const { offset, entry, crc } of central) {
|
|
753
|
+
const nameBytes = encoder2.encode(entry.name);
|
|
754
|
+
w.pushU32(33639248);
|
|
755
|
+
w.pushU16(20);
|
|
756
|
+
w.pushU16(20);
|
|
757
|
+
w.pushU16(0);
|
|
758
|
+
w.pushU16(0);
|
|
759
|
+
w.pushU16(DOS_TIME);
|
|
760
|
+
w.pushU16(DOS_DATE);
|
|
761
|
+
w.pushU32(crc);
|
|
762
|
+
w.pushU32(entry.data.length);
|
|
763
|
+
w.pushU32(entry.data.length);
|
|
764
|
+
w.pushU16(nameBytes.length);
|
|
765
|
+
w.pushU16(0);
|
|
766
|
+
w.pushU16(0);
|
|
767
|
+
w.pushU16(0);
|
|
768
|
+
w.pushU16(0);
|
|
769
|
+
w.pushU32(0);
|
|
770
|
+
w.pushU32(offset);
|
|
771
|
+
w.push(nameBytes);
|
|
772
|
+
}
|
|
773
|
+
const centralSize = w.length - centralStart;
|
|
774
|
+
w.pushU32(101010256);
|
|
775
|
+
w.pushU16(0);
|
|
776
|
+
w.pushU16(0);
|
|
777
|
+
w.pushU16(central.length);
|
|
778
|
+
w.pushU16(central.length);
|
|
779
|
+
w.pushU32(centralSize);
|
|
780
|
+
w.pushU32(centralStart);
|
|
781
|
+
w.pushU16(0);
|
|
782
|
+
return w.toUint8Array();
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// src/pptx/xml.ts
|
|
786
|
+
function xmlEscape(input) {
|
|
787
|
+
return input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// src/pptx/shapes.ts
|
|
791
|
+
var EMU_PER_PT = 12700;
|
|
792
|
+
var ptToEmu = (pt) => Math.round(pt * EMU_PER_PT);
|
|
793
|
+
function alignAttr(align) {
|
|
794
|
+
if (align === "center") return ' algn="ctr"';
|
|
795
|
+
if (align === "right") return ' algn="r"';
|
|
796
|
+
return "";
|
|
797
|
+
}
|
|
798
|
+
function rectShape(id, cmd) {
|
|
799
|
+
const x = ptToEmu(cmd.x);
|
|
800
|
+
const y = ptToEmu(cmd.y);
|
|
801
|
+
const cx = Math.max(ptToEmu(cmd.w), 1);
|
|
802
|
+
const cy = Math.max(ptToEmu(cmd.h), 1);
|
|
803
|
+
const fill = cmd.fill ? `<a:solidFill><a:srgbClr val="${toHexRRGGBB(cmd.fill)}"/></a:solidFill>` : `<a:noFill/>`;
|
|
804
|
+
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>`;
|
|
805
|
+
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>`;
|
|
806
|
+
}
|
|
807
|
+
function diamondShape(id, cmd) {
|
|
808
|
+
const x = ptToEmu(cmd.cx - cmd.r);
|
|
809
|
+
const y = ptToEmu(cmd.cy - cmd.r);
|
|
810
|
+
const size = Math.max(ptToEmu(cmd.r * 2), 1);
|
|
811
|
+
const fill = cmd.fill ? `<a:solidFill><a:srgbClr val="${toHexRRGGBB(cmd.fill)}"/></a:solidFill>` : `<a:noFill/>`;
|
|
812
|
+
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>`;
|
|
813
|
+
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>`;
|
|
814
|
+
}
|
|
815
|
+
function textShape(id, cmd) {
|
|
816
|
+
const x = ptToEmu(cmd.x);
|
|
817
|
+
const y = ptToEmu(Math.max(cmd.y - 2, 0));
|
|
818
|
+
const cx = Math.max(ptToEmu(cmd.w ?? 300), 1);
|
|
819
|
+
const cy = Math.max(ptToEmu(cmd.size * 1.6), 1);
|
|
820
|
+
const sizeHundredths = Math.round(cmd.size * 100);
|
|
821
|
+
const color = toHexRRGGBB(cmd.color);
|
|
822
|
+
const bold = cmd.bold ? ' b="1"' : "";
|
|
823
|
+
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>`;
|
|
824
|
+
}
|
|
825
|
+
function lineShape(id, cmd) {
|
|
826
|
+
if (cmd.points.length < 2) return null;
|
|
827
|
+
const xs = cmd.points.map((p) => ptToEmu(p.x));
|
|
828
|
+
const ys = cmd.points.map((p) => ptToEmu(p.y));
|
|
829
|
+
const minX = Math.min(...xs);
|
|
830
|
+
const minY = Math.min(...ys);
|
|
831
|
+
const w = Math.max(Math.max(...xs) - minX, 1);
|
|
832
|
+
const h = Math.max(Math.max(...ys) - minY, 1);
|
|
833
|
+
const pathPts = cmd.points.map((p, i) => {
|
|
834
|
+
const px = ptToEmu(p.x) - minX;
|
|
835
|
+
const py = ptToEmu(p.y) - minY;
|
|
836
|
+
return i === 0 ? `<a:moveTo><a:pt x="${px}" y="${py}"/></a:moveTo>` : `<a:lnTo><a:pt x="${px}" y="${py}"/></a:lnTo>`;
|
|
837
|
+
}).join("");
|
|
838
|
+
const color = toHexRRGGBB(cmd.stroke);
|
|
839
|
+
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>`;
|
|
840
|
+
}
|
|
841
|
+
function buildSlideShapesXml(commands) {
|
|
842
|
+
let id = 2;
|
|
843
|
+
const shapes = [];
|
|
844
|
+
for (const cmd of commands) {
|
|
845
|
+
if (cmd.kind === "rect") shapes.push(rectShape(id++, cmd));
|
|
846
|
+
else if (cmd.kind === "diamond") shapes.push(diamondShape(id++, cmd));
|
|
847
|
+
else if (cmd.kind === "text") shapes.push(textShape(id++, cmd));
|
|
848
|
+
else if (cmd.kind === "line") {
|
|
849
|
+
const s = lineShape(id++, cmd);
|
|
850
|
+
if (s) shapes.push(s);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
return shapes.join("");
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// src/pptx/render.ts
|
|
857
|
+
function buildSlideXml(commands) {
|
|
858
|
+
const shapesXml = buildSlideShapesXml(commands);
|
|
859
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
860
|
+
<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">
|
|
861
|
+
<p:cSld>
|
|
862
|
+
<p:spTree>
|
|
863
|
+
<p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
|
|
864
|
+
<p:grpSpPr/>
|
|
865
|
+
${shapesXml}
|
|
866
|
+
</p:spTree>
|
|
867
|
+
</p:cSld>
|
|
868
|
+
<p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr>
|
|
869
|
+
</p:sld>`;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// src/pptx/parts.ts
|
|
873
|
+
function contentTypesXml(slideCount) {
|
|
874
|
+
const slideOverrides = Array.from(
|
|
875
|
+
{ length: slideCount },
|
|
876
|
+
(_, i) => `<Override PartName="/ppt/slides/slide${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>`
|
|
877
|
+
).join("");
|
|
878
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
879
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
880
|
+
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
881
|
+
<Default Extension="xml" ContentType="application/xml"/>
|
|
882
|
+
<Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>
|
|
883
|
+
<Override PartName="/ppt/slideMasters/slideMaster1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"/>
|
|
884
|
+
<Override PartName="/ppt/slideLayouts/slideLayout1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"/>
|
|
885
|
+
<Override PartName="/ppt/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>
|
|
886
|
+
${slideOverrides}
|
|
887
|
+
</Types>`;
|
|
888
|
+
}
|
|
889
|
+
function rootRelsXml() {
|
|
890
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
891
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
892
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/>
|
|
893
|
+
</Relationships>`;
|
|
894
|
+
}
|
|
895
|
+
function presentationXml(slideCount, widthEmu, heightEmu) {
|
|
896
|
+
const sldIds = Array.from(
|
|
897
|
+
{ length: slideCount },
|
|
898
|
+
(_, i) => `<p:sldId id="${256 + i}" r:id="rId${i + 2}"/>`
|
|
899
|
+
).join("");
|
|
900
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
901
|
+
<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">
|
|
902
|
+
<p:sldMasterIdLst><p:sldMasterId id="2147483648" r:id="rId1"/></p:sldMasterIdLst>
|
|
903
|
+
<p:sldIdLst>${sldIds}</p:sldIdLst>
|
|
904
|
+
<p:sldSz cx="${Math.round(widthEmu)}" cy="${Math.round(heightEmu)}"/>
|
|
905
|
+
<p:notesSz cx="${Math.round(heightEmu)}" cy="${Math.round(widthEmu)}"/>
|
|
906
|
+
</p:presentation>`;
|
|
907
|
+
}
|
|
908
|
+
function presentationRelsXml(slideCount) {
|
|
909
|
+
const slideRels = Array.from(
|
|
910
|
+
{ length: slideCount },
|
|
911
|
+
(_, i) => `<Relationship Id="rId${i + 2}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide${i + 1}.xml"/>`
|
|
912
|
+
).join("");
|
|
913
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
914
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
915
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="slideMasters/slideMaster1.xml"/>
|
|
916
|
+
${slideRels}
|
|
917
|
+
</Relationships>`;
|
|
918
|
+
}
|
|
919
|
+
function slideMasterXml() {
|
|
920
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
921
|
+
<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">
|
|
922
|
+
<p:cSld>
|
|
923
|
+
<p:bg><p:bgRef idx="1001"><a:schemeClr val="bg1"/></p:bgRef></p:bg>
|
|
924
|
+
<p:spTree>
|
|
925
|
+
<p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
|
|
926
|
+
<p:grpSpPr/>
|
|
927
|
+
</p:spTree>
|
|
928
|
+
</p:cSld>
|
|
929
|
+
<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"/>
|
|
930
|
+
<p:sldLayoutIdLst><p:sldLayoutId id="2147483649" r:id="rId1"/></p:sldLayoutIdLst>
|
|
931
|
+
</p:sldMaster>`;
|
|
932
|
+
}
|
|
933
|
+
function slideMasterRelsXml() {
|
|
934
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
935
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
936
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
|
|
937
|
+
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="../theme/theme1.xml"/>
|
|
938
|
+
</Relationships>`;
|
|
939
|
+
}
|
|
940
|
+
function slideLayoutXml() {
|
|
941
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
942
|
+
<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">
|
|
943
|
+
<p:cSld name="Blank">
|
|
944
|
+
<p:spTree>
|
|
945
|
+
<p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
|
|
946
|
+
<p:grpSpPr/>
|
|
947
|
+
</p:spTree>
|
|
948
|
+
</p:cSld>
|
|
949
|
+
<p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr>
|
|
950
|
+
</p:sldLayout>`;
|
|
951
|
+
}
|
|
952
|
+
function slideLayoutRelsXml() {
|
|
953
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
954
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
955
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="../slideMasters/slideMaster1.xml"/>
|
|
956
|
+
</Relationships>`;
|
|
957
|
+
}
|
|
958
|
+
function themeXml() {
|
|
959
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
960
|
+
<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="GanttloomExportTheme">
|
|
961
|
+
<a:themeElements>
|
|
962
|
+
<a:clrScheme name="Gantt">
|
|
963
|
+
<a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1>
|
|
964
|
+
<a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1>
|
|
965
|
+
<a:dk2><a:srgbClr val="44546A"/></a:dk2>
|
|
966
|
+
<a:lt2><a:srgbClr val="E7E6E6"/></a:lt2>
|
|
967
|
+
<a:accent1><a:srgbClr val="4472C4"/></a:accent1>
|
|
968
|
+
<a:accent2><a:srgbClr val="ED7D31"/></a:accent2>
|
|
969
|
+
<a:accent3><a:srgbClr val="A5A5A5"/></a:accent3>
|
|
970
|
+
<a:accent4><a:srgbClr val="FFC000"/></a:accent4>
|
|
971
|
+
<a:accent5><a:srgbClr val="5B9BD5"/></a:accent5>
|
|
972
|
+
<a:accent6><a:srgbClr val="70AD47"/></a:accent6>
|
|
973
|
+
<a:hlink><a:srgbClr val="0563C1"/></a:hlink>
|
|
974
|
+
<a:folHlink><a:srgbClr val="954F72"/></a:folHlink>
|
|
975
|
+
</a:clrScheme>
|
|
976
|
+
<a:fontScheme name="Gantt">
|
|
977
|
+
<a:majorFont><a:latin typeface="Calibri"/><a:ea typeface=""/><a:cs typeface=""/></a:majorFont>
|
|
978
|
+
<a:minorFont><a:latin typeface="Calibri"/><a:ea typeface=""/><a:cs typeface=""/></a:minorFont>
|
|
979
|
+
</a:fontScheme>
|
|
980
|
+
<a:fmtScheme name="Gantt">
|
|
981
|
+
<a:fillStyleLst>
|
|
982
|
+
<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
|
|
983
|
+
<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
|
|
984
|
+
<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
|
|
985
|
+
</a:fillStyleLst>
|
|
986
|
+
<a:lnStyleLst>
|
|
987
|
+
<a:ln w="6350"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:ln>
|
|
988
|
+
<a:ln w="12700"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:ln>
|
|
989
|
+
<a:ln w="19050"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:ln>
|
|
990
|
+
</a:lnStyleLst>
|
|
991
|
+
<a:effectStyleLst>
|
|
992
|
+
<a:effectStyle><a:effectLst/></a:effectStyle>
|
|
993
|
+
<a:effectStyle><a:effectLst/></a:effectStyle>
|
|
994
|
+
<a:effectStyle><a:effectLst/></a:effectStyle>
|
|
995
|
+
</a:effectStyleLst>
|
|
996
|
+
<a:bgFillStyleLst>
|
|
997
|
+
<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
|
|
998
|
+
<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
|
|
999
|
+
<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
|
|
1000
|
+
</a:bgFillStyleLst>
|
|
1001
|
+
</a:fmtScheme>
|
|
1002
|
+
</a:themeElements>
|
|
1003
|
+
</a:theme>`;
|
|
1004
|
+
}
|
|
1005
|
+
function slideRelsXml() {
|
|
1006
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1007
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
1008
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
|
|
1009
|
+
</Relationships>`;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
// src/pptx/index.ts
|
|
1013
|
+
var EMU_PER_PT2 = 12700;
|
|
1014
|
+
var encoder3 = new TextEncoder();
|
|
1015
|
+
function textEntry(name, xml) {
|
|
1016
|
+
return { name, data: encoder3.encode(xml) };
|
|
1017
|
+
}
|
|
1018
|
+
function renderToPPTX(model, tasks, options = {}) {
|
|
1019
|
+
const layout = computePagination(model, options);
|
|
1020
|
+
const widthEmu = layout.pageWidthPt * EMU_PER_PT2;
|
|
1021
|
+
const heightEmu = layout.pageHeightPt * EMU_PER_PT2;
|
|
1022
|
+
const slideXmls = layout.pages.map((page) => {
|
|
1023
|
+
const content = buildPageContent(model, tasks, layout, page);
|
|
1024
|
+
return buildSlideXml(content.commands);
|
|
1025
|
+
});
|
|
1026
|
+
const entries = [
|
|
1027
|
+
textEntry("[Content_Types].xml", contentTypesXml(slideXmls.length)),
|
|
1028
|
+
textEntry("_rels/.rels", rootRelsXml()),
|
|
1029
|
+
textEntry("ppt/presentation.xml", presentationXml(slideXmls.length, widthEmu, heightEmu)),
|
|
1030
|
+
textEntry("ppt/_rels/presentation.xml.rels", presentationRelsXml(slideXmls.length)),
|
|
1031
|
+
textEntry("ppt/slideMasters/slideMaster1.xml", slideMasterXml()),
|
|
1032
|
+
textEntry("ppt/slideMasters/_rels/slideMaster1.xml.rels", slideMasterRelsXml()),
|
|
1033
|
+
textEntry("ppt/slideLayouts/slideLayout1.xml", slideLayoutXml()),
|
|
1034
|
+
textEntry("ppt/slideLayouts/_rels/slideLayout1.xml.rels", slideLayoutRelsXml()),
|
|
1035
|
+
textEntry("ppt/theme/theme1.xml", themeXml())
|
|
1036
|
+
];
|
|
1037
|
+
slideXmls.forEach((xml, i) => {
|
|
1038
|
+
entries.push(textEntry(`ppt/slides/slide${i + 1}.xml`, xml));
|
|
1039
|
+
entries.push(textEntry(`ppt/slides/_rels/slide${i + 1}.xml.rels`, slideRelsXml()));
|
|
1040
|
+
});
|
|
1041
|
+
return buildZip(entries);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
// src/xlsx/parts.ts
|
|
1045
|
+
function contentTypesXml2() {
|
|
1046
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1047
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
1048
|
+
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
1049
|
+
<Default Extension="xml" ContentType="application/xml"/>
|
|
1050
|
+
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
|
|
1051
|
+
<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
|
|
1052
|
+
<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
|
|
1053
|
+
</Types>`;
|
|
1054
|
+
}
|
|
1055
|
+
function rootRelsXml2() {
|
|
1056
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1057
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
1058
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
|
|
1059
|
+
</Relationships>`;
|
|
1060
|
+
}
|
|
1061
|
+
function workbookXml(sheetName) {
|
|
1062
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1063
|
+
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
1064
|
+
<sheets>
|
|
1065
|
+
<sheet name="${escapeXmlAttr(sheetName)}" sheetId="1" r:id="rId1"/>
|
|
1066
|
+
</sheets>
|
|
1067
|
+
</workbook>`;
|
|
1068
|
+
}
|
|
1069
|
+
function workbookRelsXml() {
|
|
1070
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1071
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
1072
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
|
|
1073
|
+
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
|
|
1074
|
+
</Relationships>`;
|
|
1075
|
+
}
|
|
1076
|
+
function stylesXml() {
|
|
1077
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1078
|
+
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
|
|
1079
|
+
<fonts count="2">
|
|
1080
|
+
<font><sz val="11"/><name val="Calibri"/></font>
|
|
1081
|
+
<font><sz val="11"/><name val="Calibri"/><b/></font>
|
|
1082
|
+
</fonts>
|
|
1083
|
+
<fills count="1"><fill><patternFill patternType="none"/></fill></fills>
|
|
1084
|
+
<borders count="1"><border/></borders>
|
|
1085
|
+
<cellStyleXfs count="1"><xf numFmtId="0" fontId="0"/></cellStyleXfs>
|
|
1086
|
+
<cellXfs count="2">
|
|
1087
|
+
<xf numFmtId="0" fontId="0" xfId="0"/>
|
|
1088
|
+
<xf numFmtId="0" fontId="1" xfId="0" applyFont="1"/>
|
|
1089
|
+
</cellXfs>
|
|
1090
|
+
</styleSheet>`;
|
|
1091
|
+
}
|
|
1092
|
+
function escapeXmlText(text) {
|
|
1093
|
+
return text.replace(/[&<>]/g, (c) => c === "&" ? "&" : c === "<" ? "<" : ">");
|
|
1094
|
+
}
|
|
1095
|
+
function escapeXmlAttr(text) {
|
|
1096
|
+
return escapeXmlText(text).replace(/"/g, """);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// src/xlsx/worksheet.ts
|
|
1100
|
+
function colLetter(index) {
|
|
1101
|
+
let n = index;
|
|
1102
|
+
let s = "";
|
|
1103
|
+
do {
|
|
1104
|
+
s = String.fromCharCode(65 + n % 26) + s;
|
|
1105
|
+
n = Math.floor(n / 26) - 1;
|
|
1106
|
+
} while (n >= 0);
|
|
1107
|
+
return s;
|
|
1108
|
+
}
|
|
1109
|
+
function buildSheetXml(headerRow, rows) {
|
|
1110
|
+
const allRows = [headerRow, ...rows];
|
|
1111
|
+
let rowsXml = "";
|
|
1112
|
+
allRows.forEach((row, rIdx) => {
|
|
1113
|
+
const rowNum = rIdx + 1;
|
|
1114
|
+
const styleAttr = rIdx === 0 ? ' s="1"' : "";
|
|
1115
|
+
const cellsXml = row.map((val, cIdx) => {
|
|
1116
|
+
const ref = `${colLetter(cIdx)}${rowNum}`;
|
|
1117
|
+
if (typeof val === "number" && Number.isFinite(val)) {
|
|
1118
|
+
return `<c r="${ref}"${styleAttr}><v>${val}</v></c>`;
|
|
1119
|
+
}
|
|
1120
|
+
const text = escapeXmlText(String(val ?? ""));
|
|
1121
|
+
return `<c r="${ref}"${styleAttr} t="inlineStr"><is><t xml:space="preserve">${text}</t></is></c>`;
|
|
1122
|
+
}).join("");
|
|
1123
|
+
rowsXml += `<row r="${rowNum}">${cellsXml}</row>`;
|
|
1124
|
+
});
|
|
1125
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1126
|
+
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>${rowsXml}</sheetData></worksheet>`;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
// src/xlsx/index.ts
|
|
1130
|
+
var encoder4 = new TextEncoder();
|
|
1131
|
+
function textEntry2(name, xml) {
|
|
1132
|
+
return { name, data: encoder4.encode(xml) };
|
|
1133
|
+
}
|
|
1134
|
+
var DEFAULT_COLUMNS = [
|
|
1135
|
+
{ id: "name", title: "Name" },
|
|
1136
|
+
{ id: "start", title: "Start", accessor: (t) => t.start.toISOString().slice(0, 10) },
|
|
1137
|
+
{ id: "end", title: "End", accessor: (t) => t.end.toISOString().slice(0, 10) },
|
|
1138
|
+
{ id: "progress", title: "Progress %", accessor: (t) => t.progress ?? 0 }
|
|
1139
|
+
];
|
|
1140
|
+
function renderToXLSX(tasks, options = {}) {
|
|
1141
|
+
const columns = options.columns ?? DEFAULT_COLUMNS;
|
|
1142
|
+
const headerRow = columns.map((c) => c.title);
|
|
1143
|
+
const rows = tasks.map(
|
|
1144
|
+
(task) => columns.map((col, colIndex) => {
|
|
1145
|
+
const raw = col.accessor ? col.accessor(task) : colIndex === 0 ? task.name : "";
|
|
1146
|
+
return raw === null || raw === void 0 ? "" : raw;
|
|
1147
|
+
})
|
|
1148
|
+
);
|
|
1149
|
+
const sheetName = (options.sheetName ?? "Tasks").slice(0, 31);
|
|
1150
|
+
const entries = [
|
|
1151
|
+
textEntry2("[Content_Types].xml", contentTypesXml2()),
|
|
1152
|
+
textEntry2("_rels/.rels", rootRelsXml2()),
|
|
1153
|
+
textEntry2("xl/workbook.xml", workbookXml(sheetName)),
|
|
1154
|
+
textEntry2("xl/_rels/workbook.xml.rels", workbookRelsXml()),
|
|
1155
|
+
textEntry2("xl/styles.xml", stylesXml()),
|
|
1156
|
+
textEntry2("xl/worksheets/sheet1.xml", buildSheetXml(headerRow, rows))
|
|
1157
|
+
];
|
|
1158
|
+
return buildZip(entries);
|
|
1159
|
+
}
|
|
1160
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1161
|
+
0 && (module.exports = {
|
|
1162
|
+
computePagination,
|
|
1163
|
+
renderToPDF,
|
|
1164
|
+
renderToPPTX,
|
|
1165
|
+
renderToXLSX
|
|
1166
|
+
});
|
|
1167
|
+
//# sourceMappingURL=index.cjs.map
|