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,649 @@
1
+ import { l as warn, n as InvalidOptionError, u as warnOnce } from "./errors-C4oENPcu.js";
2
+ import { A as inchesToEmu, E as coordToEmu, F as EMU_PER_INCH, I as EMU_PER_POINT, S as MAX_COORDINATE_EMU, b as FIXED_PCT_PER_PERCENT, t as TABLE_CELL_MARGIN_DEFAULTS_EMU, v as ANGLE_UNITS_PER_DEGREE, w as PERCENT_SCALE } from "./table-cell-margins-EVg0EwxT.js";
3
+ //#region src/ooxml/ids.ts
4
+ /** `ST_SlideId` maximum, inclusive. The schema's `maxExclusive` is 2147483648. */
5
+ const MAX_SLIDE_ID = 2147483647;
6
+ /**
7
+ * `ST_SlideMasterId` minimum (0x80000000). `ST_SlideLayoutId` has the same floor, and the two draw
8
+ * from one id space that must be unique across the whole presentation.
9
+ */
10
+ const MIN_SLIDE_MASTER_ID = 2147483648;
11
+ //#endregion
12
+ //#region src/constants-internal.ts
13
+ const LAYOUT_IDX_SERIES_BASE = 2147483649;
14
+ const LINEH_MODIFIER = 1.67;
15
+ const DEF_CELL_BORDER = {
16
+ type: "solid",
17
+ color: "666666",
18
+ width: 1
19
+ };
20
+ const DEF_CELL_MARGIN_IN = [
21
+ TABLE_CELL_MARGIN_DEFAULTS_EMU.top / EMU_PER_INCH,
22
+ TABLE_CELL_MARGIN_DEFAULTS_EMU.right / EMU_PER_INCH,
23
+ TABLE_CELL_MARGIN_DEFAULTS_EMU.bottom / EMU_PER_INCH,
24
+ TABLE_CELL_MARGIN_DEFAULTS_EMU.left / EMU_PER_INCH
25
+ ];
26
+ const DEF_CHART_BORDER = {
27
+ type: "solid",
28
+ color: "363636",
29
+ width: 1
30
+ };
31
+ const DEF_CHART_GRIDLINE = {
32
+ color: "888888",
33
+ dashType: "solid",
34
+ width: 1,
35
+ cap: "flat"
36
+ };
37
+ const DEF_FONT_COLOR = "000000";
38
+ const MAX_FONT_SIZE_PT = 4e3;
39
+ const DEF_PRES_LAYOUT = "LAYOUT_16x9";
40
+ const DEF_PRES_LAYOUT_NAME = "DEFAULT";
41
+ const DEF_SHAPE_LINE_COLOR = "333333";
42
+ const DEF_SHAPE_SHADOW = {
43
+ type: "outer",
44
+ blur: 3,
45
+ offset: 23e3 / EMU_PER_POINT,
46
+ angle: 90,
47
+ color: "000000",
48
+ _alpha: .35,
49
+ rotateWithShape: true
50
+ };
51
+ const DEF_SLIDE_MARGIN_IN = [
52
+ .5,
53
+ .5,
54
+ .5,
55
+ .5
56
+ ];
57
+ const DEF_TEXT_SHADOW = {
58
+ type: "outer",
59
+ blur: 8,
60
+ offset: 4,
61
+ angle: 270,
62
+ color: "000000",
63
+ _alpha: .75
64
+ };
65
+ const DEF_TEXT_GLOW = {
66
+ size: 8,
67
+ color: "FFFFFF",
68
+ opacity: .75
69
+ };
70
+ const AXIS_ID_VALUE_PRIMARY = "2094734552";
71
+ const AXIS_ID_VALUE_SECONDARY = "2094734553";
72
+ const AXIS_ID_CATEGORY_PRIMARY = "2094734554";
73
+ const AXIS_ID_CATEGORY_SECONDARY = "2094734555";
74
+ const AXIS_ID_SERIES_PRIMARY = "2094734556";
75
+ const LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
76
+ const BARCHART_COLORS = [
77
+ "C0504D",
78
+ "4F81BD",
79
+ "9BBB59",
80
+ "8064A2",
81
+ "4BACC6",
82
+ "F79646",
83
+ "628FC6",
84
+ "C86360"
85
+ ];
86
+ const PIECHART_COLORS = [
87
+ "5DA5DA",
88
+ "FAA43A",
89
+ "60BD68",
90
+ "F17CB0",
91
+ "B2912F",
92
+ "B276B2",
93
+ "DECF3F",
94
+ "F15854",
95
+ "A7A7A7"
96
+ ];
97
+ const SLDNUMFLDID = "{F7021451-1387-4CA6-816F-3879F97B5CBC}";
98
+ const SLDNUM_PLACEHOLDER_TEXT = "‹#›";
99
+ /**
100
+ * The nine list levels' default left margins (`a:lvlNpPr/@marL`), half an inch apart.
101
+ *
102
+ * `p:txStyles`' body and other styles, `p:notesStyle` and the presentation's
103
+ * `p:defaultTextStyle` are four separate ladders in the file and one ladder in fact: the
104
+ * three modules that emit them each carried their own copy of these nine numbers, so a
105
+ * level's margin could only be changed in one of them by accident.
106
+ */
107
+ const LEVEL_MARGINS_EMU = Array.from({ length: 9 }, (_, i) => i * (EMU_PER_INCH / 2));
108
+ /**
109
+ * The paragraph-property tail every default `a:lvlNpPr` carries, whatever else it sets.
110
+ *
111
+ * Spread it *after* the level's own attributes: `el()` emits in `Object.entries` order, so
112
+ * where these land in the tag is byte-significant, and every emitting site puts `marL` (and
113
+ * where it has one, `indent` and `algn`) ahead of them.
114
+ *
115
+ * `algn` is deliberately not in here even though three of the four sites write `algn="l"`.
116
+ * The fourth computes it per level — a configured master's title can be centred or right —
117
+ * and a spread that carried `algn` would silently overwrite that: a later key in an object
118
+ * literal replaces the value without moving the key, so the attribute order would still look
119
+ * right while the alignment was wrong. No showcase deck configures a master text style, so
120
+ * `byte-identity` cannot see that path; `master-text-styles.test.js` pins it instead.
121
+ */
122
+ const LEVEL_PPR_TAIL = {
123
+ defTabSz: EMU_PER_INCH,
124
+ rtl: 0,
125
+ eaLnBrk: 1,
126
+ latinLnBrk: 0,
127
+ hangingPunct: 1
128
+ };
129
+ //#endregion
130
+ //#region src/units-internal.ts
131
+ /**
132
+ * Generator-side unit conversion — the lenient layer over `units.ts`.
133
+ *
134
+ * `units.ts` holds the strict, public primitives: one branded `Emu` type and converters that
135
+ * take a number and throw on anything they cannot represent. This module holds what the
136
+ * generators actually need on top of that: converters that accept the loose shapes the
137
+ * authoring API has always accepted (numeric strings, `"5in"`, `null`), that clamp to the
138
+ * ranges PowerPoint will load without offering to repair the file, and that warn about legacy
139
+ * inputs instead of failing.
140
+ *
141
+ * The split is deliberate: the leniency and the back-compat warnings are policy, not
142
+ * arithmetic, and policy should not be part of the published API. Nothing here is exported
143
+ * from an entrypoint.
144
+ */
145
+ /**
146
+ * Resolve a user `Coord` (x/y/w/h) to EMU — the single user-coordinate → EMU boundary.
147
+ * - bare `number` → **inches** (no magnitude guessing); `"<n>%"` → percent of the slide axis;
148
+ * `"<n>in"`/`"<n>pt"`/`"<n>emu"` → explicit units (see {@link Coord} / {@link coordToEmu})
149
+ * - `null`/`undefined` → 0 (callers may omit a coordinate)
150
+ * - throws on a non-finite number rather than silently collapsing the object to zero size
151
+ * @param {Coord|null|undefined} size - user coordinate
152
+ * @param {'X' | 'Y'} xyDir - axis (selects slide width vs height for percentages)
153
+ * @param {PresLayout} layout - presentation layout (EMU dimensions)
154
+ * @returns {Emu} resolved EMU value
155
+ */
156
+ function getSmartParseNumber(size, xyDir, layout) {
157
+ if (size === null || size === void 0) return 0;
158
+ if (typeof size === "number" && !Number.isFinite(size)) throw new InvalidOptionError("coord/non-finite", `Invalid ${xyDir || "coordinate"} value: expected a finite number but received ${String(size)}. This usually means a layout dimension was read from a missing property (e.g. \`layout.width\` returning \`undefined\`). Use \`slide.width\`/\`slide.height\` or \`STANDARD_LAYOUTS.<NAME>.width\`/\`.height\` (inches).`);
159
+ return coordToEmu(size, xyDir === "Y" ? layout.height : layout.width);
160
+ }
161
+ /**
162
+ * Convert inches into EMU.
163
+ * - accepts a number (inches) or a numeric/`"<n>in"` string
164
+ * - no magnitude guessing: values are always treated as inches (use {@link coordToEmu} for
165
+ * user coordinates that may carry other units)
166
+ * @param {number|string} inches - inches as number or string
167
+ * @returns {Emu} EMU value
168
+ */
169
+ function inch2Emu(inches) {
170
+ if (typeof inches === "string") inches = Number(inches.replace(/in*/gi, ""));
171
+ return inchesToEmu(inches);
172
+ }
173
+ /**
174
+ * Convert a single `margin` component (table cell/table margin, or text-box/placeholder body
175
+ * inset) to EMU.
176
+ *
177
+ * Margins are INCHES, consistent with the positional API (`x`/`y`/`w`/`h`) and the value
178
+ * PowerPoint's own dialog shows (both the table cell-margin and the text-box internal-margin
179
+ * fields are inches). Historically the library read these as POINTS (table cells used a magnitude
180
+ * heuristic — `>= 1` points, `< 1` inches; text-box margins were straight points), so a legitimate
181
+ * fraction-of-an-inch value entered from the PowerPoint dialog became a tiny points value. Every
182
+ * value is now inches. A `>= 1` value is honored as inches but warns once, because it is almost
183
+ * certainly a legacy points value that should be divided by 72 (e.g. `10` points → `0.139` inches).
184
+ *
185
+ * Shared by every margin site so they stay in lockstep: the cell XML emitter and text-box/slide-
186
+ * number insets (`gen/slide/object.ts`), the autoPage row-height pass (`gen/table/autopage.ts`), and the measured-fit pass
187
+ * (`measure-fit`).
188
+ * @param {number} inches - margin component in inches
189
+ * @returns {Emu} EMU value
190
+ */
191
+ function marginToEmu(inches) {
192
+ if (inches >= 1) warnOnce("margin/legacy-points", "margins (table cell and text-box) are interpreted as inches (matching the rest of the API and the PowerPoint dialog); a value >= 1 is likely a legacy points value — divide by 72 to convert (e.g. 10pt => 0.139in).");
193
+ return inch2Emu(inches);
194
+ }
195
+ /**
196
+ * A `[Top, Right, Bottom, Left]` margin (inches) as the four text insets in EMU, or `null` when
197
+ * the caller stated no margin.
198
+ *
199
+ * A scalar broadcasts to all four sides. The index order is the whole point of naming this:
200
+ * the arrays are CSS order while `a:bodyPr`/`a:tcPr` name their insets left-first, so every
201
+ * site that mapped it by hand spelled the same 3/0/1/2 shuffle — the text box's `_bodyProp`,
202
+ * the slide-number placeholder's `a:bodyPr`, and a table cell's `marL/R/T/B`.
203
+ *
204
+ * Each component goes through {@link marginToEmu}, so the legacy-points warning still fires
205
+ * once for a value of an inch or more.
206
+ * @param margin - the caller's `margin`, a scalar or a `[T, R, B, L]` array of inches
207
+ */
208
+ function resolveInsetsEmu(margin) {
209
+ if (typeof margin === "number") {
210
+ const all = marginToEmu(margin);
211
+ return {
212
+ l: all,
213
+ t: all,
214
+ r: all,
215
+ b: all
216
+ };
217
+ }
218
+ if (!Array.isArray(margin)) return null;
219
+ const at = (idx) => marginToEmu(margin[idx] ?? 0);
220
+ return {
221
+ l: at(3),
222
+ t: at(0),
223
+ r: at(1),
224
+ b: at(2)
225
+ };
226
+ }
227
+ /**
228
+ * One line of table text, in EMU: `fontSizePt * (LINEH_MODIFIER + lineWeight) / 100`.
229
+ *
230
+ * The auto-pager prices a row by counting lines and multiplying by this, and the measured-fit
231
+ * pass estimates a cell's height with the same product. Three sites wrote the expression by
232
+ * hand and one of them left the caller's `autoPageLineWeight` out, so the pager guarded a row
233
+ * against a line it then measured at a different height.
234
+ *
235
+ * @param fontSizePt - the resolved font size in points
236
+ * @param lineWeight - the caller's `autoPageLineWeight`, an addend on the modifier
237
+ */
238
+ function autoPageLineHeightEmu(fontSizePt, lineWeight = 0) {
239
+ return inch2Emu(fontSizePt * (LINEH_MODIFIER + lineWeight) / 100);
240
+ }
241
+ /**
242
+ * The four slide margins in inches, `[top, right, bottom, left]`, from the master's own margin
243
+ * and the caller's `slideMargin`.
244
+ *
245
+ * Precedence is caller, then master, then {@link DEF_SLIDE_MARGIN_IN}. A scalar broadcasts to
246
+ * all four sides; an array is taken as-is.
247
+ *
248
+ * Three sites derived this and they already disagreed. Two gated the master on
249
+ * `typeof !== 'undefined'` and coerced with `Number.isFinite(Number(m))`; the HTML path gated
250
+ * on truthiness and tested `Number.isFinite(m)` with no coercion. So a master with
251
+ * `_margin: 0` took the master branch in two of them and the caller branch in the third, and
252
+ * a master with `_margin: "0.25"` resolved in two and was ignored in the third.
253
+ *
254
+ * **The caller-first order was decided, the master-first one it replaced was not.** All three
255
+ * pre-consolidation sites read the master first, inherited from upstream, and the consolidation
256
+ * kept that order rather than deciding it -- so on a deck whose master defined a `margin`,
257
+ * `addTable(rows, { slideMargin: 2 })` was discarded with nothing said, which is the reverse of
258
+ * the rule stated everywhere else here: an explicit option beats an inherited one. The master's
259
+ * `margin` is now the deck-wide default a per-table `slideMargin` overrides. A master margin as
260
+ * a *floor* rather than a ceiling is a third reading, and if anyone needs it, it wants a stated
261
+ * option rather than a precedence accident.
262
+ *
263
+ * @param masterMargin - the master/layout's `_margin`, in inches
264
+ * @param slideMargin - the caller's `slideMargin`, in inches
265
+ */
266
+ function resolveSlideMarginsInches(masterMargin, slideMargin) {
267
+ const broadcast = (value) => {
268
+ if (Array.isArray(value)) {
269
+ const [t = 0, r = 0, b = 0, l = 0] = value;
270
+ return [
271
+ t,
272
+ r,
273
+ b,
274
+ l
275
+ ];
276
+ }
277
+ const n = Number(value);
278
+ return Number.isFinite(n) ? [
279
+ n,
280
+ n,
281
+ n,
282
+ n
283
+ ] : null;
284
+ };
285
+ if (slideMargin !== void 0 && slideMargin !== null) {
286
+ const resolved = broadcast(slideMargin);
287
+ if (resolved) return resolved;
288
+ }
289
+ if (masterMargin !== void 0 && masterMargin !== null) {
290
+ const resolved = broadcast(masterMargin);
291
+ if (resolved) return resolved;
292
+ }
293
+ return [...DEF_SLIDE_MARGIN_IN];
294
+ }
295
+ /**
296
+ * Resolve a table's column widths to EMU, the single source of truth shared by the
297
+ * table XML emitter and the measured-fit pass (so a fitted cell sees the same grid
298
+ * the renderer draws).
299
+ * - an explicit `colW` **array** is per-column inches (`inch2Emu`); a non-finite slot
300
+ * falls back to the even-distribution width
301
+ * - otherwise the table's already-resolved width (`totalWidthEmu`, EMU) is split evenly
302
+ * across `colCount` columns. (A scalar `colW` never reaches here — `addTableDefinition`
303
+ * converts it to `w` and clears `colW`.)
304
+ *
305
+ * IMPORTANT: the even path divides an **EMU** width. Passing the raw inches `options.w`
306
+ * instead (the historical bug) produced ~0-EMU columns (e.g. `w=9` → `gridCol w="3"`),
307
+ * collapsing auto-width tables to a sliver in PowerPoint/LibreOffice.
308
+ * @param {Coord[]|Coord|undefined} colW - explicit per-column inches, or scalar/undefined
309
+ * @param {number} totalWidthEmu - the table's resolved width in EMU
310
+ * @param {number} colCount - number of grid columns (counting colspans)
311
+ * @returns {number[]} per-column widths in EMU (length `colCount`)
312
+ */
313
+ function resolveTableColWidthsEmu(colW, totalWidthEmu, colCount) {
314
+ if (!(colCount > 0)) return [];
315
+ const even = totalWidthEmu > 0 ? Math.round(totalWidthEmu / colCount) : EMU_PER_INCH;
316
+ if (Array.isArray(colW)) return Array.from({ length: colCount }, (_, i) => {
317
+ const n = colW[i];
318
+ if (typeof n === "number" && Number.isFinite(n)) return Math.round(inch2Emu(n));
319
+ if (n !== void 0 && n !== null) warnOnce("table/invalid-col-width", `colW entry ${String(n)} is not a number of inches; that column takes an even share of the table width instead.`);
320
+ return even;
321
+ });
322
+ return new Array(colCount).fill(even);
323
+ }
324
+ /**
325
+ * The slide width a table may occupy, in EMU: the slide, less the table's own left
326
+ * edge and the right slide margin.
327
+ *
328
+ * The width twin of the auto-pager's `calcSlideTabH`, and the same reading
329
+ * `addTableDefinition` applies when a table states neither `w` nor `colW`. The pager
330
+ * used to *add* the two margins here instead of subtracting them, which made its
331
+ * fallback width about one inch however wide the slide was, and every column a sliver.
332
+ *
333
+ * @param presLayout - the presentation layout, for the slide width
334
+ * @param xEmu - the table's resolved left edge in EMU; `0`/absent falls back to the left slide margin
335
+ * @param marginsIn - the four slide margins in inches, as the pager holds them
336
+ * @returns the usable width in EMU, never below zero
337
+ */
338
+ function usableTableWidthEmu(presLayout, xEmu, marginsIn) {
339
+ const startIn = xEmu ? xEmu / EMU_PER_INCH : marginsIn[3];
340
+ return Math.max(0, presLayout.width - inch2Emu(startIn) - inch2Emu(marginsIn[1]));
341
+ }
342
+ /**
343
+ * The single rule for whether one `rowH` entry pins a table row, in inches.
344
+ *
345
+ * An entry pins its row when it reads as a **finite number greater than zero**. Everything
346
+ * else returns `null` — the row is sized from the table's own height, or grows to fit — and
347
+ * an entry that is present but unusable warns, because `rowH: [0, …]` and `rowH: [-1, …]`
348
+ * are things a caller wrote on purpose and none of the readings this replaced was what they
349
+ * meant. A missing slot is not that: an array with holes is how the auto-pager spells
350
+ * "this row is auto-height", so `undefined` is silent.
351
+ *
352
+ * Three call sites read `rowH` and they disagreed. The writer and the export-time fit pass
353
+ * both used a truthiness test, so `0` fell through to the even split of `h`;
354
+ * `pptx.tableLayout()` used `typeof === 'number'`, so `0` pinned the row at zero inches and
355
+ * was then rescued by a default-line fallback — reporting 0.2in against the file's 2.0in,
356
+ * and flagging it `heightExact`. A negative entry reached `<a:tr h="-914400">`.
357
+ *
358
+ * @param entry - one `rowH` array slot, or the scalar `rowH`
359
+ * @returns the pinned height in inches, or `null` when the row is not pinned by `rowH`
360
+ */
361
+ function pinnedRowHeightInches(entry) {
362
+ if (entry === void 0 || entry === null) return null;
363
+ const inches = Number(entry);
364
+ if (Number.isFinite(inches) && inches > 0) return inches;
365
+ warnOnce("table/invalid-row-height", `rowH entry ${String(entry)} is not a positive number of inches; the row is sized from the table height instead.`);
366
+ return null;
367
+ }
368
+ /**
369
+ * A table cell's `[Top, Right, Bottom, Left]` margins in inches, defaulted and validated.
370
+ *
371
+ * Three sites spelled this out -- two of them character-identical, the third self-described as
372
+ * "mirroring" one of them -- and a fourth in the auto-pager gated each side on truthiness, so a
373
+ * cell stating `margin: [0, …]` fell through to the table's margin instead of taking its own
374
+ * zero. All of them swapped an unusable margin for the default in silence, while every sibling
375
+ * resolver in this file warns for the same class of input.
376
+ *
377
+ * `resolveInsetsEmu` above is deliberately NOT a fifth copy: it resolves a text box's
378
+ * `a:bodyPr` insets, where stating nothing means writing no attribute at all rather than
379
+ * taking a cell default, so its absent case has a different answer.
380
+ * @param margin - the caller's `margin`: a scalar, a `[T, R, B, L]` array, or nothing
381
+ * @returns four finite inch measures
382
+ */
383
+ function resolveCellMarginsInches(margin) {
384
+ if (margin === void 0 || margin === null) return [...DEF_CELL_MARGIN_IN];
385
+ const usable = (value) => typeof value === "number" && Number.isFinite(value);
386
+ if (typeof margin === "number") {
387
+ if (usable(margin)) return [
388
+ margin,
389
+ margin,
390
+ margin,
391
+ margin
392
+ ];
393
+ } else if (Array.isArray(margin) && margin.length === 4 && margin.every(usable)) return [
394
+ margin[0],
395
+ margin[1],
396
+ margin[2],
397
+ margin[3]
398
+ ];
399
+ warnOnce("table/invalid-margin", `table margin ${JSON.stringify(margin)} is not a number of inches or a [top, right, bottom, left] array of four; using the default.`);
400
+ return [...DEF_CELL_MARGIN_IN];
401
+ }
402
+ /**
403
+ * Resolve one table row's height to EMU — the row-height twin of
404
+ * {@link resolveTableColWidthsEmu}, shared by the table XML emitter, the export-time
405
+ * measured-fit pass and `pptx.tableLayout()` so a prediction cannot disagree with what the
406
+ * export bakes.
407
+ *
408
+ * - a `rowH` entry that {@link pinnedRowHeightInches} accepts pins the row
409
+ * - otherwise the table's already-resolved height (`totalHeightEmu`, EMU) is split evenly
410
+ * across `rowCount` rows
411
+ * - otherwise `null`: the row is auto-height and grows to fit its content
412
+ *
413
+ * @param rowH - the caller's `rowH`, scalar or per-row array
414
+ * @param rowIndex - which row is being resolved
415
+ * @param totalHeightEmu - the table's resolved height in EMU, or 0 when it has none
416
+ * @param rowCount - number of rows to split that height across
417
+ * @returns the row height in EMU, or `null` for an auto-height row
418
+ */
419
+ function resolveTableRowHeightEmu(rowH, rowIndex, totalHeightEmu, rowCount) {
420
+ const pinned = pinnedRowHeightInches(Array.isArray(rowH) ? rowH[rowIndex] : rowH);
421
+ if (pinned !== null) return Math.round(inch2Emu(pinned));
422
+ return totalHeightEmu > 0 && rowCount > 0 ? Math.round(totalHeightEmu / rowCount) : null;
423
+ }
424
+ /**
425
+ * Convert points to EMU, leniently: anything that does not read as a finite number — a `NaN`,
426
+ * an `Infinity`, a non-numeric string, `null` — becomes `0` rather than throwing.
427
+ *
428
+ * That is the whole difference from {@link pointsToEmu} in `units.ts`, which throws on the same
429
+ * input. Emitters use this one for the many optional size/width/offset options where a missing
430
+ * or malformed value should collapse the feature (a zero-width line, no shadow offset) rather
431
+ * than take the whole deck down.
432
+ *
433
+ * @param pt - points, as a number or a numeric string
434
+ * @returns the value in EMU, or `0` if it does not read as a finite number
435
+ */
436
+ function ptsToEmuLenient(pt) {
437
+ const points = Number(pt);
438
+ return Number.isFinite(points) ? Math.round(points * EMU_PER_POINT) : 0;
439
+ }
440
+ /**
441
+ * `fn(value)` for a numeric option the caller stated, `undefined` for one they did not.
442
+ *
443
+ * `null`, `undefined` and `0` are "not stated": an absent option and a zero one emit the same
444
+ * thing (no `rot` for `rotate: 0`, the default width for `width: 0`). Every other value reaches
445
+ * `fn`, `NaN` included, so the converter behind it decides what a value it cannot represent
446
+ * means. The same test used to be spelled as truthiness, and `NaN` is falsy: it was dropped or
447
+ * replaced by the default before the converter could refuse it, while `Infinity` on the same
448
+ * option threw.
449
+ * @param value - the caller's option
450
+ * @param fn - the conversion to apply when the option is stated
451
+ * @returns `fn(value)`, or `undefined` when the option is not stated
452
+ */
453
+ function mapStated(value, fn) {
454
+ return value === null || value === void 0 || value === 0 ? void 0 : fn(value);
455
+ }
456
+ /**
457
+ * Clamp a caller-supplied number into the range its schema type allows, warning when it had to
458
+ * move. Returns the value in the caller's own unit — each helper below applies its own
459
+ * arithmetic afterwards, because the inverting and non-inverting forms do not round alike.
460
+ *
461
+ * `NaN` throws rather than clamping: there is no nearest in-range value for it, and
462
+ * `Math.min`/`Math.max` propagate it straight through to the attribute (`val="NaN"`), which is
463
+ * exactly the degenerate output the range check exists to prevent. `Infinity` is not in that
464
+ * category — it clamps to the bound like any other out-of-range number, and warns.
465
+ *
466
+ * **This is the one policy for an out-of-range number**, and it is the policy
467
+ * `docs/contributing/development.md` ("Warn or throw?") describes: a finite value has a nearest legal
468
+ * neighbour, so the deck still comes out recognisable and the move is a warning; a value that
469
+ * is not a number at all has no neighbour, so the request is discarded and that throws.
470
+ * Rejecting a finite out-of-range value and emitting nothing is neither: it discards the request
471
+ * and reports it as a warning, which is the combination the rule exists to rule out.
472
+ *
473
+ * Most of what passes through here is a percentage, which is why the throw names one by
474
+ * default; `nonFiniteCode` is for the callers whose option is not one, such as a shadow's
475
+ * angle in degrees.
476
+ *
477
+ * @param value - the caller's value
478
+ * @param min - inclusive lower bound, in the caller's unit
479
+ * @param max - inclusive upper bound, in the caller's unit
480
+ * @param code - diagnostic code raised when the value is clamped
481
+ * @param label - option name as the caller spells it, opening the warning
482
+ * @param nonFiniteCode - error code thrown when the value is not a number at all
483
+ * @param report - how a clamp is reported: `once` dedupes the warning on its code and message, as
484
+ * a measure repeated across every run of a deck needs, and `range` states the range in the
485
+ * caller's wording (`'1-4000pt'`) rather than as the two bare bounds
486
+ */
487
+ function clampRangedInput(value, min, max, code, label, nonFiniteCode = "percent/non-finite", report = {}) {
488
+ if (typeof value !== "number" || Number.isNaN(value)) throw new InvalidOptionError(nonFiniteCode, `${label} must be a number from ${min} to ${max}; received ${String(value)}.`);
489
+ const clamped = Math.min(max, Math.max(min, value));
490
+ if (clamped !== value) {
491
+ const message = `${label} ${value} is outside the valid range ${report.range ?? `${min}-${max}`}; using ${clamped}.`;
492
+ if (report.once) warnOnce(code, message);
493
+ else warn(code, message);
494
+ }
495
+ return clamped;
496
+ }
497
+ /**
498
+ * Convert a transparency percentage (0-100) into a schema-valid `<a:alpha>` value
499
+ * (ST_PositiveFixedPercentage, 0-100000). Out-of-range transparency yields an
500
+ * alpha that PowerPoint rejects as needing repair, so clamp into range and warn.
501
+ *
502
+ * This is the **inverting** form: 0 transparency is full opacity (`val="100000"`). Use
503
+ * {@link percentToFixedPercent} where the option already reads as opacity.
504
+ */
505
+ function transparencyToAlpha(transparency) {
506
+ const pct = clampRangedInput(transparency, 0, 100, "transparency/out-of-range", "transparency");
507
+ return Math.round((100 - pct) * FIXED_PCT_PER_PERCENT);
508
+ }
509
+ /** Convert an opacity (0-1) into a schema-valid `<a:alpha>` value (0-100000); clamps + warns on out-of-range input. */
510
+ function opacityToAlpha(opacity) {
511
+ return fractionToFixedPercent(opacity, "opacity/out-of-range", "opacity");
512
+ }
513
+ /**
514
+ * Convert a percentage into a schema-valid fixed-percentage value (thousandths of a percent) —
515
+ * the **non-inverting** sibling of {@link transparencyToAlpha}, for options the caller already
516
+ * states as "how much", such as a chart series' fill opacity.
517
+ *
518
+ * `min`/`max` default to the 0-100 nearly every such option allows. They are parameters because
519
+ * the fixed-percentage attributes do not all share one range: `<a:buSzPct>` is
520
+ * ST_TextBulletSizePercent (25-400), so the bullet-size option needs its own bounds while
521
+ * keeping this function's policy.
522
+ * @param value - the caller's percentage
523
+ * @param code - diagnostic code raised when the value is clamped
524
+ * @param label - option name as the caller spells it, opening the warning
525
+ * @param min - inclusive lower bound in percent
526
+ * @param max - inclusive upper bound in percent
527
+ */
528
+ function percentToFixedPercent(value, code, label, min = 0, max = 100) {
529
+ return Math.round(clampRangedInput(value, min, max, code, label) * FIXED_PCT_PER_PERCENT);
530
+ }
531
+ /**
532
+ * Convert a 0-1 fraction into a schema-valid fixed-percentage value (0-100000), for the options
533
+ * spelled as a fraction rather than a percentage (an opacity, a luminance threshold).
534
+ * @param value - the caller's fraction
535
+ * @param code - diagnostic code raised when the value is clamped
536
+ * @param label - option name as the caller spells it, opening the warning
537
+ */
538
+ function fractionToFixedPercent(value, code, label) {
539
+ return Math.round(clampRangedInput(value, 0, 1, code, label) * PERCENT_SCALE);
540
+ }
541
+ /**
542
+ * Convert a length stated in points to EMU, clamped into the schema range of the attribute it is
543
+ * headed for.
544
+ *
545
+ * A number goes through {@link clampRangedInput}, the one policy for an out-of-range number: a
546
+ * value past either bound clamps to it and warns, `Infinity` included, and `NaN` throws
547
+ * `coord/non-finite`. Both of those used to collapse to `0` without a word, so `width: Infinity`
548
+ * became a zero-width line while `width: 5000` clamped and warned.
549
+ *
550
+ * A value that does not read as a number at all, a string such as `'wide'`, still collapses the
551
+ * feature to `0` via {@link ptsToEmuLenient}, because these are optional decorations on an object
552
+ * that still has to be emitted. A numeric string past the range clamps and warns as before.
553
+ *
554
+ * @param pts - the caller's length in points, or any of the loose shapes `ptsToEmuLenient` takes
555
+ * @param maxEmu - inclusive upper bound of the attribute's schema type, in EMU
556
+ * @param code - diagnostic code raised when the value is clamped
557
+ * @param label - option name as the caller spells it, opening the warning
558
+ * @param range - the valid range in points, as the warning states it
559
+ */
560
+ function clampLengthEmu(pts, maxEmu, code, label, range) {
561
+ if (typeof pts === "number") return Math.round(clampRangedInput(pts, 0, maxEmu / EMU_PER_POINT, code, label, "coord/non-finite") * EMU_PER_POINT);
562
+ const raw = ptsToEmuLenient(pts);
563
+ const clamped = Math.min(maxEmu, Math.max(0, raw));
564
+ if (clamped !== raw) warn(code, `${label} ${pts} is outside the valid range ${range}; using ${clamped / EMU_PER_POINT}.`);
565
+ return clamped;
566
+ }
567
+ /**
568
+ * Convert a line width (points) to EMU clamped into ST_LineWidth (0..20116800 EMU,
569
+ * i.e. 0-1584pt). Out-of-range widths make PowerPoint report the package as needing
570
+ * repair, so clamp into range and warn.
571
+ *
572
+ * `label` is what the refusal quotes back, the way {@link clampFontSizeSz} already names the nine
573
+ * options that reach it. Every caller used to be reported as `line width`, so a `NaN` error-bar
574
+ * `width` or a series `lineSize` threw "line width must be a number from 0 to 1584" and named
575
+ * nothing the caller had written.
576
+ * @param widthPts - the caller's width in points
577
+ * @param label - the option as the caller spells it, e.g. `errorBars.width`
578
+ */
579
+ function lineWidthToEmu(widthPts, label = "line width") {
580
+ return clampLengthEmu(widthPts, 20116800, "line/width-out-of-range", label, "0-1584pt");
581
+ }
582
+ /**
583
+ * Convert a shadow's blur radius or offset distance (points) to EMU clamped into
584
+ * ST_PositiveCoordinate (0..27273042316900 EMU, i.e. 0-2147483647pt) — the type both
585
+ * `blurRad` and `dist` carry on `a:outerShdw`/`a:innerShdw`.
586
+ *
587
+ * **The bound is the schema's, not the `0-100` / `0-200` those options document.** Those two
588
+ * numbers are the limits of PowerPoint's own spinners, and a blur past them is unusual rather
589
+ * than invalid: the file loads and paints. The one input that genuinely breaks the package is a
590
+ * *negative* one, because the type is unsigned — so that is what this moves, on the same
591
+ * reasoning `clampLineSpacingMultiplePct` in `gen/drawingml/clamp.ts` clamps to
592
+ * ST_TextSpacingPercent rather than to the `0.0-9.99` its own option describes. Only values
593
+ * PowerPoint reports as needing repair are worth moving.
594
+ */
595
+ function positiveCoordinateEmu(pts, code, label) {
596
+ return clampLengthEmu(pts, MAX_COORDINATE_EMU, code, label, "0-2147483647pt");
597
+ }
598
+ /**
599
+ * Convert degrees to a DrawingML angle (60000ths of a degree), **without** wrapping.
600
+ *
601
+ * Most angles in the format are not modular: a connection site at 400 degrees, a polar adjust
602
+ * handle whose range runs to 540, an arc that sweeps 400 degrees — each means something a
603
+ * reduction into 0..360 would destroy. Use this for those, and {@link convertRotationDegrees}
604
+ * for the ones that genuinely are a rotation.
605
+ *
606
+ * Non-finite input throws rather than reaching the attribute: `Math.round(Infinity * 60000)` is
607
+ * `Infinity`, which serializes as `ang="Infinity"` and makes PowerPoint offer to repair the file.
608
+ *
609
+ * @param d - degrees
610
+ * @param what - what is being converted, for the error message
611
+ * @param code - diagnostic code to throw under, where a caller has a more specific one
612
+ */
613
+ function convertAngleUnits(d, what, code = "coord/non-finite") {
614
+ assertFiniteDegrees(d, what, code);
615
+ return Math.round(d * ANGLE_UNITS_PER_DEGREE);
616
+ }
617
+ function assertFiniteDegrees(d, what, code) {
618
+ if (typeof d !== "number" || !Number.isFinite(d)) throw new InvalidOptionError(code, `${what} must be a finite number of degrees; received ${String(d)}.`);
619
+ }
620
+ /**
621
+ * Convert a shape or gradient rotation (degrees) to a DrawingML angle (60000ths of a degree).
622
+ *
623
+ * A rotation *is* modular — 800 degrees and 80 degrees point the same way — so the value is
624
+ * reduced with `% 360`. The reduction keeps the sign: `-45` stays `-45` rather than becoming
625
+ * `315`, because both are valid `ST_Angle`, PowerPoint writes negative rotations itself, and
626
+ * the read side reports back what was authored. Only an input outside -360..360 moves.
627
+ *
628
+ * @param d - degrees
629
+ * @returns the `rot` value in 60000ths of a degree
630
+ */
631
+ function convertRotationDegrees(d) {
632
+ assertFiniteDegrees(d, "rotation", "coord/non-finite");
633
+ return Math.round(d % 360 * ANGLE_UNITS_PER_DEGREE);
634
+ }
635
+ /**
636
+ * Convert a freeform arc angle (degrees) to an `<a:arcTo>` ST_AdjAngle value (60000ths).
637
+ * Unlike a shape rotation, a sweep is not modular: a 400 degree swAng draws a different
638
+ * arc than a 40 degree one, so the value is never wrapped into 0..360.
639
+ * @param {number} d degrees
640
+ * @param {'stAng' | 'swAng'} attr - attribute being emitted, for the error message
641
+ * @returns {number} ST_AdjAngle value (60000ths of a degree)
642
+ */
643
+ function convertArcAngle(d, attr) {
644
+ return convertAngleUnits(d, `Arc ${attr}`, "geometry/arc-angle-non-finite");
645
+ }
646
+ //#endregion
647
+ export { DEF_CELL_BORDER as A, DEF_TEXT_SHADOW as B, usableTableWidthEmu as C, AXIS_ID_VALUE_PRIMARY as D, AXIS_ID_SERIES_PRIMARY as E, DEF_PRES_LAYOUT_NAME as F, MAX_FONT_SIZE_PT as G, LETTERS as H, DEF_SHAPE_LINE_COLOR as I, SLDNUM_PLACEHOLDER_TEXT as J, PIECHART_COLORS as K, DEF_SHAPE_SHADOW as L, DEF_CHART_GRIDLINE as M, DEF_FONT_COLOR as N, AXIS_ID_VALUE_SECONDARY as O, DEF_PRES_LAYOUT as P, DEF_SLIDE_MARGIN_IN as R, transparencyToAlpha as S, AXIS_ID_CATEGORY_SECONDARY as T, LEVEL_MARGINS_EMU as U, LAYOUT_IDX_SERIES_BASE as V, LEVEL_PPR_TAIL as W, MIN_SLIDE_MASTER_ID as X, MAX_SLIDE_ID as Y, resolveCellMarginsInches as _, convertRotationDegrees as a, resolveTableColWidthsEmu as b, inch2Emu as c, marginToEmu as d, opacityToAlpha as f, ptsToEmuLenient as g, positiveCoordinateEmu as h, convertArcAngle as i, DEF_CHART_BORDER as j, BARCHART_COLORS as k, lineWidthToEmu as l, pinnedRowHeightInches as m, clampRangedInput as n, fractionToFixedPercent as o, percentToFixedPercent as p, SLDNUMFLDID as q, convertAngleUnits as r, getSmartParseNumber as s, autoPageLineHeightEmu as t, mapStated as u, resolveInsetsEmu as v, AXIS_ID_CATEGORY_PRIMARY as w, resolveTableRowHeightEmu as x, resolveSlideMarginsInches as y, DEF_TEXT_GLOW as z };
648
+
649
+ //# sourceMappingURL=units-internal-XJlf-hOl.js.map