@opengeni/artifact-tool 0.2.2
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 +190 -0
- package/README.md +104 -0
- package/dist/bounded-zip.d.ts +28 -0
- package/dist/chunk-4BSENMAE.js +39 -0
- package/dist/chunk-4BSENMAE.js.map +1 -0
- package/dist/chunk-4FRFNNBM.js +276 -0
- package/dist/chunk-4FRFNNBM.js.map +1 -0
- package/dist/chunk-5XIA6KHZ.js +49 -0
- package/dist/chunk-5XIA6KHZ.js.map +1 -0
- package/dist/chunk-66CT2ZRP.js +2229 -0
- package/dist/chunk-66CT2ZRP.js.map +1 -0
- package/dist/chunk-72SYDU4O.js +319 -0
- package/dist/chunk-72SYDU4O.js.map +1 -0
- package/dist/chunk-B2ZJRZDA.js +279 -0
- package/dist/chunk-B2ZJRZDA.js.map +1 -0
- package/dist/chunk-DCTLTNPW.js +2458 -0
- package/dist/chunk-DCTLTNPW.js.map +1 -0
- package/dist/chunk-FGZM4FQ6.js +473 -0
- package/dist/chunk-FGZM4FQ6.js.map +1 -0
- package/dist/chunk-HTJBLBDE.js +591 -0
- package/dist/chunk-HTJBLBDE.js.map +1 -0
- package/dist/chunk-IYFXZQ34.js +5306 -0
- package/dist/chunk-IYFXZQ34.js.map +1 -0
- package/dist/chunk-J6N7IR44.js +172 -0
- package/dist/chunk-J6N7IR44.js.map +1 -0
- package/dist/chunk-N2IHNWBS.js +1002 -0
- package/dist/chunk-N2IHNWBS.js.map +1 -0
- package/dist/chunk-NAG44K67.js +549 -0
- package/dist/chunk-NAG44K67.js.map +1 -0
- package/dist/chunk-O24KTEGP.js +33 -0
- package/dist/chunk-O24KTEGP.js.map +1 -0
- package/dist/chunk-PI2AZVGK.js +2074 -0
- package/dist/chunk-PI2AZVGK.js.map +1 -0
- package/dist/chunk-Q2FFHEY7.js +40 -0
- package/dist/chunk-Q2FFHEY7.js.map +1 -0
- package/dist/chunk-RBGQADXA.js +57 -0
- package/dist/chunk-RBGQADXA.js.map +1 -0
- package/dist/chunk-U5G2BKM2.js +613 -0
- package/dist/chunk-U5G2BKM2.js.map +1 -0
- package/dist/chunk-VOJHMSM3.js +39 -0
- package/dist/chunk-VOJHMSM3.js.map +1 -0
- package/dist/chunk-VUNV25KB.js +16 -0
- package/dist/chunk-VUNV25KB.js.map +1 -0
- package/dist/chunk-XIFUNVBO.js +101 -0
- package/dist/chunk-XIFUNVBO.js.map +1 -0
- package/dist/chunk-ZCJYRJGB.js +489 -0
- package/dist/chunk-ZCJYRJGB.js.map +1 -0
- package/dist/chunk-ZZXPEE4S.js +2260 -0
- package/dist/chunk-ZZXPEE4S.js.map +1 -0
- package/dist/document-docx-api.d.ts +49 -0
- package/dist/document-docx-codec.d.ts +9 -0
- package/dist/document-docx-codec.js +4765 -0
- package/dist/document-docx-codec.js.map +1 -0
- package/dist/document-docx-import.d.ts +261 -0
- package/dist/document-docx-packer.d.ts +18 -0
- package/dist/document-docx-state.d.ts +12 -0
- package/dist/document-render.d.ts +3 -0
- package/dist/document-render.js +492 -0
- package/dist/document-render.js.map +1 -0
- package/dist/document.d.ts +432 -0
- package/dist/document.js +37 -0
- package/dist/document.js.map +1 -0
- package/dist/errors.d.ts +23 -0
- package/dist/file-blob.d.ts +11 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +186 -0
- package/dist/index.js.map +1 -0
- package/dist/inspection.d.ts +12 -0
- package/dist/kernel.d.ts +101 -0
- package/dist/lib-GQLPACFK.js +9746 -0
- package/dist/lib-GQLPACFK.js.map +1 -0
- package/dist/materializer-cli-entry.d.ts +2 -0
- package/dist/materializer-cli-entry.js +29 -0
- package/dist/materializer-cli-entry.js.map +1 -0
- package/dist/materializer-cli.d.ts +24 -0
- package/dist/materializer-cli.js +24 -0
- package/dist/materializer-cli.js.map +1 -0
- package/dist/native-raster.d.ts +10 -0
- package/dist/native.d.ts +129 -0
- package/dist/native.js +31 -0
- package/dist/native.js.map +1 -0
- package/dist/office-import.d.ts +48 -0
- package/dist/office-import.js +154 -0
- package/dist/office-import.js.map +1 -0
- package/dist/presentation-pptx-api.d.ts +60 -0
- package/dist/presentation-pptx-import.d.ts +5 -0
- package/dist/presentation-pptx-state-digest-765CZZED.js +10 -0
- package/dist/presentation-pptx-state-digest-765CZZED.js.map +1 -0
- package/dist/presentation-pptx-state-digest.d.ts +3 -0
- package/dist/presentation-pptx-state.d.ts +4 -0
- package/dist/presentation-pptx-xml.d.ts +29 -0
- package/dist/presentation-pptx.d.ts +5 -0
- package/dist/presentation-pptx.js +2365 -0
- package/dist/presentation-pptx.js.map +1 -0
- package/dist/presentation-render.d.ts +29 -0
- package/dist/presentation-render.js +375 -0
- package/dist/presentation-render.js.map +1 -0
- package/dist/presentation.d.ts +565 -0
- package/dist/presentation.js +71 -0
- package/dist/presentation.js.map +1 -0
- package/dist/production-composite.d.ts +68 -0
- package/dist/production-document.d.ts +2 -0
- package/dist/production-document.js +176 -0
- package/dist/production-document.js.map +1 -0
- package/dist/production-native-codecs.d.ts +94 -0
- package/dist/production-presentation.d.ts +2 -0
- package/dist/production-presentation.js +176 -0
- package/dist/production-presentation.js.map +1 -0
- package/dist/production-sha256.d.ts +2 -0
- package/dist/production-spreadsheet-native.d.ts +12 -0
- package/dist/production-spreadsheet.d.ts +2 -0
- package/dist/production-spreadsheet.js +176 -0
- package/dist/production-spreadsheet.js.map +1 -0
- package/dist/production.d.ts +96 -0
- package/dist/raster-image.d.ts +34 -0
- package/dist/reference.d.ts +17 -0
- package/dist/reference.js +574 -0
- package/dist/reference.js.map +1 -0
- package/dist/runtime-cli-entry.d.ts +2 -0
- package/dist/runtime-cli-entry.js +21 -0
- package/dist/runtime-cli-entry.js.map +1 -0
- package/dist/runtime-cli.d.ts +76 -0
- package/dist/runtime-cli.js +26 -0
- package/dist/runtime-cli.js.map +1 -0
- package/dist/runtime-development.d.ts +73 -0
- package/dist/runtime-development.js +30 -0
- package/dist/runtime-development.js.map +1 -0
- package/dist/runtime-receipt.d.ts +19 -0
- package/dist/runtime.d.ts +355 -0
- package/dist/runtime.js +42 -0
- package/dist/runtime.js.map +1 -0
- package/dist/snapshot.d.ts +26 -0
- package/dist/snapshot.js +61 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/spreadsheet-address.d.ts +18 -0
- package/dist/spreadsheet-file.d.ts +17 -0
- package/dist/spreadsheet-formula.d.ts +67 -0
- package/dist/spreadsheet-image.d.ts +17 -0
- package/dist/spreadsheet-render.d.ts +5 -0
- package/dist/spreadsheet-render.js +752 -0
- package/dist/spreadsheet-render.js.map +1 -0
- package/dist/spreadsheet-types.d.ts +155 -0
- package/dist/spreadsheet-xlsx-api.d.ts +88 -0
- package/dist/spreadsheet-xlsx-codec.d.ts +16 -0
- package/dist/spreadsheet-xlsx-codec.js +16 -0
- package/dist/spreadsheet-xlsx-codec.js.map +1 -0
- package/dist/spreadsheet-xlsx-state.d.ts +16 -0
- package/dist/spreadsheet.d.ts +724 -0
- package/dist/spreadsheet.js +67 -0
- package/dist/spreadsheet.js.map +1 -0
- package/package.json +129 -0
- package/src/bounded-zip.ts +450 -0
- package/src/document-docx-api.ts +64 -0
- package/src/document-docx-codec.ts +2253 -0
- package/src/document-docx-import.ts +3814 -0
- package/src/document-docx-packer.ts +72 -0
- package/src/document-docx-state.ts +59 -0
- package/src/document-render.ts +615 -0
- package/src/document.ts +2800 -0
- package/src/errors.ts +45 -0
- package/src/file-blob.ts +43 -0
- package/src/index.ts +4 -0
- package/src/inspection.ts +50 -0
- package/src/kernel.ts +525 -0
- package/src/materializer-cli-entry.ts +11 -0
- package/src/materializer-cli.ts +743 -0
- package/src/native-raster.ts +141 -0
- package/src/native.ts +390 -0
- package/src/office-import.ts +243 -0
- package/src/presentation-pptx-api.ts +107 -0
- package/src/presentation-pptx-import.ts +2065 -0
- package/src/presentation-pptx-state-digest.ts +46 -0
- package/src/presentation-pptx-state.ts +18 -0
- package/src/presentation-pptx-xml.ts +473 -0
- package/src/presentation-pptx.ts +447 -0
- package/src/presentation-render.ts +532 -0
- package/src/presentation.ts +2599 -0
- package/src/production-composite.ts +805 -0
- package/src/production-document.ts +2 -0
- package/src/production-native-codecs.ts +1141 -0
- package/src/production-presentation.ts +2 -0
- package/src/production-sha256.ts +150 -0
- package/src/production-spreadsheet-native.ts +686 -0
- package/src/production-spreadsheet.ts +2 -0
- package/src/production.ts +1022 -0
- package/src/raster-image.ts +586 -0
- package/src/reference.ts +17 -0
- package/src/runtime-cli-entry.ts +13 -0
- package/src/runtime-cli.ts +670 -0
- package/src/runtime-development.ts +689 -0
- package/src/runtime-receipt.ts +169 -0
- package/src/runtime.ts +1463 -0
- package/src/snapshot.ts +88 -0
- package/src/spreadsheet-address.ts +90 -0
- package/src/spreadsheet-file.ts +71 -0
- package/src/spreadsheet-formula.ts +1212 -0
- package/src/spreadsheet-image.ts +242 -0
- package/src/spreadsheet-render.ts +985 -0
- package/src/spreadsheet-types.ts +171 -0
- package/src/spreadsheet-xlsx-api.ts +123 -0
- package/src/spreadsheet-xlsx-codec.ts +3063 -0
- package/src/spreadsheet-xlsx-state.ts +191 -0
- package/src/spreadsheet.ts +5195 -0
|
@@ -0,0 +1,2074 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RasterImageValidationError,
|
|
3
|
+
canonicalizeRasterDataUrl,
|
|
4
|
+
encodeRasterBase64,
|
|
5
|
+
normalizeRasterContentType,
|
|
6
|
+
validateRasterImageBytes
|
|
7
|
+
} from "./chunk-FGZM4FQ6.js";
|
|
8
|
+
import {
|
|
9
|
+
FileBlob
|
|
10
|
+
} from "./chunk-4BSENMAE.js";
|
|
11
|
+
import {
|
|
12
|
+
boundInspectionRecords,
|
|
13
|
+
defineHelpEntries,
|
|
14
|
+
filterHelpEntries
|
|
15
|
+
} from "./chunk-Q2FFHEY7.js";
|
|
16
|
+
|
|
17
|
+
// src/presentation-pptx-state.ts
|
|
18
|
+
var lossState = /* @__PURE__ */ new WeakMap();
|
|
19
|
+
function presentationLossState(presentation) {
|
|
20
|
+
return lossState.get(presentation);
|
|
21
|
+
}
|
|
22
|
+
function setPresentationLossState(presentation, envelope) {
|
|
23
|
+
if (envelope) lossState.set(presentation, envelope);
|
|
24
|
+
else lossState.delete(presentation);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/presentation-pptx-api.ts
|
|
28
|
+
var PPTX_MEDIA_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
|
29
|
+
var PresentationFidelityError = class extends Error {
|
|
30
|
+
issues;
|
|
31
|
+
constructor(message, issues) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "PresentationFidelityError";
|
|
34
|
+
this.issues = issues;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var PresentationSecurityError = class extends Error {
|
|
38
|
+
constructor(message, code, entryName) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.code = code;
|
|
41
|
+
this.entryName = entryName;
|
|
42
|
+
this.name = "PresentationSecurityError";
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/presentation.ts
|
|
47
|
+
var UnsupportedPresentationFeatureError = class extends Error {
|
|
48
|
+
code = "UNSUPPORTED_PRESENTATION_FEATURE";
|
|
49
|
+
constructor(feature, detail) {
|
|
50
|
+
super(`Unsupported presentation feature: ${feature}${detail ? `. ${detail}` : ""}`);
|
|
51
|
+
this.name = "UnsupportedPresentationFeatureError";
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var InvalidPresentationInputError = class extends Error {
|
|
55
|
+
constructor(field, detail) {
|
|
56
|
+
super(`Invalid presentation ${field}: ${detail}`);
|
|
57
|
+
this.field = field;
|
|
58
|
+
this.name = "InvalidPresentationInputError";
|
|
59
|
+
}
|
|
60
|
+
code = "INVALID_PRESENTATION_INPUT";
|
|
61
|
+
};
|
|
62
|
+
var PRESENTATION_HELP_ENTRIES = defineHelpEntries([
|
|
63
|
+
["Presentation.create", "Create editable deck", "Presentation.create()"],
|
|
64
|
+
["deck.slides.add", "Add slide", "deck.slides.add()"],
|
|
65
|
+
[
|
|
66
|
+
"slide.shapes.add",
|
|
67
|
+
"Add positioned text or shape",
|
|
68
|
+
'slide.shapes.add({ geometry: "textbox", text, position })'
|
|
69
|
+
],
|
|
70
|
+
[
|
|
71
|
+
"slide.charts.add",
|
|
72
|
+
"Add chart data",
|
|
73
|
+
'slide.charts.add("bar", { categories, series: [{ name, values }] })'
|
|
74
|
+
],
|
|
75
|
+
["slide.tables.add", "Add table", "slide.tables.add({ rows, position })"],
|
|
76
|
+
["slide.images.add", "Add image", "slide.images.add({ dataUrl, name, position })"],
|
|
77
|
+
[
|
|
78
|
+
"deck.inspect",
|
|
79
|
+
"Inspect deck records",
|
|
80
|
+
'await deck.inspect({ kind: "slide,textbox,chart,table" })'
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
"deck.export",
|
|
84
|
+
"Export SVG, PNG, WebP, or layout",
|
|
85
|
+
'await deck.export({ format: "png", montage: true })'
|
|
86
|
+
],
|
|
87
|
+
[
|
|
88
|
+
"PresentationFile.importPptx",
|
|
89
|
+
"Import editable PPTX",
|
|
90
|
+
"await PresentationFile.importPptx(blob)"
|
|
91
|
+
],
|
|
92
|
+
["PresentationFile.exportPptx", "Export PPTX", "await PresentationFile.exportPptx(deck)"]
|
|
93
|
+
]);
|
|
94
|
+
var Presentation = class _Presentation {
|
|
95
|
+
slides;
|
|
96
|
+
masters;
|
|
97
|
+
layouts;
|
|
98
|
+
slideSize;
|
|
99
|
+
objects = /* @__PURE__ */ new Map();
|
|
100
|
+
imageResolver;
|
|
101
|
+
allowedImageUriSchemes;
|
|
102
|
+
nextId = 1;
|
|
103
|
+
constructor(options = {}) {
|
|
104
|
+
this.slideSize = options.slideSize ?? { width: 1280, height: 720 };
|
|
105
|
+
validatePosition(
|
|
106
|
+
{ left: 0, top: 0, width: this.slideSize.width, height: this.slideSize.height },
|
|
107
|
+
"slideSize"
|
|
108
|
+
);
|
|
109
|
+
this.imageResolver = options.imageResolver;
|
|
110
|
+
this.allowedImageUriSchemes = validateImageUriPolicy(
|
|
111
|
+
options.imageResolver,
|
|
112
|
+
options.allowedImageUriSchemes
|
|
113
|
+
);
|
|
114
|
+
this.slides = new SlideCollection(this);
|
|
115
|
+
this.masters = new PresentationMasterCollection(this);
|
|
116
|
+
this.layouts = new PresentationLayoutCollection(this);
|
|
117
|
+
this.register("pr", this);
|
|
118
|
+
}
|
|
119
|
+
static create(options = {}) {
|
|
120
|
+
return new _Presentation(options);
|
|
121
|
+
}
|
|
122
|
+
assertImageUriAccepted(uri) {
|
|
123
|
+
assertSafeText(uri, "image uri", MAX_IMAGE_URI_LENGTH);
|
|
124
|
+
const scheme = imageUriScheme(uri);
|
|
125
|
+
if (!this.imageResolver || !this.allowedImageUriSchemes.has(scheme)) {
|
|
126
|
+
throw new InvalidPresentationInputError(
|
|
127
|
+
"image uri",
|
|
128
|
+
"requires an injected resolver and an explicitly allowed URI scheme"
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async resolveImageUri(reference) {
|
|
133
|
+
this.assertImageUriAccepted(reference.uri);
|
|
134
|
+
if (!this.imageResolver) {
|
|
135
|
+
throw new InvalidPresentationInputError("image resolver", "is not configured");
|
|
136
|
+
}
|
|
137
|
+
return await this.imageResolver(reference);
|
|
138
|
+
}
|
|
139
|
+
allocateId(prefix, value) {
|
|
140
|
+
return this.register(prefix, value);
|
|
141
|
+
}
|
|
142
|
+
resolve(id) {
|
|
143
|
+
const value = this.objects.get(id);
|
|
144
|
+
if (value === void 0) throw new Error(`Unknown presentation object id: ${id}`);
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
async inspect(options = {}) {
|
|
148
|
+
const requested = new Set(
|
|
149
|
+
(options.kind ?? "deck,master,layout,slide,textbox,shape,image,chart,table,group,notes").split(",").map((value) => value.trim()).filter(Boolean).map((value) => value === "comments" ? "thread" : value)
|
|
150
|
+
);
|
|
151
|
+
const records = [];
|
|
152
|
+
if (requested.has("deck")) {
|
|
153
|
+
records.push({
|
|
154
|
+
kind: "deck",
|
|
155
|
+
id: "pr/1",
|
|
156
|
+
slides: this.slides.items.length,
|
|
157
|
+
width: this.slideSize.width,
|
|
158
|
+
height: this.slideSize.height
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
if (requested.has("master")) {
|
|
162
|
+
for (const master of this.masters.items) records.push(master.inspectRecord());
|
|
163
|
+
}
|
|
164
|
+
if (requested.has("layout")) {
|
|
165
|
+
for (const layout of this.layouts.items) records.push(layout.inspectRecord());
|
|
166
|
+
}
|
|
167
|
+
for (const [index, slide] of this.slides.items.entries()) {
|
|
168
|
+
if (requested.has("slide")) records.push(slide.inspectRecord(index));
|
|
169
|
+
for (const shape of slide.shapes.items) {
|
|
170
|
+
const kind = shape.geometry === "textbox" ? "textbox" : "shape";
|
|
171
|
+
if (requested.has(kind) || kind === "textbox" && requested.has("shape")) {
|
|
172
|
+
records.push(shape.inspectRecord(index));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (requested.has("chart")) {
|
|
176
|
+
for (const chart of slide.charts.items) records.push(chart.inspectRecord(index));
|
|
177
|
+
}
|
|
178
|
+
if (requested.has("image")) {
|
|
179
|
+
for (const image of slide.images.items) records.push(image.inspectRecord(index));
|
|
180
|
+
}
|
|
181
|
+
if (requested.has("table")) {
|
|
182
|
+
for (const table of slide.tables.items) records.push(table.inspectRecord(index));
|
|
183
|
+
}
|
|
184
|
+
if (requested.has("group")) {
|
|
185
|
+
for (const group of slide.groups.items) records.push(group.inspectRecord(index));
|
|
186
|
+
}
|
|
187
|
+
if (requested.has("notes") && slide.notes.toString().length > 0) {
|
|
188
|
+
records.push({
|
|
189
|
+
kind: "notes",
|
|
190
|
+
id: `${slide.id}/notes`,
|
|
191
|
+
slide: index + 1,
|
|
192
|
+
text: slide.notes.toString()
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const search = options.search?.toLocaleLowerCase();
|
|
197
|
+
let filtered = search ? records.filter((record) => JSON.stringify(record).toLocaleLowerCase().includes(search)) : records;
|
|
198
|
+
if (options.target) filtered = filtered.filter((record) => record.id === options.target.id);
|
|
199
|
+
filtered = filtered.map((record) => projectRecord(record, options.include, options.exclude));
|
|
200
|
+
return boundPresentationRecords(filtered, options.maxChars ?? 2e4);
|
|
201
|
+
}
|
|
202
|
+
help(query, options = {}) {
|
|
203
|
+
if (typeof query !== "string") {
|
|
204
|
+
throw new InvalidPresentationInputError("help query", "must be a string");
|
|
205
|
+
}
|
|
206
|
+
const records = filterHelpEntries(PRESENTATION_HELP_ENTRIES, query, options.search);
|
|
207
|
+
return boundPresentationRecords(records, options.maxChars ?? 6e3);
|
|
208
|
+
}
|
|
209
|
+
async export(options) {
|
|
210
|
+
if (!options || typeof options !== "object") {
|
|
211
|
+
throw new InvalidPresentationInputError("export options", "must be an object");
|
|
212
|
+
}
|
|
213
|
+
if (!["svg", "png", "layout", "webp"].includes(options.format)) {
|
|
214
|
+
throw new InvalidPresentationInputError("export format", "is not supported");
|
|
215
|
+
}
|
|
216
|
+
validatePresentation(this, options.format !== "layout");
|
|
217
|
+
if (options.format === "layout") {
|
|
218
|
+
if (options.slide) {
|
|
219
|
+
this.assertOwns(options.slide);
|
|
220
|
+
return jsonBlob(
|
|
221
|
+
options.slide.layoutSnapshot(this.slides.items.indexOf(options.slide)),
|
|
222
|
+
`slide-${this.slides.items.indexOf(options.slide) + 1}.layout.json`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
return jsonBlob(this.layoutSnapshot(), "presentation.layout.json");
|
|
226
|
+
}
|
|
227
|
+
const scale = options.scale ?? 1;
|
|
228
|
+
validateRenderScale(scale);
|
|
229
|
+
if (options.montage) {
|
|
230
|
+
const dimensions = montageDimensions(this, options.columns, options.gap);
|
|
231
|
+
if (options.format !== "svg") {
|
|
232
|
+
assertRenderPixelBudget(dimensions.width, dimensions.height, scale);
|
|
233
|
+
}
|
|
234
|
+
const { executePresentationRender: executePresentationRender2 } = await import("@opengeni/artifact-tool/presentation/render");
|
|
235
|
+
return await executePresentationRender2({
|
|
236
|
+
kind: "montage",
|
|
237
|
+
presentation: this,
|
|
238
|
+
format: options.format,
|
|
239
|
+
scale,
|
|
240
|
+
name: `presentation-montage.${options.format}`,
|
|
241
|
+
dimensions
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
const slide = options.slide ?? (this.slides.items.length === 1 ? this.slides.items[0] : void 0);
|
|
245
|
+
if (!slide) {
|
|
246
|
+
throw new Error(
|
|
247
|
+
"A slide is required when exporting a multi-slide presentation without montage: true"
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
this.assertOwns(slide);
|
|
251
|
+
if (options.format !== "svg") {
|
|
252
|
+
assertRenderPixelBudget(this.slideSize.width, this.slideSize.height, scale);
|
|
253
|
+
}
|
|
254
|
+
const { executePresentationRender } = await import("@opengeni/artifact-tool/presentation/render");
|
|
255
|
+
return await executePresentationRender({
|
|
256
|
+
kind: "slide",
|
|
257
|
+
slide,
|
|
258
|
+
format: options.format,
|
|
259
|
+
scale,
|
|
260
|
+
name: `slide-${this.slides.items.indexOf(slide) + 1}.${options.format}`,
|
|
261
|
+
dimensions: this.slideSize
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
layoutSnapshot() {
|
|
265
|
+
return {
|
|
266
|
+
kind: "presentation-layout",
|
|
267
|
+
slideSize: { ...this.slideSize },
|
|
268
|
+
masters: this.masters.items.map((master) => master.toProto()),
|
|
269
|
+
layouts: this.layouts.items.map((layout) => layout.toProto()),
|
|
270
|
+
slides: this.slides.items.map((slide, index) => slide.layoutSnapshot(index))
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
register(prefix, value) {
|
|
274
|
+
const id = `${prefix}/${this.nextId++}`;
|
|
275
|
+
this.objects.set(id, value);
|
|
276
|
+
return id;
|
|
277
|
+
}
|
|
278
|
+
assertOwns(slide) {
|
|
279
|
+
if (slide.presentation !== this) throw new Error("Slide belongs to another presentation");
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
var PresentationPlaceholderCollection = class {
|
|
283
|
+
constructor(elements) {
|
|
284
|
+
this.elements = elements;
|
|
285
|
+
}
|
|
286
|
+
summary() {
|
|
287
|
+
const names = this.elements.filter(
|
|
288
|
+
(element) => element.kind === "shape" && element.config.placeholder !== void 0
|
|
289
|
+
).map((element) => element.config.name ?? element.config.placeholder?.type ?? "Placeholder");
|
|
290
|
+
return { count: names.length, names };
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
var PresentationMaster = class {
|
|
294
|
+
constructor(presentation, config = {}) {
|
|
295
|
+
this.presentation = presentation;
|
|
296
|
+
this.id = presentation.allocateId("mt", this);
|
|
297
|
+
this.name = config.name ?? this.id;
|
|
298
|
+
assertSafeText(this.name, "master name", 512);
|
|
299
|
+
this.background = config.background ?? "white";
|
|
300
|
+
colorValue(this.background);
|
|
301
|
+
this.elements = (config.elements ?? []).map(cloneTemplateElement);
|
|
302
|
+
this.placeholders = new PresentationPlaceholderCollection(this.elements);
|
|
303
|
+
}
|
|
304
|
+
id;
|
|
305
|
+
name;
|
|
306
|
+
background;
|
|
307
|
+
elements;
|
|
308
|
+
placeholders;
|
|
309
|
+
inspectRecord() {
|
|
310
|
+
return {
|
|
311
|
+
kind: "master",
|
|
312
|
+
id: this.id,
|
|
313
|
+
name: this.name,
|
|
314
|
+
elements: this.elements.length,
|
|
315
|
+
placeholders: this.placeholders.summary()
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
toProto() {
|
|
319
|
+
return {
|
|
320
|
+
id: this.id,
|
|
321
|
+
type: "master",
|
|
322
|
+
name: this.name,
|
|
323
|
+
background: this.background,
|
|
324
|
+
elements: this.elements.map(cloneTemplateElement)
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
var PresentationLayout = class {
|
|
329
|
+
constructor(presentation, config = {}) {
|
|
330
|
+
this.presentation = presentation;
|
|
331
|
+
this.id = presentation.allocateId("ly", this);
|
|
332
|
+
this.name = config.name ?? this.id;
|
|
333
|
+
assertSafeText(this.name, "layout name", 512);
|
|
334
|
+
if (config.masterId !== void 0 && config.parentLayoutId !== void 0 && config.masterId !== config.parentLayoutId) {
|
|
335
|
+
throw new InvalidPresentationInputError(
|
|
336
|
+
"layout masterId",
|
|
337
|
+
"conflicts with the parentLayoutId compatibility alias"
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
this.masterId = config.masterId ?? config.parentLayoutId;
|
|
341
|
+
if (this.masterId !== void 0) assertSafeText(this.masterId, "layout master id", 512);
|
|
342
|
+
this.background = config.background ?? "white";
|
|
343
|
+
colorValue(this.background);
|
|
344
|
+
this.elements = (config.elements ?? []).map(cloneTemplateElement);
|
|
345
|
+
this.placeholders = new PresentationPlaceholderCollection(this.elements);
|
|
346
|
+
}
|
|
347
|
+
id;
|
|
348
|
+
name;
|
|
349
|
+
masterId;
|
|
350
|
+
background;
|
|
351
|
+
elements;
|
|
352
|
+
placeholders;
|
|
353
|
+
get parentLayoutId() {
|
|
354
|
+
return this.masterId;
|
|
355
|
+
}
|
|
356
|
+
inspectRecord() {
|
|
357
|
+
return {
|
|
358
|
+
kind: "layout",
|
|
359
|
+
id: this.id,
|
|
360
|
+
name: this.name,
|
|
361
|
+
masterId: this.masterId,
|
|
362
|
+
parentLayoutId: this.masterId,
|
|
363
|
+
elements: this.elements.length,
|
|
364
|
+
placeholders: this.placeholders.summary()
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
toProto() {
|
|
368
|
+
return {
|
|
369
|
+
id: this.id,
|
|
370
|
+
type: "layout",
|
|
371
|
+
name: this.name,
|
|
372
|
+
masterId: this.masterId,
|
|
373
|
+
parentLayoutId: this.masterId,
|
|
374
|
+
background: this.background,
|
|
375
|
+
elements: this.elements.map(cloneTemplateElement)
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
var PresentationMasterCollection = class {
|
|
380
|
+
constructor(presentation) {
|
|
381
|
+
this.presentation = presentation;
|
|
382
|
+
}
|
|
383
|
+
items = [];
|
|
384
|
+
add(config = {}) {
|
|
385
|
+
const master = new PresentationMaster(this.presentation, config);
|
|
386
|
+
this.items.push(master);
|
|
387
|
+
return master;
|
|
388
|
+
}
|
|
389
|
+
getItem(index) {
|
|
390
|
+
const master = this.items[index];
|
|
391
|
+
if (!master) throw new Error(`Presentation master index out of range: ${index}`);
|
|
392
|
+
return master;
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
var PresentationLayoutCollection = class {
|
|
396
|
+
constructor(presentation) {
|
|
397
|
+
this.presentation = presentation;
|
|
398
|
+
}
|
|
399
|
+
items = [];
|
|
400
|
+
add(config = {}) {
|
|
401
|
+
if ((config.masterId ?? config.parentLayoutId) !== void 0 && !this.presentation.masters.items.some(
|
|
402
|
+
(master) => master.id === (config.masterId ?? config.parentLayoutId)
|
|
403
|
+
)) {
|
|
404
|
+
throw new InvalidPresentationInputError(
|
|
405
|
+
"layout parentLayoutId",
|
|
406
|
+
"must reference a master in the same presentation"
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
const layout = new PresentationLayout(this.presentation, config);
|
|
410
|
+
this.items.push(layout);
|
|
411
|
+
return layout;
|
|
412
|
+
}
|
|
413
|
+
getItem(index) {
|
|
414
|
+
const layout = this.items[index];
|
|
415
|
+
if (!layout) throw new Error(`Presentation layout index out of range: ${index}`);
|
|
416
|
+
return layout;
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
var SlideCollection = class {
|
|
420
|
+
constructor(presentation) {
|
|
421
|
+
this.presentation = presentation;
|
|
422
|
+
}
|
|
423
|
+
items = [];
|
|
424
|
+
add() {
|
|
425
|
+
const slide = new Slide(this.presentation);
|
|
426
|
+
this.items.push(slide);
|
|
427
|
+
return slide;
|
|
428
|
+
}
|
|
429
|
+
getItem(index) {
|
|
430
|
+
const slide = this.items[index];
|
|
431
|
+
if (!slide) throw new Error(`Slide index out of range: ${index}`);
|
|
432
|
+
return slide;
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
var Slide = class {
|
|
436
|
+
constructor(presentation) {
|
|
437
|
+
this.presentation = presentation;
|
|
438
|
+
this.id = presentation.allocateId("sl", this);
|
|
439
|
+
this.shapes = new PresentationShapeCollection(this);
|
|
440
|
+
this.charts = new PresentationChartCollection(this);
|
|
441
|
+
this.images = new PresentationImageCollection(this);
|
|
442
|
+
this.tables = new PresentationTableCollection(this);
|
|
443
|
+
this.groups = new PresentationGroupCollection(this);
|
|
444
|
+
this.placeholders = new SlidePlaceholderCollection(this);
|
|
445
|
+
}
|
|
446
|
+
id;
|
|
447
|
+
shapes;
|
|
448
|
+
charts;
|
|
449
|
+
images;
|
|
450
|
+
tables;
|
|
451
|
+
groups;
|
|
452
|
+
notes = new PresentationText();
|
|
453
|
+
placeholders;
|
|
454
|
+
/** Canonical cross-type z-order. Typed collections are filtered indexes over this list. */
|
|
455
|
+
elements = [];
|
|
456
|
+
background = { fill: "white" };
|
|
457
|
+
title = "";
|
|
458
|
+
layout;
|
|
459
|
+
get speakerNotes() {
|
|
460
|
+
return this.notes;
|
|
461
|
+
}
|
|
462
|
+
setLayout(layout) {
|
|
463
|
+
if (layout && layout.presentation !== this.presentation) {
|
|
464
|
+
throw new InvalidPresentationInputError("slide layout", "belongs to another presentation");
|
|
465
|
+
}
|
|
466
|
+
this.layout = layout;
|
|
467
|
+
}
|
|
468
|
+
appendElement(element) {
|
|
469
|
+
if (element.slide !== this) {
|
|
470
|
+
throw new InvalidPresentationInputError("slide element", "belongs to another slide");
|
|
471
|
+
}
|
|
472
|
+
this.elements.push(element);
|
|
473
|
+
}
|
|
474
|
+
async export(options) {
|
|
475
|
+
return await this.presentation.export({ ...options, slide: this });
|
|
476
|
+
}
|
|
477
|
+
inspectRecord(index) {
|
|
478
|
+
const textShapes = this.shapes.items.filter((shape) => shape.text.toString().length > 0);
|
|
479
|
+
return {
|
|
480
|
+
kind: "slide",
|
|
481
|
+
id: this.id,
|
|
482
|
+
slide: index + 1,
|
|
483
|
+
title: this.title,
|
|
484
|
+
textShapes: textShapes.length,
|
|
485
|
+
charts: this.charts.items.length,
|
|
486
|
+
images: this.images.items.length,
|
|
487
|
+
tables: this.tables.items.length,
|
|
488
|
+
groups: this.groups.items.length,
|
|
489
|
+
layoutId: this.layout?.id,
|
|
490
|
+
notesChars: this.notes.toString().length
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
layoutSnapshot(index) {
|
|
494
|
+
return {
|
|
495
|
+
id: this.id,
|
|
496
|
+
slide: index + 1,
|
|
497
|
+
background: { ...this.background },
|
|
498
|
+
layoutId: this.layout?.id,
|
|
499
|
+
notes: this.notes.toString(),
|
|
500
|
+
elements: [...this.elements.map((element) => element.layoutSnapshot())]
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
var SlidePlaceholderCollection = class {
|
|
505
|
+
constructor(slide) {
|
|
506
|
+
this.slide = slide;
|
|
507
|
+
}
|
|
508
|
+
get items() {
|
|
509
|
+
return this.slide.shapes.items.filter((shape) => shape.placeholder !== void 0);
|
|
510
|
+
}
|
|
511
|
+
summary() {
|
|
512
|
+
const types = {};
|
|
513
|
+
for (const shape of this.items) {
|
|
514
|
+
const type = shape.placeholder?.type ?? "unknown";
|
|
515
|
+
types[type] = (types[type] ?? 0) + 1;
|
|
516
|
+
}
|
|
517
|
+
return { count: this.items.length, types };
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
var PresentationText = class {
|
|
521
|
+
value = "";
|
|
522
|
+
styleValue = {};
|
|
523
|
+
constructor(initialValue = "", style = {}) {
|
|
524
|
+
this.set(initialValue);
|
|
525
|
+
this.style = style;
|
|
526
|
+
}
|
|
527
|
+
get style() {
|
|
528
|
+
return this.styleValue;
|
|
529
|
+
}
|
|
530
|
+
set style(value) {
|
|
531
|
+
validateTextStyle(value, "textStyle");
|
|
532
|
+
this.styleValue = { ...value };
|
|
533
|
+
}
|
|
534
|
+
set(value) {
|
|
535
|
+
assertSafeText(value, "text");
|
|
536
|
+
this.value = value;
|
|
537
|
+
}
|
|
538
|
+
replace(searchValue, replaceValue) {
|
|
539
|
+
const before = this.value;
|
|
540
|
+
let next;
|
|
541
|
+
if (typeof searchValue === "string") {
|
|
542
|
+
if (searchValue.length === 0) throw new Error("Text replacement search cannot be empty");
|
|
543
|
+
next = this.value.split(searchValue).join(replaceValue);
|
|
544
|
+
} else {
|
|
545
|
+
next = this.value.replace(searchValue, replaceValue);
|
|
546
|
+
}
|
|
547
|
+
assertSafeText(next, "text");
|
|
548
|
+
this.value = next;
|
|
549
|
+
return before === next ? 0 : 1;
|
|
550
|
+
}
|
|
551
|
+
toString() {
|
|
552
|
+
return this.value;
|
|
553
|
+
}
|
|
554
|
+
toJSON() {
|
|
555
|
+
return this.value;
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
var PresentationShape = class {
|
|
559
|
+
constructor(slide, config) {
|
|
560
|
+
this.slide = slide;
|
|
561
|
+
if (!PRESENTATION_GEOMETRIES.has(config.geometry)) {
|
|
562
|
+
throw new InvalidPresentationInputError("shape geometry", "is not supported");
|
|
563
|
+
}
|
|
564
|
+
this.id = slide.presentation.allocateId("sh", this);
|
|
565
|
+
this.geometry = config.geometry;
|
|
566
|
+
this.name = config.name ?? this.id;
|
|
567
|
+
assertSafeText(this.name, "shape name");
|
|
568
|
+
this.position = normalizePosition(config.position);
|
|
569
|
+
this.fill = config.fill ?? (config.geometry === "textbox" || config.geometry === "line" ? "none" : "white");
|
|
570
|
+
this.line = {
|
|
571
|
+
style: "solid",
|
|
572
|
+
fill: config.geometry === "textbox" ? "none" : "slate-900",
|
|
573
|
+
width: 1,
|
|
574
|
+
...config.line
|
|
575
|
+
};
|
|
576
|
+
colorValue(this.fill);
|
|
577
|
+
validateLine(this.line, "shape line");
|
|
578
|
+
this.rotation = finiteOr(config.rotation, 0);
|
|
579
|
+
validateRotation(this.rotation, "shape rotation");
|
|
580
|
+
this.borderRadius = config.borderRadius;
|
|
581
|
+
validateBorderRadius(this.borderRadius, "shape borderRadius");
|
|
582
|
+
this.placeholder = normalizePlaceholder(config.placeholder);
|
|
583
|
+
this.textValue = new PresentationText(config.text, config.textStyle);
|
|
584
|
+
}
|
|
585
|
+
id;
|
|
586
|
+
geometry;
|
|
587
|
+
name;
|
|
588
|
+
position;
|
|
589
|
+
fill;
|
|
590
|
+
line;
|
|
591
|
+
rotation;
|
|
592
|
+
borderRadius;
|
|
593
|
+
placeholder;
|
|
594
|
+
textValue;
|
|
595
|
+
get text() {
|
|
596
|
+
return this.textValue;
|
|
597
|
+
}
|
|
598
|
+
set text(value) {
|
|
599
|
+
this.textValue.set(value.toString());
|
|
600
|
+
if (value instanceof PresentationText) this.textValue.style = value.style;
|
|
601
|
+
}
|
|
602
|
+
inspectRecord(slideIndex) {
|
|
603
|
+
const text = this.text.toString();
|
|
604
|
+
return {
|
|
605
|
+
kind: this.geometry === "textbox" ? "textbox" : "shape",
|
|
606
|
+
id: this.id,
|
|
607
|
+
slide: slideIndex + 1,
|
|
608
|
+
name: this.name,
|
|
609
|
+
geometry: this.geometry,
|
|
610
|
+
text,
|
|
611
|
+
textPreview: truncate(text, 240),
|
|
612
|
+
textChars: text.length,
|
|
613
|
+
textLines: text.length === 0 ? 0 : text.split("\n").length,
|
|
614
|
+
bbox: positionTuple(this.position),
|
|
615
|
+
bboxUnit: "px"
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
layoutSnapshot() {
|
|
619
|
+
return {
|
|
620
|
+
kind: this.geometry === "textbox" ? "textbox" : "shape",
|
|
621
|
+
id: this.id,
|
|
622
|
+
name: this.name,
|
|
623
|
+
geometry: this.geometry,
|
|
624
|
+
position: { ...this.position },
|
|
625
|
+
fill: this.fill,
|
|
626
|
+
line: { ...this.line },
|
|
627
|
+
rotation: this.rotation,
|
|
628
|
+
text: this.text.toString(),
|
|
629
|
+
textStyle: { ...this.text.style },
|
|
630
|
+
placeholder: this.placeholder ? { ...this.placeholder } : void 0
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
var PresentationShapeCollection = class {
|
|
635
|
+
constructor(slide) {
|
|
636
|
+
this.slide = slide;
|
|
637
|
+
}
|
|
638
|
+
items = [];
|
|
639
|
+
add(config) {
|
|
640
|
+
const shape = new PresentationShape(this.slide, config);
|
|
641
|
+
this.items.push(shape);
|
|
642
|
+
this.slide.appendElement(shape);
|
|
643
|
+
return shape;
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
var PresentationChartSeries = class {
|
|
647
|
+
name;
|
|
648
|
+
categories;
|
|
649
|
+
values;
|
|
650
|
+
xValues;
|
|
651
|
+
bubbleSizes;
|
|
652
|
+
fill;
|
|
653
|
+
line;
|
|
654
|
+
constructor(config) {
|
|
655
|
+
this.name = config.name;
|
|
656
|
+
assertSafeText(this.name, "chart series name");
|
|
657
|
+
this.categories = [...config.categories ?? []];
|
|
658
|
+
for (const category of this.categories) assertSafeText(category, "chart series category");
|
|
659
|
+
this.values = [...config.values ?? []];
|
|
660
|
+
this.xValues = [...config.xValues ?? []];
|
|
661
|
+
this.bubbleSizes = [...config.bubbleSizes ?? []];
|
|
662
|
+
this.fill = config.fill;
|
|
663
|
+
this.line = config.line ? { ...config.line } : void 0;
|
|
664
|
+
validateFiniteArray(this.values, "chart series values");
|
|
665
|
+
validateFiniteArray(this.xValues, "chart series xValues");
|
|
666
|
+
validateFiniteArray(this.bubbleSizes, "chart series bubbleSizes");
|
|
667
|
+
if (this.fill !== void 0) colorValue(this.fill);
|
|
668
|
+
if (this.line !== void 0) validateLine(this.line, "chart series line");
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
var PresentationChartSeriesCollection = class {
|
|
672
|
+
items;
|
|
673
|
+
constructor(series) {
|
|
674
|
+
this.items = series.map((config) => new PresentationChartSeries(config));
|
|
675
|
+
}
|
|
676
|
+
getItemAt(index) {
|
|
677
|
+
const series = this.items[index];
|
|
678
|
+
if (!series) throw new Error(`Chart series index out of range: ${index}`);
|
|
679
|
+
return series;
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
var PresentationChart = class {
|
|
683
|
+
constructor(slide, type, config) {
|
|
684
|
+
this.slide = slide;
|
|
685
|
+
if (!CHART_TYPES.has(type)) {
|
|
686
|
+
throw new InvalidPresentationInputError("chart type", "is not supported");
|
|
687
|
+
}
|
|
688
|
+
this.id = slide.presentation.allocateId("ch", this);
|
|
689
|
+
this.type = type;
|
|
690
|
+
this.name = config.name ?? this.id;
|
|
691
|
+
assertSafeText(this.name, "chart name");
|
|
692
|
+
this.position = normalizePosition(config.position, {
|
|
693
|
+
left: 72,
|
|
694
|
+
top: 120,
|
|
695
|
+
width: 640,
|
|
696
|
+
height: 360
|
|
697
|
+
});
|
|
698
|
+
this.title = config.title ?? "";
|
|
699
|
+
assertSafeText(this.title, "chart title");
|
|
700
|
+
this.categories = [...config.categories ?? []];
|
|
701
|
+
for (const category of this.categories) assertSafeText(category, "chart category");
|
|
702
|
+
this.series = new PresentationChartSeriesCollection(config.series ?? []);
|
|
703
|
+
this.hasLegend = config.hasLegend ?? this.series.items.length > 1;
|
|
704
|
+
this.legend = config.legend ? { ...config.legend } : void 0;
|
|
705
|
+
this.xAxis = config.xAxis ? { ...config.xAxis } : void 0;
|
|
706
|
+
this.yAxis = config.yAxis ? { ...config.yAxis } : void 0;
|
|
707
|
+
validateOptionalFinite(this.xAxis?.min, "chart xAxis.min");
|
|
708
|
+
validateOptionalFinite(this.xAxis?.max, "chart xAxis.max");
|
|
709
|
+
validateOptionalFinite(this.yAxis?.min, "chart yAxis.min");
|
|
710
|
+
validateOptionalFinite(this.yAxis?.max, "chart yAxis.max");
|
|
711
|
+
this.dataLabels = config.dataLabels ? { ...config.dataLabels } : void 0;
|
|
712
|
+
}
|
|
713
|
+
id;
|
|
714
|
+
type;
|
|
715
|
+
name;
|
|
716
|
+
position;
|
|
717
|
+
title;
|
|
718
|
+
categories;
|
|
719
|
+
series;
|
|
720
|
+
hasLegend;
|
|
721
|
+
legend;
|
|
722
|
+
xAxis;
|
|
723
|
+
yAxis;
|
|
724
|
+
dataLabels;
|
|
725
|
+
inspectRecord(slideIndex) {
|
|
726
|
+
return {
|
|
727
|
+
kind: "chart",
|
|
728
|
+
id: this.id,
|
|
729
|
+
slide: slideIndex + 1,
|
|
730
|
+
name: this.name,
|
|
731
|
+
chartType: this.type,
|
|
732
|
+
title: this.title,
|
|
733
|
+
series: this.series.items.map((series) => ({ name: series.name, values: series.values })),
|
|
734
|
+
bbox: positionTuple(this.position),
|
|
735
|
+
bboxUnit: "px"
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
layoutSnapshot() {
|
|
739
|
+
return {
|
|
740
|
+
kind: "chart",
|
|
741
|
+
id: this.id,
|
|
742
|
+
name: this.name,
|
|
743
|
+
chartType: this.type,
|
|
744
|
+
title: this.title,
|
|
745
|
+
position: { ...this.position },
|
|
746
|
+
categories: [...this.categories],
|
|
747
|
+
series: this.series.items.map((series) => ({
|
|
748
|
+
name: series.name,
|
|
749
|
+
categories: [...series.categories],
|
|
750
|
+
values: [...series.values],
|
|
751
|
+
xValues: [...series.xValues],
|
|
752
|
+
bubbleSizes: [...series.bubbleSizes],
|
|
753
|
+
fill: series.fill,
|
|
754
|
+
line: series.line
|
|
755
|
+
}))
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
var PresentationChartCollection = class {
|
|
760
|
+
constructor(slide) {
|
|
761
|
+
this.slide = slide;
|
|
762
|
+
}
|
|
763
|
+
items = [];
|
|
764
|
+
add(type, config = {}) {
|
|
765
|
+
const chart = new PresentationChart(this.slide, type, config);
|
|
766
|
+
this.items.push(chart);
|
|
767
|
+
this.slide.appendElement(chart);
|
|
768
|
+
return chart;
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
var PresentationTableCell = class {
|
|
772
|
+
text;
|
|
773
|
+
fill;
|
|
774
|
+
rowSpan;
|
|
775
|
+
colSpan;
|
|
776
|
+
constructor(config, defaults) {
|
|
777
|
+
const normalized = typeof config === "string" ? { text: config } : config;
|
|
778
|
+
this.text = new PresentationText(normalized.text, normalized.textStyle ?? defaults.textStyle);
|
|
779
|
+
this.fill = normalized.fill ?? defaults.fill ?? "white";
|
|
780
|
+
colorValue(this.fill);
|
|
781
|
+
this.rowSpan = normalized.rowSpan ?? 1;
|
|
782
|
+
this.colSpan = normalized.colSpan ?? 1;
|
|
783
|
+
for (const [field, value] of [
|
|
784
|
+
["rowSpan", this.rowSpan],
|
|
785
|
+
["colSpan", this.colSpan]
|
|
786
|
+
]) {
|
|
787
|
+
if (!Number.isSafeInteger(value) || value <= 0 || value > MAX_TABLE_SPAN) {
|
|
788
|
+
throw new InvalidPresentationInputError(
|
|
789
|
+
`table cell ${field}`,
|
|
790
|
+
`must be 1-${MAX_TABLE_SPAN}`
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
toConfig() {
|
|
796
|
+
return {
|
|
797
|
+
text: this.text.toString(),
|
|
798
|
+
fill: this.fill,
|
|
799
|
+
textStyle: { ...this.text.style },
|
|
800
|
+
rowSpan: this.rowSpan,
|
|
801
|
+
colSpan: this.colSpan
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
var PresentationTable = class {
|
|
806
|
+
constructor(slide, config) {
|
|
807
|
+
this.slide = slide;
|
|
808
|
+
if (!Array.isArray(config.rows) || config.rows.length === 0 || config.rows.length > MAX_TABLE_ROWS) {
|
|
809
|
+
throw new InvalidPresentationInputError(
|
|
810
|
+
"table rows",
|
|
811
|
+
`must contain 1-${MAX_TABLE_ROWS} rows`
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
const columns = config.rows[0]?.length ?? 0;
|
|
815
|
+
if (columns === 0 || columns > MAX_TABLE_COLUMNS) {
|
|
816
|
+
throw new InvalidPresentationInputError(
|
|
817
|
+
"table columns",
|
|
818
|
+
`must contain 1-${MAX_TABLE_COLUMNS} columns`
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
if (config.rows.some((row) => !Array.isArray(row) || row.length !== columns)) {
|
|
822
|
+
throw new InvalidPresentationInputError("table rows", "must form a rectangular matrix");
|
|
823
|
+
}
|
|
824
|
+
this.id = slide.presentation.allocateId("tb", this);
|
|
825
|
+
this.name = config.name ?? this.id;
|
|
826
|
+
assertSafeText(this.name, "table name", 512);
|
|
827
|
+
this.position = normalizePosition(config.position, {
|
|
828
|
+
left: 72,
|
|
829
|
+
top: 120,
|
|
830
|
+
width: 640,
|
|
831
|
+
height: Math.max(48, config.rows.length * 36)
|
|
832
|
+
});
|
|
833
|
+
this.fill = config.fill ?? "white";
|
|
834
|
+
colorValue(this.fill);
|
|
835
|
+
this.line = { style: "solid", fill: "slate-300", width: 1, ...config.line };
|
|
836
|
+
validateLine(this.line, "table line");
|
|
837
|
+
this.textStyle = { fontSize: 16, ...config.textStyle };
|
|
838
|
+
validateTextStyle(this.textStyle, "table textStyle");
|
|
839
|
+
this.rows = config.rows.map(
|
|
840
|
+
(row) => row.map(
|
|
841
|
+
(cell) => cell === null ? null : new PresentationTableCell(cell, this)
|
|
842
|
+
)
|
|
843
|
+
);
|
|
844
|
+
validateTableOccupancy(this.rows);
|
|
845
|
+
this.columnWidths = normalizeTableDimensions(config.columnWidths, columns, "columnWidths");
|
|
846
|
+
this.rowHeights = normalizeTableDimensions(config.rowHeights, config.rows.length, "rowHeights");
|
|
847
|
+
}
|
|
848
|
+
id;
|
|
849
|
+
name;
|
|
850
|
+
position;
|
|
851
|
+
rows;
|
|
852
|
+
columnWidths;
|
|
853
|
+
rowHeights;
|
|
854
|
+
fill;
|
|
855
|
+
line;
|
|
856
|
+
textStyle;
|
|
857
|
+
inspectRecord(slideIndex) {
|
|
858
|
+
return {
|
|
859
|
+
kind: "table",
|
|
860
|
+
id: this.id,
|
|
861
|
+
slide: slideIndex + 1,
|
|
862
|
+
name: this.name,
|
|
863
|
+
rows: this.rows.length,
|
|
864
|
+
columns: this.rows[0]?.length ?? 0,
|
|
865
|
+
textPreview: truncate(
|
|
866
|
+
this.rows.map((row) => row.map((cell) => cell?.text.toString() ?? "").join(" | ")).join("\n"),
|
|
867
|
+
240
|
|
868
|
+
),
|
|
869
|
+
bbox: positionTuple(this.position),
|
|
870
|
+
bboxUnit: "px"
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
layoutSnapshot() {
|
|
874
|
+
return {
|
|
875
|
+
kind: "table",
|
|
876
|
+
id: this.id,
|
|
877
|
+
name: this.name,
|
|
878
|
+
position: { ...this.position },
|
|
879
|
+
rows: this.rows.map((row) => row.map((cell) => cell?.toConfig() ?? null)),
|
|
880
|
+
columnWidths: [...this.columnWidths],
|
|
881
|
+
rowHeights: [...this.rowHeights],
|
|
882
|
+
fill: this.fill,
|
|
883
|
+
line: { ...this.line },
|
|
884
|
+
textStyle: { ...this.textStyle }
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
var PresentationTableCollection = class {
|
|
889
|
+
constructor(slide) {
|
|
890
|
+
this.slide = slide;
|
|
891
|
+
}
|
|
892
|
+
items = [];
|
|
893
|
+
add(config) {
|
|
894
|
+
const table = new PresentationTable(this.slide, config);
|
|
895
|
+
this.items.push(table);
|
|
896
|
+
this.slide.appendElement(table);
|
|
897
|
+
return table;
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
var PresentationGroup = class _PresentationGroup {
|
|
901
|
+
constructor(slide, config = {}, depth = 1) {
|
|
902
|
+
this.slide = slide;
|
|
903
|
+
if (depth > MAX_GROUP_DEPTH) {
|
|
904
|
+
throw new InvalidPresentationInputError("group nesting", `exceeds ${MAX_GROUP_DEPTH} levels`);
|
|
905
|
+
}
|
|
906
|
+
this.id = slide.presentation.allocateId("gp", this);
|
|
907
|
+
this.name = config.name ?? this.id;
|
|
908
|
+
assertSafeText(this.name, "group name", 512);
|
|
909
|
+
this.position = normalizePosition(config.position);
|
|
910
|
+
this.childOffset = config.childOffset ? { ...config.childOffset } : { left: this.position.left, top: this.position.top };
|
|
911
|
+
this.childExtent = config.childExtent ? { ...config.childExtent } : { width: this.position.width, height: this.position.height };
|
|
912
|
+
validatePosition(
|
|
913
|
+
{
|
|
914
|
+
left: this.childOffset.left,
|
|
915
|
+
top: this.childOffset.top,
|
|
916
|
+
width: this.childExtent.width,
|
|
917
|
+
height: this.childExtent.height
|
|
918
|
+
},
|
|
919
|
+
"group child coordinate system"
|
|
920
|
+
);
|
|
921
|
+
this.rotation = finiteOr(config.rotation, 0);
|
|
922
|
+
validateRotation(this.rotation, "group rotation");
|
|
923
|
+
this.flipHorizontal = config.flipHorizontal ?? false;
|
|
924
|
+
this.flipVertical = config.flipVertical ?? false;
|
|
925
|
+
for (const child of config.children ?? []) {
|
|
926
|
+
if (this.children.length >= MAX_GROUP_CHILDREN) {
|
|
927
|
+
throw new InvalidPresentationInputError(
|
|
928
|
+
"group children",
|
|
929
|
+
`exceeds ${MAX_GROUP_CHILDREN} children`
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
switch (child.kind) {
|
|
933
|
+
case "shape":
|
|
934
|
+
this.children.push(new PresentationShape(slide, child.config));
|
|
935
|
+
break;
|
|
936
|
+
case "chart":
|
|
937
|
+
this.children.push(new PresentationChart(slide, child.type, child.config ?? {}));
|
|
938
|
+
break;
|
|
939
|
+
case "image":
|
|
940
|
+
this.children.push(new PresentationImage(slide, child.config));
|
|
941
|
+
break;
|
|
942
|
+
case "table":
|
|
943
|
+
this.children.push(new PresentationTable(slide, child.config));
|
|
944
|
+
break;
|
|
945
|
+
case "group":
|
|
946
|
+
this.children.push(new _PresentationGroup(slide, child.config, depth + 1));
|
|
947
|
+
break;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
id;
|
|
952
|
+
name;
|
|
953
|
+
position;
|
|
954
|
+
childOffset;
|
|
955
|
+
childExtent;
|
|
956
|
+
rotation;
|
|
957
|
+
flipHorizontal;
|
|
958
|
+
flipVertical;
|
|
959
|
+
children = [];
|
|
960
|
+
inspectRecord(slideIndex) {
|
|
961
|
+
return {
|
|
962
|
+
kind: "group",
|
|
963
|
+
id: this.id,
|
|
964
|
+
slide: slideIndex + 1,
|
|
965
|
+
name: this.name,
|
|
966
|
+
children: this.children.length,
|
|
967
|
+
bbox: positionTuple(this.position),
|
|
968
|
+
bboxUnit: "px"
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
layoutSnapshot() {
|
|
972
|
+
return {
|
|
973
|
+
kind: "group",
|
|
974
|
+
id: this.id,
|
|
975
|
+
name: this.name,
|
|
976
|
+
position: { ...this.position },
|
|
977
|
+
childOffset: { ...this.childOffset },
|
|
978
|
+
childExtent: { ...this.childExtent },
|
|
979
|
+
rotation: this.rotation,
|
|
980
|
+
flipHorizontal: this.flipHorizontal,
|
|
981
|
+
flipVertical: this.flipVertical,
|
|
982
|
+
children: this.children.map((child) => child.layoutSnapshot())
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
};
|
|
986
|
+
var PresentationGroupCollection = class {
|
|
987
|
+
constructor(slide) {
|
|
988
|
+
this.slide = slide;
|
|
989
|
+
}
|
|
990
|
+
items = [];
|
|
991
|
+
add(config = {}) {
|
|
992
|
+
const group = new PresentationGroup(this.slide, config);
|
|
993
|
+
this.items.push(group);
|
|
994
|
+
this.slide.appendElement(group);
|
|
995
|
+
return group;
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
var PresentationImage = class {
|
|
999
|
+
constructor(slide, config) {
|
|
1000
|
+
this.slide = slide;
|
|
1001
|
+
this.id = slide.presentation.allocateId("im", this);
|
|
1002
|
+
this.name = config.name ?? this.id;
|
|
1003
|
+
assertSafeText(this.name, "image name");
|
|
1004
|
+
this.position = normalizePosition(config.position ?? config.frame, {
|
|
1005
|
+
left: 72,
|
|
1006
|
+
top: 120,
|
|
1007
|
+
width: 480,
|
|
1008
|
+
height: 270
|
|
1009
|
+
});
|
|
1010
|
+
this.fit = config.fit ?? "contain";
|
|
1011
|
+
if (this.fit !== "contain" && this.fit !== "cover") {
|
|
1012
|
+
throw new InvalidPresentationInputError("image fit", "must be contain or cover");
|
|
1013
|
+
}
|
|
1014
|
+
this.contentType = config.contentType;
|
|
1015
|
+
this.alt = config.alt ?? "";
|
|
1016
|
+
assertSafeText(this.alt, "image alt");
|
|
1017
|
+
this.prompt = config.prompt;
|
|
1018
|
+
if (this.prompt !== void 0) assertSafeText(this.prompt, "image prompt");
|
|
1019
|
+
this.crop = config.crop ? normalizeCrop(config.crop) : void 0;
|
|
1020
|
+
this.geometry = config.geometry ?? "rect";
|
|
1021
|
+
if (!["rect", "roundRect", "ellipse"].includes(this.geometry)) {
|
|
1022
|
+
throw new InvalidPresentationInputError("image geometry", "is not supported");
|
|
1023
|
+
}
|
|
1024
|
+
this.borderRadius = config.borderRadius;
|
|
1025
|
+
validateBorderRadius(this.borderRadius, "image borderRadius");
|
|
1026
|
+
this.rotation = finiteOr(config.rotation, 0);
|
|
1027
|
+
validateRotation(this.rotation, "image rotation");
|
|
1028
|
+
this.flipHorizontal = config.flipHorizontal ?? false;
|
|
1029
|
+
this.flipVertical = config.flipVertical ?? false;
|
|
1030
|
+
this.lockAspectRatio = config.lockAspectRatio ?? false;
|
|
1031
|
+
this.assignSource(config);
|
|
1032
|
+
}
|
|
1033
|
+
id;
|
|
1034
|
+
name;
|
|
1035
|
+
position;
|
|
1036
|
+
fit;
|
|
1037
|
+
contentType;
|
|
1038
|
+
alt;
|
|
1039
|
+
prompt;
|
|
1040
|
+
crop;
|
|
1041
|
+
geometry;
|
|
1042
|
+
borderRadius;
|
|
1043
|
+
rotation;
|
|
1044
|
+
flipHorizontal;
|
|
1045
|
+
flipVertical;
|
|
1046
|
+
lockAspectRatio;
|
|
1047
|
+
blob;
|
|
1048
|
+
dataUrl;
|
|
1049
|
+
uri;
|
|
1050
|
+
validatedContentType;
|
|
1051
|
+
cachedCanonicalDataUrl;
|
|
1052
|
+
get frame() {
|
|
1053
|
+
return this.position;
|
|
1054
|
+
}
|
|
1055
|
+
set frame(value) {
|
|
1056
|
+
this.position = normalizePosition(value);
|
|
1057
|
+
}
|
|
1058
|
+
replace(config) {
|
|
1059
|
+
if (config.alt !== void 0) assertSafeText(config.alt, "image alt");
|
|
1060
|
+
if (config.prompt !== void 0) assertSafeText(config.prompt, "image prompt");
|
|
1061
|
+
if (config.fit !== void 0 && config.fit !== "contain" && config.fit !== "cover") {
|
|
1062
|
+
throw new InvalidPresentationInputError("image fit", "must be contain or cover");
|
|
1063
|
+
}
|
|
1064
|
+
this.assignSource(config);
|
|
1065
|
+
if (config.alt !== void 0) this.alt = config.alt;
|
|
1066
|
+
if (config.fit !== void 0) this.fit = config.fit;
|
|
1067
|
+
if (config.prompt !== void 0) this.prompt = config.prompt;
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Explicitly materializes a host-owned URI into trusted raster bytes.
|
|
1071
|
+
*
|
|
1072
|
+
* Render and file export never invoke resolvers, fetch, or read files. A host
|
|
1073
|
+
* must call this method deliberately, and returned bytes still pass the same
|
|
1074
|
+
* MIME/signature validation as direct `blob` input.
|
|
1075
|
+
*/
|
|
1076
|
+
async resolveUri() {
|
|
1077
|
+
if (!this.uri) {
|
|
1078
|
+
throw new InvalidPresentationInputError(
|
|
1079
|
+
"image uri",
|
|
1080
|
+
`Image ${this.id} has no URI to resolve`
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
const uri = this.uri;
|
|
1084
|
+
assertSafeText(this.name, `image ${this.id} name`);
|
|
1085
|
+
let resolution;
|
|
1086
|
+
try {
|
|
1087
|
+
resolution = await this.slide.presentation.resolveImageUri({
|
|
1088
|
+
uri,
|
|
1089
|
+
imageId: this.id,
|
|
1090
|
+
name: this.name
|
|
1091
|
+
});
|
|
1092
|
+
} catch (error) {
|
|
1093
|
+
const failure = new InvalidPresentationInputError(
|
|
1094
|
+
"image resolver",
|
|
1095
|
+
`failed to resolve image ${this.id}`
|
|
1096
|
+
);
|
|
1097
|
+
Object.defineProperty(failure, "cause", { value: error, enumerable: false });
|
|
1098
|
+
throw failure;
|
|
1099
|
+
}
|
|
1100
|
+
if (!resolution || typeof resolution !== "object" || !("blob" in resolution)) {
|
|
1101
|
+
throw new InvalidPresentationInputError(
|
|
1102
|
+
"image resolver",
|
|
1103
|
+
"must return { blob, contentType? }"
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
this.assignSource({
|
|
1107
|
+
blob: resolution.blob,
|
|
1108
|
+
...resolution.contentType ? { contentType: resolution.contentType } : {}
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
sourceForSvg() {
|
|
1112
|
+
if (this.uri) throw unresolvedImageUriError(this.id);
|
|
1113
|
+
if (this.dataUrl) {
|
|
1114
|
+
if (!this.contentType) {
|
|
1115
|
+
throw new InvalidPresentationInputError(
|
|
1116
|
+
"image contentType",
|
|
1117
|
+
`Image ${this.id} has no validated raster MIME type`
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
const contentType = normalizeRasterMime(this.contentType);
|
|
1121
|
+
if (contentType !== this.validatedContentType || !this.dataUrl.startsWith(`data:${contentType};base64,`)) {
|
|
1122
|
+
throw new InvalidPresentationInputError(
|
|
1123
|
+
"image contentType",
|
|
1124
|
+
`Image ${this.id} MIME type no longer matches its data URL`
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1127
|
+
return this.cachedCanonicalDataUrl ??= this.dataUrl;
|
|
1128
|
+
}
|
|
1129
|
+
if (this.blob) {
|
|
1130
|
+
if (!this.contentType) {
|
|
1131
|
+
throw new InvalidPresentationInputError(
|
|
1132
|
+
"image contentType",
|
|
1133
|
+
`Image ${this.id} has no validated raster MIME type`
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
const contentType = normalizeRasterMime(this.contentType);
|
|
1137
|
+
if (contentType !== this.validatedContentType) {
|
|
1138
|
+
throw new InvalidPresentationInputError(
|
|
1139
|
+
"image contentType",
|
|
1140
|
+
`Image ${this.id} MIME type no longer matches its validated bytes`
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
return this.cachedCanonicalDataUrl ??= `data:${contentType};base64,${encodeRasterBase64(this.blob)}`;
|
|
1144
|
+
}
|
|
1145
|
+
return void 0;
|
|
1146
|
+
}
|
|
1147
|
+
sourceForPptx() {
|
|
1148
|
+
const source = this.sourceForSvg();
|
|
1149
|
+
if (!source) {
|
|
1150
|
+
throw new UnsupportedPresentationFeatureError(
|
|
1151
|
+
"prompt-only image PPTX export",
|
|
1152
|
+
`Image ${this.id} must be regenerated to validated raster bytes or a raster data URL before export`
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
return { data: source };
|
|
1156
|
+
}
|
|
1157
|
+
inspectRecord(slideIndex) {
|
|
1158
|
+
return {
|
|
1159
|
+
kind: "image",
|
|
1160
|
+
id: this.id,
|
|
1161
|
+
slide: slideIndex + 1,
|
|
1162
|
+
name: this.name,
|
|
1163
|
+
alt: this.alt,
|
|
1164
|
+
prompt: this.prompt,
|
|
1165
|
+
fit: this.fit,
|
|
1166
|
+
geometry: this.geometry,
|
|
1167
|
+
source: this.blob ? "blob" : this.dataUrl ? "dataUrl" : this.uri ? "uri" : "prompt",
|
|
1168
|
+
bbox: positionTuple(this.position),
|
|
1169
|
+
bboxUnit: "px"
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
layoutSnapshot() {
|
|
1173
|
+
return {
|
|
1174
|
+
kind: "image",
|
|
1175
|
+
id: this.id,
|
|
1176
|
+
name: this.name,
|
|
1177
|
+
alt: this.alt,
|
|
1178
|
+
prompt: this.prompt,
|
|
1179
|
+
fit: this.fit,
|
|
1180
|
+
geometry: this.geometry,
|
|
1181
|
+
position: { ...this.position },
|
|
1182
|
+
crop: this.crop ? { ...this.crop } : void 0,
|
|
1183
|
+
rotation: this.rotation
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
assignSource(config) {
|
|
1187
|
+
const concrete = [
|
|
1188
|
+
"blob" in config && config.blob !== void 0,
|
|
1189
|
+
"dataUrl" in config && config.dataUrl !== void 0,
|
|
1190
|
+
"uri" in config && config.uri !== void 0
|
|
1191
|
+
].filter(Boolean).length;
|
|
1192
|
+
if (concrete > 1)
|
|
1193
|
+
throw new InvalidPresentationInputError(
|
|
1194
|
+
"image source",
|
|
1195
|
+
"accepts exactly one concrete source: blob, dataUrl, or uri"
|
|
1196
|
+
);
|
|
1197
|
+
if (concrete === 0 && !config.prompt)
|
|
1198
|
+
throw new InvalidPresentationInputError(
|
|
1199
|
+
"image source",
|
|
1200
|
+
"requires blob, dataUrl, uri, or prompt"
|
|
1201
|
+
);
|
|
1202
|
+
let blob;
|
|
1203
|
+
let dataUrl;
|
|
1204
|
+
let uri;
|
|
1205
|
+
let contentType;
|
|
1206
|
+
if ("blob" in config && config.blob !== void 0) {
|
|
1207
|
+
const bytes = copyImageBytes(config.blob);
|
|
1208
|
+
blob = bytes;
|
|
1209
|
+
contentType = validateRasterBytes(bytes, config.contentType, "image blob");
|
|
1210
|
+
} else if ("dataUrl" in config && config.dataUrl !== void 0) {
|
|
1211
|
+
const parsed = parseRasterDataUrl(config.dataUrl);
|
|
1212
|
+
if (config.contentType !== void 0 && normalizeRasterMime(config.contentType) !== parsed.type) {
|
|
1213
|
+
throw new InvalidPresentationInputError(
|
|
1214
|
+
"image contentType",
|
|
1215
|
+
"does not match the data URL MIME type"
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
dataUrl = parsed.canonical;
|
|
1219
|
+
contentType = parsed.type;
|
|
1220
|
+
} else if ("uri" in config && config.uri !== void 0) {
|
|
1221
|
+
this.slide.presentation.assertImageUriAccepted(config.uri);
|
|
1222
|
+
uri = config.uri;
|
|
1223
|
+
}
|
|
1224
|
+
this.blob = blob;
|
|
1225
|
+
this.dataUrl = dataUrl;
|
|
1226
|
+
this.uri = uri;
|
|
1227
|
+
this.contentType = contentType;
|
|
1228
|
+
this.validatedContentType = contentType;
|
|
1229
|
+
this.cachedCanonicalDataUrl = void 0;
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1232
|
+
var PresentationImageCollection = class {
|
|
1233
|
+
constructor(slide) {
|
|
1234
|
+
this.slide = slide;
|
|
1235
|
+
}
|
|
1236
|
+
items = [];
|
|
1237
|
+
add(config) {
|
|
1238
|
+
const image = new PresentationImage(this.slide, config);
|
|
1239
|
+
this.items.push(image);
|
|
1240
|
+
this.slide.appendElement(image);
|
|
1241
|
+
return image;
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
var PresentationFile = class {
|
|
1245
|
+
static async exportPptx(presentation, options = {}) {
|
|
1246
|
+
validatePresentation(presentation, true);
|
|
1247
|
+
const { exportPresentationPptx } = await import("@opengeni/artifact-tool/presentation/pptx");
|
|
1248
|
+
return await exportPresentationPptx(presentation, options);
|
|
1249
|
+
}
|
|
1250
|
+
static async importPptx(input, options = {}) {
|
|
1251
|
+
const { importPresentationPptx } = await import("@opengeni/artifact-tool/presentation/pptx");
|
|
1252
|
+
return await importPresentationPptx(input, options);
|
|
1253
|
+
}
|
|
1254
|
+
static fidelityReport(presentation) {
|
|
1255
|
+
const issues = [];
|
|
1256
|
+
if (presentation.masters.items.length > 1 || presentation.masters.items.some((master) => master.elements.length > 0)) {
|
|
1257
|
+
issues.push({
|
|
1258
|
+
code: "content-preserved-in-source",
|
|
1259
|
+
severity: "warning",
|
|
1260
|
+
feature: "master",
|
|
1261
|
+
message: "Master hierarchy is editable but requires the package-native PPTX writer for regeneration"
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
if (presentation.layouts.items.length > 1 || presentation.layouts.items.some((layout) => layout.elements.length > 0)) {
|
|
1265
|
+
issues.push({
|
|
1266
|
+
code: "content-preserved-in-source",
|
|
1267
|
+
severity: "warning",
|
|
1268
|
+
feature: "layout",
|
|
1269
|
+
message: "Layout inheritance is editable but requires the package-native PPTX writer for regeneration"
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
if (presentation.slides.items.some((slide) => slide.groups.items.length > 0)) {
|
|
1273
|
+
issues.push({
|
|
1274
|
+
code: "group-will-be-flattened",
|
|
1275
|
+
severity: "warning",
|
|
1276
|
+
feature: "group",
|
|
1277
|
+
message: "PptxGenJS cannot emit group scene nodes; explicit lossy export flattens their children"
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
for (const slide of presentation.slides.items) {
|
|
1281
|
+
for (const chart of slide.charts.items) {
|
|
1282
|
+
if ((chart.type === "scatter" || chart.type === "bubble") && !chartSeriesShareXValues(chart)) {
|
|
1283
|
+
issues.push({
|
|
1284
|
+
code: "style-approximated",
|
|
1285
|
+
severity: "warning",
|
|
1286
|
+
feature: "unsupported-chart",
|
|
1287
|
+
message: `Chart ${chart.id} has different X values per series; the fallback PPTX writer only supports one shared X vector`
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
const provenance = presentationLossState(presentation);
|
|
1293
|
+
if (provenance?.unsupportedParts.length) {
|
|
1294
|
+
issues.push({
|
|
1295
|
+
code: "content-preserved-in-source",
|
|
1296
|
+
severity: "warning",
|
|
1297
|
+
feature: "theme",
|
|
1298
|
+
message: "Validated source-only OOXML parts cannot be regenerated after model edits",
|
|
1299
|
+
parts: [...provenance.unsupportedParts]
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
return issues;
|
|
1303
|
+
}
|
|
1304
|
+
static lossPreservationEnvelope(presentation) {
|
|
1305
|
+
const envelope = presentationLossState(presentation);
|
|
1306
|
+
return envelope ? {
|
|
1307
|
+
...envelope,
|
|
1308
|
+
sourceBytes: envelope.sourceBytes.slice(),
|
|
1309
|
+
unsupportedParts: [...envelope.unsupportedParts]
|
|
1310
|
+
} : null;
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
function chartSeriesShareXValues(chart) {
|
|
1314
|
+
const reference = chart.series.items[0]?.xValues ?? [];
|
|
1315
|
+
if (reference.length === 0) return false;
|
|
1316
|
+
return chart.series.items.every(
|
|
1317
|
+
(series) => series.xValues.length === reference.length && series.xValues.every((value, index) => Object.is(value, reference[index]))
|
|
1318
|
+
);
|
|
1319
|
+
}
|
|
1320
|
+
function montageDimensions(presentation, requestedColumns, requestedGap) {
|
|
1321
|
+
const count = presentation.slides.items.length;
|
|
1322
|
+
if (count === 0) {
|
|
1323
|
+
throw new InvalidPresentationInputError("montage", "cannot export an empty presentation");
|
|
1324
|
+
}
|
|
1325
|
+
const columns = requestedColumns ?? Math.ceil(Math.sqrt(count));
|
|
1326
|
+
if (!Number.isInteger(columns) || columns <= 0) {
|
|
1327
|
+
throw new InvalidPresentationInputError("montage columns", "must be a positive integer");
|
|
1328
|
+
}
|
|
1329
|
+
const gap = requestedGap ?? 24;
|
|
1330
|
+
if (!Number.isFinite(gap) || gap < 0) {
|
|
1331
|
+
throw new InvalidPresentationInputError("montage gap", "must be a non-negative number");
|
|
1332
|
+
}
|
|
1333
|
+
const rows = Math.ceil(count / columns);
|
|
1334
|
+
const width = columns * presentation.slideSize.width + (columns + 1) * gap;
|
|
1335
|
+
const height = rows * presentation.slideSize.height + (rows + 1) * gap;
|
|
1336
|
+
if (!Number.isFinite(width) || !Number.isFinite(height)) {
|
|
1337
|
+
throw new InvalidPresentationInputError("montage geometry", "exceeds finite numeric bounds");
|
|
1338
|
+
}
|
|
1339
|
+
return { columns, rows, gap, width, height };
|
|
1340
|
+
}
|
|
1341
|
+
function validateRenderScale(scale) {
|
|
1342
|
+
if (!Number.isFinite(scale) || scale <= 0 || scale > 8) {
|
|
1343
|
+
throw new InvalidPresentationInputError("export scale", "must be greater than 0 and at most 8");
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
function assertRenderPixelBudget(width, height, scale) {
|
|
1347
|
+
const scaledWidth = Math.ceil(width * scale);
|
|
1348
|
+
const scaledHeight = Math.ceil(height * scale);
|
|
1349
|
+
if (!Number.isSafeInteger(scaledWidth) || !Number.isSafeInteger(scaledHeight) || scaledWidth <= 0 || scaledHeight <= 0) {
|
|
1350
|
+
throw new InvalidPresentationInputError(
|
|
1351
|
+
"render geometry",
|
|
1352
|
+
"scaled dimensions must be positive safe integers"
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
if (scaledWidth > MAX_RENDER_DIMENSION || scaledHeight > MAX_RENDER_DIMENSION) {
|
|
1356
|
+
throw new InvalidPresentationInputError(
|
|
1357
|
+
"render dimension limit",
|
|
1358
|
+
`${scaledWidth}x${scaledHeight} exceeds maximum dimension ${MAX_RENDER_DIMENSION}`
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
if (scaledWidth > Math.floor(MAX_RENDER_PIXELS / scaledHeight)) {
|
|
1362
|
+
throw new InvalidPresentationInputError(
|
|
1363
|
+
"render pixel limit",
|
|
1364
|
+
`${scaledWidth}x${scaledHeight} exceeds maximum ${MAX_RENDER_PIXELS} pixels`
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
var MAX_GEOMETRY_VALUE = 1e6;
|
|
1369
|
+
var MAX_TEXT_LENGTH = 1e6;
|
|
1370
|
+
var MAX_FONT_SIZE = 4096;
|
|
1371
|
+
var MAX_IMAGE_BYTES = 32 * 1024 * 1024;
|
|
1372
|
+
var MAX_IMAGE_URI_LENGTH = 4096;
|
|
1373
|
+
var MAX_RENDER_DIMENSION = 32768;
|
|
1374
|
+
var MAX_RENDER_PIXELS = 67108864;
|
|
1375
|
+
var MAX_TABLE_ROWS = 1e4;
|
|
1376
|
+
var MAX_TABLE_COLUMNS = 1024;
|
|
1377
|
+
var MAX_TABLE_SPAN = 1024;
|
|
1378
|
+
var MAX_GROUP_DEPTH = 32;
|
|
1379
|
+
var MAX_GROUP_CHILDREN = 1e5;
|
|
1380
|
+
var PRESENTATION_GEOMETRIES = /* @__PURE__ */ new Set([
|
|
1381
|
+
"textbox",
|
|
1382
|
+
"rect",
|
|
1383
|
+
"roundRect",
|
|
1384
|
+
"ellipse",
|
|
1385
|
+
"triangle",
|
|
1386
|
+
"rightArrow",
|
|
1387
|
+
"line"
|
|
1388
|
+
]);
|
|
1389
|
+
var CHART_TYPES = /* @__PURE__ */ new Set([
|
|
1390
|
+
"bar",
|
|
1391
|
+
"line",
|
|
1392
|
+
"area",
|
|
1393
|
+
"pie",
|
|
1394
|
+
"doughnut",
|
|
1395
|
+
"scatter",
|
|
1396
|
+
"bubble",
|
|
1397
|
+
"radar"
|
|
1398
|
+
]);
|
|
1399
|
+
var LINE_STYLES = /* @__PURE__ */ new Set(["solid", "dash", "dot", "none"]);
|
|
1400
|
+
var TEXT_ALIGNMENTS = /* @__PURE__ */ new Set(["left", "center", "right", "justify"]);
|
|
1401
|
+
var VERTICAL_ALIGNMENTS = /* @__PURE__ */ new Set(["top", "middle", "bottom"]);
|
|
1402
|
+
var BORDER_RADIUS_TOKENS = {
|
|
1403
|
+
rounded: 4,
|
|
1404
|
+
"rounded-lg": 8,
|
|
1405
|
+
"rounded-xl": 12,
|
|
1406
|
+
"rounded-2xl": 16,
|
|
1407
|
+
"rounded-3xl": 24
|
|
1408
|
+
};
|
|
1409
|
+
var FORBIDDEN_IMAGE_URI_SCHEMES = /* @__PURE__ */ new Set([
|
|
1410
|
+
"blob",
|
|
1411
|
+
"data",
|
|
1412
|
+
"file",
|
|
1413
|
+
"ftp",
|
|
1414
|
+
"ftps",
|
|
1415
|
+
"http",
|
|
1416
|
+
"https",
|
|
1417
|
+
"javascript"
|
|
1418
|
+
]);
|
|
1419
|
+
function cloneTemplateElement(element) {
|
|
1420
|
+
if (!element || typeof element !== "object") {
|
|
1421
|
+
throw new InvalidPresentationInputError("template element", "must be an object");
|
|
1422
|
+
}
|
|
1423
|
+
switch (element.kind) {
|
|
1424
|
+
case "shape":
|
|
1425
|
+
return {
|
|
1426
|
+
kind: "shape",
|
|
1427
|
+
config: {
|
|
1428
|
+
...element.config,
|
|
1429
|
+
...element.config.position ? { position: { ...element.config.position } } : {},
|
|
1430
|
+
...element.config.line ? { line: { ...element.config.line } } : {},
|
|
1431
|
+
...element.config.textStyle ? { textStyle: { ...element.config.textStyle } } : {}
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
case "chart":
|
|
1435
|
+
return {
|
|
1436
|
+
kind: "chart",
|
|
1437
|
+
type: element.type,
|
|
1438
|
+
...element.config ? {
|
|
1439
|
+
config: {
|
|
1440
|
+
...element.config,
|
|
1441
|
+
...element.config.position ? { position: { ...element.config.position } } : {},
|
|
1442
|
+
...element.config.categories ? { categories: [...element.config.categories] } : {},
|
|
1443
|
+
...element.config.series ? {
|
|
1444
|
+
series: element.config.series.map((series) => ({
|
|
1445
|
+
...series,
|
|
1446
|
+
...series.categories ? { categories: [...series.categories] } : {},
|
|
1447
|
+
...series.values ? { values: [...series.values] } : {},
|
|
1448
|
+
...series.xValues ? { xValues: [...series.xValues] } : {},
|
|
1449
|
+
...series.bubbleSizes ? { bubbleSizes: [...series.bubbleSizes] } : {},
|
|
1450
|
+
...series.line ? { line: { ...series.line } } : {}
|
|
1451
|
+
}))
|
|
1452
|
+
} : {},
|
|
1453
|
+
...element.config.legend ? { legend: { ...element.config.legend } } : {},
|
|
1454
|
+
...element.config.xAxis ? { xAxis: { ...element.config.xAxis } } : {},
|
|
1455
|
+
...element.config.yAxis ? { yAxis: { ...element.config.yAxis } } : {},
|
|
1456
|
+
...element.config.dataLabels ? { dataLabels: { ...element.config.dataLabels } } : {}
|
|
1457
|
+
}
|
|
1458
|
+
} : {}
|
|
1459
|
+
};
|
|
1460
|
+
case "image": {
|
|
1461
|
+
const config = element.config;
|
|
1462
|
+
return {
|
|
1463
|
+
kind: "image",
|
|
1464
|
+
config: {
|
|
1465
|
+
...config,
|
|
1466
|
+
...config.blob !== void 0 ? {
|
|
1467
|
+
blob: config.blob instanceof Uint8Array ? config.blob.slice() : config.blob.slice(0)
|
|
1468
|
+
} : {},
|
|
1469
|
+
...config.position ? { position: { ...config.position } } : {},
|
|
1470
|
+
...config.frame ? { frame: { ...config.frame } } : {},
|
|
1471
|
+
...config.crop ? { crop: { ...config.crop } } : {}
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
case "table":
|
|
1476
|
+
return {
|
|
1477
|
+
kind: "table",
|
|
1478
|
+
config: {
|
|
1479
|
+
...element.config,
|
|
1480
|
+
...element.config.position ? { position: { ...element.config.position } } : {},
|
|
1481
|
+
rows: element.config.rows.map(
|
|
1482
|
+
(row) => row.map(
|
|
1483
|
+
(cell) => cell === null || typeof cell === "string" ? cell : {
|
|
1484
|
+
...cell,
|
|
1485
|
+
...cell.textStyle ? { textStyle: { ...cell.textStyle } } : {}
|
|
1486
|
+
}
|
|
1487
|
+
)
|
|
1488
|
+
),
|
|
1489
|
+
...element.config.columnWidths ? { columnWidths: [...element.config.columnWidths] } : {},
|
|
1490
|
+
...element.config.rowHeights ? { rowHeights: [...element.config.rowHeights] } : {},
|
|
1491
|
+
...element.config.line ? { line: { ...element.config.line } } : {},
|
|
1492
|
+
...element.config.textStyle ? { textStyle: { ...element.config.textStyle } } : {}
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
function normalizeTableDimensions(values, expected, field) {
|
|
1498
|
+
if (values === void 0) return [];
|
|
1499
|
+
if (!Array.isArray(values) || values.length !== expected) {
|
|
1500
|
+
throw new InvalidPresentationInputError(`table ${field}`, `must contain ${expected} values`);
|
|
1501
|
+
}
|
|
1502
|
+
const output = [...values];
|
|
1503
|
+
if (output.some((value) => !Number.isFinite(value) || value <= 0 || value > MAX_GEOMETRY_VALUE)) {
|
|
1504
|
+
throw new InvalidPresentationInputError(
|
|
1505
|
+
`table ${field}`,
|
|
1506
|
+
"must contain bounded positive numbers"
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1509
|
+
return output;
|
|
1510
|
+
}
|
|
1511
|
+
function validateTableOccupancy(rows) {
|
|
1512
|
+
const rowCount = rows.length;
|
|
1513
|
+
const columnCount = rows[0]?.length ?? 0;
|
|
1514
|
+
const covered = Array.from({ length: rowCount }, () => new Uint8Array(columnCount));
|
|
1515
|
+
for (let row = 0; row < rowCount; row += 1) {
|
|
1516
|
+
for (let column = 0; column < columnCount; column += 1) {
|
|
1517
|
+
const cell = rows[row][column];
|
|
1518
|
+
if (cell === void 0) {
|
|
1519
|
+
throw new InvalidPresentationInputError("table rows", "must form a rectangular matrix");
|
|
1520
|
+
}
|
|
1521
|
+
if (cell === null) {
|
|
1522
|
+
if (covered[row][column] === 0) {
|
|
1523
|
+
throw new InvalidPresentationInputError(
|
|
1524
|
+
"table cell occupancy",
|
|
1525
|
+
`uncovered null at row ${row + 1}, column ${column + 1}`
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
continue;
|
|
1529
|
+
}
|
|
1530
|
+
if (covered[row][column] !== 0) {
|
|
1531
|
+
throw new InvalidPresentationInputError(
|
|
1532
|
+
"table cell occupancy",
|
|
1533
|
+
`spanned position at row ${row + 1}, column ${column + 1} must be null`
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
if (row + cell.rowSpan > rowCount || column + cell.colSpan > columnCount) {
|
|
1537
|
+
throw new InvalidPresentationInputError(
|
|
1538
|
+
"table cell span",
|
|
1539
|
+
`cell at row ${row + 1}, column ${column + 1} extends outside the table`
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
for (let coveredRow = row; coveredRow < row + cell.rowSpan; coveredRow += 1) {
|
|
1543
|
+
for (let coveredColumn = column; coveredColumn < column + cell.colSpan; coveredColumn += 1) {
|
|
1544
|
+
if (coveredRow === row && coveredColumn === column) continue;
|
|
1545
|
+
if (covered[coveredRow][coveredColumn] !== 0) {
|
|
1546
|
+
throw new InvalidPresentationInputError("table cell span", "overlaps another span");
|
|
1547
|
+
}
|
|
1548
|
+
covered[coveredRow][coveredColumn] = 1;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
function normalizePlaceholder(value) {
|
|
1555
|
+
if (value === void 0) return void 0;
|
|
1556
|
+
if (!value || typeof value !== "object") {
|
|
1557
|
+
throw new InvalidPresentationInputError("shape placeholder", "must be an object");
|
|
1558
|
+
}
|
|
1559
|
+
assertSafeText(value.type, "shape placeholder type", 128);
|
|
1560
|
+
if (!/^[A-Za-z][A-Za-z0-9]*$/.test(value.type)) {
|
|
1561
|
+
throw new InvalidPresentationInputError("shape placeholder type", "is invalid");
|
|
1562
|
+
}
|
|
1563
|
+
if (value.index !== void 0 && (!Number.isSafeInteger(value.index) || value.index < 0)) {
|
|
1564
|
+
throw new InvalidPresentationInputError(
|
|
1565
|
+
"shape placeholder index",
|
|
1566
|
+
"must be a non-negative safe integer"
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
1569
|
+
return value.index === void 0 ? { type: value.type } : { type: value.type, index: value.index };
|
|
1570
|
+
}
|
|
1571
|
+
function validateImageUriPolicy(resolver, schemes) {
|
|
1572
|
+
if (schemes !== void 0 && !Array.isArray(schemes)) {
|
|
1573
|
+
throw new InvalidPresentationInputError("allowedImageUriSchemes", "must be an array");
|
|
1574
|
+
}
|
|
1575
|
+
const normalized = /* @__PURE__ */ new Set();
|
|
1576
|
+
for (const value of schemes ?? []) {
|
|
1577
|
+
if (typeof value !== "string" || !/^[a-z][a-z0-9+.-]*$/i.test(value)) {
|
|
1578
|
+
throw new InvalidPresentationInputError(
|
|
1579
|
+
"allowedImageUriSchemes",
|
|
1580
|
+
"contains an invalid URI scheme"
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
const scheme = value.toLowerCase();
|
|
1584
|
+
if (FORBIDDEN_IMAGE_URI_SCHEMES.has(scheme)) {
|
|
1585
|
+
throw new InvalidPresentationInputError(
|
|
1586
|
+
"allowedImageUriSchemes",
|
|
1587
|
+
`${scheme}: is reserved for active, local, or remote resources`
|
|
1588
|
+
);
|
|
1589
|
+
}
|
|
1590
|
+
normalized.add(scheme);
|
|
1591
|
+
}
|
|
1592
|
+
if (!resolver && normalized.size > 0) {
|
|
1593
|
+
throw new InvalidPresentationInputError(
|
|
1594
|
+
"allowedImageUriSchemes",
|
|
1595
|
+
"requires an injected imageResolver"
|
|
1596
|
+
);
|
|
1597
|
+
}
|
|
1598
|
+
return normalized;
|
|
1599
|
+
}
|
|
1600
|
+
function imageUriScheme(uri) {
|
|
1601
|
+
const match = /^([a-z][a-z0-9+.-]*):/i.exec(uri);
|
|
1602
|
+
if (!match) {
|
|
1603
|
+
throw new InvalidPresentationInputError(
|
|
1604
|
+
"image uri",
|
|
1605
|
+
"must use a host-owned, explicitly allowed URI scheme"
|
|
1606
|
+
);
|
|
1607
|
+
}
|
|
1608
|
+
return match[1].toLowerCase();
|
|
1609
|
+
}
|
|
1610
|
+
function validatePresentation(presentation, requireResolvedImages) {
|
|
1611
|
+
validatePosition(
|
|
1612
|
+
{
|
|
1613
|
+
left: 0,
|
|
1614
|
+
top: 0,
|
|
1615
|
+
width: presentation.slideSize.width,
|
|
1616
|
+
height: presentation.slideSize.height
|
|
1617
|
+
},
|
|
1618
|
+
"slideSize"
|
|
1619
|
+
);
|
|
1620
|
+
for (const master of presentation.masters.items) {
|
|
1621
|
+
assertSafeText(master.name, `master ${master.id} name`, 512);
|
|
1622
|
+
colorValue(master.background);
|
|
1623
|
+
}
|
|
1624
|
+
for (const layout of presentation.layouts.items) {
|
|
1625
|
+
assertSafeText(layout.name, `layout ${layout.id} name`, 512);
|
|
1626
|
+
colorValue(layout.background);
|
|
1627
|
+
if (layout.masterId !== void 0 && !presentation.masters.items.some((master) => master.id === layout.masterId)) {
|
|
1628
|
+
throw new InvalidPresentationInputError(
|
|
1629
|
+
`layout ${layout.id} masterId`,
|
|
1630
|
+
"must reference a master in this presentation"
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
for (const [slideIndex, slide] of presentation.slides.items.entries()) {
|
|
1635
|
+
assertSafeText(slide.title, `slides[${slideIndex}].title`);
|
|
1636
|
+
assertSafeText(slide.notes.toString(), `slides[${slideIndex}].notes`);
|
|
1637
|
+
colorValue(slide.background.fill);
|
|
1638
|
+
for (const shape of slide.shapes.items) {
|
|
1639
|
+
if (!PRESENTATION_GEOMETRIES.has(shape.geometry)) {
|
|
1640
|
+
throw new InvalidPresentationInputError("shape geometry", "is not supported");
|
|
1641
|
+
}
|
|
1642
|
+
assertSafeText(shape.name, `shape ${shape.id} name`);
|
|
1643
|
+
validatePosition(shape.position, `shape ${shape.id} position`);
|
|
1644
|
+
colorValue(shape.fill);
|
|
1645
|
+
validateLine(shape.line, `shape ${shape.id} line`);
|
|
1646
|
+
validateRotation(shape.rotation, `shape ${shape.id} rotation`);
|
|
1647
|
+
validateBorderRadius(shape.borderRadius, `shape ${shape.id} borderRadius`);
|
|
1648
|
+
normalizePlaceholder(shape.placeholder);
|
|
1649
|
+
assertSafeText(shape.text.toString(), `shape ${shape.id} text`);
|
|
1650
|
+
validateTextStyle(shape.text.style, `shape ${shape.id} textStyle`);
|
|
1651
|
+
}
|
|
1652
|
+
for (const chart of slide.charts.items) {
|
|
1653
|
+
if (!CHART_TYPES.has(chart.type)) {
|
|
1654
|
+
throw new InvalidPresentationInputError("chart type", "is not supported");
|
|
1655
|
+
}
|
|
1656
|
+
assertSafeText(chart.name, `chart ${chart.id} name`);
|
|
1657
|
+
assertSafeText(chart.title, `chart ${chart.id} title`);
|
|
1658
|
+
validatePosition(chart.position, `chart ${chart.id} position`);
|
|
1659
|
+
for (const [index, category] of chart.categories.entries()) {
|
|
1660
|
+
assertSafeText(category, `chart ${chart.id} categories[${index}]`);
|
|
1661
|
+
}
|
|
1662
|
+
validateOptionalFinite(chart.xAxis?.min, `chart ${chart.id} xAxis.min`);
|
|
1663
|
+
validateOptionalFinite(chart.xAxis?.max, `chart ${chart.id} xAxis.max`);
|
|
1664
|
+
validateOptionalFinite(chart.yAxis?.min, `chart ${chart.id} yAxis.min`);
|
|
1665
|
+
validateOptionalFinite(chart.yAxis?.max, `chart ${chart.id} yAxis.max`);
|
|
1666
|
+
for (const [seriesIndex, series] of chart.series.items.entries()) {
|
|
1667
|
+
assertSafeText(series.name, `chart ${chart.id} series[${seriesIndex}].name`);
|
|
1668
|
+
for (const [index, category] of series.categories.entries()) {
|
|
1669
|
+
assertSafeText(category, `chart ${chart.id} series[${seriesIndex}].categories[${index}]`);
|
|
1670
|
+
}
|
|
1671
|
+
validateFiniteArray(series.values, `chart ${chart.id} series[${seriesIndex}].values`);
|
|
1672
|
+
validateFiniteArray(series.xValues, `chart ${chart.id} series[${seriesIndex}].xValues`);
|
|
1673
|
+
validateFiniteArray(
|
|
1674
|
+
series.bubbleSizes,
|
|
1675
|
+
`chart ${chart.id} series[${seriesIndex}].bubbleSizes`
|
|
1676
|
+
);
|
|
1677
|
+
if (series.fill !== void 0) colorValue(series.fill);
|
|
1678
|
+
if (series.line !== void 0) {
|
|
1679
|
+
validateLine(series.line, `chart ${chart.id} series[${seriesIndex}].line`);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
for (const image of slide.images.items) {
|
|
1684
|
+
assertSafeText(image.name, `image ${image.id} name`);
|
|
1685
|
+
assertSafeText(image.alt, `image ${image.id} alt`);
|
|
1686
|
+
if (image.prompt !== void 0) assertSafeText(image.prompt, `image ${image.id} prompt`);
|
|
1687
|
+
validatePosition(image.position, `image ${image.id} position`);
|
|
1688
|
+
if (image.fit !== "contain" && image.fit !== "cover") {
|
|
1689
|
+
throw new InvalidPresentationInputError(
|
|
1690
|
+
`image ${image.id} fit`,
|
|
1691
|
+
"must be contain or cover"
|
|
1692
|
+
);
|
|
1693
|
+
}
|
|
1694
|
+
if (!["rect", "roundRect", "ellipse"].includes(image.geometry)) {
|
|
1695
|
+
throw new InvalidPresentationInputError(`image ${image.id} geometry`, "is not supported");
|
|
1696
|
+
}
|
|
1697
|
+
validateRotation(image.rotation, `image ${image.id} rotation`);
|
|
1698
|
+
validateBorderRadius(image.borderRadius, `image ${image.id} borderRadius`);
|
|
1699
|
+
if (image.crop !== void 0) normalizeCrop(image.crop);
|
|
1700
|
+
for (const [key, value] of [
|
|
1701
|
+
["flipHorizontal", image.flipHorizontal],
|
|
1702
|
+
["flipVertical", image.flipVertical],
|
|
1703
|
+
["lockAspectRatio", image.lockAspectRatio]
|
|
1704
|
+
]) {
|
|
1705
|
+
if (typeof value !== "boolean") {
|
|
1706
|
+
throw new InvalidPresentationInputError(`image ${image.id} ${key}`, "must be boolean");
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
if (requireResolvedImages) image.sourceForSvg();
|
|
1710
|
+
}
|
|
1711
|
+
for (const table of slide.tables.items) validatePresentationTable(table);
|
|
1712
|
+
for (const group of slide.groups.items) {
|
|
1713
|
+
validatePresentationGroup(group, requireResolvedImages, /* @__PURE__ */ new Set());
|
|
1714
|
+
}
|
|
1715
|
+
const indexedElements = [
|
|
1716
|
+
...slide.shapes.items,
|
|
1717
|
+
...slide.charts.items,
|
|
1718
|
+
...slide.images.items,
|
|
1719
|
+
...slide.tables.items,
|
|
1720
|
+
...slide.groups.items
|
|
1721
|
+
];
|
|
1722
|
+
if (slide.elements.length !== indexedElements.length || new Set(slide.elements).size !== slide.elements.length || indexedElements.some((element) => !slide.elements.includes(element))) {
|
|
1723
|
+
throw new InvalidPresentationInputError(
|
|
1724
|
+
`slides[${slideIndex}].elements`,
|
|
1725
|
+
"must contain each typed top-level element exactly once"
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
function validatePresentationTable(table) {
|
|
1731
|
+
assertSafeText(table.name, `table ${table.id} name`, 512);
|
|
1732
|
+
validatePosition(table.position, `table ${table.id} position`);
|
|
1733
|
+
colorValue(table.fill);
|
|
1734
|
+
validateLine(table.line, `table ${table.id} line`);
|
|
1735
|
+
validateTextStyle(table.textStyle, `table ${table.id} textStyle`);
|
|
1736
|
+
validateTableOccupancy(table.rows);
|
|
1737
|
+
for (const row of table.rows) {
|
|
1738
|
+
for (const cell of row) {
|
|
1739
|
+
if (!cell) continue;
|
|
1740
|
+
assertSafeText(cell.text.toString(), `table ${table.id} cell text`);
|
|
1741
|
+
validateTextStyle(cell.text.style, `table ${table.id} cell textStyle`);
|
|
1742
|
+
colorValue(cell.fill);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
normalizeTableDimensions(
|
|
1746
|
+
table.columnWidths.length === 0 ? void 0 : table.columnWidths,
|
|
1747
|
+
table.rows[0]?.length ?? 0,
|
|
1748
|
+
"columnWidths"
|
|
1749
|
+
);
|
|
1750
|
+
normalizeTableDimensions(
|
|
1751
|
+
table.rowHeights.length === 0 ? void 0 : table.rowHeights,
|
|
1752
|
+
table.rows.length,
|
|
1753
|
+
"rowHeights"
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
function validatePresentationGroup(group, requireResolvedImages, ancestors) {
|
|
1757
|
+
if (ancestors.has(group)) {
|
|
1758
|
+
throw new InvalidPresentationInputError(`group ${group.id}`, "contains a cycle");
|
|
1759
|
+
}
|
|
1760
|
+
ancestors.add(group);
|
|
1761
|
+
assertSafeText(group.name, `group ${group.id} name`, 512);
|
|
1762
|
+
validatePosition(group.position, `group ${group.id} position`);
|
|
1763
|
+
validatePosition(
|
|
1764
|
+
{
|
|
1765
|
+
left: group.childOffset.left,
|
|
1766
|
+
top: group.childOffset.top,
|
|
1767
|
+
width: group.childExtent.width,
|
|
1768
|
+
height: group.childExtent.height
|
|
1769
|
+
},
|
|
1770
|
+
`group ${group.id} child coordinate system`
|
|
1771
|
+
);
|
|
1772
|
+
validateRotation(group.rotation, `group ${group.id} rotation`);
|
|
1773
|
+
for (const child of group.children) {
|
|
1774
|
+
if (child.slide !== group.slide) {
|
|
1775
|
+
throw new InvalidPresentationInputError(
|
|
1776
|
+
`group ${group.id}`,
|
|
1777
|
+
"contains an element from another slide"
|
|
1778
|
+
);
|
|
1779
|
+
}
|
|
1780
|
+
if (child instanceof PresentationGroup) {
|
|
1781
|
+
validatePresentationGroup(child, requireResolvedImages, ancestors);
|
|
1782
|
+
} else if (child instanceof PresentationTable) {
|
|
1783
|
+
validatePresentationTable(child);
|
|
1784
|
+
} else if (child instanceof PresentationShape) {
|
|
1785
|
+
validatePosition(child.position, `shape ${child.id} position`);
|
|
1786
|
+
assertSafeText(child.text.toString(), `shape ${child.id} text`);
|
|
1787
|
+
validateTextStyle(child.text.style, `shape ${child.id} textStyle`);
|
|
1788
|
+
colorValue(child.fill);
|
|
1789
|
+
validateLine(child.line, `shape ${child.id} line`);
|
|
1790
|
+
} else if (child instanceof PresentationChart) {
|
|
1791
|
+
validatePosition(child.position, `chart ${child.id} position`);
|
|
1792
|
+
for (const series of child.series.items)
|
|
1793
|
+
validateFiniteArray(series.values, `chart ${child.id} values`);
|
|
1794
|
+
} else if (child instanceof PresentationImage) {
|
|
1795
|
+
validatePosition(child.position, `image ${child.id} position`);
|
|
1796
|
+
if (requireResolvedImages) child.sourceForSvg();
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
ancestors.delete(group);
|
|
1800
|
+
}
|
|
1801
|
+
function validatePosition(position, field) {
|
|
1802
|
+
const values = [position.left, position.top, position.width, position.height];
|
|
1803
|
+
if (!values.every((value) => typeof value === "number" && Number.isFinite(value))) {
|
|
1804
|
+
throw new InvalidPresentationInputError(field, "values must be finite numbers");
|
|
1805
|
+
}
|
|
1806
|
+
if (position.width <= 0 || position.height <= 0) {
|
|
1807
|
+
throw new InvalidPresentationInputError(field, "width and height must be positive");
|
|
1808
|
+
}
|
|
1809
|
+
if (values.some((value) => Math.abs(value) > MAX_GEOMETRY_VALUE)) {
|
|
1810
|
+
throw new InvalidPresentationInputError(field, "values exceed the supported geometry bounds");
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
function validateRotation(value, field) {
|
|
1814
|
+
if (!Number.isFinite(value) || Math.abs(value) > MAX_GEOMETRY_VALUE) {
|
|
1815
|
+
throw new InvalidPresentationInputError(field, "must be a bounded finite number");
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
function validateBorderRadius(value, field) {
|
|
1819
|
+
if (value === void 0) return;
|
|
1820
|
+
if (typeof value === "number") {
|
|
1821
|
+
if (!Number.isFinite(value) || value < 0 || value > MAX_GEOMETRY_VALUE) {
|
|
1822
|
+
throw new InvalidPresentationInputError(field, "must be a bounded non-negative number");
|
|
1823
|
+
}
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
if (value !== "rounded-full" && !Object.hasOwn(BORDER_RADIUS_TOKENS, value)) {
|
|
1827
|
+
throw new InvalidPresentationInputError(field, "uses an unsupported radius token");
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
function validateLine(line, field) {
|
|
1831
|
+
if (!line || typeof line !== "object") {
|
|
1832
|
+
throw new InvalidPresentationInputError(field, "must be an object");
|
|
1833
|
+
}
|
|
1834
|
+
if (line.style !== void 0 && !LINE_STYLES.has(line.style)) {
|
|
1835
|
+
throw new InvalidPresentationInputError(field, "uses an unsupported line style");
|
|
1836
|
+
}
|
|
1837
|
+
if (line.fill !== void 0) colorValue(line.fill);
|
|
1838
|
+
if (line.width !== void 0 && (!Number.isFinite(line.width) || line.width < 0 || line.width > MAX_GEOMETRY_VALUE)) {
|
|
1839
|
+
throw new InvalidPresentationInputError(field, "width must be a bounded non-negative number");
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
function validateTextStyle(style, field) {
|
|
1843
|
+
if (!style || typeof style !== "object") {
|
|
1844
|
+
throw new InvalidPresentationInputError(field, "must be an object");
|
|
1845
|
+
}
|
|
1846
|
+
if (style.fontFamily !== void 0) assertSafeText(style.fontFamily, `${field}.fontFamily`, 512);
|
|
1847
|
+
if (style.fontSize !== void 0 && (!Number.isFinite(style.fontSize) || style.fontSize <= 0 || style.fontSize > MAX_FONT_SIZE)) {
|
|
1848
|
+
throw new InvalidPresentationInputError(
|
|
1849
|
+
`${field}.fontSize`,
|
|
1850
|
+
`must be greater than 0 and at most ${MAX_FONT_SIZE}`
|
|
1851
|
+
);
|
|
1852
|
+
}
|
|
1853
|
+
if (style.color !== void 0) colorValue(style.color);
|
|
1854
|
+
if (style.alignment !== void 0 && !TEXT_ALIGNMENTS.has(style.alignment)) {
|
|
1855
|
+
throw new InvalidPresentationInputError(`${field}.alignment`, "is not supported");
|
|
1856
|
+
}
|
|
1857
|
+
if (style.verticalAlignment !== void 0 && !VERTICAL_ALIGNMENTS.has(style.verticalAlignment)) {
|
|
1858
|
+
throw new InvalidPresentationInputError(`${field}.verticalAlignment`, "is not supported");
|
|
1859
|
+
}
|
|
1860
|
+
for (const key of ["bold", "italic", "underline"]) {
|
|
1861
|
+
if (style[key] !== void 0 && typeof style[key] !== "boolean") {
|
|
1862
|
+
throw new InvalidPresentationInputError(`${field}.${key}`, "must be boolean");
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
function validateOptionalFinite(value, field) {
|
|
1867
|
+
if (value !== void 0 && (!Number.isFinite(value) || Math.abs(value) > MAX_GEOMETRY_VALUE)) {
|
|
1868
|
+
throw new InvalidPresentationInputError(field, "must be a bounded finite number");
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
function validateFiniteArray(values, field) {
|
|
1872
|
+
if (!Array.isArray(values)) {
|
|
1873
|
+
throw new InvalidPresentationInputError(field, "must be an array");
|
|
1874
|
+
}
|
|
1875
|
+
for (const value of values) validateOptionalFinite(value, field);
|
|
1876
|
+
}
|
|
1877
|
+
function assertSafeText(value, field, maxLength = MAX_TEXT_LENGTH) {
|
|
1878
|
+
if (typeof value !== "string") {
|
|
1879
|
+
throw new InvalidPresentationInputError(field, "must be a string");
|
|
1880
|
+
}
|
|
1881
|
+
if (value.length > maxLength) {
|
|
1882
|
+
throw new InvalidPresentationInputError(field, `exceeds ${maxLength} UTF-16 code units`);
|
|
1883
|
+
}
|
|
1884
|
+
for (const character of value) {
|
|
1885
|
+
const codePoint = character.codePointAt(0);
|
|
1886
|
+
if (codePoint < 32 && codePoint !== 9 && codePoint !== 10 && codePoint !== 13 || codePoint >= 55296 && codePoint <= 57343 || codePoint === 65534 || codePoint === 65535) {
|
|
1887
|
+
throw new InvalidPresentationInputError(field, "contains characters forbidden by XML 1.0");
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
function copyImageBytes(value) {
|
|
1892
|
+
if (!(value instanceof ArrayBuffer) && !(value instanceof Uint8Array)) {
|
|
1893
|
+
throw new InvalidPresentationInputError("image blob", "must be an ArrayBuffer or Uint8Array");
|
|
1894
|
+
}
|
|
1895
|
+
const bytes = value instanceof Uint8Array ? new Uint8Array(value) : new Uint8Array(value.slice(0));
|
|
1896
|
+
if (bytes.byteLength === 0 || bytes.byteLength > MAX_IMAGE_BYTES) {
|
|
1897
|
+
throw new InvalidPresentationInputError(
|
|
1898
|
+
"image blob",
|
|
1899
|
+
`must contain between 1 and ${MAX_IMAGE_BYTES} bytes`
|
|
1900
|
+
);
|
|
1901
|
+
}
|
|
1902
|
+
return bytes;
|
|
1903
|
+
}
|
|
1904
|
+
function normalizeRasterMime(value) {
|
|
1905
|
+
try {
|
|
1906
|
+
return normalizeRasterContentType(value);
|
|
1907
|
+
} catch (error) {
|
|
1908
|
+
if (error instanceof RasterImageValidationError) {
|
|
1909
|
+
throw new InvalidPresentationInputError("image contentType", error.detail);
|
|
1910
|
+
}
|
|
1911
|
+
throw error;
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
function validateRasterBytes(bytes, declaredType, field) {
|
|
1915
|
+
try {
|
|
1916
|
+
return validateRasterImageBytes(bytes, declaredType);
|
|
1917
|
+
} catch (error) {
|
|
1918
|
+
if (error instanceof RasterImageValidationError) {
|
|
1919
|
+
throw new InvalidPresentationInputError(field, error.detail);
|
|
1920
|
+
}
|
|
1921
|
+
throw error;
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
function parseRasterDataUrl(value) {
|
|
1925
|
+
try {
|
|
1926
|
+
const result = canonicalizeRasterDataUrl(value, MAX_IMAGE_BYTES);
|
|
1927
|
+
return { type: result.contentType, canonical: result.canonical };
|
|
1928
|
+
} catch (error) {
|
|
1929
|
+
if (error instanceof RasterImageValidationError) {
|
|
1930
|
+
throw new InvalidPresentationInputError("image dataUrl", error.detail);
|
|
1931
|
+
}
|
|
1932
|
+
throw error;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
function unresolvedImageUriError(id) {
|
|
1936
|
+
return new UnsupportedPresentationFeatureError(
|
|
1937
|
+
"unresolved image URI",
|
|
1938
|
+
`Image ${id} is a host reference. Call image.resolveUri() explicitly before render or export`
|
|
1939
|
+
);
|
|
1940
|
+
}
|
|
1941
|
+
function normalizePosition(input, defaults = { left: 0, top: 0, width: 100, height: 100 }) {
|
|
1942
|
+
const result = {
|
|
1943
|
+
left: input?.left ?? defaults.left,
|
|
1944
|
+
top: input?.top ?? defaults.top,
|
|
1945
|
+
width: input?.width ?? defaults.width,
|
|
1946
|
+
height: input?.height ?? defaults.height
|
|
1947
|
+
};
|
|
1948
|
+
validatePosition(result, "position");
|
|
1949
|
+
return result;
|
|
1950
|
+
}
|
|
1951
|
+
function normalizeCrop(crop) {
|
|
1952
|
+
const normalized = { ...crop };
|
|
1953
|
+
if (!Object.values(normalized).every(
|
|
1954
|
+
(value) => typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1
|
|
1955
|
+
)) {
|
|
1956
|
+
throw new InvalidPresentationInputError("image crop", "values must be between 0 and 1");
|
|
1957
|
+
}
|
|
1958
|
+
if (normalized.left + normalized.right >= 1 || normalized.top + normalized.bottom >= 1) {
|
|
1959
|
+
throw new InvalidPresentationInputError(
|
|
1960
|
+
"image crop",
|
|
1961
|
+
"cannot remove the complete source image"
|
|
1962
|
+
);
|
|
1963
|
+
}
|
|
1964
|
+
return normalized;
|
|
1965
|
+
}
|
|
1966
|
+
function colorValue(fill) {
|
|
1967
|
+
if (typeof fill !== "string") {
|
|
1968
|
+
if (!fill || typeof fill !== "object" || fill.type !== void 0 && fill.type !== "solid") {
|
|
1969
|
+
throw new InvalidPresentationInputError("fill", "must be a string or a solid color object");
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
const value = typeof fill === "string" ? fill : fill.color;
|
|
1973
|
+
if (typeof value !== "string") {
|
|
1974
|
+
throw new InvalidPresentationInputError("color", "must be a string");
|
|
1975
|
+
}
|
|
1976
|
+
if (value === "none") return "none";
|
|
1977
|
+
const token = COLOR_TOKENS[value];
|
|
1978
|
+
if (token) return token;
|
|
1979
|
+
if (/^#[0-9a-f]{6}$/i.test(value)) return value.toLowerCase();
|
|
1980
|
+
if (/^#[0-9a-f]{3}$/i.test(value)) {
|
|
1981
|
+
return `#${value[1]}${value[1]}${value[2]}${value[2]}${value[3]}${value[3]}`.toLowerCase();
|
|
1982
|
+
}
|
|
1983
|
+
throw new InvalidPresentationInputError("color", "must be a known token, none, #RGB, or #RRGGBB");
|
|
1984
|
+
}
|
|
1985
|
+
var COLOR_TOKENS = {
|
|
1986
|
+
white: "#ffffff",
|
|
1987
|
+
black: "#000000",
|
|
1988
|
+
accent1: "#2563eb",
|
|
1989
|
+
accent2: "#0f766e",
|
|
1990
|
+
"slate-50": "#f8fafc",
|
|
1991
|
+
"slate-100": "#f1f5f9",
|
|
1992
|
+
"slate-200": "#e2e8f0",
|
|
1993
|
+
"slate-300": "#cbd5e1",
|
|
1994
|
+
"slate-400": "#94a3b8",
|
|
1995
|
+
"slate-500": "#64748b",
|
|
1996
|
+
"slate-600": "#475569",
|
|
1997
|
+
"slate-700": "#334155",
|
|
1998
|
+
"slate-800": "#1e293b",
|
|
1999
|
+
"slate-900": "#0f172a",
|
|
2000
|
+
"slate-950": "#020617"
|
|
2001
|
+
};
|
|
2002
|
+
function projectRecord(record, includeText, excludeText) {
|
|
2003
|
+
const include = parseProjection(includeText);
|
|
2004
|
+
const exclude = parseProjection(excludeText);
|
|
2005
|
+
if (include.size === 0 && exclude.size === 0) return record;
|
|
2006
|
+
const projected = {};
|
|
2007
|
+
for (const [key, value] of Object.entries(record)) {
|
|
2008
|
+
if ((include.size === 0 || include.has(key)) && !exclude.has(key)) projected[key] = value;
|
|
2009
|
+
}
|
|
2010
|
+
return projected;
|
|
2011
|
+
}
|
|
2012
|
+
function parseProjection(value) {
|
|
2013
|
+
return new Set(
|
|
2014
|
+
(value ?? "").split(",").map((part) => part.trim()).filter(Boolean)
|
|
2015
|
+
);
|
|
2016
|
+
}
|
|
2017
|
+
function boundPresentationRecords(records, maxChars) {
|
|
2018
|
+
if (!Number.isInteger(maxChars) || maxChars <= 0)
|
|
2019
|
+
throw new Error("inspect maxChars must be a positive integer");
|
|
2020
|
+
return boundInspectionRecords(records, maxChars);
|
|
2021
|
+
}
|
|
2022
|
+
function jsonBlob(value, name) {
|
|
2023
|
+
return new FileBlob([JSON.stringify(value, null, 2)], { name, type: "application/json" });
|
|
2024
|
+
}
|
|
2025
|
+
function positionTuple(position) {
|
|
2026
|
+
return [position.left, position.top, position.width, position.height];
|
|
2027
|
+
}
|
|
2028
|
+
function finiteOr(value, fallback) {
|
|
2029
|
+
if (value === void 0) return fallback;
|
|
2030
|
+
if (!Number.isFinite(value)) {
|
|
2031
|
+
throw new InvalidPresentationInputError("number", "must be finite");
|
|
2032
|
+
}
|
|
2033
|
+
return value;
|
|
2034
|
+
}
|
|
2035
|
+
function truncate(value, max) {
|
|
2036
|
+
return value.length <= max ? value : `${value.slice(0, Math.max(0, max - 1))}\u2026`;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
export {
|
|
2040
|
+
presentationLossState,
|
|
2041
|
+
setPresentationLossState,
|
|
2042
|
+
PPTX_MEDIA_TYPE,
|
|
2043
|
+
PresentationFidelityError,
|
|
2044
|
+
PresentationSecurityError,
|
|
2045
|
+
UnsupportedPresentationFeatureError,
|
|
2046
|
+
InvalidPresentationInputError,
|
|
2047
|
+
Presentation,
|
|
2048
|
+
PresentationMaster,
|
|
2049
|
+
PresentationLayout,
|
|
2050
|
+
PresentationMasterCollection,
|
|
2051
|
+
PresentationLayoutCollection,
|
|
2052
|
+
SlideCollection,
|
|
2053
|
+
Slide,
|
|
2054
|
+
SlidePlaceholderCollection,
|
|
2055
|
+
PresentationText,
|
|
2056
|
+
PresentationShape,
|
|
2057
|
+
PresentationShapeCollection,
|
|
2058
|
+
PresentationChartSeries,
|
|
2059
|
+
PresentationChartSeriesCollection,
|
|
2060
|
+
PresentationChart,
|
|
2061
|
+
PresentationChartCollection,
|
|
2062
|
+
PresentationTableCell,
|
|
2063
|
+
PresentationTable,
|
|
2064
|
+
PresentationTableCollection,
|
|
2065
|
+
PresentationGroup,
|
|
2066
|
+
PresentationGroupCollection,
|
|
2067
|
+
PresentationImage,
|
|
2068
|
+
PresentationImageCollection,
|
|
2069
|
+
PresentationFile,
|
|
2070
|
+
assertRenderPixelBudget,
|
|
2071
|
+
colorValue,
|
|
2072
|
+
truncate
|
|
2073
|
+
};
|
|
2074
|
+
//# sourceMappingURL=chunk-PI2AZVGK.js.map
|