@shbernal/ts-pptx 4.0.1 → 4.2.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.
Files changed (139) hide show
  1. package/README.md +147 -114
  2. package/dist/anim-presets-Qbgh8uut.js +55 -0
  3. package/dist/anim-presets-Qbgh8uut.js.map +1 -0
  4. package/dist/{autopage-odPz7zZO.js → autopage-DqZ37VpL.js} +11 -12
  5. package/dist/autopage-DqZ37VpL.js.map +1 -0
  6. package/dist/{binary-string-CKVAYI3M.js → binary-string-Ba8VM3b9.js} +3 -4
  7. package/dist/binary-string-Ba8VM3b9.js.map +1 -0
  8. package/dist/{body-insets-Dw_BeWD_.js → body-insets-BDHymJvz.js} +7 -9
  9. package/dist/body-insets-BDHymJvz.js.map +1 -0
  10. package/dist/browser.d.ts +24 -16
  11. package/dist/browser.d.ts.map +1 -1
  12. package/dist/browser.js +19 -19
  13. package/dist/browser.js.map +1 -1
  14. package/dist/{image-size-CvN2iCA4.js → chart-metadata-CwiTTcJq.js} +634 -537
  15. package/dist/chart-metadata-CwiTTcJq.js.map +1 -0
  16. package/dist/{clip-Bl01BPcN.d.ts → clip-ISfCMtWA.d.ts} +6 -10
  17. package/dist/clip-ISfCMtWA.d.ts.map +1 -0
  18. package/dist/{notes-Dh12JAbn.js → comments-CKIZYRbZ.js} +530 -401
  19. package/dist/comments-CKIZYRbZ.js.map +1 -0
  20. package/dist/{diagnostics-D8ot4Ei7.d.ts → diagnostics-MoA4MBq5.d.ts} +55 -53
  21. package/dist/diagnostics-MoA4MBq5.d.ts.map +1 -0
  22. package/dist/{embedded-fonts-DarT9aoa.d.ts → embedded-fonts-U7HIIhPO.d.ts} +4 -6
  23. package/dist/embedded-fonts-U7HIIhPO.d.ts.map +1 -0
  24. package/dist/{entry-compose-DCnvBZz2.d.ts → entry-compose-CiakSt-f.d.ts} +62 -81
  25. package/dist/entry-compose-CiakSt-f.d.ts.map +1 -0
  26. package/dist/{errors-C4oENPcu.js → entry-errors-JMthgJ2g.js} +45 -29
  27. package/dist/entry-errors-JMthgJ2g.js.map +1 -0
  28. package/dist/{table-cell-margins-EVg0EwxT.js → enums-BP9wsb9Q.js} +44 -47
  29. package/dist/enums-BP9wsb9Q.js.map +1 -0
  30. package/dist/{enums-BUKTUB6V.d.ts → enums-PcXr3o3J.d.ts} +13 -14
  31. package/dist/{enums-BUKTUB6V.d.ts.map → enums-PcXr3o3J.d.ts.map} +1 -1
  32. package/dist/families.d.ts +24 -26
  33. package/dist/families.d.ts.map +1 -1
  34. package/dist/families.js +3 -4
  35. package/dist/{fit-Cb7naFj6.js → fit-Bpmzep6O.js} +64 -56
  36. package/dist/fit-Bpmzep6O.js.map +1 -0
  37. package/dist/{font-metrics-v5fn7s7e.d.ts → font-metrics-DTppw4t3.d.ts} +62 -38
  38. package/dist/font-metrics-DTppw4t3.d.ts.map +1 -0
  39. package/dist/{grid-DikTjFey.js → grid-DUIMxjfl.js} +23 -26
  40. package/dist/grid-DUIMxjfl.js.map +1 -0
  41. package/dist/{html-dom-Iuog2wdK.js → html-dom-LZCgntnJ.js} +46 -47
  42. package/dist/html-dom-LZCgntnJ.js.map +1 -0
  43. package/dist/html.d.ts +53 -9
  44. package/dist/html.d.ts.map +1 -1
  45. package/dist/html.js +12 -13
  46. package/dist/html.js.map +1 -1
  47. package/dist/index.d.ts +21 -13
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +15 -15
  50. package/dist/index.js.map +1 -1
  51. package/dist/inspect.d.ts +16 -18
  52. package/dist/inspect.d.ts.map +1 -1
  53. package/dist/inspect.js +18 -19
  54. package/dist/inspect.js.map +1 -1
  55. package/dist/{text-anchor-CrYXAfUu.js → lock-attrs-CSQnFDHP.js} +62 -12
  56. package/dist/lock-attrs-CSQnFDHP.js.map +1 -0
  57. package/dist/math.d.ts +2 -4
  58. package/dist/math.d.ts.map +1 -1
  59. package/dist/math.js +12 -13
  60. package/dist/math.js.map +1 -1
  61. package/dist/measure.d.ts +23 -12
  62. package/dist/measure.d.ts.map +1 -1
  63. package/dist/measure.js +3 -3
  64. package/dist/node.d.ts +21 -13
  65. package/dist/node.d.ts.map +1 -1
  66. package/dist/node.js +12 -12
  67. package/dist/node.js.map +1 -1
  68. package/dist/{options-internal-WqgMf720.js → options-internal-BWgmLPdx.js} +4 -5
  69. package/dist/options-internal-BWgmLPdx.js.map +1 -0
  70. package/dist/{package-J49Ck6S2.d.ts → package-CANBWuSm.d.ts} +9 -13
  71. package/dist/package-CANBWuSm.d.ts.map +1 -0
  72. package/dist/{playbtn-CUqQdoDf.js → playbtn-BQZFJir9.js} +4 -5
  73. package/dist/playbtn-BQZFJir9.js.map +1 -0
  74. package/dist/{pres-C6qgOFAa.d.ts → pres-BkEkUAEM.d.ts} +410 -237
  75. package/dist/pres-BkEkUAEM.d.ts.map +1 -0
  76. package/dist/{presentation-DGYlAWhl.js → presentation-4Ggq0_Xa.js} +470 -315
  77. package/dist/presentation-4Ggq0_Xa.js.map +1 -0
  78. package/dist/{presentation-types-8jLQHI4M.d.ts → presentation-types-BgnZR-0a.d.ts} +1119 -468
  79. package/dist/presentation-types-BgnZR-0a.d.ts.map +1 -0
  80. package/dist/read.d.ts +18 -13
  81. package/dist/read.d.ts.map +1 -1
  82. package/dist/read.js +4 -4
  83. package/dist/script.d.ts +116 -65
  84. package/dist/script.d.ts.map +1 -1
  85. package/dist/script.js +2700 -780
  86. package/dist/script.js.map +1 -1
  87. package/dist/{shapes-re0KyQk_.js → shapes-CAPOjU0Q.js} +1564 -491
  88. package/dist/shapes-CAPOjU0Q.js.map +1 -0
  89. package/dist/{slide-Dx2ZeVZ6.d.ts → slide-BmT4kMoO.d.ts} +102 -89
  90. package/dist/slide-BmT4kMoO.d.ts.map +1 -0
  91. package/dist/{slide-size-BalTjFuv.js → slide-size-SkScz1ig.js} +2 -3
  92. package/dist/{slide-size-BalTjFuv.js.map → slide-size-SkScz1ig.js.map} +1 -1
  93. package/dist/{table-dom-BTDP3lEP.js → table-dom-BYga4FYY.js} +4 -5
  94. package/dist/{table-dom-BTDP3lEP.js.map → table-dom-BYga4FYY.js.map} +1 -1
  95. package/dist/{text-sN64Otf_.js → text-CU08xJGs.js} +393 -344
  96. package/dist/text-CU08xJGs.js.map +1 -0
  97. package/dist/{transition-speed-kUEWDWTx.js → transition-speed-CNJ0fSpQ.js} +2 -3
  98. package/dist/{transition-speed-kUEWDWTx.js.map → transition-speed-CNJ0fSpQ.js.map} +1 -1
  99. package/dist/{units-internal-XJlf-hOl.js → units-internal-BR3a4cIi.js} +34 -32
  100. package/dist/units-internal-BR3a4cIi.js.map +1 -0
  101. package/dist/{zip-CKd8ihcf.js → zip-7JTh61LX.js} +8 -10
  102. package/dist/zip-7JTh61LX.js.map +1 -0
  103. package/dist/{zip-C0leZZMj.d.ts → zip-uUjs9Cqx.d.ts} +4 -6
  104. package/dist/zip-uUjs9Cqx.d.ts.map +1 -0
  105. package/dist/zip.d.ts +3 -3
  106. package/dist/zip.js +3 -3
  107. package/dist/{zoom-DPUHh85d.js → zoom-CSSeOwM8.js} +1204 -620
  108. package/dist/zoom-CSSeOwM8.js.map +1 -0
  109. package/package.json +27 -28
  110. package/skills/{ts-pptx-upstream → pptx-ts-upstream}/SKILL.md +37 -84
  111. package/dist/autopage-odPz7zZO.js.map +0 -1
  112. package/dist/binary-string-CKVAYI3M.js.map +0 -1
  113. package/dist/body-insets-Dw_BeWD_.js.map +0 -1
  114. package/dist/clip-Bl01BPcN.d.ts.map +0 -1
  115. package/dist/diagnostics-D8ot4Ei7.d.ts.map +0 -1
  116. package/dist/embedded-fonts-DarT9aoa.d.ts.map +0 -1
  117. package/dist/entry-compose-DCnvBZz2.d.ts.map +0 -1
  118. package/dist/errors-C4oENPcu.js.map +0 -1
  119. package/dist/fit-Cb7naFj6.js.map +0 -1
  120. package/dist/font-metrics-v5fn7s7e.d.ts.map +0 -1
  121. package/dist/grid-DikTjFey.js.map +0 -1
  122. package/dist/html-dom-Iuog2wdK.js.map +0 -1
  123. package/dist/image-size-CvN2iCA4.js.map +0 -1
  124. package/dist/notes-Dh12JAbn.js.map +0 -1
  125. package/dist/options-internal-WqgMf720.js.map +0 -1
  126. package/dist/package-J49Ck6S2.d.ts.map +0 -1
  127. package/dist/playbtn-CUqQdoDf.js.map +0 -1
  128. package/dist/pres-C6qgOFAa.d.ts.map +0 -1
  129. package/dist/presentation-DGYlAWhl.js.map +0 -1
  130. package/dist/presentation-types-8jLQHI4M.d.ts.map +0 -1
  131. package/dist/shapes-re0KyQk_.js.map +0 -1
  132. package/dist/slide-Dx2ZeVZ6.d.ts.map +0 -1
  133. package/dist/table-cell-margins-EVg0EwxT.js.map +0 -1
  134. package/dist/text-anchor-CrYXAfUu.js.map +0 -1
  135. package/dist/text-sN64Otf_.js.map +0 -1
  136. package/dist/units-internal-XJlf-hOl.js.map +0 -1
  137. package/dist/zip-C0leZZMj.d.ts.map +0 -1
  138. package/dist/zip-CKd8ihcf.js.map +0 -1
  139. package/dist/zoom-DPUHh85d.js.map +0 -1
@@ -1,18 +1,19 @@
1
- import { i as PackageReadError, l as warn, n as InvalidOptionError, o as UnsupportedFeatureError, t as InternalError } from "./errors-C4oENPcu.js";
2
- import { F as EMU_PER_INCH, I as EMU_PER_POINT, P as ptToHundredths, b as FIXED_PCT_PER_PERCENT, k as emuToPoints, v as ANGLE_UNITS_PER_DEGREE, w as PERCENT_SCALE } from "./table-cell-margins-EVg0EwxT.js";
3
- import { $t as THEME_REL, E as EMBEDDED_FONT_SLOTS, Ft as OFFICE_DOCUMENT_REL, G as TEXT_HORZ_OVERFLOW, H as TEXT_ANCHORS, I as PRESET_LINE_DASHES, Jt as SLIDE_MASTER_REL, Kt as SLIDE_LAYOUT_REL, S as COLOR_MAP_TOKENS, V as SCHEME_COLOR_VALUES, X as TEXT_VERTICAL, Xt as TABLE_STYLES_CONTENT_TYPE, Y as TEXT_UNDERLINE_TYPES, Z as THEME_COLOR_SLOTS, c as checkEnumOrThrow, h as stripHash, in as OOXML_NS, jt as NOTES_MASTER_REL, on as TABLE_GRAPHIC_DATA_URI, p as isHexColor, pn as imageFormatForContentType, r as getImageSizeFromBytes, t as fitSrcRectPercents, u as boolValue, wt as IMAGE_REL } from "./image-size-CvN2iCA4.js";
1
+ import { a as PackageReadError, n as InternalError, r as InvalidOptionError, s as UnsupportedFeatureError, u as warn } from "./entry-errors-JMthgJ2g.js";
2
+ import { Ht as SCHEME_COLOR_VALUES, Kt as TEXT_HORZ_OVERFLOW, Lt as PRESET_LINE_DASHES, M as IMAGE_REL, Qt as THEME_COLOR_SLOTS, Tt as EMBEDDED_FONT_SLOTS, U as OFFICE_DOCUMENT_REL, Ut as TEXT_ANCHORS, Xt as TEXT_UNDERLINE_TYPES, Zt as TEXT_VERTICAL, a as getImageSizeFromBytes, dn as stripHash, et as SLIDE_LAYOUT_REL, ft as OOXML_NS, in as checkEnumOrThrow, it as TABLE_STYLES_CONTENT_TYPE, ln as isHexColor, mt as TABLE_GRAPHIC_DATA_URI, nt as SLIDE_MASTER_REL, on as boolValue, r as fitSrcRectPercents, st as THEME_REL, t as CHART_METADATA_EXT_URI, vn as imageFormatForContentType, xt as COLOR_MAP_TOKENS, z as NOTES_MASTER_REL } from "./chart-metadata-CwiTTcJq.js";
3
+ import { F as EMU_PER_INCH, I as EMU_PER_POINT, P as ptToHundredths, b as FIXED_PCT_PER_PERCENT, k as emuToPoints, v as ANGLE_UNITS_PER_DEGREE, w as PERCENT_SCALE } from "./enums-BP9wsb9Q.js";
4
4
  import { DOMParser, MIME_TYPE, XMLSerializer, onErrorStopParsing } from "@xmldom/xmldom";
5
5
  /** Parse an XML string strictly: any well-formedness error throws. */
6
6
  function parseXml(xml) {
7
7
  return new DOMParser({ onError: onErrorStopParsing }).parseFromString(xml, MIME_TYPE.XML_TEXT);
8
8
  }
9
9
  /**
10
- * Serialize a Document to an XML string. Does not pretty-print or normalize
11
- * whitespace. The XML declaration is not part of the DOM; callers that need
12
- * it (see `Part.serialize`) must prepend it themselves.
10
+ * Serialize a Document, or one element of it, to an XML string. Does not pretty-print or
11
+ * normalize whitespace. The XML declaration is not part of the DOM; callers that need it (see
12
+ * `Part.serialize`) must prepend it themselves. An element is written with the namespace
13
+ * declarations its subtree uses but inherits from an ancestor, so it stands alone.
13
14
  */
14
- function serializeXml(doc) {
15
- return new XMLSerializer().serializeToString(doc);
15
+ function serializeXml(node) {
16
+ return new XMLSerializer().serializeToString(node);
16
17
  }
17
18
  function splitQName(qname) {
18
19
  const colon = qname.indexOf(":");
@@ -49,7 +50,7 @@ function childElements(parent) {
49
50
  * All *descendant* elements of `root` matching a namespace + local name, snapshotted into an
50
51
  * array. Unlike {@link getElements} (direct children only) this searches the whole subtree, and
51
52
  * unlike the live `HTMLCollection` that `getElementsByTagNameNS` returns, the result does not
52
- * change as the caller mutates the tree — which is what makes it safe to iterate while replacing
53
+ * change as the caller mutates the tree, which is what makes it safe to iterate while replacing
53
54
  * the very elements being iterated.
54
55
  *
55
56
  * Note the DOM's own exclusion: `root` itself is never a candidate, only its descendants.
@@ -137,7 +138,7 @@ function removeAttr(element, qname) {
137
138
  * Get the first child element matching `qname`, creating and inserting it when
138
139
  * absent. A newly created element is inserted before the first existing child
139
140
  * whose qname appears in `before` (the schema successors of the new element),
140
- * or appended when none are present — keeping the parent's children in the
141
+ * or appended when none are present, keeping the parent's children in the
141
142
  * sequence order OOXML mandates.
142
143
  */
143
144
  function getOrAddChild(parent, qname, before = []) {
@@ -207,7 +208,7 @@ function boolAttr(element, qname) {
207
208
  *
208
209
  * `a:ST_Percentage` is a *union* in the Transitional profile: the fixed-point integer form
209
210
  * Office writes (`100%` → `100000`) and a decimal string with a literal `%` (`-?[0-9]+(\.[0-9]+)?%`),
210
- * which is the only form the Strict profile has. Both are read here — a reader that took only the
211
+ * which is the only form the Strict profile has. Both are read here. A reader that took only the
211
212
  * first dropped a schema-legal value silently, and the two are one `endsWith` apart.
212
213
  * @param value - the raw attribute value, or `null` when the attribute is absent
213
214
  */
@@ -262,8 +263,7 @@ function concatDrawingMLText(rich) {
262
263
  for (const t of rich.getElementsByTagNameNS(OOXML_NS.a, "t")) out += t.textContent ?? "";
263
264
  return out === "" ? null : out;
264
265
  }
265
- //#endregion
266
- //#region src/read/opc/partnames.ts
266
+ // src/read/opc/partnames.ts
267
267
  /**
268
268
  * OPC partname helpers (ECMA-376 Part 2 pack-URI grammar).
269
269
  *
@@ -272,8 +272,8 @@ function concatDrawingMLText(rich) {
272
272
  */
273
273
  /**
274
274
  * Resolve the single relationship of `type` owned by `partName` to its target partname, or
275
- * `null` when the part declares none. For the one-of-a-kind links in a deck's spine — a slide to
276
- * its layout, a layout to its master, a master to its theme — where a second relationship of the
275
+ * `null` when the part declares none. For the one-of-a-kind links in a deck's spine (a slide to
276
+ * its layout, a layout to its master, a master to its theme), where a second relationship of the
277
277
  * same type would itself be malformed.
278
278
  *
279
279
  * The `OpcPackage` import is type-only, so this does not create a cycle with `package.ts` (which
@@ -289,7 +289,7 @@ function resolveSingleRel(opc, partName, type) {
289
289
  *
290
290
  * Ten sites in the API layer wrote the `resolveTarget` + `opc.part` pair out; two more wrote
291
291
  * the `byType(T)[0]` that precedes it (see {@link singleRelPart}). `resolveTarget` keeps its
292
- * throw on an id the part does not declare — a dangling `r:id` is a malformed package, not a
292
+ * throw on an id the part does not declare: a dangling `r:id` is a malformed package, not a
293
293
  * missing part, and the graphic-frame accessors rely on the distinction.
294
294
  */
295
295
  function relPart(opc, rels, relId) {
@@ -321,7 +321,7 @@ function resolvePartName(relId, rels) {
321
321
  * {@link resolveSingleRel}, resolved the rest of the way to the {@link Part}.
322
322
  *
323
323
  * For the one-of-a-kind links in a deck's spine, where the caller wants the part rather than
324
- * its name — a slide to its notes slide or its layout, a layout to its master, a part to its
324
+ * its name: a slide to its notes slide or its layout, a layout to its master, a part to its
325
325
  * `core.xml` or `app.xml`.
326
326
  */
327
327
  function singleRelPart(opc, partName, type) {
@@ -337,7 +337,7 @@ function partNameToZipPath(partName) {
337
337
  }
338
338
  /**
339
339
  * Lowercased extension without the dot, or `''` when the part has none.
340
- * Per OPC, the extension is everything after the last dot — so the leading
340
+ * Per OPC, the extension is everything after the last dot, so the leading
341
341
  * dot of `/_rels/.rels` still yields `rels`.
342
342
  */
343
343
  function partNameExtension(partName) {
@@ -352,7 +352,7 @@ function relsPartNameFor(sourcePartName) {
352
352
  }
353
353
  /**
354
354
  * Build a relationship `Target` for `targetPartName` relative to its source
355
- * part's directory — the inverse of {@link resolveRelativePartName}. Both names
355
+ * part's directory. It is the inverse of {@link resolveRelativePartName}. Both names
356
356
  * are absolute partnames. E.g. source `/ppt/slides/slide1.xml`, target
357
357
  * `/ppt/media/image1.png` → `../media/image1.png`.
358
358
  */
@@ -384,15 +384,14 @@ function resolveRelativePartName(sourcePartName, target) {
384
384
  }
385
385
  return `/${segments.join("/")}`;
386
386
  }
387
- //#endregion
388
- //#region src/ooxml/sequence.ts
387
+ // src/ooxml/sequence.ts
389
388
  /**
390
389
  * Schema child-sequence order for the OOXML complexTypes both halves of the library insert into.
391
390
  *
392
391
  * **What these are for.** `getOrAddChild(parent, qname, before)` / `insertInOrder(parent, node,
393
392
  * before)` place a newly created child before the first sibling named in `before`. So `before`
394
393
  * must list exactly the children that legally *follow* the one being inserted. Get it wrong and
395
- * the part is schema-invalid — which PowerPoint reports as a corrupt file, not as a bad option,
394
+ * the part is schema-invalid, which PowerPoint reports as a corrupt file, not as a bad option,
396
395
  * so the failure surfaces far from its cause with no compile-time signal.
397
396
  *
398
397
  * **Why they are derived, not written out.** These successor lists used to be hand-maintained,
@@ -409,7 +408,7 @@ function resolveRelativePartName(sourcePartName, target) {
409
408
  * both are bound by the same ECMA-376 sequence.
410
409
  */
411
410
  /**
412
- * `EG_FillProperties` — the mutually exclusive fill choices, in schema order.
411
+ * `EG_FillProperties`: the mutually exclusive fill choices, in schema order.
413
412
  * @see ECMA-376 Part 1 §20.1.8 (DrawingML fill group)
414
413
  */
415
414
  const FILL_CHOICES = [
@@ -420,16 +419,16 @@ const FILL_CHOICES = [
420
419
  "a:pattFill",
421
420
  "a:grpFill"
422
421
  ];
423
- /** `EG_EffectProperties` — the two mutually exclusive effect containers. */
422
+ /** `EG_EffectProperties`: the two mutually exclusive effect containers. */
424
423
  const EFFECT_CHOICES = ["a:effectLst", "a:effectDag"];
425
- /** `EG_Geometry` — the two mutually exclusive geometry choices. */
424
+ /** `EG_Geometry`: the two mutually exclusive geometry choices. */
426
425
  const GEOMETRY_CHOICES = ["a:custGeom", "a:prstGeom"];
427
426
  /**
428
427
  * Everything that legally follows `member` in `sequence`, flattened.
429
428
  *
430
429
  * `member` may name a single element or any member of a choice group; either way the result
431
430
  * starts after the step it occupies, so a choice member never lists its own siblings. Throws on
432
- * an unknown name rather than returning `[]` — an empty successor list silently *appends*, which
431
+ * an unknown name rather than returning `[]`. An empty successor list silently *appends*, which
433
432
  * is exactly the corruption these lists exist to prevent, so a typo must fail loudly and at
434
433
  * module-load time rather than produce a subtly out-of-order part at run time.
435
434
  */
@@ -456,7 +455,7 @@ const SPPR_SEQUENCE = [
456
455
  * `CT_Shape` (`p:sp`), in declaration order.
457
456
  *
458
457
  * `p:pic` (`CT_Picture`) and `p:cxnSp` (`CT_Connector`) are not this type, but their successors
459
- * of `p:spPr` are a *subset* of this one's in the same relative order — both run `p:spPr`,
458
+ * of `p:spPr` are a *subset* of this one's in the same relative order: both run `p:spPr`,
460
459
  * `p:style`, `p:extLst` and neither has a `p:txBody`. So {@link SHAPE_AFTER_SPPR} serves all
461
460
  * three: naming an element that cannot occur in a given parent is inert, because the successor
462
461
  * list is only ever matched against children that are actually there.
@@ -471,7 +470,7 @@ const SP_SEQUENCE = [
471
470
  ];
472
471
  /**
473
472
  * `CT_GroupShapeProperties` (`p:grpSpPr`), in declaration order. A group carries no `a:ln` and
474
- * no `a:sp3d` — which is exactly why it needs its own successor lists rather than borrowing the
473
+ * no `a:sp3d`, which is exactly why it needs its own successor lists rather than borrowing the
475
474
  * shape ones.
476
475
  * @see ECMA-376 Part 1 §20.1.2.2.19
477
476
  */
@@ -557,7 +556,7 @@ const TCPR_SEQUENCE = [
557
556
  "a:extLst"
558
557
  ];
559
558
  /**
560
- * For each `a:tcPr` child, the children that must follow it — i.e. what a new one is inserted
559
+ * For each `a:tcPr` child, the children that must follow it, i.e. what a new one is inserted
561
560
  * *before*. Every child gets an entry, so a caller can look one up by qname.
562
561
  */
563
562
  const TCPR_AFTER = Object.fromEntries(TCPR_SEQUENCE.map((qname, index) => [qname, TCPR_SEQUENCE.slice(index + 1)]));
@@ -578,13 +577,13 @@ const GRPSPPR_AFTER_XFRM = successorsOf(GRPSPPR_SEQUENCE, "a:xfrm");
578
577
  /** Successors of a fill choice inside `p:grpSpPr`. */
579
578
  const GRPSPPR_FILL_AFTER = successorsOf(GRPSPPR_SEQUENCE, "a:solidFill");
580
579
  /**
581
- * Successors of `p:grpSpPr` within `p:grpSp` — every drawable child, plus `p:contentPart` and
580
+ * Successors of `p:grpSpPr` within `p:grpSp`: every drawable child, plus `p:contentPart` and
582
581
  * `p:extLst`. Derived rather than written out, which is the whole point of this module: the
583
582
  * literal it replaces named five of the seven.
584
583
  */
585
584
  const GROUP_AFTER_GRPSPPR = successorsOf(GROUP_SHAPE_SEQUENCE, "p:grpSpPr");
586
585
  /**
587
- * Successors of `p:spPr` within `p:sp`, and equally within `p:pic` / `p:cxnSp` — see
586
+ * Successors of `p:spPr` within `p:sp`, and equally within `p:pic` / `p:cxnSp`. See
588
587
  * {@link SP_SEQUENCE} for why one list covers all three. `p:blipFill` and the `p:nv*Pr` wrapper
589
588
  * precede `p:spPr`, so slicing the sequence drops them without anyone having to remember to.
590
589
  */
@@ -601,8 +600,8 @@ const RPR_FILL_AFTER = successorsOf(RPR_SEQUENCE, "a:solidFill");
601
600
  /** Successors of `a:latin` inside `a:rPr` (and `a:defRPr`/`a:endParaRPr`). */
602
601
  const RPR_LATIN_AFTER = successorsOf(RPR_SEQUENCE, "a:latin");
603
602
  /**
604
- * `CT_Presentation` (`p:presentation`), in declaration order. Every child is a single element —
605
- * there are no choice groups — so the declaration is a plain string list.
603
+ * `CT_Presentation` (`p:presentation`), in declaration order. Every child is a single element
604
+ * (there are no choice groups), so the declaration is a plain string list.
606
605
  * @see ECMA-376 Part 1 §19.2.1.26
607
606
  */
608
607
  const PRESENTATION_SEQUENCE = [
@@ -638,7 +637,7 @@ const PRESENTATION_AFTER_EMBEDDED_FONT_LST = successorsOf(PRESENTATION_SEQUENCE,
638
637
  */
639
638
  const EMBEDDED_FONT_ENTRY_SEQUENCE = ["p:font", ...EMBEDDED_FONT_SLOTS.map((slot) => `p:${slot}`)];
640
639
  /**
641
- * For each `p:embeddedFont` child, the children that must follow it — i.e. what a new one is
640
+ * For each `p:embeddedFont` child, the children that must follow it, i.e. what a new one is
642
641
  * inserted *before*. Every child gets an entry, so a caller can look one up by qname.
643
642
  */
644
643
  const EMBEDDED_FONT_ENTRY_AFTER = Object.fromEntries(EMBEDDED_FONT_ENTRY_SEQUENCE.map((qname, index) => [qname, EMBEDDED_FONT_ENTRY_SEQUENCE.slice(index + 1)]));
@@ -731,8 +730,7 @@ const TEXT_BODY_SEQUENCE = [
731
730
  const TXBODY_AFTER_BODYPR = successorsOf(TEXT_BODY_SEQUENCE, "a:bodyPr");
732
731
  /** Successors of `a:lstStyle` inside a text body. */
733
732
  const TXBODY_AFTER_LSTSTYLE = successorsOf(TEXT_BODY_SEQUENCE, "a:lstStyle");
734
- //#endregion
735
- //#region src/read/oxml/color-transform.ts
733
+ // src/read/oxml/color-transform.ts
736
734
  /**
737
735
  * Apply DrawingML colour-transform modifiers (`lumMod`/`lumOff`/`shade`/`tint`/
738
736
  * `satMod`/`alpha`/…) to a base sRGB colour, returning the *effective* colour a
@@ -744,22 +742,22 @@ const TXBODY_AFTER_LSTSTYLE = successorsOf(TEXT_BODY_SEQUENCE, "a:lstStyle");
744
742
  * turns that base hex + transform list into the final rendered hex, so a consumer
745
743
  * never hand-computes a tint/shade again.
746
744
  *
747
- * Colour-space model (verified against PowerPoint output — see
748
- * `test/read/color-transform.test.js`): the working colour is kept canonical as
745
+ * Colour-space model (verified against PowerPoint output; see
746
+ * `test/read/color-transform.test.ts`): the working colour is kept canonical as
749
747
  * sRGB between transforms; each transform converts into the space its family
750
748
  * operates in, applies, and converts back to sRGB. Transforms apply in **document
751
749
  * order** (ECMA-376 §20.1.2.3).
752
750
  *
753
- * - `lumMod` / `lumOff` — luminance (L) in HSL: `L' = L·mod + off`. PowerPoint's
751
+ * - `lumMod` / `lumOff`: luminance (L) in HSL: `L' = L·mod + off`. PowerPoint's
754
752
  * "Lighter/Darker N%" family.
755
- * - `satMod` / `satOff` — saturation (S) in HSL.
756
- * - `hueMod` / `hueOff` — hue (H) in HSL (`hueMod` scales, `hueOff` offsets by
753
+ * - `satMod` / `satOff`: saturation (S) in HSL.
754
+ * - `hueMod` / `hueOff`: hue (H) in HSL (`hueMod` scales, `hueOff` offsets by
757
755
  * degrees). Not exercised by any current source; implemented for completeness.
758
- * - `shade` — toward black in **linear** RGB: `c' = c·k`.
759
- * - `tint` — toward white in **linear** RGB: `c' = c·k + (1−k)`.
760
- * - `alpha` / `alphaMod` / `alphaOff` — opacity, returned alongside the hex
756
+ * - `shade`: toward black in **linear** RGB: `c' = c·k`.
757
+ * - `tint`: toward white in **linear** RGB: `c' = c·k + (1−k)`.
758
+ * - `alpha` / `alphaMod` / `alphaOff`: opacity, returned alongside the hex
761
759
  * (never folded into RGB).
762
- * - `comp` / `inv` / `gray` / `gamma` / `invGamma` — pass-through for now (no
760
+ * - `comp` / `inv` / `gray` / `gamma` / `invGamma`: pass-through for now (no
763
761
  * current source needs them; add when one does).
764
762
  *
765
763
  * All DrawingML percentage values are stored in thousandths of a percent
@@ -854,8 +852,8 @@ function hslToRgb({ h, s, l }) {
854
852
  /**
855
853
  * An `a:hslClr` triple → 6-hex sRGB, or `null` when any of the three is unparseable.
856
854
  *
857
- * The colour space is the one `lumMod`/`satMod` already work in — DrawingML's HSL is sRGB-HSL,
858
- * which is what the transform family below is verified against — so this shares that conversion
855
+ * The colour space is the one `lumMod`/`satMod` already work in (DrawingML's HSL is sRGB-HSL,
856
+ * which is what the transform family below is verified against), so this shares that conversion
859
857
  * rather than introducing a second one. Units are the schema's: `@hue` is
860
858
  * `a:ST_PositiveFixedAngle`, i.e. 60,000ths of a degree, and `@sat`/`@lum` are `a:ST_Percentage`
861
859
  * (see {@link parsePercent}). Saturation and luminance are clamped to 0–1; hue wraps.
@@ -877,7 +875,7 @@ function hslClrToHex(hue, sat, lum) {
877
875
  /**
878
876
  * Apply an ordered list of DrawingML colour transforms to a base sRGB hex,
879
877
  * returning the effective sRGB hex (and opacity, when an `alpha*` modifier set
880
- * it). Pure: no DOM, no theme lookup — the caller has already resolved the base
878
+ * it). Pure: no DOM, no theme lookup; the caller has already resolved the base
881
879
  * colour and extracted each modifier's `{ name, value }`.
882
880
  *
883
881
  * An unparseable base hex is returned unchanged (with no transforms applied); an
@@ -960,15 +958,14 @@ function applyColorTransforms(baseHex, transforms) {
960
958
  alpha
961
959
  };
962
960
  }
963
- //#endregion
964
- //#region src/read/oxml/fill.ts
961
+ // src/read/oxml/fill.ts
965
962
  /**
966
963
  * Shared solid-fill read/mutation helpers for DrawingML colour-bearing parents:
967
964
  * a run's `a:rPr`, a shape's `p:spPr` / `p:grpSpPr`, and a line's `a:ln`.
968
965
  *
969
966
  * A parent carries at most one `EG_FillProperties` choice, so setting a solid
970
967
  * fill first clears any competing choice, then inserts `a:solidFill` in document
971
- * order. These helpers never mark a part dirty — callers own that.
968
+ * order. These helpers never mark a part dirty; callers own that.
972
969
  */
973
970
  /** Normalize a 6-hex RGB string (optional leading `#`) to upper-case, or throw. */
974
971
  function normalizeHex(value) {
@@ -1062,18 +1059,17 @@ function setSolidFill(parent, afterOrder, color) {
1062
1059
  setAttr(clr, "val", color.val);
1063
1060
  fill.appendChild(clr);
1064
1061
  }
1065
- //#endregion
1066
- //#region src/read/oxml/preset-color.ts
1062
+ // src/read/oxml/preset-color.ts
1067
1063
  /**
1068
1064
  * The ECMA-376 preset colour table (`a:prstClr/@val` → literal RGB).
1069
1065
  *
1070
1066
  * `ST_PresetColorVal` (§20.1.10.47) enumerates 190 names, and the standard gives each one an
1071
1067
  * RGB triple. They are the HTML/CSS named colours: every value in the enumeration has the same
1072
1068
  * RGB here as the CSS colour of the same name, and the 190 names collapse to the 140 distinct
1073
- * CSS names below through three mechanical spelling rules the enumeration adds on top —
1069
+ * CSS names below through three mechanical spelling rules the enumeration adds on top:
1074
1070
  *
1075
1071
  * - abbreviated prefixes: `dk*`/`lt*`/`med*` alongside `dark*`/`light*`/`medium*`
1076
- * (`dkSlateBlue` is `darkSlateBlue`), and only as a *prefix* — `medium…` is not `mediumium…`;
1072
+ * (`dkSlateBlue` is `darkSlateBlue`), and only as a *prefix* (`medium…` is not `mediumium…`);
1077
1073
  * - both spellings of grey: `dimGrey` is `dimGray`;
1078
1074
  * - case: the enumeration is camelCase, the table below is folded to lower case.
1079
1075
  *
@@ -1084,7 +1080,7 @@ function setSolidFill(parent, afterOrder, color) {
1084
1080
  *
1085
1081
  * This is spec data, not observed behaviour: the values come from the enumeration's own
1086
1082
  * documentation, and the table is asserted complete against the schema's 190-value list in
1087
- * `test/read/preset-color.test.js` — a name the reader cannot resolve is a hole in the table,
1083
+ * `test/read/preset-color.test.ts`. A name the reader cannot resolve is a hole in the table,
1088
1084
  * and that test is what finds it.
1089
1085
  */
1090
1086
  /**
@@ -1248,8 +1244,7 @@ function presetColorHex(name) {
1248
1244
  const canonical = name.toLowerCase().replace(/grey/g, "gray").replace(/^dk/, "dark").replace(/^lt/, "light").replace(/^med(?!ium)/, "medium");
1249
1245
  return PRESET_COLOR_HEX[canonical] ?? null;
1250
1246
  }
1251
- //#endregion
1252
- //#region src/read/oxml/theme.ts
1247
+ // src/read/oxml/theme.ts
1253
1248
  /**
1254
1249
  * Theme colour resolution: turning a DrawingML colour reference or a style-matrix reference into
1255
1250
  * something literal.
@@ -1269,7 +1264,7 @@ function presetColorHex(name) {
1269
1264
  * order to *bake* it into a slide.
1270
1265
  *
1271
1266
  * They must agree, so they share this. The passes that write results back into a part live with
1272
- * the other import operations, not here — this module deliberately has no mutating exports.
1267
+ * the other import operations, not here; this module deliberately has no mutating exports.
1273
1268
  * Placeholder-inheritance resolution (what a placeholder gets from its layout/master chain) is
1274
1269
  * its own concern and lives in `placeholder-inherit.ts`.
1275
1270
  */
@@ -1323,8 +1318,8 @@ function isA(el, local) {
1323
1318
  * `50%` is linear-light or sRGB-encoded decides the answer and neither the schema nor the
1324
1319
  * transform work in `color-transform.ts` settles it. Guessing would produce a colour that is
1325
1320
  * wrong by a gamma curve and says nothing about it, which is worse than reporting no colour.
1326
- * Closing it needs a render oracle — a deck with an `a:scrgbClr` fill authored in PowerPoint
1327
- * and the painted pixel read back — not a reading of the schema.
1321
+ * Closing it needs a render oracle (a deck with an `a:scrgbClr` fill authored in PowerPoint
1322
+ * and the painted pixel read back), not a reading of the schema.
1328
1323
  */
1329
1324
  function colorElementHex(color) {
1330
1325
  if (!color) return null;
@@ -1349,7 +1344,7 @@ function themeElementsOfRoot(themeRoot) {
1349
1344
  * literal (unmapped token, or a colour model we do not flatten).
1350
1345
  *
1351
1346
  * Five of `a:EG_ColorChoice`'s six members resolve: `a:srgbClr`, `a:sysClr`, `a:schemeClr`,
1352
- * `a:prstClr` (through the {@link presetColorHex} table) and `a:hslClr`. `a:scrgbClr` does not —
1347
+ * `a:prstClr` (through the {@link presetColorHex} table) and `a:hslClr`. `a:scrgbClr` does not;
1353
1348
  * see {@link colorElementHex} for why that one is a `null` rather than a guess.
1354
1349
  */
1355
1350
  function resolveColor(color, ctx) {
@@ -1399,11 +1394,11 @@ function resolveThemeFont(typeface, fontScheme) {
1399
1394
  }
1400
1395
  /**
1401
1396
  * Swap one colour element for another, MOVING the old element's transform children onto the new
1402
- * one — and, optionally, seeding it with a set of transforms of its own first.
1397
+ * one and, optionally, seeding it with a set of transforms of its own first.
1403
1398
  *
1404
1399
  * The move is the whole rule the flatten path exists to preserve: only the base reference is
1405
1400
  * swapped, never the transform math. Three sites implemented it and a fourth cloned instead of
1406
- * moving, each with its own `while (old.firstChild) …` loop — duplication of exactly the one
1401
+ * moving, each with its own `while (old.firstChild) …` loop: duplication of exactly the one
1407
1402
  * invariant that must not vary.
1408
1403
  *
1409
1404
  * Transform ORDER matters and is the reason `seed` is separate: `substitutePhClr` puts the
@@ -1438,7 +1433,7 @@ function fmtEntry(ctx, listName, idx) {
1438
1433
  return entry ? entry.cloneNode(true) : null;
1439
1434
  }
1440
1435
  /**
1441
- * Build the explicit fill element a `p:style` `a:fillRef` resolves to — the indexed
1436
+ * Build the explicit fill element a `p:style` `a:fillRef` resolves to: the indexed
1442
1437
  * `fmtScheme` `fillStyleLst`/`bgFillStyleLst` entry (deep-cloned) with its `phClr`
1443
1438
  * replaced by the ref's resolved colour. Pure: mutates neither the ref nor the
1444
1439
  * theme. `null` when the ref is absent, `idx` is 0/unset, or the entry or its
@@ -1456,7 +1451,7 @@ function styleRefFill(fillRef, ctx) {
1456
1451
  return fill;
1457
1452
  }
1458
1453
  /**
1459
- * Build the explicit `a:ln` element a `p:style` `a:lnRef` resolves to — the indexed
1454
+ * Build the explicit `a:ln` element a `p:style` `a:lnRef` resolves to: the indexed
1460
1455
  * `fmtScheme` `lnStyleLst` entry (deep-cloned) with its `phClr` replaced by the
1461
1456
  * ref's resolved colour. Pure; the line/read counterpart of {@link styleRefFill}.
1462
1457
  */
@@ -1470,13 +1465,12 @@ function styleRefLine(lnRef, ctx) {
1470
1465
  substitutePhClr(ln, ref);
1471
1466
  return ln;
1472
1467
  }
1473
- //#endregion
1474
- //#region src/read/oxml/slide-dom.ts
1468
+ // src/read/oxml/slide-dom.ts
1475
1469
  /**
1476
1470
  * The `p:cSld` of a slide/layout/master/notes part root.
1477
1471
  *
1478
1472
  * Takes a nullable root and returns a nullable element so a caller can chain from
1479
- * `part?.dom.documentElement` without a guard of its own — which is what a dozen callers were
1473
+ * `part?.dom.documentElement` without a guard of its own, which is what a dozen callers were
1480
1474
  * writing by hand, each in a slightly different spelling (`root && …`, `root ? … : null`).
1481
1475
  */
1482
1476
  function cSldOf(root) {
@@ -1490,8 +1484,8 @@ function spTreeOf(root) {
1490
1484
  /**
1491
1485
  * The `p:nvPr` of a shape, whichever `*nvPr` wrapper its kind uses.
1492
1486
  *
1493
- * The wrapper's name varies by shape kind — `p:nvSpPr` on a `p:sp`, `p:nvPicPr` on a `p:pic`,
1494
- * `p:nvGraphicFramePr`, `p:nvCxnSpPr`, `p:nvGrpSpPr` — but it is always the shape's first child
1487
+ * The wrapper's name varies by shape kind (`p:nvSpPr` on a `p:sp`, `p:nvPicPr` on a `p:pic`,
1488
+ * `p:nvGraphicFramePr`, `p:nvCxnSpPr`, `p:nvGrpSpPr`), but it is always the shape's first child
1495
1489
  * element, and `p:nvPr` is always inside it. Taking the first child element rather than naming
1496
1490
  * the wrapper is what lets one helper serve every kind; the callers that named `p:nvSpPr`
1497
1491
  * silently returned null on a picture.
@@ -1633,8 +1627,7 @@ function collectElements(node, out) {
1633
1627
  out.push(node);
1634
1628
  for (let child = node.firstChild; child; child = child.nextSibling) if (child.nodeType === 1) collectElements(child, out);
1635
1629
  }
1636
- //#endregion
1637
- //#region src/read/oxml/placeholder-inherit.ts
1630
+ // src/read/oxml/placeholder-inherit.ts
1638
1631
  /**
1639
1632
  * Resolving what a placeholder shape *inherits* from its source layout/master chain.
1640
1633
  *
@@ -1783,6 +1776,22 @@ function lstStyleLevelDefRPr(listStyle, level) {
1783
1776
  const lvl = firstChild(listStyle, `a:lvl${level + 1}pPr`) ?? firstChild(listStyle, "a:defPPr");
1784
1777
  return lvl ? firstChild(lvl, "a:defRPr") : null;
1785
1778
  }
1779
+ /**
1780
+ * The paragraph properties for `level` (0-based) in a `CT_TextListStyle`: the level-specific
1781
+ * `a:lvlNpPr`, else the `a:defPPr` fallback, as {@link lstStyleLevelDefRPr} reads them.
1782
+ */
1783
+ function lstStyleLevel(listStyle, level) {
1784
+ if (!listStyle) return null;
1785
+ return firstChild(listStyle, `a:lvl${level + 1}pPr`) ?? firstChild(listStyle, "a:defPPr");
1786
+ }
1787
+ /**
1788
+ * The paragraph-property tiers a placeholder's paragraph at `level` inherits from, most specific
1789
+ * first: each source list style's level element. The paragraph-level twin of
1790
+ * {@link placeholderInheritedDefRPrs}.
1791
+ */
1792
+ function placeholderInheritedLevels(type, idx, level, ctx) {
1793
+ return styleTiers(type, idx, ctx).map((tier) => lstStyleLevel(tier, level)).filter((t) => t !== null);
1794
+ }
1786
1795
  /** The `a:solidFill` of a level's `a:defRPr`, or `null` when none is defined there. */
1787
1796
  function lstStyleLevelFill(listStyle, level) {
1788
1797
  const defRPr = lstStyleLevelDefRPr(listStyle, level);
@@ -1934,15 +1943,14 @@ function slideDefinesProp(name, run, pPr, slideLst, level) {
1934
1943
  const slideDefRPr = lstStyleLevelDefRPr(slideLst, level);
1935
1944
  return !!(slideDefRPr && attr(slideDefRPr, name) != null);
1936
1945
  }
1937
- //#endregion
1938
- //#region src/read/api/coords.ts
1946
+ // src/read/api/coords.ts
1939
1947
  /**
1940
- * ts-pptx: units at the read model's boundary — validated on the way in, converted on the way out.
1948
+ * pptx-ts: units at the read model's boundary, validated on the way in and converted on the way out.
1941
1949
  *
1942
1950
  * Every read-side setter that takes a measurement has to answer the same two questions before
1943
1951
  * the value reaches an attribute: is it finite, and (for an extent) is it positive. `NaN` or
1944
1952
  * `Infinity` written verbatim makes the part invalid, and silently coercing it to something
1945
- * drawable is exactly the footgun the project's API policy rules out — so both are a throw.
1953
+ * drawable is exactly the footgun the project's API policy rules out, so both are a throw.
1946
1954
  *
1947
1955
  * The two halves used to be split across `table-edit.ts` (threw, then rounded) and `slide.ts`
1948
1956
  * (threw only, with the rounding done eight lines later at each build site), under two spellings
@@ -1960,7 +1968,7 @@ function checkFiniteEmu(value, field, code = "coord/non-finite") {
1960
1968
  return Math.round(value);
1961
1969
  }
1962
1970
  /**
1963
- * As {@link checkFiniteEmu}, and additionally rejects a non-positive value — the guard an
1971
+ * As {@link checkFiniteEmu}, and additionally rejects a non-positive value: the guard an
1964
1972
  * *extent* needs, since `ST_PositiveCoordinate` has no room for zero or below and a zero-size
1965
1973
  * shape is a degenerate result rather than a small one.
1966
1974
  */
@@ -1989,8 +1997,8 @@ function checkLineWidthEmu(value, field, code) {
1989
1997
  * insets in points while the DOM stores EMU, so `x === null ? null : x / EMU_PER_POINT` stood at
1990
1998
  * five getters across `chart.ts`, `shapes/base.ts`, `table.ts` and `text.ts`.
1991
1999
  *
1992
- * The accumulator sites whose target is `number | undefined` do not come through here —
1993
- * propagating a `null` would only have to be unwrapped again — but they do not open-code the
2000
+ * The accumulator sites whose target is `number | undefined` do not come through here
2001
+ * (propagating a `null` would only have to be unwrapped again), but they do not open-code the
1994
2002
  * division either: they unwrap the `null` themselves and call `emuToPoints`. {@link emuToPoints}
1995
2003
  * states the whole policy and names the one place still allowed to divide inline.
1996
2004
  */
@@ -2015,8 +2023,7 @@ function ptFromEmu(emu) {
2015
2023
  function ptFromHundredths(value) {
2016
2024
  return value === null ? null : value / 100;
2017
2025
  }
2018
- //#endregion
2019
- //#region src/read/api/shapes/geometry.ts
2026
+ // src/read/api/shapes/geometry.ts
2020
2027
  /**
2021
2028
  * Reading geometry out of a shape: transform boxes, rotation and flips, and custom-geometry paths.
2022
2029
  *
@@ -2024,12 +2031,18 @@ function ptFromHundredths(value) {
2024
2031
  * readers with no theme or relationship context.
2025
2032
  */
2026
2033
  /**
2027
- * One `a:pt` coordinate as a raw path-unit integer. A guide-name reference
2028
- * (the `ST_AdjCoordinate` string form) is not produced by authored freeforms;
2029
- * a non-numeric value degrades to `0` rather than crashing (documented edge).
2034
+ * An `ST_AdjCoordinate` attribute of a path segment: a raw path-unit integer, or the guide name it
2035
+ * references (the string form). Absent reads `0`.
2030
2036
  */
2031
- function ptAxis(pt, axis) {
2032
- return (pt ? numberValue(attr(pt, axis)) : null) ?? 0;
2037
+ function adjValue(element, name) {
2038
+ const value = element ? attr(element, name) : null;
2039
+ if (value === null || value === "") return 0;
2040
+ return numberValue(value) ?? value;
2041
+ }
2042
+ /** An `ST_AdjAngle` attribute of an `arcTo` in degrees, or the guide name it references. Absent reads `0`. */
2043
+ function adjAngle(seg, name) {
2044
+ const value = adjValue(seg, name);
2045
+ return typeof value === "number" ? value / ANGLE_UNITS_PER_DEGREE : value;
2033
2046
  }
2034
2047
  /** Parse one `<a:path>` into its viewport attrs (with schema defaults) and ordered segments. */
2035
2048
  function readGeometryPath(path) {
@@ -2043,44 +2056,44 @@ function readGeometryPath(path) {
2043
2056
  case "moveTo":
2044
2057
  commands.push({
2045
2058
  cmd: "moveTo",
2046
- x: ptAxis(pts[0], "x"),
2047
- y: ptAxis(pts[0], "y")
2059
+ x: adjValue(pts[0], "x"),
2060
+ y: adjValue(pts[0], "y")
2048
2061
  });
2049
2062
  break;
2050
2063
  case "lnTo":
2051
2064
  commands.push({
2052
2065
  cmd: "lnTo",
2053
- x: ptAxis(pts[0], "x"),
2054
- y: ptAxis(pts[0], "y")
2066
+ x: adjValue(pts[0], "x"),
2067
+ y: adjValue(pts[0], "y")
2055
2068
  });
2056
2069
  break;
2057
2070
  case "cubicBezTo":
2058
2071
  commands.push({
2059
2072
  cmd: "cubicBezTo",
2060
- x1: ptAxis(pts[0], "x"),
2061
- y1: ptAxis(pts[0], "y"),
2062
- x2: ptAxis(pts[1], "x"),
2063
- y2: ptAxis(pts[1], "y"),
2064
- x: ptAxis(pts[2], "x"),
2065
- y: ptAxis(pts[2], "y")
2073
+ x1: adjValue(pts[0], "x"),
2074
+ y1: adjValue(pts[0], "y"),
2075
+ x2: adjValue(pts[1], "x"),
2076
+ y2: adjValue(pts[1], "y"),
2077
+ x: adjValue(pts[2], "x"),
2078
+ y: adjValue(pts[2], "y")
2066
2079
  });
2067
2080
  break;
2068
2081
  case "quadBezTo":
2069
2082
  commands.push({
2070
2083
  cmd: "quadBezTo",
2071
- x1: ptAxis(pts[0], "x"),
2072
- y1: ptAxis(pts[0], "y"),
2073
- x: ptAxis(pts[1], "x"),
2074
- y: ptAxis(pts[1], "y")
2084
+ x1: adjValue(pts[0], "x"),
2085
+ y1: adjValue(pts[0], "y"),
2086
+ x: adjValue(pts[1], "x"),
2087
+ y: adjValue(pts[1], "y")
2075
2088
  });
2076
2089
  break;
2077
2090
  case "arcTo":
2078
2091
  commands.push({
2079
2092
  cmd: "arcTo",
2080
- wR: numberValue(attr(seg, "wR")) ?? 0,
2081
- hR: numberValue(attr(seg, "hR")) ?? 0,
2082
- stAng: (numberValue(attr(seg, "stAng")) ?? 0) / ANGLE_UNITS_PER_DEGREE,
2083
- swAng: (numberValue(attr(seg, "swAng")) ?? 0) / ANGLE_UNITS_PER_DEGREE
2093
+ wR: adjValue(seg, "wR"),
2094
+ hR: adjValue(seg, "hR"),
2095
+ stAng: adjAngle(seg, "stAng"),
2096
+ swAng: adjAngle(seg, "swAng")
2084
2097
  });
2085
2098
  break;
2086
2099
  case "close": commands.push({ cmd: "close" });
@@ -2120,17 +2133,16 @@ function transformFlipH(xfrm) {
2120
2133
  function transformFlipV(xfrm) {
2121
2134
  return boolValue(attr(xfrm, "flipV")) === true;
2122
2135
  }
2123
- //#endregion
2124
- //#region src/read/api/theme-context.ts
2136
+ // src/read/api/theme-context.ts
2125
2137
  /**
2126
2138
  * Resolve a slide's theme colour context: the slide → slideLayout → slideMaster
2127
2139
  * → theme walk that turns a `schemeClr` token into a literal hex, plus the
2128
2140
  * `a:solidFill` helper the read-model colour getters share.
2129
2141
  *
2130
- * One implementation backs two callers — the read-model getters
2142
+ * One implementation backs two callers: the read-model getters
2131
2143
  * (`Slide.themeContext` → `Shape.resolvedFill` / `Run.resolvedColor`) and the
2132
2144
  * `importSlide` `theme: 'preserve'` flatten path, which layers its `fmtScheme` /
2133
- * background needs on top of {@link resolveSlideThemeParts} — so a token resolves
2145
+ * background needs on top of {@link resolveSlideThemeParts}. So a token resolves
2134
2146
  * identically whether it is read or baked.
2135
2147
  */
2136
2148
  /** The document element of a part, or `null` when the partname/part is absent. */
@@ -2140,7 +2152,7 @@ function documentElement(opc, partName) {
2140
2152
  /**
2141
2153
  * The presentation's `p:defaultTextStyle` (`presentation.xml`, reached via the
2142
2154
  * package `officeDocument` relationship), or `null` when absent. This is
2143
- * PowerPoint's lowest-priority text fallback — the bottom tier of a slide run's
2155
+ * PowerPoint's lowest-priority text fallback: the bottom tier of a slide run's
2144
2156
  * size/colour/face/bold resolution (see `ThemeContext.defaultTextStyle`).
2145
2157
  */
2146
2158
  function presentationDefaultTextStyle(opc) {
@@ -2207,7 +2219,7 @@ function resolveSlideColorContext(opc, parts) {
2207
2219
  * are deliberately left absent. Instead the notesMaster's `p:notesStyle` is carried
2208
2220
  * as `notesStyle`, and the notes body `TextFrame` is built with a placeholder
2209
2221
  * context (see `NotesPlaceholder`) so a body run's
2210
- * effective size/face/bold (and inherited colour) resolve against it — the notes
2222
+ * effective size/face/bold (and inherited colour) resolve against it, the notes
2211
2223
  * analogue of the slide placeholder chain. The maps are empty when the
2212
2224
  * notesMaster/theme chain is incomplete, in which case tokens stay unresolved.
2213
2225
  */
@@ -2226,7 +2238,7 @@ function resolveNotesColorContext(opc, notesPartName) {
2226
2238
  * The colour context a *slide master*'s own getters resolve against: the master's
2227
2239
  * `p:clrMap` plus its theme's `clrScheme`/`fmtScheme`/`fontScheme` (walked master →
2228
2240
  * theme). Backs the text frames of `SlideMaster`'s
2229
- * placeholders — a master placeholder run's own `schemeClr` resolves to a literal
2241
+ * placeholders: a master placeholder run's own `schemeClr` resolves to a literal
2230
2242
  * hex the same way a slide run's does. `masterRoot` is carried so an inherited
2231
2243
  * placeholder value still resolves against the master's own text styles. The maps
2232
2244
  * are empty when the theme is missing, in which case tokens stay unresolved.
@@ -2245,7 +2257,7 @@ function resolveMasterColorContext(opc, masterPartName) {
2245
2257
  * The colour context a *slide layout*'s own getters resolve against: walk layout →
2246
2258
  * master → theme. The effective `clrMap` is the layout's own
2247
2259
  * `p:clrMapOvr/a:overrideClrMapping` when present (rare), else the master's
2248
- * `p:clrMap` — the layout's usual `a:masterClrMapping` means "inherit the master
2260
+ * `p:clrMap`. The layout's usual `a:masterClrMapping` means "inherit the master
2249
2261
  * map". `clrScheme`/`fmtScheme`/`fontScheme` come from the master's theme, and both
2250
2262
  * `layoutRoot`/`masterRoot` are carried for inherited-placeholder resolution. Backs
2251
2263
  * the text frames of `SlideLayout`'s placeholders.
@@ -2297,8 +2309,8 @@ function themeElementsOf(opc, themePartName) {
2297
2309
  * The colour a run effectively renders when its own `a:rPr` defines none,
2298
2310
  * resolved to a full {@link ResolvedColor}. Walks the inheritance the way
2299
2311
  * PowerPoint does: the paragraph's `a:pPr/a:defRPr` colour, then the slide text
2300
- * body's `a:lstStyle` colour for the run's `level`, then — only for a placeholder
2301
- * run — the placeholder's layout → master → master-`p:txStyles` chain (via
2312
+ * body's `a:lstStyle` colour for the run's `level`, then (only for a placeholder
2313
+ * run) the placeholder's layout → master → master-`p:txStyles` chain (via
2302
2314
  * {@link placeholderInheritedFill}), then the presentation's `p:defaultTextStyle`
2303
2315
  * for the run's level (`ctx.defaultTextStyle`). The first tier that defines a
2304
2316
  * colour wins. `ph` is `null` for a non-placeholder run, which skips the
@@ -2321,11 +2333,11 @@ function resolveInheritedRunColor(ph, level, pPr, slideLstStyle, ctx) {
2321
2333
  /**
2322
2334
  * The `a:defRPr` tiers a run resolves an inherited character property against, in
2323
2335
  * priority order: the paragraph's `a:pPr/a:defRPr`, then the slide text body's
2324
- * `a:lstStyle` level `a:defRPr`, then — only for a placeholder run — the
2336
+ * `a:lstStyle` level `a:defRPr`, then (only for a placeholder run) the
2325
2337
  * placeholder's layout → master → master-`p:txStyles` chain (via
2326
2338
  * {@link placeholderInheritedDefRPrs}), then the presentation's
2327
2339
  * `p:defaultTextStyle` level `a:defRPr` (`ctx.defaultTextStyle`). The first tier
2328
- * that defines the property wins — each property resolves independently, mirroring
2340
+ * that defines the property wins; each property resolves independently, mirroring
2329
2341
  * how the colour resolver walks the same chain. `ph` is `null` for a
2330
2342
  * non-placeholder run, which skips the placeholder chain but still reaches
2331
2343
  * `p:defaultTextStyle`.
@@ -2380,8 +2392,8 @@ function resolveInheritedRunFontFace(ph, level, pPr, slideLstStyle, ctx) {
2380
2392
  * {@link resolveInheritedRunSize}: the first `a:defRPr/@<name>` in the paragraph →
2381
2393
  * slide → (placeholder) layout → master → `p:txStyles` → `p:defaultTextStyle`
2382
2394
  * chain. The first tier that *states* the attribute wins, and its value is parsed
2383
- * by {@link boolValue} — the same `xsd:boolean` parser the run's own `bold`/`italic`
2384
- * getters use — so an unparseable value reports `null` rather than being read as
2395
+ * by {@link boolValue} (the same `xsd:boolean` parser the run's own `bold`/`italic`
2396
+ * getters use), so an unparseable value reports `null` rather than being read as
2385
2397
  * `false`. `null` when nothing in the chain states it.
2386
2398
  */
2387
2399
  function resolveInheritedRunFlag(name, ph, level, pPr, slideLstStyle, ctx) {
@@ -2446,8 +2458,8 @@ function resolveInheritedFrame(ph, ctx) {
2446
2458
  }
2447
2459
  /**
2448
2460
  * Resolve a DrawingML colour *element* (every `a:EG_ColorChoice` member except
2449
- * `a:scrgbClr` — see `read/oxml/theme.ts`) against `ctx` into a full
2450
- * {@link ResolvedColor} — base hex, raw transform list,
2461
+ * `a:scrgbClr`; see `read/oxml/theme.ts`) against `ctx` into a full
2462
+ * {@link ResolvedColor}: base hex, raw transform list,
2451
2463
  * and the `effectiveHex`/`alpha` after applying those transforms. `null` when the
2452
2464
  * element cannot be made literal (unmapped token, or a colour model we do not
2453
2465
  * resolve). Shared by the solid-fill and gradient-stop colour reads.
@@ -2502,7 +2514,7 @@ function resolveSolidFillColor(container, ctx) {
2502
2514
  * `Shape.resolvedFill` when the shape carries no
2503
2515
  * explicit `spPr` fill choice. `null` when there is no `fillRef`, it cannot be
2504
2516
  * resolved, or the indexed style entry is not a solid fill (a gradient style fill
2505
- * has no single colour — read it through `gradientStops` instead).
2517
+ * has no single colour; read it through `gradientStops` instead).
2506
2518
  */
2507
2519
  function resolveStyleFillColor(shape, ctx) {
2508
2520
  const style = firstChild(shape, "p:style");
@@ -2523,7 +2535,7 @@ function resolveStyleLineColor(shape, ctx) {
2523
2535
  }
2524
2536
  /**
2525
2537
  * Resolve the text colour and typeface a shape derives from its `p:style/a:fontRef`
2526
- * (the style-matrix font reference) — the tier PowerPoint applies to a shape's runs
2538
+ * (the style-matrix font reference): the tier PowerPoint applies to a shape's runs
2527
2539
  * just below their own `a:rPr` and above the placeholder/`p:defaultTextStyle` chain.
2528
2540
  * The `a:fontRef` child colour resolves through `ctx` like any other (it can carry
2529
2541
  * `lumMod`/`shade` transforms); `@idx` (`major`|`minor`|`none`) maps to the theme's
@@ -2541,54 +2553,12 @@ function resolveStyleFontRef(shape, ctx) {
2541
2553
  face: token ? resolveThemeFont(token, ctx.fontScheme ?? null) : null
2542
2554
  };
2543
2555
  }
2544
- //#endregion
2545
- //#region src/read/api/gradient.ts
2546
- /**
2547
- * Shared gradient-fill reader for DrawingML colour-bearing containers: a shape's
2548
- * `p:spPr`, a line's `a:ln`, and a slide's `p:bg/p:bgPr`. Decodes an `a:gradFill`
2549
- * into its stops (colour + position) plus the linear/path geometry, resolving each
2550
- * stop's colour against the slide theme.
2551
- */
2552
- /**
2553
- * Read `a:gradFill/a:gsLst` stops from a container (`p:spPr` for a fill, `a:ln`
2554
- * for a line stroke, or `p:bgPr` for a slide background). `null` when the
2555
- * container has no gradient; `[]` when the gradient carries no stop list.
2556
- */
2557
- function readGradientStops(container, ctx) {
2558
- const grad = firstChild(container, "a:gradFill");
2559
- if (!grad) return null;
2560
- const gsLst = firstChild(grad, "a:gsLst");
2561
- if (!gsLst) return [];
2562
- return getElements(gsLst, "a:gs").map((gs) => ({
2563
- position: pctAttr(gs, "pos"),
2564
- colorRef: readColorRef(firstChildElement(gs), ctx)
2565
- }));
2566
- }
2567
- /**
2568
- * Read the full `a:gradFill` (stops + linear angle / path shape) from a container
2569
- * (`p:spPr` for a fill, `a:ln` for a line stroke, or `p:bgPr` for a slide
2570
- * background). `null` when the container has no gradient.
2571
- */
2572
- function readGradientFill(container, ctx) {
2573
- const grad = firstChild(container, "a:gradFill");
2574
- if (!grad) return null;
2575
- const lin = firstChild(grad, "a:lin");
2576
- const path = firstChild(grad, "a:path");
2577
- const ang = lin ? numberValue(attr(lin, "ang")) : null;
2578
- return {
2579
- kind: lin ? "linear" : path ? "path" : null,
2580
- angleDeg: ang === null ? null : ang / ANGLE_UNITS_PER_DEGREE,
2581
- path: path ? attr(path, "path") ?? null : null,
2582
- stops: readGradientStops(container, ctx) ?? []
2583
- };
2584
- }
2585
- //#endregion
2586
- //#region src/read/api/picture-fill.ts
2556
+ // src/read/api/picture-fill.ts
2587
2557
  /**
2588
2558
  * Shared picture-fill reader for DrawingML fill-bearing containers: a shape's
2589
2559
  * `p:spPr`, a table cell's `a:tcPr`, and a slide's `p:bg/p:bgPr`. Decodes an
2590
2560
  * `a:blipFill` into the embedded image reference plus the geometry that decides
2591
- * how the image covers the surface — stretch vs tile, the source crop, and the
2561
+ * how the image covers the surface: stretch vs tile, the source crop, and the
2592
2562
  * tile offsets/scale.
2593
2563
  *
2594
2564
  * A picture fill is not a colour, so the solid-fill accessors
@@ -2619,7 +2589,7 @@ function readTile(tile) {
2619
2589
  }
2620
2590
  /**
2621
2591
  * Read an `a:blipFill` from a container (`p:spPr`, `a:tcPr`, or `p:bgPr`) into a
2622
- * {@link PictureFill}. `rels` — the *owning part's* relationships — resolves the
2592
+ * {@link PictureFill}. `rels` (the *owning part's* relationships) resolves the
2623
2593
  * blip's `r:embed` to an absolute partname; pass `null` (or omit) to read the
2624
2594
  * geometry without resolving the image. `null` when the container's fill is not
2625
2595
  * a picture.
@@ -2647,14 +2617,57 @@ function readPictureFill(container, rels = null) {
2647
2617
  rotWithShape: boolValue(attr(blipFill, "rotWithShape"))
2648
2618
  };
2649
2619
  }
2650
- //#endregion
2651
- //#region src/read/api/pattern-fill.ts
2620
+ // src/read/api/gradient.ts
2621
+ /**
2622
+ * Shared gradient-fill reader for DrawingML colour-bearing containers: a shape's
2623
+ * `p:spPr`, a line's `a:ln`, and a slide's `p:bg/p:bgPr`. Decodes an `a:gradFill`
2624
+ * into its stops (colour + position) plus the linear/path geometry, resolving each
2625
+ * stop's colour against the slide theme.
2626
+ */
2627
+ /**
2628
+ * Read `a:gradFill/a:gsLst` stops from a container (`p:spPr` for a fill, `a:ln`
2629
+ * for a line stroke, or `p:bgPr` for a slide background). `null` when the
2630
+ * container has no gradient; `[]` when the gradient carries no stop list.
2631
+ */
2632
+ function readGradientStops(container, ctx) {
2633
+ const grad = firstChild(container, "a:gradFill");
2634
+ if (!grad) return null;
2635
+ const gsLst = firstChild(grad, "a:gsLst");
2636
+ if (!gsLst) return [];
2637
+ return getElements(gsLst, "a:gs").map((gs) => ({
2638
+ position: pctAttr(gs, "pos"),
2639
+ colorRef: readColorRef(firstChildElement(gs), ctx)
2640
+ }));
2641
+ }
2642
+ /**
2643
+ * Read the full `a:gradFill` (stops + linear angle / path shape) from a container
2644
+ * (`p:spPr` for a fill, `a:ln` for a line stroke, or `p:bgPr` for a slide
2645
+ * background). `null` when the container has no gradient.
2646
+ */
2647
+ function readGradientFill(container, ctx) {
2648
+ const grad = firstChild(container, "a:gradFill");
2649
+ if (!grad) return null;
2650
+ const lin = firstChild(grad, "a:lin");
2651
+ const path = firstChild(grad, "a:path");
2652
+ const ang = lin ? numberValue(attr(lin, "ang")) : null;
2653
+ const focus = path ? firstChild(path, "a:fillToRect") : null;
2654
+ return {
2655
+ kind: lin ? "linear" : path ? "path" : null,
2656
+ angleDeg: ang === null ? null : ang / ANGLE_UNITS_PER_DEGREE,
2657
+ path: path ? attr(path, "path") ?? null : null,
2658
+ stops: readGradientStops(container, ctx) ?? [],
2659
+ rotateWithShape: boolAttr(grad, "rotWithShape"),
2660
+ scaled: lin ? boolAttr(lin, "scaled") : null,
2661
+ focusRect: focus ? readRect(focus) : null
2662
+ };
2663
+ }
2664
+ // src/read/api/pattern-fill.ts
2652
2665
  /**
2653
2666
  * Shared pattern-fill reader for DrawingML colour-bearing containers: a shape's `p:spPr`, a
2654
2667
  * slide's `p:bg/p:bgPr`, and a table's `a:tblPr` / cell's `a:tcPr`.
2655
2668
  *
2656
2669
  * `a:pattFill` is a preset hatch name plus two wrapped colours, and decoding it is three
2657
- * lines — which is exactly why it had been open-coded in each place that needed it. Sharing
2670
+ * lines, which is exactly why it had been open-coded in each place that needed it. Sharing
2658
2671
  * it is what keeps the three from drifting into different answers for the same element, the
2659
2672
  * same reason `readGradientFill` and
2660
2673
  * `readPictureFill` are shared.
@@ -2678,8 +2691,7 @@ function readPatternFill(container, ctx) {
2678
2691
  background: colorOf("a:bgClr")
2679
2692
  };
2680
2693
  }
2681
- //#endregion
2682
- //#region src/read/api/shapes/oxml.ts
2694
+ // src/read/api/shapes/oxml.ts
2683
2695
  /**
2684
2696
  * Shape-element plumbing: the small element lookups every shape kind performs, plus the
2685
2697
  * shape-scoped re-exports of the shared schema-successor lists.
@@ -2707,8 +2719,7 @@ function emuFrom(parent, qname, attribute) {
2707
2719
  function getOrAddSpPrXfrm(shapeElement) {
2708
2720
  return getOrAddChild(getOrAddChild(shapeElement, "p:spPr", SHAPE_AFTER_SPPR), "a:xfrm", SPPR_AFTER_XFRM);
2709
2721
  }
2710
- //#endregion
2711
- //#region src/read/api/shapes/group-transform.ts
2722
+ // src/read/api/shapes/group-transform.ts
2712
2723
  /** Normalise a degree value into [0, 360). */
2713
2724
  function normalizeDegrees(value) {
2714
2725
  return (value % 360 + 360) % 360;
@@ -2733,8 +2744,8 @@ function rotatePoint(point, center, degrees) {
2733
2744
  * `groups` runs **innermost first** (the shape's immediate `p:grpSp` parent, then
2734
2745
  * outward). An empty list returns the shape's own box and orientation unchanged.
2735
2746
  *
2736
- * The returned `box` is the *unrotated* placement box — the same box PowerPoint
2737
- * writes when you Ungroup — with the effective rotation reported separately.
2747
+ * The returned `box` is the *unrotated* placement box (the same box PowerPoint
2748
+ * writes when you Ungroup), with the effective rotation reported separately.
2738
2749
  * Values are exact; callers round or convert units as they see fit.
2739
2750
  *
2740
2751
  * Returns `null` when a group's `a:chExt` is degenerate (zero on either axis):
@@ -2806,8 +2817,7 @@ function composeGroupFrame(shape, groups) {
2806
2817
  flipV
2807
2818
  };
2808
2819
  }
2809
- //#endregion
2810
- //#region src/read/api/line.ts
2820
+ // src/read/api/line.ts
2811
2821
  /**
2812
2822
  * Shared line reader for DrawingML strokes: a shape's `p:spPr/a:ln`, a table cell's edges
2813
2823
  * (`a:tcPr/a:lnL` and its siblings, each a `CT_LineProperties`), and a chart series'
@@ -2832,10 +2842,9 @@ function readLineBasics(ln, ctx) {
2832
2842
  noFill: !!firstChild(ln, "a:noFill")
2833
2843
  };
2834
2844
  }
2835
- //#endregion
2836
- //#region src/read/api/hyperlink.ts
2845
+ // src/read/api/hyperlink.ts
2837
2846
  /**
2838
- * `a:hlinkClick` — the one reading of a click hyperlink, wherever it hangs.
2847
+ * `a:hlinkClick`: the one reading of a click hyperlink, wherever it hangs.
2839
2848
  *
2840
2849
  * DrawingML puts the same element in two places: on a run's `a:rPr`, which links a span of text,
2841
2850
  * and on a shape's `p:cNvPr`, which links the whole shape. They are the same element with the same
@@ -2873,10 +2882,9 @@ function readHyperlink(parent, rels) {
2873
2882
  relId
2874
2883
  };
2875
2884
  }
2876
- //#endregion
2877
- //#region src/read/api/shapes/effects.ts
2885
+ // src/read/api/shapes/effects.ts
2878
2886
  /**
2879
- * Decoding a shape's `a:effectLst` — shadow, glow, reflection, soft edge.
2887
+ * Decoding a shape's `a:effectLst`: shadow, glow, reflection, soft edge.
2880
2888
  *
2881
2889
  * Free functions over `(effectLst, ctx)` rather than methods, because that is all they ever
2882
2890
  * needed: the block was five getters and two private helpers on `Shape` whose only reference
@@ -2885,7 +2893,7 @@ function readHyperlink(parent, rels) {
2885
2893
  * per-concern modules `shapes/` already has (`oxml.ts`, `geometry.ts`).
2886
2894
  *
2887
2895
  * Every decoder returns `null` for an absent element rather than an empty object, and leaves an
2888
- * unstated attribute off its result rather than defaulting it — "absent" and "stated as the
2896
+ * unstated attribute off its result rather than defaulting it. "Absent" and "stated as the
2889
2897
  * default" are different facts about the source part, and a replica that has to carry the
2890
2898
  * effect needs to know which it is.
2891
2899
  */
@@ -2932,7 +2940,11 @@ function readShadow(shdw, ctx) {
2932
2940
  */
2933
2941
  function readOuterShadow(effectLst, ctx) {
2934
2942
  const shdw = effect(effectLst, "a:outerShdw");
2935
- return shdw ? readShadow(shdw, ctx) : null;
2943
+ if (!shdw) return null;
2944
+ const out = readShadow(shdw, ctx);
2945
+ const rotate = boolAttr(shdw, "rotWithShape");
2946
+ if (rotate !== null) out.rotateWithShape = rotate;
2947
+ return out;
2936
2948
  }
2937
2949
  /**
2938
2950
  * The inner shadow (`a:effectLst/a:innerShdw`), or `null`.
@@ -2992,8 +3004,7 @@ function readSoftEdge(effectLst) {
2992
3004
  if (!soft) return null;
2993
3005
  return { radiusPt: scaledAttr(soft, "rad", 12700) ?? 0 };
2994
3006
  }
2995
- //#endregion
2996
- //#region src/read/api/text/run.ts
3007
+ // src/read/api/text/run.ts
2997
3008
  /** One text run (`a:r`): a span of text with uniform character formatting. */
2998
3009
  var Run = class {
2999
3010
  element;
@@ -3004,7 +3015,7 @@ var Run = class {
3004
3015
  this.context = context;
3005
3016
  this.inherited = inherited;
3006
3017
  }
3007
- /** The run's text (`a:t`), verbatim — whitespace is not normalized. */
3018
+ /** The run's text (`a:t`), verbatim; whitespace is not normalized. */
3008
3019
  get text() {
3009
3020
  return firstChild(this.element, "a:t")?.textContent ?? "";
3010
3021
  }
@@ -3059,7 +3070,7 @@ var Run = class {
3059
3070
  /**
3060
3071
  * Strikethrough token (`a:rPr/@strike`: `noStrike` | `sngStrike` |
3061
3072
  * `dblStrike`), or `null` when unset (inherited from the style). Surfaced as
3062
- * the raw token — `sngStrike` is the writer's single-strike value.
3073
+ * the raw token; `sngStrike` is the writer's single-strike value.
3063
3074
  */
3064
3075
  get strike() {
3065
3076
  return this.#rPrAttrRaw("strike");
@@ -3077,8 +3088,8 @@ var Run = class {
3077
3088
  * `30`), negative for subscript (`-40`), or `null` when unset. Reported as the
3078
3089
  * percentage (`@baseline` ÷ 1000).
3079
3090
  *
3080
- * `@baseline` is `a:ST_Percentage`, a union that also admits `"62.5%"` — the only form the
3081
- * Strict profile has — so it is read through `parsePercentPoints` rather than as a bare number.
3091
+ * `@baseline` is `a:ST_Percentage`, a union that also admits `"62.5%"` (the only form the
3092
+ * Strict profile has), so it is read through `parsePercentPoints` rather than as a bare number.
3082
3093
  */
3083
3094
  get baselinePct() {
3084
3095
  const rPr = this.#rPr();
@@ -3093,6 +3104,14 @@ var Run = class {
3093
3104
  return ptFromHundredths(this.#rPrAttr("spc"));
3094
3105
  }
3095
3106
  /**
3107
+ * Kerning threshold in points (`a:rPr/@kern`, authored in hundredths of a point), or `null`
3108
+ * when unset: text at this font size or larger is pair-kerned, and `0` means kerning is off.
3109
+ * PowerPoint's own default is `12`. The read counterpart of the write-side `kerning` option.
3110
+ */
3111
+ get kerningPt() {
3112
+ return ptFromHundredths(this.#rPrAttr("kern"));
3113
+ }
3114
+ /**
3096
3115
  * The run's highlight colour (`a:rPr/a:highlight`), resolved to a literal hex
3097
3116
  * through the owning slide's theme, or `null` when the run has no highlight or
3098
3117
  * its colour cannot be made literal. The writer authors highlights from a hex
@@ -3116,6 +3135,83 @@ var Run = class {
3116
3135
  get hyperlink() {
3117
3136
  return readHyperlink(this.#rPr(), this.context.rels);
3118
3137
  }
3138
+ /**
3139
+ * The run's language tag (`a:rPr/@lang`, e.g. `fr-FR`), or `null` when unset. Decides spelling,
3140
+ * hyphenation and font fallback. The read counterpart of the write-side `lang` option.
3141
+ */
3142
+ get language() {
3143
+ return this.#rPrAttrRaw("lang");
3144
+ }
3145
+ /**
3146
+ * The run's own glow (`a:rPr/a:effectLst/a:glow`), resolved against the slide theme, or `null`.
3147
+ * The read counterpart of the write-side text `glow` option; a shape's glow is `Shape.glow`.
3148
+ */
3149
+ get glow() {
3150
+ const rPr = this.#rPr();
3151
+ return readGlow(rPr && firstChild(rPr, "a:effectLst"), this.context.ctx);
3152
+ }
3153
+ /**
3154
+ * The run's own outer shadow (`a:rPr/a:effectLst/a:outerShdw`), resolved against the slide
3155
+ * theme, or `null`. The read counterpart of a run's `shadow` option and of a table cell's,
3156
+ * which writes it on every run of the cell; a shape's shadow is `Shape.shadow`.
3157
+ */
3158
+ get shadow() {
3159
+ const rPr = this.#rPr();
3160
+ return readOuterShadow(rPr && firstChild(rPr, "a:effectLst"), this.context.ctx);
3161
+ }
3162
+ /**
3163
+ * The run's own inner shadow (`a:rPr/a:effectLst/a:innerShdw`), resolved against the slide
3164
+ * theme, or `null`. What a run's `shadow: { type: 'inner' }` writes.
3165
+ */
3166
+ get innerShadow() {
3167
+ const rPr = this.#rPr();
3168
+ return readInnerShadow(rPr && firstChild(rPr, "a:effectLst"), this.context.ctx);
3169
+ }
3170
+ /** The run's text outline (`a:rPr/a:ln`), or `null` when it has none. The read counterpart of `outline`. */
3171
+ get outline() {
3172
+ const rPr = this.#rPr();
3173
+ const ln = rPr && firstChild(rPr, "a:ln");
3174
+ if (!ln) return null;
3175
+ const fill = firstChild(ln, "a:solidFill");
3176
+ return {
3177
+ widthPt: ptFromEmu(numberValue(attr(ln, "w"))),
3178
+ noFill: firstChild(ln, "a:noFill") !== null,
3179
+ colorRef: readColorRef(fill && firstChildElement(fill), this.context.ctx)
3180
+ };
3181
+ }
3182
+ /**
3183
+ * The underline's own colour (`a:rPr/a:uFill/a:solidFill`), as written and resolved, or `null`
3184
+ * when the underline follows the text colour. The read counterpart of `underline.color`.
3185
+ */
3186
+ get underlineColor() {
3187
+ const rPr = this.#rPr();
3188
+ const uFill = rPr && firstChild(rPr, "a:uFill");
3189
+ const fill = uFill && firstChild(uFill, "a:solidFill");
3190
+ return fill ? readColorRef(firstChildElement(fill), this.context.ctx) : null;
3191
+ }
3192
+ /**
3193
+ * The East Asian typeface (`a:rPr/a:ea/@typeface`), or `null` when unset. {@link fontName} is the
3194
+ * Latin slot. The read counterpart of the write-side `fontFaceEA` option.
3195
+ */
3196
+ get eastAsianFontName() {
3197
+ const rPr = this.#rPr();
3198
+ const ea = rPr && firstChild(rPr, "a:ea");
3199
+ return ea ? attr(ea, "typeface") : null;
3200
+ }
3201
+ /**
3202
+ * How many line breaks (`a:br`) sit directly before this run in its paragraph, with no run or
3203
+ * field between them. A soft break is not a run, so without this the runs on either side of one
3204
+ * read as plain neighbours. The read counterpart of the write-side `softBreakBefore` option.
3205
+ */
3206
+ get breaksBefore() {
3207
+ let count = 0;
3208
+ for (let node = this.element.previousSibling; node; node = node.previousSibling) {
3209
+ if (node.nodeType !== 1) continue;
3210
+ if (node.localName !== "br") break;
3211
+ count++;
3212
+ }
3213
+ return count;
3214
+ }
3119
3215
  /** Latin typeface name (`a:rPr/a:latin/@typeface`), or `null` when unset. */
3120
3216
  get fontName() {
3121
3217
  const rPr = this.#rPr();
@@ -3152,7 +3248,7 @@ var Run = class {
3152
3248
  * The colour this run effectively renders, resolved against the owning slide's
3153
3249
  * theme to a literal hex. It is the run's own solid fill
3154
3250
  * ({@link color}/{@link schemeColor}) when set; otherwise the shape's
3155
- * `p:style/a:fontRef` colour, then — for a run inside a placeholder — the colour
3251
+ * `p:style/a:fontRef` colour, then (for a run inside a placeholder) the colour
3156
3252
  * it inherits from the placeholder/list-style chain (layout → master placeholder
3157
3253
  * `a:lstStyle` → master `p:txStyles`), then the presentation's
3158
3254
  * `p:defaultTextStyle`. `null` when the run sets no colour and inherits none, or the
@@ -3160,12 +3256,24 @@ var Run = class {
3160
3256
  * `null` too when the run's own fill is not a solid colour (`a:noFill`, a gradient):
3161
3257
  * the run decides its own colour then, and the one it would otherwise inherit is not
3162
3258
  * what it paints in, the rule `Shape.resolvedFill` and `TableCell.resolvedFill` follow.
3163
- * The returned {@link ResolvedColor} carries `effectiveHex` — the base colour with
3164
- * its child transforms (`lumMod`/`shade`/…) applied — for the final rendered colour.
3259
+ * A run with a click hyperlink (`a:hlinkClick`) and no fill of its own is painted in the
3260
+ * theme's `hlink` colour instead, resolved through the slide's colour map, unless the link
3261
+ * carries the 2018 `ahyp:hlinkClr val="tx"` extension, which asks for the text colour above.
3262
+ * The returned {@link ResolvedColor} carries `effectiveHex`, the base colour with
3263
+ * its child transforms (`lumMod`/`shade`/…) applied, for the final rendered colour.
3165
3264
  */
3166
3265
  get resolvedColor() {
3167
3266
  const rPr = this.#rPr();
3168
3267
  if (hasFillChoice(rPr)) return resolveSolidFillColor(rPr, this.context.ctx);
3268
+ const hlinkClick = rPr && firstChild(rPr, "a:hlinkClick");
3269
+ if (hlinkClick && !followsTextColor(hlinkClick)) {
3270
+ const hex = resolveSchemeToken("hlink", this.context.ctx);
3271
+ return hex ? {
3272
+ hex,
3273
+ transforms: [],
3274
+ effectiveHex: hex
3275
+ } : null;
3276
+ }
3169
3277
  const inherit = this.context.inherit;
3170
3278
  return inherit?.fontRef?.color ?? inherit?.tableText?.color ?? this.inherited?.color() ?? null;
3171
3279
  }
@@ -3174,7 +3282,7 @@ var Run = class {
3174
3282
  * ({@link fontSizePt}) when set; otherwise, for a run inside a placeholder, the
3175
3283
  * size it inherits from the placeholder/list-style chain (paragraph `a:defRPr` →
3176
3284
  * slide `a:lstStyle` → layout → master placeholder `a:lstStyle` → master
3177
- * `p:txStyles`). `null` when the run sets no size and inherits none — the
3285
+ * `p:txStyles`). `null` when the run sets no size and inherits none. This is the
3178
3286
  * resolved counterpart of {@link fontSizePt}, which reports only the run's own value.
3179
3287
  */
3180
3288
  get resolvedSizePt() {
@@ -3183,13 +3291,14 @@ var Run = class {
3183
3291
  /**
3184
3292
  * The typeface this run effectively renders, resolved to a literal face name. It
3185
3293
  * is the run's own `a:latin` ({@link fontName}) when set; otherwise the face named
3186
- * by the shape's `p:style/a:fontRef` (`idx` → theme major/minor font), then — for a
3187
- * run inside a placeholder — the face it inherits from the placeholder/list-style
3294
+ * by the shape's `p:style/a:fontRef` (`idx` → theme major/minor font), then (for a
3295
+ * run inside a placeholder) the face it inherits from the placeholder/list-style
3188
3296
  * chain, then the presentation's `p:defaultTextStyle`. A `+mj-*`/`+mn-*`
3189
- * major/minor theme-font token — on the run itself or reached through the chain —
3297
+ * major/minor theme-font token, on the run itself or reached through the chain,
3190
3298
  * is resolved through the theme `fontScheme` to its concrete face. `null` when the
3191
- * run names no face and inherits none, or a token cannot be resolved — the resolved
3192
- * counterpart of {@link fontName}, which reports the raw `@typeface` (possibly a token).
3299
+ * run names no face and inherits none, or a token cannot be resolved. This is the
3300
+ * resolved counterpart of {@link fontName}, which reports the raw `@typeface` (possibly a
3301
+ * token).
3193
3302
  */
3194
3303
  get resolvedFontFace() {
3195
3304
  const own = this.fontName;
@@ -3202,7 +3311,7 @@ var Run = class {
3202
3311
  * ({@link bold}) when set; otherwise, for a run inside a placeholder, the bold
3203
3312
  * state it inherits from the placeholder/list-style chain (paragraph `a:defRPr` →
3204
3313
  * slide `a:lstStyle` → layout → master placeholder `a:lstStyle` → master
3205
- * `p:txStyles`). `null` when the run sets no `@b` and inherits none — the
3314
+ * `p:txStyles`). `null` when the run sets no `@b` and inherits none. This is the
3206
3315
  * resolved counterpart of {@link bold}, which reports only the run's own value.
3207
3316
  */
3208
3317
  get resolvedBold() {
@@ -3213,7 +3322,7 @@ var Run = class {
3213
3322
  * ({@link italic}) when set; otherwise, for a run inside a placeholder, the italic
3214
3323
  * state it inherits from the placeholder/list-style chain (paragraph `a:defRPr` →
3215
3324
  * slide `a:lstStyle` → layout → master placeholder `a:lstStyle` → master
3216
- * `p:txStyles`). `null` when the run sets no `@i` and inherits none — the
3325
+ * `p:txStyles`). `null` when the run sets no `@i` and inherits none. This is the
3217
3326
  * resolved counterpart of {@link italic}, and the twin of {@link resolvedBold}:
3218
3327
  * `@b` and `@i` are siblings a master text style states together, so a deck that
3219
3328
  * can be authored with an inherited italic can be read back with one.
@@ -3261,8 +3370,31 @@ var Run = class {
3261
3370
  if (edit === null ? applySolidFill(this.#rPr(), null) : applySolidFill(this.#rPr(), edit, () => this.#getOrAddRPr(), RPR_FILL_AFTER)) this.context.part.markDirty();
3262
3371
  }
3263
3372
  };
3264
- //#endregion
3265
- //#region src/read/api/text/edit.ts
3373
+ /** Whether a hyperlink's `ahyp:hlinkClr val="tx"` extension asks for the text colour rather than the theme's `hlink`. */
3374
+ function followsTextColor(hlinkClick) {
3375
+ const extLst = firstChild(hlinkClick, "a:extLst");
3376
+ if (!extLst) return false;
3377
+ for (const ext of getElements(extLst, "a:ext")) for (const child of childElements(ext)) if (child.localName === "hlinkClr" && child.namespaceURI === OOXML_NS.ahyp) return attr(child, "val") === "tx";
3378
+ return false;
3379
+ }
3380
+ /**
3381
+ * One text field (`a:fld`): text PowerPoint computes as it renders, such as a slide number
3382
+ * (`slidenum`) or a date (`datetime1`). A field carries the same `a:rPr` a run does, so it reads
3383
+ * and resolves its formatting like one. {@link text} is the rendering PowerPoint last cached,
3384
+ * which it recomputes on open.
3385
+ */
3386
+ var Field = class extends Run {
3387
+ runIndex;
3388
+ constructor(element, context, inherited, runIndex) {
3389
+ super(element, context, inherited);
3390
+ this.runIndex = runIndex;
3391
+ }
3392
+ /** The field type (`@type`), such as `slidenum` or `datetime1`, or `null` when unset. */
3393
+ get type() {
3394
+ return attr(this.element_, "type");
3395
+ }
3396
+ };
3397
+ // src/read/api/text/edit.ts
3266
3398
  /**
3267
3399
  * The two free text-body mutators, shared by the three proxies that own an `a:txBody`.
3268
3400
  *
@@ -3274,7 +3406,7 @@ var Run = class {
3274
3406
  * Replace a text body's content (`a:txBody` or `a:txBody`-shaped element) with a
3275
3407
  * single paragraph and run, preserving the `a:rPr` of the body's first existing
3276
3408
  * run when there is one. Shared by {@link TextFrame.text} and `TableCell.text`.
3277
- * Does **not** mark any part dirty — the caller owns the `Part` and must call
3409
+ * Does **not** mark any part dirty: the caller owns the `Part` and must call
3278
3410
  * `markDirty()` after.
3279
3411
  */
3280
3412
  function setTextBodyText(txBody, value) {
@@ -3298,7 +3430,7 @@ function setTextBodyText(txBody, value) {
3298
3430
  * Sibling paragraphs are untouched, which is the whole difference from
3299
3431
  * {@link setTextBodyText} and the reason this exists: a SmartArt drawing cache packs several
3300
3432
  * nodes' text into one `dsp:txBody`, so collapsing the body there would delete the other
3301
- * nodes' strings. Does **not** mark any part dirty — the caller owns the `Part`.
3433
+ * nodes' strings. Does **not** mark any part dirty; the caller owns the `Part`.
3302
3434
  */
3303
3435
  function setParagraphText(p, value) {
3304
3436
  const doc = p.ownerDocument;
@@ -3316,12 +3448,11 @@ function setParagraphText(p, value) {
3316
3448
  const endParaRPr = firstChild(p, "a:endParaRPr");
3317
3449
  p.insertBefore(run, endParaRPr);
3318
3450
  }
3319
- //#endregion
3320
- //#region src/read/api/text/paragraph.ts
3451
+ // src/read/api/text/paragraph.ts
3321
3452
  /**
3322
3453
  * The `Paragraph` read/write proxy (`a:p`).
3323
3454
  *
3324
- * A paragraph owns its `a:pPr` — alignment, indents, spacing, the bullet — and hands each
3455
+ * A paragraph owns its `a:pPr` (alignment, indents, spacing, the bullet) and hands each
3325
3456
  * `a:r` to a {@link Run}. It is handed its frame's {@link TextContext}, and builds the one
3326
3457
  * {@link InheritedRunProps} its runs share.
3327
3458
  */
@@ -3335,12 +3466,51 @@ var Paragraph = class {
3335
3466
  this.context = context;
3336
3467
  this.slideLstStyle = slideLstStyle;
3337
3468
  }
3338
- /** The runs (`a:r`) in document order. Fields (`a:fld`) and breaks are not runs; see `text`. */
3469
+ /** The runs (`a:r`) in document order. Fields (`a:fld`) are not runs (see {@link fields}), nor are breaks. */
3339
3470
  get runs() {
3340
3471
  const inherited = this.#inheritedRunProps();
3341
3472
  return getElements(this.element, "a:r").map((element) => new Run(element, this.context, inherited));
3342
3473
  }
3343
3474
  /**
3475
+ * The OMML equations (`a14:m`) in document order. An equation is neither a run nor part of
3476
+ * {@link text}; each says how many runs precede it, so an inline one keeps its place.
3477
+ */
3478
+ get equations() {
3479
+ const equations = [];
3480
+ let runIndex = 0;
3481
+ for (let node = this.element.firstChild; node; node = node.nextSibling) {
3482
+ if (node.nodeType !== 1) continue;
3483
+ const element = node;
3484
+ if (element.namespaceURI === OOXML_NS.a && element.localName === "r") runIndex++;
3485
+ if (element.namespaceURI !== OOXML_NS.a14 || element.localName !== "m") continue;
3486
+ const omml = firstChildElement(element);
3487
+ if (omml?.namespaceURI !== OOXML_NS.m) continue;
3488
+ equations.push({
3489
+ omml: serializeXml(omml),
3490
+ inline: omml.localName === "oMath",
3491
+ runIndex
3492
+ });
3493
+ }
3494
+ return equations;
3495
+ }
3496
+ /**
3497
+ * The fields (`a:fld`) in document order, each read like a run; see {@link Field}. Each says how
3498
+ * many runs precede it, so a field between runs keeps its place.
3499
+ */
3500
+ get fields() {
3501
+ const inherited = this.#inheritedRunProps();
3502
+ const fields = [];
3503
+ let runIndex = 0;
3504
+ for (let node = this.element.firstChild; node; node = node.nextSibling) {
3505
+ if (node.nodeType !== 1) continue;
3506
+ const element = node;
3507
+ if (element.namespaceURI !== OOXML_NS.a) continue;
3508
+ if (element.localName === "r") runIndex++;
3509
+ else if (element.localName === "fld") fields.push(new Field(element, this.context, inherited, runIndex));
3510
+ }
3511
+ return fields;
3512
+ }
3513
+ /**
3344
3514
  * What every run in this paragraph inherits when it sets no value of its own, or `null` for text
3345
3515
  * that inherits through nothing. Runs in one paragraph share a level and `a:pPr`, so each
3346
3516
  * property is resolved at most once per paragraph, and only when a run lacking its own value asks.
@@ -3364,6 +3534,15 @@ var Paragraph = class {
3364
3534
  italic: once(resolveInheritedRunItalic)
3365
3535
  };
3366
3536
  }
3537
+ /**
3538
+ * The language of the paragraph's end mark (`a:endParaRPr/@lang`, e.g. `fr-FR`), or `null` when
3539
+ * unset. It is what text typed at the end of the paragraph, or into an empty one, is spelled in.
3540
+ * The write side states it from the text's `lang` option.
3541
+ */
3542
+ get endLanguage() {
3543
+ const end = firstChild(this.element, "a:endParaRPr");
3544
+ return end ? attr(end, "lang") : null;
3545
+ }
3367
3546
  /** Indent level (`a:pPr/@lvl`), 0 when unset. */
3368
3547
  get level() {
3369
3548
  const pPr = firstChild(this.element, "a:pPr");
@@ -3374,8 +3553,7 @@ var Paragraph = class {
3374
3553
  * `dist` | `thaiDist`), or `null` when unset (inherited from the list style).
3375
3554
  */
3376
3555
  get align() {
3377
- const pPr = firstChild(this.element, "a:pPr");
3378
- return pPr ? attr(pPr, "algn") : null;
3556
+ return alignOf(this.#pPr());
3379
3557
  }
3380
3558
  /**
3381
3559
  * Space before the paragraph in points (`a:pPr/a:spcBef/a:spcPts/@val` is
@@ -3383,11 +3561,11 @@ var Paragraph = class {
3383
3561
  * (`a:spcPct`, which has no fixed point value).
3384
3562
  */
3385
3563
  get spaceBeforePt() {
3386
- return this.#spacingPt("a:spcBef");
3564
+ return spacingPt(this.#pPr(), "a:spcBef");
3387
3565
  }
3388
3566
  /** Space after the paragraph in points; see {@link spaceBeforePt} for the percentage caveat. */
3389
3567
  get spaceAfterPt() {
3390
- return this.#spacingPt("a:spcAft");
3568
+ return spacingPt(this.#pPr(), "a:spcAft");
3391
3569
  }
3392
3570
  /**
3393
3571
  * The paragraph's line spacing (`a:pPr/a:lnSpc`), as an exact point height
@@ -3395,37 +3573,112 @@ var Paragraph = class {
3395
3573
  * `null` when unset (inherited from the list style). See {@link LineSpacing}.
3396
3574
  */
3397
3575
  get lineSpacing() {
3398
- const pPr = firstChild(this.element, "a:pPr");
3399
- const lnSpc = pPr && firstChild(pPr, "a:lnSpc");
3400
- if (!lnSpc) return null;
3401
- const pts = firstChild(lnSpc, "a:spcPts");
3402
- if (pts) {
3403
- const val = numberValue(attr(pts, "val"));
3404
- return val === null ? null : {
3405
- type: "points",
3406
- valuePt: val / 100
3407
- };
3408
- }
3409
- const pct = firstChild(lnSpc, "a:spcPct");
3410
- if (pct) {
3411
- const val = pctPointsAttr(pct, "val");
3412
- return val === null ? null : {
3413
- type: "percent",
3414
- percent: val
3415
- };
3416
- }
3417
- return null;
3576
+ return lineSpacingOf(this.#pPr());
3418
3577
  }
3419
3578
  /** Left margin of the paragraph in points (`a:pPr/@marL` is EMU; 12700 EMU = 1pt), or `null` when unset. */
3420
3579
  get marginLeftPt() {
3421
- return this.#emuAttrPt("marL");
3580
+ return emuAttrPt(this.#pPr(), "marL");
3422
3581
  }
3423
3582
  /** First-line indent in points (`a:pPr/@indent` is EMU; negative for a hanging indent), or `null` when unset. */
3424
3583
  get indentPt() {
3425
- return this.#emuAttrPt("indent");
3584
+ return emuAttrPt(this.#pPr(), "indent");
3585
+ }
3586
+ /**
3587
+ * The alignment the paragraph renders with: its own, else the first its level inherits through
3588
+ * the text body's `a:lstStyle`, the placeholder's layout and master list styles, the master's
3589
+ * `p:txStyles` and the presentation's `p:defaultTextStyle`, the chain {@link Run.resolvedSizePt}
3590
+ * walks. `null` when nothing in the chain states one (PowerPoint then aligns left).
3591
+ */
3592
+ get resolvedAlign() {
3593
+ return this.#resolved(alignOf);
3594
+ }
3595
+ /** The left margin in points the paragraph renders with, resolved as {@link resolvedAlign} is. */
3596
+ get resolvedMarginLeftPt() {
3597
+ return this.#resolved((pPr) => emuAttrPt(pPr, "marL"));
3598
+ }
3599
+ /** The first-line indent in points the paragraph renders with, resolved as {@link resolvedAlign} is. */
3600
+ get resolvedIndentPt() {
3601
+ return this.#resolved((pPr) => emuAttrPt(pPr, "indent"));
3602
+ }
3603
+ /** The line spacing the paragraph renders with, resolved as {@link resolvedAlign} is. */
3604
+ get resolvedLineSpacing() {
3605
+ return this.#resolved(lineSpacingOf);
3606
+ }
3607
+ /** The space before the paragraph in points, resolved as {@link resolvedAlign} is. */
3608
+ get resolvedSpaceBeforePt() {
3609
+ return this.#resolved((pPr) => spacingPt(pPr, "a:spcBef"));
3610
+ }
3611
+ /** The space after the paragraph in points, resolved as {@link resolvedAlign} is. */
3612
+ get resolvedSpaceAfterPt() {
3613
+ return this.#resolved((pPr) => spacingPt(pPr, "a:spcAft"));
3614
+ }
3615
+ /**
3616
+ * The bullet the paragraph renders with: its own {@link bulletDetail}, else the first bullet its
3617
+ * level inherits, resolved as {@link resolvedAlign} is. A picture bullet from an inherited tier
3618
+ * reads with `imagePartName: null`, its image being a relationship of the layout or master.
3619
+ * `null` when nothing in the chain names one.
3620
+ */
3621
+ get resolvedBulletDetail() {
3622
+ const own = this.bulletDetail;
3623
+ if (own) return own;
3624
+ for (const tier of this.#inheritedLevels()) {
3625
+ const detail = this.#bulletDetailOf(tier, false);
3626
+ if (detail) return detail;
3627
+ }
3628
+ return null;
3629
+ }
3630
+ /** The paragraph's own `a:pPr`, or `null`. */
3631
+ #pPr() {
3632
+ return firstChild(this.element, "a:pPr");
3426
3633
  }
3427
3634
  /**
3428
- * This paragraph's bullet — its kind, and for a drawn bullet the glyph, the
3635
+ * The level elements the paragraph inherits paragraph properties from, most specific first: the
3636
+ * text body's `a:lstStyle`, then for a placeholder its layout and master list styles and the
3637
+ * master's `p:txStyles`, then `p:defaultTextStyle`. Empty for text that inherits through nothing.
3638
+ */
3639
+ #inheritedLevels() {
3640
+ const inherit = this.context.inherit;
3641
+ if (!inherit) return [];
3642
+ const level = this.level;
3643
+ const { ctx } = this.context;
3644
+ const tiers = [lstStyleLevel(this.slideLstStyle, level)];
3645
+ if (inherit.ph) tiers.push(...placeholderInheritedLevels(inherit.ph.type, inherit.ph.idx, level, ctx));
3646
+ tiers.push(lstStyleLevel(ctx.defaultTextStyle ?? null, level));
3647
+ return tiers.filter((tier) => tier !== null);
3648
+ }
3649
+ /** The first value `read` finds in the paragraph's own `a:pPr`, then down {@link inheritedLevels}. */
3650
+ #resolved(read) {
3651
+ const own = read(this.#pPr());
3652
+ if (own !== null) return own;
3653
+ for (const tier of this.#inheritedLevels()) {
3654
+ const value = read(tier);
3655
+ if (value !== null) return value;
3656
+ }
3657
+ return null;
3658
+ }
3659
+ /**
3660
+ * Right-to-left paragraph direction (`a:pPr/@rtl`), or `null` when unset (left to right). The
3661
+ * read counterpart of the write-side `rtlMode` option.
3662
+ */
3663
+ get rtl() {
3664
+ const pPr = firstChild(this.element, "a:pPr");
3665
+ return pPr ? boolValue(attr(pPr, "rtl")) : null;
3666
+ }
3667
+ /**
3668
+ * The paragraph's own tab stops (`a:pPr/a:tabLst/a:tab`) in document order, or `null` when it
3669
+ * states no tab list and inherits one. The read counterpart of the write-side `tabStops` option.
3670
+ */
3671
+ get tabStops() {
3672
+ const pPr = firstChild(this.element, "a:pPr");
3673
+ const tabLst = pPr && firstChild(pPr, "a:tabLst");
3674
+ if (!tabLst) return null;
3675
+ return getElements(tabLst, "a:tab").map((tab) => ({
3676
+ positionPt: ptFromEmu(numberValue(attr(tab, "pos"))),
3677
+ alignment: attr(tab, "algn") ?? null
3678
+ }));
3679
+ }
3680
+ /**
3681
+ * This paragraph's bullet: its kind, and for a drawn bullet the glyph, the
3429
3682
  * numbering start, and the bullet's own font/size/colour. `null` when the
3430
3683
  * paragraph names no bullet at all and inherits one from the list style.
3431
3684
  *
@@ -3433,11 +3686,18 @@ var Paragraph = class {
3433
3686
  * previous `bullet` accessor reported `'none'` / `'char:•'` /
3434
3687
  * `'autoNum:arabicPeriod'`, and that shape is ambiguous when the glyph is
3435
3688
  * itself a colon; worse, it reads as a bare glyph, which is exactly how the
3436
- * script converter first consumed it — `'none'.codePointAt(0)` put a literal
3689
+ * script converter first consumed it: `'none'.codePointAt(0)` put a literal
3437
3690
  * `n` bullet on every converted deck, silently. See {@link BulletDetail}.
3438
3691
  */
3439
3692
  get bulletDetail() {
3440
- const pPr = firstChild(this.element, "a:pPr");
3693
+ return this.#bulletDetailOf(this.#pPr(), true);
3694
+ }
3695
+ /**
3696
+ * The bullet a paragraph-properties element names, or `null`.
3697
+ * @param resolveImage - resolve a picture bullet's image through this slide's relationships,
3698
+ * which only holds for the paragraph's own `a:pPr`
3699
+ */
3700
+ #bulletDetailOf(pPr, resolveImage) {
3441
3701
  if (!pPr) return null;
3442
3702
  if (firstChild(pPr, "a:buNone")) return { kind: "none" };
3443
3703
  const style = this.#bulletStyle(pPr);
@@ -3457,7 +3717,7 @@ var Paragraph = class {
3457
3717
  const buBlip = firstChild(pPr, "a:buBlip");
3458
3718
  if (buBlip) return {
3459
3719
  kind: "picture",
3460
- imagePartName: this.#bulletImagePartName(buBlip),
3720
+ imagePartName: resolveImage ? this.#bulletImagePartName(buBlip) : null,
3461
3721
  ...style
3462
3722
  };
3463
3723
  return null;
@@ -3489,18 +3749,6 @@ var Paragraph = class {
3489
3749
  const blip = firstChild(buBlip, "a:blip");
3490
3750
  return resolvePartName(blip && attr(blip, "r:embed"), this.context.rels);
3491
3751
  }
3492
- /** Points from a spacing child's `a:spcPts/@val` (hundredths of a point), or `null`. */
3493
- #spacingPt(qname) {
3494
- const pPr = firstChild(this.element, "a:pPr");
3495
- const spc = pPr && firstChild(pPr, qname);
3496
- const pts = spc && firstChild(spc, "a:spcPts");
3497
- return ptFromHundredths(pts ? numberValue(attr(pts, "val")) : null);
3498
- }
3499
- /** Points from an EMU-valued `a:pPr` attribute (`marL` / `indent`), or `null`. */
3500
- #emuAttrPt(name) {
3501
- const pPr = firstChild(this.element, "a:pPr");
3502
- return ptFromEmu(pPr ? numberValue(attr(pPr, name)) : null);
3503
- }
3504
3752
  /**
3505
3753
  * The paragraph's text: run (`a:r`) and field (`a:fld`) text concatenated in
3506
3754
  * document order, with each line break (`a:br`) rendered as `\n`.
@@ -3518,7 +3766,7 @@ var Paragraph = class {
3518
3766
  /**
3519
3767
  * Replace **this paragraph's** content with a single run, preserving the character
3520
3768
  * formatting (`a:rPr`) of its first existing run and leaving its `a:pPr` (level,
3521
- * alignment, bullet) alone. Sibling paragraphs are untouched — the difference from
3769
+ * alignment, bullet) alone. Sibling paragraphs are untouched. That is the difference from
3522
3770
  * {@link TextFrame.text}, which collapses the whole body to one paragraph.
3523
3771
  */
3524
3772
  set text(value) {
@@ -3535,8 +3783,43 @@ var Paragraph = class {
3535
3783
  }
3536
3784
  };
3537
3785
  /** A shape's text frame (`p:txBody`): an ordered list of paragraphs. */
3538
- //#endregion
3539
- //#region src/read/api/text/frame.ts
3786
+ /** `@algn` of a paragraph-properties element (`a:pPr` or a list-style level), or `null`. */
3787
+ function alignOf(pPr) {
3788
+ return pPr ? attr(pPr, "algn") : null;
3789
+ }
3790
+ /** Points from a spacing child's `a:spcPts/@val` (hundredths of a point), or `null`. */
3791
+ function spacingPt(pPr, qname) {
3792
+ const spc = pPr && firstChild(pPr, qname);
3793
+ const pts = spc && firstChild(spc, "a:spcPts");
3794
+ return ptFromHundredths(pts ? numberValue(attr(pts, "val")) : null);
3795
+ }
3796
+ /** Points from an EMU-valued paragraph-properties attribute (`marL` / `indent`), or `null`. */
3797
+ function emuAttrPt(pPr, name) {
3798
+ return ptFromEmu(pPr ? numberValue(attr(pPr, name)) : null);
3799
+ }
3800
+ /** The `a:lnSpc` of a paragraph-properties element as a {@link LineSpacing}, or `null`. */
3801
+ function lineSpacingOf(pPr) {
3802
+ const lnSpc = pPr && firstChild(pPr, "a:lnSpc");
3803
+ if (!lnSpc) return null;
3804
+ const pts = firstChild(lnSpc, "a:spcPts");
3805
+ if (pts) {
3806
+ const val = numberValue(attr(pts, "val"));
3807
+ return val === null ? null : {
3808
+ type: "points",
3809
+ valuePt: val / 100
3810
+ };
3811
+ }
3812
+ const pct = firstChild(lnSpc, "a:spcPct");
3813
+ if (pct) {
3814
+ const val = pctPointsAttr(pct, "val");
3815
+ return val === null ? null : {
3816
+ type: "percent",
3817
+ percent: val
3818
+ };
3819
+ }
3820
+ return null;
3821
+ }
3822
+ // src/read/api/text/frame.ts
3540
3823
  /**
3541
3824
  * The `TextFrame` read/write proxy (`p:txBody` / `a:txBody`), and its `a:bodyPr` value types.
3542
3825
  *
@@ -3556,9 +3839,30 @@ var TextFrame = class {
3556
3839
  return getElements(this.txBody, "a:p").map((element) => new Paragraph(element, this.context, slideLstStyle));
3557
3840
  }
3558
3841
  /**
3842
+ * The character defaults this frame's own list style states for paragraphs at `level`
3843
+ * (0-based): `a:lstStyle/a:lvlNpPr/a:defRPr`, else `a:defPPr/a:defRPr`. `null` when the frame
3844
+ * has no list style or it states no defaults for that level.
3845
+ *
3846
+ * Only the frame's own tier. What a run inherits through it, and through a placeholder's
3847
+ * layout and master, is the run's `resolved*` reads.
3848
+ */
3849
+ listStyleDefaults(level = 0) {
3850
+ const defRPr = lstStyleLevelDefRPr(firstChild(this.txBody, "a:lstStyle"), level);
3851
+ if (!defRPr) return null;
3852
+ const fill = firstChild(defRPr, "a:solidFill");
3853
+ const latin = firstChild(defRPr, "a:latin");
3854
+ return {
3855
+ fontSizePt: ptFromHundredths(numberValue(attr(defRPr, "sz"))),
3856
+ fontName: latin && attr(latin, "typeface") || null,
3857
+ bold: boolValue(attr(defRPr, "b")),
3858
+ italic: boolValue(attr(defRPr, "i")),
3859
+ colorRef: fill ? readColorRef(firstChildElement(fill), this.context.ctx) : null
3860
+ };
3861
+ }
3862
+ /**
3559
3863
  * The frame's body properties (`a:bodyPr`: insets, anchor, vertical text), or
3560
3864
  * `null` when there is no `a:bodyPr`. Only explicitly-set insets are reported
3561
- * (a missing side is the PowerPoint default — see {@link BodyProperties}).
3865
+ * (a missing side is the PowerPoint default; see {@link BodyProperties}).
3562
3866
  */
3563
3867
  get bodyProperties() {
3564
3868
  const bodyPr = firstChild(this.txBody, "a:bodyPr");
@@ -3572,11 +3876,16 @@ var TextFrame = class {
3572
3876
  inset("rIns", "right");
3573
3877
  inset("tIns", "top");
3574
3878
  inset("bIns", "bottom");
3879
+ const spcCol = numberValue(attr(bodyPr, "spcCol"));
3880
+ const warp = firstChild(bodyPr, "a:prstTxWarp");
3575
3881
  return {
3576
3882
  vert: attr(bodyPr, "vert") ?? null,
3577
3883
  anchor: attr(bodyPr, "anchor") ?? null,
3578
3884
  wrap: attr(bodyPr, "wrap") ?? null,
3579
- insetsPt
3885
+ insetsPt,
3886
+ columns: numberValue(attr(bodyPr, "numCol")),
3887
+ columnSpacingPt: spcCol === null ? null : emuToPoints(spcCol),
3888
+ textWarp: warp ? attr(warp, "prst") ?? null : null
3580
3889
  };
3581
3890
  }
3582
3891
  /**
@@ -3585,7 +3894,7 @@ var TextFrame = class {
3585
3894
  * layout → master placeholder `a:bodyPr`. `null` when nothing in the chain sets
3586
3895
  * one (PowerPoint then defaults to top). Unlike {@link bodyProperties}'s
3587
3896
  * `anchor` (own attribute only), this surfaces the inherited anchor a
3588
- * placeholder title relies on — the difference between a top- and
3897
+ * placeholder title relies on: the difference between a top- and
3589
3898
  * centre-anchored title that own-attribute reads alone cannot see.
3590
3899
  */
3591
3900
  get resolvedAnchor() {
@@ -3597,8 +3906,8 @@ var TextFrame = class {
3597
3906
  }
3598
3907
  /**
3599
3908
  * The frame's vertical-autofit mode (`a:bodyPr` autofit child), or `null` when
3600
- * there is no `a:bodyPr`. A `bodyPr` with no autofit child — or an explicit
3601
- * `a:noAutofit` — reads `'none'`. See {@link AutofitMode}. This is the deep-model
3909
+ * there is no `a:bodyPr`. A `bodyPr` with no autofit child, or an explicit
3910
+ * `a:noAutofit`, reads `'none'`. See {@link AutofitMode}. This is the deep-model
3602
3911
  * counterpart of `inspectPptx`'s per-shape `autofit`; unlike the shallow surface
3603
3912
  * it hangs off the navigable text frame.
3604
3913
  */
@@ -3612,8 +3921,8 @@ var TextFrame = class {
3612
3921
  /**
3613
3922
  * The baked shrink scale of a `normAutofit` frame as a percent (62.5 = 62.5%),
3614
3923
  * or `null` when the frame has no `a:normAutofit` or bakes no scale. A bare
3615
- * `<a:normAutofit/>` (write-side `fit: 'shrink'`) carries no scale — PowerPoint
3616
- * computes it on edit and draws at 100% until then — so it too reads `null`; an
3924
+ * `<a:normAutofit/>` (write-side `fit: 'shrink'`) carries no scale (PowerPoint
3925
+ * computes it on edit and draws at 100% until then), so it too reads `null`; an
3617
3926
  * explicit scale comes from `fit: { type: 'shrink', fontScale }`.
3618
3927
  */
3619
3928
  get autofitFontScale() {
@@ -3662,17 +3971,16 @@ var TextFrame = class {
3662
3971
  this.context.part.markDirty();
3663
3972
  }
3664
3973
  };
3665
- //#endregion
3666
- //#region src/read/api/animation.ts
3974
+ // src/read/api/animation.ts
3667
3975
  /**
3668
3976
  * Spid-aware, otherwise-opaque handling of a slide's build-animation tree
3669
3977
  * (`p:timing` / `p:bldLst`).
3670
3978
  *
3671
- * The `p:timing` subtree is `CT_TimeNodeList` — deeply recursive and modeled
3979
+ * The `p:timing` subtree is `CT_TimeNodeList`, deeply recursive and modeled
3672
3980
  * here as opaque DOM (an unmodified slide round-trips it byte-identically for
3673
3981
  * free). The only structured data we extract or rewrite is the set of `spid`
3674
- * references — `<p:spTgt spid>` inside the timing tree and `<p:bldP spid>` in the
3675
- * build list — so animations stay coherent when shape ids change. Three purely
3982
+ * references (`<p:spTgt spid>` inside the timing tree and `<p:bldP spid>` in the
3983
+ * build list), so animations stay coherent when shape ids change. Three purely
3676
3984
  * structural operations: **enumerate**, **remap**, and **prune**. See
3677
3985
  * `docs/contributing/design/animations.md` ("spid-aware operations").
3678
3986
  */
@@ -3686,7 +3994,7 @@ function spidElements(root) {
3686
3994
  return out;
3687
3995
  }
3688
3996
  /**
3689
- * Whether the slide carries build animations — a `<p:bldP>` entry or any timing
3997
+ * Whether the slide carries build animations: a `<p:bldP>` entry or any timing
3690
3998
  * node with a `presetID` (distinguishing animation timing from the media-loop
3691
3999
  * timing that has neither).
3692
4000
  */
@@ -3695,6 +4003,61 @@ function hasAnimations(root) {
3695
4003
  for (const cTn of root.getElementsByTagNameNS(OOXML_NS.p, "cTn")) if (attr(cTn, "presetID") !== null) return true;
3696
4004
  return false;
3697
4005
  }
4006
+ const TRIGGERS = {
4007
+ clickEffect: "onClick",
4008
+ withEffect: "withPrevious",
4009
+ afterEffect: "afterPrevious"
4010
+ };
4011
+ /**
4012
+ * The time node enclosing `cTn`: its `p:par` (or `p:seq`) sits in a `p:childTnLst`, whose parent is
4013
+ * the enclosing `p:cTn`. `null` at the top of the tree.
4014
+ */
4015
+ function parentTimeNode(cTn) {
4016
+ const parent = cTn.parentNode?.parentNode?.parentNode;
4017
+ if (!parent || parent.nodeType !== 1) return null;
4018
+ const element = parent;
4019
+ return element.localName === "cTn" && element.namespaceURI === P_NS ? element : null;
4020
+ }
4021
+ /** A number of milliseconds from a time attribute, or `null` for `indefinite` and anything else. */
4022
+ function millis(value) {
4023
+ if (value === null || !/^\d+$/.test(value)) return null;
4024
+ return Number(value);
4025
+ }
4026
+ /** Every preset effect in the slide's `p:timing`, in document order. See {@link BuildEffect}. */
4027
+ function readBuildEffects(root) {
4028
+ const timing = firstChild(root, "p:timing");
4029
+ if (!timing) return [];
4030
+ const out = [];
4031
+ for (const cTn of timing.getElementsByTagNameNS(P_NS, "cTn")) {
4032
+ const presetClass = attr(cTn, "presetClass");
4033
+ if (presetClass === null) continue;
4034
+ let sequence = "other";
4035
+ for (let node = parentTimeNode(cTn); node; node = parentTimeNode(node)) {
4036
+ const type = attr(node, "nodeType");
4037
+ if (type === "mainSeq") sequence = "main";
4038
+ else if (type === "interactiveSeq") sequence = "interactive";
4039
+ else continue;
4040
+ break;
4041
+ }
4042
+ const stated = [...cTn.getElementsByTagNameNS(P_NS, "cTn")].map((node) => millis(attr(node, "dur"))).filter((dur) => dur !== null);
4043
+ const step = parentTimeNode(cTn);
4044
+ const stCondLst = step && firstChild(step, "p:stCondLst");
4045
+ const cond = stCondLst && firstChild(stCondLst, "p:cond");
4046
+ const spTgt = cTn.getElementsByTagNameNS(P_NS, "spTgt")[0] ?? null;
4047
+ out.push({
4048
+ sequence,
4049
+ presetClass,
4050
+ presetId: numberValue(attr(cTn, "presetID")),
4051
+ presetSubtype: numberValue(attr(cTn, "presetSubtype")),
4052
+ trigger: TRIGGERS[attr(cTn, "nodeType") ?? ""] ?? null,
4053
+ durationMs: stated.length > 0 ? Math.max(...stated) : null,
4054
+ delayMs: cond ? millis(attr(cond, "delay")) : null,
4055
+ shapeId: spTgt ? numberValue(attr(spTgt, "spid")) : null,
4056
+ textRange: spTgt !== null && firstChild(spTgt, "p:txEl") !== null
4057
+ });
4058
+ }
4059
+ return out;
4060
+ }
3698
4061
  /** The sorted, de-duplicated set of `spid`s referenced by the slide's animations. */
3699
4062
  function enumerateSpids(root) {
3700
4063
  const seen = /* @__PURE__ */ new Set();
@@ -3801,7 +4164,7 @@ function pruneSpids(root, spids) {
3801
4164
  * This is the whole-slide counterpart to {@link pruneSpids} (which drops the
3802
4165
  * builds of specific shapes). It only removes staging; it never deletes shapes,
3803
4166
  * so a slide that animated alternating states over the same region will show
3804
- * them all at once after flattening — that is "drop animations", distinct from
4167
+ * them all at once after flattening. That is "drop animations", distinct from
3805
4168
  * "remove staged/duplicate shapes".
3806
4169
  */
3807
4170
  function flattenAnimations(root) {
@@ -3906,7 +4269,7 @@ function getOrCreateBldLst(timing, doc) {
3906
4269
  * id on the destination). For each mapped shape: its mainSeq click-group `<p:par>`
3907
4270
  * (the whole click step) and `<p:bldP>` are cloned, their `spid`s remapped, their
3908
4271
  * `<p:cTn>` ids renumbered to stay collision-free in the destination, and appended
3909
- * after any existing build — matching how PowerPoint merges a pasted shape's
4272
+ * after any existing build, matching how PowerPoint merges a pasted shape's
3910
4273
  * animation. Returns `true` when anything was carried (so the caller can
3911
4274
  * `markDirty()`), `false` when the source shapes have no animation.
3912
4275
  */
@@ -3967,22 +4330,21 @@ function targetsOnly(root, spidMap) {
3967
4330
  const targets = [...root.getElementsByTagNameNS(P_NS, "spTgt")].map((spTgt) => numberValue(attr(spTgt, "spid")));
3968
4331
  return targets.length > 0 && targets.every((spid) => spid !== null && spidMap.has(spid));
3969
4332
  }
3970
- //#endregion
3971
- //#region src/read/api/shapes/base.ts
4333
+ // src/read/api/shapes/base.ts
3972
4334
  /**
3973
4335
  * The abstract base every shape kind extends.
3974
4336
  *
3975
4337
  * It owns everything a shape has regardless of kind: non-visual identity (id / name / hidden /
3976
4338
  * description / decorative), geometry through the subclass-supplied `a:xfrm`, and the `p:spPr`
3977
- * surface — fill, line, gradient, effects — resolved against the host's theme. Kind-specific
4339
+ * surface (fill, line, gradient, effects) resolved against the host's theme. Kind-specific
3978
4340
  * reads live on the subclasses in the sibling modules.
3979
4341
  *
3980
- * A proxy holds a back-reference to its owning {@link ShapeHost} — a `Slide`, a `SlideLayout` or a
3981
- * `SlideMaster` — so it can resolve relationships and theme colours, and so a mutation can mark
4342
+ * A proxy holds a back-reference to its owning {@link ShapeHost} (a `Slide`, a `SlideLayout` or a
4343
+ * `SlideMaster`), so it can resolve relationships and theme colours, and so a mutation can mark
3982
4344
  * that host's part dirty.
3983
4345
  */
3984
4346
  const ADEC_NS = "http://schemas.microsoft.com/office/drawing/2017/decorative";
3985
- /** Common base for every shape in a shape tree — a slide's, a layout's, or a master's. */
4347
+ /** Common base for every shape in a shape tree: a slide's, a layout's, or a master's. */
3986
4348
  var Shape = class {
3987
4349
  element;
3988
4350
  host;
@@ -4041,7 +4403,7 @@ var Shape = class {
4041
4403
  /**
4042
4404
  * Whether the shape is explicitly hidden (`p:cNvPr/@hidden="1"`); `false` when
4043
4405
  * the attribute is unset. A hidden shape stays in the slide XML but is not
4044
- * rendered — decks use it as a fallback layer (e.g. a duotone-recolour source
4406
+ * rendered. Decks use it as a fallback layer (e.g. a duotone-recolour source
4045
4407
  * sitting behind the visible icon), so a faithful reader must distinguish it
4046
4408
  * from the drawn shapes.
4047
4409
  */
@@ -4050,6 +4412,29 @@ var Shape = class {
4050
4412
  return boolValue(cNvPr && attr(cNvPr, "hidden")) === true;
4051
4413
  }
4052
4414
  /**
4415
+ * The shape's lock flags: the attributes of the locking element under its non-visual drawing
4416
+ * properties (`a:spLocks`, `a:picLocks`, `a:cxnSpLocks`, `a:graphicFrameLocks` or
4417
+ * `a:grpSpLocks`), each as the boolean it states, keyed by attribute name (`noMove`, `noGrp`, …).
4418
+ * `null` when the shape has no locking element. The read counterpart of `objectLock`.
4419
+ */
4420
+ get locks() {
4421
+ const cNvPr = nonVisualCNvPr(this.element);
4422
+ const nvPr = cNvPr?.parentNode;
4423
+ if (!nvPr) return null;
4424
+ for (const props of childElements(nvPr)) {
4425
+ if (props === cNvPr) continue;
4426
+ const lock = childElements(props).find((child) => child.localName?.endsWith("Locks"));
4427
+ if (!lock) continue;
4428
+ const out = {};
4429
+ for (const attribute of Array.from(lock.attributes)) {
4430
+ const value = boolValue(attribute.value);
4431
+ if (value !== null) out[attribute.localName ?? attribute.name] = value;
4432
+ }
4433
+ return out;
4434
+ }
4435
+ return null;
4436
+ }
4437
+ /**
4053
4438
  * The shape's alt-text description (`p:cNvPr/@descr`), or `null` when unset.
4054
4439
  * This is the accessibility primitive a screen reader announces; an audit or
4055
4440
  * accessible-export tool reads it here. Distinct from {@link name}, which is
@@ -4083,7 +4468,7 @@ var Shape = class {
4083
4468
  /**
4084
4469
  * Whether the shape is flagged **decorative** (`p:cNvPr/a:extLst/a:ext`
4085
4470
  * uri `{C183D7F6-…}` / `adec:decorative@val`), PowerPoint's "Mark as
4086
- * decorative" — a purely visual element assistive tech should skip. `false`
4471
+ * decorative": a purely visual element assistive tech should skip. `false`
4087
4472
  * when the extension is absent. A decorative shape typically has no
4088
4473
  * {@link description}; the two are alternatives, not companions.
4089
4474
  */
@@ -4154,8 +4539,8 @@ var Shape = class {
4154
4539
  }
4155
4540
  /**
4156
4541
  * Clockwise rotation in degrees (`a:xfrm/@rot` ÷ 60000), or `null` when the
4157
- * shape has no own transform. A present xfrm with no `@rot` reads as `0`, so
4158
- * — mirroring {@link left}/{@link top}/{@link width}/{@link height} — `null`
4542
+ * shape has no own transform. A present xfrm with no `@rot` reads as `0`, so,
4543
+ * mirroring {@link left}/{@link top}/{@link width}/{@link height}, `null`
4159
4544
  * ("inherits layout geometry") stays distinct from `0` ("has a transform, not
4160
4545
  * rotated"). The value is faithful to the XML and not normalised to a signed
4161
4546
  * range, so a `@rot` past 360° (e.g. a negative angle stored as `19216344`)
@@ -4202,7 +4587,7 @@ var Shape = class {
4202
4587
  * equal the shape's own transform values.
4203
4588
  *
4204
4589
  * `null` when the shape (or any enclosing group) has no own transform, or a
4205
- * group's `a:chExt` is degenerate (zero) — there is then no resolvable frame.
4590
+ * group's `a:chExt` is degenerate (zero). There is then no resolvable frame.
4206
4591
  * {@link absoluteFrameFailure} says which of those it was.
4207
4592
  *
4208
4593
  * A rotated shape's returned `left`/`top` remain PowerPoint's unrotated
@@ -4217,7 +4602,7 @@ var Shape = class {
4217
4602
  * Why {@link absoluteFrame} is `null`, or `null` when it resolved.
4218
4603
  *
4219
4604
  * `absoluteFrame` reports one `null` for three different situations, and only a
4220
- * caller that wants to *report* the unresolvable shape needs them apart — an
4605
+ * caller that wants to *report* the unresolvable shape needs them apart: an
4221
4606
  * inherited placeholder box is ordinary, a group with no usable `a:xfrm` is not.
4222
4607
  * The distinction is not recoverable from the `null`, and re-deriving it means
4223
4608
  * walking the same ancestry a second time, so it is named here instead. See
@@ -4320,13 +4705,13 @@ var Shape = class {
4320
4705
  this.#applyFill(value === null ? null : { scheme: value });
4321
4706
  }
4322
4707
  /**
4323
- * `true` when the shape sets an explicit no-fill (`spPr/a:noFill`) — a
4708
+ * `true` when the shape sets an explicit no-fill (`spPr/a:noFill`): a
4324
4709
  * deliberately transparent surface. The fill-side counterpart of
4325
4710
  * {@link lineNoFill}, and the only accessor that separates it from a shape
4326
4711
  * carrying no fill child at all (one inheriting through `p:style/a:fillRef`):
4327
- * every other fill accessor — {@link fillColor}, {@link fillSchemeColor},
4712
+ * every other fill accessor ({@link fillColor}, {@link fillSchemeColor},
4328
4713
  * {@link resolvedFill}, {@link gradientFill}, {@link patternFill},
4329
- * {@link pictureFill} — reports `null` for both. The two paint completely
4714
+ * {@link pictureFill}) reports `null` for both. The two paint completely
4330
4715
  * differently, so a consumer that cannot tell them apart paints a transparent
4331
4716
  * shape in the theme's accent colour.
4332
4717
  */
@@ -4335,7 +4720,7 @@ var Shape = class {
4335
4720
  return !!(props && firstChild(props, "a:noFill"));
4336
4721
  }
4337
4722
  /**
4338
- * Set an explicit `<a:noFill/>` on the shape — a transparent surface. This is
4723
+ * Set an explicit `<a:noFill/>` on the shape: a transparent surface. This is
4339
4724
  * distinct from clearing the fill (`fillColor = null`), which removes the
4340
4725
  * `a:solidFill` and lets the fill inherit from the shape's style/placeholder.
4341
4726
  * Read it back with {@link fillNoFill}.
@@ -4365,14 +4750,14 @@ var Shape = class {
4365
4750
  /**
4366
4751
  * The line/border dash style (`spPr/a:ln/a:prstDash/@val`), e.g. `'dash'`,
4367
4752
  * `'lgDashDot'`, `'sysDot'`, or `null` when the line is solid/unset. A faithful
4368
- * replica of dashed dividers and dashed card borders needs this — it is
4753
+ * replica of dashed dividers and dashed card borders needs this: it is
4369
4754
  * otherwise invisible in {@link lineColor}/{@link lineWidthPt} alone.
4370
4755
  */
4371
4756
  get lineDash() {
4372
4757
  return this.#lineBasics()?.dash ?? null;
4373
4758
  }
4374
4759
  /**
4375
- * `true` when the shape sets an explicit no-line (`spPr/a:ln/a:noFill`) — a
4760
+ * `true` when the shape sets an explicit no-line (`spPr/a:ln/a:noFill`): a
4376
4761
  * deliberately border-less shape. Distinct from simply having no `a:ln`
4377
4762
  * (an inherited line), which {@link resolvedLine} cannot tell apart: both
4378
4763
  * report `null`. A replica that relies on a shadow instead of a border needs
@@ -4382,8 +4767,20 @@ var Shape = class {
4382
4767
  return this.#lineBasics()?.noFill ?? false;
4383
4768
  }
4384
4769
  /**
4385
- * The line end cap (`spPr/a:ln/@cap`) as the raw OOXML token — `'flat'`,
4386
- * `'rnd'` (round) or `'sq'` (square) — or `null` when unset (PowerPoint's
4770
+ * Whether the shape states an outline of its **own** (`spPr/a:ln`), however little it holds.
4771
+ *
4772
+ * The line accessors all report `null` both for a shape with no `a:ln` and for one whose `a:ln`
4773
+ * is empty, and the two are different statements to a writer: an empty `<a:ln/>` says "take the
4774
+ * whole outline from `p:style` or the placeholder", which is what PowerPoint writes on its own
4775
+ * placeholders, and `addShape` writes it for `line: { type: 'inherit' }`. A copy that has to
4776
+ * reproduce the outline rather than describe it needs to know which one it has.
4777
+ */
4778
+ get hasOwnLine() {
4779
+ return this.#line() !== null;
4780
+ }
4781
+ /**
4782
+ * The line end cap (`spPr/a:ln/@cap`) as the raw OOXML token, `'flat'`,
4783
+ * `'rnd'` (round) or `'sq'` (square), or `null` when unset (PowerPoint's
4387
4784
  * default is `flat`). The write API authors this attribute through
4388
4785
  * `ShapeLineProps.cap`, so without the accessor a deck this library produced
4389
4786
  * could not be read back without losing it.
@@ -4398,8 +4795,8 @@ var Shape = class {
4398
4795
  return ln ? attr(ln, "cap") ?? null : null;
4399
4796
  }
4400
4797
  /**
4401
- * The line alignment (`spPr/a:ln/@algn`) as the raw OOXML token — `'ctr'`
4402
- * (centred on the shape's outline) or `'in'` (inset, drawn wholly inside it) —
4798
+ * The line alignment (`spPr/a:ln/@algn`) as the raw OOXML token, `'ctr'`
4799
+ * (centred on the shape's outline) or `'in'` (inset, drawn wholly inside it),
4403
4800
  * or `null` when unset. It shifts a thick outline by half its width, so it
4404
4801
  * changes where the border sits relative to the fill.
4405
4802
  */
@@ -4424,7 +4821,7 @@ var Shape = class {
4424
4821
  * shape uses preset geometry / none. The faithful, multi-path counterpart of
4425
4822
  * {@link presetGeometry}: each `a:path` keeps its own path-unit viewport
4426
4823
  * (`w`/`h`) and ordered {@link GeometryCommand}s. Coordinates are raw path-unit
4427
- * integers, not EMU — pair the path `w`/`h` with the shape's box size to map
4824
+ * integers, not EMU. Pair the path `w`/`h` with the shape's box size to map
4428
4825
  * them into slide space.
4429
4826
  *
4430
4827
  * Not an auto-shape-only property either: a picture clipped to a freeform (what
@@ -4436,7 +4833,12 @@ var Shape = class {
4436
4833
  const custGeom = props && firstChild(props, "a:custGeom");
4437
4834
  if (!custGeom) return null;
4438
4835
  const pathLst = firstChild(custGeom, "a:pathLst");
4439
- return { paths: pathLst ? getElements(pathLst, "a:path").map((p) => readGeometryPath(p)) : [] };
4836
+ return {
4837
+ paths: pathLst ? getElements(pathLst, "a:path").map((p) => readGeometryPath(p)) : [],
4838
+ guides: readGeometryGuides(firstChild(custGeom, "a:gdLst")),
4839
+ adjustHandles: readAdjustHandles(firstChild(custGeom, "a:ahLst")),
4840
+ connectionSites: readConnectionSites(firstChild(custGeom, "a:cxnLst"))
4841
+ };
4440
4842
  }
4441
4843
  /**
4442
4844
  * Preset-geometry adjustment values (`spPr/a:prstGeom/a:avLst/a:gd`) as a
@@ -4485,7 +4887,7 @@ var Shape = class {
4485
4887
  * The shape's gradient fill with its geometry (`spPr/a:gradFill`), or `null`
4486
4888
  * when the fill is not a gradient. Unlike {@link gradientStops} (stops only),
4487
4889
  * this also carries the linear {@link GradientFill.angleDeg} or the
4488
- * {@link GradientFill.path} shape — the geometry a faithful replica needs and
4890
+ * {@link GradientFill.path} shape: the geometry a faithful replica needs and
4489
4891
  * which the bare stop list omits.
4490
4892
  */
4491
4893
  get gradientFill() {
@@ -4496,8 +4898,8 @@ var Shape = class {
4496
4898
  * The shape's line/border **gradient** stroke (`spPr/a:ln/a:gradFill`), or
4497
4899
  * `null` when the line is a solid, absent, or inherited border (see
4498
4900
  * {@link resolvedLine}). The line counterpart of {@link gradientFill}: a
4499
- * gradient-stroked connector — common for faded process arrows in styled decks
4500
- * — otherwise surfaces only its {@link lineWidthPt}, dropping the colour
4901
+ * gradient-stroked connector (common for faded process arrows in styled decks)
4902
+ * otherwise surfaces only its {@link lineWidthPt}, dropping the colour
4501
4903
  * entirely, so a replica cannot reproduce the stroke.
4502
4904
  */
4503
4905
  get lineGradient() {
@@ -4505,6 +4907,15 @@ var Shape = class {
4505
4907
  return ln ? this.#gradientFillIn(ln) : null;
4506
4908
  }
4507
4909
  /**
4910
+ * The shape's line/border **pattern** stroke (`spPr/a:ln/a:pattFill`), or `null` when the line
4911
+ * is not a pattern. The line counterpart of {@link patternFill}, and what the write side's
4912
+ * `line.pattern` writes.
4913
+ */
4914
+ get linePatternFill() {
4915
+ const ln = this.#line();
4916
+ return ln ? readPatternFill(ln, this.host.themeContext()) : null;
4917
+ }
4918
+ /**
4508
4919
  * The shape's line/connector arrowheads (`a:ln/a:headEnd` + `a:tailEnd`), or
4509
4920
  * `null` when neither end carries one. Essential for replicating connectors,
4510
4921
  * whose dot/arrow ends are otherwise invisible in the geometry.
@@ -4557,7 +4968,7 @@ var Shape = class {
4557
4968
  /**
4558
4969
  * The shape's reflection (`spPr/a:effectLst/a:reflection`), or `null` when it has
4559
4970
  * none. Read-only: this library authors no reflection, so a replica should carry
4560
- * the part rather than regenerate it — see {@link Reflection}.
4971
+ * the part rather than regenerate it (see {@link Reflection}).
4561
4972
  */
4562
4973
  get reflection() {
4563
4974
  return readReflection(this.#effectLst());
@@ -4572,7 +4983,7 @@ var Shape = class {
4572
4983
  /**
4573
4984
  * The shape's pattern (hatch) fill (`spPr/a:pattFill`), or `null` when the fill
4574
4985
  * is not a pattern. Surfaces the {@link PatternFill.preset} name and both
4575
- * colours resolved against the host's theme — the pattern counterpart of
4986
+ * colours resolved against the host's theme: the pattern counterpart of
4576
4987
  * {@link resolvedFill}, which reports `null` for a non-solid fill and so drops a
4577
4988
  * hatched surface entirely.
4578
4989
  */
@@ -4583,8 +4994,8 @@ var Shape = class {
4583
4994
  /**
4584
4995
  * The shape's picture (image) fill (`spPr/a:blipFill`), or `null` when the fill
4585
4996
  * is not a picture. The image-fill counterpart of {@link patternFill}: a shape
4586
- * whose *surface* is an image is not a {@link Picture} — it is an autoShape with
4587
- * a blip fill — and {@link resolvedFill} reports `null` for one, so without this
4997
+ * whose *surface* is an image is not a {@link Picture} (it is an autoShape with
4998
+ * a blip fill), and {@link resolvedFill} reports `null` for one, so without this
4588
4999
  * an image-filled shape reads as unfilled. Carries the embedded image
4589
5000
  * ({@link PictureFill.relId}/{@link PictureFill.partName}) plus the stretch/tile
4590
5001
  * geometry.
@@ -4593,18 +5004,18 @@ var Shape = class {
4593
5004
  const props = this.properties();
4594
5005
  return props ? readPictureFill(props, this.host.relationships) : null;
4595
5006
  }
4596
- /** The shape's effect list (`spPr/a:effectLst`), or `null` — what `shapes/effects.ts` decodes. */
5007
+ /** The shape's effect list (`spPr/a:effectLst`), or `null`: what `shapes/effects.ts` decodes. */
4597
5008
  #effectLst() {
4598
5009
  const props = this.properties();
4599
5010
  return props ? firstChild(props, "a:effectLst") : null;
4600
5011
  }
4601
5012
  /**
4602
5013
  * The shape's solid fill resolved against the host's theme
4603
- * ({@link Slide.themeContext}) to a literal hex — the resolved counterpart of
5014
+ * ({@link Slide.themeContext}) to a literal hex: the resolved counterpart of
4604
5015
  * {@link fillColor}/{@link fillSchemeColor}, which report the raw reference.
4605
5016
  * `null` when the shape has no `a:solidFill` (a gradient/none/inherited fill)
4606
5017
  * or the colour cannot be made literal. The returned {@link ResolvedColor}
4607
- * carries the base `hex` and raw transforms, and `effectiveHex` — the base with
5018
+ * carries the base `hex` and raw transforms, and `effectiveHex`, the base with
4608
5019
  * its colour transforms (`lumMod`/`shade`/…) applied (read that for the final
4609
5020
  * rendered colour).
4610
5021
  *
@@ -4620,7 +5031,7 @@ var Shape = class {
4620
5031
  }
4621
5032
  /**
4622
5033
  * The shape's line/border solid fill resolved against the host's theme to a
4623
- * literal hex — the resolved counterpart of {@link lineColor}/{@link lineSchemeColor}.
5034
+ * literal hex: the resolved counterpart of {@link lineColor}/{@link lineSchemeColor}.
4624
5035
  * `null` when the shape has no `a:ln/a:solidFill` or it cannot be made literal.
4625
5036
  * Like {@link resolvedFill}, the result carries `effectiveHex` (the base colour
4626
5037
  * with its transforms applied) for the final rendered colour.
@@ -4708,11 +5119,20 @@ var Shape = class {
4708
5119
  }
4709
5120
  /**
4710
5121
  * This shape's placeholder identity (`p:ph` `type`/`idx`), or `null` when it is
4711
- * not a placeholder. Only `p:sp` shapes can be placeholders, so the base
4712
- * implementation always returns `null`; {@link AutoShape} overrides it.
5122
+ * not a placeholder. `idx` defaults to `'0'` when the attribute is absent, as
5123
+ * PowerPoint does. A text placeholder is a `p:sp`; a filled picture, table or chart
5124
+ * placeholder is a `p:pic` or `p:graphicFrame` carrying the same `p:ph`, which is what
5125
+ * inserting a picture into a picture placeholder produces. This used to be `null` for
5126
+ * every shape but a `p:sp` (#111). Use {@link Slide.placeholder} to find a text
5127
+ * placeholder by type.
4713
5128
  */
4714
5129
  get placeholder() {
4715
- return null;
5130
+ const nvPr = nvPrOf(this.element);
5131
+ const ph = nvPr && firstChild(nvPr, "p:ph");
5132
+ return ph ? {
5133
+ type: attr(ph, "type"),
5134
+ idx: attr(ph, "idx") ?? "0"
5135
+ } : null;
4716
5136
  }
4717
5137
  /**
4718
5138
  * Remove this shape from its parent (the host's shape tree, or an enclosing
@@ -4739,10 +5159,74 @@ var Shape = class {
4739
5159
  return this.element;
4740
5160
  }
4741
5161
  };
4742
- //#endregion
4743
- //#region src/read/api/shapes/autoshape.ts
5162
+ /** An `ST_AdjCoordinate` attribute: a number in EMU, a guide name, or `null` when absent. */
5163
+ function adjCoordinate(element, name) {
5164
+ const value = attr(element, name);
5165
+ if (value === null || value === "") return null;
5166
+ const number = numberValue(value);
5167
+ return number === null ? value : number;
5168
+ }
5169
+ /** An `ST_AdjAngle` attribute in degrees, or `null` when absent or a guide name. */
5170
+ function adjAngleDeg(element, name) {
5171
+ const number = numberValue(attr(element, name));
5172
+ return number === null ? null : number / 6e4;
5173
+ }
5174
+ /** A custom geometry's `a:gdLst` as {@link CustomGeometryGuide}s. */
5175
+ function readGeometryGuides(gdLst) {
5176
+ if (!gdLst) return [];
5177
+ return getElements(gdLst, "a:gd").map((gd) => ({
5178
+ name: attr(gd, "name") ?? "",
5179
+ formula: attr(gd, "fmla") ?? ""
5180
+ }));
5181
+ }
5182
+ /** A custom geometry's `a:ahLst` as {@link CustomGeometryAdjustHandle}s, XY and polar in document order. */
5183
+ function readAdjustHandles(ahLst) {
5184
+ if (!ahLst) return [];
5185
+ const handles = [];
5186
+ for (const handle of childElements(ahLst)) {
5187
+ const pos = firstChild(handle, "a:pos");
5188
+ const x = pos ? adjCoordinate(pos, "x") : null;
5189
+ const y = pos ? adjCoordinate(pos, "y") : null;
5190
+ if (handle.localName === "ahXY") handles.push({
5191
+ kind: "xy",
5192
+ x,
5193
+ y,
5194
+ gdRefX: attr(handle, "gdRefX"),
5195
+ minX: adjCoordinate(handle, "minX"),
5196
+ maxX: adjCoordinate(handle, "maxX"),
5197
+ gdRefY: attr(handle, "gdRefY"),
5198
+ minY: adjCoordinate(handle, "minY"),
5199
+ maxY: adjCoordinate(handle, "maxY")
5200
+ });
5201
+ else if (handle.localName === "ahPolar") handles.push({
5202
+ kind: "polar",
5203
+ x,
5204
+ y,
5205
+ gdRefR: attr(handle, "gdRefR"),
5206
+ minR: adjCoordinate(handle, "minR"),
5207
+ maxR: adjCoordinate(handle, "maxR"),
5208
+ gdRefAng: attr(handle, "gdRefAng"),
5209
+ minAngDeg: adjAngleDeg(handle, "minAng"),
5210
+ maxAngDeg: adjAngleDeg(handle, "maxAng")
5211
+ });
5212
+ }
5213
+ return handles;
5214
+ }
5215
+ /** A custom geometry's `a:cxnLst` as {@link CustomGeometryConnectionSite}s. */
5216
+ function readConnectionSites(cxnLst) {
5217
+ if (!cxnLst) return [];
5218
+ return getElements(cxnLst, "a:cxn").map((cxn) => {
5219
+ const pos = firstChild(cxn, "a:pos");
5220
+ return {
5221
+ angleDeg: adjAngleDeg(cxn, "ang"),
5222
+ x: pos ? adjCoordinate(pos, "x") : null,
5223
+ y: pos ? adjCoordinate(pos, "y") : null
5224
+ };
5225
+ });
5226
+ }
5227
+ // src/read/api/shapes/autoshape.ts
4744
5228
  /**
4745
- * An auto shape, text box, or placeholder (`p:sp`) — the only shape kind that holds text.
5229
+ * An auto shape, text box, or placeholder (`p:sp`): the only shape kind that holds text.
4746
5230
  */
4747
5231
  /** An auto shape, text box, or placeholder (`p:sp`). The only kind that holds text. */
4748
5232
  var AutoShape = class extends Shape {
@@ -4765,22 +5249,8 @@ var AutoShape = class extends Shape {
4765
5249
  inherit
4766
5250
  });
4767
5251
  }
4768
- /**
4769
- * This shape's placeholder identity (`p:ph` `type`/`idx`), or `null` when it is
4770
- * not a placeholder. `idx` defaults to `'0'` when the attribute is absent, as
4771
- * PowerPoint does. Use {@link Slide.placeholder} to find a placeholder by type.
4772
- */
4773
- get placeholder() {
4774
- const nvPr = nvPrOf(this.element);
4775
- const ph = nvPr && firstChild(nvPr, "p:ph");
4776
- return ph ? {
4777
- type: attr(ph, "type"),
4778
- idx: attr(ph, "idx") ?? "0"
4779
- } : null;
4780
- }
4781
5252
  };
4782
- //#endregion
4783
- //#region src/read/api/shapes/picture.ts
5253
+ // src/read/api/shapes/picture.ts
4784
5254
  /**
4785
5255
  * A picture (`p:pic`).
4786
5256
  *
@@ -4809,6 +5279,34 @@ function recolorColorOf(color, ctx) {
4809
5279
  if (!color || color.namespaceURI !== OOXML_NS.a) return null;
4810
5280
  return readColorRef(color, ctx);
4811
5281
  }
5282
+ /** One `a:blip` child as a {@link Recolor}, or `null` when it is not one of the five recognised effects. */
5283
+ function recolorOf(child, ctx) {
5284
+ switch (child.localName) {
5285
+ case "duotone": return {
5286
+ kind: "duotone",
5287
+ stops: childElements(child).map((color) => recolorColorOf(color, ctx)).filter((c) => c !== null)
5288
+ };
5289
+ case "clrChange": {
5290
+ const from = firstChild(child, "a:clrFrom");
5291
+ const to = firstChild(child, "a:clrTo");
5292
+ return {
5293
+ kind: "clrChange",
5294
+ from: from ? recolorColorOf(firstChildElement(from), ctx) : null,
5295
+ to: to ? recolorColorOf(firstChildElement(to), ctx) : null
5296
+ };
5297
+ }
5298
+ case "grayscl": return { kind: "grayscale" };
5299
+ case "biLevel": return {
5300
+ kind: "biLevel",
5301
+ threshold: pctAttr(child, "thresh")
5302
+ };
5303
+ case "alphaModFix": return {
5304
+ kind: "alphaModFix",
5305
+ amount: pctAttr(child, "amt") ?? 1
5306
+ };
5307
+ }
5308
+ return null;
5309
+ }
4812
5310
  /** A picture (`p:pic`). */
4813
5311
  var Picture = class extends Shape {
4814
5312
  shapeType = "picture";
@@ -4848,7 +5346,7 @@ var Picture = class extends Shape {
4848
5346
  * Microsoft SVG blip extension (`a:blip/a:extLst/a:ext/asvg:svgBlip/@r:embed`),
4849
5347
  * or `null` when the picture has no SVG. PowerPoint usually pairs this with a
4850
5348
  * raster fallback in `a:blip/@r:embed` ({@link imageRelId}), but some exporters
4851
- * emit an SVG-only blip where `imageRelId` is absent and only this resolves —
5349
+ * emit an SVG-only blip where `imageRelId` is absent and only this resolves,
4852
5350
  * so a reader that wants the real drawn art must consult both.
4853
5351
  */
4854
5352
  get svgRelId() {
@@ -4861,14 +5359,14 @@ var Picture = class extends Shape {
4861
5359
  }
4862
5360
  /**
4863
5361
  * Which drawable media this picture carries:
4864
- * - `'raster'` — only a raster blip (`a:blip/@r:embed`);
4865
- * - `'svg'` — only a vector blip (`asvg:svgBlip/@r:embed`, no raster). This
5362
+ * - `'raster'`: only a raster blip (`a:blip/@r:embed`);
5363
+ * - `'svg'`: only a vector blip (`asvg:svgBlip/@r:embed`, no raster). This
4866
5364
  * is what PowerPoint's *Insert → Icons* and a plain SVG insert produce;
4867
- * - `'both'` — a raster fallback *and* an SVG (PowerPoint's usual pairing);
4868
- * - `'none'` — a `p:pic` with no embedded blip at all (e.g. a linked image).
5365
+ * - `'both'`: a raster fallback *and* an SVG (PowerPoint's usual pairing);
5366
+ * - `'none'`: a `p:pic` with no embedded blip at all (e.g. a linked image).
4869
5367
  *
4870
- * Lets a caller distinguish an SVG-only picture — where {@link imagePartName}
4871
- * is legitimately `null` — from a genuinely empty one, without two null checks.
5368
+ * Lets a caller distinguish an SVG-only picture (where {@link imagePartName}
5369
+ * is legitimately `null`) from a genuinely empty one, without two null checks.
4872
5370
  */
4873
5371
  get mediaKind() {
4874
5372
  const hasRaster = this.imageRelId != null;
@@ -4880,7 +5378,7 @@ var Picture = class extends Shape {
4880
5378
  }
4881
5379
  /**
4882
5380
  * Absolute partname of whichever part actually carries this picture's drawn
4883
- * data — the raster part when present, otherwise the SVG part — or `null`
5381
+ * data (the raster part when present, otherwise the SVG part), or `null`
4884
5382
  * when the picture embeds neither. Use this when you just want "the bytes
4885
5383
  * this picture shows"; prefer {@link imagePartName} / {@link svgPartName}
4886
5384
  * (and {@link mediaKind}) when you need to know *which* kind it is. An
@@ -4891,8 +5389,52 @@ var Picture = class extends Shape {
4891
5389
  return this.imagePartName ?? this.svgPartName;
4892
5390
  }
4893
5391
  /**
5392
+ * The audio or video this picture plays (`p:nvPr/a:audioFile` or `a:videoFile`), or `null` for a
5393
+ * still picture. Its blip ({@link imagePartName}) is then the poster frame. See {@link PictureMedia}.
5394
+ */
5395
+ get media() {
5396
+ const nvPr = firstChild(this.element, "p:nvPicPr");
5397
+ const props = nvPr && firstChild(nvPr, "p:nvPr");
5398
+ if (!props) return null;
5399
+ const file = firstChild(props, "a:videoFile") ?? firstChild(props, "a:audioFile");
5400
+ if (!file) return null;
5401
+ const rels = this.host.relationships;
5402
+ const embedded = descendantsByTag(props, OOXML_NS.p14, "media").map((media) => resolvePartName(attr(media, "r:embed"), rels)).find((name) => name !== null);
5403
+ const linkId = attr(file, "r:link");
5404
+ const partName = embedded ?? resolvePartName(linkId, rels);
5405
+ const linkRel = linkId ? rels.get(linkId) : void 0;
5406
+ return {
5407
+ kind: file.localName === "audioFile" ? "audio" : "video",
5408
+ partName,
5409
+ contentType: partName === null ? null : this.host.opc.part(partName)?.contentType ?? null,
5410
+ link: partName === null && linkRel?.targetMode === "External" ? linkRel.target : null,
5411
+ repeatCount: this.#repeatCount()
5412
+ };
5413
+ }
5414
+ /**
5415
+ * The `repeatCount` of the media node the host's `p:timing` aims at this picture, as
5416
+ * {@link PictureMedia.repeatCount} reports it.
5417
+ */
5418
+ #repeatCount() {
5419
+ const id = this.id;
5420
+ const root = this.element.ownerDocument?.documentElement;
5421
+ if (id === null || !root) return null;
5422
+ for (const node of descendantsByTag(root, OOXML_NS.p, "cMediaNode")) {
5423
+ const target = firstChild(node, "p:tgtEl");
5424
+ const spTgt = target && firstChild(target, "p:spTgt");
5425
+ if (!spTgt || attr(spTgt, "spid") !== String(id)) continue;
5426
+ const cTn = firstChild(node, "p:cTn");
5427
+ const value = cTn && attr(cTn, "repeatCount");
5428
+ if (value === null) return null;
5429
+ if (value === "indefinite") return "indefinite";
5430
+ const count = Number(value);
5431
+ return Number.isFinite(count) && count > 0 ? count / 1e3 : null;
5432
+ }
5433
+ return null;
5434
+ }
5435
+ /**
4894
5436
  * The picture's crop as fractions of the *source image*, read from
4895
- * `p:blipFill/a:srcRect` — `{ left, top, right, bottom }`, each the amount
5437
+ * `p:blipFill/a:srcRect` as `{ left, top, right, bottom }`, each the amount
4896
5438
  * trimmed off that edge (so `0.1` = 10 % cropped away, an uncropped edge is
4897
5439
  * `0`). `null` when there is no `a:srcRect` at all; an explicit
4898
5440
  * `{0,0,0,0}` crop still reports zeros, since its presence is meaningful.
@@ -4910,44 +5452,32 @@ var Picture = class extends Shape {
4910
5452
  * `null` when the blip carries none. Recognises the effects a faithful reader
4911
5453
  * needs to reproduce a recoloured image: `a:duotone` (the two-stop icon-tint
4912
5454
  * trick), `a:clrChange`, `a:grayscl`, `a:biLevel`, and `a:alphaModFix`; the
4913
- * first such effect in document order wins. Colours mirror the
5455
+ * first such effect in document order wins, and {@link recolors} lists them all. Colours mirror the
4914
5456
  * {@link GradientStop} split (`color`/`schemeColor`/`presetColor`) so theme
4915
5457
  * tokens resolve through {@link Slide.themeContext}. `threshold`/`amount` are
4916
5458
  * 0–1 fractions. {@link hidden} (the duotone fallback-layer trick) reports the
4917
5459
  * *visibility* of a recolour source; this reports the *tint* itself.
4918
5460
  */
4919
5461
  get recolor() {
5462
+ return this.recolors[0] ?? null;
5463
+ }
5464
+ /**
5465
+ * Every blip effect {@link recolor} recognises, in document order: the first is
5466
+ * {@link recolor}. They combine (a picture can be faded with `a:alphaModFix` and
5467
+ * recoloured too, which is how the write API emits `transparency` beside a
5468
+ * recolour), and {@link recolor} alone reported only the first of them.
5469
+ */
5470
+ get recolors() {
4920
5471
  const blip = this.#blip();
4921
- if (!blip) return null;
5472
+ if (!blip) return [];
4922
5473
  const ctx = this.host.themeContext();
5474
+ const out = [];
4923
5475
  for (const child of childElements(blip)) {
4924
5476
  if (child.namespaceURI !== OOXML_NS.a) continue;
4925
- switch (child.localName) {
4926
- case "duotone": return {
4927
- kind: "duotone",
4928
- stops: childElements(child).map((color) => recolorColorOf(color, ctx)).filter((c) => c !== null)
4929
- };
4930
- case "clrChange": {
4931
- const from = firstChild(child, "a:clrFrom");
4932
- const to = firstChild(child, "a:clrTo");
4933
- return {
4934
- kind: "clrChange",
4935
- from: from ? recolorColorOf(firstChildElement(from), ctx) : null,
4936
- to: to ? recolorColorOf(firstChildElement(to), ctx) : null
4937
- };
4938
- }
4939
- case "grayscl": return { kind: "grayscale" };
4940
- case "biLevel": return {
4941
- kind: "biLevel",
4942
- threshold: pctAttr(child, "thresh")
4943
- };
4944
- case "alphaModFix": return {
4945
- kind: "alphaModFix",
4946
- amount: pctAttr(child, "amt") ?? 1
4947
- };
4948
- }
5477
+ const effect = recolorOf(child, ctx);
5478
+ if (effect) out.push(effect);
4949
5479
  }
4950
- return null;
5480
+ return out;
4951
5481
  }
4952
5482
  /**
4953
5483
  * The `p:blipFill/a:blip` this picture draws, or `null` when it has none. Three getters and
@@ -4983,7 +5513,7 @@ var Picture = class extends Shape {
4983
5513
  *
4984
5514
  * `fit` controls the picture's `a:srcRect` crop against its current frame
4985
5515
  * extent (`a:xfrm/a:ext`):
4986
- * - omitted (default): geometry and crop are left untouched — the caller owns
5516
+ * - omitted (default): geometry and crop are left untouched; the caller owns
4987
5517
  * sizing. Note an inherited `a:srcRect` was tuned to the *previous* image's
4988
5518
  * aspect ratio, so swapping in an image of a different ratio reuses a crop
4989
5519
  * that no longer fits and the result looks stretched; pass `fit` to refit.
@@ -5050,8 +5580,7 @@ var Picture = class extends Shape {
5050
5580
  return getOrAddChild(getOrAddChild(this.element, "p:blipFill", PIC_BLIPFILL_AFTER), "a:blip", BLIPFILL_BLIP_AFTER);
5051
5581
  }
5052
5582
  };
5053
- //#endregion
5054
- //#region src/read/api/shapes/connector.ts
5583
+ // src/read/api/shapes/connector.ts
5055
5584
  /**
5056
5585
  * A connector / line (`p:cxnSp`).
5057
5586
  */
@@ -5090,22 +5619,21 @@ var Connector = class extends Shape {
5090
5619
  };
5091
5620
  }
5092
5621
  };
5093
- //#endregion
5094
- //#region src/read/oxml/point-cache.ts
5622
+ // src/read/oxml/point-cache.ts
5095
5623
  /**
5096
5624
  * The one place a chart point cache is turned into an idx-ordered array.
5097
5625
  *
5098
5626
  * Both chart families cache their category/value data the same way: a run of indexed point
5099
5627
  * elements plus a declared count, which the reader widens back into a dense array so
5100
5628
  * `series.values[3]` means the point at `idx="3"` and a gap reads as `null`. Classic `c:`
5101
- * charts and 2016 `cx:` charts spell it differently — `c:ptCount/@val` and a `c:v` child
5102
- * against `cx:lvl/@ptCount` and the point's own text — and nothing else about the two decodes
5629
+ * charts and 2016 `cx:` charts spell it differently (`c:ptCount/@val` and a `c:v` child
5630
+ * against `cx:lvl/@ptCount` and the point's own text), and nothing else about the two decodes
5103
5631
  * differs, so the shape parameterizes over exactly those two differences.
5104
5632
  *
5105
5633
  * **Why neither number out of the file is trusted.** `@ptCount` is `xsd:unsignedInt`, so
5106
5634
  * `4294967295` is schema-valid, and `new Array(n).fill(null)` at that size is not a slow path
5107
5635
  * but a process kill: V8 answers `FATAL ERROR: invalid table size` and the host dies with no
5108
- * exception to catch. That was reachable through the public read API — `Presentation.load()`
5636
+ * exception to catch. That was reachable through the public read API: `Presentation.load()`
5109
5637
  * on a deck with an edited `c:ptCount` returned normally and the first `chart.series` access
5110
5638
  * killed the process. A single `<c:pt idx="900000000"/>` does the same thing on its own.
5111
5639
  * Reading a deck is the one part of this library whose input comes from somewhere other than
@@ -5113,8 +5641,8 @@ var Connector = class extends Shape {
5113
5641
  *
5114
5642
  * The array is sized by the points that are really there, not by the declared count. The cost
5115
5643
  * of that is a *trailing* gap: a cache declaring four points but carrying three (the fourth
5116
- * cell blank) reads three long rather than four. No value is lost — the dropped slots are the
5117
- * ones that would have been `null` — and the disagreement is warned about either way.
5644
+ * cell blank) reads three long rather than four. No value is lost (the dropped slots are the
5645
+ * ones that would have been `null`), and the disagreement is warned about either way.
5118
5646
  */
5119
5647
  /**
5120
5648
  * The highest point index this reader will honour.
@@ -5123,7 +5651,7 @@ var Connector = class extends Shape {
5123
5651
  * a point index at or above that cannot describe data any producer could have charted. Using
5124
5652
  * the sheet bound rather than a round number keeps the ceiling something a reader can check:
5125
5653
  * below it every real cache fits, above it the file is wrong by construction. It also caps the
5126
- * worst-case allocation at about 8 MB of `null` — survivable, and reported — instead of an
5654
+ * worst-case allocation at about 8 MB of `null`, survivable and reported, instead of an
5127
5655
  * unbounded one that takes the process with it.
5128
5656
  */
5129
5657
  const MAX_POINT_INDEX = 1048576;
@@ -5163,13 +5691,38 @@ function readIndexedPoints(pts, declaredCount, valueOf, label) {
5163
5691
  }
5164
5692
  return points;
5165
5693
  }
5166
- //#endregion
5167
- //#region src/read/api/chart.ts
5694
+ // src/read/api/chart.ts
5168
5695
  /** A chart graphic-frame's chart, backed by its chart part. */
5169
5696
  var Chart = class {
5170
5697
  part;
5171
- constructor(part) {
5698
+ rels;
5699
+ constructor(part, rels = null) {
5172
5700
  this.part = part;
5701
+ this.rels = rels;
5702
+ }
5703
+ /** The chart area's fill and border (`c:chartSpace/c:spPr`), or `null` when it states none. */
5704
+ get chartArea() {
5705
+ const root = this.part.dom.documentElement;
5706
+ const spPr = root ? firstChild(root, "c:spPr") : null;
5707
+ return spPr ? readAreaFormat(spPr, null, this.rels) : null;
5708
+ }
5709
+ /** Whether the chart area's corners are rounded (`c:roundedCorners`), or `null` when unstated. */
5710
+ get roundedCorners() {
5711
+ const root = this.part.dom.documentElement;
5712
+ const element = root ? firstChild(root, "c:roundedCorners") : null;
5713
+ return element ? boolValue(attr(element, "val")) : null;
5714
+ }
5715
+ /** The plot area's fill, border and manual layout, or `null` when the chart has no plot area. */
5716
+ get plotArea() {
5717
+ const plotArea = this.#plotArea();
5718
+ if (!plotArea) return null;
5719
+ return readAreaFormat(firstChild(plotArea, "c:spPr"), firstChild(plotArea, "c:layout"), this.rels);
5720
+ }
5721
+ /** The title's manual layout (`c:title/c:layout`), or `null` when it is placed automatically. */
5722
+ get titleLayout() {
5723
+ const chart = this.#chart();
5724
+ const title = chart && firstChild(chart, "c:title");
5725
+ return readManualLayout(title && firstChild(title, "c:layout"));
5173
5726
  }
5174
5727
  /** Partname of the chart part. */
5175
5728
  get partName() {
@@ -5224,9 +5777,28 @@ var Chart = class {
5224
5777
  const overlay = firstChild(legend, "c:overlay");
5225
5778
  return {
5226
5779
  position: pos ? attr(pos, "val") : null,
5227
- overlay: overlay ? boolValue(attr(overlay, "val")) : null
5780
+ overlay: overlay ? boolValue(attr(overlay, "val")) : null,
5781
+ textStyle: readTextStyle(firstChild(legend, "c:txPr")),
5782
+ layout: readManualLayout(firstChild(legend, "c:layout"))
5228
5783
  };
5229
5784
  }
5785
+ /** The chart title's formatting (`c:title/c:tx/c:rich`), or `null` when the chart has no rich title. */
5786
+ get titleStyle() {
5787
+ const chart = this.#chart();
5788
+ return readTitleStyle(chart && firstChild(chart, "c:title"));
5789
+ }
5790
+ /**
5791
+ * The language the chart's text is tagged with: `c:lang`, which PowerPoint writes, else the first
5792
+ * paragraph end's `@lang` in the part, where `addChart` writes its `lang`. `null` when neither says.
5793
+ */
5794
+ get language() {
5795
+ const root = this.part.dom.documentElement;
5796
+ if (!root) return null;
5797
+ const lang = firstChild(root, "c:lang");
5798
+ if (lang) return attr(lang, "val");
5799
+ const end = descendantsByTag(root, OOXML_NS.a, "endParaRPr").find((element) => attr(element, "lang"));
5800
+ return end ? attr(end, "lang") : null;
5801
+ }
5230
5802
  /**
5231
5803
  * The chart-level data-label settings, read from the first plot group's
5232
5804
  * aggregate `c:dLbls` (the group-wide block after the series). `null` when the
@@ -5241,6 +5813,72 @@ var Chart = class {
5241
5813
  const group = this.#chartGroups()[0];
5242
5814
  return readDataLabels(group ? firstChild(group, "c:dLbls") : null);
5243
5815
  }
5816
+ /** The data table under the plot (`c:plotArea/c:dTable`), or `null` when the chart draws none. */
5817
+ get dataTable() {
5818
+ const plotArea = this.#plotArea();
5819
+ const table = plotArea && firstChild(plotArea, "c:dTable");
5820
+ if (!table) return null;
5821
+ const flag = (qname) => {
5822
+ const element = firstChild(table, qname);
5823
+ return element ? boolValue(attr(element, "val")) : null;
5824
+ };
5825
+ return {
5826
+ showHorizontalBorder: flag("c:showHorzBorder"),
5827
+ showVerticalBorder: flag("c:showVertBorder"),
5828
+ showOutline: flag("c:showOutline"),
5829
+ showKeys: flag("c:showKeys"),
5830
+ fontSizePt: readTextSizePt(firstChild(table, "c:txPr"))
5831
+ };
5832
+ }
5833
+ /**
5834
+ * The key/value annotations `addChart`'s `metadata` option writes: the pptx-ts extension on
5835
+ * `c:chartSpace/c:extLst` (uri `{094A432E-1F6C-499B-95B8-B57DC9536949}`). `null` when the chart
5836
+ * carries no such extension; an extension with no item reads as an empty map.
5837
+ */
5838
+ get metadata() {
5839
+ const root = this.part.dom.documentElement;
5840
+ const extLst = root ? firstChild(root, "c:extLst") : null;
5841
+ const ext = extLst ? getElements(extLst, "c:ext").find((element) => attr(element, "uri") === CHART_METADATA_EXT_URI) : void 0;
5842
+ const payload = ext ? childElements(ext).find((element) => element.namespaceURI === "http://ts-pptx.com/schema/chart/metadata" && element.localName === "metadata") : void 0;
5843
+ if (!payload) return null;
5844
+ const out = {};
5845
+ for (const item of childElements(payload)) {
5846
+ if (item.namespaceURI !== "http://ts-pptx.com/schema/chart/metadata" || item.localName !== "item") continue;
5847
+ const key = attr(item, "key");
5848
+ if (key) out[key] = attr(item, "value") ?? "";
5849
+ }
5850
+ return out;
5851
+ }
5852
+ /**
5853
+ * The plot groups (`c:barChart`, `c:lineChart`, …) of the plot area, in document order, each
5854
+ * with what it sets on all its series and the series it plots. A combo chart has several.
5855
+ */
5856
+ get plotGroups() {
5857
+ return this.#chartGroups().map((group) => new ChartPlotGroup(group, this.part));
5858
+ }
5859
+ /** The 3-D view (`c:chart/c:view3D`), or `null` when the chart states none. */
5860
+ get view3D() {
5861
+ const chart = this.#chart();
5862
+ const view = chart && firstChild(chart, "c:view3D");
5863
+ if (!view) return null;
5864
+ const number = (qname) => {
5865
+ const element = firstChild(view, qname);
5866
+ return element ? numberValue(attr(element, "val")) : null;
5867
+ };
5868
+ const rAngAx = firstChild(view, "c:rAngAx");
5869
+ return {
5870
+ rotX: number("c:rotX"),
5871
+ rotY: number("c:rotY"),
5872
+ perspective: number("c:perspective"),
5873
+ rightAngleAxes: rAngAx ? boolValue(attr(rAngAx, "val")) : null
5874
+ };
5875
+ }
5876
+ /** How blank cells plot (`c:chart/c:dispBlanksAs`): `gap`, `zero` or `span`, or `null` when unstated. */
5877
+ get displayBlanksAs() {
5878
+ const chart = this.#chart();
5879
+ const element = chart && firstChild(chart, "c:dispBlanksAs");
5880
+ return element ? attr(element, "val") : null;
5881
+ }
5244
5882
  /** The data series (`c:ser`) across all chart groups, in document order. */
5245
5883
  get series() {
5246
5884
  return this.#chartGroups().flatMap((group) => getElements(group, "c:ser")).map((ser) => new ChartSeries(ser, this.part));
@@ -5288,6 +5926,106 @@ var Chart = class {
5288
5926
  }
5289
5927
  };
5290
5928
  /**
5929
+ * One plot group of a chart's plot area (`c:barChart`, `c:lineChart`, `c:pieChart`, …): the series
5930
+ * it plots and what it sets on all of them. Each accessor reads `null` (or `false` for an element
5931
+ * that is either there or not) where the group does not state it, including on a plot type whose
5932
+ * schema has no such element.
5933
+ */
5934
+ var ChartPlotGroup = class {
5935
+ group;
5936
+ part;
5937
+ constructor(group, part) {
5938
+ this.group = group;
5939
+ this.part = part;
5940
+ }
5941
+ /** The plot type, the element's local name less `Chart`: `bar`, `line`, `bar3D`, `doughnut`, … */
5942
+ get type() {
5943
+ return (this.group.localName ?? "").replace(/Chart$/, "");
5944
+ }
5945
+ /** The series this group plots (`c:ser`), in document order. */
5946
+ get series() {
5947
+ return getElements(this.group, "c:ser").map((ser) => new ChartSeries(ser, this.part));
5948
+ }
5949
+ /**
5950
+ * The ids of the axes this group plots against (`c:axId`), as {@link ChartAxis.id} reads them: a
5951
+ * combo's group on the secondary axes names the secondary pair. Empty for a pie or a doughnut.
5952
+ */
5953
+ get axisIds() {
5954
+ return childElements(this.group).filter((child) => child.localName === "axId").map((axId) => numberValue(attr(axId, "val"))).filter((id) => id !== null);
5955
+ }
5956
+ /** A bar group's direction (`c:barDir`): `bar` (horizontal) or `col` (vertical). */
5957
+ get barDirection() {
5958
+ return this.#value("c:barDir");
5959
+ }
5960
+ /** How the series stack (`c:grouping`): `clustered`, `stacked`, `percentStacked` or `standard`. */
5961
+ get grouping() {
5962
+ return this.#value("c:grouping");
5963
+ }
5964
+ /** The gap between bar clusters as a percentage of a bar's width (`c:gapWidth`). */
5965
+ get gapWidthPct() {
5966
+ return this.#number("c:gapWidth");
5967
+ }
5968
+ /** How far bars in a cluster overlap, -100 to 100 percent (`c:overlap`). */
5969
+ get overlapPct() {
5970
+ return this.#number("c:overlap");
5971
+ }
5972
+ /** Whether stacked bars are joined by series lines (`c:serLines`). */
5973
+ get seriesLines() {
5974
+ return firstChild(this.group, "c:serLines") !== null;
5975
+ }
5976
+ /** The series lines' stroke (`c:serLines/c:spPr/a:ln`), or `null` when they state none. */
5977
+ get seriesLinesLine() {
5978
+ return spPrLine(firstChild(this.group, "c:serLines"));
5979
+ }
5980
+ /** The depth gap of a 3-D bar group, as a percentage (`c:gapDepth`). */
5981
+ get gapDepthPct() {
5982
+ return this.#number("c:gapDepth");
5983
+ }
5984
+ /** The group-wide 3-D bar shape (`c:shape`); a series can state its own ({@link ChartSeries.shape}). */
5985
+ get shape() {
5986
+ return this.#value("c:shape");
5987
+ }
5988
+ /** The angle of a pie's first slice, 0 to 360 degrees (`c:firstSliceAng`). */
5989
+ get firstSliceAngle() {
5990
+ return this.#number("c:firstSliceAng");
5991
+ }
5992
+ /** A doughnut's hole, 10 to 90 percent of its size (`c:holeSize`). */
5993
+ get holeSizePct() {
5994
+ return this.#number("c:holeSize");
5995
+ }
5996
+ /** A radar's style (`c:radarStyle`): `standard`, `marker` or `filled`. */
5997
+ get radarStyle() {
5998
+ return this.#value("c:radarStyle");
5999
+ }
6000
+ /** Whether a surface is drawn as a wireframe (`c:wireframe`). */
6001
+ get wireframe() {
6002
+ const element = firstChild(this.group, "c:wireframe");
6003
+ return element ? boolValue(attr(element, "val")) : null;
6004
+ }
6005
+ /** Whether a line or stock group draws up/down bars (`c:upDownBars`). */
6006
+ get upDownBars() {
6007
+ return firstChild(this.group, "c:upDownBars") !== null;
6008
+ }
6009
+ /** Escape hatch: the underlying plot-group element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
6010
+ get element_() {
6011
+ return this.group;
6012
+ }
6013
+ /** Mark the owning chart part dirty so `save()` reserializes it. Call after mutating {@link element_}. */
6014
+ markDirty() {
6015
+ this.part.markDirty();
6016
+ }
6017
+ /** The `@val` of a named child, or `null`. */
6018
+ #value(qname) {
6019
+ const element = firstChild(this.group, qname);
6020
+ return element ? attr(element, "val") : null;
6021
+ }
6022
+ /** The `@val` of a named child as a number, or `null`. */
6023
+ #number(qname) {
6024
+ const element = firstChild(this.group, qname);
6025
+ return element ? numberValue(attr(element, "val")) : null;
6026
+ }
6027
+ };
6028
+ /**
5291
6029
  * One axis (`c:catAx`/`c:valAx`/`c:dateAx`/`c:serAx`) of a chart's plot area.
5292
6030
  * All four share the scaling/delete/position/tickmark structure; `kind`
5293
6031
  * distinguishes them.
@@ -5385,6 +6123,81 @@ var ChartAxis = class {
5385
6123
  const el = firstChild(this.ax, "c:minorUnit");
5386
6124
  return el ? numberValue(attr(el, "val")) : null;
5387
6125
  }
6126
+ /** The tick labels' text formatting and rotation (`c:txPr`), or `null` when the axis states none. */
6127
+ get labelStyle() {
6128
+ return readTextStyle(firstChild(this.ax, "c:txPr"));
6129
+ }
6130
+ /** The axis title's formatting (`c:title/c:tx/c:rich`), or `null` when it has no rich title. */
6131
+ get titleStyle() {
6132
+ return readTitleStyle(firstChild(this.ax, "c:title"));
6133
+ }
6134
+ /** Where this axis crosses the other one, as a value on it (`c:crossesAt`), or `null`. */
6135
+ get crossesAt() {
6136
+ const element = firstChild(this.ax, "c:crossesAt");
6137
+ return element ? numberValue(attr(element, "val")) : null;
6138
+ }
6139
+ /** Where this axis crosses the other one, as a rule (`c:crosses`: `autoZero`, `max`, `min`), or `null`. */
6140
+ get crosses() {
6141
+ const element = firstChild(this.ax, "c:crosses");
6142
+ return element ? attr(element, "val") : null;
6143
+ }
6144
+ /** Whether a value axis crosses between categories or on them (`c:crossBetween`: `between`, `midCat`). */
6145
+ get crossBetween() {
6146
+ const element = firstChild(this.ax, "c:crossBetween");
6147
+ return element ? attr(element, "val") : null;
6148
+ }
6149
+ /** A value axis' display units (`c:dispUnits`), or `null` when it shows plain values. */
6150
+ get displayUnits() {
6151
+ const units = firstChild(this.ax, "c:dispUnits");
6152
+ if (!units) return null;
6153
+ const builtIn = firstChild(units, "c:builtInUnit");
6154
+ const custom = firstChild(units, "c:custUnit");
6155
+ return {
6156
+ builtIn: builtIn ? attr(builtIn, "val") : null,
6157
+ custom: custom ? numberValue(attr(custom, "val")) : null,
6158
+ showLabel: firstChild(units, "c:dispUnitsLbl") !== null
6159
+ };
6160
+ }
6161
+ /** How many categories pass between two labels (`c:tickLblSkip`), or `null` when automatic. */
6162
+ get labelSkip() {
6163
+ const element = firstChild(this.ax, "c:tickLblSkip");
6164
+ return element ? numberValue(attr(element, "val")) : null;
6165
+ }
6166
+ /** Whether a category axis flattens a multi-level category list (`c:noMultiLvlLbl`), or `null` when unstated. */
6167
+ get noMultiLevelLabels() {
6168
+ const element = firstChild(this.ax, "c:noMultiLvlLbl");
6169
+ return element ? boolValue(attr(element, "val")) : null;
6170
+ }
6171
+ /** A date axis' base unit (`c:baseTimeUnit`: `days`, `months`, `years`), or `null`. */
6172
+ get baseTimeUnit() {
6173
+ return this.#value("c:baseTimeUnit");
6174
+ }
6175
+ /** A date axis' major unit scale (`c:majorTimeUnit`), or `null`. */
6176
+ get majorTimeUnit() {
6177
+ return this.#value("c:majorTimeUnit");
6178
+ }
6179
+ /** A date axis' minor unit scale (`c:minorTimeUnit`), or `null`. */
6180
+ get minorTimeUnit() {
6181
+ return this.#value("c:minorTimeUnit");
6182
+ }
6183
+ /** The axis line's own stroke (`c:spPr/a:ln`), or `null` when it states none. */
6184
+ get line() {
6185
+ const spPr = firstChild(this.ax, "c:spPr");
6186
+ const ln = spPr && firstChild(spPr, "a:ln");
6187
+ return ln ? readChartLine(ln) : null;
6188
+ }
6189
+ /** The major gridlines' stroke (`c:majorGridlines/c:spPr/a:ln`), or `null` when they state none. */
6190
+ get majorGridlinesLine() {
6191
+ const gridlines = firstChild(this.ax, "c:majorGridlines");
6192
+ const spPr = gridlines && firstChild(gridlines, "c:spPr");
6193
+ const ln = spPr && firstChild(spPr, "a:ln");
6194
+ return ln ? readChartLine(ln) : null;
6195
+ }
6196
+ /** The `@val` of a named child, or `null`. */
6197
+ #value(qname) {
6198
+ const element = firstChild(this.ax, qname);
6199
+ return element ? attr(element, "val") : null;
6200
+ }
5388
6201
  /** Escape hatch: the underlying axis element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
5389
6202
  get element_() {
5390
6203
  return this.ax;
@@ -5416,20 +6229,14 @@ var ChartSeries = class {
5416
6229
  return spPr ? readSolidFill(spPr) : null;
5417
6230
  }
5418
6231
  /**
5419
- * Series line/stroke (`c:spPr/a:ln`) — width, dash, colour. `null` when the
6232
+ * Series line/stroke (`c:spPr/a:ln`): width, dash, colour. `null` when the
5420
6233
  * series `c:spPr` has no `a:ln` (bar/area fills carry no line by default).
5421
6234
  */
5422
6235
  get line() {
5423
6236
  const spPr = firstChild(this.ser, "c:spPr");
5424
6237
  const ln = spPr && firstChild(spPr, "a:ln");
5425
6238
  if (!ln) return null;
5426
- const { widthPt, dash, colorRef, noFill } = readLineBasics(ln, null);
5427
- return {
5428
- widthPt,
5429
- dash,
5430
- colorRef,
5431
- noFill
5432
- };
6239
+ return readChartLine(ln);
5433
6240
  }
5434
6241
  /** Series index (`c:idx/@val`), or `null` if absent. */
5435
6242
  get index() {
@@ -5478,6 +6285,16 @@ var ChartSeries = class {
5478
6285
  get yValues() {
5479
6286
  return this.#numbers("c:yVal");
5480
6287
  }
6288
+ /**
6289
+ * The number format the series' values are cached in (`c:numCache/c:formatCode` under `c:val`, or
6290
+ * under `c:yVal` on a scatter or bubble series), or `null` when the cache states none. PowerPoint
6291
+ * paints a source-linked data label and a data table in it.
6292
+ */
6293
+ get valueFormatCode() {
6294
+ const cache = findCache(firstChild(this.ser, "c:val") ?? firstChild(this.ser, "c:yVal"));
6295
+ const formatCode = cache && firstChild(cache, "c:formatCode");
6296
+ return formatCode ? formatCode.textContent ?? null : null;
6297
+ }
5481
6298
  /** Cached bubble sizes of a bubble series (`c:bubbleSize`); non-numeric or missing points are `null`. */
5482
6299
  get bubbleSizes() {
5483
6300
  return this.#numbers("c:bubbleSize");
@@ -5510,6 +6327,113 @@ var ChartSeries = class {
5510
6327
  get dataLabels() {
5511
6328
  return readDataLabels(firstChild(this.ser, "c:dLbls"));
5512
6329
  }
6330
+ /**
6331
+ * The series' labels that state their own text (`c:ser/c:dLbls/c:dLbl` with `c:tx/c:rich`), in
6332
+ * document order. A label that only formats or deletes its point has no text and is left out.
6333
+ */
6334
+ get customLabels() {
6335
+ const dLbls = firstChild(this.ser, "c:dLbls");
6336
+ if (!dLbls) return [];
6337
+ const labels = [];
6338
+ for (const dLbl of childElements(dLbls)) {
6339
+ if (dLbl.localName !== "dLbl") continue;
6340
+ const idx = firstChild(dLbl, "c:idx");
6341
+ const index = idx ? numberValue(attr(idx, "val")) : null;
6342
+ const tx = firstChild(dLbl, "c:tx");
6343
+ const rich = tx && firstChild(tx, "c:rich");
6344
+ if (index === null || !rich) continue;
6345
+ const runs = [];
6346
+ for (const paragraph of childElements(rich)) {
6347
+ if (paragraph.localName !== "p") continue;
6348
+ for (const run of childElements(paragraph)) {
6349
+ if (run.localName !== "r" && run.localName !== "fld") continue;
6350
+ const text = firstChild(run, "a:t")?.textContent ?? "";
6351
+ runs.push({
6352
+ text,
6353
+ field: run.localName === "fld" ? attr(run, "type") : null
6354
+ });
6355
+ }
6356
+ }
6357
+ labels.push({
6358
+ index,
6359
+ runs
6360
+ });
6361
+ }
6362
+ return labels;
6363
+ }
6364
+ /** The series' gradient fill (`c:spPr/a:gradFill`), or `null`. */
6365
+ get gradientFill() {
6366
+ const spPr = firstChild(this.ser, "c:spPr");
6367
+ return spPr ? readGradientFill(spPr, NO_THEME) : null;
6368
+ }
6369
+ /** The series' pattern fill (`c:spPr/a:pattFill`), or `null`. */
6370
+ get patternFill() {
6371
+ const spPr = firstChild(this.ser, "c:spPr");
6372
+ return spPr ? readPatternFill(spPr, NO_THEME) : null;
6373
+ }
6374
+ /** The series' outer shadow (`c:spPr/a:effectLst/a:outerShdw`), or `null`. */
6375
+ get shadow() {
6376
+ const spPr = firstChild(this.ser, "c:spPr");
6377
+ return readOuterShadow(spPr && firstChild(spPr, "a:effectLst"), NO_THEME);
6378
+ }
6379
+ /** The series' inner shadow (`c:spPr/a:effectLst/a:innerShdw`), or `null`. */
6380
+ get innerShadow() {
6381
+ const spPr = firstChild(this.ser, "c:spPr");
6382
+ return readInnerShadow(spPr && firstChild(spPr, "a:effectLst"), NO_THEME);
6383
+ }
6384
+ /** Whether a line or scatter series is smoothed (`c:smooth`), or `null` when unstated. */
6385
+ get smooth() {
6386
+ const element = firstChild(this.ser, "c:smooth");
6387
+ return element ? boolValue(attr(element, "val")) : null;
6388
+ }
6389
+ /** The series' data-point marker (`c:marker`), or `null` when it states none. */
6390
+ get marker() {
6391
+ const marker = firstChild(this.ser, "c:marker");
6392
+ if (!marker) return null;
6393
+ const symbol = firstChild(marker, "c:symbol");
6394
+ const size = firstChild(marker, "c:size");
6395
+ const spPr = firstChild(marker, "c:spPr");
6396
+ const ln = spPr && firstChild(spPr, "a:ln");
6397
+ return {
6398
+ symbol: symbol ? attr(symbol, "val") : null,
6399
+ size: size ? numberValue(attr(size, "val")) : null,
6400
+ fill: spPr ? readSolidFill(spPr) : null,
6401
+ line: ln ? readChartLine(ln) : null
6402
+ };
6403
+ }
6404
+ /**
6405
+ * The points this series formats on its own (`c:dPt`), in document order: a bar's per-point
6406
+ * colours, a pie's slice colours. A `c:dPt` with no `c:idx` is left out.
6407
+ */
6408
+ get dataPoints() {
6409
+ const points = [];
6410
+ for (const dPt of childElements(this.ser)) {
6411
+ if (dPt.localName !== "dPt") continue;
6412
+ const idx = firstChild(dPt, "c:idx");
6413
+ const index = idx ? numberValue(attr(idx, "val")) : null;
6414
+ if (index === null) continue;
6415
+ const invert = firstChild(dPt, "c:invertIfNegative");
6416
+ const spPr = firstChild(dPt, "c:spPr");
6417
+ points.push({
6418
+ index,
6419
+ invertIfNegative: invert ? boolValue(attr(invert, "val")) : null,
6420
+ fill: spPr ? readSolidFill(spPr) : null,
6421
+ patternFill: spPr ? readPatternFill(spPr, NO_THEME) : null,
6422
+ line: spPrLine(dPt)
6423
+ });
6424
+ }
6425
+ return points;
6426
+ }
6427
+ /** Whether a bar series paints a negative bar inverted (`c:invertIfNegative`), or `null` when unstated. */
6428
+ get invertIfNegative() {
6429
+ const element = firstChild(this.ser, "c:invertIfNegative");
6430
+ return element ? boolValue(attr(element, "val")) : null;
6431
+ }
6432
+ /** A 3-D bar series' own shape (`c:shape`: `box`, `cylinder`, `cone`, …), or `null` when unstated. */
6433
+ get shape() {
6434
+ const element = firstChild(this.ser, "c:shape");
6435
+ return element ? attr(element, "val") : null;
6436
+ }
5513
6437
  /** Escape hatch: the underlying `c:ser` element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
5514
6438
  get element_() {
5515
6439
  return this.ser;
@@ -5524,6 +6448,34 @@ var ChartSeries = class {
5524
6448
  return readPoints(container && findCache(container)).map(numberValue);
5525
6449
  }
5526
6450
  };
6451
+ /** The size, in points, a chart text block (`c:txPr`) sets on its first paragraph's `a:defRPr`. */
6452
+ function readTextSizePt(txPr) {
6453
+ const paragraph = txPr && firstChild(txPr, "a:p");
6454
+ const pPr = paragraph && firstChild(paragraph, "a:pPr");
6455
+ const defRPr = pPr && firstChild(pPr, "a:defRPr");
6456
+ const size = defRPr ? numberValue(attr(defRPr, "sz")) : null;
6457
+ return size === null ? null : size / 100;
6458
+ }
6459
+ /**
6460
+ * A chart stroke (`a:ln`): {@link readLineBasics}' width, dash, colour and no-fill, then the cap and
6461
+ * the colour's `a:alpha`, which a chart part read without a theme leaves unresolved otherwise.
6462
+ */
6463
+ function readChartLine(ln) {
6464
+ const { widthPt, dash, colorRef, noFill } = readLineBasics(ln, null);
6465
+ const solidFill = firstChild(ln, "a:solidFill");
6466
+ const color = solidFill && firstChildElement(solidFill);
6467
+ const alpha = color && firstChild(color, "a:alpha");
6468
+ const alphaValue = alpha ? numberValue(attr(alpha, "val")) : null;
6469
+ return {
6470
+ widthPt,
6471
+ dash,
6472
+ colorRef,
6473
+ noFill,
6474
+ cap: attr(ln, "cap") ?? null,
6475
+ alpha: alphaValue === null ? null : alphaValue / 1e5,
6476
+ colorTransformed: color ? childElements(color).some((child) => child.localName !== "alpha") : false
6477
+ };
6478
+ }
5527
6479
  /** Read a `c:dLbls` block's show flags, position and number format, or `null` when there is no block. */
5528
6480
  function readDataLabels(dLbls) {
5529
6481
  if (!dLbls) return null;
@@ -5541,9 +6493,59 @@ function readDataLabels(dLbls) {
5541
6493
  showBubbleSize: flag("c:showBubbleSize"),
5542
6494
  showLeaderLines: flag("c:showLeaderLines"),
5543
6495
  position: pos ? attr(pos, "val") : null,
5544
- numberFormat: readNumberFormat(dLbls)
6496
+ numberFormat: readNumberFormat(dLbls),
6497
+ textStyle: readTextStyle(firstChild(dLbls, "c:txPr")),
6498
+ fill: spPrFill(dLbls),
6499
+ leaderLines: spPrLine(firstChild(dLbls, "c:leaderLines"))
5545
6500
  };
5546
6501
  }
6502
+ /** The size, in 1/60000 of a degree, of the `@rot` PowerPoint writes for an upright default. */
6503
+ const MAX_TEXT_ROTATION = 54e5;
6504
+ /** Read a chart text block, a `c:txPr` or a title's `c:rich`; see {@link ChartTextStyle}. */
6505
+ function readTextStyle(block) {
6506
+ if (!block) return null;
6507
+ const bodyPr = firstChild(block, "a:bodyPr");
6508
+ const paragraph = firstChild(block, "a:p");
6509
+ const pPr = paragraph && firstChild(paragraph, "a:pPr");
6510
+ const defRPr = pPr && firstChild(pPr, "a:defRPr");
6511
+ const run = paragraph && firstChild(paragraph, "a:r");
6512
+ const props = [run && firstChild(run, "a:rPr"), defRPr].filter((element) => element !== null);
6513
+ const first = (name) => {
6514
+ for (const element of props) {
6515
+ const value = attr(element, name);
6516
+ if (value !== null) return value;
6517
+ }
6518
+ return null;
6519
+ };
6520
+ const child = (qname) => {
6521
+ for (const element of props) {
6522
+ const found = firstChild(element, qname);
6523
+ if (found) return found;
6524
+ }
6525
+ return null;
6526
+ };
6527
+ const solidFill = child("a:solidFill");
6528
+ const colorElement = solidFill && firstChildElement(solidFill);
6529
+ const latin = child("a:latin");
6530
+ const size = numberValue(first("sz"));
6531
+ const rot = bodyPr ? numberValue(attr(bodyPr, "rot")) : null;
6532
+ return {
6533
+ sizePt: size === null ? null : size / 100,
6534
+ bold: boolValue(first("b")),
6535
+ italic: boolValue(first("i")),
6536
+ underline: first("u"),
6537
+ color: colorElement ? readColorRef(colorElement, null) : null,
6538
+ colorTransformed: colorElement ? childElements(colorElement).some((transform) => transform.localName !== "alpha") : false,
6539
+ fontName: latin ? attr(latin, "typeface") : null,
6540
+ rotationDeg: rot === null || Math.abs(rot) > MAX_TEXT_ROTATION ? null : rot / 6e4,
6541
+ align: pPr ? attr(pPr, "algn") : null
6542
+ };
6543
+ }
6544
+ /** A `c:title`'s formatting, off its `c:tx/c:rich`. */
6545
+ function readTitleStyle(title) {
6546
+ const tx = title && firstChild(title, "c:tx");
6547
+ return readTextStyle(tx && firstChild(tx, "c:rich"));
6548
+ }
5547
6549
  /**
5548
6550
  * Read a `c:multiLvlStrCache` into its levels, in document order.
5549
6551
  *
@@ -5590,11 +6592,79 @@ function readSolidFill(container) {
5590
6592
  const solidFill = firstChild(container, "a:solidFill");
5591
6593
  const noFill = !!firstChild(container, "a:noFill");
5592
6594
  if (!solidFill && !noFill) return null;
6595
+ const color = solidFill ? firstChildElement(solidFill) : null;
6596
+ return {
6597
+ colorRef: readColorRef(color, null),
6598
+ noFill,
6599
+ alpha: colorAlpha(color)
6600
+ };
6601
+ }
6602
+ /** The `a:alpha` of a colour element, 0 to 1, or `null`. */
6603
+ function colorAlpha(color) {
6604
+ const alpha = color && firstChild(color, "a:alpha");
6605
+ const value = alpha ? numberValue(attr(alpha, "val")) : null;
6606
+ return value === null ? null : value / 1e5;
6607
+ }
6608
+ /**
6609
+ * The colour context a chart part's fills resolve against: none. A literal resolves with its
6610
+ * transforms; a scheme colour does not, since the part is read without its slide's theme.
6611
+ */
6612
+ const NO_THEME = {
6613
+ clrMap: /* @__PURE__ */ new Map(),
6614
+ clrScheme: /* @__PURE__ */ new Map()
6615
+ };
6616
+ /** The solid fill of an element's `c:spPr`, or `null`. */
6617
+ function spPrFill(parent) {
6618
+ const spPr = parent && firstChild(parent, "c:spPr");
6619
+ return spPr ? readSolidFill(spPr) : null;
6620
+ }
6621
+ /** The stroke of an element's `c:spPr/a:ln`, or `null`. */
6622
+ function spPrLine(parent) {
6623
+ const spPr = parent && firstChild(parent, "c:spPr");
6624
+ const ln = spPr && firstChild(spPr, "a:ln");
6625
+ return ln ? readChartLine(ln) : null;
6626
+ }
6627
+ /** A `c:layout`'s manual layout, or `null` when it has none. */
6628
+ function readManualLayout(layout) {
6629
+ const manual = layout && firstChild(layout, "c:manualLayout");
6630
+ if (!manual) return null;
6631
+ const value = (qname) => {
6632
+ const element = firstChild(manual, qname);
6633
+ return element ? attr(element, "val") : null;
6634
+ };
6635
+ const number = (qname) => numberValue(value(qname));
6636
+ return {
6637
+ target: value("c:layoutTarget"),
6638
+ xMode: value("c:xMode"),
6639
+ yMode: value("c:yMode"),
6640
+ x: number("c:x"),
6641
+ y: number("c:y"),
6642
+ w: number("c:w"),
6643
+ h: number("c:h")
6644
+ };
6645
+ }
6646
+ /** A chart surface's `c:spPr` and `c:layout` as a {@link ChartAreaFormat}. */
6647
+ function readAreaFormat(spPr, layout, rels) {
6648
+ const solidFill = spPr && firstChild(spPr, "a:solidFill");
6649
+ const color = solidFill && firstChildElement(solidFill);
6650
+ const ln = spPr && firstChild(spPr, "a:ln");
5593
6651
  return {
5594
- colorRef: readColorRef(solidFill ? firstChildElement(solidFill) : null, null),
5595
- noFill
6652
+ fillNoFill: spPr ? firstChild(spPr, "a:noFill") !== null : false,
6653
+ hasOwnFill: hasFillChoice(spPr),
6654
+ fillColor: colorValueOf(color, "srgbClr"),
6655
+ fillSchemeColor: colorValueOf(color, "schemeClr"),
6656
+ resolvedFill: color && color.localName === "srgbClr" ? resolveColorElement(color, NO_THEME) : null,
6657
+ gradientFill: spPr ? readGradientFill(spPr, NO_THEME) : null,
6658
+ patternFill: spPr ? readPatternFill(spPr, NO_THEME) : null,
6659
+ pictureFill: spPr ? readPictureFill(spPr, rels) : null,
6660
+ border: ln ? readChartLine(ln) : null,
6661
+ layout: readManualLayout(layout)
5596
6662
  };
5597
6663
  }
6664
+ /** The `@val` of a colour element of the given local name, or `null`. */
6665
+ function colorValueOf(color, localName) {
6666
+ return color && color.localName === localName ? attr(color, "val") : null;
6667
+ }
5598
6668
  /** Resolve a `c:cat`/`c:val`/`c:tx` container to its cache element (`c:numCache`/`c:strCache`/literal). */
5599
6669
  function findCache(container) {
5600
6670
  if (!container) return null;
@@ -5633,8 +6703,7 @@ function readPoints(cache) {
5633
6703
  const ptCount = firstChild(cache, "c:ptCount");
5634
6704
  return readIndexedPoints(getElements(cache, "c:pt"), ptCount ? numberValue(attr(ptCount, "val")) : null, (pt) => firstChild(pt, "c:v")?.textContent ?? null, "c:ptCount");
5635
6705
  }
5636
- //#endregion
5637
- //#region src/read/api/chartex.ts
6706
+ // src/read/api/chartex.ts
5638
6707
  /** A chartEx graphic-frame's chart, backed by its `cx:chartSpace` part. */
5639
6708
  var ChartEx = class {
5640
6709
  part;
@@ -5646,7 +6715,7 @@ var ChartEx = class {
5646
6715
  return this.part.partName;
5647
6716
  }
5648
6717
  /**
5649
- * The `cx:series/@layoutId` tokens in plot-area document order — the raw chartEx
6718
+ * The `cx:series/@layoutId` tokens in plot-area document order: the raw chartEx
5650
6719
  * layout names (`waterfall`, `funnel`, `treemap`, `sunburst`, `boxWhisker`,
5651
6720
  * `regionMap`, or `clusteredColumn`/`paretoLine` for histogram & pareto). This is
5652
6721
  * the OOXML truth: a histogram and a pareto both surface as `clusteredColumn`, so
@@ -5775,7 +6844,7 @@ var ChartExSeries = class {
5775
6844
  return readLevelPoints(numDim && firstChild(numDim, "cx:lvl")).map(numberValue);
5776
6845
  }
5777
6846
  /**
5778
- * Cached category labels (`cx:strDim`'s leaf `cx:lvl` — the first level, which
6847
+ * Cached category labels (`cx:strDim`'s leaf `cx:lvl`, the first level, which
5779
6848
  * the writer emits leaf-first). Empty when the data block carries no `cx:strDim`.
5780
6849
  */
5781
6850
  get categories() {
@@ -5866,16 +6935,15 @@ function readLevelPoints(lvl) {
5866
6935
  if (!lvl) return [];
5867
6936
  return readIndexedPoints(getElements(lvl, "cx:pt"), numberValue(attr(lvl, "ptCount")), (pt) => pt.textContent ?? null, "cx:lvl/@ptCount");
5868
6937
  }
5869
- //#endregion
5870
- //#region src/read/api/diagram.ts
6938
+ // src/read/api/diagram.ts
5871
6939
  /**
5872
6940
  * Read proxies for a SmartArt diagram (`dgm:dataModel`) hosted in a `p:graphicFrame`.
5873
6941
  *
5874
6942
  * SmartArt is a four-part construct, and the frame itself holds none of it: the
5875
6943
  * `a:graphicData` carries only a `dgm:relIds` with four relationship ids pointing at the
5876
6944
  * data, layout, quick-style and colours parts under `/ppt/diagrams/`. The **data** part is
5877
- * the one with content in it — a flat `dgm:ptLst` of points plus a `dgm:cxnLst` of
5878
- * connections that give the points their tree — and it is what this surface decodes. The
6945
+ * the one with content in it (a flat `dgm:ptLst` of points plus a `dgm:cxnLst` of
6946
+ * connections that give the points their tree), and it is what this surface decodes. The
5879
6947
  * other three are generated presets (`urn:microsoft.com/office/officeart/…`) describing how
5880
6948
  * PowerPoint *draws* the graph rather than what it says; they surface as parts, not getters.
5881
6949
  *
@@ -5908,14 +6976,14 @@ function readLevelPoints(lvl) {
5908
6976
  *
5909
6977
  * **A point's text is stored twice.** The data model is what PowerPoint reads, but a deck
5910
6978
  * also carries a *drawing* part (`dsp:drawing`) holding a copy of every drawn string, and
5911
- * every renderer without a SmartArt layout engine — LibreOffice, Google Slides, thumbnailers
5912
- * — paints that copy and nothing else. {@link DiagramPoint.drawnShape} is the link between
6979
+ * every renderer without a SmartArt layout engine (LibreOffice, Google Slides, thumbnailers)
6980
+ * paints that copy and nothing else. {@link DiagramPoint.drawnShape} is the link between
5913
6981
  * the two, and {@link DiagramPoint.text}'s setter is what keeps them in step.
5914
6982
  *
5915
6983
  * Authoring a SmartArt graphic from nothing is out of scope: the layout part is a
5916
6984
  * constraint-solver program PowerPoint executes, and the presentation tree it generates is
5917
6985
  * not derivable from the user's content. Editing the text of one that already exists is
5918
- * supported — see {@link DiagramPoint.text}.
6986
+ * supported; see {@link DiagramPoint.text}.
5919
6987
  */
5920
6988
  const POINT_TYPES = [
5921
6989
  "node",
@@ -5937,7 +7005,7 @@ const DRAWING_EXT_URI = OOXML_NS.dsp;
5937
7005
  * The node → drawn-shape index of one diagram: the `presOf` edges of the data model and the
5938
7006
  * `dsp:sp` elements of the drawing part, both keyed for lookup.
5939
7007
  *
5940
- * Built once per {@link Diagram}, and only when a point is actually asked what draws it — the
7008
+ * Built once per {@link Diagram}, and only when a point is actually asked what draws it. The
5941
7009
  * drawing part is a second XML DOM and a consumer reading node text should not pay to parse
5942
7010
  * it. The consequence of that memo: a consumer who *adds* or *removes* shapes in the drawing
5943
7011
  * part through the escape hatches must re-reach the diagram to see them here.
@@ -5980,8 +7048,8 @@ var DrawingIndex = class {
5980
7048
  * Resolve one authored point to the `pres` point that presents it and to the drawn
5981
7049
  * paragraph carrying its text.
5982
7050
  *
5983
- * A point commonly has **several** `presOf` arms — an org-chart node's box and the
5984
- * connector beneath it are two presentations of one node — so the arm is chosen by what it
7051
+ * A point commonly has **several** `presOf` arms (an org-chart node's box and the
7052
+ * connector beneath it are two presentations of one node), so the arm is chosen by what it
5985
7053
  * reaches rather than by its `@srcOrd`: the first, in `@srcOrd` order, whose `pres` point
5986
7054
  * draws a `dsp:sp` that has a `dsp:txBody`. That selector produced a unique hit for all 90
5987
7055
  * authored points measured across five layout families, and it degrades to "no arm" rather
@@ -6052,7 +7120,7 @@ var DiagramPoint = class {
6052
7120
  return idAttr(this.pt, "cxnId");
6053
7121
  }
6054
7122
  /**
6055
- * Whether this point is an unfilled prompt (`dgm:prSet/@phldr`) — a node PowerPoint draws
7123
+ * Whether this point is an unfilled prompt (`dgm:prSet/@phldr`): a node PowerPoint draws
6056
7124
  * with a grey placeholder string and treats as empty. Excluded from {@link Diagram.text}:
6057
7125
  * a layout that stores its prompt in `dgm:t` would otherwise extract as content.
6058
7126
  */
@@ -6071,7 +7139,7 @@ var DiagramPoint = class {
6071
7139
  * `resolved*` getters all apply.
6072
7140
  *
6073
7141
  * **Writing through this edits the data model only**, and leaves the drawing cache
6074
- * holding the old string — which is what PowerPoint-less renderers draw. It is the escape
7142
+ * holding the old string, which is what PowerPoint-less renderers draw. It is the escape
6075
7143
  * hatch for run-level formatting, so it does exactly what it is told and nothing more;
6076
7144
  * {@link text}'s setter is the path that mirrors.
6077
7145
  */
@@ -6086,7 +7154,7 @@ var DiagramPoint = class {
6086
7154
  }
6087
7155
  /**
6088
7156
  * The generated `pres` point that presents this one (`dgm:cxn[@type="presOf"]/@destId`),
6089
- * or `null` when nothing presents it — the normal state of an unlabelled transition point.
7157
+ * or `null` when nothing presents it (the normal state of an unlabelled transition point).
6090
7158
  *
6091
7159
  * Where a point has several `presOf` arms the one named here is the arm that draws text;
6092
7160
  * see {@link drawnShape}, which resolves both.
@@ -6100,7 +7168,7 @@ var DiagramPoint = class {
6100
7168
  *
6101
7169
  * The mapping runs through the `pres` point rather than directly: a `dsp:sp/@modelId` is
6102
7170
  * always a `pres` point's id, so a lookup keyed on the authored point's own id finds
6103
- * nothing at all. It is many-to-one in both directions — one shape draws several points
7171
+ * nothing at all. It is many-to-one in both directions: one shape draws several points
6104
7172
  * (`@destOrd` says which paragraph is whose, and it can contradict document order), and
6105
7173
  * one point has several presentations, of which at most one holds text.
6106
7174
  *
@@ -6134,7 +7202,7 @@ var DiagramPoint = class {
6134
7202
  * Two things it deliberately does not do. It does **not** recompute geometry: the drawn
6135
7203
  * shape keeps its cached position and size, so a much longer string overflows its box in
6136
7204
  * those same renderers until PowerPoint re-lays the diagram out. And it does not *create*
6137
- * a place to draw text — a transition point whose layout has no label slot has no
7205
+ * a place to draw text. A transition point whose layout has no label slot has no
6138
7206
  * `dsp:sp`, and PowerPoint strips text put on one, so the honest outcome is the
6139
7207
  * diagnostic. Like every other `text` setter here it collapses the point to one run,
6140
7208
  * keeping the first run's `a:rPr`; use {@link textFrame} for run-level work.
@@ -6168,7 +7236,7 @@ var DiagramPoint = class {
6168
7236
  }
6169
7237
  };
6170
7238
  /**
6171
- * One user-authored point in its place in the diagram's tree — a `node` or an `asst`, never a
7239
+ * One user-authored point in its place in the diagram's tree: a `node` or an `asst`, never a
6172
7240
  * generated `pres` point or a transition label. See {@link Diagram.nodes}.
6173
7241
  */
6174
7242
  var DiagramNode = class {
@@ -6207,7 +7275,7 @@ var Diagram = class {
6207
7275
  return this.part.partName;
6208
7276
  }
6209
7277
  /**
6210
- * Every point (`dgm:ptLst/dgm:pt`) in document order, unfiltered — generated `pres`
7278
+ * Every point (`dgm:ptLst/dgm:pt`) in document order, unfiltered, with generated `pres`
6211
7279
  * points and the `doc` root included. Filter on {@link DiagramPoint.type} for the user's
6212
7280
  * nodes; {@link text} is the shortcut for the common case, {@link nodes} the structured one.
6213
7281
  *
@@ -6224,8 +7292,8 @@ var Diagram = class {
6224
7292
  return getElements(ptLst, "dgm:pt").map((pt) => new DiagramPoint(pt, this.part, this.themeContext, relationships, drawing));
6225
7293
  }
6226
7294
  /**
6227
- * The authored tree — every `node` and `asst` point in its place, roots in `@srcOrd`
6228
- * order — as a view over {@link points} and {@link connections} rather than a replacement
7295
+ * The authored tree (every `node` and `asst` point in its place, roots in `@srcOrd`
7296
+ * order) as a view over {@link points} and {@link connections} rather than a replacement
6229
7297
  * for either.
6230
7298
  *
6231
7299
  * A root is a content point whose `parOf` parent is not itself content, which in a
@@ -6286,10 +7354,12 @@ var Diagram = class {
6286
7354
  return tree;
6287
7355
  }
6288
7356
  /**
6289
- * The point with this `@modelId`, or `null` — the lookup every connection getter implies,
6290
- * since a `DiagramConnection` names its ends by id and nothing else resolves them.
7357
+ * The point with this `@modelId`, or `null`. This is the lookup every connection getter implies,
7358
+ * since a `DiagramConnection` names its ends by id and nothing else resolves them. Takes
7359
+ * `null`, as a connection end with no id reads, and misses on it.
6291
7360
  */
6292
7361
  point(modelId) {
7362
+ if (modelId === null) return null;
6293
7363
  return this.points.find((candidate) => candidate.modelId === modelId) ?? null;
6294
7364
  }
6295
7365
  /** Every edge (`dgm:cxnLst/dgm:cxn`) in document order. */
@@ -6313,7 +7383,7 @@ var Diagram = class {
6313
7383
  });
6314
7384
  }
6315
7385
  /**
6316
- * The diagram's text in document order, blocks joined by `\n` — the counterpart to
7386
+ * The diagram's text in document order, blocks joined by `\n`: the counterpart to
6317
7387
  * `TextFrame.text` for a construct that has no single text body.
6318
7388
  *
6319
7389
  * Contributing points are the ones a user can type into: everything except the generated
@@ -6385,7 +7455,7 @@ var Diagram = class {
6385
7455
  return new PackageReadError("diagram/parent-edge-cycle", `Diagram ${this.part.partName} has a parOf cycle reaching point ${modelId}, so its points do not form a tree`);
6386
7456
  }
6387
7457
  /**
6388
- * The `doc` point's `dgm:prSet`, which carries all three preset ids — resolved once
7458
+ * The `doc` point's `dgm:prSet`, which carries all three preset ids, resolved once
6389
7459
  * rather than once per getter, since each lookup is a scan of the whole `ptLst`.
6390
7460
  * `undefined` means "not looked up yet"; `null` means "looked up, not there".
6391
7461
  */
@@ -6401,7 +7471,7 @@ var Diagram = class {
6401
7471
  return prSet ? attr(prSet, name) : null;
6402
7472
  }
6403
7473
  /**
6404
- * The drawing index, built on first use and kept — see {@link DrawingIndex} for what that
7474
+ * The drawing index, built on first use and kept. See {@link DrawingIndex} for what that
6405
7475
  * memo costs a consumer who mutates the drawing part's shape tree by hand.
6406
7476
  */
6407
7477
  #drawingIndex() {
@@ -6421,15 +7491,14 @@ function idAttr(element, name) {
6421
7491
  const raw = attr(element, name);
6422
7492
  return raw === null || raw === "" || raw === "0" ? null : raw;
6423
7493
  }
6424
- //#endregion
6425
- //#region src/read/api/table-structure.ts
7494
+ // src/read/api/table-structure.ts
6426
7495
  /**
6427
7496
  * Structural edits to an existing `a:tbl`: adding and removing rows and columns, and
6428
7497
  * merging and unmerging cells.
6429
7498
  *
6430
7499
  * The one fact that makes this tractable is that a stored table's grid is already
6431
- * **rectangular**. Unlike the write path — which receives lopsided row arrays and has to
6432
- * build the merge grid itself (`gen/slide/objects/table.ts` STEP 3) — every `a:tr` in a
7500
+ * **rectangular**. Unlike the write path, which receives lopsided row arrays and has to
7501
+ * build the merge grid itself (`gen/slide/objects/table.ts` STEP 3), every `a:tr` in a
6433
7502
  * conformant `a:tbl` holds exactly one `a:tc` per grid column, with the covered half of each
6434
7503
  * span present and flagged. So "the cell at (r, c)" is simply the c-th `a:tc` of the r-th
6435
7504
  * `a:tr`, and an edit's whole job is keeping that rectangle true.
@@ -6463,7 +7532,7 @@ function rowsOf(tbl) {
6463
7532
  function cellsOf(tr) {
6464
7533
  return getElements(tr, "a:tc");
6465
7534
  }
6466
- /** The table's `a:tblGrid`, or a thrown error — `CT_Table` requires one. */
7535
+ /** The table's `a:tblGrid`, or a thrown error: `CT_Table` requires one. */
6467
7536
  function gridOf(tbl) {
6468
7537
  const grid = firstChild(tbl, "a:tblGrid");
6469
7538
  if (!grid) throw new InvalidOptionError("table/column-index-out-of-range", "This table has no a:tblGrid, so its columns cannot be edited");
@@ -6486,7 +7555,7 @@ function isVMerge(tc) {
6486
7555
  return boolAttr(tc, "vMerge") === true;
6487
7556
  }
6488
7557
  /**
6489
- * Set a span attribute, removing it when the span collapses to 1 — the schema default, and
7558
+ * Set a span attribute, removing it when the span collapses to 1: the schema default, and
6490
7559
  * what PowerPoint itself writes for an unmerged cell.
6491
7560
  */
6492
7561
  function setSpan(tc, name, value) {
@@ -6711,8 +7780,8 @@ function removeColumn(tbl, index) {
6711
7780
  /**
6712
7781
  * Merge the rectangle `(row1, col1)`–`(row2, col2)` into one cell.
6713
7782
  *
6714
- * The top-left cell becomes the origin; every other cell in the rectangle becomes a covered cell
6715
- * — flagged, given the spans PowerPoint repeats on a region's first row and column, and emptied,
7783
+ * The top-left cell becomes the origin; every other cell in the rectangle becomes a covered cell:
7784
+ * flagged, given the spans PowerPoint repeats on a region's first row and column, and emptied,
6716
7785
  * since a covered cell is never rendered.
6717
7786
  *
6718
7787
  * **The covered cells' text moves into the origin**, as PowerPoint's own Merge Cells does: each
@@ -6823,8 +7892,7 @@ function emptyCellText(tc) {
6823
7892
  for (const p of getElements(txBody, "a:p")) txBody.removeChild(p);
6824
7893
  txBody.appendChild(createElement(ownerDocumentOf(txBody), "a:p"));
6825
7894
  }
6826
- //#endregion
6827
- //#region src/read/api/table-style-resolve.ts
7895
+ // src/read/api/table-style-resolve.ts
6828
7896
  /**
6829
7897
  * Look up the `a:tblStyle` whose `@styleId` equals `styleId` in the deck's single
6830
7898
  * `tableStyles.xml`, or `null` when there is no such part or no matching style
@@ -6884,7 +7952,7 @@ function cellStyleParts(flags, row, col, rowCount, colCount) {
6884
7952
  /**
6885
7953
  * The fill a single `a:tcStyle` contributes. `fall` means the part defines no fill
6886
7954
  * (only borders, or nothing) so resolution continues to the next-lower part;
6887
- * `stop` means the part settles the fill — either a resolved solid colour or `null`
7955
+ * `stop` means the part settles the fill, with either a resolved solid colour or `null`
6888
7956
  * for an explicit `a:noFill` / a non-solid style fill (a gradient has no single hex).
6889
7957
  */
6890
7958
  function tcStyleFill(tcStyle, ctx) {
@@ -6980,29 +8048,28 @@ function resolveTableCellTextStyle(style, flags, row, col, rowCount, colCount, c
6980
8048
  }
6981
8049
  return out;
6982
8050
  }
6983
- //#endregion
6984
- //#region src/read/api/table-edit.ts
8051
+ // src/read/api/table-edit.ts
6985
8052
  /**
6986
8053
  * Ordered mutation of a table cell's `a:tcPr`, and the cell-scoped schema facts every such
6987
8054
  * edit needs.
6988
8055
  *
6989
8056
  * `CT_TableCellProperties` declares its children as a **sequence**, so an append-only
6990
- * implementation produces an out-of-order `a:tcPr` — which PowerPoint reports as a corrupt
8057
+ * implementation produces an out-of-order `a:tcPr`, which PowerPoint reports as a corrupt
6991
8058
  * file rather than as a bad edit. Every insertion therefore goes through
6992
- * {@link getOrAddChild} / {@link insertInOrder} with the new element's *successors*, which is
8059
+ * {@link insertInOrder} with the new element's *successors*, which is
6993
8060
  * what `TCPR_AFTER` tabulates; it and the sequence it derives from live in
6994
8061
  * `src/ooxml/sequence.ts` alongside the other complexType orderings.
6995
8062
  *
6996
8063
  * The attribute values are the other half. A value outside its `ST_` union is equally fatal
6997
- * and equally silent, so each is checked against the enum before it is written — the same
8064
+ * and equally silent, so each is checked against the enum before it is written: the same
6998
8065
  * validate-at-the-boundary rule the write path follows, with the difference that here there
6999
8066
  * is a caller to throw at rather than a deck to degrade (see `ooxml/check-enum.ts`).
7000
8067
  */
7001
- /** `ST_TextAnchoringType` — `a:tcPr/@anchor`. */
8068
+ /** `ST_TextAnchoringType`, for `a:tcPr/@anchor`. */
7002
8069
  const ANCHOR_VALUES = TEXT_ANCHORS;
7003
- /** `ST_TextVerticalType` — `a:tcPr/@vert`. */
8070
+ /** `ST_TextVerticalType`, for `a:tcPr/@vert`. */
7004
8071
  const VERT_VALUES = TEXT_VERTICAL;
7005
- /** `ST_TextHorzOverflowType` — `a:tcPr/@horzOverflow`. */
8072
+ /** `ST_TextHorzOverflowType`, for `a:tcPr/@horzOverflow`. */
7006
8073
  const HORZ_OVERFLOW_VALUES = TEXT_HORZ_OVERFLOW;
7007
8074
  /** The four edges plus the two diagonals, keyed as {@link TableCellEdge} names them. */
7008
8075
  const EDGE_QNAMES = {
@@ -7022,8 +8089,7 @@ const EDGE_QNAMES = {
7022
8089
  function insertTcPrChild(tcPr, qname, node) {
7023
8090
  insertInOrder(tcPr, node, TCPR_AFTER[qname] ?? []);
7024
8091
  }
7025
- //#endregion
7026
- //#region src/read/api/table-cell.ts
8092
+ // src/read/api/table-cell.ts
7027
8093
  /** One table cell (`a:tc`). */
7028
8094
  var TableCell = class {
7029
8095
  tc;
@@ -7114,7 +8180,7 @@ var TableCell = class {
7114
8180
  * The cell's `a:tcPr`, creating it when absent.
7115
8181
  *
7116
8182
  * `CT_TableCell` sequences `a:txBody` then `a:tcPr` then `a:extLst`, so a newly created
7117
- * one is inserted before `a:extLst` — appending it blindly would put it after and make
8183
+ * one is inserted before `a:extLst`; appending it blindly would put it after and make
7118
8184
  * the part invalid. Every setter below goes through here for that reason.
7119
8185
  */
7120
8186
  #getOrAddTcPr() {
@@ -7137,7 +8203,7 @@ var TableCell = class {
7137
8203
  this.#setTcPrAttr("vert", value, VERT_VALUES, "table/invalid-cell-vert");
7138
8204
  }
7139
8205
  /**
7140
- * Set (or clear) `a:tcPr/@horzOverflow` — what a single glyph too wide for the cell does.
8206
+ * Set (or clear) `a:tcPr/@horzOverflow`: what a single glyph too wide for the cell does.
7141
8207
  * `null` removes the attribute; so does `'clip'` in effect, since it is the schema
7142
8208
  * default, but the attribute is still written as asked.
7143
8209
  * @throws {InvalidOptionError} when the value is outside `ST_TextHorzOverflowType`
@@ -7161,7 +8227,7 @@ var TableCell = class {
7161
8227
  *
7162
8228
  * Partial: only the sides named are touched, so `{ left: 0 }` flushes the text left and
7163
8229
  * leaves the other three alone. A side given as `null` has its attribute removed, which
7164
- * returns it to the schema default (91440 EMU left/right, 45720 top/bottom) — not to
8230
+ * returns it to the schema default (91440 EMU left/right, 45720 top/bottom), not to
7165
8231
  * zero. Pass `{}` to change nothing.
7166
8232
  * @throws {InvalidOptionError} when a value is not a finite number
7167
8233
  */
@@ -7181,7 +8247,7 @@ var TableCell = class {
7181
8247
  this.part.markDirty();
7182
8248
  }
7183
8249
  /**
7184
- * Set (or clear) one of the cell's six borders — the four edges and the two diagonals.
8250
+ * Set (or clear) one of the cell's six borders: the four edges and the two diagonals.
7185
8251
  *
7186
8252
  * `null` removes the element entirely, which is "inherit", and is a different thing from
7187
8253
  * `{ noFill: true }`, which writes an explicit no-border and suppresses whatever would
@@ -7234,7 +8300,7 @@ var TableCell = class {
7234
8300
  * `null` removes the `a:solidFill` and lets the cell inherit from the table style again.
7235
8301
  * That is not the same as {@link noFill}, which writes an explicit `a:noFill` and so
7236
8302
  * suppresses the inherited shading. Any competing fill choice (`a:gradFill`,
7237
- * `a:blipFill`, …) is dropped first — `EG_FillProperties` admits one.
8303
+ * `a:blipFill`, …) is dropped first, since `EG_FillProperties` admits one.
7238
8304
  * @throws {InvalidOptionError} when `color` is not a 6-digit hex string
7239
8305
  */
7240
8306
  setFillColor(color) {
@@ -7249,7 +8315,7 @@ var TableCell = class {
7249
8315
  this.#applyFill(token === null ? null : { scheme: token });
7250
8316
  }
7251
8317
  /**
7252
- * Write an explicit `<a:noFill/>` on the cell — a transparent cell that shows the table
8318
+ * Write an explicit `<a:noFill/>` on the cell: a transparent cell that shows the table
7253
8319
  * background (or the slide) through. Distinct from `setFillColor(null)`, which removes
7254
8320
  * the fill and lets the table style's shading apply again.
7255
8321
  */
@@ -7275,23 +8341,23 @@ var TableCell = class {
7275
8341
  }
7276
8342
  /**
7277
8343
  * The cell's solid fill resolved against the table's theme colour context to a
7278
- * literal hex — the table-cell counterpart of
8344
+ * literal hex. It is the table-cell counterpart of
7279
8345
  * {@link AutoShape.resolvedFill}. The cell's own
7280
8346
  * `a:tcPr/a:solidFill` wins; when the cell defines none, this falls back to the
7281
8347
  * table **style** graph (the `firstRow`/banded/`wholeTbl` shading the
7282
- * `a:tableStyleId` supplies — see {@link Table.resolvedStyle}), so a styled cell
8348
+ * `a:tableStyleId` supplies; see {@link Table.resolvedStyle}), so a styled cell
7283
8349
  * with an empty `a:tcPr` reports the colour PowerPoint actually renders rather
7284
8350
  * than `null`.
7285
8351
  *
7286
8352
  * A cell that carries *some other* fill choice (`a:blipFill`/`a:gradFill`/
7287
8353
  * `a:pattFill`/`a:noFill`) overrides the style graph in PowerPoint, so this
7288
- * reports `null` for one rather than falling through to the inherited shading —
8354
+ * reports `null` for one rather than falling through to the inherited shading,
7289
8355
  * the same guard {@link AutoShape.resolvedFill} applies to
7290
8356
  * the style matrix. Read {@link pictureFill} for an image-filled cell. Also
7291
8357
  * `null` when neither source yields a solid colour (an
7292
8358
  * unmapped token, an explicit style `a:noFill`). The returned
7293
8359
  * {@link ResolvedColor} carries `effectiveHex` (the base colour with its
7294
- * `lumMod`/`lumOff`/… transforms applied) — read that for the final colour.
8360
+ * `lumMod`/`lumOff`/… transforms applied); read that for the final colour.
7295
8361
  */
7296
8362
  get resolvedFill() {
7297
8363
  if (this.hasOwnFill) return resolveSolidFillColor(this.#tcPr(), this.themeContext);
@@ -7302,7 +8368,7 @@ var TableCell = class {
7302
8368
  * child), as opposed to inheriting one from the table style's header/banding rules.
7303
8369
  *
7304
8370
  * This is the flag that disambiguates {@link resolvedFill}, which deliberately reports
7305
- * either — it answers "what colour does this cell render as", and both sources are valid
8371
+ * either: it answers "what colour does this cell render as", and both sources are valid
7306
8372
  * answers to that. Anything that has to reproduce the cell rather than describe it needs
7307
8373
  * to know which: baking an inherited banding colour into a copy makes the copy look right
7308
8374
  * until someone changes its table style, and then nothing moves. `false` for a cell whose
@@ -7312,20 +8378,20 @@ var TableCell = class {
7312
8378
  return hasFillChoice(this.#tcPr());
7313
8379
  }
7314
8380
  /**
7315
- * `true` when the cell sets an explicit no-fill (`a:tcPr/a:noFill`) — a deliberately
8381
+ * `true` when the cell sets an explicit no-fill (`a:tcPr/a:noFill`): a deliberately
7316
8382
  * transparent cell showing the table background (or the slide) through. The cell-side
7317
8383
  * counterpart of {@link AutoShape.fillNoFill}, and what
7318
8384
  * {@link noFill} writes.
7319
8385
  *
7320
8386
  * {@link hasOwnFill} is not this question: it is `true` for *any* `EG_FillProperties`
7321
8387
  * child, so on its own it cannot separate a suppressed fill from a gradient or an image
7322
- * one — and every colour accessor ({@link resolvedFill}, {@link fillSchemeColor})
8388
+ * one, and every colour accessor ({@link resolvedFill}, {@link fillSchemeColor})
7323
8389
  * reports `null` for a no-fill cell exactly as it does for a
7324
8390
  * cell that inherits its shading from the table style. Deriving it as "has a fill of its
7325
8391
  * own, and no accessor recognises it" instead of reading it has two failure modes: it
7326
8392
  * folds `a:grpFill` in with `a:noFill`, and its meaning changes silently the day a
7327
- * further fill kind gets an accessor. The two paint completely differently — an
7328
- * inherited banding colour versus nothing at all — so a consumer that cannot tell them
8393
+ * further fill kind gets an accessor. The two paint completely differently (an
8394
+ * inherited banding colour versus nothing at all), so a consumer that cannot tell them
7329
8395
  * apart paints a transparent cell in the style's shading.
7330
8396
  */
7331
8397
  get fillNoFill() {
@@ -7383,7 +8449,7 @@ var TableCell = class {
7383
8449
  * `null` when the cell defines none. Each present edge decodes to a
7384
8450
  * {@link CellBorder} (width / dash / resolved colour + its raw token and transform
7385
8451
  * list / suppressed flag);
7386
- * absent edges are `null`. Cell borders are the biggest visible table gap — a
8452
+ * absent edges are `null`. Cell borders are the biggest visible table gap: a
7387
8453
  * replica built only from geometry and fill draws every cell edge-to-edge with
7388
8454
  * no rule, so this surfaces the per-side stroke the writer's `border` option emits.
7389
8455
  */
@@ -7393,7 +8459,10 @@ var TableCell = class {
7393
8459
  const decode = (qname) => {
7394
8460
  const ln = firstChild(tcPr, qname);
7395
8461
  if (!ln) return null;
7396
- return readLineBasics(ln, this.themeContext);
8462
+ return {
8463
+ ...readLineBasics(ln, this.themeContext),
8464
+ cap: attr(ln, "cap") ?? null
8465
+ };
7397
8466
  };
7398
8467
  const borders = {
7399
8468
  left: decode("a:lnL"),
@@ -7423,7 +8492,7 @@ var TableCell = class {
7423
8492
  }
7424
8493
  /**
7425
8494
  * Whether the cell's whole text **block** is centred horizontally within it
7426
- * (`a:tcPr/@anchorCtr`). `false` when unset — that is the schema default, and it is
8495
+ * (`a:tcPr/@anchorCtr`). `false` when unset; that is the schema default, and it is
7427
8496
  * what PowerPoint writes nothing for.
7428
8497
  *
7429
8498
  * Distinct from a paragraph's `align`, which decides where each *line* sits inside the
@@ -7467,12 +8536,12 @@ var TableCell = class {
7467
8536
  *
7468
8537
  * This is what {@link headerIds} references: a data cell names the header cells that
7469
8538
  * govern it by their `@id`, which is how a complex table tells a screen reader what a
7470
- * value means. Unrelated to the shape-level `p:cNvPr/@id` — it is an `xs:string` scoped
8539
+ * value means. Unrelated to the shape-level `p:cNvPr/@id`: it is an `xs:string` scoped
7471
8540
  * to the table, not a slide-wide numeric id.
7472
8541
  *
7473
8542
  * **PowerPoint does not write this and strips it on save**, so it will read `null` on
7474
8543
  * any deck PowerPoint has saved, however it was produced. It is surfaced for decks from
7475
- * other producers. There is deliberately no write-API counterpart — see
8544
+ * other producers. There is deliberately no write-API counterpart; see
7476
8545
  * `test/read/fixtures/authoring/probe-table-cell-a11y-and-3d.ps1` for the measurement.
7477
8546
  */
7478
8547
  get id() {
@@ -7481,7 +8550,7 @@ var TableCell = class {
7481
8550
  /**
7482
8551
  * The `@id`s of the header cells that govern this cell
7483
8552
  * (`a:tcPr/a:headers/a:header/@val`), in document order. Empty when the cell declares no
7484
- * header association — which is the common case, since only a complex table needs one.
8553
+ * header association, which is the common case, since only a complex table needs one.
7485
8554
  *
7486
8555
  * Carries the same caveat as {@link id}: PowerPoint strips `a:headers` on save, so this
7487
8556
  * is empty on any PowerPoint-saved deck. `Table.firstRowHeader` (`a:tblPr/@firstRow`) is
@@ -7496,7 +8565,7 @@ var TableCell = class {
7496
8565
  /**
7497
8566
  * How the cell treats a glyph too wide for its text width (`a:tcPr/@horzOverflow`):
7498
8567
  * `'clip'` cuts it at the cell edge, `'overflow'` lets it draw past. `null` when unset
7499
- * (PowerPoint clips). Not a wrap flag — cell text always wraps to the column width.
8568
+ * (PowerPoint clips). Not a wrap flag: cell text always wraps to the column width.
7500
8569
  */
7501
8570
  get horzOverflow() {
7502
8571
  const tcPr = this.#tcPr();
@@ -7542,8 +8611,7 @@ var TableCell = class {
7542
8611
  this.part.markDirty();
7543
8612
  }
7544
8613
  };
7545
- //#endregion
7546
- //#region src/read/api/table-row.ts
8614
+ // src/read/api/table-row.ts
7547
8615
  /** One table row (`a:tr`). */
7548
8616
  var TableRow = class {
7549
8617
  tr;
@@ -7577,8 +8645,7 @@ var TableRow = class {
7577
8645
  this.part.markDirty();
7578
8646
  }
7579
8647
  };
7580
- //#endregion
7581
- //#region src/read/api/table.ts
8648
+ // src/read/api/table.ts
7582
8649
  /** A table: a grid of rows and cells inside a graphic frame. */
7583
8650
  var Table = class {
7584
8651
  tbl;
@@ -7603,7 +8670,7 @@ var Table = class {
7603
8670
  * from the deck's `tableStyles.xml`, or `null` when the table names no
7604
8671
  * `a:tableStyleId`, the deck has no `tableStyles.xml`, or the id is a built-in
7605
8672
  * `[MS-OE376]` style the deck does not materialise. This is what supplies the
7606
- * banded-row / header shading a cell with no own fill inherits — read the
8673
+ * banded-row / header shading a cell with no own fill inherits. Read the
7607
8674
  * resolved per-cell colour off {@link TableCell.resolvedFill}.
7608
8675
  *
7609
8676
  * Resolved afresh on every read, not through the per-instance memo the cells share, so an
@@ -7671,7 +8738,7 @@ var Table = class {
7671
8738
  * `{5940675A-B579-460E-94D1-54222C63F5DA}`, or `null` when the table carries no
7672
8739
  * `a:tableStyleId`. This is the reference into `ppt/tableStyles.xml` (or a
7673
8740
  * built-in `[MS-OE376]` style) that supplies the banded-row / header shading the
7674
- * `firstRow`/`bandRow` flags activate — without it a replica loses the whole
8741
+ * `firstRow`/`bandRow` flags activate. Without it a replica loses the whole
7675
8742
  * table style, so it is the read counterpart of the writer's `tableStyle` option.
7676
8743
  */
7677
8744
  get styleId() {
@@ -7686,7 +8753,7 @@ var Table = class {
7686
8753
  * This is the table-level counterpart of {@link TableCell.resolvedFill}, and it is a
7687
8754
  * genuinely different thing from a cell fill: a `a:tblPr` fill sits *behind* the grid, so
7688
8755
  * a cell with no fill of its own shows it through. Reports `null` for a non-solid choice
7689
- * (`a:blipFill`/`a:gradFill`/`a:pattFill`/`a:noFill`) — read {@link pictureFill} for an
8756
+ * (`a:blipFill`/`a:gradFill`/`a:pattFill`/`a:noFill`); read {@link pictureFill} for an
7690
8757
  * image background.
7691
8758
  */
7692
8759
  get resolvedFill() {
@@ -7764,6 +8831,13 @@ var Table = class {
7764
8831
  return this.#tblPrFlag("lastCol");
7765
8832
  }
7766
8833
  /**
8834
+ * Whether the table reads right to left (`a:tblPr/@rtl`), from `addTable`'s `rtl`: its columns
8835
+ * run from the right edge, so column 0 is the rightmost.
8836
+ */
8837
+ get rtl() {
8838
+ return this.#tblPrFlag("rtl");
8839
+ }
8840
+ /**
7767
8841
  * The cell at `(rowIndex, columnIndex)` (both zero-based), or `null` when out
7768
8842
  * of range. Column index counts `a:tc` elements in the row, so a cell that
7769
8843
  * spans columns (`gridSpan`) occupies a single index here.
@@ -7793,7 +8867,7 @@ var Table = class {
7793
8867
  * Remove the row at `index`, with its content.
7794
8868
  *
7795
8869
  * A cell in the row that *continues* a vertical merge shortens that merge. A cell that
7796
- * *starts* one hands the region to its first continuation, which becomes the new origin —
8870
+ * *starts* one hands the region to its first continuation, which becomes the new origin:
7797
8871
  * the merged region survives one row shorter, and only the removed row's own text is lost.
7798
8872
  * @throws {InvalidOptionError} when `index` is out of range
7799
8873
  */
@@ -7832,7 +8906,7 @@ var Table = class {
7832
8906
  * never rendered.
7833
8907
  *
7834
8908
  * A rectangle that cuts through an existing merge is **rejected**, not silently widened
7835
- * to fit — the caller asked for a specific region, and quietly producing a different one
8909
+ * to fit. The caller asked for a specific region, and quietly producing a different one
7836
8910
  * is how a layout ends up wrong with nothing to point at. Unmerge first.
7837
8911
  * @throws {InvalidOptionError} when an index is out of range, the range covers one cell,
7838
8912
  * or it partially overlaps an existing merge
@@ -7864,10 +8938,9 @@ var Table = class {
7864
8938
  return tblPr ? boolAttr(tblPr, name) === true : false;
7865
8939
  }
7866
8940
  };
7867
- //#endregion
7868
- //#region src/read/api/shapes/graphic-frame.ts
8941
+ // src/read/api/shapes/graphic-frame.ts
7869
8942
  /**
7870
- * A graphic frame (`p:graphicFrame`) — the host element for a table, a chart, a chartEx
8943
+ * A graphic frame (`p:graphicFrame`): the host element for a table, a chart, a chartEx
7871
8944
  * chart, or a SmartArt diagram.
7872
8945
  *
7873
8946
  * Which one it holds is told by the `a:graphicData/@uri`, so the accessors below each check
@@ -7876,7 +8949,7 @@ var Table = class {
7876
8949
  * without it a consumer cannot tell "this frame holds content I cannot reach" from "this
7877
8950
  * frame holds a chart with nothing in it", since every predicate answers `false` for both.
7878
8951
  */
7879
- /** A graphic frame (`p:graphicFrame`) — host for tables, charts and SmartArt diagrams. */
8952
+ /** A graphic frame (`p:graphicFrame`), the host for tables, charts and SmartArt diagrams. */
7880
8953
  var GraphicFrame = class extends Shape {
7881
8954
  shapeType = "graphicFrame";
7882
8955
  xfrm() {
@@ -7911,7 +8984,7 @@ var GraphicFrame = class extends Shape {
7911
8984
  return this.#graphicDataUri() === OOXML_NS.dgm;
7912
8985
  }
7913
8986
  /**
7914
- * The `a:graphicData/@uri` verbatim — the namespace naming what this frame hosts, or
8987
+ * The `a:graphicData/@uri` verbatim: the namespace naming what this frame hosts, or
7915
8988
  * `null` when the frame carries no `a:graphicData`.
7916
8989
  *
7917
8990
  * Every `has*` predicate is a comparison against this, so a frame whose URI matches none
@@ -7938,7 +9011,7 @@ var GraphicFrame = class extends Shape {
7938
9011
  const relId = chartRef && attr(chartRef, "r:id");
7939
9012
  if (!relId) return null;
7940
9013
  const part = relPart(this.host.opc, this.host.relationships, relId);
7941
- return part ? new Chart(part) : null;
9014
+ return part ? new Chart(part, this.host.opc.relationshipsFor(part.partName)) : null;
7942
9015
  }
7943
9016
  /**
7944
9017
  * The hosted chartEx chart (waterfall/funnel/treemap/…), or `null` when this
@@ -7978,25 +9051,24 @@ var GraphicFrame = class extends Shape {
7978
9051
  return graphicData ? attr(graphicData, "uri") : null;
7979
9052
  }
7980
9053
  };
7981
- //#endregion
7982
- //#region src/read/api/shapes.ts
9054
+ // src/read/api/shapes.ts
7983
9055
  /**
7984
9056
  * Read-model proxies for the shapes in a `p:spTree`.
7985
9057
  *
7986
9058
  * The `p:spTree` holds five shape kinds; each wraps its element and exposes
7987
9059
  * non-visual identity (id/name), geometry (left/top/width/height in EMU), and
7988
9060
  * kind-specific reads. Proxies hold a back-reference to the owning
7989
- * {@link ShapeHost} — the slide, layout, or master whose part carries the tree —
9061
+ * {@link ShapeHost} (the slide, layout, or master whose part carries the tree),
7990
9062
  * so pictures can resolve their image relationship and so edits can mark that
7991
9063
  * part dirty.
7992
9064
  *
7993
9065
  * This module is the entry point for that model: the `Shape` base and the four leaf kinds live in
7994
- * `./shapes/`, and only the dispatch that turns an element into a proxy — plus `GroupShape`, which
7995
- * recurses back through it — is here. Everything the read API surfaces is re-exported below, so
9066
+ * `./shapes/`, and only the dispatch that turns an element into a proxy (plus `GroupShape`, which
9067
+ * recurses back through it) is here. Everything the read API surfaces is re-exported below, so
7996
9068
  * `pptx-ts/read` and its consumers keep importing shapes from one place.
7997
9069
  */
7998
9070
  /**
7999
- * A group shape (`p:grpSp`) — contains nested shapes.
9071
+ * A group shape (`p:grpSp`), which contains nested shapes.
8000
9072
  *
8001
9073
  * It is the one kind defined here rather than under `./shapes/`: its `shapes` getter recurses
8002
9074
  * through `buildShapes` below, so putting it in a sibling module would make the dispatch and the
@@ -8030,7 +9102,7 @@ var GroupShape = class extends Shape {
8030
9102
  * element is incomplete.
8031
9103
  *
8032
9104
  * Most consumers should not need this: {@link Shape.absoluteFrame} already
8033
- * composes the whole ancestor chain — child space, group flips and rotations —
9105
+ * composes the whole ancestor chain (child space, group flips and rotations),
8034
9106
  * and doing that arithmetic per consumer is how a shape in a nested group ends
8035
9107
  * up subtly displaced. It is here for a *replica* consumer rather than a
8036
9108
  * *paint* one: rebuilding this group and its children as OOXML needs the
@@ -8053,7 +9125,7 @@ var GroupShape = class extends Shape {
8053
9125
  return buildShapes(this.element, this.host);
8054
9126
  }
8055
9127
  };
8056
- /** Narrow a shape to an {@link AutoShape} (`p:sp` — auto shape, text box, or placeholder). */
9128
+ /** Narrow a shape to an {@link AutoShape} (`p:sp`: auto shape, text box, or placeholder). */
8057
9129
  function isAutoShape(shape) {
8058
9130
  return shape.shapeType === "autoShape";
8059
9131
  }
@@ -8065,7 +9137,7 @@ function isPicture(shape) {
8065
9137
  function isConnector(shape) {
8066
9138
  return shape.shapeType === "connector";
8067
9139
  }
8068
- /** Narrow a shape to a {@link GraphicFrame} (`p:graphicFrame` — table/chart host). */
9140
+ /** Narrow a shape to a {@link GraphicFrame} (`p:graphicFrame`, the table/chart host). */
8069
9141
  function isGraphicFrame(shape) {
8070
9142
  return shape.shapeType === "graphicFrame";
8071
9143
  }
@@ -8093,7 +9165,7 @@ function wrapShapeElement(element, host) {
8093
9165
  * The richer of an `mc:AlternateContent`'s two branches: the first `mc:Choice`
8094
9166
  * that carries a shape element (what PowerPoint renders when it understands the
8095
9167
  * required feature), falling back to `mc:Fallback`. PowerPoint wraps a shape this
8096
- * way when its full form needs a feature namespace a plain consumer lacks — a
9168
+ * way when its full form needs a feature namespace a plain consumer lacks: a
8097
9169
  * chartEx chart's `p:graphicFrame`, a zoom frame, an inline-math shape. Returns
8098
9170
  * the wrapped inner shape, or `null` when neither branch holds one.
8099
9171
  */
@@ -8130,11 +9202,13 @@ function buildShapes(parent, host) {
8130
9202
  /**
8131
9203
  * The first shape in `shapes` with the given drawing id (`p:cNvPr/@id`), walked
8132
9204
  * pre-order so a group is visited before its children. Backs
8133
- * {@link ShapeHost.shapeByIdDeep} on all three hosts — drawing ids are unique
9205
+ * {@link ShapeHost.shapeByIdDeep} on all three hosts. Drawing ids are unique
8134
9206
  * within a part, so the first match is the only match and the walk order just
8135
- * fixes a deterministic one.
9207
+ * fixes a deterministic one. A `null` id, as a shape's `id` reads when it has
9208
+ * no `@id`, is a miss, not a match on the first shape that lacks one too.
8136
9209
  */
8137
9210
  function findShapeByIdDeep(shapes, id) {
9211
+ if (id === null) return void 0;
8138
9212
  for (const shape of shapes) {
8139
9213
  if (shape.id === id) return shape;
8140
9214
  if (shape instanceof GroupShape) {
@@ -8143,7 +9217,6 @@ function findShapeByIdDeep(shapes, id) {
8143
9217
  }
8144
9218
  }
8145
9219
  }
8146
- //#endregion
8147
- export { placeholderInheritedColor as $, relsPartNameFor as $t, flattenAnimations as A, CSLD_AFTER_BG as At, readColorRef as B, SLIDE_AFTER_TRANSITION as Bt, ChartSeries as C, styleRefFill as Ct, Shape as D, presetColorHex as Dt, AutoShape as E, themeElementsOfRoot as Et, Paragraph as F, PRESENTATION_AFTER_SLD_ID_LST as Ft, resolveSlideColorContext as G, SPPR_LN_AFTER as Gt, resolveLayoutColorContext as H, SPPR_AFTER_XFRM as Ht, Run as I, PRESENTATION_AFTER_SLD_MASTER_ID_LST as It, checkPositiveEmu as J, TXBODY_AFTER_BODYPR as Jt, resolveSlideThemeParts as K, SPPR_SCENE3D_AFTER as Kt, readPatternFill as L, RPR_FILL_AFTER as Lt, pruneSpids as M, LN_SLOTS as Mt, remapSpids as N, PRESENTATION_AFTER_EMBEDDED_FONT_LST as Nt, carryShapeAnimations as O, hasFillChoice as Ot, TextFrame as P, PRESENTATION_AFTER_NOTES_MASTER_ID_LST as Pt, placeholderInheritedAnchor as Q, relativePartName as Qt, readPictureFill as R, RUN_AFTER_RPR as Rt, ChartAxis as S, resolveSchemeToken as St, Picture as T, substitutePhClr as Tt, resolveMasterColorContext as U, SPPR_EFFECT_AFTER as Ut, resolveColorElement as V, SLIDE_MASTER_AFTER_SLD_LAYOUT_ID_LST as Vt, resolveNotesColorContext as W, SPPR_FILL_AFTER as Wt, RUN_PROP_NAMES as X, partNameExtension as Xt, LST_STYLE_LEVELS as Y, TXBODY_AFTER_LSTSTYLE as Yt, lstStyleLevelDefRPr as Z, partNameToZipPath as Zt, DiagramPoint as _, parseXml as _n, isA as _t, isConnector as a, boolAttr as an, slideDefinesProp as at, ChartExSeries as b, serializeXml as bn, replaceColorElement as bt, isPicture as c, descendantsByTag as cn, carriedDecorations as ct, Table as d, getElements as dn, nextDrawingId as dt, resolveRelativePartName as en, placeholderInheritedListStyles as et, TableRow as f, getOrAddChild as fn, nthShapeChild as ft, DiagramNode as g, ownerDocumentOf as gn, fmtEntry as gt, Diagram as h, numberValue as hn, spTreeOf as ht, isAutoShape as i, attr as in, slideDefinesColor as it, hasAnimations as j, EMBEDDED_FONT_ENTRY_AFTER as jt, enumerateSpids as k, applyColorTransforms as kt, wrapShapeElement as l, firstChild as ln, collectElements as lt, resolveTableCellTextStyle as m, numberAttr as mn, reassignDrawingIds as mt, buildShapes as n, singleRelPart as nn, placeholderInheritedXfrm as nt, isGraphicFrame as o, childElements as on, cSldName as ot, TableCell as p, insertInOrder as pn, nvPrOf as pt, checkFiniteEmu as q, SPPR_SP3D_AFTER as qt, findShapeByIdDeep as r, zipPathToPartName as rn, placeholderOf as rt, isGroupShape as s, createElement as sn, cSldOf as st, GroupShape as t, resolveSingleRel as tn, placeholderInheritedRunProps as tt, GraphicFrame as u, firstChildElement as un, firstShapeChild as ut, ChartEx as v, removeAttr as vn, parseClrMap as vt, Connector as w, styleRefLine as wt, Chart as x, setAttr as xn, resolveColor as xt, ChartExAxis as y, removeChildrenByQName as yn, parseClrScheme as yt, readGradientFill as z, SHAPE_AFTER_SPPR as zt };
9220
+ export { LST_STYLE_LEVELS as $, TXBODY_AFTER_LSTSTYLE as $t, enumerateSpids as A, themeElementsOfRoot as At, composeGroupFrame as B, PRESENTATION_AFTER_SLD_MASTER_ID_LST as Bt, ChartPlotGroup as C, removeChildrenByQName as Cn, parseClrScheme as Ct, AutoShape as D, styleRefFill as Dt, Picture as E, resolveSchemeToken as Et, remapSpids as F, EMBEDDED_FONT_ENTRY_AFTER as Ft, resolveColorElement as G, SLIDE_MASTER_AFTER_SLD_LAYOUT_ID_LST as Gt, readGradientFill as H, RUN_AFTER_RPR as Ht, TextFrame as I, LN_SLOTS as It, resolveNotesColorContext as J, SPPR_FILL_AFTER as Jt, resolveLayoutColorContext as K, SPPR_AFTER_XFRM as Kt, Paragraph as L, PRESENTATION_AFTER_EMBEDDED_FONT_LST as Lt, hasAnimations as M, hasFillChoice as Mt, pruneSpids as N, applyColorTransforms as Nt, Shape as O, styleRefLine as Ot, readBuildEffects as P, CSLD_AFTER_BG as Pt, checkPositiveEmu as Q, TXBODY_AFTER_BODYPR as Qt, Field as R, PRESENTATION_AFTER_NOTES_MASTER_ID_LST as Rt, ChartAxis as S, removeAttr as Sn, parseClrMap as St, Connector as T, setAttr as Tn, resolveColor as Tt, readPictureFill as U, SHAPE_AFTER_SPPR as Ut, readPatternFill as V, RPR_FILL_AFTER as Vt, readColorRef as W, SLIDE_AFTER_TRANSITION as Wt, resolveSlideThemeParts as X, SPPR_SCENE3D_AFTER as Xt, resolveSlideColorContext as Y, SPPR_LN_AFTER as Yt, checkFiniteEmu as Z, SPPR_SP3D_AFTER as Zt, DiagramPoint as _, insertInOrder as _n, nvPrOf as _t, isConnector as a, resolveSingleRel as an, placeholderInheritedRunProps as at, ChartExSeries as b, ownerDocumentOf as bn, fmtEntry as bt, isPicture as c, attr as cn, slideDefinesColor as ct, Table as d, createElement as dn, cSldOf as dt, partNameExtension as en, RUN_PROP_NAMES as et, TableRow as f, descendantsByTag as fn, carriedDecorations as ft, DiagramNode as g, getOrAddChild as gn, nthShapeChild as gt, Diagram as h, getElements as hn, nextDrawingId as ht, isAutoShape as i, resolveRelativePartName as in, placeholderInheritedListStyles as it, flattenAnimations as j, presetColorHex as jt, carryShapeAnimations as k, substitutePhClr as kt, wrapShapeElement as l, boolAttr as ln, slideDefinesProp as lt, resolveTableCellTextStyle as m, firstChildElement as mn, firstShapeChild as mt, buildShapes as n, relativePartName as nn, placeholderInheritedAnchor as nt, isGraphicFrame as o, singleRelPart as on, placeholderInheritedXfrm as ot, TableCell as p, firstChild as pn, collectElements as pt, resolveMasterColorContext as q, SPPR_EFFECT_AFTER as qt, findShapeByIdDeep as r, relsPartNameFor as rn, placeholderInheritedColor as rt, isGroupShape as s, zipPathToPartName as sn, placeholderOf as st, GroupShape as t, partNameToZipPath as tn, lstStyleLevelDefRPr as tt, GraphicFrame as u, childElements as un, cSldName as ut, ChartEx as v, numberAttr as vn, reassignDrawingIds as vt, ChartSeries as w, serializeXml as wn, replaceColorElement as wt, Chart as x, parseXml as xn, isA as xt, ChartExAxis as y, numberValue as yn, spTreeOf as yt, Run as z, PRESENTATION_AFTER_SLD_ID_LST as zt };
8148
9221
 
8149
- //# sourceMappingURL=shapes-re0KyQk_.js.map
9222
+ //# sourceMappingURL=shapes-CAPOjU0Q.js.map