ppt2json 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,10 +1,5 @@
1
1
  // index.ts
2
- import JSZip2 from "jszip";
3
- import {
4
- ENABLE_DECK_JSON,
5
- PPTX_JSON_PAYLOAD_PATH,
6
- PPTX_JSON_PAYLOAD_VERSION
7
- } from "json2pptx";
2
+ import { parseDocument } from "json2pptx-schema";
8
3
 
9
4
  // parser/parse.ts
10
5
  import JSZip from "jszip";
@@ -3129,176 +3124,8 @@ function shapeArc(cX, cY, rX, rY, stAng, endAng, isClose) {
3129
3124
  }
3130
3125
  function getCustomShapePath(custShapType, w, h) {
3131
3126
  const pathLstNode = getTextByPathList(custShapType, ["a:pathLst"]);
3132
- let pathNodes = getTextByPathList(pathLstNode, ["a:path"]);
3133
- if (Array.isArray(pathNodes)) pathNodes = pathNodes.shift();
3134
- const maxX = parseInt(pathNodes["attrs"]["w"]);
3135
- const maxY = parseInt(pathNodes["attrs"]["h"]);
3136
- const cX = maxX === 0 ? 0 : 1 / maxX * w;
3137
- const cY = maxY === 0 ? 0 : 1 / maxY * h;
3138
- let d = "";
3139
- let moveToNode = getTextByPathList(pathNodes, ["a:moveTo"]);
3140
- let lnToNodes = pathNodes["a:lnTo"];
3141
- let cubicBezToNodes = pathNodes["a:cubicBezTo"];
3142
- let quadBezToNodes = pathNodes["a:quadBezTo"];
3143
- const arcToNodes = pathNodes["a:arcTo"];
3144
- let closeNode = getTextByPathList(pathNodes, ["a:close"]);
3145
- if (!Array.isArray(moveToNode)) moveToNode = [moveToNode];
3146
- const multiSapeAry = [];
3147
- if (moveToNode.length > 0) {
3148
- Object.keys(moveToNode).forEach((key) => {
3149
- const moveToPtNode = moveToNode[key]["a:pt"];
3150
- if (moveToPtNode) {
3151
- Object.keys(moveToPtNode).forEach((key2) => {
3152
- const moveToNoPt = moveToPtNode[key2];
3153
- const spX = moveToNoPt["attrs"]?.["x"];
3154
- const spY = moveToNoPt["attrs"]?.["y"];
3155
- const order = moveToNoPt["attrs"]?.["order"];
3156
- multiSapeAry.push({
3157
- type: "movto",
3158
- x: spX,
3159
- y: spY,
3160
- order
3161
- });
3162
- });
3163
- }
3164
- });
3165
- if (lnToNodes) {
3166
- if (!Array.isArray(lnToNodes)) lnToNodes = [lnToNodes];
3167
- Object.keys(lnToNodes).forEach((key) => {
3168
- const lnToPtNode = lnToNodes[key]["a:pt"];
3169
- if (lnToPtNode) {
3170
- Object.keys(lnToPtNode).forEach((key2) => {
3171
- const lnToNoPt = lnToPtNode[key2];
3172
- const ptX = lnToNoPt["attrs"]?.["x"];
3173
- const ptY = lnToNoPt["attrs"]?.["y"];
3174
- const order = lnToNoPt["attrs"]?.["order"];
3175
- multiSapeAry.push({
3176
- type: "lnto",
3177
- x: ptX,
3178
- y: ptY,
3179
- order
3180
- });
3181
- });
3182
- }
3183
- });
3184
- }
3185
- if (cubicBezToNodes) {
3186
- const cubicBezToPtNodesAry = [];
3187
- if (!Array.isArray(cubicBezToNodes)) cubicBezToNodes = [cubicBezToNodes];
3188
- Object.keys(cubicBezToNodes).forEach((key) => {
3189
- cubicBezToPtNodesAry.push(cubicBezToNodes[key]["a:pt"]);
3190
- });
3191
- cubicBezToPtNodesAry.forEach((key) => {
3192
- const pts_ary = [];
3193
- key.forEach((pt) => {
3194
- const pt_obj = {
3195
- x: pt["attrs"]["x"],
3196
- y: pt["attrs"]["y"]
3197
- };
3198
- pts_ary.push(pt_obj);
3199
- });
3200
- const order = key[0]["attrs"]["order"];
3201
- multiSapeAry.push({
3202
- type: "cubicBezTo",
3203
- cubBzPt: pts_ary,
3204
- order
3205
- });
3206
- });
3207
- }
3208
- if (quadBezToNodes) {
3209
- const quadBezToPtNodesAry = [];
3210
- if (!Array.isArray(quadBezToNodes)) quadBezToNodes = [quadBezToNodes];
3211
- Object.keys(quadBezToNodes).forEach((key) => {
3212
- quadBezToPtNodesAry.push(quadBezToNodes[key]["a:pt"]);
3213
- });
3214
- quadBezToPtNodesAry.forEach((key) => {
3215
- const pts_ary = [];
3216
- key.forEach((pt) => {
3217
- const pt_obj = {
3218
- x: pt["attrs"]["x"],
3219
- y: pt["attrs"]["y"]
3220
- };
3221
- pts_ary.push(pt_obj);
3222
- });
3223
- const order = key[0]["attrs"]["order"];
3224
- multiSapeAry.push({
3225
- type: "quadBezTo",
3226
- quadBzPt: pts_ary,
3227
- order
3228
- });
3229
- });
3230
- }
3231
- if (arcToNodes) {
3232
- const arcToNodesAttrs = arcToNodes["attrs"];
3233
- const order = arcToNodesAttrs["order"];
3234
- const hR = arcToNodesAttrs["hR"];
3235
- const wR = arcToNodesAttrs["wR"];
3236
- const stAng = arcToNodesAttrs["stAng"];
3237
- const swAng = arcToNodesAttrs["swAng"];
3238
- let shftX = 0;
3239
- let shftY = 0;
3240
- const arcToPtNode = getTextByPathList(arcToNodes, ["a:pt", "attrs"]);
3241
- if (arcToPtNode) {
3242
- shftX = arcToPtNode["x"];
3243
- shftY = arcToPtNode["y"];
3244
- }
3245
- multiSapeAry.push({
3246
- type: "arcTo",
3247
- hR,
3248
- wR,
3249
- stAng,
3250
- swAng,
3251
- shftX,
3252
- shftY,
3253
- order
3254
- });
3255
- }
3256
- if (closeNode) {
3257
- if (!Array.isArray(closeNode)) closeNode = [closeNode];
3258
- Object.keys(closeNode).forEach(() => {
3259
- multiSapeAry.push({
3260
- type: "close",
3261
- order: Infinity
3262
- });
3263
- });
3264
- }
3265
- multiSapeAry.sort((a, b) => a.order - b.order);
3266
- let k = 0;
3267
- while (k < multiSapeAry.length) {
3268
- if (multiSapeAry[k].type === "movto") {
3269
- const spX = parseInt(multiSapeAry[k].x) * cX;
3270
- const spY = parseInt(multiSapeAry[k].y) * cY;
3271
- d += " M" + spX + "," + spY;
3272
- } else if (multiSapeAry[k].type === "lnto") {
3273
- const Lx = parseInt(multiSapeAry[k].x) * cX;
3274
- const Ly = parseInt(multiSapeAry[k].y) * cY;
3275
- d += " L" + Lx + "," + Ly;
3276
- } else if (multiSapeAry[k].type === "cubicBezTo") {
3277
- const Cx1 = parseInt(multiSapeAry[k].cubBzPt[0].x) * cX;
3278
- const Cy1 = parseInt(multiSapeAry[k].cubBzPt[0].y) * cY;
3279
- const Cx2 = parseInt(multiSapeAry[k].cubBzPt[1].x) * cX;
3280
- const Cy2 = parseInt(multiSapeAry[k].cubBzPt[1].y) * cY;
3281
- const Cx3 = parseInt(multiSapeAry[k].cubBzPt[2].x) * cX;
3282
- const Cy3 = parseInt(multiSapeAry[k].cubBzPt[2].y) * cY;
3283
- d += " C" + Cx1 + "," + Cy1 + " " + Cx2 + "," + Cy2 + " " + Cx3 + "," + Cy3;
3284
- } else if (multiSapeAry[k].type === "quadBezTo") {
3285
- const Qx1 = parseInt(multiSapeAry[k].quadBzPt[0].x) * cX;
3286
- const Qy1 = parseInt(multiSapeAry[k].quadBzPt[0].y) * cY;
3287
- const Qx2 = parseInt(multiSapeAry[k].quadBzPt[1].x) * cX;
3288
- const Qy2 = parseInt(multiSapeAry[k].quadBzPt[1].y) * cY;
3289
- d += " Q" + Qx1 + "," + Qy1 + " " + Qx2 + "," + Qy2;
3290
- } else if (multiSapeAry[k].type === "arcTo") {
3291
- const hR = parseInt(multiSapeAry[k].hR) * cX;
3292
- const wR = parseInt(multiSapeAry[k].wR) * cY;
3293
- const stAng = parseInt(multiSapeAry[k].stAng) / 6e4;
3294
- const swAng = parseInt(multiSapeAry[k].swAng) / 6e4;
3295
- const endAng = stAng + swAng;
3296
- d += shapeArc(wR, hR, wR, hR, stAng, endAng, false);
3297
- } else if (multiSapeAry[k].type === "close") d += "z";
3298
- k++;
3299
- }
3300
- }
3301
- return d;
3127
+ const pathNodes = normalizeToArray(getTextByPathList(pathLstNode, ["a:path"]));
3128
+ return pathNodes.map((pathNode) => buildCustomShapeSegment(pathNode, w, h)).filter(Boolean).join(" ");
3302
3129
  }
3303
3130
  function identifyShape(shapeData) {
3304
3131
  const pathLst = shapeData["a:pathLst"];
@@ -3313,16 +3140,16 @@ function identifyShape(shapeData) {
3313
3140
  }
3314
3141
  function extractPathCommands(path) {
3315
3142
  const commands = [];
3316
- if (path["a:moveTo"]) {
3317
- const moveTo = path["a:moveTo"];
3318
- const pt = moveTo["a:pt"];
3143
+ const moveToList = normalizeToArray(path["a:moveTo"]);
3144
+ moveToList.forEach((moveTo) => {
3145
+ const pt = Array.isArray(moveTo?.["a:pt"]) ? moveTo["a:pt"][0] : moveTo?.["a:pt"];
3319
3146
  if (pt) {
3320
3147
  commands.push({
3321
3148
  type: "moveTo",
3322
3149
  points: [{ x: parseInt(pt.attrs?.x) || 0, y: parseInt(pt.attrs?.y) || 0 }]
3323
3150
  });
3324
3151
  }
3325
- }
3152
+ });
3326
3153
  const lineToList = normalizeToArray(path["a:lnTo"]);
3327
3154
  lineToList.forEach((lnTo) => {
3328
3155
  const pt = lnTo["a:pt"];
@@ -3372,6 +3199,55 @@ function normalizeToArray(value) {
3372
3199
  if (!value) return [];
3373
3200
  return Array.isArray(value) ? value : [value];
3374
3201
  }
3202
+ function buildCustomShapeSegment(pathNode, w, h) {
3203
+ if (!pathNode || typeof pathNode !== "object") return "";
3204
+ const maxX = parseInt(pathNode.attrs?.w) || 0;
3205
+ const maxY = parseInt(pathNode.attrs?.h) || 0;
3206
+ const scaleX = maxX === 0 ? 0 : w / maxX;
3207
+ const scaleY = maxY === 0 ? 0 : h / maxY;
3208
+ const commands = extractPathCommands(pathNode);
3209
+ if (!commands.length) return "";
3210
+ return commands.map((command) => mapCommandToPath(command, scaleX, scaleY)).filter(Boolean).join(" ");
3211
+ }
3212
+ function mapCommandToPath(command, scaleX, scaleY) {
3213
+ switch (command.type) {
3214
+ case "moveTo": {
3215
+ const point = command.points?.[0];
3216
+ return `M${scaleValue(point?.x, scaleX)},${scaleValue(point?.y, scaleY)}`;
3217
+ }
3218
+ case "lineTo": {
3219
+ const point = command.points?.[0];
3220
+ return `L${scaleValue(point?.x, scaleX)},${scaleValue(point?.y, scaleY)}`;
3221
+ }
3222
+ case "cubicBezTo": {
3223
+ if (!Array.isArray(command.points) || command.points.length < 3) return "";
3224
+ const [p1, p2, p3] = command.points;
3225
+ return `C${scaleValue(p1?.x, scaleX)},${scaleValue(p1?.y, scaleY)} ${scaleValue(p2?.x, scaleX)},${scaleValue(p2?.y, scaleY)} ${scaleValue(p3?.x, scaleX)},${scaleValue(p3?.y, scaleY)}`;
3226
+ }
3227
+ case "quadBezTo": {
3228
+ if (!Array.isArray(command.points) || command.points.length < 2) return "";
3229
+ const [p1, p2] = command.points;
3230
+ return `Q${scaleValue(p1?.x, scaleX)},${scaleValue(p1?.y, scaleY)} ${scaleValue(p2?.x, scaleX)},${scaleValue(p2?.y, scaleY)}`;
3231
+ }
3232
+ case "arcTo": {
3233
+ const startAngle = (parseInt(command.stAng) || 0) / 6e4;
3234
+ const sweepAngle = (parseInt(command.swAng) || 0) / 6e4;
3235
+ const endAngle = startAngle + sweepAngle;
3236
+ const radiusX = scaleValue(command.wR, scaleX);
3237
+ const radiusY = scaleValue(command.hR, scaleY);
3238
+ return shapeArc(radiusX, radiusY, radiusX, radiusY, startAngle, endAngle, false).trim();
3239
+ }
3240
+ case "close":
3241
+ return "z";
3242
+ default:
3243
+ return "";
3244
+ }
3245
+ }
3246
+ function scaleValue(value, scale) {
3247
+ const numeric = typeof value === "number" ? value : parseFloat(String(value ?? 0));
3248
+ if (!Number.isFinite(numeric) || !Number.isFinite(scale)) return 0;
3249
+ return numeric * scale;
3250
+ }
3375
3251
  function analyzePathCommands(commands, pathWidth, pathHeight) {
3376
3252
  const analysis = {
3377
3253
  lineCount: 0,
@@ -9310,6 +9186,7 @@ async function genDiagram(node, warpObj) {
9310
9186
 
9311
9187
  // utils.ts
9312
9188
  var PT_TO_PX = 96 / 72;
9189
+ var EXPORTED_OBJECT_NAME_RE = /^(shape-text|shape|text)-(\d+)-(.+)$/;
9313
9190
  function toPx(value) {
9314
9191
  if (value === void 0 || value === null) return void 0;
9315
9192
  return value * PT_TO_PX;
@@ -9327,18 +9204,80 @@ function mapFillColor(value) {
9327
9204
  if (!value) return void 0;
9328
9205
  return value.startsWith("#") ? value : `#${value}`;
9329
9206
  }
9207
+ function parseExportedObjectName(name) {
9208
+ if (!name) return void 0;
9209
+ const match = name.match(EXPORTED_OBJECT_NAME_RE);
9210
+ if (!match) return void 0;
9211
+ const slideIndex = Number.parseInt(match[2], 10);
9212
+ if (!Number.isFinite(slideIndex)) return void 0;
9213
+ return {
9214
+ kind: match[1],
9215
+ slideIndex,
9216
+ refId: match[3]
9217
+ };
9218
+ }
9219
+ function parseGradientPosition(value) {
9220
+ if (typeof value === "number" && Number.isFinite(value)) {
9221
+ return value > 100 ? value / 1e3 : value;
9222
+ }
9223
+ if (typeof value !== "string") return 0;
9224
+ const numeric = Number.parseFloat(value);
9225
+ if (!Number.isFinite(numeric)) return 0;
9226
+ if (value.trim().endsWith("%")) return numeric;
9227
+ return numeric > 100 ? numeric / 1e3 : numeric;
9228
+ }
9229
+ function mapGradient(value) {
9230
+ if (!value || typeof value !== "object") return void 0;
9231
+ const colors = Array.isArray(value.colors) ? value.colors.filter((stop) => stop && typeof stop === "object").map((stop) => ({
9232
+ pos: parseGradientPosition(stop.pos),
9233
+ color: mapFillColor(stop.color) ?? "#FFFFFF"
9234
+ })) : [];
9235
+ return {
9236
+ type: typeof value.type === "string" ? value.type === "line" ? "linear" : value.type : typeof value.path === "string" ? value.path === "line" ? "linear" : value.path : "linear",
9237
+ rotate: typeof value.rotate === "number" ? value.rotate : typeof value.rot === "number" ? value.rot : 0,
9238
+ colors
9239
+ };
9240
+ }
9330
9241
  function mapFill(fill) {
9331
- if (!fill) return {};
9332
- if (typeof fill === "string") return { fill: mapFillColor(fill) ?? void 0 };
9333
- if (typeof fill === "object") {
9334
- if (fill.type === "color" && typeof fill.value === "string") {
9335
- return { fill: mapFillColor(fill.value) ?? void 0 };
9336
- }
9337
- if (fill.type === "image" && fill.value?.picBase64) {
9338
- return { fill: "", pattern: fill.value.picBase64 };
9339
- }
9242
+ if (!fill) return void 0;
9243
+ if (typeof fill === "string") {
9244
+ const color = mapFillColor(fill);
9245
+ return color ? { type: "solid", color } : void 0;
9246
+ }
9247
+ if (typeof fill !== "object") {
9248
+ return void 0;
9249
+ }
9250
+ if (fill.type === "color" && typeof fill.value === "string") {
9251
+ const color = mapFillColor(fill.value);
9252
+ return color ? { type: "solid", color } : void 0;
9253
+ }
9254
+ if (fill.type === "solid" || fill.type === void 0 && typeof fill.color === "string") {
9255
+ const color = mapFillColor(fill.color);
9256
+ return color ? { type: "solid", color } : void 0;
9340
9257
  }
9341
- return {};
9258
+ if (fill.type === "gradient") {
9259
+ const gradient = mapGradient(fill.value ?? fill.gradient ?? fill);
9260
+ return gradient ? { type: "gradient", gradient } : void 0;
9261
+ }
9262
+ if ((fill.type === "image" || fill.type === "pattern") && fill.value?.picBase64) {
9263
+ return {
9264
+ type: "image",
9265
+ src: fill.value.picBase64,
9266
+ ...typeof fill.value.opacity === "number" ? { opacity: fill.value.opacity } : {}
9267
+ };
9268
+ }
9269
+ if (fill.type === "image" && typeof fill.src === "string") {
9270
+ return {
9271
+ type: "image",
9272
+ src: fill.src,
9273
+ ...typeof fill.opacity === "number" ? { opacity: fill.opacity } : {}
9274
+ };
9275
+ }
9276
+ if (fill.gradient || fill.colors) {
9277
+ const gradient = mapGradient(fill.gradient ?? fill);
9278
+ return gradient ? { type: "gradient", gradient } : void 0;
9279
+ }
9280
+ return void 0;
9342
9281
  }
9343
9282
  function convertFontSizeToPx(content) {
9344
9283
  return content.replace(/font-size:\s*([0-9.]+)pt/gi, (_, size) => {
@@ -9463,14 +9402,16 @@ function mapKeypoints(pathFormula, raw) {
9463
9402
  // element-mapper.ts
9464
9403
  function mapElement(raw) {
9465
9404
  if (!raw || !raw.type) return null;
9466
- const base = mapBaseElement(raw);
9467
- if (raw.type === "text") {
9405
+ const exportedMeta = parseExportedObjectName(raw.name);
9406
+ const base = mapBaseElement(raw, exportedMeta);
9407
+ if (raw.type === "text" || isExportedTextShape(raw, exportedMeta)) {
9468
9408
  const content = raw.content !== void 0 ? normalizeTextContent(raw.content) : raw.content;
9469
9409
  const fallbackLineHeight = hasMultiLineText(content) ? 1.5 : 1;
9470
9410
  const lineHeight = raw.lineHeight ?? fallbackLineHeight;
9471
9411
  const normalizedHeight = normalizeTextHeight(base.height, content, lineHeight);
9472
9412
  return {
9473
9413
  ...base,
9414
+ type: "text",
9474
9415
  height: normalizedHeight,
9475
9416
  rotate: base.rotate ?? 0,
9476
9417
  flipH: void 0,
@@ -9482,7 +9423,7 @@ function mapElement(raw) {
9482
9423
  lineHeight,
9483
9424
  paragraphSpace: raw.paragraphSpace,
9484
9425
  vertical: raw.isVertical,
9485
- fill: mapFill(raw.fill).fill ?? ""
9426
+ fill: mapFill(raw.fill)
9486
9427
  };
9487
9428
  }
9488
9429
  if (raw.type === "image") {
@@ -9507,10 +9448,11 @@ function mapElement(raw) {
9507
9448
  };
9508
9449
  }
9509
9450
  if (raw.type === "shape") {
9510
- const { fill, pattern } = mapFill(raw.fill);
9451
+ const fill = mapFill(raw.fill) ?? { type: "solid", color: "rgba(255,255,255,0)" };
9511
9452
  const pathFormula = mapPathFormula(raw.shapType);
9512
9453
  const keypoints = pathFormula ? mapKeypoints(pathFormula, raw.keypoints) : void 0;
9513
- const hasText = raw.content !== void 0;
9454
+ const normalizedShapeText = raw.content !== void 0 ? normalizeTextContent(raw.content) : void 0;
9455
+ const hasText = hasRenderableText(normalizedShapeText);
9514
9456
  let path = raw.path;
9515
9457
  let viewBox = raw.viewBox ?? [raw.width ?? 0, raw.height ?? 0];
9516
9458
  let special = false;
@@ -9549,17 +9491,17 @@ function mapElement(raw) {
9549
9491
  }
9550
9492
  return {
9551
9493
  ...base,
9494
+ type: "shape",
9552
9495
  rotate: base.rotate ?? 0,
9553
9496
  fixedRatio: false,
9554
9497
  path,
9555
9498
  viewBox,
9556
9499
  pathFormula: pathFormula ?? void 0,
9557
9500
  keypoints: keypoints ?? void 0,
9558
- pattern,
9559
- fill: fill ?? "",
9501
+ fill,
9560
9502
  ...special ? { special: true } : {},
9561
9503
  text: hasText ? {
9562
- content: normalizeTextContent(raw.content),
9504
+ content: normalizedShapeText,
9563
9505
  align: normalizeVAlign(raw.vAlign) ?? "middle",
9564
9506
  defaultColor: normalizeColor(raw.defaultColor) ?? normalizeColor(raw.color) ?? "#333",
9565
9507
  defaultFontName: raw.fontName ?? "",
@@ -9570,6 +9512,7 @@ function mapElement(raw) {
9570
9512
  if (raw.type === "line") {
9571
9513
  return {
9572
9514
  ...base,
9515
+ type: "line",
9573
9516
  start: toPxPair(raw.start),
9574
9517
  end: toPxPair(raw.end),
9575
9518
  broken: toPxPair(raw.broken),
@@ -9606,9 +9549,6 @@ function normalizeElement(element) {
9606
9549
  points: ["", ""]
9607
9550
  };
9608
9551
  }
9609
- if (element.type === "shape" && element.fill) {
9610
- element.fill = mapFillColor(element.fill) ?? element.fill;
9611
- }
9612
9552
  if (element.type === "text" && element.content) {
9613
9553
  element.content = normalizeTextContent(element.content);
9614
9554
  }
@@ -9659,20 +9599,21 @@ function mapOutline(raw) {
9659
9599
  style: raw.borderType
9660
9600
  };
9661
9601
  }
9662
- function mapBaseElement(raw) {
9602
+ function mapBaseElement(raw, exportedMeta) {
9663
9603
  const outline = mapOutline(raw);
9664
9604
  const shadow = mapShadow(raw.shadow);
9665
- const { fill } = mapFill(raw.fill);
9605
+ const fill = mapFill(raw.fill);
9606
+ const exportedId = exportedMeta?.kind === "shape-text" ? `${exportedMeta.refId}__text` : exportedMeta?.refId;
9666
9607
  return {
9667
9608
  type: raw.type,
9668
- id: raw.id,
9609
+ id: exportedId ?? raw.id,
9669
9610
  groupId: raw.groupId,
9670
9611
  left: toPx(raw.left),
9671
9612
  top: toPx(raw.top),
9672
9613
  width: toPx(raw.width),
9673
9614
  height: toPx(raw.height),
9674
9615
  rotate: raw.rotate,
9675
- fill: fill ?? void 0,
9616
+ fill,
9676
9617
  opacity: raw.opacity,
9677
9618
  outline: outline ?? void 0,
9678
9619
  shadow: shadow ?? void 0,
@@ -9680,6 +9621,15 @@ function mapBaseElement(raw) {
9680
9621
  flipV: raw.isFlipV
9681
9622
  };
9682
9623
  }
9624
+ function isExportedTextShape(raw, exportedMeta) {
9625
+ if (raw?.type !== "shape" || !raw?.content) return false;
9626
+ return exportedMeta?.kind === "text" || exportedMeta?.kind === "shape-text";
9627
+ }
9628
+ function hasRenderableText(content) {
9629
+ if (!content) return false;
9630
+ const plainText = content.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
9631
+ return plainText.length > 0;
9632
+ }
9683
9633
 
9684
9634
  // slide-normalizer.ts
9685
9635
  var ID_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
@@ -9694,7 +9644,8 @@ function normalizeSlide(slide, index) {
9694
9644
  const orderB = typeof b?.order === "number" ? b.order : 0;
9695
9645
  return orderA - orderB;
9696
9646
  });
9697
- const elements = flattenElements(orderedElements).map((element, elementIndex) => {
9647
+ const restoredElements = restoreExportedRoundTripElements(orderedElements);
9648
+ const elements = flattenElements(restoredElements).map((element, elementIndex) => {
9698
9649
  if (!element.id) {
9699
9650
  element.id = makeId(`slide-${index}-element-${elementIndex}-${element.type}`);
9700
9651
  }
@@ -9702,7 +9653,7 @@ function normalizeSlide(slide, index) {
9702
9653
  });
9703
9654
  return {
9704
9655
  id: slide?.id ?? slide?.slideId ?? makeId(`slide-${index}`),
9705
- background: backgroundFill.fill ? { type: "solid", color: backgroundFill.fill } : backgroundFill.pattern ? { type: "image", src: backgroundFill.pattern } : void 0,
9656
+ background: backgroundFill,
9706
9657
  elements,
9707
9658
  remark: slide?.remark ?? ""
9708
9659
  };
@@ -9719,6 +9670,44 @@ function makeId(seed) {
9719
9670
  }
9720
9671
  return id;
9721
9672
  }
9673
+ function restoreExportedRoundTripElements(elements) {
9674
+ const restored = elements.map((element) => {
9675
+ if (element?.type === "group" && Array.isArray(element.elements)) {
9676
+ return {
9677
+ ...element,
9678
+ elements: restoreExportedRoundTripElements(element.elements)
9679
+ };
9680
+ }
9681
+ return element;
9682
+ });
9683
+ const shapesByRefId = /* @__PURE__ */ new Map();
9684
+ for (const element of restored) {
9685
+ const meta = parseExportedObjectName(element?.name);
9686
+ if (meta?.kind === "shape") {
9687
+ shapesByRefId.set(meta.refId, element);
9688
+ }
9689
+ }
9690
+ return restored.filter((element) => {
9691
+ const meta = parseExportedObjectName(element?.name);
9692
+ if (meta?.kind !== "shape-text") {
9693
+ return true;
9694
+ }
9695
+ const targetShape = shapesByRefId.get(meta.refId);
9696
+ if (!targetShape || targetShape.type !== "shape") {
9697
+ return true;
9698
+ }
9699
+ mergeShapeText(targetShape, element);
9700
+ return false;
9701
+ });
9702
+ }
9703
+ function mergeShapeText(targetShape, textShape) {
9704
+ if (!targetShape.content && textShape?.content) {
9705
+ targetShape.content = textShape.content;
9706
+ }
9707
+ if (textShape?.vAlign) {
9708
+ targetShape.vAlign = textShape.vAlign;
9709
+ }
9710
+ }
9722
9711
  function hashSeed(value) {
9723
9712
  let hash = 1779033703 ^ value.length;
9724
9713
  for (let index = 0; index < value.length; index += 1) {
@@ -9752,49 +9741,14 @@ var DEFAULT_THEME = {
9752
9741
  };
9753
9742
  async function parsePptxToJson(file) {
9754
9743
  const fileBuffer = await file.arrayBuffer();
9755
- const zip = await JSZip2.loadAsync(fileBuffer);
9756
- const embedded = ENABLE_DECK_JSON ? await resolveEmbeddedDeck(zip) : null;
9757
- if (embedded?.deck) {
9758
- return {
9759
- deck: embedded.deck,
9760
- warnings: embedded.warnings
9761
- };
9762
- }
9763
9744
  return {
9764
9745
  deck: await buildDeckFromPptx(fileBuffer),
9765
- warnings: embedded?.warnings ?? []
9746
+ warnings: []
9766
9747
  };
9767
9748
  }
9768
- async function resolveEmbeddedDeck(zip) {
9769
- const embeddedFile = zip.file(PPTX_JSON_PAYLOAD_PATH) ?? Object.values(zip.files).find(
9770
- (entry) => entry.name.endsWith(`/${PPTX_JSON_PAYLOAD_PATH}`)
9771
- ) ?? null;
9772
- if (!embeddedFile) {
9773
- return null;
9774
- }
9775
- const payloadText = await embeddedFile.async("string");
9776
- try {
9777
- const parsed = JSON.parse(payloadText);
9778
- if (isDeckEnvelope(parsed) && parsed.deck) {
9779
- return {
9780
- deck: parsed.deck,
9781
- warnings: parsed.version && parsed.version !== PPTX_JSON_PAYLOAD_VERSION ? [
9782
- `Embedded JSON payload version ${parsed.version} differs from ${PPTX_JSON_PAYLOAD_VERSION}.`
9783
- ] : []
9784
- };
9785
- }
9786
- return {
9787
- deck: parsed,
9788
- warnings: []
9789
- };
9790
- } catch {
9791
- return {
9792
- warnings: ["Embedded JSON payload could not be parsed. Falling back to PPTX parsing."]
9793
- };
9794
- }
9795
- }
9796
- function isDeckEnvelope(payload) {
9797
- return typeof payload === "object" && payload !== null && "deck" in payload;
9749
+ function normalizeDeck(value) {
9750
+ const document = parseDocument(value);
9751
+ return document;
9798
9752
  }
9799
9753
  async function buildDeckFromPptx(buffer) {
9800
9754
  const pptxJson = await parse2(buffer);
@@ -9804,7 +9758,7 @@ async function buildDeckFromPptx(buffer) {
9804
9758
  const slides = (Array.isArray(pptxJson.slides) ? pptxJson.slides : []).map(
9805
9759
  (slide, index) => normalizeSlide(slide, index)
9806
9760
  );
9807
- return {
9761
+ return normalizeDeck({
9808
9762
  title: DEFAULT_DECK_TITLE,
9809
9763
  width,
9810
9764
  height,
@@ -9813,7 +9767,7 @@ async function buildDeckFromPptx(buffer) {
9813
9767
  themeColors
9814
9768
  },
9815
9769
  slides
9816
- };
9770
+ });
9817
9771
  }
9818
9772
  function mapThemeColors(pptxJson) {
9819
9773
  if (!Array.isArray(pptxJson.themeColors)) {