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.mjs
CHANGED
|
@@ -898,10 +898,117 @@ function getDashTypeMap() {
|
|
|
898
898
|
return dashTypeMap;
|
|
899
899
|
}
|
|
900
900
|
|
|
901
|
+
// src/renderers/fill-patch.ts
|
|
902
|
+
function stripFillTags(value) {
|
|
903
|
+
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, "");
|
|
904
|
+
}
|
|
905
|
+
function buildColorXml(color) {
|
|
906
|
+
const normalized = formatColor(color);
|
|
907
|
+
const alphaXml = normalized.alpha < 1 ? `<a:alpha val="${Math.round(normalized.alpha * 1e5)}"/>` : "";
|
|
908
|
+
return `<a:srgbClr val="${normalized.color.replace("#", "").toUpperCase()}">${alphaXml}</a:srgbClr>`;
|
|
909
|
+
}
|
|
910
|
+
function buildGradientFillXml(gradient) {
|
|
911
|
+
var _a, _b;
|
|
912
|
+
const stops = (_a = gradient == null ? void 0 : gradient.colors) != null ? _a : [];
|
|
913
|
+
const colors = stops.length ? stops : [
|
|
914
|
+
{ pos: 0, color: "#FFFFFF" },
|
|
915
|
+
{ pos: 100, color: "#FFFFFF" }
|
|
916
|
+
];
|
|
917
|
+
const gsLst = colors.map((stop) => {
|
|
918
|
+
var _a2, _b2;
|
|
919
|
+
const pos = Math.max(0, Math.min(100, (_a2 = stop.pos) != null ? _a2 : 0));
|
|
920
|
+
return `<a:gs pos="${Math.round(pos * 1e3)}">${buildColorXml(
|
|
921
|
+
(_b2 = stop.color) != null ? _b2 : "#FFFFFF"
|
|
922
|
+
)}</a:gs>`;
|
|
923
|
+
}).join("");
|
|
924
|
+
const rotate = Math.round(((_b = gradient == null ? void 0 : gradient.rotate) != null ? _b : 0) * 6e4);
|
|
925
|
+
return `<a:gradFill rotWithShape="1"><a:gsLst>${gsLst}</a:gsLst><a:lin ang="${rotate}" scaled="0"/></a:gradFill>`;
|
|
926
|
+
}
|
|
927
|
+
function buildImageFillXml(relId, opacity) {
|
|
928
|
+
const alphaXml = opacity !== void 0 && Number.isFinite(opacity) && opacity >= 0 && opacity < 1 ? `<a:alphaModFix amt="${Math.round(opacity * 1e5)}"/>` : "";
|
|
929
|
+
return `<a:blipFill><a:blip r:embed="${relId}">${alphaXml}</a:blip><a:srcRect/><a:stretch><a:fillRect/></a:stretch></a:blipFill>`;
|
|
930
|
+
}
|
|
931
|
+
function insertFillXml(spPrInner, fillXml) {
|
|
932
|
+
const cleanedInner = stripFillTags(spPrInner);
|
|
933
|
+
if (cleanedInner.includes("</a:custGeom>")) {
|
|
934
|
+
return cleanedInner.replace("</a:custGeom>", `</a:custGeom>${fillXml}`);
|
|
935
|
+
}
|
|
936
|
+
if (cleanedInner.includes("</a:prstGeom>")) {
|
|
937
|
+
return cleanedInner.replace("</a:prstGeom>", `</a:prstGeom>${fillXml}`);
|
|
938
|
+
}
|
|
939
|
+
const lineIndex = cleanedInner.indexOf("<a:ln");
|
|
940
|
+
if (lineIndex === -1) {
|
|
941
|
+
return `${cleanedInner}${fillXml}`;
|
|
942
|
+
}
|
|
943
|
+
return `${cleanedInner.slice(0, lineIndex)}${fillXml}${cleanedInner.slice(lineIndex)}`;
|
|
944
|
+
}
|
|
945
|
+
function buildFillXml(fill, relId) {
|
|
946
|
+
var _a;
|
|
947
|
+
if (fill.type === "gradient") {
|
|
948
|
+
return buildGradientFillXml(fill.gradient);
|
|
949
|
+
}
|
|
950
|
+
if (fill.type === "image") {
|
|
951
|
+
if (!relId) {
|
|
952
|
+
throw new Error("Image fills require a relationship id");
|
|
953
|
+
}
|
|
954
|
+
return buildImageFillXml(relId, fill.opacity);
|
|
955
|
+
}
|
|
956
|
+
return `<a:solidFill>${buildColorXml((_a = fill.color) != null ? _a : "#FFFFFF")}</a:solidFill>`;
|
|
957
|
+
}
|
|
958
|
+
function fillRequiresXmlPatch(fill) {
|
|
959
|
+
return (fill == null ? void 0 : fill.type) === "gradient" || (fill == null ? void 0 : fill.type) === "image";
|
|
960
|
+
}
|
|
961
|
+
function getFillFallbackColor(fill, fallback = "#FFFFFF") {
|
|
962
|
+
var _a, _b, _c, _d, _e;
|
|
963
|
+
if (!fill) return fallback;
|
|
964
|
+
if (fill.type === "solid") return (_a = fill.color) != null ? _a : fallback;
|
|
965
|
+
if (fill.type === "gradient") {
|
|
966
|
+
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;
|
|
967
|
+
}
|
|
968
|
+
return fallback;
|
|
969
|
+
}
|
|
970
|
+
function applyShapeFillPatch(slideXml, objectName, fill, relId) {
|
|
971
|
+
const nameToken = `name="${objectName}"`;
|
|
972
|
+
let cursor = 0;
|
|
973
|
+
let result = slideXml;
|
|
974
|
+
while (true) {
|
|
975
|
+
const nameIndex = result.indexOf(nameToken, cursor);
|
|
976
|
+
if (nameIndex === -1) break;
|
|
977
|
+
const spStart = result.lastIndexOf("<p:sp", nameIndex);
|
|
978
|
+
const spEnd = result.indexOf("</p:sp>", nameIndex);
|
|
979
|
+
if (spStart === -1 || spEnd === -1) break;
|
|
980
|
+
const spXml = result.slice(spStart, spEnd + "</p:sp>".length);
|
|
981
|
+
const spPrStart = spXml.indexOf("<p:spPr>");
|
|
982
|
+
const spPrEnd = spXml.indexOf("</p:spPr>");
|
|
983
|
+
if (spPrStart === -1 || spPrEnd === -1) {
|
|
984
|
+
cursor = spEnd + 1;
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
const spPrOpenEnd = spXml.indexOf(">", spPrStart);
|
|
988
|
+
const spPrInner = spXml.slice(spPrOpenEnd + 1, spPrEnd);
|
|
989
|
+
const fillXml = buildFillXml(fill, relId);
|
|
990
|
+
const nextInner = insertFillXml(spPrInner, fillXml);
|
|
991
|
+
const updatedSpXml = spXml.slice(0, spPrOpenEnd + 1) + nextInner + spXml.slice(spPrEnd);
|
|
992
|
+
result = result.slice(0, spStart) + updatedSpXml + result.slice(spEnd + "</p:sp>".length);
|
|
993
|
+
cursor = spStart + updatedSpXml.length;
|
|
994
|
+
}
|
|
995
|
+
return result;
|
|
996
|
+
}
|
|
997
|
+
function applyBackgroundFillPatch(slideXml, fill, relId) {
|
|
998
|
+
const bgPrStart = slideXml.indexOf("<p:bgPr>");
|
|
999
|
+
const bgPrEnd = slideXml.indexOf("</p:bgPr>");
|
|
1000
|
+
if (bgPrStart === -1 || bgPrEnd === -1) return slideXml;
|
|
1001
|
+
const openEnd = slideXml.indexOf(">", bgPrStart);
|
|
1002
|
+
const bgPrInner = slideXml.slice(openEnd + 1, bgPrEnd);
|
|
1003
|
+
const fillXml = buildFillXml(fill, relId);
|
|
1004
|
+
const nextInner = `${stripFillTags(bgPrInner)}${fillXml}`;
|
|
1005
|
+
return slideXml.slice(0, openEnd + 1) + nextInner + slideXml.slice(bgPrEnd);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
901
1008
|
// src/renderers/background.ts
|
|
902
1009
|
function applySlideBackground(slide, slideJson, theme) {
|
|
903
|
-
var _a
|
|
904
|
-
const backgroundColor =
|
|
1010
|
+
var _a;
|
|
1011
|
+
const backgroundColor = slideJson.background ? getFillFallbackColor(slideJson.background, (_a = theme == null ? void 0 : theme.backgroundColor) != null ? _a : "#FFFFFF") : theme == null ? void 0 : theme.backgroundColor;
|
|
905
1012
|
if (!backgroundColor) return;
|
|
906
1013
|
const c = formatColor(backgroundColor);
|
|
907
1014
|
slide.background = { color: c.color, transparency: (1 - c.alpha) * 100 };
|
|
@@ -1086,18 +1193,20 @@ function getLineArrowType(value) {
|
|
|
1086
1193
|
}
|
|
1087
1194
|
|
|
1088
1195
|
// src/renderers/elements.ts
|
|
1089
|
-
function addTextElement(slide, element, template, ratioPx2Pt, ratioPx2Inch, textPadding) {
|
|
1090
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1196
|
+
function addTextElement(slide, element, template, slideIndex, elementIndex, ratioPx2Pt, ratioPx2Inch, textPadding, fillPatches) {
|
|
1197
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1091
1198
|
if (element.type !== "text" || !element.content) return;
|
|
1092
1199
|
const textProps = formatHTML(element.content, ratioPx2Pt);
|
|
1093
1200
|
const opacity = getElementOpacity(element.opacity);
|
|
1201
|
+
const objectName = `text-${slideIndex}-${(_a = element.id) != null ? _a : elementIndex}`;
|
|
1202
|
+
const fill = element.fill;
|
|
1094
1203
|
const options = {
|
|
1095
|
-
x: ((
|
|
1096
|
-
y: ((
|
|
1097
|
-
w: ((
|
|
1098
|
-
h: ((
|
|
1204
|
+
x: ((_b = element.left) != null ? _b : 0) / ratioPx2Inch,
|
|
1205
|
+
y: ((_c = element.top) != null ? _c : 0) / ratioPx2Inch,
|
|
1206
|
+
w: ((_d = element.width) != null ? _d : 0) / ratioPx2Inch,
|
|
1207
|
+
h: ((_e = element.height) != null ? _e : 0) / ratioPx2Inch,
|
|
1099
1208
|
fontSize: DEFAULT_FONT_SIZE / ratioPx2Pt,
|
|
1100
|
-
fontFace: element.defaultFontName || ((
|
|
1209
|
+
fontFace: element.defaultFontName || ((_f = template.theme) == null ? void 0 : _f.fontName) || DEFAULT_FONT_FACE,
|
|
1101
1210
|
color: "#000000",
|
|
1102
1211
|
valign: "top",
|
|
1103
1212
|
// pptxgenjs margin order: [left, right, bottom, top]
|
|
@@ -1105,13 +1214,14 @@ function addTextElement(slide, element, template, ratioPx2Pt, ratioPx2Inch, text
|
|
|
1105
1214
|
margin: [textPadding, textPadding, 0, textPadding * 0.42],
|
|
1106
1215
|
paraSpaceBefore: 0,
|
|
1107
1216
|
lineSpacingMultiple: 1.5,
|
|
1108
|
-
fit: "none"
|
|
1217
|
+
fit: "none",
|
|
1218
|
+
objectName
|
|
1109
1219
|
};
|
|
1110
1220
|
if (element.rotate) options.rotate = element.rotate;
|
|
1111
1221
|
if (element.wordSpace) options.charSpacing = element.wordSpace / ratioPx2Pt;
|
|
1112
1222
|
if (element.lineHeight) options.lineSpacingMultiple = element.lineHeight;
|
|
1113
|
-
if (
|
|
1114
|
-
const c = formatColor(
|
|
1223
|
+
if ((fill == null ? void 0 : fill.type) === "solid" && fill.color) {
|
|
1224
|
+
const c = formatColor(fill.color);
|
|
1115
1225
|
options.fill = {
|
|
1116
1226
|
color: c.color,
|
|
1117
1227
|
transparency: (1 - c.alpha * opacity) * 100
|
|
@@ -1119,9 +1229,17 @@ function addTextElement(slide, element, template, ratioPx2Pt, ratioPx2Inch, text
|
|
|
1119
1229
|
} else {
|
|
1120
1230
|
options.fill = { color: "FFFFFF", transparency: 100 };
|
|
1121
1231
|
}
|
|
1232
|
+
if (fill && fillRequiresXmlPatch(fill)) {
|
|
1233
|
+
fillPatches.push({
|
|
1234
|
+
kind: "shape",
|
|
1235
|
+
slideIndex,
|
|
1236
|
+
objectName,
|
|
1237
|
+
fill
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1122
1240
|
if (element.defaultColor) options.color = formatColor(element.defaultColor).color;
|
|
1123
1241
|
if (element.shadow) options.shadow = getShadowOption(element.shadow, ratioPx2Pt);
|
|
1124
|
-
if ((
|
|
1242
|
+
if ((_g = element.outline) == null ? void 0 : _g.width) {
|
|
1125
1243
|
options.line = getOutlineOption(element.outline, ratioPx2Pt, opacity);
|
|
1126
1244
|
}
|
|
1127
1245
|
if (element.opacity !== void 0) options.transparency = (1 - opacity) * 100;
|
|
@@ -1172,8 +1290,8 @@ async function addImageElement(slide, element, ratioPx2Inch) {
|
|
|
1172
1290
|
}
|
|
1173
1291
|
slide.addImage(options);
|
|
1174
1292
|
}
|
|
1175
|
-
function addShapeElement(slide, element, ratioPx2Pt, ratioPx2Inch, slideIndex, elementIndex,
|
|
1176
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
1293
|
+
function addShapeElement(slide, element, ratioPx2Pt, ratioPx2Inch, slideIndex, elementIndex, fillPatches) {
|
|
1294
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
1177
1295
|
if (element.type !== "shape" || !element.path || !element.viewBox) return;
|
|
1178
1296
|
const scale = {
|
|
1179
1297
|
x: ((_a = element.width) != null ? _a : 0) / element.viewBox[0],
|
|
@@ -1182,23 +1300,19 @@ function addShapeElement(slide, element, ratioPx2Pt, ratioPx2Inch, slideIndex, e
|
|
|
1182
1300
|
const rawPoints = toPoints(element.path);
|
|
1183
1301
|
if (!rawPoints.length) return;
|
|
1184
1302
|
const points = formatPoints(rawPoints, ratioPx2Inch, scale);
|
|
1185
|
-
const
|
|
1186
|
-
const hasFill = typeof element.fill === "string" ? element.fill.trim().length > 0 : false;
|
|
1303
|
+
const fill = element.fill;
|
|
1187
1304
|
const opacity = getElementOpacity(element.opacity);
|
|
1305
|
+
const objectName = `shape-${slideIndex}-${(_c = element.id) != null ? _c : elementIndex}`;
|
|
1188
1306
|
const options = {
|
|
1189
|
-
x: ((
|
|
1190
|
-
y: ((
|
|
1191
|
-
w: ((
|
|
1192
|
-
h: ((
|
|
1193
|
-
points
|
|
1307
|
+
x: ((_d = element.left) != null ? _d : 0) / ratioPx2Inch,
|
|
1308
|
+
y: ((_e = element.top) != null ? _e : 0) / ratioPx2Inch,
|
|
1309
|
+
w: ((_f = element.width) != null ? _f : 0) / ratioPx2Inch,
|
|
1310
|
+
h: ((_g = element.height) != null ? _g : 0) / ratioPx2Inch,
|
|
1311
|
+
points,
|
|
1312
|
+
objectName
|
|
1194
1313
|
};
|
|
1195
|
-
if (
|
|
1196
|
-
const
|
|
1197
|
-
options.objectName = objectName;
|
|
1198
|
-
options.fill = { color: "FFFFFF", transparency: 100 };
|
|
1199
|
-
patternShapes.push({ slideIndex, objectName, dataUrl: pattern });
|
|
1200
|
-
} else if (hasFill) {
|
|
1201
|
-
const fillColor = formatColor(element.fill || "#000000");
|
|
1314
|
+
if ((fill == null ? void 0 : fill.type) === "solid" && fill.color) {
|
|
1315
|
+
const fillColor = formatColor(fill.color);
|
|
1202
1316
|
options.fill = {
|
|
1203
1317
|
color: fillColor.color,
|
|
1204
1318
|
transparency: (1 - fillColor.alpha * opacity) * 100
|
|
@@ -1206,6 +1320,14 @@ function addShapeElement(slide, element, ratioPx2Pt, ratioPx2Inch, slideIndex, e
|
|
|
1206
1320
|
} else {
|
|
1207
1321
|
options.fill = { color: "FFFFFF", transparency: 100 };
|
|
1208
1322
|
}
|
|
1323
|
+
if (fill && fillRequiresXmlPatch(fill)) {
|
|
1324
|
+
fillPatches.push({
|
|
1325
|
+
kind: "shape",
|
|
1326
|
+
slideIndex,
|
|
1327
|
+
objectName,
|
|
1328
|
+
fill
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1209
1331
|
if (element.flipH) options.flipH = element.flipH;
|
|
1210
1332
|
if (element.flipV) options.flipV = element.flipV;
|
|
1211
1333
|
if (element.shadow) options.shadow = getShadowOption(element.shadow, ratioPx2Pt);
|
|
@@ -1225,9 +1347,10 @@ function addShapeElement(slide, element, ratioPx2Pt, ratioPx2Inch, slideIndex, e
|
|
|
1225
1347
|
fontFace: element.text.defaultFontName || DEFAULT_FONT_FACE,
|
|
1226
1348
|
color: "#000000",
|
|
1227
1349
|
paraSpaceBefore: 0,
|
|
1228
|
-
valign: element.text.align,
|
|
1350
|
+
valign: element.text.align === "middle" ? "mid" : element.text.align,
|
|
1229
1351
|
fill: { color: "FFFFFF", transparency: 100 },
|
|
1230
|
-
fit: "none"
|
|
1352
|
+
fit: "none",
|
|
1353
|
+
objectName: `shape-text-${slideIndex}-${(_n = element.id) != null ? _n : elementIndex}`
|
|
1231
1354
|
};
|
|
1232
1355
|
textOptions.margin = 0;
|
|
1233
1356
|
if (element.rotate) textOptions.rotate = element.rotate;
|
|
@@ -1308,49 +1431,9 @@ function addTableElement(slide, element, ratioPx2Pt, ratioPx2Inch) {
|
|
|
1308
1431
|
}
|
|
1309
1432
|
|
|
1310
1433
|
// src/index.ts
|
|
1311
|
-
var ENABLE_DECK_JSON = false;
|
|
1312
|
-
var PPTX_JSON_PAYLOAD_PATH = "json2ppt-editor.json";
|
|
1313
|
-
var PPTX_JSON_PAYLOAD_VERSION = 1;
|
|
1314
1434
|
function sanitizeFileName(name) {
|
|
1315
1435
|
return name.replace(/[\\/:*?"<>|]/g, "").trim() || "presentation";
|
|
1316
1436
|
}
|
|
1317
|
-
function stripFillTags(value) {
|
|
1318
|
-
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, "");
|
|
1319
|
-
}
|
|
1320
|
-
function applyPatternFill(slideXml, objectName, relId) {
|
|
1321
|
-
const nameToken = `name="${objectName}"`;
|
|
1322
|
-
let cursor = 0;
|
|
1323
|
-
let result = slideXml;
|
|
1324
|
-
while (true) {
|
|
1325
|
-
const nameIndex = result.indexOf(nameToken, cursor);
|
|
1326
|
-
if (nameIndex === -1) break;
|
|
1327
|
-
const spStart = result.lastIndexOf("<p:sp", nameIndex);
|
|
1328
|
-
const spEnd = result.indexOf("</p:sp>", nameIndex);
|
|
1329
|
-
if (spStart === -1 || spEnd === -1) break;
|
|
1330
|
-
const spXml = result.slice(spStart, spEnd + "</p:sp>".length);
|
|
1331
|
-
const spPrStart = spXml.indexOf("<p:spPr>");
|
|
1332
|
-
const spPrEnd = spXml.indexOf("</p:spPr>");
|
|
1333
|
-
if (spPrStart === -1 || spPrEnd === -1) {
|
|
1334
|
-
cursor = spEnd + 1;
|
|
1335
|
-
continue;
|
|
1336
|
-
}
|
|
1337
|
-
const spPrOpenEnd = spXml.indexOf(">", spPrStart);
|
|
1338
|
-
const spPrInner = spXml.slice(spPrOpenEnd + 1, spPrEnd);
|
|
1339
|
-
const cleanedInner = stripFillTags(spPrInner);
|
|
1340
|
-
const blipFill = `<a:blipFill><a:blip r:embed="${relId}"/><a:srcRect/><a:stretch><a:fillRect/></a:stretch></a:blipFill>`;
|
|
1341
|
-
let nextInner = cleanedInner;
|
|
1342
|
-
if (cleanedInner.includes("</a:custGeom>")) {
|
|
1343
|
-
nextInner = cleanedInner.replace("</a:custGeom>", `</a:custGeom>${blipFill}`);
|
|
1344
|
-
} else {
|
|
1345
|
-
const lnIndex = cleanedInner.indexOf("<a:ln");
|
|
1346
|
-
nextInner = lnIndex === -1 ? `${cleanedInner}${blipFill}` : `${cleanedInner.slice(0, lnIndex)}${blipFill}${cleanedInner.slice(lnIndex)}`;
|
|
1347
|
-
}
|
|
1348
|
-
const updatedSpXml = spXml.slice(0, spPrOpenEnd + 1) + nextInner + spXml.slice(spPrEnd);
|
|
1349
|
-
result = result.slice(0, spStart) + updatedSpXml + result.slice(spEnd + "</p:sp>".length);
|
|
1350
|
-
cursor = spStart + updatedSpXml.length;
|
|
1351
|
-
}
|
|
1352
|
-
return result;
|
|
1353
|
-
}
|
|
1354
1437
|
function parseDataUrlImage(dataUrl) {
|
|
1355
1438
|
var _a;
|
|
1356
1439
|
const match = dataUrl.match(/^data:(image\/[^;]+);base64,(.+)$/);
|
|
@@ -1373,7 +1456,7 @@ async function createPPTX(template) {
|
|
|
1373
1456
|
const parsedTemplate = parseDocument(template);
|
|
1374
1457
|
const renderTemplate = parsedTemplate;
|
|
1375
1458
|
const pptx = new PptxGenJS();
|
|
1376
|
-
const
|
|
1459
|
+
const fillPatches = [];
|
|
1377
1460
|
const width = parsedTemplate.width;
|
|
1378
1461
|
const height = parsedTemplate.height;
|
|
1379
1462
|
const ratioPx2Inch = 96 * (width / 960);
|
|
@@ -1383,8 +1466,25 @@ async function createPPTX(template) {
|
|
|
1383
1466
|
for (const [slideIndex, slideJson] of ((_a = renderTemplate.slides) != null ? _a : []).entries()) {
|
|
1384
1467
|
const slide = pptx.addSlide();
|
|
1385
1468
|
applySlideBackground(slide, slideJson, renderTemplate.theme);
|
|
1469
|
+
if (slideJson.background && fillRequiresXmlPatch(slideJson.background)) {
|
|
1470
|
+
fillPatches.push({
|
|
1471
|
+
kind: "background",
|
|
1472
|
+
slideIndex,
|
|
1473
|
+
fill: slideJson.background
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1386
1476
|
for (const [elementIndex, element] of ((_b = slideJson.elements) != null ? _b : []).entries()) {
|
|
1387
|
-
addTextElement(
|
|
1477
|
+
addTextElement(
|
|
1478
|
+
slide,
|
|
1479
|
+
element,
|
|
1480
|
+
renderTemplate,
|
|
1481
|
+
slideIndex,
|
|
1482
|
+
elementIndex,
|
|
1483
|
+
ratioPx2Pt,
|
|
1484
|
+
ratioPx2Inch,
|
|
1485
|
+
textPadding,
|
|
1486
|
+
fillPatches
|
|
1487
|
+
);
|
|
1388
1488
|
await addImageElement(slide, element, ratioPx2Inch);
|
|
1389
1489
|
addShapeElement(
|
|
1390
1490
|
slide,
|
|
@@ -1393,7 +1493,7 @@ async function createPPTX(template) {
|
|
|
1393
1493
|
ratioPx2Inch,
|
|
1394
1494
|
slideIndex,
|
|
1395
1495
|
elementIndex,
|
|
1396
|
-
|
|
1496
|
+
fillPatches
|
|
1397
1497
|
);
|
|
1398
1498
|
addLineElement(slide, element, ratioPx2Pt, ratioPx2Inch);
|
|
1399
1499
|
addTableElement(slide, element, ratioPx2Pt, ratioPx2Inch);
|
|
@@ -1404,32 +1504,33 @@ async function createPPTX(template) {
|
|
|
1404
1504
|
outputType: "arraybuffer",
|
|
1405
1505
|
compression: true
|
|
1406
1506
|
});
|
|
1407
|
-
|
|
1408
|
-
if (!needsZip) {
|
|
1507
|
+
if (!fillPatches.length) {
|
|
1409
1508
|
return { blob: new Blob([pptxBuffer]), fileName };
|
|
1410
1509
|
}
|
|
1411
1510
|
const zip = await JSZip.loadAsync(pptxBuffer);
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
if (Number.isFinite(index)) maxImageIndex = Math.max(maxImageIndex, index);
|
|
1420
|
-
}
|
|
1511
|
+
const mediaFiles = Object.keys(zip.files).filter((name) => name.startsWith("ppt/media/"));
|
|
1512
|
+
let maxImageIndex = 0;
|
|
1513
|
+
for (const name of mediaFiles) {
|
|
1514
|
+
const match = name.match(/ppt\/media\/image(\d+)/);
|
|
1515
|
+
if (match) {
|
|
1516
|
+
const index = Number.parseInt(match[1], 10);
|
|
1517
|
+
if (Number.isFinite(index)) maxImageIndex = Math.max(maxImageIndex, index);
|
|
1421
1518
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1519
|
+
}
|
|
1520
|
+
const slideCache = /* @__PURE__ */ new Map();
|
|
1521
|
+
const relsCache = /* @__PURE__ */ new Map();
|
|
1522
|
+
for (const patch of fillPatches) {
|
|
1523
|
+
const slideNumber = patch.slideIndex + 1;
|
|
1524
|
+
const slidePath = `ppt/slides/slide${slideNumber}.xml`;
|
|
1525
|
+
const relsPath = `ppt/slides/_rels/slide${slideNumber}.xml.rels`;
|
|
1526
|
+
let relId;
|
|
1527
|
+
if (patch.fill.type === "image" && patch.fill.src) {
|
|
1528
|
+
const dataUrl = await resolveImageData(patch.fill.src);
|
|
1529
|
+
const parsed = parseDataUrlImage(dataUrl);
|
|
1426
1530
|
if (!parsed) continue;
|
|
1427
1531
|
maxImageIndex += 1;
|
|
1428
1532
|
const imageName = `image${maxImageIndex}.${parsed.ext}`;
|
|
1429
1533
|
zip.file(`ppt/media/${imageName}`, parsed.data, { base64: true });
|
|
1430
|
-
const slideNumber = pattern.slideIndex + 1;
|
|
1431
|
-
const slidePath = `ppt/slides/slide${slideNumber}.xml`;
|
|
1432
|
-
const relsPath = `ppt/slides/_rels/slide${slideNumber}.xml.rels`;
|
|
1433
1534
|
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>`;
|
|
1434
1535
|
let maxRelId = 0;
|
|
1435
1536
|
relsXml.replace(/Id="rId(\d+)"/g, (_, id) => {
|
|
@@ -1437,32 +1538,28 @@ async function createPPTX(template) {
|
|
|
1437
1538
|
if (Number.isFinite(value)) maxRelId = Math.max(maxRelId, value);
|
|
1438
1539
|
return "";
|
|
1439
1540
|
});
|
|
1440
|
-
|
|
1541
|
+
relId = `rId${maxRelId + 1}`;
|
|
1441
1542
|
const relEntry = `<Relationship Id="${relId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="../media/${imageName}"/>`;
|
|
1442
|
-
|
|
1443
|
-
"</Relationships>",
|
|
1444
|
-
`${relEntry}</Relationships>`
|
|
1445
|
-
);
|
|
1446
|
-
relsCache.set(slideNumber, nextRelsXml);
|
|
1447
|
-
const slideXml = (_d = slideCache.get(slideNumber)) != null ? _d : zip.file(slidePath) ? await zip.file(slidePath).async("string") : "";
|
|
1448
|
-
const nextSlideXml = slideXml ? applyPatternFill(slideXml, pattern.objectName, relId) : slideXml;
|
|
1449
|
-
slideCache.set(slideNumber, nextSlideXml);
|
|
1450
|
-
}
|
|
1451
|
-
for (const [slideNumber, xml] of slideCache.entries()) {
|
|
1452
|
-
zip.file(`ppt/slides/slide${slideNumber}.xml`, xml);
|
|
1543
|
+
relsCache.set(slideNumber, relsXml.replace("</Relationships>", `${relEntry}</Relationships>`));
|
|
1453
1544
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1545
|
+
const slideXml = (_d = slideCache.get(slideNumber)) != null ? _d : zip.file(slidePath) ? await zip.file(slidePath).async("string") : "";
|
|
1546
|
+
if (!slideXml) {
|
|
1547
|
+
continue;
|
|
1456
1548
|
}
|
|
1549
|
+
const nextSlideXml = patch.kind === "background" ? applyBackgroundFillPatch(slideXml, patch.fill, relId) : applyShapeFillPatch(slideXml, patch.objectName, patch.fill, relId);
|
|
1550
|
+
slideCache.set(slideNumber, nextSlideXml);
|
|
1551
|
+
}
|
|
1552
|
+
for (const [slideNumber, xml] of slideCache.entries()) {
|
|
1553
|
+
zip.file(`ppt/slides/slide${slideNumber}.xml`, xml);
|
|
1554
|
+
}
|
|
1555
|
+
for (const [slideNumber, xml] of relsCache.entries()) {
|
|
1556
|
+
zip.file(`ppt/slides/_rels/slide${slideNumber}.xml.rels`, xml);
|
|
1457
1557
|
}
|
|
1458
1558
|
const blob = await zip.generateAsync({ type: "blob" });
|
|
1459
1559
|
return { blob, fileName };
|
|
1460
1560
|
}
|
|
1461
1561
|
var buildPptxBlob = createPPTX;
|
|
1462
1562
|
export {
|
|
1463
|
-
ENABLE_DECK_JSON,
|
|
1464
|
-
PPTX_JSON_PAYLOAD_PATH,
|
|
1465
|
-
PPTX_JSON_PAYLOAD_VERSION,
|
|
1466
1563
|
buildPptxBlob,
|
|
1467
1564
|
createPPTX,
|
|
1468
1565
|
getElementRange,
|