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.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, _b;
904
- const backgroundColor = (_b = (_a = slideJson.background) == null ? void 0 : _a.color) != null ? _b : theme == null ? void 0 : theme.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: ((_a = element.left) != null ? _a : 0) / ratioPx2Inch,
1096
- y: ((_b = element.top) != null ? _b : 0) / ratioPx2Inch,
1097
- w: ((_c = element.width) != null ? _c : 0) / ratioPx2Inch,
1098
- h: ((_d = element.height) != null ? _d : 0) / ratioPx2Inch,
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 || ((_e = template.theme) == null ? void 0 : _e.fontName) || DEFAULT_FONT_FACE,
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 (element.fill) {
1114
- const c = formatColor(element.fill);
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 ((_f = element.outline) == null ? void 0 : _f.width) {
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, patternShapes) {
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 pattern = element.pattern;
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: ((_c = element.left) != null ? _c : 0) / ratioPx2Inch,
1190
- y: ((_d = element.top) != null ? _d : 0) / ratioPx2Inch,
1191
- w: ((_e = element.width) != null ? _e : 0) / ratioPx2Inch,
1192
- h: ((_f = element.height) != null ? _f : 0) / ratioPx2Inch,
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 (pattern) {
1196
- const objectName = `pattern-${slideIndex}-${(_g = element.id) != null ? _g : elementIndex}`;
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 patternShapes = [];
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(slide, element, renderTemplate, ratioPx2Pt, ratioPx2Inch, textPadding);
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
- patternShapes
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
- const needsZip = patternShapes.length > 0;
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
- if (patternShapes.length) {
1413
- const mediaFiles = Object.keys(zip.files).filter((name) => name.startsWith("ppt/media/"));
1414
- let maxImageIndex = 0;
1415
- for (const name of mediaFiles) {
1416
- const match = name.match(/ppt\/media\/image(\d+)/);
1417
- if (match) {
1418
- const index = Number.parseInt(match[1], 10);
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
- const slideCache = /* @__PURE__ */ new Map();
1423
- const relsCache = /* @__PURE__ */ new Map();
1424
- for (const pattern of patternShapes) {
1425
- const parsed = parseDataUrlImage(pattern.dataUrl);
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
- const relId = `rId${maxRelId + 1}`;
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
- const nextRelsXml = relsXml.replace(
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
- for (const [slideNumber, xml] of relsCache.entries()) {
1455
- zip.file(`ppt/slides/_rels/slide${slideNumber}.xml.rels`, xml);
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,