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/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, _b;
945
- const backgroundColor = (_b = (_a = slideJson.background) == null ? void 0 : _a.color) != null ? _b : theme == null ? void 0 : theme.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: ((_a = element.left) != null ? _a : 0) / ratioPx2Inch,
1137
- y: ((_b = element.top) != null ? _b : 0) / ratioPx2Inch,
1138
- w: ((_c = element.width) != null ? _c : 0) / ratioPx2Inch,
1139
- h: ((_d = element.height) != null ? _d : 0) / ratioPx2Inch,
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 || ((_e = template.theme) == null ? void 0 : _e.fontName) || DEFAULT_FONT_FACE,
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 (element.fill) {
1155
- const c = formatColor(element.fill);
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 ((_f = element.outline) == null ? void 0 : _f.width) {
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, patternShapes) {
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 pattern = element.pattern;
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: ((_c = element.left) != null ? _c : 0) / ratioPx2Inch,
1231
- y: ((_d = element.top) != null ? _d : 0) / ratioPx2Inch,
1232
- w: ((_e = element.width) != null ? _e : 0) / ratioPx2Inch,
1233
- h: ((_f = element.height) != null ? _f : 0) / ratioPx2Inch,
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 (pattern) {
1237
- const objectName = `pattern-${slideIndex}-${(_g = element.id) != null ? _g : elementIndex}`;
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 patternShapes = [];
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(slide, element, renderTemplate, ratioPx2Pt, ratioPx2Inch, textPadding);
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
- patternShapes
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
- const needsZip = patternShapes.length > 0;
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
- if (patternShapes.length) {
1454
- const mediaFiles = Object.keys(zip.files).filter((name) => name.startsWith("ppt/media/"));
1455
- let maxImageIndex = 0;
1456
- for (const name of mediaFiles) {
1457
- const match = name.match(/ppt\/media\/image(\d+)/);
1458
- if (match) {
1459
- const index = Number.parseInt(match[1], 10);
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
- const slideCache = /* @__PURE__ */ new Map();
1464
- const relsCache = /* @__PURE__ */ new Map();
1465
- for (const pattern of patternShapes) {
1466
- const parsed = parseDataUrlImage(pattern.dataUrl);
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
- const relId = `rId${maxRelId + 1}`;
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
- const nextRelsXml = relsXml.replace(
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
- for (const [slideNumber, xml] of relsCache.entries()) {
1496
- zip.file(`ppt/slides/_rels/slide${slideNumber}.xml.rels`, xml);
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,