json2pptx 0.4.2 → 0.5.1
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/README.md +15 -6
- package/dist/index.d.mts +39 -14
- package/dist/index.d.ts +39 -14
- package/dist/index.js +206 -112
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +206 -109
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -30,9 +30,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
ENABLE_DECK_JSON: () => ENABLE_DECK_JSON,
|
|
34
|
-
PPTX_JSON_PAYLOAD_PATH: () => PPTX_JSON_PAYLOAD_PATH,
|
|
35
|
-
PPTX_JSON_PAYLOAD_VERSION: () => PPTX_JSON_PAYLOAD_VERSION,
|
|
36
33
|
buildPptxBlob: () => buildPptxBlob,
|
|
37
34
|
createPPTX: () => createPPTX,
|
|
38
35
|
getElementRange: () => getElementRange,
|
|
@@ -939,10 +936,117 @@ function getDashTypeMap() {
|
|
|
939
936
|
return dashTypeMap;
|
|
940
937
|
}
|
|
941
938
|
|
|
939
|
+
// src/renderers/fill-patch.ts
|
|
940
|
+
function stripFillTags(value) {
|
|
941
|
+
return value.replace(/<a:solidFill>[\s\S]*?<\/a:solidFill>/g, "").replace(/<a:gradFill[\s\S]*?<\/a:gradFill>/g, "").replace(/<a:blipFill>[\s\S]*?<\/a:blipFill>/g, "").replace(/<a:noFill\s*\/>/g, "").replace(/<a:noFill><\/a:noFill>/g, "");
|
|
942
|
+
}
|
|
943
|
+
function buildColorXml(color) {
|
|
944
|
+
const normalized = formatColor(color);
|
|
945
|
+
const alphaXml = normalized.alpha < 1 ? `<a:alpha val="${Math.round(normalized.alpha * 1e5)}"/>` : "";
|
|
946
|
+
return `<a:srgbClr val="${normalized.color.replace("#", "").toUpperCase()}">${alphaXml}</a:srgbClr>`;
|
|
947
|
+
}
|
|
948
|
+
function buildGradientFillXml(gradient) {
|
|
949
|
+
var _a, _b;
|
|
950
|
+
const stops = (_a = gradient == null ? void 0 : gradient.colors) != null ? _a : [];
|
|
951
|
+
const colors = stops.length ? stops : [
|
|
952
|
+
{ pos: 0, color: "#FFFFFF" },
|
|
953
|
+
{ pos: 100, color: "#FFFFFF" }
|
|
954
|
+
];
|
|
955
|
+
const gsLst = colors.map((stop) => {
|
|
956
|
+
var _a2, _b2;
|
|
957
|
+
const pos = Math.max(0, Math.min(100, (_a2 = stop.pos) != null ? _a2 : 0));
|
|
958
|
+
return `<a:gs pos="${Math.round(pos * 1e3)}">${buildColorXml(
|
|
959
|
+
(_b2 = stop.color) != null ? _b2 : "#FFFFFF"
|
|
960
|
+
)}</a:gs>`;
|
|
961
|
+
}).join("");
|
|
962
|
+
const rotate = Math.round(((_b = gradient == null ? void 0 : gradient.rotate) != null ? _b : 0) * 6e4);
|
|
963
|
+
return `<a:gradFill rotWithShape="1"><a:gsLst>${gsLst}</a:gsLst><a:lin ang="${rotate}" scaled="0"/></a:gradFill>`;
|
|
964
|
+
}
|
|
965
|
+
function buildImageFillXml(relId, opacity) {
|
|
966
|
+
const alphaXml = opacity !== void 0 && Number.isFinite(opacity) && opacity >= 0 && opacity < 1 ? `<a:alphaModFix amt="${Math.round(opacity * 1e5)}"/>` : "";
|
|
967
|
+
return `<a:blipFill><a:blip r:embed="${relId}">${alphaXml}</a:blip><a:srcRect/><a:stretch><a:fillRect/></a:stretch></a:blipFill>`;
|
|
968
|
+
}
|
|
969
|
+
function insertFillXml(spPrInner, fillXml) {
|
|
970
|
+
const cleanedInner = stripFillTags(spPrInner);
|
|
971
|
+
if (cleanedInner.includes("</a:custGeom>")) {
|
|
972
|
+
return cleanedInner.replace("</a:custGeom>", `</a:custGeom>${fillXml}`);
|
|
973
|
+
}
|
|
974
|
+
if (cleanedInner.includes("</a:prstGeom>")) {
|
|
975
|
+
return cleanedInner.replace("</a:prstGeom>", `</a:prstGeom>${fillXml}`);
|
|
976
|
+
}
|
|
977
|
+
const lineIndex = cleanedInner.indexOf("<a:ln");
|
|
978
|
+
if (lineIndex === -1) {
|
|
979
|
+
return `${cleanedInner}${fillXml}`;
|
|
980
|
+
}
|
|
981
|
+
return `${cleanedInner.slice(0, lineIndex)}${fillXml}${cleanedInner.slice(lineIndex)}`;
|
|
982
|
+
}
|
|
983
|
+
function buildFillXml(fill, relId) {
|
|
984
|
+
var _a;
|
|
985
|
+
if (fill.type === "gradient") {
|
|
986
|
+
return buildGradientFillXml(fill.gradient);
|
|
987
|
+
}
|
|
988
|
+
if (fill.type === "image") {
|
|
989
|
+
if (!relId) {
|
|
990
|
+
throw new Error("Image fills require a relationship id");
|
|
991
|
+
}
|
|
992
|
+
return buildImageFillXml(relId, fill.opacity);
|
|
993
|
+
}
|
|
994
|
+
return `<a:solidFill>${buildColorXml((_a = fill.color) != null ? _a : "#FFFFFF")}</a:solidFill>`;
|
|
995
|
+
}
|
|
996
|
+
function fillRequiresXmlPatch(fill) {
|
|
997
|
+
return (fill == null ? void 0 : fill.type) === "gradient" || (fill == null ? void 0 : fill.type) === "image";
|
|
998
|
+
}
|
|
999
|
+
function getFillFallbackColor(fill, fallback = "#FFFFFF") {
|
|
1000
|
+
var _a, _b, _c, _d, _e;
|
|
1001
|
+
if (!fill) return fallback;
|
|
1002
|
+
if (fill.type === "solid") return (_a = fill.color) != null ? _a : fallback;
|
|
1003
|
+
if (fill.type === "gradient") {
|
|
1004
|
+
return (_e = (_d = (_c = (_b = fill.gradient) == null ? void 0 : _b.colors) == null ? void 0 : _c[0]) == null ? void 0 : _d.color) != null ? _e : fallback;
|
|
1005
|
+
}
|
|
1006
|
+
return fallback;
|
|
1007
|
+
}
|
|
1008
|
+
function applyShapeFillPatch(slideXml, objectName, fill, relId) {
|
|
1009
|
+
const nameToken = `name="${objectName}"`;
|
|
1010
|
+
let cursor = 0;
|
|
1011
|
+
let result = slideXml;
|
|
1012
|
+
while (true) {
|
|
1013
|
+
const nameIndex = result.indexOf(nameToken, cursor);
|
|
1014
|
+
if (nameIndex === -1) break;
|
|
1015
|
+
const spStart = result.lastIndexOf("<p:sp", nameIndex);
|
|
1016
|
+
const spEnd = result.indexOf("</p:sp>", nameIndex);
|
|
1017
|
+
if (spStart === -1 || spEnd === -1) break;
|
|
1018
|
+
const spXml = result.slice(spStart, spEnd + "</p:sp>".length);
|
|
1019
|
+
const spPrStart = spXml.indexOf("<p:spPr>");
|
|
1020
|
+
const spPrEnd = spXml.indexOf("</p:spPr>");
|
|
1021
|
+
if (spPrStart === -1 || spPrEnd === -1) {
|
|
1022
|
+
cursor = spEnd + 1;
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
const spPrOpenEnd = spXml.indexOf(">", spPrStart);
|
|
1026
|
+
const spPrInner = spXml.slice(spPrOpenEnd + 1, spPrEnd);
|
|
1027
|
+
const fillXml = buildFillXml(fill, relId);
|
|
1028
|
+
const nextInner = insertFillXml(spPrInner, fillXml);
|
|
1029
|
+
const updatedSpXml = spXml.slice(0, spPrOpenEnd + 1) + nextInner + spXml.slice(spPrEnd);
|
|
1030
|
+
result = result.slice(0, spStart) + updatedSpXml + result.slice(spEnd + "</p:sp>".length);
|
|
1031
|
+
cursor = spStart + updatedSpXml.length;
|
|
1032
|
+
}
|
|
1033
|
+
return result;
|
|
1034
|
+
}
|
|
1035
|
+
function applyBackgroundFillPatch(slideXml, fill, relId) {
|
|
1036
|
+
const bgPrStart = slideXml.indexOf("<p:bgPr>");
|
|
1037
|
+
const bgPrEnd = slideXml.indexOf("</p:bgPr>");
|
|
1038
|
+
if (bgPrStart === -1 || bgPrEnd === -1) return slideXml;
|
|
1039
|
+
const openEnd = slideXml.indexOf(">", bgPrStart);
|
|
1040
|
+
const bgPrInner = slideXml.slice(openEnd + 1, bgPrEnd);
|
|
1041
|
+
const fillXml = buildFillXml(fill, relId);
|
|
1042
|
+
const nextInner = `${stripFillTags(bgPrInner)}${fillXml}`;
|
|
1043
|
+
return slideXml.slice(0, openEnd + 1) + nextInner + slideXml.slice(bgPrEnd);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
942
1046
|
// src/renderers/background.ts
|
|
943
1047
|
function applySlideBackground(slide, slideJson, theme) {
|
|
944
|
-
var _a
|
|
945
|
-
const backgroundColor =
|
|
1048
|
+
var _a;
|
|
1049
|
+
const backgroundColor = slideJson.background ? getFillFallbackColor(slideJson.background, (_a = theme == null ? void 0 : theme.backgroundColor) != null ? _a : "#FFFFFF") : theme == null ? void 0 : theme.backgroundColor;
|
|
946
1050
|
if (!backgroundColor) return;
|
|
947
1051
|
const c = formatColor(backgroundColor);
|
|
948
1052
|
slide.background = { color: c.color, transparency: (1 - c.alpha) * 100 };
|
|
@@ -1127,18 +1231,20 @@ function getLineArrowType(value) {
|
|
|
1127
1231
|
}
|
|
1128
1232
|
|
|
1129
1233
|
// src/renderers/elements.ts
|
|
1130
|
-
function addTextElement(slide, element, template, ratioPx2Pt, ratioPx2Inch, textPadding) {
|
|
1131
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1234
|
+
function addTextElement(slide, element, template, slideIndex, elementIndex, ratioPx2Pt, ratioPx2Inch, textPadding, fillPatches) {
|
|
1235
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1132
1236
|
if (element.type !== "text" || !element.content) return;
|
|
1133
1237
|
const textProps = formatHTML(element.content, ratioPx2Pt);
|
|
1134
1238
|
const opacity = getElementOpacity(element.opacity);
|
|
1239
|
+
const objectName = `text-${slideIndex}-${(_a = element.id) != null ? _a : elementIndex}`;
|
|
1240
|
+
const fill = element.fill;
|
|
1135
1241
|
const options = {
|
|
1136
|
-
x: ((
|
|
1137
|
-
y: ((
|
|
1138
|
-
w: ((
|
|
1139
|
-
h: ((
|
|
1242
|
+
x: ((_b = element.left) != null ? _b : 0) / ratioPx2Inch,
|
|
1243
|
+
y: ((_c = element.top) != null ? _c : 0) / ratioPx2Inch,
|
|
1244
|
+
w: ((_d = element.width) != null ? _d : 0) / ratioPx2Inch,
|
|
1245
|
+
h: ((_e = element.height) != null ? _e : 0) / ratioPx2Inch,
|
|
1140
1246
|
fontSize: DEFAULT_FONT_SIZE / ratioPx2Pt,
|
|
1141
|
-
fontFace: element.defaultFontName || ((
|
|
1247
|
+
fontFace: element.defaultFontName || ((_f = template.theme) == null ? void 0 : _f.fontName) || DEFAULT_FONT_FACE,
|
|
1142
1248
|
color: "#000000",
|
|
1143
1249
|
valign: "top",
|
|
1144
1250
|
// pptxgenjs margin order: [left, right, bottom, top]
|
|
@@ -1146,13 +1252,14 @@ function addTextElement(slide, element, template, ratioPx2Pt, ratioPx2Inch, text
|
|
|
1146
1252
|
margin: [textPadding, textPadding, 0, textPadding * 0.42],
|
|
1147
1253
|
paraSpaceBefore: 0,
|
|
1148
1254
|
lineSpacingMultiple: 1.5,
|
|
1149
|
-
fit: "none"
|
|
1255
|
+
fit: "none",
|
|
1256
|
+
objectName
|
|
1150
1257
|
};
|
|
1151
1258
|
if (element.rotate) options.rotate = element.rotate;
|
|
1152
1259
|
if (element.wordSpace) options.charSpacing = element.wordSpace / ratioPx2Pt;
|
|
1153
1260
|
if (element.lineHeight) options.lineSpacingMultiple = element.lineHeight;
|
|
1154
|
-
if (
|
|
1155
|
-
const c = formatColor(
|
|
1261
|
+
if ((fill == null ? void 0 : fill.type) === "solid" && fill.color) {
|
|
1262
|
+
const c = formatColor(fill.color);
|
|
1156
1263
|
options.fill = {
|
|
1157
1264
|
color: c.color,
|
|
1158
1265
|
transparency: (1 - c.alpha * opacity) * 100
|
|
@@ -1160,9 +1267,17 @@ function addTextElement(slide, element, template, ratioPx2Pt, ratioPx2Inch, text
|
|
|
1160
1267
|
} else {
|
|
1161
1268
|
options.fill = { color: "FFFFFF", transparency: 100 };
|
|
1162
1269
|
}
|
|
1270
|
+
if (fill && fillRequiresXmlPatch(fill)) {
|
|
1271
|
+
fillPatches.push({
|
|
1272
|
+
kind: "shape",
|
|
1273
|
+
slideIndex,
|
|
1274
|
+
objectName,
|
|
1275
|
+
fill
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1163
1278
|
if (element.defaultColor) options.color = formatColor(element.defaultColor).color;
|
|
1164
1279
|
if (element.shadow) options.shadow = getShadowOption(element.shadow, ratioPx2Pt);
|
|
1165
|
-
if ((
|
|
1280
|
+
if ((_g = element.outline) == null ? void 0 : _g.width) {
|
|
1166
1281
|
options.line = getOutlineOption(element.outline, ratioPx2Pt, opacity);
|
|
1167
1282
|
}
|
|
1168
1283
|
if (element.opacity !== void 0) options.transparency = (1 - opacity) * 100;
|
|
@@ -1213,8 +1328,8 @@ async function addImageElement(slide, element, ratioPx2Inch) {
|
|
|
1213
1328
|
}
|
|
1214
1329
|
slide.addImage(options);
|
|
1215
1330
|
}
|
|
1216
|
-
function addShapeElement(slide, element, ratioPx2Pt, ratioPx2Inch, slideIndex, elementIndex,
|
|
1217
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
1331
|
+
function addShapeElement(slide, element, ratioPx2Pt, ratioPx2Inch, slideIndex, elementIndex, fillPatches) {
|
|
1332
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
1218
1333
|
if (element.type !== "shape" || !element.path || !element.viewBox) return;
|
|
1219
1334
|
const scale = {
|
|
1220
1335
|
x: ((_a = element.width) != null ? _a : 0) / element.viewBox[0],
|
|
@@ -1223,23 +1338,19 @@ function addShapeElement(slide, element, ratioPx2Pt, ratioPx2Inch, slideIndex, e
|
|
|
1223
1338
|
const rawPoints = toPoints(element.path);
|
|
1224
1339
|
if (!rawPoints.length) return;
|
|
1225
1340
|
const points = formatPoints(rawPoints, ratioPx2Inch, scale);
|
|
1226
|
-
const
|
|
1227
|
-
const hasFill = typeof element.fill === "string" ? element.fill.trim().length > 0 : false;
|
|
1341
|
+
const fill = element.fill;
|
|
1228
1342
|
const opacity = getElementOpacity(element.opacity);
|
|
1343
|
+
const objectName = `shape-${slideIndex}-${(_c = element.id) != null ? _c : elementIndex}`;
|
|
1229
1344
|
const options = {
|
|
1230
|
-
x: ((
|
|
1231
|
-
y: ((
|
|
1232
|
-
w: ((
|
|
1233
|
-
h: ((
|
|
1234
|
-
points
|
|
1345
|
+
x: ((_d = element.left) != null ? _d : 0) / ratioPx2Inch,
|
|
1346
|
+
y: ((_e = element.top) != null ? _e : 0) / ratioPx2Inch,
|
|
1347
|
+
w: ((_f = element.width) != null ? _f : 0) / ratioPx2Inch,
|
|
1348
|
+
h: ((_g = element.height) != null ? _g : 0) / ratioPx2Inch,
|
|
1349
|
+
points,
|
|
1350
|
+
objectName
|
|
1235
1351
|
};
|
|
1236
|
-
if (
|
|
1237
|
-
const
|
|
1238
|
-
options.objectName = objectName;
|
|
1239
|
-
options.fill = { color: "FFFFFF", transparency: 100 };
|
|
1240
|
-
patternShapes.push({ slideIndex, objectName, dataUrl: pattern });
|
|
1241
|
-
} else if (hasFill) {
|
|
1242
|
-
const fillColor = formatColor(element.fill || "#000000");
|
|
1352
|
+
if ((fill == null ? void 0 : fill.type) === "solid" && fill.color) {
|
|
1353
|
+
const fillColor = formatColor(fill.color);
|
|
1243
1354
|
options.fill = {
|
|
1244
1355
|
color: fillColor.color,
|
|
1245
1356
|
transparency: (1 - fillColor.alpha * opacity) * 100
|
|
@@ -1247,6 +1358,14 @@ function addShapeElement(slide, element, ratioPx2Pt, ratioPx2Inch, slideIndex, e
|
|
|
1247
1358
|
} else {
|
|
1248
1359
|
options.fill = { color: "FFFFFF", transparency: 100 };
|
|
1249
1360
|
}
|
|
1361
|
+
if (fill && fillRequiresXmlPatch(fill)) {
|
|
1362
|
+
fillPatches.push({
|
|
1363
|
+
kind: "shape",
|
|
1364
|
+
slideIndex,
|
|
1365
|
+
objectName,
|
|
1366
|
+
fill
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1250
1369
|
if (element.flipH) options.flipH = element.flipH;
|
|
1251
1370
|
if (element.flipV) options.flipV = element.flipV;
|
|
1252
1371
|
if (element.shadow) options.shadow = getShadowOption(element.shadow, ratioPx2Pt);
|
|
@@ -1266,9 +1385,10 @@ function addShapeElement(slide, element, ratioPx2Pt, ratioPx2Inch, slideIndex, e
|
|
|
1266
1385
|
fontFace: element.text.defaultFontName || DEFAULT_FONT_FACE,
|
|
1267
1386
|
color: "#000000",
|
|
1268
1387
|
paraSpaceBefore: 0,
|
|
1269
|
-
valign: element.text.align,
|
|
1388
|
+
valign: element.text.align === "middle" ? "mid" : element.text.align,
|
|
1270
1389
|
fill: { color: "FFFFFF", transparency: 100 },
|
|
1271
|
-
fit: "none"
|
|
1390
|
+
fit: "none",
|
|
1391
|
+
objectName: `shape-text-${slideIndex}-${(_n = element.id) != null ? _n : elementIndex}`
|
|
1272
1392
|
};
|
|
1273
1393
|
textOptions.margin = 0;
|
|
1274
1394
|
if (element.rotate) textOptions.rotate = element.rotate;
|
|
@@ -1349,49 +1469,9 @@ function addTableElement(slide, element, ratioPx2Pt, ratioPx2Inch) {
|
|
|
1349
1469
|
}
|
|
1350
1470
|
|
|
1351
1471
|
// src/index.ts
|
|
1352
|
-
var ENABLE_DECK_JSON = false;
|
|
1353
|
-
var PPTX_JSON_PAYLOAD_PATH = "json2ppt-editor.json";
|
|
1354
|
-
var PPTX_JSON_PAYLOAD_VERSION = 1;
|
|
1355
1472
|
function sanitizeFileName(name) {
|
|
1356
1473
|
return name.replace(/[\\/:*?"<>|]/g, "").trim() || "presentation";
|
|
1357
1474
|
}
|
|
1358
|
-
function stripFillTags(value) {
|
|
1359
|
-
return value.replace(/<a:solidFill>[\s\S]*?<\/a:solidFill>/g, "").replace(/<a:gradFill>[\s\S]*?<\/a:gradFill>/g, "").replace(/<a:blipFill>[\s\S]*?<\/a:blipFill>/g, "").replace(/<a:noFill\s*\/>/g, "").replace(/<a:noFill><\/a:noFill>/g, "");
|
|
1360
|
-
}
|
|
1361
|
-
function applyPatternFill(slideXml, objectName, relId) {
|
|
1362
|
-
const nameToken = `name="${objectName}"`;
|
|
1363
|
-
let cursor = 0;
|
|
1364
|
-
let result = slideXml;
|
|
1365
|
-
while (true) {
|
|
1366
|
-
const nameIndex = result.indexOf(nameToken, cursor);
|
|
1367
|
-
if (nameIndex === -1) break;
|
|
1368
|
-
const spStart = result.lastIndexOf("<p:sp", nameIndex);
|
|
1369
|
-
const spEnd = result.indexOf("</p:sp>", nameIndex);
|
|
1370
|
-
if (spStart === -1 || spEnd === -1) break;
|
|
1371
|
-
const spXml = result.slice(spStart, spEnd + "</p:sp>".length);
|
|
1372
|
-
const spPrStart = spXml.indexOf("<p:spPr>");
|
|
1373
|
-
const spPrEnd = spXml.indexOf("</p:spPr>");
|
|
1374
|
-
if (spPrStart === -1 || spPrEnd === -1) {
|
|
1375
|
-
cursor = spEnd + 1;
|
|
1376
|
-
continue;
|
|
1377
|
-
}
|
|
1378
|
-
const spPrOpenEnd = spXml.indexOf(">", spPrStart);
|
|
1379
|
-
const spPrInner = spXml.slice(spPrOpenEnd + 1, spPrEnd);
|
|
1380
|
-
const cleanedInner = stripFillTags(spPrInner);
|
|
1381
|
-
const blipFill = `<a:blipFill><a:blip r:embed="${relId}"/><a:srcRect/><a:stretch><a:fillRect/></a:stretch></a:blipFill>`;
|
|
1382
|
-
let nextInner = cleanedInner;
|
|
1383
|
-
if (cleanedInner.includes("</a:custGeom>")) {
|
|
1384
|
-
nextInner = cleanedInner.replace("</a:custGeom>", `</a:custGeom>${blipFill}`);
|
|
1385
|
-
} else {
|
|
1386
|
-
const lnIndex = cleanedInner.indexOf("<a:ln");
|
|
1387
|
-
nextInner = lnIndex === -1 ? `${cleanedInner}${blipFill}` : `${cleanedInner.slice(0, lnIndex)}${blipFill}${cleanedInner.slice(lnIndex)}`;
|
|
1388
|
-
}
|
|
1389
|
-
const updatedSpXml = spXml.slice(0, spPrOpenEnd + 1) + nextInner + spXml.slice(spPrEnd);
|
|
1390
|
-
result = result.slice(0, spStart) + updatedSpXml + result.slice(spEnd + "</p:sp>".length);
|
|
1391
|
-
cursor = spStart + updatedSpXml.length;
|
|
1392
|
-
}
|
|
1393
|
-
return result;
|
|
1394
|
-
}
|
|
1395
1475
|
function parseDataUrlImage(dataUrl) {
|
|
1396
1476
|
var _a;
|
|
1397
1477
|
const match = dataUrl.match(/^data:(image\/[^;]+);base64,(.+)$/);
|
|
@@ -1414,7 +1494,7 @@ async function createPPTX(template) {
|
|
|
1414
1494
|
const parsedTemplate = (0, import_json2pptx_schema.parseDocument)(template);
|
|
1415
1495
|
const renderTemplate = parsedTemplate;
|
|
1416
1496
|
const pptx = new import_pptxgenjs.default();
|
|
1417
|
-
const
|
|
1497
|
+
const fillPatches = [];
|
|
1418
1498
|
const width = parsedTemplate.width;
|
|
1419
1499
|
const height = parsedTemplate.height;
|
|
1420
1500
|
const ratioPx2Inch = 96 * (width / 960);
|
|
@@ -1424,8 +1504,25 @@ async function createPPTX(template) {
|
|
|
1424
1504
|
for (const [slideIndex, slideJson] of ((_a = renderTemplate.slides) != null ? _a : []).entries()) {
|
|
1425
1505
|
const slide = pptx.addSlide();
|
|
1426
1506
|
applySlideBackground(slide, slideJson, renderTemplate.theme);
|
|
1507
|
+
if (slideJson.background && fillRequiresXmlPatch(slideJson.background)) {
|
|
1508
|
+
fillPatches.push({
|
|
1509
|
+
kind: "background",
|
|
1510
|
+
slideIndex,
|
|
1511
|
+
fill: slideJson.background
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1427
1514
|
for (const [elementIndex, element] of ((_b = slideJson.elements) != null ? _b : []).entries()) {
|
|
1428
|
-
addTextElement(
|
|
1515
|
+
addTextElement(
|
|
1516
|
+
slide,
|
|
1517
|
+
element,
|
|
1518
|
+
renderTemplate,
|
|
1519
|
+
slideIndex,
|
|
1520
|
+
elementIndex,
|
|
1521
|
+
ratioPx2Pt,
|
|
1522
|
+
ratioPx2Inch,
|
|
1523
|
+
textPadding,
|
|
1524
|
+
fillPatches
|
|
1525
|
+
);
|
|
1429
1526
|
await addImageElement(slide, element, ratioPx2Inch);
|
|
1430
1527
|
addShapeElement(
|
|
1431
1528
|
slide,
|
|
@@ -1434,7 +1531,7 @@ async function createPPTX(template) {
|
|
|
1434
1531
|
ratioPx2Inch,
|
|
1435
1532
|
slideIndex,
|
|
1436
1533
|
elementIndex,
|
|
1437
|
-
|
|
1534
|
+
fillPatches
|
|
1438
1535
|
);
|
|
1439
1536
|
addLineElement(slide, element, ratioPx2Pt, ratioPx2Inch);
|
|
1440
1537
|
addTableElement(slide, element, ratioPx2Pt, ratioPx2Inch);
|
|
@@ -1445,32 +1542,33 @@ async function createPPTX(template) {
|
|
|
1445
1542
|
outputType: "arraybuffer",
|
|
1446
1543
|
compression: true
|
|
1447
1544
|
});
|
|
1448
|
-
|
|
1449
|
-
if (!needsZip) {
|
|
1545
|
+
if (!fillPatches.length) {
|
|
1450
1546
|
return { blob: new Blob([pptxBuffer]), fileName };
|
|
1451
1547
|
}
|
|
1452
1548
|
const zip = await import_jszip.default.loadAsync(pptxBuffer);
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
if (Number.isFinite(index)) maxImageIndex = Math.max(maxImageIndex, index);
|
|
1461
|
-
}
|
|
1549
|
+
const mediaFiles = Object.keys(zip.files).filter((name) => name.startsWith("ppt/media/"));
|
|
1550
|
+
let maxImageIndex = 0;
|
|
1551
|
+
for (const name of mediaFiles) {
|
|
1552
|
+
const match = name.match(/ppt\/media\/image(\d+)/);
|
|
1553
|
+
if (match) {
|
|
1554
|
+
const index = Number.parseInt(match[1], 10);
|
|
1555
|
+
if (Number.isFinite(index)) maxImageIndex = Math.max(maxImageIndex, index);
|
|
1462
1556
|
}
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1557
|
+
}
|
|
1558
|
+
const slideCache = /* @__PURE__ */ new Map();
|
|
1559
|
+
const relsCache = /* @__PURE__ */ new Map();
|
|
1560
|
+
for (const patch of fillPatches) {
|
|
1561
|
+
const slideNumber = patch.slideIndex + 1;
|
|
1562
|
+
const slidePath = `ppt/slides/slide${slideNumber}.xml`;
|
|
1563
|
+
const relsPath = `ppt/slides/_rels/slide${slideNumber}.xml.rels`;
|
|
1564
|
+
let relId;
|
|
1565
|
+
if (patch.fill.type === "image" && patch.fill.src) {
|
|
1566
|
+
const dataUrl = await resolveImageData(patch.fill.src);
|
|
1567
|
+
const parsed = parseDataUrlImage(dataUrl);
|
|
1467
1568
|
if (!parsed) continue;
|
|
1468
1569
|
maxImageIndex += 1;
|
|
1469
1570
|
const imageName = `image${maxImageIndex}.${parsed.ext}`;
|
|
1470
1571
|
zip.file(`ppt/media/${imageName}`, parsed.data, { base64: true });
|
|
1471
|
-
const slideNumber = pattern.slideIndex + 1;
|
|
1472
|
-
const slidePath = `ppt/slides/slide${slideNumber}.xml`;
|
|
1473
|
-
const relsPath = `ppt/slides/_rels/slide${slideNumber}.xml.rels`;
|
|
1474
1572
|
const relsXml = (_c = relsCache.get(slideNumber)) != null ? _c : zip.file(relsPath) ? await zip.file(relsPath).async("string") : `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>`;
|
|
1475
1573
|
let maxRelId = 0;
|
|
1476
1574
|
relsXml.replace(/Id="rId(\d+)"/g, (_, id) => {
|
|
@@ -1478,23 +1576,22 @@ async function createPPTX(template) {
|
|
|
1478
1576
|
if (Number.isFinite(value)) maxRelId = Math.max(maxRelId, value);
|
|
1479
1577
|
return "";
|
|
1480
1578
|
});
|
|
1481
|
-
|
|
1579
|
+
relId = `rId${maxRelId + 1}`;
|
|
1482
1580
|
const relEntry = `<Relationship Id="${relId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="../media/${imageName}"/>`;
|
|
1483
|
-
|
|
1484
|
-
"</Relationships>",
|
|
1485
|
-
`${relEntry}</Relationships>`
|
|
1486
|
-
);
|
|
1487
|
-
relsCache.set(slideNumber, nextRelsXml);
|
|
1488
|
-
const slideXml = (_d = slideCache.get(slideNumber)) != null ? _d : zip.file(slidePath) ? await zip.file(slidePath).async("string") : "";
|
|
1489
|
-
const nextSlideXml = slideXml ? applyPatternFill(slideXml, pattern.objectName, relId) : slideXml;
|
|
1490
|
-
slideCache.set(slideNumber, nextSlideXml);
|
|
1491
|
-
}
|
|
1492
|
-
for (const [slideNumber, xml] of slideCache.entries()) {
|
|
1493
|
-
zip.file(`ppt/slides/slide${slideNumber}.xml`, xml);
|
|
1581
|
+
relsCache.set(slideNumber, relsXml.replace("</Relationships>", `${relEntry}</Relationships>`));
|
|
1494
1582
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1583
|
+
const slideXml = (_d = slideCache.get(slideNumber)) != null ? _d : zip.file(slidePath) ? await zip.file(slidePath).async("string") : "";
|
|
1584
|
+
if (!slideXml) {
|
|
1585
|
+
continue;
|
|
1497
1586
|
}
|
|
1587
|
+
const nextSlideXml = patch.kind === "background" ? applyBackgroundFillPatch(slideXml, patch.fill, relId) : applyShapeFillPatch(slideXml, patch.objectName, patch.fill, relId);
|
|
1588
|
+
slideCache.set(slideNumber, nextSlideXml);
|
|
1589
|
+
}
|
|
1590
|
+
for (const [slideNumber, xml] of slideCache.entries()) {
|
|
1591
|
+
zip.file(`ppt/slides/slide${slideNumber}.xml`, xml);
|
|
1592
|
+
}
|
|
1593
|
+
for (const [slideNumber, xml] of relsCache.entries()) {
|
|
1594
|
+
zip.file(`ppt/slides/_rels/slide${slideNumber}.xml.rels`, xml);
|
|
1498
1595
|
}
|
|
1499
1596
|
const blob = await zip.generateAsync({ type: "blob" });
|
|
1500
1597
|
return { blob, fileName };
|
|
@@ -1502,9 +1599,6 @@ async function createPPTX(template) {
|
|
|
1502
1599
|
var buildPptxBlob = createPPTX;
|
|
1503
1600
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1504
1601
|
0 && (module.exports = {
|
|
1505
|
-
ENABLE_DECK_JSON,
|
|
1506
|
-
PPTX_JSON_PAYLOAD_PATH,
|
|
1507
|
-
PPTX_JSON_PAYLOAD_VERSION,
|
|
1508
1602
|
buildPptxBlob,
|
|
1509
1603
|
createPPTX,
|
|
1510
1604
|
getElementRange,
|