@shbernal/ts-pptx 4.1.0 → 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 +11 -11
  2. package/dist/anim-presets-Qbgh8uut.js +55 -0
  3. package/dist/anim-presets-Qbgh8uut.js.map +1 -0
  4. package/dist/{autopage-D4kyVp11.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-kVHmjGc6.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-DwDbakcl.js → chart-metadata-CwiTTcJq.js} +576 -538
  15. package/dist/chart-metadata-CwiTTcJq.js.map +1 -0
  16. package/dist/{clip-DHDlnHUq.d.ts → clip-ISfCMtWA.d.ts} +6 -10
  17. package/dist/clip-ISfCMtWA.d.ts.map +1 -0
  18. package/dist/{notes-BcT571R4.js → comments-CKIZYRbZ.js} +517 -389
  19. package/dist/comments-CKIZYRbZ.js.map +1 -0
  20. package/dist/{diagnostics-DiMmBU8P.d.ts → diagnostics-MoA4MBq5.d.ts} +52 -50
  21. package/dist/diagnostics-MoA4MBq5.d.ts.map +1 -0
  22. package/dist/{embedded-fonts-CI_YGW2d.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-6hF9aTmT.d.ts → entry-compose-CiakSt-f.d.ts} +53 -74
  25. package/dist/entry-compose-CiakSt-f.d.ts.map +1 -0
  26. package/dist/{errors-Da2nXCJm.js → entry-errors-JMthgJ2g.js} +41 -25
  27. package/dist/entry-errors-JMthgJ2g.js.map +1 -0
  28. package/dist/{table-cell-margins-PDshga8T.js → enums-BP9wsb9Q.js} +42 -45
  29. package/dist/enums-BP9wsb9Q.js.map +1 -0
  30. package/dist/{enums-DQRpVOLh.d.ts → enums-PcXr3o3J.d.ts} +11 -12
  31. package/dist/{enums-DQRpVOLh.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-CcXbg__l.js → fit-Bpmzep6O.js} +60 -52
  36. package/dist/fit-Bpmzep6O.js.map +1 -0
  37. package/dist/{font-metrics-CBGmhLo0.d.ts → font-metrics-DTppw4t3.d.ts} +61 -37
  38. package/dist/font-metrics-DTppw4t3.d.ts.map +1 -0
  39. package/dist/{grid-9B2w5EaY.js → grid-DUIMxjfl.js} +23 -26
  40. package/dist/grid-DUIMxjfl.js.map +1 -0
  41. package/dist/{html-dom-DvJmuHt2.js → html-dom-LZCgntnJ.js} +42 -43
  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 +14 -16
  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-BgLPbwbL.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-KoEgVwII.d.ts → package-CANBWuSm.d.ts} +4 -12
  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-BA9_lJbY.d.ts → pres-BkEkUAEM.d.ts} +383 -230
  75. package/dist/pres-BkEkUAEM.d.ts.map +1 -0
  76. package/dist/{presentation-Cyl74rq2.js → presentation-4Ggq0_Xa.js} +408 -288
  77. package/dist/presentation-4Ggq0_Xa.js.map +1 -0
  78. package/dist/{presentation-types-B3VQH4Bz.d.ts → presentation-types-BgnZR-0a.d.ts} +1104 -457
  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 +2697 -777
  86. package/dist/script.js.map +1 -1
  87. package/dist/{shapes-BcyFdZpp.js → shapes-CAPOjU0Q.js} +1556 -487
  88. package/dist/shapes-CAPOjU0Q.js.map +1 -0
  89. package/dist/{slide-BqPtyKmb.d.ts → slide-BmT4kMoO.d.ts} +94 -86
  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-CVbvUfgh.js → table-dom-BYga4FYY.js} +3 -4
  94. package/dist/{table-dom-CVbvUfgh.js.map → table-dom-BYga4FYY.js.map} +1 -1
  95. package/dist/{text-BvFFMMFQ.js → text-CU08xJGs.js} +365 -318
  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-DFcUYjh3.js → units-internal-BR3a4cIi.js} +26 -29
  100. package/dist/units-internal-BR3a4cIi.js.map +1 -0
  101. package/dist/{zip-CYZ0F6BV.js → zip-7JTh61LX.js} +8 -10
  102. package/dist/zip-7JTh61LX.js.map +1 -0
  103. package/dist/{zip-CTll_dr0.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-BoNyOlPm.js → zoom-CSSeOwM8.js} +1153 -583
  108. package/dist/zoom-CSSeOwM8.js.map +1 -0
  109. package/package.json +20 -21
  110. package/skills/{ts-pptx-upstream → pptx-ts-upstream}/SKILL.md +27 -74
  111. package/dist/autopage-D4kyVp11.js.map +0 -1
  112. package/dist/binary-string-CKVAYI3M.js.map +0 -1
  113. package/dist/body-insets-kVHmjGc6.js.map +0 -1
  114. package/dist/clip-DHDlnHUq.d.ts.map +0 -1
  115. package/dist/diagnostics-DiMmBU8P.d.ts.map +0 -1
  116. package/dist/embedded-fonts-CI_YGW2d.d.ts.map +0 -1
  117. package/dist/entry-compose-6hF9aTmT.d.ts.map +0 -1
  118. package/dist/errors-Da2nXCJm.js.map +0 -1
  119. package/dist/fit-CcXbg__l.js.map +0 -1
  120. package/dist/font-metrics-CBGmhLo0.d.ts.map +0 -1
  121. package/dist/grid-9B2w5EaY.js.map +0 -1
  122. package/dist/html-dom-DvJmuHt2.js.map +0 -1
  123. package/dist/image-size-DwDbakcl.js.map +0 -1
  124. package/dist/notes-BcT571R4.js.map +0 -1
  125. package/dist/options-internal-WqgMf720.js.map +0 -1
  126. package/dist/package-KoEgVwII.d.ts.map +0 -1
  127. package/dist/playbtn-CUqQdoDf.js.map +0 -1
  128. package/dist/pres-BA9_lJbY.d.ts.map +0 -1
  129. package/dist/presentation-Cyl74rq2.js.map +0 -1
  130. package/dist/presentation-types-B3VQH4Bz.d.ts.map +0 -1
  131. package/dist/shapes-BcyFdZpp.js.map +0 -1
  132. package/dist/slide-BqPtyKmb.d.ts.map +0 -1
  133. package/dist/table-cell-margins-PDshga8T.js.map +0 -1
  134. package/dist/text-BvFFMMFQ.js.map +0 -1
  135. package/dist/text-anchor-BgLPbwbL.js.map +0 -1
  136. package/dist/units-internal-DFcUYjh3.js.map +0 -1
  137. package/dist/zip-CTll_dr0.d.ts.map +0 -1
  138. package/dist/zip-CYZ0F6BV.js.map +0 -1
  139. package/dist/zoom-BoNyOlPm.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import { n as InvalidOptionError, u as warnOnce } from "./errors-Da2nXCJm.js";
2
- import { w as PERCENT_SCALE } from "./table-cell-margins-PDshga8T.js";
3
- import { t as bytesToBinaryString } from "./binary-string-CKVAYI3M.js";
4
- //#region src/media/image-formats.ts
1
+ import { d as warnOnce, r as InvalidOptionError } from "./entry-errors-JMthgJ2g.js";
2
+ import { w as PERCENT_SCALE } from "./enums-BP9wsb9Q.js";
3
+ import { t as bytesToBinaryString } from "./binary-string-Ba8VM3b9.js";
4
+ // src/media/image-formats.ts
5
5
  /**
6
6
  * Every recognised format, keyed by its canonical extension. Iteration order is sniffing order;
7
7
  * no two signatures overlap, so it only has to be stable, not carefully chosen.
@@ -87,10 +87,9 @@ function imageFormatForBytes(bytes) {
87
87
  for (const format of Object.values(IMAGE_FORMATS)) if (format.magic?.(bytes)) return format;
88
88
  return null;
89
89
  }
90
- //#endregion
91
- //#region src/media/base64.ts
90
+ // src/media/base64.ts
92
91
  /**
93
- * Base64 payload handling for media parts — isomorphic across Node and browsers
92
+ * Base64 payload handling for media parts, isomorphic across Node and browsers
94
93
  * (`TextEncoder`/`atob`/`btoa` only, no `Buffer`).
95
94
  */
96
95
  /**
@@ -116,7 +115,7 @@ function bytesToBase64(bytes) {
116
115
  * Present a freshly loaded media payload as a `data:` URI.
117
116
  *
118
117
  * A {@link RuntimeAdapter.loadMedia} implementation returns whichever encoding its host
119
- * makes cheap — the browser adapter a full URI, because `FileReader.readAsDataURL` is what
118
+ * makes cheap: the browser adapter a full URI, because `FileReader.readAsDataURL` is what
120
119
  * decodes a blob without blocking; Node and the neutral adapter raw base64, because neither
121
120
  * has a `FileReader`. This is where the two become one, so nothing downstream of the load
122
121
  * has to ask which it got.
@@ -178,341 +177,178 @@ function decodeBase64ToBytes(b64) {
178
177
  function hasBase64Header(data) {
179
178
  return typeof data === "string" && base64PayloadStart(data) >= 0;
180
179
  }
181
- //#endregion
182
- //#region src/ooxml/namespaces.ts
180
+ // src/hex-color.ts
183
181
  /**
184
- * OOXML namespace URIs, by their canonical prefix.
182
+ * pptx-ts: hex colour text, in the one spelling both halves use.
185
183
  *
186
- * A namespace URI is a fact about the format, not about reading or writing one, so it belongs
187
- * here rather than on either side — the same reasoning `rel-types.ts` states at length, and for
188
- * the same failure mode: these are long, near-identical strings, and a typo does not throw. It
189
- * silently matches nothing on the read side and produces a part PowerPoint ignores on the write
190
- * side.
184
+ * A caller may write a colour with or without a leading `#`, and the library accepts both
185
+ * everywhere, which means every site that validates or parses one strips it first. Those
186
+ * strips had drifted into three spellings across six sites, and one of them was not
187
+ * anchored: `.replace('#', '')` takes the `#` out of `'FF00#0'` and hands `'FF000'` on, which
188
+ * then fails the six-digit test and takes a different branch than the anchored forms would.
189
+ * No caller is known to reach that, which is exactly why it wants one spelling rather than a
190
+ * test at each site.
191
191
  *
192
- * The table lived in `read/oxml/dom.ts` and could not be reached from `gen/`, because that
193
- * module imports `@xmldom/xmldom` at module scope and pulling an XML DOM into the write-only
194
- * bundle is a real cost. So the write side hand-wrote the URIs instead: three separate `A_NS`
195
- * constants and seven copies of the same `xmlns:a`/`xmlns:r`/`xmlns:p` triple. This module has
196
- * **no runtime imports**, and no module beside it imports from `gen/` or `read/`, so both sides can
197
- * reach it and neither pays for the other's dependencies. The two that fail loudly reach outside
198
- * `ooxml/` for that alone: `sequence.ts` for `errors.ts`, and `check-enum.ts` for `errors.ts` and
199
- * `diagnostics.ts`. `read/oxml/dom.ts` re-exports it, so no read-side import path changed.
192
+ * This lives at the root rather than under `gen/` or `read/` because both halves need it:
193
+ * the emitters strip on the way in, the readers strip on the way back out. For the same
194
+ * reason, the *validation* belongs here too. `REGEX_HEX_COLOR` used to live in
195
+ * `constants-internal.ts`, which is a write-side module the read path cannot import, so the read
196
+ * path re-spelled the regex; so did one write-side site. Six sites, three spellings of one fact.
200
197
  */
201
- /** Canonical OOXML prefix → namespace URI registry. */
202
- const OOXML_NS = Object.freeze({
203
- a: "http://schemas.openxmlformats.org/drawingml/2006/main",
204
- a14: "http://schemas.microsoft.com/office/drawing/2010/main",
205
- asvg: "http://schemas.microsoft.com/office/drawing/2016/SVG/main",
206
- c: "http://schemas.openxmlformats.org/drawingml/2006/chart",
207
- c15: "http://schemas.microsoft.com/office/drawing/2012/chart",
208
- cp: "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
209
- ct: "http://schemas.openxmlformats.org/package/2006/content-types",
210
- cx: "http://schemas.microsoft.com/office/drawing/2014/chartex",
211
- dc: "http://purl.org/dc/elements/1.1/",
212
- dcterms: "http://purl.org/dc/terms/",
213
- dgm: "http://schemas.openxmlformats.org/drawingml/2006/diagram",
214
- dsp: "http://schemas.microsoft.com/office/drawing/2008/diagram",
215
- ep: "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties",
216
- m: "http://schemas.openxmlformats.org/officeDocument/2006/math",
217
- mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
218
- p: "http://schemas.openxmlformats.org/presentationml/2006/main",
219
- p14: "http://schemas.microsoft.com/office/powerpoint/2010/main",
220
- p15: "http://schemas.microsoft.com/office/powerpoint/2012/main",
221
- p159: "http://schemas.microsoft.com/office/powerpoint/2015/9/main",
222
- p188: "http://schemas.microsoft.com/office/powerpoint/2018/8/main",
223
- pr: "http://schemas.openxmlformats.org/package/2006/relationships",
224
- r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
225
- vt: "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"
226
- });
227
198
  /**
228
- * `a:graphicData/@uri` for a DrawingML table.
199
+ * A colour written as six hex digits, `#` already stripped.
229
200
  *
230
- * The other three payloads a `p:graphicFrame` can hold name a URI that is also a namespace, so
231
- * they read it off {@link OOXML_NS} (`c`, `cx`, `dgm`). A table's does not: nothing binds a
232
- * prefix to it, and the table elements themselves are in the `a` namespace. So it lives beside
233
- * the registry rather than in it, which keeps every entry there a prefix a document can declare.
201
+ * Not exported: {@link isHexColor} is the whole of what the call sites want, and a shared mutable
202
+ * `RegExp` object is a worse export than a predicate.
234
203
  */
235
- const TABLE_GRAPHIC_DATA_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";
204
+ const REGEX_HEX_COLOR = /^[0-9a-fA-F]{6}$/;
205
+ /** An 8-hex RGBA colour, `#` already stripped: {@link splitRgbaHex}'s test. */
206
+ const REGEX_ARGB_HEX = /^[0-9a-fA-F]{8}$/;
207
+ /** Strip a single leading `#` from a colour, leaving any other character alone. */
208
+ function stripHash(value) {
209
+ return value.startsWith("#") ? value.slice(1) : value;
210
+ }
236
211
  /**
237
- * The `xmlns` declarations every PresentationML part root carries, in the order the emitters
238
- * have always written them.
212
+ * Is `value` a 6-hex RGB colour? A leading `#` is stripped first, so both spellings agree.
239
213
  *
240
- * Spread this **first** in a root element's attribute map: `el()` serializes attributes in
241
- * object-key order, so the three keys have to keep the positions the literals occupied or the
242
- * emitted bytes move. A root with extra attributes (`show` on `p:sld`, `def` on `a:tblStyleLst`)
243
- * lists them after the spread, which is where they already were.
214
+ * This is the *shape* test only. Whether the caller may also name a scheme colour there is the
215
+ * call site's question, and each still asks it: `createColorElement` falls back to the default
216
+ * font colour, `makeXmlTheme` keeps the Office default and warns, `normalizeHex` throws.
217
+ * @param value - the caller's colour, with or without a leading `#`
244
218
  */
245
- const PML_ROOT_NS = Object.freeze({
246
- "xmlns:a": OOXML_NS.a,
247
- "xmlns:r": OOXML_NS.r,
248
- "xmlns:p": OOXML_NS.p
249
- });
250
- //#endregion
251
- //#region src/ooxml/rel-types.ts
219
+ function isHexColor(value) {
220
+ return REGEX_HEX_COLOR.test(stripHash(value));
221
+ }
252
222
  /**
253
- * OPC relationship type URIs and part content types, and the schema-URI bases they are built from.
254
- *
255
- * These are long, near-identical strings that differ in one path segment, which makes a typo
256
- * both easy to write and invisible on review — a wrong URI does not throw, it silently matches
257
- * nothing on the read side and produces a part PowerPoint ignores or rejects on the write side.
258
- *
259
- * **Why this module is neither `gen/` nor `read/`.** Both halves need the same URIs, and for a
260
- * while each kept its own copy (`gen/oxml/schema-uris.ts`, `read/api/rel-types.ts`), with a note
261
- * in both saying a shared home was "a separate decision". It is this module: a rel type is a fact
262
- * about the OOXML package format, not about writing or reading one, so neither side should own it.
263
- * A divergence between the two copies would mean the writer emitting a rel the reader cannot find
264
- * — a round-trip bug with no compile-time signal.
265
- *
266
- * Only types used by more than one module live here. A rel type exactly one module cares about
267
- * (`tags`, `oleObject`, the modern comments part, …) stays declared next to the code that reads or
268
- * emits it — hoisting those would trade a definition you can see for one you have to go and find,
269
- * and buy nothing. A writer and a reader of the same part count as two modules even when each
270
- * names the string once: that pair is exactly the divergence this module exists to prevent.
223
+ * Is `value` an 8-hex RGBA colour? A leading `#` is stripped first.
271
224
  *
272
- * The same reasoning covers **part content types**, which are the same kind of fact and fail the
273
- * same silent way, including every entry the writer's `[Content_Types].xml` declares.
274
- */
275
- /**
276
- * Prefix for `.rels` `Type` URIs — append the type name (`slide`, `image`, `theme`, …).
277
- * Exported for the write side, which builds one-off rel types at their call sites.
278
- */
279
- const OFFICE_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/";
280
- /** `xmlns` of every `<Relationships>` part. Also the prefix of the package-scoped rel types. */
281
- const PACKAGE_REL_NS = OOXML_NS.pr;
282
- /**
283
- * Prefix for the Microsoft rel types this library emits and reads. Exported for the same reason
284
- * as {@link OFFICE_REL}: a rel type only one module names is built from it at that module's call site.
285
- */
286
- const MS_REL = "http://schemas.microsoft.com/office/";
287
- /** `p:sldIdLst` → a slide part. */
288
- const SLIDE_REL = OFFICE_REL + "slide";
289
- /** A slide → its layout; a master → each of its layouts. */
290
- const SLIDE_LAYOUT_REL = OFFICE_REL + "slideLayout";
291
- /** A layout → its master; the presentation → each master. */
292
- const SLIDE_MASTER_REL = OFFICE_REL + "slideMaster";
293
- /** A slide → its notes slide. */
294
- const NOTES_SLIDE_REL = OFFICE_REL + "notesSlide";
295
- /** A notes slide → the notes master; the presentation → the notes master. */
296
- const NOTES_MASTER_REL = OFFICE_REL + "notesMaster";
297
- /** A master or notes master → its theme. Layouts and slides inherit it rather than holding one. */
298
- const THEME_REL = OFFICE_REL + "theme";
299
- /** The package root → `ppt/presentation.xml`. The entry point to everything above. */
300
- const OFFICE_DOCUMENT_REL = OFFICE_REL + "officeDocument";
301
- /** Any part → an image in `ppt/media/`. */
302
- const IMAGE_REL = OFFICE_REL + "image";
303
- /** Any part → an external or internal jump target. External ones carry `TargetMode="External"`. */
304
- const HYPERLINK_REL = OFFICE_REL + "hyperlink";
305
- /** The presentation → `ppt/tableStyles.xml`. */
306
- const TABLE_STYLES_REL = OFFICE_REL + "tableStyles";
307
- /** The package root → `docProps/core.xml`. Package-scoped, so not under {@link OFFICE_REL}. */
308
- const CORE_PROPS_REL = PACKAGE_REL_NS + "/metadata/core-properties";
309
- /** The package root → `docProps/app.xml`. */
310
- const EXTENDED_PROPS_REL = OFFICE_REL + "extended-properties";
311
- /** The package root → `docProps/custom.xml`. */
312
- const CUSTOM_PROPS_REL = OFFICE_REL + "custom-properties";
313
- /** A slide → its legacy comments part (`ppt/comments/commentN.xml`). */
314
- const COMMENTS_REL = OFFICE_REL + "comments";
315
- /** The presentation → the deck-wide legacy comment-author registry (`ppt/commentAuthors.xml`). */
316
- const COMMENT_AUTHORS_REL = OFFICE_REL + "commentAuthors";
317
- /** The presentation → the 2018 comment-author registry (`ppt/authors.xml`), a Microsoft rel type. */
318
- const MODERN_COMMENT_AUTHORS_REL = MS_REL + "2018/10/relationships/authors";
319
- /** The presentation → `ppt/presProps.xml`. */
320
- const PRES_PROPS_REL = OFFICE_REL + "presProps";
321
- /** The presentation → `ppt/viewProps.xml`. */
322
- const VIEW_PROPS_REL = OFFICE_REL + "viewProps";
323
- /** The presentation → the handout master. */
324
- const HANDOUT_MASTER_REL = OFFICE_REL + "handoutMaster";
325
- /** A part → a theme override, which replaces the deck theme for that part alone. */
326
- const THEME_OVERRIDE_REL = OFFICE_REL + "themeOverride";
327
- /** The presentation → an embedded font part, one per face. */
328
- const FONT_REL = OFFICE_REL + "font";
329
- /** A slide → an audio blob in `ppt/media/`. Paired with {@link MS_MEDIA_REL} on the same target. */
330
- const AUDIO_REL = OFFICE_REL + "audio";
331
- /** A slide → a video blob in `ppt/media/`, or an online-video placeholder. Paired with {@link MS_MEDIA_REL}. */
332
- const VIDEO_REL = OFFICE_REL + "video";
333
- /**
334
- * The MS-2007 `media` rel every audio/video shape carries alongside the ECMA one, pointing at the
335
- * same target. It is what `<p14:media r:embed>` resolves, so a media shape needs both.
225
+ * The 8-digit form is RGB followed by one alpha byte; {@link splitRgbaHex} is what takes it apart.
226
+ * @param value - the caller's colour, with or without a leading `#`
336
227
  */
337
- const MS_MEDIA_REL = MS_REL + "2007/relationships/media";
338
- /** A slide → an embedded 3D model (`.glb`). Note `2017/06`, not the `2017` of the model3d namespace. */
339
- const MODEL3D_REL = MS_REL + "2017/06/relationships/model3d";
340
- /** A slide → a classic (`c:`) chart part. */
341
- const CHART_REL = OFFICE_REL + "chart";
342
- /** A slide → a chartEx (`cx:`, Office 2016) chart part — an MS rel, not the ECMA `chart` one. */
343
- const CHARTEX_REL = MS_REL + "2014/relationships/chartEx";
344
- /** A chart part → its mandatory style sidecar. */
345
- const CHART_STYLE_REL = MS_REL + "2011/relationships/chartStyle";
346
- /** A chart part → its mandatory colour-style sidecar. */
347
- const CHART_COLOR_STYLE_REL = MS_REL + "2011/relationships/chartColorStyle";
348
- /** Any part → an embedded OPC package (an Office file, itself a zip): a chart's workbook, an OLE payload. */
349
- const PACKAGE_REL = OFFICE_REL + "package";
228
+ function isArgbHex(value) {
229
+ return REGEX_ARGB_HEX.test(stripHash(value));
230
+ }
350
231
  /**
351
- * Root of the ECMA-376 content types. Exported for a content type only one module names, which is
352
- * built from it at that module's call site; a shared one is a constant below.
232
+ * Split an 8-hex RGBA colour into its 6-hex RGB and a 0–1 alpha; anything else passes through
233
+ * unchanged with no alpha.
234
+ *
235
+ * `#` is stripped first, so both spellings reach the same answer. Two sites parsed this by
236
+ * hand (`createColorElement`, which turns the alpha into an `<a:alpha>` sibling, and
237
+ * `normalizeShadowOptions`, which turns it into a shadow's `_alpha`), and they divided by 255 at
238
+ * different points in their arithmetic.
239
+ * @param value - the caller's colour, with or without a leading `#`
353
240
  */
354
- const OD_CONTENT = "application/vnd.openxmlformats-officedocument.";
355
- /** Root of the OPC package-level content types. Private: callers want one of the constants below. */
356
- const PACKAGE_CONTENT = "application/vnd.openxmlformats-package.";
357
- /** Root of the Microsoft Office content types the chartEx family uses. Private. */
358
- const MS_OFFICE_CONTENT = "application/vnd.ms-office.";
359
- /** Root of the PowerPoint-specific Microsoft content types. Exported for the same reason as {@link OD_CONTENT}. */
360
- const MS_POWERPOINT_CONTENT = "application/vnd.ms-powerpoint.";
361
- /** `ppt/slides/slideN.xml`. */
362
- const SLIDE_CONTENT_TYPE = OD_CONTENT + "presentationml.slide+xml";
363
- /** `ppt/slideMasters/slideMasterN.xml`. */
364
- const SLIDE_MASTER_CONTENT_TYPE = OD_CONTENT + "presentationml.slideMaster+xml";
365
- /** `ppt/slideLayouts/slideLayoutN.xml`. */
366
- const SLIDE_LAYOUT_CONTENT_TYPE = OD_CONTENT + "presentationml.slideLayout+xml";
367
- /** `ppt/notesSlides/notesSlideN.xml`. */
368
- const NOTES_SLIDE_CONTENT_TYPE = OD_CONTENT + "presentationml.notesSlide+xml";
369
- /** `ppt/notesMasters/notesMasterN.xml`. */
370
- const NOTES_MASTER_CONTENT_TYPE = OD_CONTENT + "presentationml.notesMaster+xml";
371
- /** `ppt/theme/themeN.xml`. Not `presentationml.`: a theme is a DrawingML part. */
372
- const THEME_CONTENT_TYPE = OD_CONTENT + "theme+xml";
373
- /** `ppt/tableStyles.xml`. */
374
- const TABLE_STYLES_CONTENT_TYPE = OD_CONTENT + "presentationml.tableStyles+xml";
375
- /** `ppt/presentation.xml` in an editable `.pptx`. A `.potx` template's main part is a different one. */
376
- const PRESENTATION_MAIN_CONTENT_TYPE = OD_CONTENT + "presentationml.presentation.main+xml";
377
- /** Every `.rels` part. Declared once as a `Default` for the `rels` extension, never as an Override. */
378
- const RELATIONSHIPS_CONTENT_TYPE = PACKAGE_CONTENT + "relationships+xml";
379
- /** `ppt/presProps.xml`. */
380
- const PRES_PROPS_CONTENT_TYPE = OD_CONTENT + "presentationml.presProps+xml";
381
- /** `ppt/viewProps.xml`. */
382
- const VIEW_PROPS_CONTENT_TYPE = OD_CONTENT + "presentationml.viewProps+xml";
383
- /** `ppt/comments/commentN.xml`, a legacy comments part. */
384
- const COMMENTS_CONTENT_TYPE = OD_CONTENT + "presentationml.comments+xml";
385
- /** `ppt/commentAuthors.xml`. */
386
- const COMMENT_AUTHORS_CONTENT_TYPE = OD_CONTENT + "presentationml.commentAuthors+xml";
387
- /** `docProps/core.xml`. Package-scoped, so not under the officedocument prefix. */
388
- const CORE_PROPS_CONTENT_TYPE = PACKAGE_CONTENT + "core-properties+xml";
389
- /** `docProps/app.xml`. */
390
- const EXTENDED_PROPS_CONTENT_TYPE = OD_CONTENT + "extended-properties+xml";
391
- /** `docProps/custom.xml`. */
392
- const CUSTOM_PROPS_CONTENT_TYPE = OD_CONTENT + "custom-properties+xml";
393
- /** `ppt/charts/chartN.xml`, a classic (`c:`) chart. */
394
- const CHART_CONTENT_TYPE = OD_CONTENT + "drawingml.chart+xml";
395
- /** `ppt/charts/chartExN.xml`. A chartEx part carries a Microsoft content type, not an `openxmlformats` one. */
396
- const CHARTEX_CONTENT_TYPE = MS_OFFICE_CONTENT + "chartex+xml";
397
- /** A chart's mandatory `style{N}.xml` sidecar. */
398
- const CHART_STYLE_CONTENT_TYPE = MS_OFFICE_CONTENT + "chartstyle+xml";
399
- /** A chart's mandatory `colors{N}.xml` sidecar. */
400
- const CHART_COLOR_STYLE_CONTENT_TYPE = MS_OFFICE_CONTENT + "chartcolorstyle+xml";
401
- /** An embedded `.xlsx` workbook: a chart's data, or an OLE payload. */
402
- const XLSX_CONTENT_TYPE = OD_CONTENT + "spreadsheetml.sheet";
241
+ function splitRgbaHex(value) {
242
+ const hex = stripHash(value);
243
+ if (!isArgbHex(hex)) return { rgb: hex };
244
+ return {
245
+ rgb: hex.slice(0, 6),
246
+ alpha: parseInt(hex.slice(6, 8), 16) / 255
247
+ };
248
+ }
249
+ // src/ooxml/xsd-boolean.ts
403
250
  /**
404
- * Every kind of part a page points at without owning it.
251
+ * Parsing an `xsd:boolean` attribute value.
405
252
  *
406
- * Two questions read this table, under different keys. Copying a page asks whether a relationship's
407
- * target may be shared by the copies rather than copied (every row, by `relType`). Pruning after a
408
- * slide is removed asks whether a part is deck chrome that stays even while nothing reaches it (the
409
- * `deck` rows, by `contentType`). They were two hand-kept lists, and the two author registries were
410
- * on the first and missing from the second.
411
- */
412
- const SHARED_PARTS = [
413
- {
414
- relType: SLIDE_LAYOUT_REL,
415
- contentType: SLIDE_LAYOUT_CONTENT_TYPE,
416
- scope: "deck"
417
- },
418
- {
419
- relType: SLIDE_MASTER_REL,
420
- contentType: SLIDE_MASTER_CONTENT_TYPE,
421
- scope: "deck"
422
- },
423
- {
424
- relType: NOTES_MASTER_REL,
425
- contentType: NOTES_MASTER_CONTENT_TYPE,
426
- scope: "deck"
427
- },
428
- {
429
- relType: THEME_REL,
430
- contentType: THEME_CONTENT_TYPE,
431
- scope: "deck"
432
- },
433
- {
434
- relType: THEME_OVERRIDE_REL,
435
- contentType: OD_CONTENT + "themeOverride+xml",
436
- scope: "deck"
437
- },
438
- {
439
- relType: HANDOUT_MASTER_REL,
440
- contentType: OD_CONTENT + "presentationml.handoutMaster+xml",
441
- scope: "deck"
442
- },
443
- {
444
- relType: PRES_PROPS_REL,
445
- contentType: PRES_PROPS_CONTENT_TYPE,
446
- scope: "deck"
447
- },
448
- {
449
- relType: VIEW_PROPS_REL,
450
- contentType: VIEW_PROPS_CONTENT_TYPE,
451
- scope: "deck"
452
- },
453
- {
454
- relType: TABLE_STYLES_REL,
455
- contentType: TABLE_STYLES_CONTENT_TYPE,
456
- scope: "deck"
457
- },
458
- {
459
- relType: OFFICE_DOCUMENT_REL,
460
- contentType: PRESENTATION_MAIN_CONTENT_TYPE,
461
- scope: "deck"
462
- },
463
- {
464
- relType: COMMENT_AUTHORS_REL,
465
- contentType: COMMENT_AUTHORS_CONTENT_TYPE,
466
- scope: "deck"
467
- },
468
- {
469
- relType: MODERN_COMMENT_AUTHORS_REL,
470
- contentType: "application/vnd.ms-powerpoint.authors+xml",
471
- scope: "deck"
472
- },
473
- {
474
- relType: IMAGE_REL,
475
- contentType: null,
476
- scope: "media"
477
- },
478
- {
479
- relType: AUDIO_REL,
480
- contentType: null,
481
- scope: "media"
482
- },
483
- {
484
- relType: VIDEO_REL,
485
- contentType: null,
486
- scope: "media"
487
- },
488
- {
489
- relType: MS_MEDIA_REL,
490
- contentType: null,
491
- scope: "media"
492
- },
493
- {
494
- relType: MODEL3D_REL,
495
- contentType: null,
496
- scope: "media"
497
- },
498
- {
499
- relType: FONT_REL,
500
- contentType: null,
501
- scope: "media"
502
- },
503
- {
504
- relType: SLIDE_REL,
505
- contentType: null,
506
- scope: "page"
507
- },
508
- {
509
- relType: HYPERLINK_REL,
510
- contentType: null,
511
- scope: "page"
512
- }
513
- ];
514
- //#endregion
515
- //#region src/ooxml/st-enums.ts
253
+ * **Why a function at all.** `xsd:boolean` has four lexical forms (`1`, `0`, `true`, `false`),
254
+ * and OOXML attributes typed with it accept every one of them. PowerPoint writes only `1` and
255
+ * `0`, so a hand-rolled `=== '1'` test passes against every deck this library produces and
256
+ * against every deck PowerPoint produces, and silently misreads the other producers. That is the
257
+ * worst shape a bug can have: invisible to the fixtures, visible only on a customer's file.
258
+ *
259
+ * **Why this module is neither `gen/` nor `read/`.** The lexical space of a schema type is a fact
260
+ * about the schema, like `sequence.ts`'s document order and `body-insets.ts`'s defaults. The
261
+ * parser lived in `read/oxml/dom.ts` because reading is where most of the demand is, and three
262
+ * private copies had collapsed into it, but `read/oxml/dom.ts` imports `@xmldom/xmldom`, so
263
+ * anything outside `read/` that wants this five-line function has to take a DOM dependency to get
264
+ * it, or write a fourth copy. `script/from-read/detect.ts` hit exactly that and wrote the copy.
265
+ *
266
+ * The DOM-typed convenience wrapper stays behind in `read/oxml/dom.ts` as `boolAttr`, because
267
+ * *that* one really is about reading an element.
268
+ *
269
+ * **The write direction is here too**, and it is where the interesting distinction lives: an
270
+ * emitter writing a boolean has THREE useful shapes, not one; see {@link xsdBool} and
271
+ * {@link xsdBoolIfTrue}. Sixty-odd inline ternaries spelled them in three ways with nothing
272
+ * naming which was which.
273
+ */
274
+ /**
275
+ * Parse an `xsd:boolean` OOXML attribute (`1`/`0`/`true`/`false`); else `null`.
276
+ *
277
+ * `null` for an absent *or* unparseable value is deliberate and load-bearing at the call sites:
278
+ * several attributes have a schema default of `true`, so they test `!== false` rather than
279
+ * `=== true` and an unset attribute has to be distinguishable from an explicit `0`.
280
+ */
281
+ function boolValue(value) {
282
+ if (value === "1" || value === "true") return true;
283
+ if (value === "0" || value === "false") return false;
284
+ return null;
285
+ }
286
+ /**
287
+ * A boolean as the `1`/`0` an `xsd:boolean` attribute takes.
288
+ *
289
+ * Writes the attribute either way, so `false` is *stated*. Use this wherever the schema
290
+ * default is `true`, or wherever the absence of the attribute would mean something other
291
+ * than `false`: a value inherited from a list style, a layout or the theme.
292
+ * @param value - the resolved boolean
293
+ */
294
+ function xsdBool(value) {
295
+ return value ? "1" : "0";
296
+ }
297
+ /**
298
+ * A boolean as `1`, or `null` so the attribute is left out entirely.
299
+ *
300
+ * **Correct only where the schema default is `false`.** It collapses "false" and "unset" into
301
+ * one output, which is the two-spellings-for-three-states shape the API policy warns about: the
302
+ * next attribute with a `true` default written this way reads as correct and is wrong. Every
303
+ * current caller is on a `false`-defaulting attribute, so the collapse costs nothing today;
304
+ * naming it is what makes the next one visible.
305
+ * @param value - the resolved boolean
306
+ */
307
+ function xsdBoolIfTrue(value) {
308
+ return value ? "1" : null;
309
+ }
310
+ // src/ooxml/check-enum.ts
311
+ /**
312
+ * Check a value against its `ST_` union, reporting and dropping it when outside (the write-path
313
+ * policy, described in the module note).
314
+ *
315
+ * `undefined`/`null` returns `null` without reporting: an unset option is not an invalid one, and
316
+ * it is by far the common case.
317
+ *
318
+ * The offending value is interpolated into the message deliberately. {@link warnOnce} dedups on
319
+ * code **and** message, so keeping the value in the text is what lets a second, *different* bad
320
+ * value under the same code still get reported.
321
+ * @param value - the value the caller supplied, if any
322
+ * @param valid - the enum's members
323
+ * @param code - the condition to report
324
+ * @param label - how to name the offending option in the message, e.g. ``'table cell: cell3D `preset`'``
325
+ * @param detail - extra structured context to merge into the diagnostic's `detail`
326
+ * @returns the value when legal, else `null`
327
+ */
328
+ function checkEnumOrWarn(value, valid, code, label, detail) {
329
+ if (value === void 0 || value === null) return null;
330
+ if (valid.includes(value)) return value;
331
+ warnOnce(code, `${label} value \`${String(value)}\` is not valid and is ignored. Use one of ${valid.join(", ")}.`, {
332
+ received: value,
333
+ valid,
334
+ ...detail
335
+ });
336
+ return null;
337
+ }
338
+ /**
339
+ * Check a value against its `ST_` union, throwing when outside (the read/edit-path policy, described
340
+ * in the module note).
341
+ * @param value - the value the caller asked for
342
+ * @param valid - the enum's members
343
+ * @param attribute - the attribute's name, for the message
344
+ * @param code - the condition to report
345
+ * @returns `value`, when it is legal
346
+ */
347
+ function checkEnumOrThrow(value, valid, attribute, code) {
348
+ if (valid.includes(value)) return value;
349
+ throw new InvalidOptionError(code, `Invalid ${attribute}: ${JSON.stringify(value)}. Expected one of: ${valid.join(", ")}.`);
350
+ }
351
+ // src/ooxml/st-enums.ts
516
352
  /**
517
353
  * `ST_` simple-type enumerations from ECMA-376, declared once as the single source of both the
518
354
  * TypeScript union a caller is checked against and the runtime list a value is validated against.
@@ -525,7 +361,7 @@ const SHARED_PARTS = [
525
361
  * which is harmless, but nothing would have caught a divergence in *content*.
526
362
  *
527
363
  * **The shape.** Every entry is a `readonly` tuple declared `as const`, with its union derived via
528
- * `(typeof X)[number]`. Widening the tuple widens the union in the same edit — they cannot drift.
364
+ * `(typeof X)[number]`. Widening the tuple widens the union in the same edit, so they cannot drift.
529
365
  * This is the idiom `EXTRA_SHAPE_PRESETS` already used; this module brings the stragglers onto it,
530
366
  * and holds a few closed schema lists that are not `ST_` types, such as `EMBEDDED_FONT_SLOTS`.
531
367
  *
@@ -543,11 +379,11 @@ const SHARED_PARTS = [
543
379
  * unfinished for a while. `src/types/chart.ts`
544
380
  * hand-wrote four of those unions beside the option that takes them and typed the other three as
545
381
  * bare `string`, so `barDir` carried no compile-time check at all, and `dataLabelPosition`'s
546
- * hand-written union was missing `inBase` — a value the validator accepts on a stacked bar, so
382
+ * hand-written union was missing `inBase`, a value the validator accepts on a stacked bar, so
547
383
  * the two spellings had already diverged in content. Both are gone: for every option here, the
548
384
  * type a caller is checked against and the tuple the validator checks are one declaration.
549
385
  */
550
- /** `ST_TextAutonumberScheme` — the numbering an auto-numbered bullet counts in (`a:buAutoNum/@type`). */
386
+ /** `ST_TextAutonumberScheme`: the numbering an auto-numbered bullet counts in (`a:buAutoNum/@type`). */
551
387
  const TEXT_AUTONUM_SCHEMES = [
552
388
  "alphaLcParenBoth",
553
389
  "alphaUcParenBoth",
@@ -591,19 +427,19 @@ const TEXT_AUTONUM_SCHEMES = [
591
427
  "hindiNumParenR",
592
428
  "hindiAlpha1Period"
593
429
  ];
594
- /** `ST_TextStrikeType` — a run's strikethrough (`a:rPr/@strike`). */
430
+ /** `ST_TextStrikeType`: a run's strikethrough (`a:rPr/@strike`). */
595
431
  const TEXT_STRIKE_TYPES = [
596
432
  "noStrike",
597
433
  "sngStrike",
598
434
  "dblStrike"
599
435
  ];
600
- /** `ST_TextCapsType` — a run's capitalization (`a:rPr/@cap`). */
436
+ /** `ST_TextCapsType`: a run's capitalization (`a:rPr/@cap`). */
601
437
  const TEXT_CAPS_TYPES = [
602
438
  "none",
603
439
  "small",
604
440
  "all"
605
441
  ];
606
- /** `ST_TextUnderlineType` — a run's underline (`a:rPr/@u`). */
442
+ /** `ST_TextUnderlineType`: a run's underline (`a:rPr/@u`). */
607
443
  const TEXT_UNDERLINE_TYPES = [
608
444
  "none",
609
445
  "words",
@@ -624,7 +460,7 @@ const TEXT_UNDERLINE_TYPES = [
624
460
  "wavyHeavy",
625
461
  "wavyDbl"
626
462
  ];
627
- /** `ST_TextTabAlignType` — a tab stop's alignment (`a:tab/@algn`). */
463
+ /** `ST_TextTabAlignType`: a tab stop's alignment (`a:tab/@algn`). */
628
464
  const TEXT_TAB_ALIGN_TYPES = [
629
465
  "l",
630
466
  "ctr",
@@ -632,7 +468,7 @@ const TEXT_TAB_ALIGN_TYPES = [
632
468
  "dec"
633
469
  ];
634
470
  /**
635
- * `ST_SchemeColorVal` — a theme colour reference (`a:schemeClr/@val`). Wider than
471
+ * `ST_SchemeColorVal`: a theme colour reference (`a:schemeClr/@val`). Wider than
636
472
  * {@link THEME_COLOR_SLOTS}: it adds the `bg1`/`tx1`/`bg2`/`tx2` aliases the colour map resolves,
637
473
  * and `phClr`, the colour a theme style-matrix entry is applied in.
638
474
  */
@@ -713,7 +549,7 @@ const TRANSITION_VARIANT_ATTRIBUTES = {
713
549
  zoom: ["dir"]
714
550
  };
715
551
  /**
716
- * `ST_BevelPresetType` — the bevel profile applied to a 3-D edge.
552
+ * `ST_BevelPresetType`: the bevel profile applied to a 3-D edge.
717
553
  * @see ECMA-376 Part 1 §20.1.10.9
718
554
  */
719
555
  const BEVEL_PRESETS = [
@@ -731,7 +567,7 @@ const BEVEL_PRESETS = [
731
567
  "artDeco"
732
568
  ];
733
569
  /**
734
- * `ST_PresetMaterialType` — how a 3-D surface responds to light.
570
+ * `ST_PresetMaterialType`: how a 3-D surface responds to light.
735
571
  * @see ECMA-376 Part 1 §20.1.10.50
736
572
  */
737
573
  const PRESET_MATERIALS = [
@@ -752,7 +588,7 @@ const PRESET_MATERIALS = [
752
588
  "softmetal"
753
589
  ];
754
590
  /**
755
- * `ST_LightRigType` — the preset lighting scene.
591
+ * `ST_LightRigType`: the preset lighting scene.
756
592
  * @see ECMA-376 Part 1 §20.1.10.35
757
593
  */
758
594
  const LIGHT_RIGS = [
@@ -785,7 +621,7 @@ const LIGHT_RIGS = [
785
621
  "brightRoom"
786
622
  ];
787
623
  /**
788
- * `ST_LightRigDirection` — where the light rig sits relative to the scene.
624
+ * `ST_LightRigDirection`: where the light rig sits relative to the scene.
789
625
  * @see ECMA-376 Part 1 §20.1.10.33
790
626
  */
791
627
  const LIGHT_RIG_DIRECTIONS = [
@@ -799,12 +635,12 @@ const LIGHT_RIG_DIRECTIONS = [
799
635
  "br"
800
636
  ];
801
637
  /**
802
- * `ST_TextHorzOverflowType` — `a:bodyPr/@horzOverflow` and `a:tcPr/@horzOverflow`.
638
+ * `ST_TextHorzOverflowType`: `a:bodyPr/@horzOverflow` and `a:tcPr/@horzOverflow`.
803
639
  * @see ECMA-376 Part 1 §20.1.10.68
804
640
  */
805
641
  const TEXT_HORZ_OVERFLOW = ["clip", "overflow"];
806
642
  /**
807
- * `ST_TextAnchoringType` — `a:bodyPr/@anchor` and `a:tcPr/@anchor`.
643
+ * `ST_TextAnchoringType`: `a:bodyPr/@anchor` and `a:tcPr/@anchor`.
808
644
  * @see ECMA-376 Part 1 §20.1.10.60
809
645
  */
810
646
  const TEXT_ANCHORS = [
@@ -815,7 +651,7 @@ const TEXT_ANCHORS = [
815
651
  "dist"
816
652
  ];
817
653
  /**
818
- * `ST_PresetPatternVal` — `a:pattFill/@prst`, the hatch a pattern fill draws.
654
+ * `ST_PresetPatternVal`: `a:pattFill/@prst`, the hatch a pattern fill draws.
819
655
  */
820
656
  const PATTERN_PRESETS = [
821
657
  "pct5",
@@ -874,7 +710,7 @@ const PATTERN_PRESETS = [
874
710
  "zigZag"
875
711
  ];
876
712
  /**
877
- * `ST_TextVerticalType` — `a:bodyPr/@vert` and `a:tcPr/@vert`.
713
+ * `ST_TextVerticalType`: `a:bodyPr/@vert` and `a:tcPr/@vert`.
878
714
  * @see ECMA-376 Part 1 §20.1.10.83
879
715
  */
880
716
  const TEXT_VERTICAL = [
@@ -887,7 +723,7 @@ const TEXT_VERTICAL = [
887
723
  "wordArtVertRtl"
888
724
  ];
889
725
  /**
890
- * `ST_LineEndType` — the arrowhead at either end of a stroke (`a:headEnd/@type`,
726
+ * `ST_LineEndType`: the arrowhead at either end of a stroke (`a:headEnd/@type`,
891
727
  * `a:tailEnd/@type`).
892
728
  * @see ECMA-376 Part 1 §20.1.10.33
893
729
  */
@@ -914,11 +750,34 @@ const EMBEDDED_FONT_SLOTS = [
914
750
  "boldItalic"
915
751
  ];
916
752
  /**
917
- * `ST_TextShapeType` — the preset path text is bent along (`a:prstTxWarp/@prst`), which
753
+ * `ST_PlaceholderType`: the kind of placeholder a `p:ph` fills (`p:ph/@type`). Absent, it means
754
+ * `obj`.
755
+ * @see ECMA-376 Part 1 §19.7.10
756
+ */
757
+ const PLACEHOLDER_TYPES = [
758
+ "title",
759
+ "body",
760
+ "ctrTitle",
761
+ "subTitle",
762
+ "dt",
763
+ "sldNum",
764
+ "ftr",
765
+ "hdr",
766
+ "obj",
767
+ "chart",
768
+ "tbl",
769
+ "clipArt",
770
+ "dgm",
771
+ "media",
772
+ "sldImg",
773
+ "pic"
774
+ ];
775
+ /**
776
+ * `ST_TextShapeType`: the preset path text is bent along (`a:prstTxWarp/@prst`), which
918
777
  * PowerPoint calls WordArt.
919
778
  *
920
779
  * `textNoShape` is the identity member: it is legal, and it warps nothing. The library does not
921
- * narrow this set — every member is a shape PowerPoint draws.
780
+ * narrow this set: every member is a shape PowerPoint draws.
922
781
  * @see ECMA-376 Part 1 §20.1.10.76
923
782
  */
924
783
  const TEXT_SHAPE_TYPES = [
@@ -965,7 +824,7 @@ const TEXT_SHAPE_TYPES = [
965
824
  "textCascadeDown"
966
825
  ];
967
826
  /**
968
- * `ST_PresetLineDashVal` — the dash pattern of a line or border (`a:prstDash/@val`).
827
+ * `ST_PresetLineDashVal`: the dash pattern of a line or border (`a:prstDash/@val`).
969
828
  *
970
829
  * The `sys*` members are the "system" dashes whose exact dot/gap ratio the renderer picks; the
971
830
  * rest are defined by the spec. Both `BorderProps.dashType` and `ShapeLineProps.dashType` accept
@@ -986,12 +845,12 @@ const PRESET_LINE_DASHES = [
986
845
  "sysDashDotDot"
987
846
  ];
988
847
  /**
989
- * `ST_BarDir` — whether a bar/column plot runs horizontally or vertically (`c:barDir/@val`).
848
+ * `ST_BarDir`: whether a bar/column plot runs horizontally or vertically (`c:barDir/@val`).
990
849
  * @see ECMA-376 Part 1 §21.2.3.3
991
850
  */
992
851
  const BAR_DIRECTIONS = ["bar", "col"];
993
852
  /**
994
- * `ST_BarGrouping` — how a bar/column plot stacks its series (`c:grouping/@val` inside
853
+ * `ST_BarGrouping`: how a bar/column plot stacks its series (`c:grouping/@val` inside
995
854
  * `c:barChart`/`c:bar3DChart`).
996
855
  * @see ECMA-376 Part 1 §21.2.3.4
997
856
  */
@@ -1002,7 +861,7 @@ const BAR_GROUPINGS = [
1002
861
  "stacked"
1003
862
  ];
1004
863
  /**
1005
- * `ST_Grouping` — the same decision for the plots that have no clustered form
864
+ * `ST_Grouping`: the same decision for the plots that have no clustered form
1006
865
  * (`c:areaChart`, `c:lineChart`).
1007
866
  * @see ECMA-376 Part 1 §21.2.3.17
1008
867
  */
@@ -1012,7 +871,7 @@ const GROUPINGS = [
1012
871
  "stacked"
1013
872
  ];
1014
873
  /**
1015
- * `ST_Shape` — the solid a 3-D bar is drawn as (`c:shape/@val`).
874
+ * `ST_Shape`: the solid a 3-D bar is drawn as (`c:shape/@val`).
1016
875
  * @see ECMA-376 Part 1 §21.2.3.35
1017
876
  */
1018
877
  const BAR_3D_SHAPES = [
@@ -1024,7 +883,7 @@ const BAR_3D_SHAPES = [
1024
883
  "pyramidToMax"
1025
884
  ];
1026
885
  /**
1027
- * `ST_RadarStyle` — how a radar plot is drawn (`c:radarStyle/@val`).
886
+ * `ST_RadarStyle`: how a radar plot is drawn (`c:radarStyle/@val`).
1028
887
  * @see ECMA-376 Part 1 §21.2.3.33
1029
888
  */
1030
889
  const RADAR_STYLES = [
@@ -1039,8 +898,8 @@ const RADAR_STYLES = [
1039
898
  * what made the other two read as typos rather than as a second vocabulary.
1040
899
  *
1041
900
  * Both spellings are accepted on the public option and normalized to the wire one at definition
1042
- * time. Before that, `radarStyle: 'marker'` — the schema's word, and the obvious guess for anyone
1043
- * reading a chart part — failed the enum check and silently fell back to `standard`, so a caller
901
+ * time. Before that, `radarStyle: 'marker'` (the schema's word, and the obvious guess for anyone
902
+ * reading a chart part) failed the enum check and silently fell back to `standard`, so a caller
1044
903
  * asking for markers got a plain radar.
1045
904
  */
1046
905
  const RADAR_STYLE_ALIASES = {
@@ -1049,12 +908,12 @@ const RADAR_STYLE_ALIASES = {
1049
908
  };
1050
909
  /**
1051
910
  * The alias spellings alone, for the definer's enum check. Derived from the map rather than
1052
- * written out again — the whole point of this module is that the list a value is validated
911
+ * written out again: the whole point of this module is that the list a value is validated
1053
912
  * against and the union it is typed against cannot drift apart.
1054
913
  */
1055
914
  const RADAR_STYLE_ALIAS_NAMES = Object.keys(RADAR_STYLE_ALIASES);
1056
915
  /**
1057
- * `ST_LegendPos` — where the legend sits relative to the plot area (`c:legendPos/@val`).
916
+ * `ST_LegendPos`: where the legend sits relative to the plot area (`c:legendPos/@val`).
1058
917
  * @see ECMA-376 Part 1 §21.2.3.24
1059
918
  */
1060
919
  const LEGEND_POSITIONS = [
@@ -1065,7 +924,7 @@ const LEGEND_POSITIONS = [
1065
924
  "t"
1066
925
  ];
1067
926
  /**
1068
- * `ST_DispBlanksAs` — what a plot draws where a value is missing (`c:dispBlanksAs/@val`).
927
+ * `ST_DispBlanksAs`: what a plot draws where a value is missing (`c:dispBlanksAs/@val`).
1069
928
  * @see ECMA-376 Part 1 §21.2.3.10
1070
929
  */
1071
930
  const DISPLAY_BLANKS_AS = [
@@ -1078,7 +937,7 @@ const DISPLAY_BLANKS_AS = [
1078
937
  *
1079
938
  * The schema also has `plus`, `star`, `x`, `picture` and `auto`. Neither PowerPoint 2013 nor
1080
939
  * PowerPoint Online draws the first three, `picture` needs a `c:spPr` blip this API has no
1081
- * spelling for, and `auto` is what leaving the option off already means — so the accepted set is
940
+ * spelling for, and `auto` is what leaving the option off already means, so the accepted set is
1082
941
  * deliberately smaller than the type. Widen it only with evidence that the member renders.
1083
942
  * @see ECMA-376 Part 1 §21.2.3.27
1084
943
  */
@@ -1091,25 +950,25 @@ const LINE_DATA_SYMBOLS = [
1091
950
  "square",
1092
951
  "triangle"
1093
952
  ];
1094
- /** `ST_TickLblPos` — where an axis draws its tick labels (`c:tickLblPos/@val`). */
953
+ /** `ST_TickLblPos`: where an axis draws its tick labels (`c:tickLblPos/@val`). */
1095
954
  const TICK_LABEL_POSITIONS = [
1096
955
  "high",
1097
956
  "low",
1098
957
  "nextTo",
1099
958
  "none"
1100
959
  ];
1101
- /** `ST_Orientation` — which way an axis runs (`c:orientation/@val`). */
960
+ /** `ST_Orientation`: which way an axis runs (`c:orientation/@val`). */
1102
961
  const AXIS_ORIENTATIONS = ["maxMin", "minMax"];
1103
- /** `ST_TickMark` — an axis' major or minor tick marks (`c:majorTickMark/@val`, `c:minorTickMark/@val`). */
962
+ /** `ST_TickMark`: an axis' major or minor tick marks (`c:majorTickMark/@val`, `c:minorTickMark/@val`). */
1104
963
  const TICK_MARKS = [
1105
964
  "cross",
1106
965
  "in",
1107
966
  "none",
1108
967
  "out"
1109
968
  ];
1110
- /** `ST_CrossBetween` — whether a value axis crosses its category axis between or on categories (`c:crossBetween/@val`). */
969
+ /** `ST_CrossBetween`: whether a value axis crosses its category axis between or on categories (`c:crossBetween/@val`). */
1111
970
  const CROSS_BETWEEN = ["between", "midCat"];
1112
- /** `ST_BuiltInUnit` — the unit a value axis displays its labels in (`c:builtInUnit/@val`). */
971
+ /** `ST_BuiltInUnit`: the unit a value axis displays its labels in (`c:builtInUnit/@val`). */
1113
972
  const BUILT_IN_UNITS = [
1114
973
  "hundreds",
1115
974
  "thousands",
@@ -1121,21 +980,21 @@ const BUILT_IN_UNITS = [
1121
980
  "billions",
1122
981
  "trillions"
1123
982
  ];
1124
- /** `ST_TimeUnit` — a date axis' base, major or minor time unit (`c:baseTimeUnit/@val` and its two siblings). */
983
+ /** `ST_TimeUnit`: a date axis' base, major or minor time unit (`c:baseTimeUnit/@val` and its two siblings). */
1125
984
  const TIME_UNITS = [
1126
985
  "days",
1127
986
  "months",
1128
987
  "years"
1129
988
  ];
1130
- /** `ST_ErrDir` — the axis an error bar measures along (`c:errDir/@val`). */
989
+ /** `ST_ErrDir`: the axis an error bar measures along (`c:errDir/@val`). */
1131
990
  const ERROR_BAR_DIRECTIONS = ["x", "y"];
1132
- /** `ST_ErrBarType` — which sides of a point draw an error bar (`c:errBarType/@val`). */
991
+ /** `ST_ErrBarType`: which sides of a point draw an error bar (`c:errBarType/@val`). */
1133
992
  const ERROR_BAR_TYPES = [
1134
993
  "both",
1135
994
  "minus",
1136
995
  "plus"
1137
996
  ];
1138
- /** `ST_ErrValType` — how an error bar's amount is read (`c:errValType/@val`). */
997
+ /** `ST_ErrValType`: how an error bar's amount is read (`c:errValType/@val`). */
1139
998
  const ERROR_BAR_VALUE_TYPES = [
1140
999
  "cust",
1141
1000
  "fixedVal",
@@ -1146,7 +1005,7 @@ const ERROR_BAR_VALUE_TYPES = [
1146
1005
  /**
1147
1006
  * The twelve theme colour slots (`a:clrScheme` children) a `p:clrMap` token can point at.
1148
1007
  *
1149
- * Not an `ST_` type — these are element names inside `a:CT_ColorScheme`, which the schema
1008
+ * Not an `ST_` type: these are element names inside `a:CT_ColorScheme`, which the schema
1150
1009
  * declares as a fixed sequence rather than an enumeration. The effect is the same: a closed,
1151
1010
  * ordered set of twelve names, in the order the scheme declares them.
1152
1011
  * @see ECMA-376 Part 1 §20.1.6.2
@@ -1170,7 +1029,7 @@ const THEME_COLOR_SLOTS = [
1170
1029
  *
1171
1030
  * A slide's `schemeClr val="tx1"` names a *token*, not a slot: the master's `p:clrMap` is the
1172
1031
  * indirection that sends it to a {@link ThemeColorSlot}. Which is why the two lists look alike
1173
- * and are not the same list — `bg1`/`tx1` are tokens, `lt1`/`dk1` are slots.
1032
+ * and are not the same list: `bg1`/`tx1` are tokens, `lt1`/`dk1` are slots.
1174
1033
  * @see ECMA-376 Part 1 §19.3.1.6
1175
1034
  */
1176
1035
  const COLOR_MAP_TOKENS = [
@@ -1192,8 +1051,8 @@ const COLOR_MAP_TOKENS = [
1192
1051
  * and the one the script printer recognises so it can skip printing a map that is the default.
1193
1052
  *
1194
1053
  * "Identity" up to the light/dark naming: each token points at the slot of the same name, with
1195
- * `bg`/`tx` reading as light/dark. A deck that maps them anywhere else — swapping the light and
1196
- * dark slots is the usual case — resolves every `schemeClr` in the deck differently.
1054
+ * `bg`/`tx` reading as light/dark. A deck that maps them anywhere else (swapping the light and
1055
+ * dark slots is the usual case) resolves every `schemeClr` in the deck differently.
1197
1056
  *
1198
1057
  * Typed `Record<ColorMapToken, ThemeColorSlot>` so a token added to {@link COLOR_MAP_TOKENS}
1199
1058
  * without a mapping, or a slot misspelled, is a compile error rather than a theme that opens
@@ -1214,182 +1073,339 @@ const DEFAULT_COLOR_MAP = {
1214
1073
  hlink: "hlink",
1215
1074
  folHlink: "folHlink"
1216
1075
  };
1217
- //#endregion
1218
- //#region src/hex-color.ts
1219
- /**
1220
- * pptx-ts: hex colour text, in the one spelling both halves use.
1221
- *
1222
- * A caller may write a colour with or without a leading `#`, and the library accepts both
1223
- * everywhere — which means every site that validates or parses one strips it first. Those
1224
- * strips had drifted into three spellings across six sites, and one of them was not
1225
- * anchored: `.replace('#', '')` takes the `#` out of `'FF00#0'` and hands `'FF000'` on, which
1226
- * then fails the six-digit test and takes a different branch than the anchored forms would.
1227
- * No caller is known to reach that, which is exactly why it wants one spelling rather than a
1228
- * test at each site.
1229
- *
1230
- * This lives at the root rather than under `gen/` or `read/` because both halves need it:
1231
- * the emitters strip on the way in, the readers strip on the way back out — and for the same
1232
- * reason the *validation* belongs here too. `REGEX_HEX_COLOR` used to live in
1233
- * `constants-internal.ts`, which is a write-side module the read path cannot import, so the read
1234
- * path re-spelled the regex; so did one write-side site. Six sites, three spellings of one fact.
1235
- */
1076
+ // src/ooxml/namespaces.ts
1236
1077
  /**
1237
- * A colour written as six hex digits, `#` already stripped.
1078
+ * OOXML namespace URIs, by their canonical prefix.
1238
1079
  *
1239
- * Not exported: {@link isHexColor} is the whole of what the call sites want, and a shared mutable
1240
- * `RegExp` object is a worse export than a predicate.
1241
- */
1242
- const REGEX_HEX_COLOR = /^[0-9a-fA-F]{6}$/;
1243
- /** An 8-hex RGBA colour, `#` already stripped — {@link splitRgbaHex}'s test. */
1244
- const REGEX_ARGB_HEX = /^[0-9a-fA-F]{8}$/;
1245
- /** Strip a single leading `#` from a colour, leaving any other character alone. */
1246
- function stripHash(value) {
1247
- return value.startsWith("#") ? value.slice(1) : value;
1248
- }
1249
- /**
1250
- * Is `value` a 6-hex RGB colour? A leading `#` is stripped first, so both spellings agree.
1080
+ * A namespace URI is a fact about the format, not about reading or writing one, so it belongs
1081
+ * here rather than on either side, by the same reasoning `rel-types.ts` states at length, and for
1082
+ * the same failure mode: these are long, near-identical strings, and a typo does not throw. It
1083
+ * silently matches nothing on the read side and produces a part PowerPoint ignores on the write
1084
+ * side.
1251
1085
  *
1252
- * This is the *shape* test only. Whether the caller may also name a scheme colour there is the
1253
- * call site's question, and each still asks it: `createColorElement` falls back to the default
1254
- * font colour, `makeXmlTheme` keeps the Office default and warns, `normalizeHex` throws.
1255
- * @param value - the caller's colour, with or without a leading `#`
1086
+ * The table lived in `read/oxml/dom.ts` and could not be reached from `gen/`, because that
1087
+ * module imports `@xmldom/xmldom` at module scope and pulling an XML DOM into the write-only
1088
+ * bundle is a real cost. So the write side hand-wrote the URIs instead: three separate `A_NS`
1089
+ * constants and seven copies of the same `xmlns:a`/`xmlns:r`/`xmlns:p` triple. This module has
1090
+ * **no runtime imports**, and no module beside it imports from `gen/` or `read/`, so both sides can
1091
+ * reach it and neither pays for the other's dependencies. The two that fail loudly reach outside
1092
+ * `ooxml/` for that alone: `sequence.ts` for `errors.ts`, and `check-enum.ts` for `errors.ts` and
1093
+ * `diagnostics.ts`. `read/oxml/dom.ts` re-exports it, so no read-side import path changed.
1256
1094
  */
1257
- function isHexColor(value) {
1258
- return REGEX_HEX_COLOR.test(stripHash(value));
1259
- }
1095
+ /** Canonical OOXML prefix → namespace URI registry. */
1096
+ const OOXML_NS = Object.freeze({
1097
+ a: "http://schemas.openxmlformats.org/drawingml/2006/main",
1098
+ a14: "http://schemas.microsoft.com/office/drawing/2010/main",
1099
+ ahyp: "http://schemas.microsoft.com/office/drawing/2018/hyperlinkcolor",
1100
+ asvg: "http://schemas.microsoft.com/office/drawing/2016/SVG/main",
1101
+ c: "http://schemas.openxmlformats.org/drawingml/2006/chart",
1102
+ c15: "http://schemas.microsoft.com/office/drawing/2012/chart",
1103
+ cp: "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
1104
+ ct: "http://schemas.openxmlformats.org/package/2006/content-types",
1105
+ cx: "http://schemas.microsoft.com/office/drawing/2014/chartex",
1106
+ dc: "http://purl.org/dc/elements/1.1/",
1107
+ dcterms: "http://purl.org/dc/terms/",
1108
+ dgm: "http://schemas.openxmlformats.org/drawingml/2006/diagram",
1109
+ dsp: "http://schemas.microsoft.com/office/drawing/2008/diagram",
1110
+ ep: "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties",
1111
+ m: "http://schemas.openxmlformats.org/officeDocument/2006/math",
1112
+ mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
1113
+ p: "http://schemas.openxmlformats.org/presentationml/2006/main",
1114
+ p14: "http://schemas.microsoft.com/office/powerpoint/2010/main",
1115
+ p15: "http://schemas.microsoft.com/office/powerpoint/2012/main",
1116
+ p159: "http://schemas.microsoft.com/office/powerpoint/2015/9/main",
1117
+ p188: "http://schemas.microsoft.com/office/powerpoint/2018/8/main",
1118
+ pr: "http://schemas.openxmlformats.org/package/2006/relationships",
1119
+ r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
1120
+ vt: "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"
1121
+ });
1260
1122
  /**
1261
- * Is `value` an 8-hex RGBA colour? A leading `#` is stripped first.
1123
+ * `a:graphicData/@uri` for a DrawingML table.
1262
1124
  *
1263
- * The 8-digit form is RGB followed by one alpha byte; {@link splitRgbaHex} is what takes it apart.
1264
- * @param value - the caller's colour, with or without a leading `#`
1125
+ * The other three payloads a `p:graphicFrame` can hold name a URI that is also a namespace, so
1126
+ * they read it off {@link OOXML_NS} (`c`, `cx`, `dgm`). A table's does not: nothing binds a
1127
+ * prefix to it, and the table elements themselves are in the `a` namespace. So it lives beside
1128
+ * the registry rather than in it, which keeps every entry there a prefix a document can declare.
1265
1129
  */
1266
- function isArgbHex(value) {
1267
- return REGEX_ARGB_HEX.test(stripHash(value));
1268
- }
1130
+ const TABLE_GRAPHIC_DATA_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";
1269
1131
  /**
1270
- * Split an 8-hex RGBA colour into its 6-hex RGB and a 0–1 alpha; anything else passes through
1271
- * unchanged with no alpha.
1132
+ * The `xmlns` declarations every PresentationML part root carries, in the order the emitters
1133
+ * have always written them.
1272
1134
  *
1273
- * `#` is stripped first, so both spellings reach the same answer. Two sites parsed this by
1274
- * hand — `createColorElement`, which turns the alpha into an `<a:alpha>` sibling, and
1275
- * `normalizeShadowOptions`, which turns it into a shadow's `_alpha` — and they divided by 255 at
1276
- * different points in their arithmetic.
1277
- * @param value - the caller's colour, with or without a leading `#`
1135
+ * Spread this **first** in a root element's attribute map: `el()` serializes attributes in
1136
+ * object-key order, so the three keys have to keep the positions the literals occupied or the
1137
+ * emitted bytes move. A root with extra attributes (`show` on `p:sld`, `def` on `a:tblStyleLst`)
1138
+ * lists them after the spread, which is where they already were.
1278
1139
  */
1279
- function splitRgbaHex(value) {
1280
- const hex = stripHash(value);
1281
- if (!isArgbHex(hex)) return { rgb: hex };
1282
- return {
1283
- rgb: hex.slice(0, 6),
1284
- alpha: parseInt(hex.slice(6, 8), 16) / 255
1285
- };
1286
- }
1287
- //#endregion
1288
- //#region src/ooxml/xsd-boolean.ts
1140
+ const PML_ROOT_NS = Object.freeze({
1141
+ "xmlns:a": OOXML_NS.a,
1142
+ "xmlns:r": OOXML_NS.r,
1143
+ "xmlns:p": OOXML_NS.p
1144
+ });
1145
+ // src/ooxml/rel-types.ts
1289
1146
  /**
1290
- * Parsing an `xsd:boolean` attribute value.
1147
+ * OPC relationship type URIs and part content types, and the schema-URI bases they are built from.
1291
1148
  *
1292
- * **Why a function at all.** `xsd:boolean` has four lexical forms — `1`, `0`, `true`, `false` —
1293
- * and OOXML attributes typed with it accept every one of them. PowerPoint writes only `1` and
1294
- * `0`, so a hand-rolled `=== '1'` test passes against every deck this library produces and
1295
- * against every deck PowerPoint produces, and silently misreads the other producers. That is the
1296
- * worst shape a bug can have: invisible to the fixtures, visible only on a customer's file.
1149
+ * These are long, near-identical strings that differ in one path segment, which makes a typo
1150
+ * both easy to write and invisible on review: a wrong URI does not throw, it silently matches
1151
+ * nothing on the read side and produces a part PowerPoint ignores or rejects on the write side.
1297
1152
  *
1298
- * **Why this module is neither `gen/` nor `read/`.** The lexical space of a schema type is a fact
1299
- * about the schema, like `sequence.ts`'s document order and `body-insets.ts`'s defaults. The
1300
- * parser lived in `read/oxml/dom.ts` because reading is where most of the demand is, and three
1301
- * private copies had collapsed into it — but `read/oxml/dom.ts` imports `@xmldom/xmldom`, so
1302
- * anything outside `read/` that wants this five-line function has to take a DOM dependency to get
1303
- * it, or write a fourth copy. `script/from-read/detect.ts` hit exactly that and wrote the copy.
1153
+ * **Why this module is neither `gen/` nor `read/`.** Both halves need the same URIs, and for a
1154
+ * while each kept its own copy (`gen/oxml/schema-uris.ts`, `read/api/rel-types.ts`), with a note
1155
+ * in both saying a shared home was "a separate decision". It is this module: a rel type is a fact
1156
+ * about the OOXML package format, not about writing or reading one, so neither side should own it.
1157
+ * A divergence between the two copies would mean the writer emitting a rel the reader cannot find,
1158
+ * a round-trip bug with no compile-time signal.
1304
1159
  *
1305
- * The DOM-typed convenience wrapper stays behind in `read/oxml/dom.ts` as `boolAttr`, because
1306
- * *that* one really is about reading an element.
1160
+ * Only types used by more than one module live here. A rel type exactly one module cares about
1161
+ * (`tags`, `oleObject`, the modern comments part, …) stays declared next to the code that reads or
1162
+ * emits it. Hoisting those would trade a definition you can see for one you have to go and find,
1163
+ * and buy nothing. A writer and a reader of the same part count as two modules even when each
1164
+ * names the string once: that pair is exactly the divergence this module exists to prevent.
1307
1165
  *
1308
- * **The write direction is here too**, and it is where the interesting distinction lives: an
1309
- * emitter writing a boolean has THREE useful shapes, not one — see {@link xsdBool} and
1310
- * {@link xsdBoolIfTrue}. Sixty-odd inline ternaries spelled them in three ways with nothing
1311
- * naming which was which.
1166
+ * The same reasoning covers **part content types**, which are the same kind of fact and fail the
1167
+ * same silent way, including every entry the writer's `[Content_Types].xml` declares.
1312
1168
  */
1313
1169
  /**
1314
- * Parse an `xsd:boolean` OOXML attribute (`1`/`0`/`true`/`false`); else `null`.
1315
- *
1316
- * `null` for an absent *or* unparseable value is deliberate and load-bearing at the call sites:
1317
- * several attributes have a schema default of `true`, so they test `!== false` rather than
1318
- * `=== true` and an unset attribute has to be distinguishable from an explicit `0`.
1170
+ * Prefix for `.rels` `Type` URIs; append the type name (`slide`, `image`, `theme`, …).
1171
+ * Exported for the write side, which builds one-off rel types at their call sites.
1319
1172
  */
1320
- function boolValue(value) {
1321
- if (value === "1" || value === "true") return true;
1322
- if (value === "0" || value === "false") return false;
1323
- return null;
1324
- }
1173
+ const OFFICE_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/";
1174
+ /** `xmlns` of every `<Relationships>` part. Also the prefix of the package-scoped rel types. */
1175
+ const PACKAGE_REL_NS = OOXML_NS.pr;
1176
+ /**
1177
+ * Prefix for the Microsoft rel types this library emits and reads. Exported for the same reason
1178
+ * as {@link OFFICE_REL}: a rel type only one module names is built from it at that module's call site.
1179
+ */
1180
+ const MS_REL = "http://schemas.microsoft.com/office/";
1181
+ /** `p:sldIdLst` → a slide part. */
1182
+ const SLIDE_REL = OFFICE_REL + "slide";
1183
+ /** A slide → its layout; a master → each of its layouts. */
1184
+ const SLIDE_LAYOUT_REL = OFFICE_REL + "slideLayout";
1185
+ /** A layout → its master; the presentation → each master. */
1186
+ const SLIDE_MASTER_REL = OFFICE_REL + "slideMaster";
1187
+ /** A slide → its notes slide. */
1188
+ const NOTES_SLIDE_REL = OFFICE_REL + "notesSlide";
1189
+ /** A notes slide → the notes master; the presentation → the notes master. */
1190
+ const NOTES_MASTER_REL = OFFICE_REL + "notesMaster";
1191
+ /** A master or notes master → its theme. Layouts and slides inherit it rather than holding one. */
1192
+ const THEME_REL = OFFICE_REL + "theme";
1193
+ /** The package root → `ppt/presentation.xml`. The entry point to everything above. */
1194
+ const OFFICE_DOCUMENT_REL = OFFICE_REL + "officeDocument";
1195
+ /** Any part → an image in `ppt/media/`. */
1196
+ const IMAGE_REL = OFFICE_REL + "image";
1197
+ /** Any part → an external or internal jump target. External ones carry `TargetMode="External"`. */
1198
+ const HYPERLINK_REL = OFFICE_REL + "hyperlink";
1199
+ /** The presentation → `ppt/tableStyles.xml`. */
1200
+ const TABLE_STYLES_REL = OFFICE_REL + "tableStyles";
1201
+ /** The package root → `docProps/core.xml`. Package-scoped, so not under {@link OFFICE_REL}. */
1202
+ const CORE_PROPS_REL = PACKAGE_REL_NS + "/metadata/core-properties";
1203
+ /** The package root → `docProps/app.xml`. */
1204
+ const EXTENDED_PROPS_REL = OFFICE_REL + "extended-properties";
1205
+ /** The package root → `docProps/custom.xml`. */
1206
+ const CUSTOM_PROPS_REL = OFFICE_REL + "custom-properties";
1207
+ /** A slide → its legacy comments part (`ppt/comments/commentN.xml`). */
1208
+ const COMMENTS_REL = OFFICE_REL + "comments";
1209
+ /** The presentation → the deck-wide legacy comment-author registry (`ppt/commentAuthors.xml`). */
1210
+ const COMMENT_AUTHORS_REL = OFFICE_REL + "commentAuthors";
1211
+ /** The presentation → the 2018 comment-author registry (`ppt/authors.xml`), a Microsoft rel type. */
1212
+ const MODERN_COMMENT_AUTHORS_REL = MS_REL + "2018/10/relationships/authors";
1213
+ /** The presentation → `ppt/presProps.xml`. */
1214
+ const PRES_PROPS_REL = OFFICE_REL + "presProps";
1215
+ /** The presentation → `ppt/viewProps.xml`. */
1216
+ const VIEW_PROPS_REL = OFFICE_REL + "viewProps";
1217
+ /** The presentation → the handout master. */
1218
+ const HANDOUT_MASTER_REL = OFFICE_REL + "handoutMaster";
1219
+ /** A part → a theme override, which replaces the deck theme for that part alone. */
1220
+ const THEME_OVERRIDE_REL = OFFICE_REL + "themeOverride";
1221
+ /** The presentation → an embedded font part, one per face. */
1222
+ const FONT_REL = OFFICE_REL + "font";
1223
+ /** A slide → an audio blob in `ppt/media/`. Paired with {@link MS_MEDIA_REL} on the same target. */
1224
+ const AUDIO_REL = OFFICE_REL + "audio";
1225
+ /** A slide → a video blob in `ppt/media/`, or an online-video placeholder. Paired with {@link MS_MEDIA_REL}. */
1226
+ const VIDEO_REL = OFFICE_REL + "video";
1325
1227
  /**
1326
- * A boolean as the `1`/`0` an `xsd:boolean` attribute takes.
1327
- *
1328
- * Writes the attribute either way, so `false` is *stated*. Use this wherever the schema
1329
- * default is `true`, or wherever the absence of the attribute would mean something other
1330
- * than `false` — a value inherited from a list style, a layout or the theme.
1331
- * @param value - the resolved boolean
1228
+ * The MS-2007 `media` rel every audio/video shape carries alongside the ECMA one, pointing at the
1229
+ * same target. It is what `<p14:media r:embed>` resolves, so a media shape needs both.
1332
1230
  */
1333
- function xsdBool(value) {
1334
- return value ? "1" : "0";
1335
- }
1231
+ const MS_MEDIA_REL = MS_REL + "2007/relationships/media";
1232
+ /** A slide → an embedded 3D model (`.glb`). Note `2017/06`, not the `2017` of the model3d namespace. */
1233
+ const MODEL3D_REL = MS_REL + "2017/06/relationships/model3d";
1234
+ /** A slide → a classic (`c:`) chart part. */
1235
+ const CHART_REL = OFFICE_REL + "chart";
1236
+ /** A slide → a chartEx (`cx:`, Office 2016) chart part: an MS rel, not the ECMA `chart` one. */
1237
+ const CHARTEX_REL = MS_REL + "2014/relationships/chartEx";
1238
+ /** A chart part → its mandatory style sidecar. */
1239
+ const CHART_STYLE_REL = MS_REL + "2011/relationships/chartStyle";
1240
+ /** A chart part → its mandatory colour-style sidecar. */
1241
+ const CHART_COLOR_STYLE_REL = MS_REL + "2011/relationships/chartColorStyle";
1242
+ /** Any part → an embedded OPC package (an Office file, itself a zip): a chart's workbook, an OLE payload. */
1243
+ const PACKAGE_REL = OFFICE_REL + "package";
1336
1244
  /**
1337
- * A boolean as `1`, or `null` so the attribute is left out entirely.
1338
- *
1339
- * **Correct only where the schema default is `false`.** It collapses "false" and "unset" into
1340
- * one output, which is the two-spellings-for-three-states shape the API policy warns about: the
1341
- * next attribute with a `true` default written this way reads as correct and is wrong. Every
1342
- * current caller is on a `false`-defaulting attribute, so the collapse costs nothing today —
1343
- * naming it is what makes the next one visible.
1344
- * @param value - the resolved boolean
1245
+ * Root of the ECMA-376 content types. Exported for a content type only one module names, which is
1246
+ * built from it at that module's call site; a shared one is a constant below.
1345
1247
  */
1346
- function xsdBoolIfTrue(value) {
1347
- return value ? "1" : null;
1348
- }
1349
- //#endregion
1350
- //#region src/ooxml/check-enum.ts
1248
+ const OD_CONTENT = "application/vnd.openxmlformats-officedocument.";
1249
+ /** Root of the OPC package-level content types. Private: callers want one of the constants below. */
1250
+ const PACKAGE_CONTENT = "application/vnd.openxmlformats-package.";
1251
+ /** Root of the Microsoft Office content types the chartEx family uses. Private. */
1252
+ const MS_OFFICE_CONTENT = "application/vnd.ms-office.";
1253
+ /** Root of the PowerPoint-specific Microsoft content types. Exported for the same reason as {@link OD_CONTENT}. */
1254
+ const MS_POWERPOINT_CONTENT = "application/vnd.ms-powerpoint.";
1255
+ /** `ppt/slides/slideN.xml`. */
1256
+ const SLIDE_CONTENT_TYPE = OD_CONTENT + "presentationml.slide+xml";
1257
+ /** `ppt/slideMasters/slideMasterN.xml`. */
1258
+ const SLIDE_MASTER_CONTENT_TYPE = OD_CONTENT + "presentationml.slideMaster+xml";
1259
+ /** `ppt/slideLayouts/slideLayoutN.xml`. */
1260
+ const SLIDE_LAYOUT_CONTENT_TYPE = OD_CONTENT + "presentationml.slideLayout+xml";
1261
+ /** `ppt/notesSlides/notesSlideN.xml`. */
1262
+ const NOTES_SLIDE_CONTENT_TYPE = OD_CONTENT + "presentationml.notesSlide+xml";
1263
+ /** `ppt/notesMasters/notesMasterN.xml`. */
1264
+ const NOTES_MASTER_CONTENT_TYPE = OD_CONTENT + "presentationml.notesMaster+xml";
1265
+ /** `ppt/theme/themeN.xml`. Not `presentationml.`: a theme is a DrawingML part. */
1266
+ const THEME_CONTENT_TYPE = OD_CONTENT + "theme+xml";
1267
+ /** `ppt/tableStyles.xml`. */
1268
+ const TABLE_STYLES_CONTENT_TYPE = OD_CONTENT + "presentationml.tableStyles+xml";
1269
+ /** `ppt/presentation.xml` in an editable `.pptx`. A `.potx` template's main part is a different one. */
1270
+ const PRESENTATION_MAIN_CONTENT_TYPE = OD_CONTENT + "presentationml.presentation.main+xml";
1271
+ /** Every `.rels` part. Declared once as a `Default` for the `rels` extension, never as an Override. */
1272
+ const RELATIONSHIPS_CONTENT_TYPE = PACKAGE_CONTENT + "relationships+xml";
1273
+ /** `ppt/presProps.xml`. */
1274
+ const PRES_PROPS_CONTENT_TYPE = OD_CONTENT + "presentationml.presProps+xml";
1275
+ /** `ppt/viewProps.xml`. */
1276
+ const VIEW_PROPS_CONTENT_TYPE = OD_CONTENT + "presentationml.viewProps+xml";
1277
+ /** `ppt/comments/commentN.xml`, a legacy comments part. */
1278
+ const COMMENTS_CONTENT_TYPE = OD_CONTENT + "presentationml.comments+xml";
1279
+ /** `ppt/commentAuthors.xml`. */
1280
+ const COMMENT_AUTHORS_CONTENT_TYPE = OD_CONTENT + "presentationml.commentAuthors+xml";
1281
+ /** `docProps/core.xml`. Package-scoped, so not under the officedocument prefix. */
1282
+ const CORE_PROPS_CONTENT_TYPE = PACKAGE_CONTENT + "core-properties+xml";
1283
+ /** `docProps/app.xml`. */
1284
+ const EXTENDED_PROPS_CONTENT_TYPE = OD_CONTENT + "extended-properties+xml";
1285
+ /** `docProps/custom.xml`. */
1286
+ const CUSTOM_PROPS_CONTENT_TYPE = OD_CONTENT + "custom-properties+xml";
1287
+ /** `ppt/charts/chartN.xml`, a classic (`c:`) chart. */
1288
+ const CHART_CONTENT_TYPE = OD_CONTENT + "drawingml.chart+xml";
1289
+ /** `ppt/charts/chartExN.xml`. A chartEx part carries a Microsoft content type, not an `openxmlformats` one. */
1290
+ const CHARTEX_CONTENT_TYPE = MS_OFFICE_CONTENT + "chartex+xml";
1291
+ /** A chart's mandatory `style{N}.xml` sidecar. */
1292
+ const CHART_STYLE_CONTENT_TYPE = MS_OFFICE_CONTENT + "chartstyle+xml";
1293
+ /** A chart's mandatory `colors{N}.xml` sidecar. */
1294
+ const CHART_COLOR_STYLE_CONTENT_TYPE = MS_OFFICE_CONTENT + "chartcolorstyle+xml";
1295
+ /** An embedded `.xlsx` workbook: a chart's data, or an OLE payload. */
1296
+ const XLSX_CONTENT_TYPE = OD_CONTENT + "spreadsheetml.sheet";
1351
1297
  /**
1352
- * Check a value against its `ST_` union, reporting and dropping it when outside (the write-path
1353
- * policy — see the module note).
1354
- *
1355
- * `undefined`/`null` returns `null` without reporting: an unset option is not an invalid one, and
1356
- * it is by far the common case.
1298
+ * Every kind of part a page points at without owning it.
1357
1299
  *
1358
- * The offending value is interpolated into the message deliberately. {@link warnOnce} dedups on
1359
- * code **and** message, so keeping the value in the text is what lets a second, *different* bad
1360
- * value under the same code still get reported.
1361
- * @param value - the value the caller supplied, if any
1362
- * @param valid - the enum's members
1363
- * @param code - the condition to report
1364
- * @param label - how to name the offending option in the message, e.g. ``'table cell: cell3D `preset`'``
1365
- * @param detail - extra structured context to merge into the diagnostic's `detail`
1366
- * @returns the value when legal, else `null`
1367
- */
1368
- function checkEnumOrWarn(value, valid, code, label, detail) {
1369
- if (value === void 0 || value === null) return null;
1370
- if (valid.includes(value)) return value;
1371
- warnOnce(code, `${label} value \`${String(value)}\` is not valid and is ignored — use one of ${valid.join(", ")}.`, {
1372
- received: value,
1373
- valid,
1374
- ...detail
1375
- });
1376
- return null;
1377
- }
1378
- /**
1379
- * Check a value against its `ST_` union, throwing when outside (the read/edit-path policy — see
1380
- * the module note).
1381
- * @param value - the value the caller asked for
1382
- * @param valid - the enum's members
1383
- * @param attribute - the attribute's name, for the message
1384
- * @param code - the condition to report
1385
- * @returns `value`, when it is legal
1300
+ * Two questions read this table, under different keys. Copying a page asks whether a relationship's
1301
+ * target may be shared by the copies rather than copied (every row, by `relType`). Pruning after a
1302
+ * slide is removed asks whether a part is deck chrome that stays even while nothing reaches it (the
1303
+ * `deck` rows, by `contentType`). They were two hand-kept lists, and the two author registries were
1304
+ * on the first and missing from the second.
1386
1305
  */
1387
- function checkEnumOrThrow(value, valid, attribute, code) {
1388
- if (valid.includes(value)) return value;
1389
- throw new InvalidOptionError(code, `Invalid ${attribute}: ${JSON.stringify(value)}. Expected one of: ${valid.join(", ")}.`);
1390
- }
1391
- //#endregion
1392
- //#region src/ooxml/line-cap.ts
1306
+ const SHARED_PARTS = [
1307
+ {
1308
+ relType: SLIDE_LAYOUT_REL,
1309
+ contentType: SLIDE_LAYOUT_CONTENT_TYPE,
1310
+ scope: "deck"
1311
+ },
1312
+ {
1313
+ relType: SLIDE_MASTER_REL,
1314
+ contentType: SLIDE_MASTER_CONTENT_TYPE,
1315
+ scope: "deck"
1316
+ },
1317
+ {
1318
+ relType: NOTES_MASTER_REL,
1319
+ contentType: NOTES_MASTER_CONTENT_TYPE,
1320
+ scope: "deck"
1321
+ },
1322
+ {
1323
+ relType: THEME_REL,
1324
+ contentType: THEME_CONTENT_TYPE,
1325
+ scope: "deck"
1326
+ },
1327
+ {
1328
+ relType: THEME_OVERRIDE_REL,
1329
+ contentType: OD_CONTENT + "themeOverride+xml",
1330
+ scope: "deck"
1331
+ },
1332
+ {
1333
+ relType: HANDOUT_MASTER_REL,
1334
+ contentType: OD_CONTENT + "presentationml.handoutMaster+xml",
1335
+ scope: "deck"
1336
+ },
1337
+ {
1338
+ relType: PRES_PROPS_REL,
1339
+ contentType: PRES_PROPS_CONTENT_TYPE,
1340
+ scope: "deck"
1341
+ },
1342
+ {
1343
+ relType: VIEW_PROPS_REL,
1344
+ contentType: VIEW_PROPS_CONTENT_TYPE,
1345
+ scope: "deck"
1346
+ },
1347
+ {
1348
+ relType: TABLE_STYLES_REL,
1349
+ contentType: TABLE_STYLES_CONTENT_TYPE,
1350
+ scope: "deck"
1351
+ },
1352
+ {
1353
+ relType: OFFICE_DOCUMENT_REL,
1354
+ contentType: PRESENTATION_MAIN_CONTENT_TYPE,
1355
+ scope: "deck"
1356
+ },
1357
+ {
1358
+ relType: COMMENT_AUTHORS_REL,
1359
+ contentType: COMMENT_AUTHORS_CONTENT_TYPE,
1360
+ scope: "deck"
1361
+ },
1362
+ {
1363
+ relType: MODERN_COMMENT_AUTHORS_REL,
1364
+ contentType: "application/vnd.ms-powerpoint.authors+xml",
1365
+ scope: "deck"
1366
+ },
1367
+ {
1368
+ relType: IMAGE_REL,
1369
+ contentType: null,
1370
+ scope: "media"
1371
+ },
1372
+ {
1373
+ relType: AUDIO_REL,
1374
+ contentType: null,
1375
+ scope: "media"
1376
+ },
1377
+ {
1378
+ relType: VIDEO_REL,
1379
+ contentType: null,
1380
+ scope: "media"
1381
+ },
1382
+ {
1383
+ relType: MS_MEDIA_REL,
1384
+ contentType: null,
1385
+ scope: "media"
1386
+ },
1387
+ {
1388
+ relType: MODEL3D_REL,
1389
+ contentType: null,
1390
+ scope: "media"
1391
+ },
1392
+ {
1393
+ relType: FONT_REL,
1394
+ contentType: null,
1395
+ scope: "media"
1396
+ },
1397
+ {
1398
+ relType: SLIDE_REL,
1399
+ contentType: null,
1400
+ scope: "page"
1401
+ },
1402
+ {
1403
+ relType: HYPERLINK_REL,
1404
+ contentType: null,
1405
+ scope: "page"
1406
+ }
1407
+ ];
1408
+ // src/ooxml/line-cap.ts
1393
1409
  /**
1394
1410
  * Every `cap` the write API names and the token it writes. `satisfies` over `Record<LineCap, …>`
1395
1411
  * makes a member added to `LineCap` without a token a compile error, and `ST_LineCap` has exactly
@@ -1406,8 +1422,16 @@ function lineCapToken(cap) {
1406
1422
  }
1407
1423
  /** A `cap` token back to the `LineCap` that authors it. */
1408
1424
  const LINE_CAP_BY_TOKEN = Object.fromEntries(Object.entries(TOKEN_BY_LINE_CAP).map(([cap, token]) => [token, cap]));
1409
- //#endregion
1410
- //#region src/ooxml/text-align.ts
1425
+ // src/ooxml/show-jumps.ts
1426
+ const SHOW_JUMPS = [
1427
+ "firstslide",
1428
+ "previousslide",
1429
+ "nextslide",
1430
+ "lastslide",
1431
+ "lastslideviewed",
1432
+ "endshow"
1433
+ ];
1434
+ // src/ooxml/text-align.ts
1411
1435
  /**
1412
1436
  * Every `align` the write API names and the token it writes. `satisfies` over `Record<HAlign, …>`
1413
1437
  * makes a member added to `HAlign` without a token a compile error. `justLow`, `dist` and
@@ -1425,21 +1449,20 @@ function textAlignToken(align) {
1425
1449
  }
1426
1450
  /** An `algn` token back to the `align` a generated script writes. */
1427
1451
  const HALIGN_BY_TEXT_ALIGN = Object.fromEntries(Object.entries(ALGN_BY_ALIGN).map(([align, token]) => [token, align]));
1428
- //#endregion
1429
- //#region src/media/image-size.ts
1452
+ // src/media/image-size.ts
1430
1453
  /**
1431
1454
  * Intrinsic image geometry: reading a natural pixel size out of header bytes, and the
1432
1455
  * `<a:srcRect>` crop math that fits that natural size into a display box.
1433
1456
  *
1434
- * Shared by both sides of the library — the write path (`addImage({ sizing })`) and the read
1435
- * API's `Picture.setImage({ fit })` — which is why it lives outside `gen/` and `read/`.
1457
+ * Shared by both sides of the library, the write path (`addImage({ sizing })`) and the read
1458
+ * API's `Picture.setImage({ fit })`, which is why it lives outside `gen/` and `read/`.
1436
1459
  * Everything here is synchronous and pure: headers only, never pixel decoding.
1437
1460
  */
1438
1461
  /**
1439
1462
  * Read the intrinsic dimensions of an image from its header bytes.
1440
1463
  * - synchronous: parses only file-format headers, never decodes pixels
1441
- * - raster: PNG, JPEG, GIF, BMP, TIFF, and WebP (VP8 / VP8L / VP8X) — natural pixels
1442
- * - vector: SVG — intrinsic size from the root `<svg>` width/height or viewBox
1464
+ * - raster: PNG, JPEG, GIF, BMP, TIFF, and WebP (VP8 / VP8L / VP8X): natural pixels
1465
+ * - vector: SVG, with the intrinsic size from the root `<svg>` width/height or viewBox
1443
1466
  * - unrecognized formats return `null` (no measurable intrinsic size)
1444
1467
  *
1445
1468
  * Used by image `sizing: 'cover' | 'contain'` to compute an aspect-correct
@@ -1452,7 +1475,7 @@ function getImageSizeFromBase64(dataB64) {
1452
1475
  return b ? getImageSizeFromBytes(b) : null;
1453
1476
  }
1454
1477
  /**
1455
- * Read the intrinsic dimensions of an image from raw header bytes — the
1478
+ * Read the intrinsic dimensions of an image from raw header bytes: the
1456
1479
  * byte-level core shared by {@link getImageSizeFromBase64} and the read API's
1457
1480
  * `Picture.setImage({ fit })`, which already holds the media bytes.
1458
1481
  * @param {Uint8Array} b - image bytes
@@ -1461,7 +1484,7 @@ function getImageSizeFromBase64(dataB64) {
1461
1484
  function getImageSizeFromBytes(b) {
1462
1485
  if (!b || b.length < 24) return null;
1463
1486
  const u = (n) => b[n] ?? 0;
1464
- if (IMAGE_FORMATS.png.magic?.(b)) {
1487
+ if (IMAGE_FORMATS.png.magic(b)) {
1465
1488
  const w = u(16) << 24 | u(17) << 16 | u(18) << 8 | u(19);
1466
1489
  const h = u(20) << 24 | u(21) << 16 | u(22) << 8 | u(23);
1467
1490
  return w > 0 && h > 0 ? {
@@ -1469,7 +1492,7 @@ function getImageSizeFromBytes(b) {
1469
1492
  h
1470
1493
  } : null;
1471
1494
  }
1472
- if (IMAGE_FORMATS.gif.magic?.(b)) {
1495
+ if (IMAGE_FORMATS.gif.magic(b)) {
1473
1496
  const w = u(6) | u(7) << 8;
1474
1497
  const h = u(8) | u(9) << 8;
1475
1498
  return w > 0 && h > 0 ? {
@@ -1477,7 +1500,7 @@ function getImageSizeFromBytes(b) {
1477
1500
  h
1478
1501
  } : null;
1479
1502
  }
1480
- if (IMAGE_FORMATS.bmp.magic?.(b)) {
1503
+ if (IMAGE_FORMATS.bmp.magic(b)) {
1481
1504
  if ((u(14) | u(15) << 8 | u(16) << 16 | u(17) << 24) === 12) {
1482
1505
  const w = u(18) | u(19) << 8;
1483
1506
  const h = u(20) | u(21) << 8;
@@ -1496,7 +1519,7 @@ function getImageSizeFromBytes(b) {
1496
1519
  h: ah
1497
1520
  } : null;
1498
1521
  }
1499
- if (IMAGE_FORMATS.webp.magic?.(b)) {
1522
+ if (IMAGE_FORMATS.webp.magic(b)) {
1500
1523
  const fourCC = String.fromCharCode(u(12), u(13), u(14), u(15));
1501
1524
  if (fourCC === "VP8 " && b.length >= 30) {
1502
1525
  const w = (u(26) | u(27) << 8) & 16383;
@@ -1525,7 +1548,7 @@ function getImageSizeFromBytes(b) {
1525
1548
  }
1526
1549
  return null;
1527
1550
  }
1528
- if (IMAGE_FORMATS.jpeg.magic?.(b)) {
1551
+ if (IMAGE_FORMATS.jpeg.magic(b)) {
1529
1552
  let i = 2;
1530
1553
  while (i + 9 < b.length) {
1531
1554
  if (b[i] !== 255) {
@@ -1664,7 +1687,22 @@ function getSvgSizeFromMarkup(svg) {
1664
1687
  function utf8Decode(bytes) {
1665
1688
  return new TextDecoder().decode(bytes);
1666
1689
  }
1667
- //#endregion
1668
- export { TICK_LABEL_POSITIONS as $, THEME_CONTENT_TYPE as $t, GROUPINGS as A, MS_REL as At, RADAR_STYLE_ALIASES as B, PRESENTATION_MAIN_CONTENT_TYPE as Bt, CROSS_BETWEEN as C, FONT_REL as Ct, ERROR_BAR_DIRECTIONS as D, MODERN_COMMENT_AUTHORS_REL as Dt, EMBEDDED_FONT_SLOTS as E, MODEL3D_REL as Et, LINE_END_TYPES as F, OD_CONTENT as Ft, TEXT_CAPS_TYPES as G, SLIDE_CONTENT_TYPE as Gt, SCHEME_COLOR_VALUES as H, PRES_PROPS_REL as Ht, PATTERN_PRESETS as I, OFFICE_DOCUMENT_REL as It, TEXT_STRIKE_TYPES as J, SLIDE_MASTER_CONTENT_TYPE as Jt, TEXT_HORZ_OVERFLOW as K, SLIDE_LAYOUT_CONTENT_TYPE as Kt, PRESET_LINE_DASHES as L, OFFICE_REL as Lt, LIGHT_RIGS as M, NOTES_MASTER_REL as Mt, LIGHT_RIG_DIRECTIONS as N, NOTES_SLIDE_CONTENT_TYPE as Nt, ERROR_BAR_TYPES as O, MS_MEDIA_REL as Ot, LINE_DATA_SYMBOLS as P, NOTES_SLIDE_REL as Pt, THEME_COLOR_SLOTS as Q, TABLE_STYLES_REL as Qt, PRESET_MATERIALS as R, PACKAGE_REL as Rt, COLOR_MAP_TOKENS as S, EXTENDED_PROPS_REL as St, DISPLAY_BLANKS_AS as T, IMAGE_REL as Tt, TEXT_ANCHORS as U, RELATIONSHIPS_CONTENT_TYPE as Ut, RADAR_STYLE_ALIAS_NAMES as V, PRES_PROPS_CONTENT_TYPE as Vt, TEXT_AUTONUM_SCHEMES as W, SHARED_PARTS as Wt, TEXT_UNDERLINE_TYPES as X, SLIDE_REL as Xt, TEXT_TAB_ALIGN_TYPES as Y, SLIDE_MASTER_REL as Yt, TEXT_VERTICAL as Z, TABLE_STYLES_CONTENT_TYPE as Zt, BAR_3D_SHAPES as _, CORE_PROPS_CONTENT_TYPE as _t, textAlignToken as a, OOXML_NS as an, CHARTEX_CONTENT_TYPE as at, BEVEL_PRESETS as b, CUSTOM_PROPS_REL as bt, checkEnumOrThrow as c, bytesToBase64 as cn, CHART_COLOR_STYLE_REL as ct, xsdBool as d, svgMarkupToDataUri as dn, CHART_STYLE_CONTENT_TYPE as dt, THEME_REL as en, TICK_MARKS as et, xsdBoolIfTrue as f, toMediaDataUri as fn, CHART_STYLE_REL as ft, AXIS_ORIENTATIONS as g, COMMENT_AUTHORS_REL as gt, stripHash as h, imageFormatForExtension as hn, COMMENT_AUTHORS_CONTENT_TYPE as ht, HALIGN_BY_TEXT_ALIGN as i, XLSX_CONTENT_TYPE as in, AUDIO_REL as it, LEGEND_POSITIONS as j, NOTES_MASTER_CONTENT_TYPE as jt, ERROR_BAR_VALUE_TYPES as k, MS_POWERPOINT_CONTENT as kt, checkEnumOrWarn as l, decodeBase64ToBytes as ln, CHART_CONTENT_TYPE as lt, splitRgbaHex as m, imageFormatForContentType as mn, COMMENTS_REL as mt, getImageSizeFromBase64 as n, VIEW_PROPS_CONTENT_TYPE as nn, TRANSITION_TYPES as nt, LINE_CAP_BY_TOKEN as o, PML_ROOT_NS as on, CHARTEX_REL as ot, isHexColor as p, imageFormatForBytes as pn, COMMENTS_CONTENT_TYPE as pt, TEXT_SHAPE_TYPES as q, SLIDE_LAYOUT_REL as qt, getImageSizeFromBytes as r, VIEW_PROPS_REL as rn, TRANSITION_VARIANT_ATTRIBUTES as rt, lineCapToken as s, TABLE_GRAPHIC_DATA_URI as sn, CHART_COLOR_STYLE_CONTENT_TYPE as st, fitSrcRectPercents as t, VIDEO_REL as tn, TIME_UNITS as tt, boolValue as u, hasBase64Header as un, CHART_REL as ut, BAR_DIRECTIONS as v, CORE_PROPS_REL as vt, DEFAULT_COLOR_MAP as w, HYPERLINK_REL as wt, BUILT_IN_UNITS as x, EXTENDED_PROPS_CONTENT_TYPE as xt, BAR_GROUPINGS as y, CUSTOM_PROPS_CONTENT_TYPE as yt, RADAR_STYLES as z, PACKAGE_REL_NS as zt };
1690
+ // src/ooxml/chart-metadata.ts
1691
+ /**
1692
+ * The pptx-ts chart-metadata extension on `c:chartSpace/c:extLst`: the `c:ext/@uri` it is filed
1693
+ * under and the namespace of its payload.
1694
+ *
1695
+ * Here because both sides spell them: the writer stamps them from `addChart`'s `metadata`, and
1696
+ * `Chart.metadata` finds the extension by them. No runtime imports.
1697
+ */
1698
+ /**
1699
+ * Stable pptx-ts vendor GUID identifying the chart-metadata extension. Custom data rides under
1700
+ * this URI in a foreign namespace so PowerPoint preserves it (the extLst mechanism) instead of
1701
+ * stripping or repairing it as it would an unrecognised sibling element.
1702
+ */
1703
+ const CHART_METADATA_EXT_URI = "{094A432E-1F6C-499B-95B8-B57DC9536949}";
1704
+ /** Foreign namespace for the chart-metadata extension payload. */
1705
+ const CHART_METADATA_NS = "http://ts-pptx.com/schema/chart/metadata";
1706
+ export { SLIDE_LAYOUT_CONTENT_TYPE as $, TICK_LABEL_POSITIONS as $t, FONT_REL as A, LEGEND_POSITIONS as At, NOTES_SLIDE_CONTENT_TYPE as B, RADAR_STYLE_ALIASES as Bt, COMMENT_AUTHORS_REL as C, DEFAULT_COLOR_MAP as Ct, CUSTOM_PROPS_REL as D, ERROR_BAR_TYPES as Dt, CUSTOM_PROPS_CONTENT_TYPE as E, ERROR_BAR_DIRECTIONS as Et, MS_MEDIA_REL as F, PATTERN_PRESETS as Ft, PACKAGE_REL as G, TEXT_CAPS_TYPES as Gt, OD_CONTENT as H, SCHEME_COLOR_VALUES as Ht, MS_POWERPOINT_CONTENT as I, PLACEHOLDER_TYPES as It, PRES_PROPS_CONTENT_TYPE as J, TEXT_STRIKE_TYPES as Jt, PACKAGE_REL_NS as K, TEXT_HORZ_OVERFLOW as Kt, MS_REL as L, PRESET_LINE_DASHES as Lt, IMAGE_REL as M, LIGHT_RIG_DIRECTIONS as Mt, MODEL3D_REL as N, LINE_DATA_SYMBOLS as Nt, EXTENDED_PROPS_CONTENT_TYPE as O, ERROR_BAR_VALUE_TYPES as Ot, MODERN_COMMENT_AUTHORS_REL as P, LINE_END_TYPES as Pt, SLIDE_CONTENT_TYPE as Q, THEME_COLOR_SLOTS as Qt, NOTES_MASTER_CONTENT_TYPE as R, PRESET_MATERIALS as Rt, COMMENT_AUTHORS_CONTENT_TYPE as S, CROSS_BETWEEN as St, CORE_PROPS_REL as T, EMBEDDED_FONT_SLOTS as Tt, OFFICE_DOCUMENT_REL as U, TEXT_ANCHORS as Ut, NOTES_SLIDE_REL as V, RADAR_STYLE_ALIAS_NAMES as Vt, OFFICE_REL as W, TEXT_AUTONUM_SCHEMES as Wt, RELATIONSHIPS_CONTENT_TYPE as X, TEXT_UNDERLINE_TYPES as Xt, PRES_PROPS_REL as Y, TEXT_TAB_ALIGN_TYPES as Yt, SHARED_PARTS as Z, TEXT_VERTICAL as Zt, CHART_REL as _, imageFormatForBytes as _n, BAR_DIRECTIONS as _t, getImageSizeFromBytes as a, checkEnumOrWarn as an, TABLE_STYLES_REL as at, COMMENTS_CONTENT_TYPE as b, BUILT_IN_UNITS as bt, SHOW_JUMPS as c, xsdBoolIfTrue as cn, VIDEO_REL as ct, AUDIO_REL as d, stripHash as dn, XLSX_CONTENT_TYPE as dt, TICK_MARKS as en, SLIDE_LAYOUT_REL as et, CHARTEX_CONTENT_TYPE as f, bytesToBase64 as fn, OOXML_NS as ft, CHART_CONTENT_TYPE as g, toMediaDataUri as gn, BAR_3D_SHAPES as gt, CHART_COLOR_STYLE_REL as h, svgMarkupToDataUri as hn, AXIS_ORIENTATIONS as ht, getImageSizeFromBase64 as i, checkEnumOrThrow as in, TABLE_STYLES_CONTENT_TYPE as it, HYPERLINK_REL as j, LIGHT_RIGS as jt, EXTENDED_PROPS_REL as k, GROUPINGS as kt, LINE_CAP_BY_TOKEN as l, isHexColor as ln, VIEW_PROPS_CONTENT_TYPE as lt, CHART_COLOR_STYLE_CONTENT_TYPE as m, hasBase64Header as mn, TABLE_GRAPHIC_DATA_URI as mt, CHART_METADATA_NS as n, TRANSITION_TYPES as nn, SLIDE_MASTER_REL as nt, HALIGN_BY_TEXT_ALIGN as o, boolValue as on, THEME_CONTENT_TYPE as ot, CHARTEX_REL as p, decodeBase64ToBytes as pn, PML_ROOT_NS as pt, PRESENTATION_MAIN_CONTENT_TYPE as q, TEXT_SHAPE_TYPES as qt, fitSrcRectPercents as r, TRANSITION_VARIANT_ATTRIBUTES as rn, SLIDE_REL as rt, textAlignToken as s, xsdBool as sn, THEME_REL as st, CHART_METADATA_EXT_URI as t, TIME_UNITS as tn, SLIDE_MASTER_CONTENT_TYPE as tt, lineCapToken as u, splitRgbaHex as un, VIEW_PROPS_REL as ut, CHART_STYLE_CONTENT_TYPE as v, imageFormatForContentType as vn, BAR_GROUPINGS as vt, CORE_PROPS_CONTENT_TYPE as w, DISPLAY_BLANKS_AS as wt, COMMENTS_REL as x, COLOR_MAP_TOKENS as xt, CHART_STYLE_REL as y, imageFormatForExtension as yn, BEVEL_PRESETS as yt, NOTES_MASTER_REL as z, RADAR_STYLES as zt };
1669
1707
 
1670
- //# sourceMappingURL=image-size-DwDbakcl.js.map
1708
+ //# sourceMappingURL=chart-metadata-CwiTTcJq.js.map