@json-to-office/core-pptx 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/chart.d.ts.map +1 -1
- package/dist/components/highcharts.d.ts.map +1 -1
- package/dist/components/image.d.ts +2 -5
- package/dist/components/image.d.ts.map +1 -1
- package/dist/components/text.d.ts +2 -5
- package/dist/components/text.d.ts.map +1 -1
- package/dist/core/generator.d.ts +17 -3
- package/dist/core/generator.d.ts.map +1 -1
- package/dist/core/packagePresentation.d.ts +17 -0
- package/dist/core/packagePresentation.d.ts.map +1 -0
- package/dist/core/structure.d.ts.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +424 -114
- package/dist/index.js.map +1 -1
- package/dist/plugin/createPresentationGenerator.d.ts +5 -2
- package/dist/plugin/createPresentationGenerator.d.ts.map +1 -1
- package/dist/plugin/index.d.ts +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/types.d.ts +15 -3
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/plugin/validation.d.ts +9 -6
- package/dist/plugin/validation.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/color.d.ts +16 -4
- package/dist/utils/color.d.ts.map +1 -1
- package/dist/utils/hyperlink.d.ts +40 -0
- package/dist/utils/hyperlink.d.ts.map +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// src/core/generator.ts
|
|
2
|
-
import JSZip from "jszip";
|
|
3
2
|
import { writeFileSync } from "fs";
|
|
4
3
|
|
|
5
4
|
// src/types.ts
|
|
@@ -307,8 +306,82 @@ function resolveComponentTree(components, theme) {
|
|
|
307
306
|
});
|
|
308
307
|
}
|
|
309
308
|
|
|
309
|
+
// src/utils/hyperlink.ts
|
|
310
|
+
var HYPERLINK_SLIDE_UNRESOLVED = "HYPERLINK_SLIDE_UNRESOLVED";
|
|
311
|
+
function remapHyperlink(hyperlink, map) {
|
|
312
|
+
if (hyperlink.url || hyperlink.slide == null) return hyperlink;
|
|
313
|
+
const rendered = map.get(hyperlink.slide);
|
|
314
|
+
if (rendered === void 0) {
|
|
315
|
+
const { slide, ...rest } = hyperlink;
|
|
316
|
+
return { ...rest, unresolvedSlideRef: slide };
|
|
317
|
+
}
|
|
318
|
+
return rendered === hyperlink.slide ? hyperlink : { ...hyperlink, slide: rendered };
|
|
319
|
+
}
|
|
320
|
+
function remapHyperlinkProps(props, map) {
|
|
321
|
+
const hyperlink = props.hyperlink;
|
|
322
|
+
if (!hyperlink || typeof hyperlink !== "object") return props;
|
|
323
|
+
const remapped = remapHyperlink(hyperlink, map);
|
|
324
|
+
return remapped === hyperlink ? props : { ...props, hyperlink: remapped };
|
|
325
|
+
}
|
|
326
|
+
function remapHyperlinkSlideRefs(component, map) {
|
|
327
|
+
const hyperlink = component.props?.hyperlink;
|
|
328
|
+
let next = component;
|
|
329
|
+
if (hyperlink && typeof hyperlink === "object") {
|
|
330
|
+
const remapped = remapHyperlink(hyperlink, map);
|
|
331
|
+
if (remapped !== hyperlink) {
|
|
332
|
+
next = { ...next, props: { ...next.props, hyperlink: remapped } };
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (next.children && next.children.length > 0) {
|
|
336
|
+
next = {
|
|
337
|
+
...next,
|
|
338
|
+
children: next.children.map(
|
|
339
|
+
(child) => remapHyperlinkSlideRefs(child, map)
|
|
340
|
+
)
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
return next;
|
|
344
|
+
}
|
|
345
|
+
function applyHyperlink(opts, hyperlink, componentName, warnings) {
|
|
346
|
+
if (!hyperlink) return;
|
|
347
|
+
if (hyperlink.url) {
|
|
348
|
+
opts.hyperlink = { url: hyperlink.url, tooltip: hyperlink.tooltip };
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (hyperlink.unresolvedSlideRef != null) {
|
|
352
|
+
const message = `hyperlink.slide ${hyperlink.unresolvedSlideRef} matches no slide in the generated presentation (slide disabled, or index out of range) \u2014 hyperlink dropped`;
|
|
353
|
+
if (warnings) {
|
|
354
|
+
warnings.push({
|
|
355
|
+
code: HYPERLINK_SLIDE_UNRESOLVED,
|
|
356
|
+
message,
|
|
357
|
+
component: componentName
|
|
358
|
+
});
|
|
359
|
+
} else {
|
|
360
|
+
console.warn(message);
|
|
361
|
+
}
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (hyperlink.slide) {
|
|
365
|
+
opts.hyperlink = { slide: hyperlink.slide, tooltip: hyperlink.tooltip };
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
310
369
|
// src/core/structure.ts
|
|
311
370
|
import { mergeWithDefaults as mergeWithDefaults2 } from "@json-to-office/shared";
|
|
371
|
+
function isSlideEnabled(child) {
|
|
372
|
+
return !("enabled" in child && child.enabled === false);
|
|
373
|
+
}
|
|
374
|
+
function buildSlideIndexMap(children) {
|
|
375
|
+
const map = /* @__PURE__ */ new Map();
|
|
376
|
+
let authored = 0;
|
|
377
|
+
let rendered = 0;
|
|
378
|
+
for (const child of children) {
|
|
379
|
+
if (!isSlideComponent(child)) continue;
|
|
380
|
+
authored++;
|
|
381
|
+
if (isSlideEnabled(child)) map.set(authored, ++rendered);
|
|
382
|
+
}
|
|
383
|
+
return map;
|
|
384
|
+
}
|
|
312
385
|
function processPresentation(document, options) {
|
|
313
386
|
const { props, children = [] } = document;
|
|
314
387
|
const baseTheme = typeof props.theme === "object" && props.theme !== null ? props.theme : options?.customThemes?.[props.theme ?? "default"] ?? getPptxTheme(props.theme ?? "default");
|
|
@@ -322,34 +395,41 @@ function processPresentation(document, options) {
|
|
|
322
395
|
} : baseTheme;
|
|
323
396
|
const slideWidth = props.slideWidth ?? 10;
|
|
324
397
|
const slideHeight = props.slideHeight ?? 7.5;
|
|
398
|
+
const slideIndexMap = buildSlideIndexMap(children);
|
|
325
399
|
let templates;
|
|
326
400
|
if (props.templates && props.templates.length > 0) {
|
|
327
401
|
templates = props.templates.map((m) => {
|
|
328
402
|
const effectiveGrid = mergeGridConfigs(props.grid, m.grid);
|
|
329
403
|
const resolvedPhs = m.placeholders?.map((ph) => {
|
|
330
|
-
|
|
404
|
+
const phDefaults = ph.defaults;
|
|
405
|
+
const defaultProps = phDefaults?.props ? remapHyperlinkProps(phDefaults.props, slideIndexMap) : void 0;
|
|
406
|
+
const base = phDefaults && defaultProps && defaultProps !== phDefaults.props ? { ...ph, defaults: { ...phDefaults, props: defaultProps } } : ph;
|
|
407
|
+
if (!base.grid) return base;
|
|
331
408
|
const abs = resolveGridPosition(
|
|
332
|
-
|
|
409
|
+
base.grid,
|
|
333
410
|
effectiveGrid,
|
|
334
411
|
slideWidth,
|
|
335
412
|
slideHeight
|
|
336
413
|
);
|
|
337
414
|
return {
|
|
338
|
-
...
|
|
339
|
-
x:
|
|
340
|
-
y:
|
|
341
|
-
w:
|
|
342
|
-
h:
|
|
415
|
+
...base,
|
|
416
|
+
x: base.x ?? abs.x,
|
|
417
|
+
y: base.y ?? abs.y,
|
|
418
|
+
w: base.w ?? abs.w,
|
|
419
|
+
h: base.h ?? abs.h,
|
|
343
420
|
grid: void 0
|
|
344
421
|
};
|
|
345
422
|
});
|
|
346
423
|
const defaultedObjects = m.objects ? resolveComponentTree(m.objects, theme) : void 0;
|
|
347
424
|
const resolvedObjects = defaultedObjects?.map(
|
|
348
|
-
(obj) =>
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
425
|
+
(obj) => remapHyperlinkSlideRefs(
|
|
426
|
+
resolveComponentGridPosition(
|
|
427
|
+
obj,
|
|
428
|
+
effectiveGrid,
|
|
429
|
+
slideWidth,
|
|
430
|
+
slideHeight
|
|
431
|
+
),
|
|
432
|
+
slideIndexMap
|
|
353
433
|
)
|
|
354
434
|
);
|
|
355
435
|
return { ...m, placeholders: resolvedPhs, objects: resolvedObjects };
|
|
@@ -358,13 +438,18 @@ function processPresentation(document, options) {
|
|
|
358
438
|
const slides = [];
|
|
359
439
|
for (const child of children) {
|
|
360
440
|
if (isSlideComponent(child)) {
|
|
441
|
+
if (!isSlideEnabled(child)) continue;
|
|
361
442
|
const slideComponents = [];
|
|
362
443
|
if (child.children) {
|
|
363
444
|
for (const slideChild of child.children) {
|
|
364
445
|
slideComponents.push(slideChild);
|
|
365
446
|
}
|
|
366
447
|
}
|
|
367
|
-
const resolvedComponents = resolveComponentTree(
|
|
448
|
+
const resolvedComponents = resolveComponentTree(
|
|
449
|
+
slideComponents,
|
|
450
|
+
theme
|
|
451
|
+
).map((component) => remapHyperlinkSlideRefs(component, slideIndexMap));
|
|
452
|
+
const placeholders = child.props.placeholders;
|
|
368
453
|
slides.push({
|
|
369
454
|
components: resolvedComponents,
|
|
370
455
|
background: child.props.background,
|
|
@@ -372,7 +457,12 @@ function processPresentation(document, options) {
|
|
|
372
457
|
layout: child.props.layout,
|
|
373
458
|
hidden: child.props.hidden,
|
|
374
459
|
template: child.props.template,
|
|
375
|
-
placeholders:
|
|
460
|
+
placeholders: placeholders ? Object.fromEntries(
|
|
461
|
+
Object.entries(placeholders).map(([name, component]) => [
|
|
462
|
+
name,
|
|
463
|
+
remapHyperlinkSlideRefs(component, slideIndexMap)
|
|
464
|
+
])
|
|
465
|
+
) : void 0
|
|
376
466
|
});
|
|
377
467
|
}
|
|
378
468
|
}
|
|
@@ -401,6 +491,7 @@ import PptxGenJS from "pptxgenjs";
|
|
|
401
491
|
|
|
402
492
|
// src/utils/color.ts
|
|
403
493
|
import { SEMANTIC_COLOR_NAMES } from "@json-to-office/shared-pptx";
|
|
494
|
+
import { DEFAULT_CHART_THEME_COLORS } from "@json-to-office/shared";
|
|
404
495
|
var SEMANTIC_TO_THEME_KEY = {
|
|
405
496
|
...Object.fromEntries(SEMANTIC_COLOR_NAMES.map((n) => [n, n])),
|
|
406
497
|
// Aliases (PowerPoint XML compat)
|
|
@@ -412,26 +503,49 @@ var SEMANTIC_TO_THEME_KEY = {
|
|
|
412
503
|
bg1: "background",
|
|
413
504
|
bg2: "background2"
|
|
414
505
|
};
|
|
415
|
-
|
|
416
|
-
"
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
506
|
+
function chainToHex(value, theme, seen) {
|
|
507
|
+
const bare = value.startsWith("#") ? value.slice(1) : value;
|
|
508
|
+
if (/^[0-9A-Fa-f]{6}$/.test(bare)) return bare;
|
|
509
|
+
if (/^[0-9A-Fa-f]{3}$/.test(bare)) {
|
|
510
|
+
return bare[0] + bare[0] + bare[1] + bare[1] + bare[2] + bare[2];
|
|
511
|
+
}
|
|
512
|
+
const themeKey = SEMANTIC_TO_THEME_KEY[value];
|
|
513
|
+
if (!themeKey || seen.has(themeKey)) return void 0;
|
|
514
|
+
seen.add(themeKey);
|
|
515
|
+
const next = theme?.colors?.[themeKey];
|
|
516
|
+
if (typeof next !== "string" || next.length === 0) return void 0;
|
|
517
|
+
return chainToHex(next, theme, seen)?.toUpperCase();
|
|
518
|
+
}
|
|
519
|
+
function definedChartColorTokens(theme) {
|
|
520
|
+
const colors = theme?.colors;
|
|
521
|
+
if (!colors) return [];
|
|
522
|
+
return DEFAULT_CHART_THEME_COLORS.filter((token) => {
|
|
523
|
+
const themeKey = SEMANTIC_TO_THEME_KEY[token] ?? token;
|
|
524
|
+
const value = colors[themeKey];
|
|
525
|
+
if (typeof value !== "string" || value.length === 0) return false;
|
|
526
|
+
return chainToHex(value, theme, /* @__PURE__ */ new Set([themeKey])) !== void 0;
|
|
527
|
+
});
|
|
528
|
+
}
|
|
423
529
|
function resolveColor(color, theme, warnings) {
|
|
424
530
|
const themeKey = SEMANTIC_TO_THEME_KEY[color];
|
|
425
531
|
if (themeKey) {
|
|
426
532
|
const resolved = theme.colors[themeKey];
|
|
427
|
-
if (resolved)
|
|
428
|
-
|
|
533
|
+
if (resolved) {
|
|
534
|
+
const hex = chainToHex(resolved, theme, /* @__PURE__ */ new Set([themeKey]));
|
|
535
|
+
if (hex) return hex;
|
|
536
|
+
warn(
|
|
537
|
+
warnings,
|
|
538
|
+
W.UNKNOWN_COLOR,
|
|
539
|
+
`Theme color "${themeKey}" is "${resolved}", which is not a hex color or a theme color name; falling back to primary`
|
|
540
|
+
);
|
|
541
|
+
return resolvePrimary(theme);
|
|
542
|
+
}
|
|
429
543
|
warn(
|
|
430
544
|
warnings,
|
|
431
545
|
W.THEME_COLOR_FALLBACK,
|
|
432
546
|
`Theme color "${themeKey}" not defined, falling back to primary`
|
|
433
547
|
);
|
|
434
|
-
return
|
|
548
|
+
return resolvePrimary(theme);
|
|
435
549
|
}
|
|
436
550
|
const bare = color.startsWith("#") ? color.slice(1) : color;
|
|
437
551
|
if (/^[0-9A-Fa-f]{3}$/.test(bare)) {
|
|
@@ -446,6 +560,10 @@ function resolveColor(color, theme, warnings) {
|
|
|
446
560
|
}
|
|
447
561
|
return bare;
|
|
448
562
|
}
|
|
563
|
+
function resolvePrimary(theme) {
|
|
564
|
+
const primary = theme.colors.primary;
|
|
565
|
+
return chainToHex(primary, theme, /* @__PURE__ */ new Set(["primary"])) ?? (primary.startsWith("#") ? primary.slice(1) : primary);
|
|
566
|
+
}
|
|
449
567
|
|
|
450
568
|
// src/utils/fontAliasContext.ts
|
|
451
569
|
import { synthesizeFamilyName } from "@json-to-office/shared";
|
|
@@ -541,19 +659,7 @@ function renderTextComponent(slide, props, theme, warnings, slideCtx) {
|
|
|
541
659
|
opts.fill.transparency = props.fill.transparency;
|
|
542
660
|
}
|
|
543
661
|
}
|
|
544
|
-
|
|
545
|
-
if (props.hyperlink.url) {
|
|
546
|
-
opts.hyperlink = {
|
|
547
|
-
url: props.hyperlink.url,
|
|
548
|
-
tooltip: props.hyperlink.tooltip
|
|
549
|
-
};
|
|
550
|
-
} else if (props.hyperlink.slide) {
|
|
551
|
-
opts.hyperlink = {
|
|
552
|
-
slide: props.hyperlink.slide,
|
|
553
|
-
tooltip: props.hyperlink.tooltip
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
}
|
|
662
|
+
applyHyperlink(opts, props.hyperlink, "text", warnings);
|
|
557
663
|
const lineSpacing = props.lineSpacing ?? style?.lineSpacing;
|
|
558
664
|
if (lineSpacing !== void 0) opts.lineSpacing = lineSpacing;
|
|
559
665
|
const charSpacing = props.charSpacing ?? style?.charSpacing;
|
|
@@ -731,19 +837,7 @@ async function renderImageComponent(slide, props, theme, warnings, slideWidth =
|
|
|
731
837
|
opacity: props.shadow.opacity ?? 0.5
|
|
732
838
|
};
|
|
733
839
|
}
|
|
734
|
-
|
|
735
|
-
if (props.hyperlink.url) {
|
|
736
|
-
opts.hyperlink = {
|
|
737
|
-
url: props.hyperlink.url,
|
|
738
|
-
tooltip: props.hyperlink.tooltip
|
|
739
|
-
};
|
|
740
|
-
} else if (props.hyperlink.slide) {
|
|
741
|
-
opts.hyperlink = {
|
|
742
|
-
slide: props.hyperlink.slide,
|
|
743
|
-
tooltip: props.hyperlink.tooltip
|
|
744
|
-
};
|
|
745
|
-
}
|
|
746
|
-
}
|
|
840
|
+
applyHyperlink(opts, props.hyperlink, "image", warnings);
|
|
747
841
|
if (props.alt) opts.altText = props.alt;
|
|
748
842
|
slide.addImage(opts);
|
|
749
843
|
}
|
|
@@ -1131,9 +1225,10 @@ Cause: ${error instanceof Error ? error.message : String(error)}`
|
|
|
1131
1225
|
}
|
|
1132
1226
|
function withThemeColors(props, theme, warnings) {
|
|
1133
1227
|
if (!props.options || props.options.colors || !theme?.colors) return props;
|
|
1134
|
-
const palette =
|
|
1228
|
+
const palette = definedChartColorTokens(theme).map(
|
|
1135
1229
|
(token) => `#${resolveColor(token, theme, warnings)}`
|
|
1136
1230
|
);
|
|
1231
|
+
if (palette.length === 0) return props;
|
|
1137
1232
|
return {
|
|
1138
1233
|
...props,
|
|
1139
1234
|
options: { ...props.options, colors: palette }
|
|
@@ -1167,7 +1262,6 @@ var CHART_TYPE_MAP = {
|
|
|
1167
1262
|
radar: "radar",
|
|
1168
1263
|
scatter: "scatter"
|
|
1169
1264
|
};
|
|
1170
|
-
var DEFAULT_THEME_COLORS = DEFAULT_CHART_THEME_COLORS;
|
|
1171
1265
|
function renderChartComponent(slide, props, theme, _pptx, warnings) {
|
|
1172
1266
|
const chartType = CHART_TYPE_MAP[props.type];
|
|
1173
1267
|
if (!chartType) {
|
|
@@ -1214,8 +1308,12 @@ function renderChartComponent(slide, props, theme, _pptx, warnings) {
|
|
|
1214
1308
|
if (props.y !== void 0) opts.y = props.y;
|
|
1215
1309
|
if (props.w !== void 0) opts.w = props.w;
|
|
1216
1310
|
if (props.h !== void 0) opts.h = props.h;
|
|
1217
|
-
const colorSources = props.chartColors ??
|
|
1218
|
-
|
|
1311
|
+
const colorSources = props.chartColors ?? definedChartColorTokens(theme);
|
|
1312
|
+
if (colorSources.length > 0) {
|
|
1313
|
+
opts.chartColors = colorSources.map(
|
|
1314
|
+
(c) => resolveColor(c, theme, warnings)
|
|
1315
|
+
);
|
|
1316
|
+
}
|
|
1219
1317
|
const themeTextColor = resolveColor("text", theme, warnings);
|
|
1220
1318
|
opts.titleColor = props.titleColor ? resolveColor(props.titleColor, theme, warnings) : themeTextColor;
|
|
1221
1319
|
opts.legendColor = props.legendColor ? resolveColor(props.legendColor, theme, warnings) : themeTextColor;
|
|
@@ -1612,13 +1710,161 @@ async function resolveDocumentFonts(document, theme, warnings, fonts) {
|
|
|
1612
1710
|
|
|
1613
1711
|
// src/core/generator.ts
|
|
1614
1712
|
import { applyExportMode, scopedThemeName } from "@json-to-office/shared";
|
|
1615
|
-
import {
|
|
1713
|
+
import {
|
|
1714
|
+
collectImageSourceConflicts,
|
|
1715
|
+
validateJsonPresentationDocument,
|
|
1716
|
+
validatePresentationDocument
|
|
1717
|
+
} from "@json-to-office/shared-pptx";
|
|
1718
|
+
|
|
1719
|
+
// src/core/packagePresentation.ts
|
|
1720
|
+
import JSZip from "jszip";
|
|
1721
|
+
var MEDIUM_STYLE_2_ACCENT_1 = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}";
|
|
1722
|
+
var NO_STYLE_NO_GRID = "{2D5ABB26-0587-4C30-8999-92F81FD0307C}";
|
|
1723
|
+
var DEFAULT_GENERATED_AT = "2000-01-01T00:00:00.000Z";
|
|
1724
|
+
function resolveGeneratedAt(value) {
|
|
1725
|
+
const date = value === void 0 ? new Date(DEFAULT_GENERATED_AT) : new Date(value);
|
|
1726
|
+
if (Number.isNaN(date.getTime())) {
|
|
1727
|
+
throw new Error(`Invalid generatedAt value: ${String(value)}`);
|
|
1728
|
+
}
|
|
1729
|
+
if (date.getUTCFullYear() < 1980) {
|
|
1730
|
+
throw new Error(
|
|
1731
|
+
"generatedAt must be on or after 1980-01-01 for ZIP compatibility"
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1734
|
+
return date;
|
|
1735
|
+
}
|
|
1736
|
+
function replaceCoreTimestamp(xml, tag, value) {
|
|
1737
|
+
const expression = new RegExp(
|
|
1738
|
+
`(<dcterms:${tag}\\b[^>]*>)[^<]*(</dcterms:${tag}>)`,
|
|
1739
|
+
"g"
|
|
1740
|
+
);
|
|
1741
|
+
return xml.replace(expression, `$1${value}$2`);
|
|
1742
|
+
}
|
|
1743
|
+
var EMBEDDED_OFFICE_PACKAGE = /\.(?:docx|pptx|xlsx|xlsm)$/i;
|
|
1744
|
+
function remapChartReferences(value, chartIds) {
|
|
1745
|
+
return value.replace(/chart(\d+)\.xml/g, (match, rawId) => {
|
|
1746
|
+
const id = chartIds.get(Number(rawId));
|
|
1747
|
+
return id === void 0 ? match : `chart${id}.xml`;
|
|
1748
|
+
}).replace(
|
|
1749
|
+
/Microsoft_Excel_Worksheet(\d+)\.xlsx/g,
|
|
1750
|
+
(match, rawId) => {
|
|
1751
|
+
const id = chartIds.get(Number(rawId));
|
|
1752
|
+
return id === void 0 ? match : `Microsoft_Excel_Worksheet${id}.xlsx`;
|
|
1753
|
+
}
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
async function canonicalizeChartIds(zip) {
|
|
1757
|
+
const sourceIds = Object.keys(zip.files).map((path2) => path2.match(/^ppt\/charts\/chart(\d+)\.xml$/)?.[1]).filter((value) => value !== void 0).map(Number).sort((a, b) => a - b);
|
|
1758
|
+
const chartIds = new Map(sourceIds.map((id, index) => [id, index + 1]));
|
|
1759
|
+
if (chartIds.size === 0) return;
|
|
1760
|
+
for (const [path2, entry] of Object.entries(zip.files)) {
|
|
1761
|
+
if (entry.dir || !path2.endsWith(".xml") && !path2.endsWith(".rels")) {
|
|
1762
|
+
continue;
|
|
1763
|
+
}
|
|
1764
|
+
const xml = await entry.async("string");
|
|
1765
|
+
const remapped = remapChartReferences(xml, chartIds);
|
|
1766
|
+
if (remapped !== xml) zip.file(path2, remapped);
|
|
1767
|
+
}
|
|
1768
|
+
const renames = [];
|
|
1769
|
+
for (const [path2, entry] of Object.entries(zip.files)) {
|
|
1770
|
+
if (entry.dir) continue;
|
|
1771
|
+
const remappedPath = remapChartReferences(path2, chartIds);
|
|
1772
|
+
if (remappedPath === path2) continue;
|
|
1773
|
+
renames.push({
|
|
1774
|
+
from: path2,
|
|
1775
|
+
to: remappedPath,
|
|
1776
|
+
data: await entry.async("nodebuffer"),
|
|
1777
|
+
date: entry.date
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1780
|
+
for (const entry of renames) zip.remove(entry.from);
|
|
1781
|
+
for (const entry of renames) {
|
|
1782
|
+
zip.file(entry.to, entry.data, { date: entry.date });
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
async function generateZip(zip) {
|
|
1786
|
+
return await zip.generateAsync({
|
|
1787
|
+
type: "nodebuffer",
|
|
1788
|
+
compression: "DEFLATE",
|
|
1789
|
+
compressionOptions: { level: 6 },
|
|
1790
|
+
platform: "DOS",
|
|
1791
|
+
streamFiles: false
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1794
|
+
async function canonicalizePackage(zip, generatedAt, depth = 0) {
|
|
1795
|
+
const timestamp = generatedAt.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
1796
|
+
const coreEntry = zip.file("docProps/core.xml");
|
|
1797
|
+
if (coreEntry) {
|
|
1798
|
+
let coreXml = await coreEntry.async("string");
|
|
1799
|
+
coreXml = replaceCoreTimestamp(coreXml, "created", timestamp);
|
|
1800
|
+
coreXml = replaceCoreTimestamp(coreXml, "modified", timestamp);
|
|
1801
|
+
zip.file("docProps/core.xml", coreXml);
|
|
1802
|
+
}
|
|
1803
|
+
if (depth < 3) {
|
|
1804
|
+
for (const [path2, entry] of Object.entries(zip.files)) {
|
|
1805
|
+
if (entry.dir || !EMBEDDED_OFFICE_PACKAGE.test(path2)) continue;
|
|
1806
|
+
try {
|
|
1807
|
+
const nested = await JSZip.loadAsync(await entry.async("nodebuffer"));
|
|
1808
|
+
await canonicalizePackage(nested, generatedAt, depth + 1);
|
|
1809
|
+
zip.file(path2, await generateZip(nested));
|
|
1810
|
+
} catch {
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
for (const entry of Object.values(zip.files)) {
|
|
1815
|
+
entry.date = generatedAt;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
async function packagePresentationBuffer(buffer, options = {}) {
|
|
1819
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
1820
|
+
let changed = false;
|
|
1821
|
+
for (const [path2, entry] of Object.entries(zip.files)) {
|
|
1822
|
+
if (!path2.match(/^ppt\/slides\/slide\d+\.xml$/)) continue;
|
|
1823
|
+
const xml = await entry.async("string");
|
|
1824
|
+
if (xml.includes(MEDIUM_STYLE_2_ACCENT_1)) {
|
|
1825
|
+
zip.file(path2, xml.replaceAll(MEDIUM_STYLE_2_ACCENT_1, NO_STYLE_NO_GRID));
|
|
1826
|
+
changed = true;
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
if (options.deterministic !== false) {
|
|
1830
|
+
const generatedAt = resolveGeneratedAt(options.generatedAt);
|
|
1831
|
+
await canonicalizeChartIds(zip);
|
|
1832
|
+
await canonicalizePackage(zip, generatedAt);
|
|
1833
|
+
changed = true;
|
|
1834
|
+
}
|
|
1835
|
+
if (!changed) return buffer;
|
|
1836
|
+
return generateZip(zip);
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
// src/core/generator.ts
|
|
1840
|
+
var PresentationValidationError = class extends Error {
|
|
1841
|
+
errors;
|
|
1842
|
+
constructor(errors) {
|
|
1843
|
+
super(
|
|
1844
|
+
`Presentation validation failed:
|
|
1845
|
+
${errors.map((error) => ` - ${error.path}: ${error.message}`).join("\n")}`
|
|
1846
|
+
);
|
|
1847
|
+
this.name = "PresentationValidationError";
|
|
1848
|
+
this.errors = errors;
|
|
1849
|
+
}
|
|
1850
|
+
};
|
|
1851
|
+
function assertValidPresentation(input, validation) {
|
|
1852
|
+
if (validation?.enabled === false) return;
|
|
1853
|
+
const options = {
|
|
1854
|
+
allowUnknownFields: validation?.allowUnknownFields
|
|
1855
|
+
};
|
|
1856
|
+
const result = typeof input === "string" ? validateJsonPresentationDocument(input, options) : validatePresentationDocument(input, options);
|
|
1857
|
+
if (!result.valid) {
|
|
1858
|
+
throw new PresentationValidationError(result.errors);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1616
1861
|
function isPresentationComponentDefinition(definition) {
|
|
1617
1862
|
if (typeof definition !== "object" || definition === null) return false;
|
|
1618
1863
|
const def = definition;
|
|
1619
1864
|
return def.name === "pptx" && "props" in def;
|
|
1620
1865
|
}
|
|
1621
1866
|
async function generatePresentation(document, options, warnings) {
|
|
1867
|
+
assertValidPresentation(document, options?.validation);
|
|
1622
1868
|
if (!document || document.name !== "pptx") {
|
|
1623
1869
|
throw new Error("Top-level component must be a pptx component");
|
|
1624
1870
|
}
|
|
@@ -1637,6 +1883,7 @@ async function generateBufferFromJson(jsonConfig, options) {
|
|
|
1637
1883
|
return result.buffer;
|
|
1638
1884
|
}
|
|
1639
1885
|
async function generateBufferWithWarnings(jsonConfig, options) {
|
|
1886
|
+
assertValidPresentation(jsonConfig, options?.validation);
|
|
1640
1887
|
let component;
|
|
1641
1888
|
if (typeof jsonConfig === "string") {
|
|
1642
1889
|
const parsed = JSON.parse(jsonConfig);
|
|
@@ -1705,36 +1952,21 @@ async function generateBufferWithWarnings(jsonConfig, options) {
|
|
|
1705
1952
|
warnings
|
|
1706
1953
|
);
|
|
1707
1954
|
const data = await pptx.write({ outputType: "nodebuffer" });
|
|
1708
|
-
const buffer = await
|
|
1955
|
+
const buffer = await packagePresentationBuffer(data, options);
|
|
1709
1956
|
return { buffer, warnings };
|
|
1710
1957
|
}
|
|
1711
1958
|
async function generateAndSaveFromJson(jsonConfig, outputPath, options) {
|
|
1712
1959
|
const buffer = await generateBufferFromJson(jsonConfig, options);
|
|
1713
1960
|
writeFileSync(outputPath, buffer);
|
|
1714
1961
|
}
|
|
1715
|
-
async function generateFromFile(filePath, outputPath) {
|
|
1962
|
+
async function generateFromFile(filePath, outputPath, options) {
|
|
1716
1963
|
const { readFileSync } = await import("fs");
|
|
1717
1964
|
const json = readFileSync(filePath, "utf-8");
|
|
1718
|
-
await generateAndSaveFromJson(json, outputPath);
|
|
1719
|
-
}
|
|
1720
|
-
var MEDIUM_STYLE_2_ACCENT_1 = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}";
|
|
1721
|
-
var NO_STYLE_NO_GRID = "{2D5ABB26-0587-4C30-8999-92F81FD0307C}";
|
|
1722
|
-
async function neutralizeTableStyle(buffer) {
|
|
1723
|
-
const zip = await JSZip.loadAsync(buffer);
|
|
1724
|
-
let changed = false;
|
|
1725
|
-
for (const [path2, entry] of Object.entries(zip.files)) {
|
|
1726
|
-
if (!path2.match(/^ppt\/slides\/slide\d+\.xml$/)) continue;
|
|
1727
|
-
const xml = await entry.async("string");
|
|
1728
|
-
if (xml.includes(MEDIUM_STYLE_2_ACCENT_1)) {
|
|
1729
|
-
zip.file(path2, xml.replaceAll(MEDIUM_STYLE_2_ACCENT_1, NO_STYLE_NO_GRID));
|
|
1730
|
-
changed = true;
|
|
1731
|
-
}
|
|
1732
|
-
}
|
|
1733
|
-
return changed ? await zip.generateAsync({ type: "nodebuffer" }) : buffer;
|
|
1965
|
+
await generateAndSaveFromJson(json, outputPath, options);
|
|
1734
1966
|
}
|
|
1735
|
-
async function savePresentation(pptx, outputPath) {
|
|
1967
|
+
async function savePresentation(pptx, outputPath, options) {
|
|
1736
1968
|
const data = await pptx.write({ outputType: "nodebuffer" });
|
|
1737
|
-
const buffer = await
|
|
1969
|
+
const buffer = await packagePresentationBuffer(data, options);
|
|
1738
1970
|
writeFileSync(outputPath, buffer);
|
|
1739
1971
|
}
|
|
1740
1972
|
var PresentationGenerator = {
|
|
@@ -1754,7 +1986,6 @@ import {
|
|
|
1754
1986
|
} from "@json-to-office/shared/plugin";
|
|
1755
1987
|
|
|
1756
1988
|
// src/plugin/createPresentationGenerator.ts
|
|
1757
|
-
import JSZip2 from "jszip";
|
|
1758
1989
|
import {
|
|
1759
1990
|
resolveComponentVersion as resolveComponentVersion2,
|
|
1760
1991
|
DuplicateComponentError as DuplicateComponentError2,
|
|
@@ -1767,23 +1998,33 @@ import {
|
|
|
1767
1998
|
validateCustomComponentProps,
|
|
1768
1999
|
ComponentValidationError
|
|
1769
2000
|
} from "@json-to-office/shared/plugin";
|
|
1770
|
-
import {
|
|
2001
|
+
import { validatePresentationDocument as validatePresentationDocument2 } from "@json-to-office/shared-pptx";
|
|
1771
2002
|
import {
|
|
1772
2003
|
DuplicateComponentError,
|
|
1773
2004
|
ComponentValidationError as ComponentValidationError2
|
|
1774
2005
|
} from "@json-to-office/shared/plugin";
|
|
1775
|
-
function validateComponentProps(schema, props, componentName) {
|
|
2006
|
+
function validateComponentProps(schema, props, componentName, opts) {
|
|
1776
2007
|
return validateCustomComponentProps(schema.propsSchema, props, {
|
|
1777
|
-
|
|
1778
|
-
|
|
2008
|
+
// Render-time cleaning remains the default. The document-validation path
|
|
2009
|
+
// passes clean:false so unknown custom props are rejected when the custom
|
|
2010
|
+
// schema declares additionalProperties:false.
|
|
2011
|
+
clean: opts?.clean ?? true,
|
|
2012
|
+
applyDefaults: opts?.applyDefaults ?? true,
|
|
1779
2013
|
componentName
|
|
1780
2014
|
});
|
|
1781
2015
|
}
|
|
1782
|
-
function validatePresentation(document, customComponents) {
|
|
1783
|
-
const
|
|
1784
|
-
|
|
2016
|
+
function validatePresentation(document, customComponents, options) {
|
|
2017
|
+
const knownCustomNames = new Set(customComponents.map((c) => c.name));
|
|
2018
|
+
const documentResult = validatePresentationDocument2(document, {
|
|
2019
|
+
knownCustomNames,
|
|
2020
|
+
allowUnknownFields: options?.allowUnknownFields
|
|
2021
|
+
});
|
|
2022
|
+
const errors = [...documentResult.errors];
|
|
1785
2023
|
function validateComponents(components, pathPrefix = "children") {
|
|
1786
2024
|
components.forEach((componentData, index) => {
|
|
2025
|
+
if (!componentData || typeof componentData !== "object" || Array.isArray(componentData)) {
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
1787
2028
|
const customComponent = customComponents.find(
|
|
1788
2029
|
(cc) => cc.name === componentData.name
|
|
1789
2030
|
);
|
|
@@ -1796,7 +2037,8 @@ function validatePresentation(document, customComponents) {
|
|
|
1796
2037
|
const validation = validateComponentProps(
|
|
1797
2038
|
versionEntry,
|
|
1798
2039
|
componentData.props,
|
|
1799
|
-
customComponent.name
|
|
2040
|
+
customComponent.name,
|
|
2041
|
+
{ clean: options?.allowUnknownFields === true }
|
|
1800
2042
|
);
|
|
1801
2043
|
if (!validation.valid && validation.errors) {
|
|
1802
2044
|
const indexedErrors = validation.errors.map(
|
|
@@ -1816,7 +2058,7 @@ function validatePresentation(document, customComponents) {
|
|
|
1816
2058
|
}
|
|
1817
2059
|
});
|
|
1818
2060
|
}
|
|
1819
|
-
if (document.children) {
|
|
2061
|
+
if (document && Array.isArray(document.children)) {
|
|
1820
2062
|
validateComponents(document.children);
|
|
1821
2063
|
}
|
|
1822
2064
|
return errors.length > 0 ? { valid: false, errors } : { valid: true, errors: [] };
|
|
@@ -1866,24 +2108,9 @@ async function exportPluginSchema(customComponents, outputPath, options = {}) {
|
|
|
1866
2108
|
|
|
1867
2109
|
// src/plugin/createPresentationGenerator.ts
|
|
1868
2110
|
import { applyExportMode as applyExportMode2, scopedThemeName as scopedThemeName2 } from "@json-to-office/shared";
|
|
1869
|
-
var MEDIUM_STYLE_2_ACCENT_12 = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}";
|
|
1870
|
-
var NO_STYLE_NO_GRID2 = "{2D5ABB26-0587-4C30-8999-92F81FD0307C}";
|
|
1871
|
-
async function neutralizeTableStyle2(buffer) {
|
|
1872
|
-
const zip = await JSZip2.loadAsync(buffer);
|
|
1873
|
-
let changed = false;
|
|
1874
|
-
for (const [path2, entry] of Object.entries(zip.files)) {
|
|
1875
|
-
if (!path2.match(/^ppt\/slides\/slide\d+\.xml$/)) continue;
|
|
1876
|
-
const xml = await entry.async("string");
|
|
1877
|
-
if (xml.includes(MEDIUM_STYLE_2_ACCENT_12)) {
|
|
1878
|
-
zip.file(path2, xml.replaceAll(MEDIUM_STYLE_2_ACCENT_12, NO_STYLE_NO_GRID2));
|
|
1879
|
-
changed = true;
|
|
1880
|
-
}
|
|
1881
|
-
}
|
|
1882
|
-
return changed ? await zip.generateAsync({ type: "nodebuffer" }) : buffer;
|
|
1883
|
-
}
|
|
1884
2111
|
function createBuilderImpl(state) {
|
|
1885
2112
|
const componentMap = new Map(state.components.map((c) => [c.name, c]));
|
|
1886
|
-
async function processSlideComponents(components, warningsCollector, theme, depth = 0) {
|
|
2113
|
+
async function processSlideComponents(components, warningsCollector, theme, validateEmitted, parentName, depth = 0) {
|
|
1887
2114
|
if (depth > 20) {
|
|
1888
2115
|
throw new Error(
|
|
1889
2116
|
"Maximum component nesting depth exceeded (20). Check for circular component references."
|
|
@@ -1915,6 +2142,8 @@ function createBuilderImpl(state) {
|
|
|
1915
2142
|
componentWithVersion.children,
|
|
1916
2143
|
warningsCollector,
|
|
1917
2144
|
theme,
|
|
2145
|
+
validateEmitted,
|
|
2146
|
+
void 0,
|
|
1918
2147
|
depth + 1
|
|
1919
2148
|
);
|
|
1920
2149
|
}
|
|
@@ -1934,10 +2163,13 @@ function createBuilderImpl(state) {
|
|
|
1934
2163
|
children: nestedChildren
|
|
1935
2164
|
});
|
|
1936
2165
|
const resultComponents = Array.isArray(result) ? result : [result];
|
|
2166
|
+
validateEmitted?.(resultComponents, versionLabel, parentName);
|
|
1937
2167
|
const processedResult = await processSlideComponents(
|
|
1938
2168
|
resultComponents,
|
|
1939
2169
|
warningsCollector,
|
|
1940
2170
|
theme,
|
|
2171
|
+
validateEmitted,
|
|
2172
|
+
parentName,
|
|
1941
2173
|
depth + 1
|
|
1942
2174
|
);
|
|
1943
2175
|
processed.push(...processedResult);
|
|
@@ -1961,6 +2193,8 @@ function createBuilderImpl(state) {
|
|
|
1961
2193
|
componentData.children,
|
|
1962
2194
|
warningsCollector,
|
|
1963
2195
|
theme,
|
|
2196
|
+
validateEmitted,
|
|
2197
|
+
componentData.name,
|
|
1964
2198
|
depth + 1
|
|
1965
2199
|
);
|
|
1966
2200
|
processed.push({
|
|
@@ -1990,16 +2224,31 @@ function createBuilderImpl(state) {
|
|
|
1990
2224
|
customThemes: state.customThemes,
|
|
1991
2225
|
debug: state.debug,
|
|
1992
2226
|
services: state.services,
|
|
1993
|
-
fonts: state.fonts
|
|
2227
|
+
fonts: state.fonts,
|
|
2228
|
+
validation: state.validation,
|
|
2229
|
+
packaging: state.packaging
|
|
1994
2230
|
};
|
|
1995
2231
|
return createBuilderImpl(
|
|
1996
2232
|
newState
|
|
1997
2233
|
);
|
|
1998
2234
|
}
|
|
1999
|
-
async function generate(document) {
|
|
2235
|
+
async function generate(document, options) {
|
|
2000
2236
|
try {
|
|
2001
2237
|
let internalDocument = document;
|
|
2002
|
-
|
|
2238
|
+
const validationOptions = {
|
|
2239
|
+
...state.validation,
|
|
2240
|
+
...options?.validation
|
|
2241
|
+
};
|
|
2242
|
+
if (validationOptions.enabled !== false) {
|
|
2243
|
+
const result = validatePresentation(
|
|
2244
|
+
internalDocument,
|
|
2245
|
+
state.components,
|
|
2246
|
+
{ allowUnknownFields: validationOptions.allowUnknownFields }
|
|
2247
|
+
);
|
|
2248
|
+
if (!result.valid) {
|
|
2249
|
+
throw new ComponentValidationError3(result.errors, internalDocument);
|
|
2250
|
+
}
|
|
2251
|
+
} else if (!internalDocument || internalDocument.name !== "pptx") {
|
|
2003
2252
|
throw new Error("Top-level component must be a pptx component");
|
|
2004
2253
|
}
|
|
2005
2254
|
let inlineTheme;
|
|
@@ -2030,7 +2279,39 @@ function createBuilderImpl(state) {
|
|
|
2030
2279
|
component: "fontRegistry"
|
|
2031
2280
|
});
|
|
2032
2281
|
}
|
|
2033
|
-
const
|
|
2282
|
+
const validateEmitted = validationOptions.enabled === false ? void 0 : (emitted, componentLabel, parentName) => {
|
|
2283
|
+
let validationDocument;
|
|
2284
|
+
if (parentName === "pptx") {
|
|
2285
|
+
validationDocument = { ...mode.doc, children: emitted };
|
|
2286
|
+
} else if (parentName === "slide") {
|
|
2287
|
+
validationDocument = {
|
|
2288
|
+
...mode.doc,
|
|
2289
|
+
children: [{ name: "slide", props: {}, children: emitted }]
|
|
2290
|
+
};
|
|
2291
|
+
} else {
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
const result = validatePresentation(
|
|
2295
|
+
validationDocument,
|
|
2296
|
+
state.components,
|
|
2297
|
+
{ allowUnknownFields: validationOptions.allowUnknownFields }
|
|
2298
|
+
);
|
|
2299
|
+
if (!result.valid) {
|
|
2300
|
+
throw new ComponentValidationError3(
|
|
2301
|
+
result.errors.map((error) => ({
|
|
2302
|
+
...error,
|
|
2303
|
+
message: `custom component '${componentLabel}' emitted invalid output \u2014 ${error.message}`
|
|
2304
|
+
})),
|
|
2305
|
+
emitted
|
|
2306
|
+
);
|
|
2307
|
+
}
|
|
2308
|
+
};
|
|
2309
|
+
const processedChildren = mode.doc.children ? await processAllSlides(
|
|
2310
|
+
mode.doc.children,
|
|
2311
|
+
warnings,
|
|
2312
|
+
resolvedTheme,
|
|
2313
|
+
validateEmitted
|
|
2314
|
+
) : [];
|
|
2034
2315
|
const themeName = scopedThemeName2(baseThemeName, state.fonts?.mode);
|
|
2035
2316
|
const docWithScopedTheme = themeName !== baseThemeName ? {
|
|
2036
2317
|
...mode.doc,
|
|
@@ -2038,6 +2319,20 @@ function createBuilderImpl(state) {
|
|
|
2038
2319
|
children: processedChildren
|
|
2039
2320
|
} : { ...mode.doc, children: processedChildren };
|
|
2040
2321
|
const processedDocument = docWithScopedTheme;
|
|
2322
|
+
if (validationOptions.enabled !== false) {
|
|
2323
|
+
const result = validatePresentation(processedDocument, [], {
|
|
2324
|
+
allowUnknownFields: validationOptions.allowUnknownFields
|
|
2325
|
+
});
|
|
2326
|
+
if (!result.valid) {
|
|
2327
|
+
throw new ComponentValidationError3(
|
|
2328
|
+
result.errors.map((error) => ({
|
|
2329
|
+
...error,
|
|
2330
|
+
message: `expanded plugin output failed validation \u2014 ${error.message}`
|
|
2331
|
+
})),
|
|
2332
|
+
processedDocument
|
|
2333
|
+
);
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2041
2336
|
await resolveDocumentFonts(
|
|
2042
2337
|
processedDocument,
|
|
2043
2338
|
resolvedTheme,
|
|
@@ -2054,7 +2349,10 @@ function createBuilderImpl(state) {
|
|
|
2054
2349
|
});
|
|
2055
2350
|
const pptx = await renderPresentation(processed, warnings);
|
|
2056
2351
|
const data = await pptx.write({ outputType: "nodebuffer" });
|
|
2057
|
-
const buffer = await
|
|
2352
|
+
const buffer = await packagePresentationBuffer(data, {
|
|
2353
|
+
deterministic: options?.deterministic ?? state.packaging.deterministic,
|
|
2354
|
+
generatedAt: options?.generatedAt ?? state.packaging.generatedAt
|
|
2355
|
+
});
|
|
2058
2356
|
return { buffer, warnings };
|
|
2059
2357
|
} catch (error) {
|
|
2060
2358
|
if (state.debug) {
|
|
@@ -2063,29 +2361,33 @@ function createBuilderImpl(state) {
|
|
|
2063
2361
|
throw error;
|
|
2064
2362
|
}
|
|
2065
2363
|
}
|
|
2066
|
-
async function processAllSlides(children, warnings, theme) {
|
|
2364
|
+
async function processAllSlides(children, warnings, theme, validateEmitted) {
|
|
2067
2365
|
const result = [];
|
|
2068
2366
|
for (const child of children) {
|
|
2069
2367
|
if (child.name === "slide" && child.children) {
|
|
2070
2368
|
const processedSlideChildren = await processSlideComponents(
|
|
2071
2369
|
child.children,
|
|
2072
2370
|
warnings,
|
|
2073
|
-
theme
|
|
2371
|
+
theme,
|
|
2372
|
+
validateEmitted,
|
|
2373
|
+
"slide"
|
|
2074
2374
|
);
|
|
2075
2375
|
result.push({ ...child, children: processedSlideChildren });
|
|
2076
2376
|
} else {
|
|
2077
2377
|
const processedTopLevel = await processSlideComponents(
|
|
2078
2378
|
[child],
|
|
2079
2379
|
warnings,
|
|
2080
|
-
theme
|
|
2380
|
+
theme,
|
|
2381
|
+
validateEmitted,
|
|
2382
|
+
"pptx"
|
|
2081
2383
|
);
|
|
2082
2384
|
result.push(...processedTopLevel);
|
|
2083
2385
|
}
|
|
2084
2386
|
}
|
|
2085
2387
|
return result;
|
|
2086
2388
|
}
|
|
2087
|
-
async function generateFile(document, outputPath) {
|
|
2088
|
-
const { buffer, warnings } = await generate(document);
|
|
2389
|
+
async function generateFile(document, outputPath, options) {
|
|
2390
|
+
const { buffer, warnings } = await generate(document, options);
|
|
2089
2391
|
const fs = await import("fs/promises");
|
|
2090
2392
|
await fs.writeFile(outputPath, new Uint8Array(buffer));
|
|
2091
2393
|
return { warnings };
|
|
@@ -2162,7 +2464,12 @@ function createPresentationGenerator(options = {}) {
|
|
|
2162
2464
|
customThemes: options.customThemes,
|
|
2163
2465
|
debug: options.debug ?? false,
|
|
2164
2466
|
services: options.services,
|
|
2165
|
-
fonts: options.fonts
|
|
2467
|
+
fonts: options.fonts,
|
|
2468
|
+
validation: options.validation,
|
|
2469
|
+
packaging: {
|
|
2470
|
+
deterministic: options.deterministic,
|
|
2471
|
+
generatedAt: options.generatedAt
|
|
2472
|
+
}
|
|
2166
2473
|
};
|
|
2167
2474
|
return createBuilderImpl(initialState);
|
|
2168
2475
|
}
|
|
@@ -2176,9 +2483,11 @@ function getPptxCoreVersion() {
|
|
|
2176
2483
|
}
|
|
2177
2484
|
export {
|
|
2178
2485
|
ComponentValidationError2 as ComponentValidationError,
|
|
2486
|
+
DEFAULT_GENERATED_AT,
|
|
2179
2487
|
DEFAULT_PPTX_THEME,
|
|
2180
2488
|
DuplicateComponentError,
|
|
2181
2489
|
PresentationGenerator,
|
|
2490
|
+
PresentationValidationError,
|
|
2182
2491
|
W as WarningCodes,
|
|
2183
2492
|
cleanComponentProps,
|
|
2184
2493
|
createComponent,
|
|
@@ -2196,6 +2505,7 @@ export {
|
|
|
2196
2505
|
isPresentationComponent,
|
|
2197
2506
|
isPresentationComponentDefinition,
|
|
2198
2507
|
isSlideComponent,
|
|
2508
|
+
packagePresentationBuffer,
|
|
2199
2509
|
pptxThemes,
|
|
2200
2510
|
renderComponent,
|
|
2201
2511
|
renderHighchartsComponent,
|