@shbernal/ts-pptx 4.0.1 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +147 -114
  2. package/dist/anim-presets-Qbgh8uut.js +55 -0
  3. package/dist/anim-presets-Qbgh8uut.js.map +1 -0
  4. package/dist/{autopage-odPz7zZO.js → autopage-DqZ37VpL.js} +11 -12
  5. package/dist/autopage-DqZ37VpL.js.map +1 -0
  6. package/dist/{binary-string-CKVAYI3M.js → binary-string-Ba8VM3b9.js} +3 -4
  7. package/dist/binary-string-Ba8VM3b9.js.map +1 -0
  8. package/dist/{body-insets-Dw_BeWD_.js → body-insets-BDHymJvz.js} +7 -9
  9. package/dist/body-insets-BDHymJvz.js.map +1 -0
  10. package/dist/browser.d.ts +24 -16
  11. package/dist/browser.d.ts.map +1 -1
  12. package/dist/browser.js +19 -19
  13. package/dist/browser.js.map +1 -1
  14. package/dist/{image-size-CvN2iCA4.js → chart-metadata-CwiTTcJq.js} +634 -537
  15. package/dist/chart-metadata-CwiTTcJq.js.map +1 -0
  16. package/dist/{clip-Bl01BPcN.d.ts → clip-ISfCMtWA.d.ts} +6 -10
  17. package/dist/clip-ISfCMtWA.d.ts.map +1 -0
  18. package/dist/{notes-Dh12JAbn.js → comments-CKIZYRbZ.js} +530 -401
  19. package/dist/comments-CKIZYRbZ.js.map +1 -0
  20. package/dist/{diagnostics-D8ot4Ei7.d.ts → diagnostics-MoA4MBq5.d.ts} +55 -53
  21. package/dist/diagnostics-MoA4MBq5.d.ts.map +1 -0
  22. package/dist/{embedded-fonts-DarT9aoa.d.ts → embedded-fonts-U7HIIhPO.d.ts} +4 -6
  23. package/dist/embedded-fonts-U7HIIhPO.d.ts.map +1 -0
  24. package/dist/{entry-compose-DCnvBZz2.d.ts → entry-compose-CiakSt-f.d.ts} +62 -81
  25. package/dist/entry-compose-CiakSt-f.d.ts.map +1 -0
  26. package/dist/{errors-C4oENPcu.js → entry-errors-JMthgJ2g.js} +45 -29
  27. package/dist/entry-errors-JMthgJ2g.js.map +1 -0
  28. package/dist/{table-cell-margins-EVg0EwxT.js → enums-BP9wsb9Q.js} +44 -47
  29. package/dist/enums-BP9wsb9Q.js.map +1 -0
  30. package/dist/{enums-BUKTUB6V.d.ts → enums-PcXr3o3J.d.ts} +13 -14
  31. package/dist/{enums-BUKTUB6V.d.ts.map → enums-PcXr3o3J.d.ts.map} +1 -1
  32. package/dist/families.d.ts +24 -26
  33. package/dist/families.d.ts.map +1 -1
  34. package/dist/families.js +3 -4
  35. package/dist/{fit-Cb7naFj6.js → fit-Bpmzep6O.js} +64 -56
  36. package/dist/fit-Bpmzep6O.js.map +1 -0
  37. package/dist/{font-metrics-v5fn7s7e.d.ts → font-metrics-DTppw4t3.d.ts} +62 -38
  38. package/dist/font-metrics-DTppw4t3.d.ts.map +1 -0
  39. package/dist/{grid-DikTjFey.js → grid-DUIMxjfl.js} +23 -26
  40. package/dist/grid-DUIMxjfl.js.map +1 -0
  41. package/dist/{html-dom-Iuog2wdK.js → html-dom-LZCgntnJ.js} +46 -47
  42. package/dist/html-dom-LZCgntnJ.js.map +1 -0
  43. package/dist/html.d.ts +53 -9
  44. package/dist/html.d.ts.map +1 -1
  45. package/dist/html.js +12 -13
  46. package/dist/html.js.map +1 -1
  47. package/dist/index.d.ts +21 -13
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +15 -15
  50. package/dist/index.js.map +1 -1
  51. package/dist/inspect.d.ts +16 -18
  52. package/dist/inspect.d.ts.map +1 -1
  53. package/dist/inspect.js +18 -19
  54. package/dist/inspect.js.map +1 -1
  55. package/dist/{text-anchor-CrYXAfUu.js → lock-attrs-CSQnFDHP.js} +62 -12
  56. package/dist/lock-attrs-CSQnFDHP.js.map +1 -0
  57. package/dist/math.d.ts +2 -4
  58. package/dist/math.d.ts.map +1 -1
  59. package/dist/math.js +12 -13
  60. package/dist/math.js.map +1 -1
  61. package/dist/measure.d.ts +23 -12
  62. package/dist/measure.d.ts.map +1 -1
  63. package/dist/measure.js +3 -3
  64. package/dist/node.d.ts +21 -13
  65. package/dist/node.d.ts.map +1 -1
  66. package/dist/node.js +12 -12
  67. package/dist/node.js.map +1 -1
  68. package/dist/{options-internal-WqgMf720.js → options-internal-BWgmLPdx.js} +4 -5
  69. package/dist/options-internal-BWgmLPdx.js.map +1 -0
  70. package/dist/{package-J49Ck6S2.d.ts → package-CANBWuSm.d.ts} +9 -13
  71. package/dist/package-CANBWuSm.d.ts.map +1 -0
  72. package/dist/{playbtn-CUqQdoDf.js → playbtn-BQZFJir9.js} +4 -5
  73. package/dist/playbtn-BQZFJir9.js.map +1 -0
  74. package/dist/{pres-C6qgOFAa.d.ts → pres-BkEkUAEM.d.ts} +410 -237
  75. package/dist/pres-BkEkUAEM.d.ts.map +1 -0
  76. package/dist/{presentation-DGYlAWhl.js → presentation-4Ggq0_Xa.js} +470 -315
  77. package/dist/presentation-4Ggq0_Xa.js.map +1 -0
  78. package/dist/{presentation-types-8jLQHI4M.d.ts → presentation-types-BgnZR-0a.d.ts} +1119 -468
  79. package/dist/presentation-types-BgnZR-0a.d.ts.map +1 -0
  80. package/dist/read.d.ts +18 -13
  81. package/dist/read.d.ts.map +1 -1
  82. package/dist/read.js +4 -4
  83. package/dist/script.d.ts +116 -65
  84. package/dist/script.d.ts.map +1 -1
  85. package/dist/script.js +2700 -780
  86. package/dist/script.js.map +1 -1
  87. package/dist/{shapes-re0KyQk_.js → shapes-CAPOjU0Q.js} +1564 -491
  88. package/dist/shapes-CAPOjU0Q.js.map +1 -0
  89. package/dist/{slide-Dx2ZeVZ6.d.ts → slide-BmT4kMoO.d.ts} +102 -89
  90. package/dist/slide-BmT4kMoO.d.ts.map +1 -0
  91. package/dist/{slide-size-BalTjFuv.js → slide-size-SkScz1ig.js} +2 -3
  92. package/dist/{slide-size-BalTjFuv.js.map → slide-size-SkScz1ig.js.map} +1 -1
  93. package/dist/{table-dom-BTDP3lEP.js → table-dom-BYga4FYY.js} +4 -5
  94. package/dist/{table-dom-BTDP3lEP.js.map → table-dom-BYga4FYY.js.map} +1 -1
  95. package/dist/{text-sN64Otf_.js → text-CU08xJGs.js} +393 -344
  96. package/dist/text-CU08xJGs.js.map +1 -0
  97. package/dist/{transition-speed-kUEWDWTx.js → transition-speed-CNJ0fSpQ.js} +2 -3
  98. package/dist/{transition-speed-kUEWDWTx.js.map → transition-speed-CNJ0fSpQ.js.map} +1 -1
  99. package/dist/{units-internal-XJlf-hOl.js → units-internal-BR3a4cIi.js} +34 -32
  100. package/dist/units-internal-BR3a4cIi.js.map +1 -0
  101. package/dist/{zip-CKd8ihcf.js → zip-7JTh61LX.js} +8 -10
  102. package/dist/zip-7JTh61LX.js.map +1 -0
  103. package/dist/{zip-C0leZZMj.d.ts → zip-uUjs9Cqx.d.ts} +4 -6
  104. package/dist/zip-uUjs9Cqx.d.ts.map +1 -0
  105. package/dist/zip.d.ts +3 -3
  106. package/dist/zip.js +3 -3
  107. package/dist/{zoom-DPUHh85d.js → zoom-CSSeOwM8.js} +1204 -620
  108. package/dist/zoom-CSSeOwM8.js.map +1 -0
  109. package/package.json +27 -28
  110. package/skills/{ts-pptx-upstream → pptx-ts-upstream}/SKILL.md +37 -84
  111. package/dist/autopage-odPz7zZO.js.map +0 -1
  112. package/dist/binary-string-CKVAYI3M.js.map +0 -1
  113. package/dist/body-insets-Dw_BeWD_.js.map +0 -1
  114. package/dist/clip-Bl01BPcN.d.ts.map +0 -1
  115. package/dist/diagnostics-D8ot4Ei7.d.ts.map +0 -1
  116. package/dist/embedded-fonts-DarT9aoa.d.ts.map +0 -1
  117. package/dist/entry-compose-DCnvBZz2.d.ts.map +0 -1
  118. package/dist/errors-C4oENPcu.js.map +0 -1
  119. package/dist/fit-Cb7naFj6.js.map +0 -1
  120. package/dist/font-metrics-v5fn7s7e.d.ts.map +0 -1
  121. package/dist/grid-DikTjFey.js.map +0 -1
  122. package/dist/html-dom-Iuog2wdK.js.map +0 -1
  123. package/dist/image-size-CvN2iCA4.js.map +0 -1
  124. package/dist/notes-Dh12JAbn.js.map +0 -1
  125. package/dist/options-internal-WqgMf720.js.map +0 -1
  126. package/dist/package-J49Ck6S2.d.ts.map +0 -1
  127. package/dist/playbtn-CUqQdoDf.js.map +0 -1
  128. package/dist/pres-C6qgOFAa.d.ts.map +0 -1
  129. package/dist/presentation-DGYlAWhl.js.map +0 -1
  130. package/dist/presentation-types-8jLQHI4M.d.ts.map +0 -1
  131. package/dist/shapes-re0KyQk_.js.map +0 -1
  132. package/dist/slide-Dx2ZeVZ6.d.ts.map +0 -1
  133. package/dist/table-cell-margins-EVg0EwxT.js.map +0 -1
  134. package/dist/text-anchor-CrYXAfUu.js.map +0 -1
  135. package/dist/text-sN64Otf_.js.map +0 -1
  136. package/dist/units-internal-XJlf-hOl.js.map +0 -1
  137. package/dist/zip-C0leZZMj.d.ts.map +0 -1
  138. package/dist/zip-CKd8ihcf.js.map +0 -1
  139. package/dist/zoom-DPUHh85d.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import { n as InvalidOptionError, u as warnOnce } from "./errors-C4oENPcu.js";
2
- import { w as PERCENT_SCALE } from "./table-cell-margins-EVg0EwxT.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,66 @@ const TEXT_ANCHORS = [
815
651
  "dist"
816
652
  ];
817
653
  /**
818
- * `ST_TextVerticalType` — `a:bodyPr/@vert` and `a:tcPr/@vert`.
654
+ * `ST_PresetPatternVal`: `a:pattFill/@prst`, the hatch a pattern fill draws.
655
+ */
656
+ const PATTERN_PRESETS = [
657
+ "pct5",
658
+ "pct10",
659
+ "pct20",
660
+ "pct25",
661
+ "pct30",
662
+ "pct40",
663
+ "pct50",
664
+ "pct60",
665
+ "pct70",
666
+ "pct75",
667
+ "pct80",
668
+ "pct90",
669
+ "horz",
670
+ "vert",
671
+ "ltHorz",
672
+ "ltVert",
673
+ "dkHorz",
674
+ "dkVert",
675
+ "narHorz",
676
+ "narVert",
677
+ "dashHorz",
678
+ "dashVert",
679
+ "cross",
680
+ "dnDiag",
681
+ "upDiag",
682
+ "ltDnDiag",
683
+ "ltUpDiag",
684
+ "dkDnDiag",
685
+ "dkUpDiag",
686
+ "wdDnDiag",
687
+ "wdUpDiag",
688
+ "dashDnDiag",
689
+ "dashUpDiag",
690
+ "diagCross",
691
+ "smCheck",
692
+ "lgCheck",
693
+ "smGrid",
694
+ "lgGrid",
695
+ "dotGrid",
696
+ "smConfetti",
697
+ "lgConfetti",
698
+ "horzBrick",
699
+ "diagBrick",
700
+ "solidDmnd",
701
+ "openDmnd",
702
+ "dotDmnd",
703
+ "plaid",
704
+ "sphere",
705
+ "weave",
706
+ "divot",
707
+ "shingle",
708
+ "wave",
709
+ "trellis",
710
+ "zigZag"
711
+ ];
712
+ /**
713
+ * `ST_TextVerticalType`: `a:bodyPr/@vert` and `a:tcPr/@vert`.
819
714
  * @see ECMA-376 Part 1 §20.1.10.83
820
715
  */
821
716
  const TEXT_VERTICAL = [
@@ -828,7 +723,7 @@ const TEXT_VERTICAL = [
828
723
  "wordArtVertRtl"
829
724
  ];
830
725
  /**
831
- * `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`,
832
727
  * `a:tailEnd/@type`).
833
728
  * @see ECMA-376 Part 1 §20.1.10.33
834
729
  */
@@ -855,11 +750,34 @@ const EMBEDDED_FONT_SLOTS = [
855
750
  "boldItalic"
856
751
  ];
857
752
  /**
858
- * `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
859
777
  * PowerPoint calls WordArt.
860
778
  *
861
779
  * `textNoShape` is the identity member: it is legal, and it warps nothing. The library does not
862
- * narrow this set — every member is a shape PowerPoint draws.
780
+ * narrow this set: every member is a shape PowerPoint draws.
863
781
  * @see ECMA-376 Part 1 §20.1.10.76
864
782
  */
865
783
  const TEXT_SHAPE_TYPES = [
@@ -906,7 +824,7 @@ const TEXT_SHAPE_TYPES = [
906
824
  "textCascadeDown"
907
825
  ];
908
826
  /**
909
- * `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`).
910
828
  *
911
829
  * The `sys*` members are the "system" dashes whose exact dot/gap ratio the renderer picks; the
912
830
  * rest are defined by the spec. Both `BorderProps.dashType` and `ShapeLineProps.dashType` accept
@@ -927,12 +845,12 @@ const PRESET_LINE_DASHES = [
927
845
  "sysDashDotDot"
928
846
  ];
929
847
  /**
930
- * `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`).
931
849
  * @see ECMA-376 Part 1 §21.2.3.3
932
850
  */
933
851
  const BAR_DIRECTIONS = ["bar", "col"];
934
852
  /**
935
- * `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
936
854
  * `c:barChart`/`c:bar3DChart`).
937
855
  * @see ECMA-376 Part 1 §21.2.3.4
938
856
  */
@@ -943,7 +861,7 @@ const BAR_GROUPINGS = [
943
861
  "stacked"
944
862
  ];
945
863
  /**
946
- * `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
947
865
  * (`c:areaChart`, `c:lineChart`).
948
866
  * @see ECMA-376 Part 1 §21.2.3.17
949
867
  */
@@ -953,7 +871,7 @@ const GROUPINGS = [
953
871
  "stacked"
954
872
  ];
955
873
  /**
956
- * `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`).
957
875
  * @see ECMA-376 Part 1 §21.2.3.35
958
876
  */
959
877
  const BAR_3D_SHAPES = [
@@ -965,7 +883,7 @@ const BAR_3D_SHAPES = [
965
883
  "pyramidToMax"
966
884
  ];
967
885
  /**
968
- * `ST_RadarStyle` — how a radar plot is drawn (`c:radarStyle/@val`).
886
+ * `ST_RadarStyle`: how a radar plot is drawn (`c:radarStyle/@val`).
969
887
  * @see ECMA-376 Part 1 §21.2.3.33
970
888
  */
971
889
  const RADAR_STYLES = [
@@ -980,8 +898,8 @@ const RADAR_STYLES = [
980
898
  * what made the other two read as typos rather than as a second vocabulary.
981
899
  *
982
900
  * Both spellings are accepted on the public option and normalized to the wire one at definition
983
- * time. Before that, `radarStyle: 'marker'` — the schema's word, and the obvious guess for anyone
984
- * 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
985
903
  * asking for markers got a plain radar.
986
904
  */
987
905
  const RADAR_STYLE_ALIASES = {
@@ -990,12 +908,12 @@ const RADAR_STYLE_ALIASES = {
990
908
  };
991
909
  /**
992
910
  * The alias spellings alone, for the definer's enum check. Derived from the map rather than
993
- * 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
994
912
  * against and the union it is typed against cannot drift apart.
995
913
  */
996
914
  const RADAR_STYLE_ALIAS_NAMES = Object.keys(RADAR_STYLE_ALIASES);
997
915
  /**
998
- * `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`).
999
917
  * @see ECMA-376 Part 1 §21.2.3.24
1000
918
  */
1001
919
  const LEGEND_POSITIONS = [
@@ -1006,7 +924,7 @@ const LEGEND_POSITIONS = [
1006
924
  "t"
1007
925
  ];
1008
926
  /**
1009
- * `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`).
1010
928
  * @see ECMA-376 Part 1 §21.2.3.10
1011
929
  */
1012
930
  const DISPLAY_BLANKS_AS = [
@@ -1019,7 +937,7 @@ const DISPLAY_BLANKS_AS = [
1019
937
  *
1020
938
  * The schema also has `plus`, `star`, `x`, `picture` and `auto`. Neither PowerPoint 2013 nor
1021
939
  * PowerPoint Online draws the first three, `picture` needs a `c:spPr` blip this API has no
1022
- * 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
1023
941
  * deliberately smaller than the type. Widen it only with evidence that the member renders.
1024
942
  * @see ECMA-376 Part 1 §21.2.3.27
1025
943
  */
@@ -1032,25 +950,25 @@ const LINE_DATA_SYMBOLS = [
1032
950
  "square",
1033
951
  "triangle"
1034
952
  ];
1035
- /** `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`). */
1036
954
  const TICK_LABEL_POSITIONS = [
1037
955
  "high",
1038
956
  "low",
1039
957
  "nextTo",
1040
958
  "none"
1041
959
  ];
1042
- /** `ST_Orientation` — which way an axis runs (`c:orientation/@val`). */
960
+ /** `ST_Orientation`: which way an axis runs (`c:orientation/@val`). */
1043
961
  const AXIS_ORIENTATIONS = ["maxMin", "minMax"];
1044
- /** `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`). */
1045
963
  const TICK_MARKS = [
1046
964
  "cross",
1047
965
  "in",
1048
966
  "none",
1049
967
  "out"
1050
968
  ];
1051
- /** `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`). */
1052
970
  const CROSS_BETWEEN = ["between", "midCat"];
1053
- /** `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`). */
1054
972
  const BUILT_IN_UNITS = [
1055
973
  "hundreds",
1056
974
  "thousands",
@@ -1062,21 +980,21 @@ const BUILT_IN_UNITS = [
1062
980
  "billions",
1063
981
  "trillions"
1064
982
  ];
1065
- /** `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). */
1066
984
  const TIME_UNITS = [
1067
985
  "days",
1068
986
  "months",
1069
987
  "years"
1070
988
  ];
1071
- /** `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`). */
1072
990
  const ERROR_BAR_DIRECTIONS = ["x", "y"];
1073
- /** `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`). */
1074
992
  const ERROR_BAR_TYPES = [
1075
993
  "both",
1076
994
  "minus",
1077
995
  "plus"
1078
996
  ];
1079
- /** `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`). */
1080
998
  const ERROR_BAR_VALUE_TYPES = [
1081
999
  "cust",
1082
1000
  "fixedVal",
@@ -1087,7 +1005,7 @@ const ERROR_BAR_VALUE_TYPES = [
1087
1005
  /**
1088
1006
  * The twelve theme colour slots (`a:clrScheme` children) a `p:clrMap` token can point at.
1089
1007
  *
1090
- * 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
1091
1009
  * declares as a fixed sequence rather than an enumeration. The effect is the same: a closed,
1092
1010
  * ordered set of twelve names, in the order the scheme declares them.
1093
1011
  * @see ECMA-376 Part 1 §20.1.6.2
@@ -1111,7 +1029,7 @@ const THEME_COLOR_SLOTS = [
1111
1029
  *
1112
1030
  * A slide's `schemeClr val="tx1"` names a *token*, not a slot: the master's `p:clrMap` is the
1113
1031
  * indirection that sends it to a {@link ThemeColorSlot}. Which is why the two lists look alike
1114
- * 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.
1115
1033
  * @see ECMA-376 Part 1 §19.3.1.6
1116
1034
  */
1117
1035
  const COLOR_MAP_TOKENS = [
@@ -1133,8 +1051,8 @@ const COLOR_MAP_TOKENS = [
1133
1051
  * and the one the script printer recognises so it can skip printing a map that is the default.
1134
1052
  *
1135
1053
  * "Identity" up to the light/dark naming: each token points at the slot of the same name, with
1136
- * `bg`/`tx` reading as light/dark. A deck that maps them anywhere else — swapping the light and
1137
- * 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.
1138
1056
  *
1139
1057
  * Typed `Record<ColorMapToken, ThemeColorSlot>` so a token added to {@link COLOR_MAP_TOKENS}
1140
1058
  * without a mapping, or a slot misspelled, is a compile error rather than a theme that opens
@@ -1155,182 +1073,339 @@ const DEFAULT_COLOR_MAP = {
1155
1073
  hlink: "hlink",
1156
1074
  folHlink: "folHlink"
1157
1075
  };
1158
- //#endregion
1159
- //#region src/hex-color.ts
1160
- /**
1161
- * ts-pptx: hex colour text, in the one spelling both halves use.
1162
- *
1163
- * A caller may write a colour with or without a leading `#`, and the library accepts both
1164
- * everywhere — which means every site that validates or parses one strips it first. Those
1165
- * strips had drifted into three spellings across six sites, and one of them was not
1166
- * anchored: `.replace('#', '')` takes the `#` out of `'FF00#0'` and hands `'FF000'` on, which
1167
- * then fails the six-digit test and takes a different branch than the anchored forms would.
1168
- * No caller is known to reach that, which is exactly why it wants one spelling rather than a
1169
- * test at each site.
1170
- *
1171
- * This lives at the root rather than under `gen/` or `read/` because both halves need it:
1172
- * the emitters strip on the way in, the readers strip on the way back out — and for the same
1173
- * reason the *validation* belongs here too. `REGEX_HEX_COLOR` used to live in
1174
- * `constants-internal.ts`, which is a write-side module the read path cannot import, so the read
1175
- * path re-spelled the regex; so did one write-side site. Six sites, three spellings of one fact.
1176
- */
1076
+ // src/ooxml/namespaces.ts
1177
1077
  /**
1178
- * A colour written as six hex digits, `#` already stripped.
1078
+ * OOXML namespace URIs, by their canonical prefix.
1179
1079
  *
1180
- * Not exported: {@link isHexColor} is the whole of what the call sites want, and a shared mutable
1181
- * `RegExp` object is a worse export than a predicate.
1182
- */
1183
- const REGEX_HEX_COLOR = /^[0-9a-fA-F]{6}$/;
1184
- /** An 8-hex RGBA colour, `#` already stripped — {@link splitRgbaHex}'s test. */
1185
- const REGEX_ARGB_HEX = /^[0-9a-fA-F]{8}$/;
1186
- /** Strip a single leading `#` from a colour, leaving any other character alone. */
1187
- function stripHash(value) {
1188
- return value.startsWith("#") ? value.slice(1) : value;
1189
- }
1190
- /**
1191
- * 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.
1192
1085
  *
1193
- * This is the *shape* test only. Whether the caller may also name a scheme colour there is the
1194
- * call site's question, and each still asks it: `createColorElement` falls back to the default
1195
- * font colour, `makeXmlTheme` keeps the Office default and warns, `normalizeHex` throws.
1196
- * @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.
1197
1094
  */
1198
- function isHexColor(value) {
1199
- return REGEX_HEX_COLOR.test(stripHash(value));
1200
- }
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
+ });
1201
1122
  /**
1202
- * Is `value` an 8-hex RGBA colour? A leading `#` is stripped first.
1123
+ * `a:graphicData/@uri` for a DrawingML table.
1203
1124
  *
1204
- * The 8-digit form is RGB followed by one alpha byte; {@link splitRgbaHex} is what takes it apart.
1205
- * @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.
1206
1129
  */
1207
- function isArgbHex(value) {
1208
- return REGEX_ARGB_HEX.test(stripHash(value));
1209
- }
1130
+ const TABLE_GRAPHIC_DATA_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";
1210
1131
  /**
1211
- * Split an 8-hex RGBA colour into its 6-hex RGB and a 0–1 alpha; anything else passes through
1212
- * unchanged with no alpha.
1132
+ * The `xmlns` declarations every PresentationML part root carries, in the order the emitters
1133
+ * have always written them.
1213
1134
  *
1214
- * `#` is stripped first, so both spellings reach the same answer. Two sites parsed this by
1215
- * hand — `createColorElement`, which turns the alpha into an `<a:alpha>` sibling, and
1216
- * `normalizeShadowOptions`, which turns it into a shadow's `_alpha` — and they divided by 255 at
1217
- * different points in their arithmetic.
1218
- * @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.
1219
1139
  */
1220
- function splitRgbaHex(value) {
1221
- const hex = stripHash(value);
1222
- if (!isArgbHex(hex)) return { rgb: hex };
1223
- return {
1224
- rgb: hex.slice(0, 6),
1225
- alpha: parseInt(hex.slice(6, 8), 16) / 255
1226
- };
1227
- }
1228
- //#endregion
1229
- //#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
1230
1146
  /**
1231
- * Parsing an `xsd:boolean` attribute value.
1147
+ * OPC relationship type URIs and part content types, and the schema-URI bases they are built from.
1232
1148
  *
1233
- * **Why a function at all.** `xsd:boolean` has four lexical forms — `1`, `0`, `true`, `false` —
1234
- * and OOXML attributes typed with it accept every one of them. PowerPoint writes only `1` and
1235
- * `0`, so a hand-rolled `=== '1'` test passes against every deck this library produces and
1236
- * against every deck PowerPoint produces, and silently misreads the other producers. That is the
1237
- * 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.
1238
1152
  *
1239
- * **Why this module is neither `gen/` nor `read/`.** The lexical space of a schema type is a fact
1240
- * about the schema, like `sequence.ts`'s document order and `body-insets.ts`'s defaults. The
1241
- * parser lived in `read/oxml/dom.ts` because reading is where most of the demand is, and three
1242
- * private copies had collapsed into it — but `read/oxml/dom.ts` imports `@xmldom/xmldom`, so
1243
- * anything outside `read/` that wants this five-line function has to take a DOM dependency to get
1244
- * 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.
1245
1159
  *
1246
- * The DOM-typed convenience wrapper stays behind in `read/oxml/dom.ts` as `boolAttr`, because
1247
- * *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.
1248
1165
  *
1249
- * **The write direction is here too**, and it is where the interesting distinction lives: an
1250
- * emitter writing a boolean has THREE useful shapes, not one — see {@link xsdBool} and
1251
- * {@link xsdBoolIfTrue}. Sixty-odd inline ternaries spelled them in three ways with nothing
1252
- * 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.
1253
1168
  */
1254
1169
  /**
1255
- * Parse an `xsd:boolean` OOXML attribute (`1`/`0`/`true`/`false`); else `null`.
1256
- *
1257
- * `null` for an absent *or* unparseable value is deliberate and load-bearing at the call sites:
1258
- * several attributes have a schema default of `true`, so they test `!== false` rather than
1259
- * `=== 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.
1260
1172
  */
1261
- function boolValue(value) {
1262
- if (value === "1" || value === "true") return true;
1263
- if (value === "0" || value === "false") return false;
1264
- return null;
1265
- }
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";
1266
1227
  /**
1267
- * A boolean as the `1`/`0` an `xsd:boolean` attribute takes.
1268
- *
1269
- * Writes the attribute either way, so `false` is *stated*. Use this wherever the schema
1270
- * default is `true`, or wherever the absence of the attribute would mean something other
1271
- * than `false` — a value inherited from a list style, a layout or the theme.
1272
- * @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.
1273
1230
  */
1274
- function xsdBool(value) {
1275
- return value ? "1" : "0";
1276
- }
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";
1277
1244
  /**
1278
- * A boolean as `1`, or `null` so the attribute is left out entirely.
1279
- *
1280
- * **Correct only where the schema default is `false`.** It collapses "false" and "unset" into
1281
- * one output, which is the two-spellings-for-three-states shape the API policy warns about: the
1282
- * next attribute with a `true` default written this way reads as correct and is wrong. Every
1283
- * current caller is on a `false`-defaulting attribute, so the collapse costs nothing today —
1284
- * naming it is what makes the next one visible.
1285
- * @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.
1286
1247
  */
1287
- function xsdBoolIfTrue(value) {
1288
- return value ? "1" : null;
1289
- }
1290
- //#endregion
1291
- //#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";
1292
1297
  /**
1293
- * Check a value against its `ST_` union, reporting and dropping it when outside (the write-path
1294
- * policy — see the module note).
1295
- *
1296
- * `undefined`/`null` returns `null` without reporting: an unset option is not an invalid one, and
1297
- * it is by far the common case.
1298
+ * Every kind of part a page points at without owning it.
1298
1299
  *
1299
- * The offending value is interpolated into the message deliberately. {@link warnOnce} dedups on
1300
- * code **and** message, so keeping the value in the text is what lets a second, *different* bad
1301
- * value under the same code still get reported.
1302
- * @param value - the value the caller supplied, if any
1303
- * @param valid - the enum's members
1304
- * @param code - the condition to report
1305
- * @param label - how to name the offending option in the message, e.g. ``'table cell: cell3D `preset`'``
1306
- * @param detail - extra structured context to merge into the diagnostic's `detail`
1307
- * @returns the value when legal, else `null`
1308
- */
1309
- function checkEnumOrWarn(value, valid, code, label, detail) {
1310
- if (value === void 0 || value === null) return null;
1311
- if (valid.includes(value)) return value;
1312
- warnOnce(code, `${label} value \`${String(value)}\` is not valid and is ignored — use one of ${valid.join(", ")}.`, {
1313
- received: value,
1314
- valid,
1315
- ...detail
1316
- });
1317
- return null;
1318
- }
1319
- /**
1320
- * Check a value against its `ST_` union, throwing when outside (the read/edit-path policy — see
1321
- * the module note).
1322
- * @param value - the value the caller asked for
1323
- * @param valid - the enum's members
1324
- * @param attribute - the attribute's name, for the message
1325
- * @param code - the condition to report
1326
- * @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.
1327
1305
  */
1328
- function checkEnumOrThrow(value, valid, attribute, code) {
1329
- if (valid.includes(value)) return value;
1330
- throw new InvalidOptionError(code, `Invalid ${attribute}: ${JSON.stringify(value)}. Expected one of: ${valid.join(", ")}.`);
1331
- }
1332
- //#endregion
1333
- //#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
1334
1409
  /**
1335
1410
  * Every `cap` the write API names and the token it writes. `satisfies` over `Record<LineCap, …>`
1336
1411
  * makes a member added to `LineCap` without a token a compile error, and `ST_LineCap` has exactly
@@ -1347,8 +1422,16 @@ function lineCapToken(cap) {
1347
1422
  }
1348
1423
  /** A `cap` token back to the `LineCap` that authors it. */
1349
1424
  const LINE_CAP_BY_TOKEN = Object.fromEntries(Object.entries(TOKEN_BY_LINE_CAP).map(([cap, token]) => [token, cap]));
1350
- //#endregion
1351
- //#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
1352
1435
  /**
1353
1436
  * Every `align` the write API names and the token it writes. `satisfies` over `Record<HAlign, …>`
1354
1437
  * makes a member added to `HAlign` without a token a compile error. `justLow`, `dist` and
@@ -1366,21 +1449,20 @@ function textAlignToken(align) {
1366
1449
  }
1367
1450
  /** An `algn` token back to the `align` a generated script writes. */
1368
1451
  const HALIGN_BY_TEXT_ALIGN = Object.fromEntries(Object.entries(ALGN_BY_ALIGN).map(([align, token]) => [token, align]));
1369
- //#endregion
1370
- //#region src/media/image-size.ts
1452
+ // src/media/image-size.ts
1371
1453
  /**
1372
1454
  * Intrinsic image geometry: reading a natural pixel size out of header bytes, and the
1373
1455
  * `<a:srcRect>` crop math that fits that natural size into a display box.
1374
1456
  *
1375
- * Shared by both sides of the library — the write path (`addImage({ sizing })`) and the read
1376
- * 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/`.
1377
1459
  * Everything here is synchronous and pure: headers only, never pixel decoding.
1378
1460
  */
1379
1461
  /**
1380
1462
  * Read the intrinsic dimensions of an image from its header bytes.
1381
1463
  * - synchronous: parses only file-format headers, never decodes pixels
1382
- * - raster: PNG, JPEG, GIF, BMP, TIFF, and WebP (VP8 / VP8L / VP8X) — natural pixels
1383
- * - 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
1384
1466
  * - unrecognized formats return `null` (no measurable intrinsic size)
1385
1467
  *
1386
1468
  * Used by image `sizing: 'cover' | 'contain'` to compute an aspect-correct
@@ -1393,7 +1475,7 @@ function getImageSizeFromBase64(dataB64) {
1393
1475
  return b ? getImageSizeFromBytes(b) : null;
1394
1476
  }
1395
1477
  /**
1396
- * 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
1397
1479
  * byte-level core shared by {@link getImageSizeFromBase64} and the read API's
1398
1480
  * `Picture.setImage({ fit })`, which already holds the media bytes.
1399
1481
  * @param {Uint8Array} b - image bytes
@@ -1402,7 +1484,7 @@ function getImageSizeFromBase64(dataB64) {
1402
1484
  function getImageSizeFromBytes(b) {
1403
1485
  if (!b || b.length < 24) return null;
1404
1486
  const u = (n) => b[n] ?? 0;
1405
- if (IMAGE_FORMATS.png.magic?.(b)) {
1487
+ if (IMAGE_FORMATS.png.magic(b)) {
1406
1488
  const w = u(16) << 24 | u(17) << 16 | u(18) << 8 | u(19);
1407
1489
  const h = u(20) << 24 | u(21) << 16 | u(22) << 8 | u(23);
1408
1490
  return w > 0 && h > 0 ? {
@@ -1410,7 +1492,7 @@ function getImageSizeFromBytes(b) {
1410
1492
  h
1411
1493
  } : null;
1412
1494
  }
1413
- if (IMAGE_FORMATS.gif.magic?.(b)) {
1495
+ if (IMAGE_FORMATS.gif.magic(b)) {
1414
1496
  const w = u(6) | u(7) << 8;
1415
1497
  const h = u(8) | u(9) << 8;
1416
1498
  return w > 0 && h > 0 ? {
@@ -1418,7 +1500,7 @@ function getImageSizeFromBytes(b) {
1418
1500
  h
1419
1501
  } : null;
1420
1502
  }
1421
- if (IMAGE_FORMATS.bmp.magic?.(b)) {
1503
+ if (IMAGE_FORMATS.bmp.magic(b)) {
1422
1504
  if ((u(14) | u(15) << 8 | u(16) << 16 | u(17) << 24) === 12) {
1423
1505
  const w = u(18) | u(19) << 8;
1424
1506
  const h = u(20) | u(21) << 8;
@@ -1437,7 +1519,7 @@ function getImageSizeFromBytes(b) {
1437
1519
  h: ah
1438
1520
  } : null;
1439
1521
  }
1440
- if (IMAGE_FORMATS.webp.magic?.(b)) {
1522
+ if (IMAGE_FORMATS.webp.magic(b)) {
1441
1523
  const fourCC = String.fromCharCode(u(12), u(13), u(14), u(15));
1442
1524
  if (fourCC === "VP8 " && b.length >= 30) {
1443
1525
  const w = (u(26) | u(27) << 8) & 16383;
@@ -1466,7 +1548,7 @@ function getImageSizeFromBytes(b) {
1466
1548
  }
1467
1549
  return null;
1468
1550
  }
1469
- if (IMAGE_FORMATS.jpeg.magic?.(b)) {
1551
+ if (IMAGE_FORMATS.jpeg.magic(b)) {
1470
1552
  let i = 2;
1471
1553
  while (i + 9 < b.length) {
1472
1554
  if (b[i] !== 255) {
@@ -1605,7 +1687,22 @@ function getSvgSizeFromMarkup(svg) {
1605
1687
  function utf8Decode(bytes) {
1606
1688
  return new TextDecoder().decode(bytes);
1607
1689
  }
1608
- //#endregion
1609
- export { TICK_MARKS as $, THEME_REL as $t, GROUPINGS as A, NOTES_MASTER_CONTENT_TYPE as At, RADAR_STYLE_ALIAS_NAMES as B, PRES_PROPS_CONTENT_TYPE as Bt, CROSS_BETWEEN as C, HYPERLINK_REL as Ct, ERROR_BAR_DIRECTIONS as D, MS_MEDIA_REL as Dt, EMBEDDED_FONT_SLOTS as E, MODERN_COMMENT_AUTHORS_REL as Et, LINE_END_TYPES as F, OFFICE_DOCUMENT_REL as Ft, TEXT_HORZ_OVERFLOW as G, SLIDE_LAYOUT_CONTENT_TYPE as Gt, TEXT_ANCHORS as H, RELATIONSHIPS_CONTENT_TYPE as Ht, PRESET_LINE_DASHES as I, OFFICE_REL as It, TEXT_TAB_ALIGN_TYPES as J, SLIDE_MASTER_REL as Jt, TEXT_SHAPE_TYPES as K, SLIDE_LAYOUT_REL as Kt, PRESET_MATERIALS as L, PACKAGE_REL as Lt, LIGHT_RIGS as M, NOTES_SLIDE_CONTENT_TYPE as Mt, LIGHT_RIG_DIRECTIONS as N, NOTES_SLIDE_REL as Nt, ERROR_BAR_TYPES as O, MS_POWERPOINT_CONTENT as Ot, LINE_DATA_SYMBOLS as P, OD_CONTENT as Pt, TICK_LABEL_POSITIONS as Q, THEME_CONTENT_TYPE as Qt, RADAR_STYLES as R, PACKAGE_REL_NS as Rt, COLOR_MAP_TOKENS as S, FONT_REL as St, DISPLAY_BLANKS_AS as T, MODEL3D_REL as Tt, TEXT_AUTONUM_SCHEMES as U, SHARED_PARTS as Ut, SCHEME_COLOR_VALUES as V, PRES_PROPS_REL as Vt, TEXT_CAPS_TYPES as W, SLIDE_CONTENT_TYPE as Wt, TEXT_VERTICAL as X, TABLE_STYLES_CONTENT_TYPE as Xt, TEXT_UNDERLINE_TYPES as Y, SLIDE_REL as Yt, THEME_COLOR_SLOTS as Z, TABLE_STYLES_REL as Zt, BAR_3D_SHAPES as _, CORE_PROPS_REL as _t, textAlignToken as a, PML_ROOT_NS as an, CHARTEX_REL as at, BEVEL_PRESETS as b, EXTENDED_PROPS_CONTENT_TYPE as bt, checkEnumOrThrow as c, decodeBase64ToBytes as cn, CHART_CONTENT_TYPE as ct, xsdBool as d, toMediaDataUri as dn, CHART_STYLE_REL as dt, VIDEO_REL as en, TIME_UNITS as et, xsdBoolIfTrue as f, imageFormatForBytes as fn, COMMENTS_CONTENT_TYPE as ft, AXIS_ORIENTATIONS as g, CORE_PROPS_CONTENT_TYPE as gt, stripHash as h, COMMENT_AUTHORS_REL as ht, HALIGN_BY_TEXT_ALIGN as i, OOXML_NS as in, CHARTEX_CONTENT_TYPE as it, LEGEND_POSITIONS as j, NOTES_MASTER_REL as jt, ERROR_BAR_VALUE_TYPES as k, MS_REL as kt, checkEnumOrWarn as l, hasBase64Header as ln, CHART_REL as lt, splitRgbaHex as m, imageFormatForExtension as mn, COMMENT_AUTHORS_CONTENT_TYPE as mt, getImageSizeFromBase64 as n, VIEW_PROPS_REL as nn, TRANSITION_VARIANT_ATTRIBUTES as nt, LINE_CAP_BY_TOKEN as o, TABLE_GRAPHIC_DATA_URI as on, CHART_COLOR_STYLE_CONTENT_TYPE as ot, isHexColor as p, imageFormatForContentType as pn, COMMENTS_REL as pt, TEXT_STRIKE_TYPES as q, SLIDE_MASTER_CONTENT_TYPE as qt, getImageSizeFromBytes as r, XLSX_CONTENT_TYPE as rn, AUDIO_REL as rt, lineCapToken as s, bytesToBase64 as sn, CHART_COLOR_STYLE_REL as st, fitSrcRectPercents as t, VIEW_PROPS_CONTENT_TYPE as tn, TRANSITION_TYPES as tt, boolValue as u, svgMarkupToDataUri as un, CHART_STYLE_CONTENT_TYPE as ut, BAR_DIRECTIONS as v, CUSTOM_PROPS_CONTENT_TYPE as vt, DEFAULT_COLOR_MAP as w, IMAGE_REL as wt, BUILT_IN_UNITS as x, EXTENDED_PROPS_REL as xt, BAR_GROUPINGS as y, CUSTOM_PROPS_REL as yt, RADAR_STYLE_ALIASES as z, PRESENTATION_MAIN_CONTENT_TYPE 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 };
1610
1707
 
1611
- //# sourceMappingURL=image-size-CvN2iCA4.js.map
1708
+ //# sourceMappingURL=chart-metadata-CwiTTcJq.js.map