@json-to-office/core-pptx 0.22.0 → 0.25.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 +21 -0
- package/dist/components/chart.d.ts.map +1 -1
- package/dist/components/shape.d.ts +22 -7
- package/dist/components/shape.d.ts.map +1 -1
- package/dist/components/text.d.ts +21 -1
- package/dist/components/text.d.ts.map +1 -1
- package/dist/core/generator.d.ts +2 -2
- package/dist/core/generator.d.ts.map +1 -1
- package/dist/core/packagePresentation.d.ts +7 -0
- package/dist/core/packagePresentation.d.ts.map +1 -1
- package/dist/core/render.d.ts +2 -2
- package/dist/core/render.d.ts.map +1 -1
- package/dist/index.js +276 -31
- package/dist/index.js.map +1 -1
- package/dist/plugin/createPresentationGenerator.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +21 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/fillXml.d.ts +19 -0
- package/dist/utils/fillXml.d.ts.map +1 -0
- package/dist/utils/warn.d.ts +3 -0
- package/dist/utils/warn.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -25,6 +25,9 @@ var W = {
|
|
|
25
25
|
THEME_COLOR_FALLBACK: "THEME_COLOR_FALLBACK",
|
|
26
26
|
UNKNOWN_COLOR: "UNKNOWN_COLOR",
|
|
27
27
|
GRID_POSITION_CLAMPED: "GRID_POSITION_CLAMPED",
|
|
28
|
+
TEXT_NO_CONTENT: "TEXT_NO_CONTENT",
|
|
29
|
+
UNKNOWN_PATTERN_PRESET: "UNKNOWN_PATTERN_PRESET",
|
|
30
|
+
ADVANCED_FILL_FALLBACK: "ADVANCED_FILL_FALLBACK",
|
|
28
31
|
IMAGE_ZERO_BOX: "IMAGE_ZERO_BOX",
|
|
29
32
|
FONT_UNRESOLVED: "FONT_UNRESOLVED"
|
|
30
33
|
};
|
|
@@ -591,6 +594,16 @@ function resolvePagePlaceholders(text, ctx) {
|
|
|
591
594
|
return text.replace(/\{PAGE_NUMBER\}/g, fmt(slideNumber)).replace(/\{PAGE_COUNT\}/g, fmt(totalSlides));
|
|
592
595
|
}
|
|
593
596
|
function renderTextComponent(slide, props, theme, warnings, slideCtx) {
|
|
597
|
+
const runs = props.runs && props.runs.length > 0 ? props.runs : void 0;
|
|
598
|
+
if (props.text === void 0 && !runs) {
|
|
599
|
+
warn(
|
|
600
|
+
warnings,
|
|
601
|
+
W.TEXT_NO_CONTENT,
|
|
602
|
+
'Text component has neither "text" nor "runs" \u2014 skipped',
|
|
603
|
+
{ component: "text" }
|
|
604
|
+
);
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
594
607
|
const style = props.style ? theme.styles?.[props.style] : void 0;
|
|
595
608
|
const isHeadingStyle = props.style && /^(title|heading)/.test(props.style);
|
|
596
609
|
const opts = {};
|
|
@@ -600,7 +613,10 @@ function renderTextComponent(slide, props, theme, warnings, slideCtx) {
|
|
|
600
613
|
if (props.h !== void 0) opts.h = props.h;
|
|
601
614
|
if (props.h === void 0) {
|
|
602
615
|
const fontSize = props.fontSize ?? theme.defaults.fontSize ?? 18;
|
|
603
|
-
const lines =
|
|
616
|
+
const lines = runs ? runs.reduce(
|
|
617
|
+
(count, run) => count + (run.breakLine ? 1 : 0) + (run.text.match(/\n/g)?.length ?? 0),
|
|
618
|
+
1
|
|
619
|
+
) : (props.text.match(/\n/g)?.length ?? 0) + 1;
|
|
604
620
|
opts.h = Math.max(0.5, fontSize / 72 * 1.6 * lines);
|
|
605
621
|
opts.isTextBox = true;
|
|
606
622
|
}
|
|
@@ -611,6 +627,7 @@ function renderTextComponent(slide, props, theme, warnings, slideCtx) {
|
|
|
611
627
|
theme,
|
|
612
628
|
warnings
|
|
613
629
|
);
|
|
630
|
+
const preAliasFamily = opts.fontFace;
|
|
614
631
|
const bold = props.bold ?? style?.bold;
|
|
615
632
|
const italic = props.italic ?? style?.italic;
|
|
616
633
|
const fontWeight = props.fontWeight ?? style?.fontWeight;
|
|
@@ -661,7 +678,11 @@ function renderTextComponent(slide, props, theme, warnings, slideCtx) {
|
|
|
661
678
|
}
|
|
662
679
|
applyHyperlink(opts, props.hyperlink, "text", warnings);
|
|
663
680
|
const lineSpacing = props.lineSpacing ?? style?.lineSpacing;
|
|
664
|
-
if (
|
|
681
|
+
if (props.lineSpacingMultiple !== void 0) {
|
|
682
|
+
opts.lineSpacingMultiple = props.lineSpacingMultiple;
|
|
683
|
+
} else if (lineSpacing !== void 0) {
|
|
684
|
+
opts.lineSpacing = lineSpacing;
|
|
685
|
+
}
|
|
665
686
|
const charSpacing = props.charSpacing ?? style?.charSpacing;
|
|
666
687
|
if (charSpacing !== void 0) opts.charSpacing = charSpacing;
|
|
667
688
|
if (props.paraSpaceBefore !== void 0)
|
|
@@ -669,6 +690,49 @@ function renderTextComponent(slide, props, theme, warnings, slideCtx) {
|
|
|
669
690
|
const paraSpaceAfter = props.paraSpaceAfter ?? style?.paraSpaceAfter;
|
|
670
691
|
if (paraSpaceAfter !== void 0) opts.paraSpaceAfter = paraSpaceAfter;
|
|
671
692
|
if (props.breakLine) opts.breakLine = true;
|
|
693
|
+
if (runs) {
|
|
694
|
+
const runSegments = runs.map((run) => {
|
|
695
|
+
const runOpts = {};
|
|
696
|
+
if (run.fontSize != null) runOpts.fontSize = run.fontSize;
|
|
697
|
+
if (run.fontFace != null) runOpts.fontFace = run.fontFace;
|
|
698
|
+
if (run.color != null)
|
|
699
|
+
runOpts.color = resolveColor(run.color, theme, warnings);
|
|
700
|
+
if (run.strike != null) runOpts.strike = run.strike;
|
|
701
|
+
if (run.underline !== void 0) {
|
|
702
|
+
if (typeof run.underline === "boolean") {
|
|
703
|
+
if (run.underline) runOpts.underline = { style: "sng" };
|
|
704
|
+
} else {
|
|
705
|
+
runOpts.underline = run.underline;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
if (run.superscript != null) runOpts.superscript = run.superscript;
|
|
709
|
+
if (run.subscript != null) runOpts.subscript = run.subscript;
|
|
710
|
+
if (run.charSpacing != null) runOpts.charSpacing = run.charSpacing;
|
|
711
|
+
if (run.breakLine != null) runOpts.breakLine = run.breakLine;
|
|
712
|
+
const effWeight = run.fontWeight ?? fontWeight;
|
|
713
|
+
const effBold = run.bold ?? bold;
|
|
714
|
+
const effItalic = run.italic ?? italic;
|
|
715
|
+
if (effBold != null) runOpts.bold = effBold;
|
|
716
|
+
if (effItalic != null) runOpts.italic = effItalic;
|
|
717
|
+
if (effWeight != null || effBold === true) {
|
|
718
|
+
if (run.fontFace == null) {
|
|
719
|
+
const w = applyFontWeight({
|
|
720
|
+
family: preAliasFamily,
|
|
721
|
+
fontWeight: effWeight,
|
|
722
|
+
italic: effItalic,
|
|
723
|
+
bold: effBold
|
|
724
|
+
});
|
|
725
|
+
if (w.fontFace !== void 0) runOpts.fontFace = w.fontFace;
|
|
726
|
+
if (w.bold !== void 0) runOpts.bold = w.bold;
|
|
727
|
+
if (w.italic !== void 0) runOpts.italic = w.italic;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
const runText = slideCtx ? resolvePagePlaceholders(run.text, slideCtx) : run.text;
|
|
731
|
+
return { text: runText, options: runOpts };
|
|
732
|
+
});
|
|
733
|
+
slide.addText(runSegments, opts);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
672
736
|
const text = slideCtx ? resolvePagePlaceholders(props.text, slideCtx) : props.text;
|
|
673
737
|
slide.addText(text, opts);
|
|
674
738
|
}
|
|
@@ -842,6 +906,44 @@ async function renderImageComponent(slide, props, theme, warnings, slideWidth =
|
|
|
842
906
|
slide.addImage(opts);
|
|
843
907
|
}
|
|
844
908
|
|
|
909
|
+
// src/components/shape.ts
|
|
910
|
+
import { PATTERN_FILL_PRESETS } from "@json-to-office/shared-pptx";
|
|
911
|
+
|
|
912
|
+
// src/utils/fillXml.ts
|
|
913
|
+
var ANGLE_UNIT = 6e4;
|
|
914
|
+
var PCT_UNIT = 1e3;
|
|
915
|
+
var RADIAL_FOCUS_RECTS = {
|
|
916
|
+
center: { l: 5e4, t: 5e4, r: 5e4, b: 5e4 },
|
|
917
|
+
topLeft: { l: 0, t: 0, r: 1e5, b: 1e5 },
|
|
918
|
+
topRight: { l: 1e5, t: 0, r: 0, b: 1e5 },
|
|
919
|
+
bottomLeft: { l: 0, t: 1e5, r: 1e5, b: 0 },
|
|
920
|
+
bottomRight: { l: 1e5, t: 1e5, r: 0, b: 0 }
|
|
921
|
+
};
|
|
922
|
+
function gradientStopXml(color, pos, transparency, theme, warnings) {
|
|
923
|
+
const hex = resolveColor(color, theme, warnings).toUpperCase();
|
|
924
|
+
const alpha = transparency !== void 0 ? `<a:alpha val="${Math.round((100 - transparency) * PCT_UNIT)}"/>` : "";
|
|
925
|
+
return `<a:gs pos="${Math.round(pos * PCT_UNIT)}"><a:srgbClr val="${hex}">${alpha}</a:srgbClr></a:gs>`;
|
|
926
|
+
}
|
|
927
|
+
function buildGradientFillXml(gradient, theme, warnings) {
|
|
928
|
+
const stops = gradient.stops.map(
|
|
929
|
+
(stop) => gradientStopXml(stop.color, stop.pos, stop.transparency, theme, warnings)
|
|
930
|
+
).join("");
|
|
931
|
+
let shade;
|
|
932
|
+
if (gradient.type === "radial") {
|
|
933
|
+
const rect = RADIAL_FOCUS_RECTS[gradient.focus ?? "center"];
|
|
934
|
+
shade = `<a:path path="circle"><a:fillToRect l="${rect.l}" t="${rect.t}" r="${rect.r}" b="${rect.b}"/></a:path>`;
|
|
935
|
+
} else {
|
|
936
|
+
const angle = ((gradient.angle ?? 0) % 360 + 360) % 360;
|
|
937
|
+
shade = `<a:lin ang="${Math.round(angle * ANGLE_UNIT)}" scaled="1"/>`;
|
|
938
|
+
}
|
|
939
|
+
return `<a:gradFill rotWithShape="1"><a:gsLst>${stops}</a:gsLst>${shade}</a:gradFill>`;
|
|
940
|
+
}
|
|
941
|
+
function buildPatternFillXml(pattern, theme, warnings) {
|
|
942
|
+
const fg = resolveColor(pattern.foreground, theme, warnings).toUpperCase();
|
|
943
|
+
const bg = resolveColor(pattern.background, theme, warnings).toUpperCase();
|
|
944
|
+
return `<a:pattFill prst="${pattern.preset}"><a:fgClr><a:srgbClr val="${fg}"/></a:fgClr><a:bgClr><a:srgbClr val="${bg}"/></a:bgClr></a:pattFill>`;
|
|
945
|
+
}
|
|
946
|
+
|
|
845
947
|
// src/components/shape.ts
|
|
846
948
|
var SHAPE_TYPE_MAP = {
|
|
847
949
|
rect: "rect",
|
|
@@ -860,17 +962,67 @@ var SHAPE_TYPE_MAP = {
|
|
|
860
962
|
heart: "heart",
|
|
861
963
|
lightning: "lightningBolt"
|
|
862
964
|
};
|
|
863
|
-
function
|
|
965
|
+
function applyShapeFill(opts, fill, theme, warnings, pendingFills) {
|
|
966
|
+
let gradient = fill.gradient;
|
|
967
|
+
let pattern = fill.pattern;
|
|
968
|
+
if (gradient && pattern) {
|
|
969
|
+
warn(
|
|
970
|
+
warnings,
|
|
971
|
+
W.ADVANCED_FILL_FALLBACK,
|
|
972
|
+
'Shape fill sets both "gradient" and "pattern" \u2014 using the gradient',
|
|
973
|
+
{ component: "shape" }
|
|
974
|
+
);
|
|
975
|
+
pattern = void 0;
|
|
976
|
+
}
|
|
977
|
+
let unknownPresetForeground;
|
|
978
|
+
if (pattern && !PATTERN_FILL_PRESETS.includes(pattern.preset)) {
|
|
979
|
+
warn(
|
|
980
|
+
warnings,
|
|
981
|
+
W.UNKNOWN_PATTERN_PRESET,
|
|
982
|
+
`Unknown pattern preset "${pattern.preset}" \u2014 falling back to solid foreground`,
|
|
983
|
+
{ component: "shape" }
|
|
984
|
+
);
|
|
985
|
+
unknownPresetForeground = pattern.foreground;
|
|
986
|
+
pattern = void 0;
|
|
987
|
+
}
|
|
988
|
+
if (gradient || pattern) {
|
|
989
|
+
const sentinel = resolveColor(
|
|
990
|
+
fill.color ?? (gradient ? gradient.stops[0].color : pattern.foreground),
|
|
991
|
+
theme,
|
|
992
|
+
warnings
|
|
993
|
+
);
|
|
994
|
+
if (pendingFills) {
|
|
995
|
+
const xml = gradient ? buildGradientFillXml(gradient, theme, warnings) : buildPatternFillXml(pattern, theme, warnings);
|
|
996
|
+
const objectName = `__jto_fill_${pendingFills.length}__`;
|
|
997
|
+
pendingFills.push({ objectName, xml });
|
|
998
|
+
opts.objectName = objectName;
|
|
999
|
+
} else {
|
|
1000
|
+
warn(
|
|
1001
|
+
warnings,
|
|
1002
|
+
W.ADVANCED_FILL_FALLBACK,
|
|
1003
|
+
`${gradient ? "Gradient" : "Pattern"} fill requires the buffer generation pipeline \u2014 rendering a solid fill instead`,
|
|
1004
|
+
{ component: "shape" }
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
opts.fill = { color: sentinel };
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
const solid = fill.color ?? unknownPresetForeground;
|
|
1011
|
+
if (solid !== void 0) {
|
|
1012
|
+
opts.fill = { color: resolveColor(solid, theme, warnings) };
|
|
1013
|
+
if (fill.transparency !== void 0) {
|
|
1014
|
+
opts.fill.transparency = fill.transparency;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
function buildShapeOpts(props, theme, warnings, pendingFills) {
|
|
864
1019
|
const opts = {};
|
|
865
1020
|
if (props.x !== void 0) opts.x = props.x;
|
|
866
1021
|
if (props.y !== void 0) opts.y = props.y;
|
|
867
1022
|
if (props.w !== void 0) opts.w = props.w;
|
|
868
1023
|
if (props.h !== void 0) opts.h = props.h;
|
|
869
1024
|
if (props.fill) {
|
|
870
|
-
opts
|
|
871
|
-
if (props.fill.transparency !== void 0) {
|
|
872
|
-
opts.fill.transparency = props.fill.transparency;
|
|
873
|
-
}
|
|
1025
|
+
applyShapeFill(opts, props.fill, theme, warnings, pendingFills);
|
|
874
1026
|
}
|
|
875
1027
|
if (props.line) {
|
|
876
1028
|
opts.line = {};
|
|
@@ -886,6 +1038,9 @@ function buildShapeOpts(props, theme, warnings) {
|
|
|
886
1038
|
opts.line.dashType = props.line.dashType;
|
|
887
1039
|
}
|
|
888
1040
|
if (props.rotate !== void 0) opts.rotate = props.rotate;
|
|
1041
|
+
if (props.angleRange !== void 0) opts.angleRange = props.angleRange;
|
|
1042
|
+
if (props.flipH !== void 0) opts.flipH = props.flipH;
|
|
1043
|
+
if (props.flipV !== void 0) opts.flipV = props.flipV;
|
|
889
1044
|
if (props.rectRadius !== void 0) opts.rectRadius = props.rectRadius;
|
|
890
1045
|
if (props.shadow) {
|
|
891
1046
|
opts.shadow = {
|
|
@@ -899,7 +1054,7 @@ function buildShapeOpts(props, theme, warnings) {
|
|
|
899
1054
|
}
|
|
900
1055
|
return opts;
|
|
901
1056
|
}
|
|
902
|
-
function renderShapeComponent(slide, props, theme, pptx, warnings) {
|
|
1057
|
+
function renderShapeComponent(slide, props, theme, pptx, warnings, ctx) {
|
|
903
1058
|
const shapeTypeName = SHAPE_TYPE_MAP[props.type] || props.type;
|
|
904
1059
|
const shapeType = pptx.ShapeType[shapeTypeName];
|
|
905
1060
|
if (!shapeType) {
|
|
@@ -910,7 +1065,7 @@ function renderShapeComponent(slide, props, theme, pptx, warnings) {
|
|
|
910
1065
|
}
|
|
911
1066
|
const style = props.style ? theme.styles?.[props.style] : void 0;
|
|
912
1067
|
const isHeadingStyle = props.style && /^(title|heading)/.test(props.style);
|
|
913
|
-
const opts = buildShapeOpts(props, theme, warnings);
|
|
1068
|
+
const opts = buildShapeOpts(props, theme, warnings, ctx?.pendingFills);
|
|
914
1069
|
if (props.text && (!Array.isArray(props.text) || props.text.length > 0)) {
|
|
915
1070
|
opts.shape = shapeType;
|
|
916
1071
|
opts.fontSize = props.fontSize ?? style?.fontSize ?? theme.defaults.fontSize;
|
|
@@ -1262,6 +1417,14 @@ var CHART_TYPE_MAP = {
|
|
|
1262
1417
|
radar: "radar",
|
|
1263
1418
|
scatter: "scatter"
|
|
1264
1419
|
};
|
|
1420
|
+
function resolveGridLine(gridLine, theme, warnings) {
|
|
1421
|
+
const resolved = {};
|
|
1422
|
+
if (gridLine.style !== void 0) resolved.style = gridLine.style;
|
|
1423
|
+
if (gridLine.size !== void 0) resolved.size = gridLine.size;
|
|
1424
|
+
if (gridLine.color !== void 0)
|
|
1425
|
+
resolved.color = resolveColor(gridLine.color, theme, warnings);
|
|
1426
|
+
return resolved;
|
|
1427
|
+
}
|
|
1265
1428
|
function renderChartComponent(slide, props, theme, _pptx, warnings) {
|
|
1266
1429
|
const chartType = CHART_TYPE_MAP[props.type];
|
|
1267
1430
|
if (!chartType) {
|
|
@@ -1319,6 +1482,18 @@ function renderChartComponent(slide, props, theme, _pptx, warnings) {
|
|
|
1319
1482
|
opts.legendColor = props.legendColor ? resolveColor(props.legendColor, theme, warnings) : themeTextColor;
|
|
1320
1483
|
opts.catAxisLabelColor = props.catAxisLabelColor ? resolveColor(props.catAxisLabelColor, theme, warnings) : themeTextColor;
|
|
1321
1484
|
opts.valAxisLabelColor = props.valAxisLabelColor ? resolveColor(props.valAxisLabelColor, theme, warnings) : themeTextColor;
|
|
1485
|
+
if (props.valAxisLabelFontSize !== void 0)
|
|
1486
|
+
opts.valAxisLabelFontSize = props.valAxisLabelFontSize;
|
|
1487
|
+
if (props.catAxisLineShow !== void 0)
|
|
1488
|
+
opts.catAxisLineShow = props.catAxisLineShow;
|
|
1489
|
+
if (props.valAxisLineShow !== void 0)
|
|
1490
|
+
opts.valAxisLineShow = props.valAxisLineShow;
|
|
1491
|
+
if (props.dataBorder !== void 0) {
|
|
1492
|
+
opts.dataBorder = {
|
|
1493
|
+
pt: props.dataBorder.pt,
|
|
1494
|
+
color: resolveColor(props.dataBorder.color, theme, warnings)
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1322
1497
|
if (props.showLegend !== void 0) opts.showLegend = props.showLegend;
|
|
1323
1498
|
if (props.showTitle !== void 0) opts.showTitle = props.showTitle;
|
|
1324
1499
|
if (props.showValue !== void 0) opts.showValue = props.showValue;
|
|
@@ -1345,6 +1520,10 @@ function renderChartComponent(slide, props, theme, _pptx, warnings) {
|
|
|
1345
1520
|
opts.catAxisLabelRotate = props.catAxisLabelRotate;
|
|
1346
1521
|
if (props.catAxisLabelFontSize !== void 0)
|
|
1347
1522
|
opts.catAxisLabelFontSize = props.catAxisLabelFontSize;
|
|
1523
|
+
if (props.catAxisLabelFontFace !== void 0)
|
|
1524
|
+
opts.catAxisLabelFontFace = props.catAxisLabelFontFace;
|
|
1525
|
+
if (props.catGridLine !== void 0)
|
|
1526
|
+
opts.catGridLine = resolveGridLine(props.catGridLine, theme, warnings);
|
|
1348
1527
|
if (props.valAxisTitle !== void 0) {
|
|
1349
1528
|
opts.valAxisTitle = props.valAxisTitle;
|
|
1350
1529
|
opts.showValAxisTitle = true;
|
|
@@ -1359,14 +1538,22 @@ function renderChartComponent(slide, props, theme, _pptx, warnings) {
|
|
|
1359
1538
|
opts.valAxisLabelFormatCode = props.valAxisLabelFormatCode;
|
|
1360
1539
|
if (props.valAxisMajorUnit !== void 0)
|
|
1361
1540
|
opts.valAxisMajorUnit = props.valAxisMajorUnit;
|
|
1541
|
+
if (props.valAxisLabelFontFace !== void 0)
|
|
1542
|
+
opts.valAxisLabelFontFace = props.valAxisLabelFontFace;
|
|
1543
|
+
if (props.valGridLine !== void 0)
|
|
1544
|
+
opts.valGridLine = resolveGridLine(props.valGridLine, theme, warnings);
|
|
1362
1545
|
if (props.barDir !== void 0) opts.barDir = props.barDir;
|
|
1363
1546
|
if (props.barGrouping !== void 0) opts.barGrouping = props.barGrouping;
|
|
1364
1547
|
if (props.barGapWidthPct !== void 0)
|
|
1365
1548
|
opts.barGapWidthPct = props.barGapWidthPct;
|
|
1549
|
+
if (props.barOverlapPct !== void 0)
|
|
1550
|
+
opts.barOverlapPct = props.barOverlapPct;
|
|
1366
1551
|
if (props.lineSmooth !== void 0) opts.lineSmooth = props.lineSmooth;
|
|
1367
1552
|
if (props.lineDataSymbol !== void 0)
|
|
1368
1553
|
opts.lineDataSymbol = props.lineDataSymbol;
|
|
1369
1554
|
if (props.lineSize !== void 0) opts.lineSize = props.lineSize;
|
|
1555
|
+
if (props.lineDataSymbolSize !== void 0)
|
|
1556
|
+
opts.lineDataSymbolSize = props.lineDataSymbolSize;
|
|
1370
1557
|
if (props.firstSliceAng !== void 0)
|
|
1371
1558
|
opts.firstSliceAng = props.firstSliceAng;
|
|
1372
1559
|
if (props.holeSize !== void 0) opts.holeSize = props.holeSize;
|
|
@@ -1403,7 +1590,7 @@ async function renderComponent(slide, component, theme, pptx, warnings, ctx) {
|
|
|
1403
1590
|
);
|
|
1404
1591
|
break;
|
|
1405
1592
|
case "shape":
|
|
1406
|
-
renderShapeComponent(slide, p, theme, pptx, warnings);
|
|
1593
|
+
renderShapeComponent(slide, p, theme, pptx, warnings, ctx);
|
|
1407
1594
|
break;
|
|
1408
1595
|
case "table":
|
|
1409
1596
|
renderTableComponent(slide, p, theme, pptx, warnings);
|
|
@@ -1460,7 +1647,7 @@ function buildSlideTemplateProps(def, theme, warnings) {
|
|
|
1460
1647
|
|
|
1461
1648
|
// src/core/render.ts
|
|
1462
1649
|
import { mergeWithDefaults as mergeWithDefaults3 } from "@json-to-office/shared";
|
|
1463
|
-
async function renderPresentation(processed, warnings) {
|
|
1650
|
+
async function renderPresentation(processed, warnings, pendingFills) {
|
|
1464
1651
|
const pptx = new PptxGenJS();
|
|
1465
1652
|
if (processed.metadata.title) pptx.title = processed.metadata.title;
|
|
1466
1653
|
if (processed.metadata.author) pptx.author = processed.metadata.author;
|
|
@@ -1505,10 +1692,37 @@ async function renderPresentation(processed, warnings) {
|
|
|
1505
1692
|
slideCtx,
|
|
1506
1693
|
services: processed.services,
|
|
1507
1694
|
slideWidth: processed.slideWidth,
|
|
1508
|
-
slideHeight: processed.slideHeight
|
|
1695
|
+
slideHeight: processed.slideHeight,
|
|
1696
|
+
pendingFills
|
|
1509
1697
|
};
|
|
1510
1698
|
const slide = slideData.template ? pptx.addSlide({ masterName: slideData.template }) : pptx.addSlide();
|
|
1511
|
-
|
|
1699
|
+
const templateDef = slideData.template ? templateMap.get(slideData.template) : void 0;
|
|
1700
|
+
if (slideData.template && !templateDef) {
|
|
1701
|
+
warn(
|
|
1702
|
+
warnings,
|
|
1703
|
+
W.MISSING_TEMPLATE,
|
|
1704
|
+
`Unknown template "${slideData.template}". Available: ${[...templateMap.keys()].join(", ")}`,
|
|
1705
|
+
{ slide: slideIdx }
|
|
1706
|
+
);
|
|
1707
|
+
}
|
|
1708
|
+
const backgroundGradient = slideData.background?.gradient ?? (slideData.background ? void 0 : templateDef?.background?.gradient);
|
|
1709
|
+
if (backgroundGradient) {
|
|
1710
|
+
renderShapeComponent(
|
|
1711
|
+
slide,
|
|
1712
|
+
{
|
|
1713
|
+
type: "rect",
|
|
1714
|
+
x: 0,
|
|
1715
|
+
y: 0,
|
|
1716
|
+
w: processed.slideWidth,
|
|
1717
|
+
h: processed.slideHeight,
|
|
1718
|
+
fill: { gradient: backgroundGradient }
|
|
1719
|
+
},
|
|
1720
|
+
processed.theme,
|
|
1721
|
+
pptx,
|
|
1722
|
+
warnings,
|
|
1723
|
+
renderCtx
|
|
1724
|
+
);
|
|
1725
|
+
} else if (slideData.background) {
|
|
1512
1726
|
if (slideData.background.color) {
|
|
1513
1727
|
slide.background = {
|
|
1514
1728
|
color: resolveColor(
|
|
@@ -1528,15 +1742,6 @@ async function renderPresentation(processed, warnings) {
|
|
|
1528
1742
|
if (slideData.hidden) {
|
|
1529
1743
|
slide.hidden = true;
|
|
1530
1744
|
}
|
|
1531
|
-
const templateDef = slideData.template ? templateMap.get(slideData.template) : void 0;
|
|
1532
|
-
if (slideData.template && !templateDef) {
|
|
1533
|
-
warn(
|
|
1534
|
-
warnings,
|
|
1535
|
-
W.MISSING_TEMPLATE,
|
|
1536
|
-
`Unknown template "${slideData.template}". Available: ${[...templateMap.keys()].join(", ")}`,
|
|
1537
|
-
{ slide: slideIdx }
|
|
1538
|
-
);
|
|
1539
|
-
}
|
|
1540
1745
|
const effectiveGrid = mergeGridConfigs(processed.grid, templateDef?.grid);
|
|
1541
1746
|
if (templateDef?.objects) {
|
|
1542
1747
|
for (const obj of templateDef.objects) {
|
|
@@ -1712,6 +1917,7 @@ async function resolveDocumentFonts(document, theme, warnings, fonts) {
|
|
|
1712
1917
|
import { applyExportMode, scopedThemeName } from "@json-to-office/shared";
|
|
1713
1918
|
import {
|
|
1714
1919
|
collectImageSourceConflicts,
|
|
1920
|
+
collectTextContentConflicts,
|
|
1715
1921
|
validateJsonPresentationDocument,
|
|
1716
1922
|
validatePresentationDocument
|
|
1717
1923
|
} from "@json-to-office/shared-pptx";
|
|
@@ -1721,6 +1927,23 @@ import JSZip from "jszip";
|
|
|
1721
1927
|
var MEDIUM_STYLE_2_ACCENT_1 = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}";
|
|
1722
1928
|
var NO_STYLE_NO_GRID = "{2D5ABB26-0587-4C30-8999-92F81FD0307C}";
|
|
1723
1929
|
var DEFAULT_GENERATED_AT = "2000-01-01T00:00:00.000Z";
|
|
1930
|
+
function applyPendingFills(xml, pendingFills) {
|
|
1931
|
+
let out = xml;
|
|
1932
|
+
for (const [index, fill] of pendingFills.entries()) {
|
|
1933
|
+
const marker = `name="${fill.objectName}"`;
|
|
1934
|
+
const markerIdx = out.indexOf(marker);
|
|
1935
|
+
if (markerIdx === -1) continue;
|
|
1936
|
+
const spEnd = out.indexOf("</p:sp>", markerIdx);
|
|
1937
|
+
const solidStart = out.indexOf("<a:solidFill>", markerIdx);
|
|
1938
|
+
const solidEndTag = "</a:solidFill>";
|
|
1939
|
+
const solidEnd = out.indexOf(solidEndTag, solidStart);
|
|
1940
|
+
if (solidStart !== -1 && solidEnd !== -1 && spEnd !== -1 && solidStart < spEnd) {
|
|
1941
|
+
out = out.slice(0, solidStart) + fill.xml + out.slice(solidEnd + solidEndTag.length);
|
|
1942
|
+
}
|
|
1943
|
+
out = out.slice(0, markerIdx) + `name="Fill ${index + 1}"` + out.slice(markerIdx + marker.length);
|
|
1944
|
+
}
|
|
1945
|
+
return out;
|
|
1946
|
+
}
|
|
1724
1947
|
function resolveGeneratedAt(value) {
|
|
1725
1948
|
const date = value === void 0 ? new Date(DEFAULT_GENERATED_AT) : new Date(value);
|
|
1726
1949
|
if (Number.isNaN(date.getTime())) {
|
|
@@ -1820,9 +2043,21 @@ async function packagePresentationBuffer(buffer, options = {}) {
|
|
|
1820
2043
|
let changed = false;
|
|
1821
2044
|
for (const [path2, entry] of Object.entries(zip.files)) {
|
|
1822
2045
|
if (!path2.match(/^ppt\/slides\/slide\d+\.xml$/)) continue;
|
|
1823
|
-
|
|
2046
|
+
let xml = await entry.async("string");
|
|
2047
|
+
let fileChanged = false;
|
|
1824
2048
|
if (xml.includes(MEDIUM_STYLE_2_ACCENT_1)) {
|
|
1825
|
-
|
|
2049
|
+
xml = xml.replaceAll(MEDIUM_STYLE_2_ACCENT_1, NO_STYLE_NO_GRID);
|
|
2050
|
+
fileChanged = true;
|
|
2051
|
+
}
|
|
2052
|
+
if (options.pendingFills?.length) {
|
|
2053
|
+
const withFills = applyPendingFills(xml, options.pendingFills);
|
|
2054
|
+
if (withFills !== xml) {
|
|
2055
|
+
xml = withFills;
|
|
2056
|
+
fileChanged = true;
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
if (fileChanged) {
|
|
2060
|
+
zip.file(path2, xml);
|
|
1826
2061
|
changed = true;
|
|
1827
2062
|
}
|
|
1828
2063
|
}
|
|
@@ -1863,12 +2098,15 @@ function isPresentationComponentDefinition(definition) {
|
|
|
1863
2098
|
const def = definition;
|
|
1864
2099
|
return def.name === "pptx" && "props" in def;
|
|
1865
2100
|
}
|
|
1866
|
-
async function generatePresentation(document, options, warnings) {
|
|
2101
|
+
async function generatePresentation(document, options, warnings, pendingFills) {
|
|
1867
2102
|
assertValidPresentation(document, options?.validation);
|
|
1868
2103
|
if (!document || document.name !== "pptx") {
|
|
1869
2104
|
throw new Error("Top-level component must be a pptx component");
|
|
1870
2105
|
}
|
|
1871
|
-
const sourceConflicts =
|
|
2106
|
+
const sourceConflicts = [
|
|
2107
|
+
...collectImageSourceConflicts(document),
|
|
2108
|
+
...collectTextContentConflicts(document)
|
|
2109
|
+
];
|
|
1872
2110
|
if (sourceConflicts.length > 0) {
|
|
1873
2111
|
throw new Error(
|
|
1874
2112
|
`Document validation failed:
|
|
@@ -1876,7 +2114,7 @@ ${sourceConflicts.map((e) => ` - ${e.path}: ${e.message}`).join("\n")}`
|
|
|
1876
2114
|
);
|
|
1877
2115
|
}
|
|
1878
2116
|
const processed = processPresentation(document, options);
|
|
1879
|
-
return await renderPresentation(processed, warnings);
|
|
2117
|
+
return await renderPresentation(processed, warnings, pendingFills);
|
|
1880
2118
|
}
|
|
1881
2119
|
async function generateBufferFromJson(jsonConfig, options) {
|
|
1882
2120
|
const result = await generateBufferWithWarnings(jsonConfig, options);
|
|
@@ -1946,13 +2184,18 @@ async function generateBufferWithWarnings(jsonConfig, options) {
|
|
|
1946
2184
|
[themeName]: resolvedTheme
|
|
1947
2185
|
}
|
|
1948
2186
|
};
|
|
2187
|
+
const pendingFills = [];
|
|
1949
2188
|
const pptx = await generatePresentation(
|
|
1950
2189
|
component,
|
|
1951
2190
|
effectiveOptions,
|
|
1952
|
-
warnings
|
|
2191
|
+
warnings,
|
|
2192
|
+
pendingFills
|
|
1953
2193
|
);
|
|
1954
2194
|
const data = await pptx.write({ outputType: "nodebuffer" });
|
|
1955
|
-
const buffer = await packagePresentationBuffer(data,
|
|
2195
|
+
const buffer = await packagePresentationBuffer(data, {
|
|
2196
|
+
...options,
|
|
2197
|
+
pendingFills
|
|
2198
|
+
});
|
|
1956
2199
|
return { buffer, warnings };
|
|
1957
2200
|
}
|
|
1958
2201
|
async function generateAndSaveFromJson(jsonConfig, outputPath, options) {
|
|
@@ -2347,11 +2590,13 @@ function createBuilderImpl(state) {
|
|
|
2347
2590
|
customThemes: effectiveCustomThemes,
|
|
2348
2591
|
services: state.services
|
|
2349
2592
|
});
|
|
2350
|
-
const
|
|
2593
|
+
const pendingFills = [];
|
|
2594
|
+
const pptx = await renderPresentation(processed, warnings, pendingFills);
|
|
2351
2595
|
const data = await pptx.write({ outputType: "nodebuffer" });
|
|
2352
2596
|
const buffer = await packagePresentationBuffer(data, {
|
|
2353
2597
|
deterministic: options?.deterministic ?? state.packaging.deterministic,
|
|
2354
|
-
generatedAt: options?.generatedAt ?? state.packaging.generatedAt
|
|
2598
|
+
generatedAt: options?.generatedAt ?? state.packaging.generatedAt,
|
|
2599
|
+
pendingFills
|
|
2355
2600
|
});
|
|
2356
2601
|
return { buffer, warnings };
|
|
2357
2602
|
} catch (error) {
|