@office-open/pptx 0.6.2 → 0.6.3

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
@@ -4,6 +4,7 @@ import { BevelPresetType, CompoundLine, LineCap, LineJoin, PathShadeType, PenAli
4
4
  import { ChartCollection, ChartSpace } from "@office-open/core/chart";
5
5
  import { DEFAULT_DRAWING_XML, SmartArtCollection, createDataModel, getColorXml, getLayoutXml, getStyleXml } from "@office-open/core/smartart";
6
6
  import { attr, attrBool, attrNum, children, findChild, findDeep, textOf, xml } from "@office-open/xml";
7
+ import { toUint8Array } from "undio";
7
8
  export * from "@office-open/core/values";
8
9
  //#region src/file/drawingml/effects.ts
9
10
  /** Convert PPTX simple color to core SolidFillOptions. */
@@ -5191,6 +5192,274 @@ var ParseContext = class {
5191
5192
  }
5192
5193
  };
5193
5194
  //#endregion
5195
+ //#region src/parse/animation.ts
5196
+ /**
5197
+ * Animation parser for PPTX documents.
5198
+ *
5199
+ * Parses p:timing elements and maps animations to their target shapes.
5200
+ *
5201
+ * @module
5202
+ */
5203
+ const ENTR_PRESET_TO_TYPE = /* @__PURE__ */ new Map();
5204
+ const EXIT_PRESET_TO_TYPE = /* @__PURE__ */ new Map();
5205
+ const EMPH_PRESET_TO_ID = /* @__PURE__ */ new Map();
5206
+ const PATH_PRESET_TO_TYPE = /* @__PURE__ */ new Map();
5207
+ const ENTR_IDS = {
5208
+ appear: 1,
5209
+ fade: 10,
5210
+ fly: 2,
5211
+ wipe: 22,
5212
+ dissolve: 34,
5213
+ split: 21,
5214
+ blinds: 25,
5215
+ checker: 26,
5216
+ randomBars: 24,
5217
+ wheel: 27,
5218
+ zoom: 10,
5219
+ cover: 28,
5220
+ push: 19,
5221
+ strips: 23
5222
+ };
5223
+ const EXIT_IDS = {
5224
+ appear: 53,
5225
+ fade: 59,
5226
+ fly: 51,
5227
+ wipe: 72,
5228
+ dissolve: 84,
5229
+ split: 71,
5230
+ blinds: 75,
5231
+ checker: 76,
5232
+ randomBars: 74,
5233
+ wheel: 77,
5234
+ zoom: 60,
5235
+ cover: 78,
5236
+ push: 69,
5237
+ strips: 73
5238
+ };
5239
+ const EMPH_IDS = {
5240
+ growShrink: 53,
5241
+ spin: 54,
5242
+ growWithTurn: 56,
5243
+ colorChange: 29,
5244
+ transparency: 57,
5245
+ boldFlash: 50,
5246
+ wave: 55,
5247
+ pulse: 58
5248
+ };
5249
+ const PATH_IDS = {
5250
+ customPath: 200,
5251
+ arc: 201,
5252
+ bounce: 202,
5253
+ circle: 203,
5254
+ curve: 204,
5255
+ figureEight: 205,
5256
+ line: 206,
5257
+ loop: 207
5258
+ };
5259
+ for (const [k, v] of Object.entries(ENTR_IDS)) ENTR_PRESET_TO_TYPE.set(v, k);
5260
+ for (const [k, v] of Object.entries(EXIT_IDS)) EXIT_PRESET_TO_TYPE.set(v, k);
5261
+ for (const [k, v] of Object.entries(EMPH_IDS)) EMPH_PRESET_TO_ID.set(v, k);
5262
+ for (const [k, v] of Object.entries(PATH_IDS)) PATH_PRESET_TO_TYPE.set(v, k);
5263
+ const SUBTYPE_TO_DIRECTION = /* @__PURE__ */ new Map();
5264
+ for (const [k, v] of Object.entries({
5265
+ left: 4,
5266
+ right: 8,
5267
+ up: 2,
5268
+ down: 1,
5269
+ horizontal: 16,
5270
+ vertical: 32
5271
+ })) SUBTYPE_TO_DIRECTION.set(v, k);
5272
+ /**
5273
+ * Parse p:timing element and return a map of shape ID → AnimationOptions.
5274
+ */
5275
+ function parseTiming(el) {
5276
+ const result = /* @__PURE__ */ new Map();
5277
+ const tnLst = findChild(el, "p:tnLst");
5278
+ if (!tnLst) return result;
5279
+ const mainPar = findChild(tnLst, "p:par");
5280
+ if (!mainPar) return result;
5281
+ const mainCTn = findChild(mainPar, "p:cTn");
5282
+ if (!mainCTn) return result;
5283
+ const childTnLst = findChild(mainCTn, "p:childTnLst");
5284
+ if (!childTnLst) return result;
5285
+ const seq = findChild(childTnLst, "p:seq");
5286
+ if (!seq) return result;
5287
+ const seqCTn = findChild(seq, "p:cTn");
5288
+ const seqChildTnLst = seqCTn ? findChild(seqCTn, "p:childTnLst") : void 0;
5289
+ if (!seqChildTnLst) return result;
5290
+ for (const parEl of seqChildTnLst.elements ?? []) {
5291
+ if (parEl.name !== "p:par") continue;
5292
+ const parCTn = findChild(parEl, "p:cTn");
5293
+ if (!parCTn) continue;
5294
+ const parChildTnLst = findChild(parCTn, "p:childTnLst");
5295
+ if (!parChildTnLst) continue;
5296
+ for (const effectEl of parChildTnLst.elements ?? []) {
5297
+ const anim = parseAnimationEffect(effectEl);
5298
+ if (!anim) continue;
5299
+ const shapeId = extractTargetShapeId(effectEl);
5300
+ if (shapeId !== void 0) result.set(shapeId, anim);
5301
+ }
5302
+ }
5303
+ return result;
5304
+ }
5305
+ function parseAnimationEffect(el) {
5306
+ const opts = {};
5307
+ const cTn = findChild(el, "p:cTn") ?? el;
5308
+ const nodeType = attr(cTn, "nodeType");
5309
+ if (nodeType === "clickEffect") opts.trigger = "onClick";
5310
+ else if (nodeType === "withEffect") opts.trigger = "withPrevious";
5311
+ else if (nodeType === "afterEffect") opts.trigger = "afterPrevious";
5312
+ const presetClass = attr(cTn, "presetClass");
5313
+ if (presetClass) opts.class = presetClass;
5314
+ const presetID = attrNum(cTn, "presetID");
5315
+ const dur = attr(cTn, "dur");
5316
+ if (dur) {
5317
+ const ms = parseDuration(dur);
5318
+ if (ms !== void 0) opts.duration = ms;
5319
+ }
5320
+ const stCondLst = findChild(cTn, "p:stCondLst");
5321
+ if (stCondLst) {
5322
+ const cond = findChild(stCondLst, "p:cond");
5323
+ if (cond) {
5324
+ const delay = attr(cond, "delay");
5325
+ if (delay) {
5326
+ const ms = parseDuration(delay);
5327
+ if (ms !== void 0) opts.delay = ms;
5328
+ }
5329
+ }
5330
+ }
5331
+ const presetSubtype = attrNum(cTn, "presetSubtype");
5332
+ if (presetSubtype !== void 0) {
5333
+ const dir = SUBTYPE_TO_DIRECTION.get(presetSubtype);
5334
+ if (dir) opts.direction = dir;
5335
+ }
5336
+ if (presetID !== void 0) {
5337
+ const cls = presetClass ?? "entr";
5338
+ if (cls === "entr") {
5339
+ const type = ENTR_PRESET_TO_TYPE.get(presetID);
5340
+ if (type) opts.type = type;
5341
+ } else if (cls === "exit") {
5342
+ const type = EXIT_PRESET_TO_TYPE.get(presetID);
5343
+ if (type) opts.type = type;
5344
+ } else if (cls === "emph") {
5345
+ const emphType = EMPH_PRESET_TO_ID.get(presetID);
5346
+ if (emphType) {
5347
+ opts.emphasisType = emphType;
5348
+ opts.type = "appear";
5349
+ }
5350
+ } else if (cls === "mediacall") {
5351
+ opts.mediaType = "play";
5352
+ opts.type = "appear";
5353
+ }
5354
+ }
5355
+ const childTnLst = findChild(cTn, "p:childTnLst");
5356
+ if (childTnLst) for (const sub of childTnLst.elements ?? []) switch (sub.name) {
5357
+ case "p:animEffect": {
5358
+ const subCTn = findChild(sub, "p:cTn");
5359
+ if (subCTn) {
5360
+ const subDur = attr(subCTn, "dur");
5361
+ if (subDur) {
5362
+ const ms = parseDuration(subDur);
5363
+ if (ms !== void 0) opts.duration = ms;
5364
+ }
5365
+ }
5366
+ break;
5367
+ }
5368
+ case "p:anim": {
5369
+ const subCTn = findChild(sub, "p:cTn");
5370
+ if (subCTn) {
5371
+ const attrName = findChild(subCTn, "p:attrNameLst");
5372
+ if (attrName) {
5373
+ const name = findChild(attrName, "p:attrName");
5374
+ if (name) {
5375
+ const text = name.elements?.[0]?.text;
5376
+ if (text) opts.attributeName = text;
5377
+ }
5378
+ }
5379
+ }
5380
+ const from = findChild(sub, "p:cb");
5381
+ if (from) {
5382
+ const val = findChild(from, "p:val");
5383
+ if (val) {
5384
+ const v = attr(val, "val");
5385
+ if (v) opts.from = v;
5386
+ }
5387
+ }
5388
+ const toEl = findChild(sub, "p:tavLst");
5389
+ if (toEl) {
5390
+ const tav = findChild(toEl, "p:tav");
5391
+ if (tav) {
5392
+ const toVal = findChild(tav, "p:val");
5393
+ if (toVal) {
5394
+ const v = attr(toVal, "val");
5395
+ if (v) opts.to = v;
5396
+ }
5397
+ }
5398
+ }
5399
+ break;
5400
+ }
5401
+ case "p:animMotion": {
5402
+ opts.pathType = "customPath";
5403
+ const path = attr(sub, "path");
5404
+ if (path) opts.path = path;
5405
+ break;
5406
+ }
5407
+ case "p:animScale":
5408
+ opts.emphasisType = "growShrink";
5409
+ break;
5410
+ case "p:animRot":
5411
+ opts.emphasisType = "spin";
5412
+ break;
5413
+ case "p:animClr":
5414
+ opts.emphasisType = "colorChange";
5415
+ break;
5416
+ case "p:cmd":
5417
+ if (attr(sub, "type") === "call") opts.mediaType = "play";
5418
+ break;
5419
+ }
5420
+ return Object.keys(opts).length > 0 ? opts : void 0;
5421
+ }
5422
+ function extractTargetShapeId(el) {
5423
+ const cBhvr = findDeep$1(el, "p:cBhvr")[0];
5424
+ if (!cBhvr) return void 0;
5425
+ const tgtEl = findChild(cBhvr, "p:tgtEl");
5426
+ if (!tgtEl) return void 0;
5427
+ const spTgt = findChild(tgtEl, "p:spTgt");
5428
+ if (!spTgt) return void 0;
5429
+ return attrNum(spTgt, "spid");
5430
+ }
5431
+ function findDeep$1(parent, name) {
5432
+ const results = [];
5433
+ function walk(el) {
5434
+ if (el.name === name) {
5435
+ results.push(el);
5436
+ return;
5437
+ }
5438
+ for (const child of el.elements ?? []) walk(child);
5439
+ }
5440
+ for (const child of parent.elements ?? []) walk(child);
5441
+ return results;
5442
+ }
5443
+ /**
5444
+ * Parse OOXML duration string to milliseconds.
5445
+ * Format: "PT#H#M#S" or "###ms" or just a number string
5446
+ */
5447
+ function parseDuration(val) {
5448
+ if (val.startsWith("PT")) {
5449
+ let ms = 0;
5450
+ const sMatch = val.match(/(\d+\.?\d*)S/);
5451
+ if (sMatch) ms += Math.round(parseFloat(sMatch[1]) * 1e3);
5452
+ const mMatch = val.match(/(\d+\.?\d*)M/);
5453
+ if (mMatch) ms += Math.round(parseFloat(mMatch[1]) * 6e4);
5454
+ const hMatch = val.match(/(\d+\.?\d*)H/);
5455
+ if (hMatch) ms += Math.round(parseFloat(hMatch[1]) * 36e5);
5456
+ return ms;
5457
+ }
5458
+ if (val === "indefinite") return void 0;
5459
+ const num = parseInt(val, 10);
5460
+ return isNaN(num) ? void 0 : num;
5461
+ }
5462
+ //#endregion
5194
5463
  //#region src/parse/slide.ts
5195
5464
  /**
5196
5465
  * Slide parser for PPTX documents.
@@ -5217,9 +5486,31 @@ function parseSlide(el, ctx) {
5217
5486
  }
5218
5487
  if (slideChildren.length > 0) opts.children = slideChildren;
5219
5488
  }
5489
+ const hf = findChild(cSld, "p:hf");
5490
+ if (hf) {
5491
+ const hfOpts = {};
5492
+ if (findChild(hf, "p:sldNum")) hfOpts.slideNumber = true;
5493
+ if (findChild(hf, "p:dt")) hfOpts.dateTime = true;
5494
+ if (findChild(hf, "p:ftr")) hfOpts.footer = true;
5495
+ if (findChild(hf, "p:hdr")) hfOpts.header = true;
5496
+ if (Object.keys(hfOpts).length > 0) opts.headerFooter = hfOpts;
5497
+ }
5220
5498
  }
5221
5499
  const transition = findChild(el, "p:transition");
5222
5500
  if (transition) opts.transition = parseTransition(transition);
5501
+ const timing = findChild(el, "p:timing");
5502
+ if (timing) {
5503
+ const animMap = parseTiming(timing);
5504
+ if (animMap.size > 0 && opts.children) {
5505
+ const children = opts.children;
5506
+ for (let i = 0; i < children.length; i++) {
5507
+ const child = children[i];
5508
+ const inner = child[Object.keys(child)[0]] ?? {};
5509
+ const shapeId = inner.id;
5510
+ if (shapeId !== void 0 && animMap.has(shapeId)) inner.animation = animMap.get(shapeId);
5511
+ }
5512
+ }
5513
+ }
5223
5514
  for (const [, path] of ctx.slideRels) {
5224
5515
  if (!path.includes("notesSlides")) continue;
5225
5516
  const notesEl = ctx.pptx.doc.get(path);
@@ -5232,8 +5523,18 @@ function parseSlide(el, ctx) {
5232
5523
  }
5233
5524
  function parseSlideChild(el, ctx) {
5234
5525
  switch (el.name) {
5235
- case "p:sp": return { shape: parseShape(el, ctx) };
5236
- case "p:pic": return { picture: parsePicture(el, ctx) };
5526
+ case "p:sp": {
5527
+ const spPr = findChild(el, "p:spPr");
5528
+ const prstGeom = spPr ? findChild(spPr, "a:prstGeom") : void 0;
5529
+ if ((prstGeom ? attr(prstGeom, "prst") : void 0) === "line") return { line: parseLineShape(el) };
5530
+ return { shape: parseShape(el, ctx) };
5531
+ }
5532
+ case "p:pic": {
5533
+ const mediaType = detectMediaType(el);
5534
+ if (mediaType === "video") return { video: parseMediaFrame(el, ctx, "video") };
5535
+ if (mediaType === "audio") return { audio: parseMediaFrame(el, ctx, "audio") };
5536
+ return { picture: parsePicture(el, ctx) };
5537
+ }
5237
5538
  case "p:graphicFrame": return parseGraphicFrame(el, ctx);
5238
5539
  case "p:cxnSp": return { connector: parseConnector(el) };
5239
5540
  case "p:grpSp": return { group: parseGroup(el, ctx) };
@@ -5352,6 +5653,48 @@ function parseShape(el, _ctx) {
5352
5653
  if (txBody) parseTextBody(txBody, opts);
5353
5654
  return opts;
5354
5655
  }
5656
+ function parseLineShape(el) {
5657
+ const opts = {};
5658
+ const nvSpPr = findChild(el, "p:nvSpPr");
5659
+ if (nvSpPr) {
5660
+ const cNvPr = findChild(nvSpPr, "p:cNvPr");
5661
+ if (cNvPr) {
5662
+ const id = attrNum(cNvPr, "id");
5663
+ if (id !== void 0) opts.id = id;
5664
+ const name = attr(cNvPr, "name");
5665
+ if (name) opts.name = name;
5666
+ }
5667
+ }
5668
+ const spPr = findChild(el, "p:spPr");
5669
+ if (spPr) {
5670
+ const xfrm = findChild(spPr, "a:xfrm");
5671
+ if (xfrm) {
5672
+ const off = findChild(xfrm, "a:off");
5673
+ const ext = findChild(xfrm, "a:ext");
5674
+ const flipH = attrBool(xfrm, "flipH");
5675
+ const flipV = attrBool(xfrm, "flipV");
5676
+ if (off && ext) {
5677
+ const offX = attrNum(off, "x") ?? 0;
5678
+ const offY = attrNum(off, "y") ?? 0;
5679
+ const cx = attrNum(ext, "cx") ?? 0;
5680
+ const cy = attrNum(ext, "cy") ?? 0;
5681
+ const x1 = flipH ? offX + cx : offX;
5682
+ const y1 = flipV ? offY + cy : offY;
5683
+ const x2 = flipH ? offX : offX + cx;
5684
+ const y2 = flipV ? offY : offY + cy;
5685
+ opts.x1 = convertEmuToPixels$1(x1);
5686
+ opts.y1 = convertEmuToPixels$1(y1);
5687
+ opts.x2 = convertEmuToPixels$1(x2);
5688
+ opts.y2 = convertEmuToPixels$1(y2);
5689
+ }
5690
+ }
5691
+ const fill = parseFillFromElement(spPr);
5692
+ if (fill) opts.fill = fill;
5693
+ const outline = parseOutlineFromElement(spPr);
5694
+ if (outline) opts.outline = outline;
5695
+ }
5696
+ return opts;
5697
+ }
5355
5698
  function parsePicture(el, ctx) {
5356
5699
  const opts = {};
5357
5700
  const spPr = findChild(el, "p:spPr");
@@ -5604,6 +5947,24 @@ function parseTableFrame(el, tbl) {
5604
5947
  if (attrBool(tblPr, "firstCol")) opts.firstCol = true;
5605
5948
  if (attrBool(tblPr, "lastCol")) opts.lastCol = true;
5606
5949
  if (attrBool(tblPr, "bandCol")) opts.bandCol = true;
5950
+ const borders = {};
5951
+ for (const [elName, key] of [
5952
+ ["a:lnL", "left"],
5953
+ ["a:lnR", "right"],
5954
+ ["a:lnT", "top"],
5955
+ ["a:lnB", "bottom"]
5956
+ ]) {
5957
+ const borderEl = findChild(tblPr, elName);
5958
+ if (borderEl) {
5959
+ const borderOpts = {};
5960
+ const w = attrNum(borderEl, "w");
5961
+ if (w !== void 0) borderOpts.width = w;
5962
+ const fill = parseFillFromElement(borderEl);
5963
+ if (typeof fill === "string") borderOpts.color = fill;
5964
+ if (Object.keys(borderOpts).length > 0) borders[key] = borderOpts;
5965
+ }
5966
+ }
5967
+ if (Object.keys(borders).length > 0) opts.borders = borders;
5607
5968
  }
5608
5969
  const tblGrid = findChild(tbl, "a:tblGrid");
5609
5970
  if (tblGrid) {
@@ -6027,6 +6388,34 @@ function parseFillFromElement(parent) {
6027
6388
  };
6028
6389
  }
6029
6390
  if (findChild(parent, "a:noFill")) return { type: "none" };
6391
+ const pattFill = findChild(parent, "a:pattFill");
6392
+ if (pattFill) {
6393
+ const result = {
6394
+ type: "pattern",
6395
+ pattern: attr(pattFill, "prst") ?? ""
6396
+ };
6397
+ const fgClr = findChild(pattFill, "a:fgClr");
6398
+ if (fgClr) {
6399
+ const srgbClr = findChild(fgClr, "a:srgbClr");
6400
+ if (srgbClr) {
6401
+ const val = attr(srgbClr, "val");
6402
+ if (val) result.foregroundColor = val;
6403
+ }
6404
+ }
6405
+ const bgClr = findChild(pattFill, "a:bgClr");
6406
+ if (bgClr) {
6407
+ const srgbClr = findChild(bgClr, "a:srgbClr");
6408
+ if (srgbClr) {
6409
+ const val = attr(srgbClr, "val");
6410
+ if (val) result.backgroundColor = val;
6411
+ }
6412
+ }
6413
+ return result;
6414
+ }
6415
+ const blipFill = findChild(parent, "a:blipFill");
6416
+ if (blipFill) {
6417
+ if (findChild(blipFill, "a:blip")) return { type: "blip" };
6418
+ }
6030
6419
  const gradFill = findChild(parent, "a:gradFill");
6031
6420
  if (gradFill) {
6032
6421
  const stops = [];
@@ -6068,6 +6457,11 @@ function parseOutlineFromElement(parent) {
6068
6457
  if (val) opts.color = val;
6069
6458
  }
6070
6459
  }
6460
+ const prstDash = findChild(ln, "a:prstDash");
6461
+ if (prstDash) {
6462
+ const val = attr(prstDash, "val");
6463
+ if (val) opts.dashStyle = val;
6464
+ }
6071
6465
  return Object.keys(opts).length > 0 ? opts : void 0;
6072
6466
  }
6073
6467
  function parseEffectsFromElement(parent) {
@@ -6209,6 +6603,96 @@ function imageTypeFromPath(path) {
6209
6603
  default: return "png";
6210
6604
  }
6211
6605
  }
6606
+ const VIDEO_EXT_URI = "{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}";
6607
+ const AUDIO_EXT_URI = "{CF1602FD-DB20-4165-A070-5F299619DA56}";
6608
+ /**
6609
+ * Check if a p:pic element is actually a video or audio frame
6610
+ * by looking for media extension URIs in nvPr > extLst.
6611
+ */
6612
+ function detectMediaType(el) {
6613
+ const nvPicPr = findChild(el, "p:nvPicPr");
6614
+ if (!nvPicPr) return void 0;
6615
+ const nvPr = findChild(nvPicPr, "p:nvPr");
6616
+ if (!nvPr) return void 0;
6617
+ const extLst = findChild(nvPr, "p:extLst");
6618
+ if (!extLst) return void 0;
6619
+ for (const ext of extLst.elements ?? []) {
6620
+ if (ext.name !== "p:ext") continue;
6621
+ const uri = attr(ext, "uri");
6622
+ if (uri === VIDEO_EXT_URI) return "video";
6623
+ if (uri === AUDIO_EXT_URI) return "audio";
6624
+ }
6625
+ }
6626
+ function parseMediaFrame(el, ctx, mediaKind) {
6627
+ const opts = {};
6628
+ const spPr = findChild(el, "p:spPr");
6629
+ if (spPr) parseTransformFromSpPr(spPr, opts);
6630
+ const nvPicPr = findChild(el, "p:nvPicPr");
6631
+ if (nvPicPr) {
6632
+ const cNvPr = findChild(nvPicPr, "p:cNvPr");
6633
+ if (cNvPr) {
6634
+ const name = attr(cNvPr, "name");
6635
+ if (name) opts.name = name;
6636
+ }
6637
+ }
6638
+ const blip = findDeep(el, "a:blip")[0];
6639
+ if (blip) {
6640
+ const rEmbed = attr(blip, "r:embed");
6641
+ if (rEmbed) {
6642
+ const mediaPath = ctx.slideRels.get(rEmbed);
6643
+ if (mediaPath) {
6644
+ const data = ctx.pptx.doc.getRaw(mediaPath);
6645
+ if (data) {
6646
+ opts.data = data;
6647
+ opts.type = mediaTypeFromPath(mediaPath, mediaKind);
6648
+ }
6649
+ }
6650
+ }
6651
+ }
6652
+ if (mediaKind === "video") {
6653
+ const blipFill = findChild(el, "p:blipFill");
6654
+ if (blipFill) {
6655
+ const posterBlip = findChild(blipFill, "a:blip");
6656
+ if (posterBlip) {
6657
+ const rEmbed = attr(posterBlip, "r:embed");
6658
+ if (rEmbed) {
6659
+ const posterPath = ctx.slideRels.get(rEmbed);
6660
+ if (posterPath) {
6661
+ const posterData = ctx.pptx.doc.getRaw(posterPath);
6662
+ if (posterData) {
6663
+ opts.poster = posterData;
6664
+ opts.posterType = imageTypeFromPath(posterPath);
6665
+ }
6666
+ }
6667
+ }
6668
+ }
6669
+ }
6670
+ }
6671
+ return opts;
6672
+ }
6673
+ function mediaTypeFromPath(path, kind) {
6674
+ const ext = path.split(".").pop()?.toLowerCase() ?? "";
6675
+ if (kind === "video") switch (ext) {
6676
+ case "mp4": return "mp4";
6677
+ case "mov":
6678
+ case "qt": return "mov";
6679
+ case "wmv": return "wmv";
6680
+ case "avi": return "avi";
6681
+ default: return "mp4";
6682
+ }
6683
+ else switch (ext) {
6684
+ case "mp3": return "mp3";
6685
+ case "wav": return "wav";
6686
+ case "wma": return "wma";
6687
+ case "aac": return "aac";
6688
+ default: return "mp3";
6689
+ }
6690
+ }
6691
+ /**
6692
+ * Parse a:blipFill from a shape's spPr.
6693
+ * Used when a shape has an image fill (not a picture element).
6694
+ * Returns { type: "blip", data } if found.
6695
+ */
6212
6696
  //#endregion
6213
6697
  //#region src/parse/slide-layout.ts
6214
6698
  const NAME_TO_TYPE = {
@@ -6361,7 +6845,7 @@ function parseSlideRels(doc, slidePaths, refs) {
6361
6845
  }
6362
6846
  }
6363
6847
  function parsePptx(data) {
6364
- const doc = parseArchive(data);
6848
+ const doc = parseArchive(toUint8Array(data));
6365
6849
  const presentation = doc.get("ppt/presentation.xml");
6366
6850
  const relsXml = doc.get("ppt/_rels/presentation.xml.rels");
6367
6851
  const slides = [];
@@ -6535,12 +7019,27 @@ function parsePresentation(data) {
6535
7019
  masterDefs.push(masterDef);
6536
7020
  }
6537
7021
  if (masterCount > 1) opts.masters = masterDefs;
7022
+ const commentAuthors = /* @__PURE__ */ new Map();
7023
+ if (pptx.partRefs.commentAuthors) {
7024
+ const authorsEl = pptx.doc.get(pptx.partRefs.commentAuthors);
7025
+ if (authorsEl) for (const child of authorsEl.elements ?? []) {
7026
+ if (child.name !== "p:cmAuthor") continue;
7027
+ const id = attrNum(child, "id");
7028
+ const name = attr(child, "name");
7029
+ const initials = attr(child, "initials");
7030
+ if (id !== void 0) commentAuthors.set(id, {
7031
+ name: name ?? "",
7032
+ initials: initials ?? ""
7033
+ });
7034
+ }
7035
+ }
6538
7036
  const result = [];
6539
7037
  for (let si = 0; si < pptx.slides.length; si++) {
6540
7038
  const slidePath = pptx.slides[si];
6541
7039
  const slideEl = pptx.doc.get(slidePath);
6542
7040
  if (!slideEl) continue;
6543
- const slideOpts = parseSlide(slideEl, new ParseContext(pptx, parseSlideRelMap(pptx.doc, slidePath)));
7041
+ const slideRels = parseSlideRelMap(pptx.doc, slidePath);
7042
+ const slideOpts = parseSlide(slideEl, new ParseContext(pptx, slideRels));
6544
7043
  const layoutPath = slideLayoutPaths.get(slidePath);
6545
7044
  if (layoutPath) {
6546
7045
  const layoutEl = pptx.doc.get(layoutPath);
@@ -6551,11 +7050,57 @@ function parsePresentation(data) {
6551
7050
  if (masterIdx >= 0 && masterDefs[masterIdx]) slideOpts.master = masterDefs[masterIdx].name;
6552
7051
  }
6553
7052
  }
7053
+ for (const [, relPath] of slideRels) {
7054
+ if (!relPath.includes("/comments/")) continue;
7055
+ const commentsEl = pptx.doc.get(relPath);
7056
+ if (!commentsEl) continue;
7057
+ const comments = [];
7058
+ for (const cm of commentsEl.elements ?? []) {
7059
+ if (cm.name !== "p:cm") continue;
7060
+ const authorId = attrNum(cm, "authorId");
7061
+ const dt = attr(cm, "dt");
7062
+ const idx = attrNum(cm, "idx");
7063
+ const author = authorId !== void 0 ? commentAuthors.get(authorId) : void 0;
7064
+ const pos = findChild(cm, "p:pos");
7065
+ const x = pos ? attrNum(pos, "x") : void 0;
7066
+ const y = pos ? attrNum(pos, "y") : void 0;
7067
+ const txBody = findChild(cm, "p:txBody");
7068
+ const text = txBody ? extractCommentText(txBody) : "";
7069
+ const commentEntry = {};
7070
+ if (authorId !== void 0) commentEntry.authorId = authorId;
7071
+ if (author) commentEntry.author = author.name;
7072
+ if (idx !== void 0) commentEntry.idx = idx;
7073
+ if (dt) commentEntry.date = dt;
7074
+ if (x !== void 0) commentEntry.x = x;
7075
+ if (y !== void 0) commentEntry.y = y;
7076
+ if (text) commentEntry.text = text;
7077
+ comments.push(commentEntry);
7078
+ }
7079
+ if (comments.length > 0) slideOpts.comments = comments;
7080
+ break;
7081
+ }
6554
7082
  result.push(slideOpts);
6555
7083
  }
6556
7084
  opts.slides = result;
6557
7085
  return opts;
6558
7086
  }
7087
+ /**
7088
+ * Extract plain text from a p:txBody in a comment element.
7089
+ */
7090
+ function extractCommentText(txBody) {
7091
+ const parts = [];
7092
+ for (const p of txBody.elements ?? []) {
7093
+ if (p.name !== "a:p") continue;
7094
+ for (const r of p.elements ?? []) {
7095
+ if (r.name !== "a:r") continue;
7096
+ for (const t of r.elements ?? []) if (t.name === "a:t") {
7097
+ const text = textOf(t);
7098
+ if (text) parts.push(text);
7099
+ }
7100
+ }
7101
+ }
7102
+ return parts.join("");
7103
+ }
6559
7104
  __reExport(/* @__PURE__ */ __exportAll({
6560
7105
  AppProperties: () => AppProperties,
6561
7106
  AudioFrame: () => AudioFrame,