pptx-ts 3.7.0 → 4.0.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 (136) hide show
  1. package/README.md +116 -269
  2. package/dist/autopage-odPz7zZO.js +536 -0
  3. package/dist/autopage-odPz7zZO.js.map +1 -0
  4. package/dist/binary-string-CKVAYI3M.js +24 -0
  5. package/dist/binary-string-CKVAYI3M.js.map +1 -0
  6. package/dist/body-insets-Dw_BeWD_.js +54 -0
  7. package/dist/body-insets-Dw_BeWD_.js.map +1 -0
  8. package/dist/browser.d.ts +29 -9
  9. package/dist/browser.d.ts.map +1 -1
  10. package/dist/browser.js +41 -14
  11. package/dist/browser.js.map +1 -1
  12. package/dist/clip-Bl01BPcN.d.ts +67 -0
  13. package/dist/clip-Bl01BPcN.d.ts.map +1 -0
  14. package/dist/diagnostics-D8ot4Ei7.d.ts +244 -0
  15. package/dist/diagnostics-D8ot4Ei7.d.ts.map +1 -0
  16. package/dist/embedded-fonts-DarT9aoa.d.ts +20 -0
  17. package/dist/embedded-fonts-DarT9aoa.d.ts.map +1 -0
  18. package/dist/entry-compose-DCnvBZz2.d.ts +1046 -0
  19. package/dist/entry-compose-DCnvBZz2.d.ts.map +1 -0
  20. package/dist/{enums-CvRYniUA.d.ts → enums-BUKTUB6V.d.ts} +21 -13
  21. package/dist/{enums-CvRYniUA.d.ts.map → enums-BUKTUB6V.d.ts.map} +1 -1
  22. package/dist/{errors-Bvln0H-7.js → errors-C4oENPcu.js} +92 -2
  23. package/dist/errors-C4oENPcu.js.map +1 -0
  24. package/dist/families.d.ts +260 -0
  25. package/dist/families.d.ts.map +1 -0
  26. package/dist/families.js +57 -0
  27. package/dist/families.js.map +1 -0
  28. package/dist/{fit-BKs0zxMI.js → fit-Cb7naFj6.js} +249 -167
  29. package/dist/fit-Cb7naFj6.js.map +1 -0
  30. package/dist/font-metrics-v5fn7s7e.d.ts +613 -0
  31. package/dist/font-metrics-v5fn7s7e.d.ts.map +1 -0
  32. package/dist/grid-DikTjFey.js +187 -0
  33. package/dist/grid-DikTjFey.js.map +1 -0
  34. package/dist/{html-dom-B0I0QSbU.js → html-dom-Iuog2wdK.js} +121 -91
  35. package/dist/html-dom-Iuog2wdK.js.map +1 -0
  36. package/dist/html.d.ts +6 -6
  37. package/dist/html.d.ts.map +1 -1
  38. package/dist/html.js +7 -6
  39. package/dist/html.js.map +1 -1
  40. package/dist/image-size-CvN2iCA4.js +1611 -0
  41. package/dist/image-size-CvN2iCA4.js.map +1 -0
  42. package/dist/index.d.ts +24 -10
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +31 -11
  45. package/dist/index.js.map +1 -1
  46. package/dist/inspect.d.ts +40 -31
  47. package/dist/inspect.d.ts.map +1 -1
  48. package/dist/inspect.js +40 -48
  49. package/dist/inspect.js.map +1 -1
  50. package/dist/math.d.ts +7 -5
  51. package/dist/math.d.ts.map +1 -1
  52. package/dist/math.js +16 -7
  53. package/dist/math.js.map +1 -1
  54. package/dist/measure.d.ts +12 -225
  55. package/dist/measure.d.ts.map +1 -1
  56. package/dist/measure.js +3 -3
  57. package/dist/node.d.ts +22 -8
  58. package/dist/node.d.ts.map +1 -1
  59. package/dist/node.js +27 -7
  60. package/dist/node.js.map +1 -1
  61. package/dist/notes-Dh12JAbn.js +3264 -0
  62. package/dist/notes-Dh12JAbn.js.map +1 -0
  63. package/dist/options-internal-WqgMf720.js +43 -0
  64. package/dist/options-internal-WqgMf720.js.map +1 -0
  65. package/dist/{package-CaOLXSNi.d.ts → package-J49Ck6S2.d.ts} +27 -12
  66. package/dist/package-J49Ck6S2.d.ts.map +1 -0
  67. package/dist/playbtn-CUqQdoDf.js +22 -0
  68. package/dist/playbtn-CUqQdoDf.js.map +1 -0
  69. package/dist/{slide-ZqAdVvHV.d.ts → pres-C6qgOFAa.d.ts} +1021 -1423
  70. package/dist/pres-C6qgOFAa.d.ts.map +1 -0
  71. package/dist/{presentation-DrJtj3-C.js → presentation-DGYlAWhl.js} +3208 -2727
  72. package/dist/presentation-DGYlAWhl.js.map +1 -0
  73. package/dist/{presentation-types-CDjScsev.d.ts → presentation-types-8jLQHI4M.d.ts} +1070 -683
  74. package/dist/presentation-types-8jLQHI4M.d.ts.map +1 -0
  75. package/dist/read.d.ts +9 -9
  76. package/dist/read.d.ts.map +1 -1
  77. package/dist/read.js +4 -4
  78. package/dist/script.d.ts +80 -66
  79. package/dist/script.d.ts.map +1 -1
  80. package/dist/script.js +2399 -1139
  81. package/dist/script.js.map +1 -1
  82. package/dist/{shapes-BemroU-z.js → shapes-re0KyQk_.js} +2807 -1609
  83. package/dist/shapes-re0KyQk_.js.map +1 -0
  84. package/dist/slide-Dx2ZeVZ6.d.ts +1256 -0
  85. package/dist/slide-Dx2ZeVZ6.d.ts.map +1 -0
  86. package/dist/slide-size-BalTjFuv.js +88 -0
  87. package/dist/slide-size-BalTjFuv.js.map +1 -0
  88. package/dist/{units-internal-Dg95w_YD.js → table-cell-margins-EVg0EwxT.js} +235 -273
  89. package/dist/table-cell-margins-EVg0EwxT.js.map +1 -0
  90. package/dist/table-dom-BTDP3lEP.js +21 -0
  91. package/dist/table-dom-BTDP3lEP.js.map +1 -0
  92. package/dist/text-KnLOtEEu.js +3980 -0
  93. package/dist/text-KnLOtEEu.js.map +1 -0
  94. package/dist/text-anchor-CrYXAfUu.js +176 -0
  95. package/dist/text-anchor-CrYXAfUu.js.map +1 -0
  96. package/dist/transition-speed-kUEWDWTx.js +14 -0
  97. package/dist/transition-speed-kUEWDWTx.js.map +1 -0
  98. package/dist/units-internal-XJlf-hOl.js +649 -0
  99. package/dist/units-internal-XJlf-hOl.js.map +1 -0
  100. package/dist/zip-C0leZZMj.d.ts +67 -0
  101. package/dist/zip-C0leZZMj.d.ts.map +1 -0
  102. package/dist/zip-CKd8ihcf.js +177 -0
  103. package/dist/zip-CKd8ihcf.js.map +1 -0
  104. package/dist/zip.d.ts +3 -68
  105. package/dist/zip.js +3 -163
  106. package/dist/zoom-cX9sllpH.js +8865 -0
  107. package/dist/zoom-cX9sllpH.js.map +1 -0
  108. package/package.json +106 -19
  109. package/skills/ts-pptx-upstream/SKILL.md +58 -57
  110. package/dist/autopage-DkjYZUFD.js +0 -423
  111. package/dist/autopage-DkjYZUFD.js.map +0 -1
  112. package/dist/diagnostics-DfBdDHre.d.ts +0 -829
  113. package/dist/diagnostics-DfBdDHre.d.ts.map +0 -1
  114. package/dist/errors-Bvln0H-7.js.map +0 -1
  115. package/dist/errors-PflMPWBn.d.ts +0 -182
  116. package/dist/errors-PflMPWBn.d.ts.map +0 -1
  117. package/dist/fit-BKs0zxMI.js.map +0 -1
  118. package/dist/html-dom-B0I0QSbU.js.map +0 -1
  119. package/dist/namespaces-BVnNu7sg.js +0 -666
  120. package/dist/namespaces-BVnNu7sg.js.map +0 -1
  121. package/dist/notes-CJFJaG6M.js +0 -1724
  122. package/dist/notes-CJFJaG6M.js.map +0 -1
  123. package/dist/package-CaOLXSNi.d.ts.map +0 -1
  124. package/dist/pres-BcBR3CSM.d.ts +0 -202
  125. package/dist/pres-BcBR3CSM.d.ts.map +0 -1
  126. package/dist/presentation-DrJtj3-C.js.map +0 -1
  127. package/dist/presentation-types-CDjScsev.d.ts.map +0 -1
  128. package/dist/shapes-BemroU-z.js.map +0 -1
  129. package/dist/slide-ZqAdVvHV.d.ts.map +0 -1
  130. package/dist/text-B8SLEaZx.js +0 -11327
  131. package/dist/text-B8SLEaZx.js.map +0 -1
  132. package/dist/units-1kRzYShk.js +0 -231
  133. package/dist/units-1kRzYShk.js.map +0 -1
  134. package/dist/units-internal-Dg95w_YD.js.map +0 -1
  135. package/dist/zip.d.ts.map +0 -1
  136. package/dist/zip.js.map +0 -1
@@ -0,0 +1,1611 @@
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
5
+ /**
6
+ * Every recognised format, keyed by its canonical extension. Iteration order is sniffing order;
7
+ * no two signatures overlap, so it only has to be stable, not carefully chosen.
8
+ */
9
+ const IMAGE_FORMATS = Object.freeze({
10
+ png: {
11
+ ext: "png",
12
+ contentType: "image/png",
13
+ altExts: [],
14
+ filenameExt: "png",
15
+ magic: (b) => b.length >= 8 && b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71
16
+ },
17
+ jpeg: {
18
+ ext: "jpeg",
19
+ contentType: "image/jpeg",
20
+ altExts: ["jpg"],
21
+ filenameExt: "jpg",
22
+ magic: (b) => b.length >= 3 && b[0] === 255 && b[1] === 216 && b[2] === 255
23
+ },
24
+ gif: {
25
+ ext: "gif",
26
+ contentType: "image/gif",
27
+ altExts: [],
28
+ filenameExt: "gif",
29
+ magic: (b) => b.length >= 4 && b[0] === 71 && b[1] === 73 && b[2] === 70 && b[3] === 56
30
+ },
31
+ bmp: {
32
+ ext: "bmp",
33
+ contentType: "image/bmp",
34
+ altExts: [],
35
+ filenameExt: "bmp",
36
+ magic: (b) => b.length >= 2 && b[0] === 66 && b[1] === 77
37
+ },
38
+ tiff: {
39
+ ext: "tiff",
40
+ contentType: "image/tiff",
41
+ altExts: ["tif"],
42
+ filenameExt: "tif",
43
+ magic: (b) => b.length >= 4 && (b[0] === 73 && b[1] === 73 && b[2] === 42 && b[3] === 0 || b[0] === 77 && b[1] === 77 && b[2] === 0 && b[3] === 42)
44
+ },
45
+ webp: {
46
+ ext: "webp",
47
+ contentType: "image/webp",
48
+ altExts: [],
49
+ filenameExt: "webp",
50
+ magic: (b) => b.length >= 12 && b[0] === 82 && b[1] === 73 && b[2] === 70 && b[3] === 70 && b[8] === 87 && b[9] === 69 && b[10] === 66 && b[11] === 80
51
+ },
52
+ svg: {
53
+ ext: "svg",
54
+ contentType: "image/svg+xml",
55
+ altExts: [],
56
+ filenameExt: "svg",
57
+ magic: null
58
+ },
59
+ emf: {
60
+ ext: "emf",
61
+ contentType: "image/x-emf",
62
+ altExts: [],
63
+ filenameExt: "emf",
64
+ magic: null
65
+ },
66
+ wmf: {
67
+ ext: "wmf",
68
+ contentType: "image/x-wmf",
69
+ altExts: [],
70
+ filenameExt: "wmf",
71
+ magic: null
72
+ }
73
+ });
74
+ const BY_EXTENSION = new Map(Object.values(IMAGE_FORMATS).flatMap((f) => [f.ext, ...f.altExts].map((e) => [e, f])));
75
+ const BY_CONTENT_TYPE = new Map(Object.values(IMAGE_FORMATS).map((f) => [f.contentType, f]));
76
+ /** The format a caller's file extension names, or `null` for one the registry does not carry. */
77
+ function imageFormatForExtension(extn) {
78
+ return BY_EXTENSION.get((extn || "").toLowerCase()) ?? null;
79
+ }
80
+ /** The format a content type names, or `null` for one the registry does not carry. */
81
+ function imageFormatForContentType(contentType) {
82
+ return BY_CONTENT_TYPE.get((contentType || "").toLowerCase()) ?? null;
83
+ }
84
+ /** The format these leading bytes are the header of, or `null` when nothing matches. */
85
+ function imageFormatForBytes(bytes) {
86
+ if (!bytes) return null;
87
+ for (const format of Object.values(IMAGE_FORMATS)) if (format.magic?.(bytes)) return format;
88
+ return null;
89
+ }
90
+ //#endregion
91
+ //#region src/media/base64.ts
92
+ /**
93
+ * Base64 payload handling for media parts — isomorphic across Node and browsers
94
+ * (`TextEncoder`/`atob`/`btoa` only, no `Buffer`).
95
+ */
96
+ /**
97
+ * Encode raw SVG markup as a base64 `image/svg+xml` data URI.
98
+ * - lets callers pass inline SVG to `addImage({ svg })` without hand-rolling base64
99
+ * - isomorphic and UTF-8 safe: uses the global `TextEncoder`/`btoa` (Node and browsers)
100
+ * @param {string} svg - SVG markup, e.g. `'<svg ...>...</svg>'`
101
+ * @returns {string} a `data:image/svg+xml;base64,...` URI
102
+ */
103
+ function svgMarkupToDataUri(svg) {
104
+ return `data:image/svg+xml;base64,${bytesToBase64(new TextEncoder().encode(svg))}`;
105
+ }
106
+ /**
107
+ * Encode raw bytes as base64 (no `data:` prefix) using only `btoa`, so the result is the same
108
+ * on Node, in a browser, and in a runtime that has neither `Buffer` nor `FileReader`.
109
+ * @param {Uint8Array} bytes - the payload
110
+ * @returns {string} base64 text
111
+ */
112
+ function bytesToBase64(bytes) {
113
+ return btoa(bytesToBinaryString(bytes));
114
+ }
115
+ /**
116
+ * Present a freshly loaded media payload as a `data:` URI.
117
+ *
118
+ * A {@link RuntimeAdapter.loadMedia} implementation returns whichever encoding its host
119
+ * makes cheap — the browser adapter a full URI, because `FileReader.readAsDataURL` is what
120
+ * decodes a blob without blocking; Node and the neutral adapter raw base64, because neither
121
+ * has a `FileReader`. This is where the two become one, so nothing downstream of the load
122
+ * has to ask which it got.
123
+ *
124
+ * The mime label is documentary: {@link decodeBase64ToBytes} ignores it, and the one consumer
125
+ * that needs a real URI (`image.src`, in the browser adapter's SVG preview) is handed the
126
+ * adapter's own. It is taken from the rel anyway rather than hard-coded, so an already-correct
127
+ * label is not overwritten with a wrong one.
128
+ * @param {string} payload - what the adapter returned: raw base64, or an already-formed URI
129
+ * @param {string} contentType - the rel's content type, e.g. `image/png`
130
+ * @returns {string} a `data:` URI
131
+ */
132
+ function toMediaDataUri(payload, contentType) {
133
+ return payload.startsWith("data:") ? payload : `data:${contentType};base64,${payload}`;
134
+ }
135
+ /**
136
+ * Where the payload begins in an inline `data:` URI, or `-1` when the value carries no
137
+ * `base64,` header and is therefore raw base64 already.
138
+ *
139
+ * The one reading of that header, because the two functions below used to spell it separately and
140
+ * disagree: {@link hasBase64Header} lower-cased before looking, {@link decodeBase64ToBytes} did
141
+ * not. RFC 2397 does not case the `;base64` token, so `data:image/png;BASE64,…` passed every
142
+ * definer's check and then decoded from index 0, which `atob` rejects. The media part was written
143
+ * empty, with nothing said. One search means the two can no longer answer differently.
144
+ *
145
+ * Matched with a regular expression rather than by lower-casing and indexing, because
146
+ * `toLowerCase` is not length-preserving for every code point and a shifted index would slice the
147
+ * payload apart.
148
+ */
149
+ function base64PayloadStart(data) {
150
+ const match = /base64,/i.exec(data);
151
+ return match ? match.index + match[0].length : -1;
152
+ }
153
+ /**
154
+ * Decode a base64 image payload (raw base64 or a `data:` URI) to bytes.
155
+ * - tolerant of the `data:[mime];base64,` prefix and of whitespace in the payload
156
+ * @param {string} b64 - base64 string or data URI
157
+ * @returns {Uint8Array | null} decoded bytes, or `null` when the payload is empty/undecodable
158
+ */
159
+ function decodeBase64ToBytes(b64) {
160
+ if (!b64) return null;
161
+ const start = base64PayloadStart(b64);
162
+ const payload = (start >= 0 ? b64.slice(start) : b64).replace(/\s/g, "");
163
+ if (!payload) return null;
164
+ try {
165
+ const binary = atob(payload);
166
+ const bytes = new Uint8Array(binary.length);
167
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
168
+ return bytes;
169
+ } catch {
170
+ return null;
171
+ }
172
+ }
173
+ /**
174
+ * Whether an inline payload carries the `base64,` header the definers ask image and media bytes
175
+ * to arrive with. A value that is not a string has none.
176
+ * @param data - the caller's `data`
177
+ */
178
+ function hasBase64Header(data) {
179
+ return typeof data === "string" && base64PayloadStart(data) >= 0;
180
+ }
181
+ //#endregion
182
+ //#region src/ooxml/namespaces.ts
183
+ /**
184
+ * OOXML namespace URIs, by their canonical prefix.
185
+ *
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.
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.
200
+ */
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
+ /**
228
+ * `a:graphicData/@uri` for a DrawingML table.
229
+ *
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.
234
+ */
235
+ const TABLE_GRAPHIC_DATA_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";
236
+ /**
237
+ * The `xmlns` declarations every PresentationML part root carries, in the order the emitters
238
+ * have always written them.
239
+ *
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.
244
+ */
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
252
+ /**
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.
271
+ *
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.
336
+ */
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";
350
+ /**
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.
353
+ */
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";
403
+ /**
404
+ * Every kind of part a page points at without owning it.
405
+ *
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
516
+ /**
517
+ * `ST_` simple-type enumerations from ECMA-376, declared once as the single source of both the
518
+ * TypeScript union a caller is checked against and the runtime list a value is validated against.
519
+ *
520
+ * **The problem this solves.** Each of these used to exist twice: a hand-written string union in
521
+ * `src/types/` (what the compiler enforces) and a hand-written array in `src/gen/` or `src/read/`
522
+ * (what the validator checks at run time). Nothing tied the two together, so adding a member to
523
+ * one and not the other produced either a value TypeScript accepts and the validator drops, or a
524
+ * value the validator passes and the type rejects. Two of them had already diverged in *order*,
525
+ * which is harmless, but nothing would have caught a divergence in *content*.
526
+ *
527
+ * **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.
529
+ * This is the idiom `EXTRA_SHAPE_PRESETS` already used; this module brings the stragglers onto it,
530
+ * and holds a few closed schema lists that are not `ST_` types, such as `EMBEDDED_FONT_SLOTS`.
531
+ *
532
+ * **Why this module is neither `gen/` nor `read/`.** `src/types/` needs the unions for the public
533
+ * write API, `src/gen/` needs the tuples to vet a value before emitting it, and `src/read/` needs
534
+ * them to vet a value before writing it into a loaded part. It is a fact about the schema, so it
535
+ * belongs to none of the three.
536
+ *
537
+ * Values are listed in **schema declaration order**, which is the order the diagnostics print
538
+ * when they tell a caller what is legal.
539
+ *
540
+ * **The chart option bag reads its enumerations from here**: `BarDirection`, `BarGrouping`,
541
+ * `Bar3DShape`, `LegendPosition`, `DisplayBlanksAs`, `DataLabelPosition` and `LineDataSymbol`
542
+ * first, then the axis and error-bar ones. They were the half of this consolidation that went
543
+ * unfinished for a while. `src/types/chart.ts`
544
+ * hand-wrote four of those unions beside the option that takes them and typed the other three as
545
+ * 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
547
+ * the two spellings had already diverged in content. Both are gone: for every option here, the
548
+ * type a caller is checked against and the tuple the validator checks are one declaration.
549
+ */
550
+ /** `ST_TextAutonumberScheme` — the numbering an auto-numbered bullet counts in (`a:buAutoNum/@type`). */
551
+ const TEXT_AUTONUM_SCHEMES = [
552
+ "alphaLcParenBoth",
553
+ "alphaUcParenBoth",
554
+ "alphaLcParenR",
555
+ "alphaUcParenR",
556
+ "alphaLcPeriod",
557
+ "alphaUcPeriod",
558
+ "arabicParenBoth",
559
+ "arabicParenR",
560
+ "arabicPeriod",
561
+ "arabicPlain",
562
+ "romanLcParenBoth",
563
+ "romanUcParenBoth",
564
+ "romanLcParenR",
565
+ "romanUcParenR",
566
+ "romanLcPeriod",
567
+ "romanUcPeriod",
568
+ "circleNumDbPlain",
569
+ "circleNumWdBlackPlain",
570
+ "circleNumWdWhitePlain",
571
+ "arabicDbPeriod",
572
+ "arabicDbPlain",
573
+ "ea1ChsPeriod",
574
+ "ea1ChsPlain",
575
+ "ea1ChtPeriod",
576
+ "ea1ChtPlain",
577
+ "ea1JpnChsDbPeriod",
578
+ "ea1JpnKorPlain",
579
+ "ea1JpnKorPeriod",
580
+ "arabic1Minus",
581
+ "arabic2Minus",
582
+ "hebrew2Minus",
583
+ "thaiAlphaPeriod",
584
+ "thaiAlphaParenR",
585
+ "thaiAlphaParenBoth",
586
+ "thaiNumPeriod",
587
+ "thaiNumParenR",
588
+ "thaiNumParenBoth",
589
+ "hindiAlphaPeriod",
590
+ "hindiNumPeriod",
591
+ "hindiNumParenR",
592
+ "hindiAlpha1Period"
593
+ ];
594
+ /** `ST_TextStrikeType` — a run's strikethrough (`a:rPr/@strike`). */
595
+ const TEXT_STRIKE_TYPES = [
596
+ "noStrike",
597
+ "sngStrike",
598
+ "dblStrike"
599
+ ];
600
+ /** `ST_TextCapsType` — a run's capitalization (`a:rPr/@cap`). */
601
+ const TEXT_CAPS_TYPES = [
602
+ "none",
603
+ "small",
604
+ "all"
605
+ ];
606
+ /** `ST_TextUnderlineType` — a run's underline (`a:rPr/@u`). */
607
+ const TEXT_UNDERLINE_TYPES = [
608
+ "none",
609
+ "words",
610
+ "sng",
611
+ "dbl",
612
+ "heavy",
613
+ "dotted",
614
+ "dottedHeavy",
615
+ "dash",
616
+ "dashHeavy",
617
+ "dashLong",
618
+ "dashLongHeavy",
619
+ "dotDash",
620
+ "dotDashHeavy",
621
+ "dotDotDash",
622
+ "dotDotDashHeavy",
623
+ "wavy",
624
+ "wavyHeavy",
625
+ "wavyDbl"
626
+ ];
627
+ /** `ST_TextTabAlignType` — a tab stop's alignment (`a:tab/@algn`). */
628
+ const TEXT_TAB_ALIGN_TYPES = [
629
+ "l",
630
+ "ctr",
631
+ "r",
632
+ "dec"
633
+ ];
634
+ /**
635
+ * `ST_SchemeColorVal` — a theme colour reference (`a:schemeClr/@val`). Wider than
636
+ * {@link THEME_COLOR_SLOTS}: it adds the `bg1`/`tx1`/`bg2`/`tx2` aliases the colour map resolves,
637
+ * and `phClr`, the colour a theme style-matrix entry is applied in.
638
+ */
639
+ const SCHEME_COLOR_VALUES = [
640
+ "bg1",
641
+ "tx1",
642
+ "bg2",
643
+ "tx2",
644
+ "accent1",
645
+ "accent2",
646
+ "accent3",
647
+ "accent4",
648
+ "accent5",
649
+ "accent6",
650
+ "hlink",
651
+ "folHlink",
652
+ "phClr",
653
+ "dk1",
654
+ "lt1",
655
+ "dk2",
656
+ "lt2"
657
+ ];
658
+ /**
659
+ * The base slide transitions: the `p:` type elements of `CT_SlideTransition`'s choice, in its
660
+ * declaration order. The writer emits the type as `<p:TYPE/>`, so this is also the list a type is
661
+ * checked against before it becomes an element name, and the list the script converter uses to
662
+ * decide which read transitions the write API can express. PowerPoint's modern transitions live in
663
+ * other namespaces and are not here.
664
+ */
665
+ const TRANSITION_TYPES = [
666
+ "blinds",
667
+ "checker",
668
+ "circle",
669
+ "dissolve",
670
+ "comb",
671
+ "cover",
672
+ "cut",
673
+ "diamond",
674
+ "fade",
675
+ "newsflash",
676
+ "plus",
677
+ "pull",
678
+ "push",
679
+ "random",
680
+ "randomBar",
681
+ "split",
682
+ "strips",
683
+ "wedge",
684
+ "wheel",
685
+ "wipe",
686
+ "zoom"
687
+ ];
688
+ /**
689
+ * The attributes each of {@link TRANSITION_TYPES} declares, which is every key a transition's
690
+ * `variant` can write. Seven are `CT_Empty` and take none.
691
+ */
692
+ const TRANSITION_VARIANT_ATTRIBUTES = {
693
+ blinds: ["dir"],
694
+ checker: ["dir"],
695
+ circle: [],
696
+ dissolve: [],
697
+ comb: ["dir"],
698
+ cover: ["dir"],
699
+ cut: ["thruBlk"],
700
+ diamond: [],
701
+ fade: ["thruBlk"],
702
+ newsflash: [],
703
+ plus: [],
704
+ pull: ["dir"],
705
+ push: ["dir"],
706
+ random: [],
707
+ randomBar: ["dir"],
708
+ split: ["orient", "dir"],
709
+ strips: ["dir"],
710
+ wedge: [],
711
+ wheel: ["spokes"],
712
+ wipe: ["dir"],
713
+ zoom: ["dir"]
714
+ };
715
+ /**
716
+ * `ST_BevelPresetType` — the bevel profile applied to a 3-D edge.
717
+ * @see ECMA-376 Part 1 §20.1.10.9
718
+ */
719
+ const BEVEL_PRESETS = [
720
+ "relaxedInset",
721
+ "circle",
722
+ "slope",
723
+ "cross",
724
+ "angle",
725
+ "softRound",
726
+ "convex",
727
+ "coolSlant",
728
+ "divot",
729
+ "riblet",
730
+ "hardEdge",
731
+ "artDeco"
732
+ ];
733
+ /**
734
+ * `ST_PresetMaterialType` — how a 3-D surface responds to light.
735
+ * @see ECMA-376 Part 1 §20.1.10.50
736
+ */
737
+ const PRESET_MATERIALS = [
738
+ "legacyMatte",
739
+ "legacyPlastic",
740
+ "legacyMetal",
741
+ "legacyWireframe",
742
+ "matte",
743
+ "plastic",
744
+ "metal",
745
+ "warmMatte",
746
+ "translucentPowder",
747
+ "powder",
748
+ "dkEdge",
749
+ "softEdge",
750
+ "clear",
751
+ "flat",
752
+ "softmetal"
753
+ ];
754
+ /**
755
+ * `ST_LightRigType` — the preset lighting scene.
756
+ * @see ECMA-376 Part 1 §20.1.10.35
757
+ */
758
+ const LIGHT_RIGS = [
759
+ "legacyFlat1",
760
+ "legacyFlat2",
761
+ "legacyFlat3",
762
+ "legacyFlat4",
763
+ "legacyNormal1",
764
+ "legacyNormal2",
765
+ "legacyNormal3",
766
+ "legacyNormal4",
767
+ "legacyHarsh1",
768
+ "legacyHarsh2",
769
+ "legacyHarsh3",
770
+ "legacyHarsh4",
771
+ "threePt",
772
+ "balanced",
773
+ "soft",
774
+ "harsh",
775
+ "flood",
776
+ "contrasting",
777
+ "morning",
778
+ "sunrise",
779
+ "sunset",
780
+ "chilly",
781
+ "freezing",
782
+ "flat",
783
+ "twoPt",
784
+ "glow",
785
+ "brightRoom"
786
+ ];
787
+ /**
788
+ * `ST_LightRigDirection` — where the light rig sits relative to the scene.
789
+ * @see ECMA-376 Part 1 §20.1.10.33
790
+ */
791
+ const LIGHT_RIG_DIRECTIONS = [
792
+ "tl",
793
+ "t",
794
+ "tr",
795
+ "l",
796
+ "r",
797
+ "bl",
798
+ "b",
799
+ "br"
800
+ ];
801
+ /**
802
+ * `ST_TextHorzOverflowType` — `a:bodyPr/@horzOverflow` and `a:tcPr/@horzOverflow`.
803
+ * @see ECMA-376 Part 1 §20.1.10.68
804
+ */
805
+ const TEXT_HORZ_OVERFLOW = ["clip", "overflow"];
806
+ /**
807
+ * `ST_TextAnchoringType` — `a:bodyPr/@anchor` and `a:tcPr/@anchor`.
808
+ * @see ECMA-376 Part 1 §20.1.10.60
809
+ */
810
+ const TEXT_ANCHORS = [
811
+ "t",
812
+ "ctr",
813
+ "b",
814
+ "just",
815
+ "dist"
816
+ ];
817
+ /**
818
+ * `ST_TextVerticalType` — `a:bodyPr/@vert` and `a:tcPr/@vert`.
819
+ * @see ECMA-376 Part 1 §20.1.10.83
820
+ */
821
+ const TEXT_VERTICAL = [
822
+ "horz",
823
+ "vert",
824
+ "vert270",
825
+ "wordArtVert",
826
+ "eaVert",
827
+ "mongolianVert",
828
+ "wordArtVertRtl"
829
+ ];
830
+ /**
831
+ * `ST_LineEndType` — the arrowhead at either end of a stroke (`a:headEnd/@type`,
832
+ * `a:tailEnd/@type`).
833
+ * @see ECMA-376 Part 1 §20.1.10.33
834
+ */
835
+ const LINE_END_TYPES = [
836
+ "none",
837
+ "triangle",
838
+ "stealth",
839
+ "diamond",
840
+ "oval",
841
+ "arrow"
842
+ ];
843
+ /**
844
+ * The four face slots of `CT_EmbeddedFontListEntry`, in schema child order (`p:font` first, then
845
+ * these). Not an `ST_` type, but a closed, ordered set the schema declares, like
846
+ * {@link THEME_COLOR_SLOTS}. The read and write sides iterate it so their emitters agree on
847
+ * element order, and `sequence.ts` derives `EMBEDDED_FONT_ENTRY_SEQUENCE` from it, so the order
848
+ * the serializer writes and the order the insertion points are computed from are one fact.
849
+ * `embedded-fonts.ts` re-exports it.
850
+ */
851
+ const EMBEDDED_FONT_SLOTS = [
852
+ "regular",
853
+ "bold",
854
+ "italic",
855
+ "boldItalic"
856
+ ];
857
+ /**
858
+ * `ST_TextShapeType` — the preset path text is bent along (`a:prstTxWarp/@prst`), which
859
+ * PowerPoint calls WordArt.
860
+ *
861
+ * `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.
863
+ * @see ECMA-376 Part 1 §20.1.10.76
864
+ */
865
+ const TEXT_SHAPE_TYPES = [
866
+ "textNoShape",
867
+ "textPlain",
868
+ "textStop",
869
+ "textTriangle",
870
+ "textTriangleInverted",
871
+ "textChevron",
872
+ "textChevronInverted",
873
+ "textRingInside",
874
+ "textRingOutside",
875
+ "textArchUp",
876
+ "textArchDown",
877
+ "textCircle",
878
+ "textButton",
879
+ "textArchUpPour",
880
+ "textArchDownPour",
881
+ "textCirclePour",
882
+ "textButtonPour",
883
+ "textCurveUp",
884
+ "textCurveDown",
885
+ "textCanUp",
886
+ "textCanDown",
887
+ "textWave1",
888
+ "textWave2",
889
+ "textDoubleWave1",
890
+ "textWave4",
891
+ "textInflate",
892
+ "textDeflate",
893
+ "textInflateBottom",
894
+ "textDeflateBottom",
895
+ "textInflateTop",
896
+ "textDeflateTop",
897
+ "textDeflateInflate",
898
+ "textDeflateInflateDeflate",
899
+ "textFadeRight",
900
+ "textFadeLeft",
901
+ "textFadeUp",
902
+ "textFadeDown",
903
+ "textSlantUp",
904
+ "textSlantDown",
905
+ "textCascadeUp",
906
+ "textCascadeDown"
907
+ ];
908
+ /**
909
+ * `ST_PresetLineDashVal` — the dash pattern of a line or border (`a:prstDash/@val`).
910
+ *
911
+ * The `sys*` members are the "system" dashes whose exact dot/gap ratio the renderer picks; the
912
+ * rest are defined by the spec. Both `BorderProps.dashType` and `ShapeLineProps.dashType` accept
913
+ * this whole set, so a caller who knows one knows the other.
914
+ * @see ECMA-376 Part 1 §20.1.10.49
915
+ */
916
+ const PRESET_LINE_DASHES = [
917
+ "solid",
918
+ "dot",
919
+ "dash",
920
+ "lgDash",
921
+ "dashDot",
922
+ "lgDashDot",
923
+ "lgDashDotDot",
924
+ "sysDash",
925
+ "sysDot",
926
+ "sysDashDot",
927
+ "sysDashDotDot"
928
+ ];
929
+ /**
930
+ * `ST_BarDir` — whether a bar/column plot runs horizontally or vertically (`c:barDir/@val`).
931
+ * @see ECMA-376 Part 1 §21.2.3.3
932
+ */
933
+ const BAR_DIRECTIONS = ["bar", "col"];
934
+ /**
935
+ * `ST_BarGrouping` — how a bar/column plot stacks its series (`c:grouping/@val` inside
936
+ * `c:barChart`/`c:bar3DChart`).
937
+ * @see ECMA-376 Part 1 §21.2.3.4
938
+ */
939
+ const BAR_GROUPINGS = [
940
+ "percentStacked",
941
+ "clustered",
942
+ "standard",
943
+ "stacked"
944
+ ];
945
+ /**
946
+ * `ST_Grouping` — the same decision for the plots that have no clustered form
947
+ * (`c:areaChart`, `c:lineChart`).
948
+ * @see ECMA-376 Part 1 §21.2.3.17
949
+ */
950
+ const GROUPINGS = [
951
+ "percentStacked",
952
+ "standard",
953
+ "stacked"
954
+ ];
955
+ /**
956
+ * `ST_Shape` — the solid a 3-D bar is drawn as (`c:shape/@val`).
957
+ * @see ECMA-376 Part 1 §21.2.3.35
958
+ */
959
+ const BAR_3D_SHAPES = [
960
+ "cone",
961
+ "coneToMax",
962
+ "box",
963
+ "cylinder",
964
+ "pyramid",
965
+ "pyramidToMax"
966
+ ];
967
+ /**
968
+ * `ST_RadarStyle` — how a radar plot is drawn (`c:radarStyle/@val`).
969
+ * @see ECMA-376 Part 1 §21.2.3.33
970
+ */
971
+ const RADAR_STYLES = [
972
+ "standard",
973
+ "marker",
974
+ "filled"
975
+ ];
976
+ /**
977
+ * This library's own names for two of the three radar styles, mapped to the wire spelling each
978
+ * one means. They are the PowerPoint UI's words ("Radar", "Radar with Markers"), which is why
979
+ * they were chosen; `filled` needed no rename and is already the schema's own spelling, which is
980
+ * what made the other two read as typos rather than as a second vocabulary.
981
+ *
982
+ * 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
985
+ * asking for markers got a plain radar.
986
+ */
987
+ const RADAR_STYLE_ALIASES = {
988
+ radar: "standard",
989
+ markers: "marker"
990
+ };
991
+ /**
992
+ * 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
994
+ * against and the union it is typed against cannot drift apart.
995
+ */
996
+ const RADAR_STYLE_ALIAS_NAMES = Object.keys(RADAR_STYLE_ALIASES);
997
+ /**
998
+ * `ST_LegendPos` — where the legend sits relative to the plot area (`c:legendPos/@val`).
999
+ * @see ECMA-376 Part 1 §21.2.3.24
1000
+ */
1001
+ const LEGEND_POSITIONS = [
1002
+ "b",
1003
+ "tr",
1004
+ "l",
1005
+ "r",
1006
+ "t"
1007
+ ];
1008
+ /**
1009
+ * `ST_DispBlanksAs` — what a plot draws where a value is missing (`c:dispBlanksAs/@val`).
1010
+ * @see ECMA-376 Part 1 §21.2.3.10
1011
+ */
1012
+ const DISPLAY_BLANKS_AS = [
1013
+ "span",
1014
+ "gap",
1015
+ "zero"
1016
+ ];
1017
+ /**
1018
+ * `ST_MarkerStyle`, **narrowed** to what the library authors (`c:symbol/@val`).
1019
+ *
1020
+ * The schema also has `plus`, `star`, `x`, `picture` and `auto`. Neither PowerPoint 2013 nor
1021
+ * 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
1023
+ * deliberately smaller than the type. Widen it only with evidence that the member renders.
1024
+ * @see ECMA-376 Part 1 §21.2.3.27
1025
+ */
1026
+ const LINE_DATA_SYMBOLS = [
1027
+ "circle",
1028
+ "dash",
1029
+ "diamond",
1030
+ "dot",
1031
+ "none",
1032
+ "square",
1033
+ "triangle"
1034
+ ];
1035
+ /** `ST_TickLblPos` — where an axis draws its tick labels (`c:tickLblPos/@val`). */
1036
+ const TICK_LABEL_POSITIONS = [
1037
+ "high",
1038
+ "low",
1039
+ "nextTo",
1040
+ "none"
1041
+ ];
1042
+ /** `ST_Orientation` — which way an axis runs (`c:orientation/@val`). */
1043
+ const AXIS_ORIENTATIONS = ["maxMin", "minMax"];
1044
+ /** `ST_TickMark` — an axis' major or minor tick marks (`c:majorTickMark/@val`, `c:minorTickMark/@val`). */
1045
+ const TICK_MARKS = [
1046
+ "cross",
1047
+ "in",
1048
+ "none",
1049
+ "out"
1050
+ ];
1051
+ /** `ST_CrossBetween` — whether a value axis crosses its category axis between or on categories (`c:crossBetween/@val`). */
1052
+ const CROSS_BETWEEN = ["between", "midCat"];
1053
+ /** `ST_BuiltInUnit` — the unit a value axis displays its labels in (`c:builtInUnit/@val`). */
1054
+ const BUILT_IN_UNITS = [
1055
+ "hundreds",
1056
+ "thousands",
1057
+ "tenThousands",
1058
+ "hundredThousands",
1059
+ "millions",
1060
+ "tenMillions",
1061
+ "hundredMillions",
1062
+ "billions",
1063
+ "trillions"
1064
+ ];
1065
+ /** `ST_TimeUnit` — a date axis' base, major or minor time unit (`c:baseTimeUnit/@val` and its two siblings). */
1066
+ const TIME_UNITS = [
1067
+ "days",
1068
+ "months",
1069
+ "years"
1070
+ ];
1071
+ /** `ST_ErrDir` — the axis an error bar measures along (`c:errDir/@val`). */
1072
+ const ERROR_BAR_DIRECTIONS = ["x", "y"];
1073
+ /** `ST_ErrBarType` — which sides of a point draw an error bar (`c:errBarType/@val`). */
1074
+ const ERROR_BAR_TYPES = [
1075
+ "both",
1076
+ "minus",
1077
+ "plus"
1078
+ ];
1079
+ /** `ST_ErrValType` — how an error bar's amount is read (`c:errValType/@val`). */
1080
+ const ERROR_BAR_VALUE_TYPES = [
1081
+ "cust",
1082
+ "fixedVal",
1083
+ "percentage",
1084
+ "stdDev",
1085
+ "stdErr"
1086
+ ];
1087
+ /**
1088
+ * The twelve theme colour slots (`a:clrScheme` children) a `p:clrMap` token can point at.
1089
+ *
1090
+ * Not an `ST_` type — these are element names inside `a:CT_ColorScheme`, which the schema
1091
+ * declares as a fixed sequence rather than an enumeration. The effect is the same: a closed,
1092
+ * ordered set of twelve names, in the order the scheme declares them.
1093
+ * @see ECMA-376 Part 1 §20.1.6.2
1094
+ */
1095
+ const THEME_COLOR_SLOTS = [
1096
+ "dk1",
1097
+ "lt1",
1098
+ "dk2",
1099
+ "lt2",
1100
+ "accent1",
1101
+ "accent2",
1102
+ "accent3",
1103
+ "accent4",
1104
+ "accent5",
1105
+ "accent6",
1106
+ "hlink",
1107
+ "folHlink"
1108
+ ];
1109
+ /**
1110
+ * The twelve colour-map tokens (`p:clrMap` attributes), in schema attribute order.
1111
+ *
1112
+ * A slide's `schemeClr val="tx1"` names a *token*, not a slot: the master's `p:clrMap` is the
1113
+ * 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.
1115
+ * @see ECMA-376 Part 1 §19.3.1.6
1116
+ */
1117
+ const COLOR_MAP_TOKENS = [
1118
+ "bg1",
1119
+ "tx1",
1120
+ "bg2",
1121
+ "tx2",
1122
+ "accent1",
1123
+ "accent2",
1124
+ "accent3",
1125
+ "accent4",
1126
+ "accent5",
1127
+ "accent6",
1128
+ "hlink",
1129
+ "folHlink"
1130
+ ];
1131
+ /**
1132
+ * The identity colour map: the `p:clrMap` this library emits on every master and notes master,
1133
+ * and the one the script printer recognises so it can skip printing a map that is the default.
1134
+ *
1135
+ * "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.
1138
+ *
1139
+ * Typed `Record<ColorMapToken, ThemeColorSlot>` so a token added to {@link COLOR_MAP_TOKENS}
1140
+ * without a mapping, or a slot misspelled, is a compile error rather than a theme that opens
1141
+ * fine and paints one script in the wrong colour. Declared in the same order as the tokens,
1142
+ * because it is emitted by iterating this object and the attribute order is part of the bytes.
1143
+ */
1144
+ const DEFAULT_COLOR_MAP = {
1145
+ bg1: "lt1",
1146
+ tx1: "dk1",
1147
+ bg2: "lt2",
1148
+ tx2: "dk2",
1149
+ accent1: "accent1",
1150
+ accent2: "accent2",
1151
+ accent3: "accent3",
1152
+ accent4: "accent4",
1153
+ accent5: "accent5",
1154
+ accent6: "accent6",
1155
+ hlink: "hlink",
1156
+ folHlink: "folHlink"
1157
+ };
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
+ */
1177
+ /**
1178
+ * A colour written as six hex digits, `#` already stripped.
1179
+ *
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.
1192
+ *
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 `#`
1197
+ */
1198
+ function isHexColor(value) {
1199
+ return REGEX_HEX_COLOR.test(stripHash(value));
1200
+ }
1201
+ /**
1202
+ * Is `value` an 8-hex RGBA colour? A leading `#` is stripped first.
1203
+ *
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 `#`
1206
+ */
1207
+ function isArgbHex(value) {
1208
+ return REGEX_ARGB_HEX.test(stripHash(value));
1209
+ }
1210
+ /**
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.
1213
+ *
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 `#`
1219
+ */
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
1230
+ /**
1231
+ * Parsing an `xsd:boolean` attribute value.
1232
+ *
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.
1238
+ *
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.
1245
+ *
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.
1248
+ *
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.
1253
+ */
1254
+ /**
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`.
1260
+ */
1261
+ function boolValue(value) {
1262
+ if (value === "1" || value === "true") return true;
1263
+ if (value === "0" || value === "false") return false;
1264
+ return null;
1265
+ }
1266
+ /**
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
1273
+ */
1274
+ function xsdBool(value) {
1275
+ return value ? "1" : "0";
1276
+ }
1277
+ /**
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
1286
+ */
1287
+ function xsdBoolIfTrue(value) {
1288
+ return value ? "1" : null;
1289
+ }
1290
+ //#endregion
1291
+ //#region src/ooxml/check-enum.ts
1292
+ /**
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
+ *
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
1327
+ */
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
1334
+ /**
1335
+ * Every `cap` the write API names and the token it writes. `satisfies` over `Record<LineCap, …>`
1336
+ * makes a member added to `LineCap` without a token a compile error, and `ST_LineCap` has exactly
1337
+ * these three members, so the inverse covers every token too.
1338
+ */
1339
+ const TOKEN_BY_LINE_CAP = {
1340
+ flat: "flat",
1341
+ square: "sq",
1342
+ round: "rnd"
1343
+ };
1344
+ /** The `cap` token a `LineCap` writes, or `undefined` for a value the write API does not name. */
1345
+ function lineCapToken(cap) {
1346
+ return Object.hasOwn(TOKEN_BY_LINE_CAP, cap) ? TOKEN_BY_LINE_CAP[cap] : void 0;
1347
+ }
1348
+ /** A `cap` token back to the `LineCap` that authors it. */
1349
+ 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
1352
+ /**
1353
+ * Every `align` the write API names and the token it writes. `satisfies` over `Record<HAlign, …>`
1354
+ * makes a member added to `HAlign` without a token a compile error. `justLow`, `dist` and
1355
+ * `thaiDist` have no `align` spelling.
1356
+ */
1357
+ const ALGN_BY_ALIGN = {
1358
+ left: "l",
1359
+ center: "ctr",
1360
+ right: "r",
1361
+ justify: "just"
1362
+ };
1363
+ /** The `algn` token an `align` value writes, or `undefined` for a value the write API does not name. */
1364
+ function textAlignToken(align) {
1365
+ return Object.hasOwn(ALGN_BY_ALIGN, align) ? ALGN_BY_ALIGN[align] : void 0;
1366
+ }
1367
+ /** An `algn` token back to the `align` a generated script writes. */
1368
+ 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
1371
+ /**
1372
+ * Intrinsic image geometry: reading a natural pixel size out of header bytes, and the
1373
+ * `<a:srcRect>` crop math that fits that natural size into a display box.
1374
+ *
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/`.
1377
+ * Everything here is synchronous and pure: headers only, never pixel decoding.
1378
+ */
1379
+ /**
1380
+ * Read the intrinsic dimensions of an image from its header bytes.
1381
+ * - 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
1384
+ * - unrecognized formats return `null` (no measurable intrinsic size)
1385
+ *
1386
+ * Used by image `sizing: 'cover' | 'contain'` to compute an aspect-correct
1387
+ * `<a:srcRect>` crop from the *natural* image ratio rather than the displayed box.
1388
+ * @param {string} dataB64 - base64 image payload or `data:` URI
1389
+ * @returns {{ w: number, h: number } | null} natural size, or `null` when unmeasurable
1390
+ */
1391
+ function getImageSizeFromBase64(dataB64) {
1392
+ const b = decodeBase64ToBytes(dataB64);
1393
+ return b ? getImageSizeFromBytes(b) : null;
1394
+ }
1395
+ /**
1396
+ * Read the intrinsic dimensions of an image from raw header bytes — the
1397
+ * byte-level core shared by {@link getImageSizeFromBase64} and the read API's
1398
+ * `Picture.setImage({ fit })`, which already holds the media bytes.
1399
+ * @param {Uint8Array} b - image bytes
1400
+ * @returns {{ w: number, h: number } | null} natural size, or `null` when unmeasurable
1401
+ */
1402
+ function getImageSizeFromBytes(b) {
1403
+ if (!b || b.length < 24) return null;
1404
+ const u = (n) => b[n] ?? 0;
1405
+ if (IMAGE_FORMATS.png.magic?.(b)) {
1406
+ const w = u(16) << 24 | u(17) << 16 | u(18) << 8 | u(19);
1407
+ const h = u(20) << 24 | u(21) << 16 | u(22) << 8 | u(23);
1408
+ return w > 0 && h > 0 ? {
1409
+ w,
1410
+ h
1411
+ } : null;
1412
+ }
1413
+ if (IMAGE_FORMATS.gif.magic?.(b)) {
1414
+ const w = u(6) | u(7) << 8;
1415
+ const h = u(8) | u(9) << 8;
1416
+ return w > 0 && h > 0 ? {
1417
+ w,
1418
+ h
1419
+ } : null;
1420
+ }
1421
+ if (IMAGE_FORMATS.bmp.magic?.(b)) {
1422
+ if ((u(14) | u(15) << 8 | u(16) << 16 | u(17) << 24) === 12) {
1423
+ const w = u(18) | u(19) << 8;
1424
+ const h = u(20) | u(21) << 8;
1425
+ return w > 0 && h > 0 ? {
1426
+ w,
1427
+ h
1428
+ } : null;
1429
+ }
1430
+ if (b.length < 26) return null;
1431
+ const w = u(18) | u(19) << 8 | u(20) << 16 | u(21) << 24;
1432
+ const h = u(22) | u(23) << 8 | u(24) << 16 | u(25) << 24;
1433
+ const aw = Math.abs(w);
1434
+ const ah = Math.abs(h);
1435
+ return aw > 0 && ah > 0 ? {
1436
+ w: aw,
1437
+ h: ah
1438
+ } : null;
1439
+ }
1440
+ if (IMAGE_FORMATS.webp.magic?.(b)) {
1441
+ const fourCC = String.fromCharCode(u(12), u(13), u(14), u(15));
1442
+ if (fourCC === "VP8 " && b.length >= 30) {
1443
+ const w = (u(26) | u(27) << 8) & 16383;
1444
+ const h = (u(28) | u(29) << 8) & 16383;
1445
+ return w > 0 && h > 0 ? {
1446
+ w,
1447
+ h
1448
+ } : null;
1449
+ }
1450
+ if (fourCC === "VP8L" && b.length >= 25) {
1451
+ const bits = u(21) | u(22) << 8 | u(23) << 16 | u(24) << 24;
1452
+ const w = (bits & 16383) + 1;
1453
+ const h = (bits >> 14 & 16383) + 1;
1454
+ return w > 0 && h > 0 ? {
1455
+ w,
1456
+ h
1457
+ } : null;
1458
+ }
1459
+ if (fourCC === "VP8X" && b.length >= 30) {
1460
+ const w = (u(24) | u(25) << 8 | u(26) << 16) + 1;
1461
+ const h = (u(27) | u(28) << 8 | u(29) << 16) + 1;
1462
+ return w > 0 && h > 0 ? {
1463
+ w,
1464
+ h
1465
+ } : null;
1466
+ }
1467
+ return null;
1468
+ }
1469
+ if (IMAGE_FORMATS.jpeg.magic?.(b)) {
1470
+ let i = 2;
1471
+ while (i + 9 < b.length) {
1472
+ if (b[i] !== 255) {
1473
+ i++;
1474
+ continue;
1475
+ }
1476
+ const marker = u(i + 1);
1477
+ if (marker === 255) {
1478
+ i++;
1479
+ continue;
1480
+ }
1481
+ if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) {
1482
+ const h = u(i + 5) << 8 | u(i + 6);
1483
+ const w = u(i + 7) << 8 | u(i + 8);
1484
+ return w > 0 && h > 0 ? {
1485
+ w,
1486
+ h
1487
+ } : null;
1488
+ }
1489
+ if (marker >= 208 && marker <= 217 || marker === 1) {
1490
+ i += 2;
1491
+ continue;
1492
+ }
1493
+ const segLen = u(i + 2) << 8 | u(i + 3);
1494
+ if (segLen < 2) break;
1495
+ i += 2 + segLen;
1496
+ }
1497
+ return null;
1498
+ }
1499
+ if (IMAGE_FORMATS.tiff.magic(b)) {
1500
+ const little = b[0] === 73;
1501
+ const u16 = (n) => little ? u(n) | u(n + 1) << 8 : u(n) << 8 | u(n + 1);
1502
+ const u32 = (n) => little ? (u(n) | u(n + 1) << 8 | u(n + 2) << 16 | u(n + 3) << 24) >>> 0 : (u(n) << 24 | u(n + 1) << 16 | u(n + 2) << 8 | u(n + 3)) >>> 0;
1503
+ const ifd = u32(4);
1504
+ if (ifd + 2 > b.length) return null;
1505
+ const entries = u16(ifd);
1506
+ let w = 0;
1507
+ let h = 0;
1508
+ for (let i = 0; i < entries; i++) {
1509
+ const entry = ifd + 2 + i * 12;
1510
+ if (entry + 12 > b.length) return null;
1511
+ const tag = u16(entry);
1512
+ if (tag !== 256 && tag !== 257) continue;
1513
+ const value = u16(entry + 2) === 3 ? u16(entry + 8) : u32(entry + 8);
1514
+ if (tag === 256) w = value;
1515
+ else h = value;
1516
+ }
1517
+ return w > 0 && h > 0 ? {
1518
+ w,
1519
+ h
1520
+ } : null;
1521
+ }
1522
+ const text = utf8Decode(b);
1523
+ if (/<svg[\s>]/i.test(text)) return getSvgSizeFromMarkup(text);
1524
+ return null;
1525
+ }
1526
+ /**
1527
+ * Compute the `<a:srcRect>` crop percentages (each in 1/1000 of a percent, the
1528
+ * OOXML unit) for fitting an image of natural size `img` into a display `box`,
1529
+ * assuming the cropped region is then stretched to fill the box (`<a:stretch>`).
1530
+ *
1531
+ * - `cover`: fill the box, cropping the overflowing axis (positive l/r or t/b)
1532
+ * - `contain`: fit inside the box, letterboxing the short axis (negative l/r or t/b)
1533
+ *
1534
+ * Single source of truth for the crop math shared by the write side
1535
+ * (`ImageSizingXml`) and the read API's `Picture.setImage({ fit })`. `l`/`r` and
1536
+ * `t`/`b` are symmetric (centered crop).
1537
+ *
1538
+ * There is no fit for a side that is not greater than zero: a zero-size box gave every percentage
1539
+ * as `NaN`, and a zero-width one gave `-Infinity`, both written straight into the part.
1540
+ * @param {'cover' | 'contain'} type - fit mode
1541
+ * @param {{ w: number, h: number }} img - natural image pixel size
1542
+ * @param {{ w: number, h: number }} box - displayed frame size (any consistent unit)
1543
+ * @returns {{ l: number, r: number, t: number, b: number } | null} srcRect percentages, or `null`
1544
+ * when any of the four sizes is not greater than zero
1545
+ */
1546
+ function fitSrcRectPercents(type, img, box) {
1547
+ if (!(img.w > 0 && img.h > 0 && box.w > 0 && box.h > 0)) return null;
1548
+ const imgRatio = img.h / img.w;
1549
+ const boxRatio = box.h / box.w;
1550
+ let width;
1551
+ let height;
1552
+ if (type === "cover") {
1553
+ const isBoxBased = boxRatio > imgRatio;
1554
+ width = isBoxBased ? box.h / imgRatio : box.w;
1555
+ height = isBoxBased ? box.h : box.w * imgRatio;
1556
+ } else {
1557
+ const widthBased = boxRatio > imgRatio;
1558
+ width = widthBased ? box.w : box.h / imgRatio;
1559
+ height = widthBased ? box.w * imgRatio : box.h;
1560
+ }
1561
+ const hz = Math.round(PERCENT_SCALE * .5 * (1 - box.w / width));
1562
+ const vz = Math.round(PERCENT_SCALE * .5 * (1 - box.h / height));
1563
+ return {
1564
+ l: hz,
1565
+ r: hz,
1566
+ t: vz,
1567
+ b: vz
1568
+ };
1569
+ }
1570
+ /**
1571
+ * Read the intrinsic size of an SVG document from its root `<svg>` element.
1572
+ * Follows the SVG sizing model: an explicit absolute `width`/`height` pair wins;
1573
+ * otherwise the `viewBox` width/height defines the size (and thus aspect ratio).
1574
+ * Percentage or missing `width`/`height` fall through to `viewBox`.
1575
+ * @param {string} svg - SVG markup
1576
+ * @returns {{ w: number, h: number } | null} intrinsic size, or `null` when undeterminable
1577
+ */
1578
+ function getSvgSizeFromMarkup(svg) {
1579
+ const openTag = /<svg\b[^>]*>/i.exec(svg)?.[0];
1580
+ if (!openTag) return null;
1581
+ const attr = (name) => new RegExp(`(?:^|\\s)${name}\\s*=\\s*(["'])(.*?)\\1`, "i").exec(openTag)?.[2] ?? null;
1582
+ const absLength = (val) => {
1583
+ if (val == null || /%\s*$/.test(val)) return NaN;
1584
+ const m = /^\s*\+?(\d*\.?\d+)/.exec(val);
1585
+ return m ? parseFloat(m[1] ?? "") : NaN;
1586
+ };
1587
+ let w = absLength(attr("width"));
1588
+ let h = absLength(attr("height"));
1589
+ if (!(w > 0 && h > 0)) {
1590
+ const vb = attr("viewBox");
1591
+ const p = vb ? vb.trim().split(/[\s,]+/).map(Number) : [];
1592
+ const vw = p[2];
1593
+ const vh = p[3];
1594
+ if (p.length === 4 && vw != null && vh != null && vw > 0 && vh > 0) {
1595
+ w = vw;
1596
+ h = vh;
1597
+ }
1598
+ }
1599
+ return w > 0 && h > 0 ? {
1600
+ w,
1601
+ h
1602
+ } : null;
1603
+ }
1604
+ /** Decode UTF-8 bytes to a string, isomorphic across Node and browsers. */
1605
+ function utf8Decode(bytes) {
1606
+ return new TextDecoder().decode(bytes);
1607
+ }
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 };
1610
+
1611
+ //# sourceMappingURL=image-size-CvN2iCA4.js.map