pptx-ts 0.0.1

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 (78) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +319 -0
  3. package/dist/autopage-DkjYZUFD.js +423 -0
  4. package/dist/autopage-DkjYZUFD.js.map +1 -0
  5. package/dist/browser.d.ts +31 -0
  6. package/dist/browser.d.ts.map +1 -0
  7. package/dist/browser.js +110 -0
  8. package/dist/browser.js.map +1 -0
  9. package/dist/diagnostics-Du_s75MT.d.ts +829 -0
  10. package/dist/diagnostics-Du_s75MT.d.ts.map +1 -0
  11. package/dist/enums-CvRYniUA.d.ts +463 -0
  12. package/dist/enums-CvRYniUA.d.ts.map +1 -0
  13. package/dist/errors-Bvln0H-7.js +112 -0
  14. package/dist/errors-Bvln0H-7.js.map +1 -0
  15. package/dist/errors-PflMPWBn.d.ts +182 -0
  16. package/dist/errors-PflMPWBn.d.ts.map +1 -0
  17. package/dist/fit-BKs0zxMI.js +1356 -0
  18. package/dist/fit-BKs0zxMI.js.map +1 -0
  19. package/dist/html-dom-B0I0QSbU.js +679 -0
  20. package/dist/html-dom-B0I0QSbU.js.map +1 -0
  21. package/dist/html.d.ts +26 -0
  22. package/dist/html.d.ts.map +1 -0
  23. package/dist/html.js +63 -0
  24. package/dist/html.js.map +1 -0
  25. package/dist/index.d.ts +24 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +57 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/inspect.d.ts +197 -0
  30. package/dist/inspect.d.ts.map +1 -0
  31. package/dist/inspect.js +317 -0
  32. package/dist/inspect.js.map +1 -0
  33. package/dist/math.d.ts +37 -0
  34. package/dist/math.d.ts.map +1 -0
  35. package/dist/math.js +203 -0
  36. package/dist/math.js.map +1 -0
  37. package/dist/measure.d.ts +313 -0
  38. package/dist/measure.d.ts.map +1 -0
  39. package/dist/measure.js +3 -0
  40. package/dist/namespaces-BVnNu7sg.js +666 -0
  41. package/dist/namespaces-BVnNu7sg.js.map +1 -0
  42. package/dist/node.d.ts +20 -0
  43. package/dist/node.d.ts.map +1 -0
  44. package/dist/node.js +55 -0
  45. package/dist/node.js.map +1 -0
  46. package/dist/notes-RLCwdlTE.js +1703 -0
  47. package/dist/notes-RLCwdlTE.js.map +1 -0
  48. package/dist/package-CaOLXSNi.d.ts +193 -0
  49. package/dist/package-CaOLXSNi.d.ts.map +1 -0
  50. package/dist/pres-C5__xkDh.d.ts +202 -0
  51. package/dist/pres-C5__xkDh.d.ts.map +1 -0
  52. package/dist/presentation-Cp7vzlAw.js +5427 -0
  53. package/dist/presentation-Cp7vzlAw.js.map +1 -0
  54. package/dist/presentation-types-DP_8jog7.d.ts +4872 -0
  55. package/dist/presentation-types-DP_8jog7.d.ts.map +1 -0
  56. package/dist/read.d.ts +55 -0
  57. package/dist/read.d.ts.map +1 -0
  58. package/dist/read.js +4 -0
  59. package/dist/script.d.ts +581 -0
  60. package/dist/script.d.ts.map +1 -0
  61. package/dist/script.js +3741 -0
  62. package/dist/script.js.map +1 -0
  63. package/dist/shapes-BemroU-z.js +6951 -0
  64. package/dist/shapes-BemroU-z.js.map +1 -0
  65. package/dist/slide-js0QpRV-.d.ts +3873 -0
  66. package/dist/slide-js0QpRV-.d.ts.map +1 -0
  67. package/dist/text-CyetbF6p.js +11278 -0
  68. package/dist/text-CyetbF6p.js.map +1 -0
  69. package/dist/units-1kRzYShk.js +231 -0
  70. package/dist/units-1kRzYShk.js.map +1 -0
  71. package/dist/units-internal-Dg95w_YD.js +753 -0
  72. package/dist/units-internal-Dg95w_YD.js.map +1 -0
  73. package/dist/zip.d.ts +68 -0
  74. package/dist/zip.d.ts.map +1 -0
  75. package/dist/zip.js +163 -0
  76. package/dist/zip.js.map +1 -0
  77. package/package.json +152 -0
  78. package/skills/ts-pptx-upstream/SKILL.md +340 -0
@@ -0,0 +1,679 @@
1
+ import { r as EMU_PER_INCH, x as warn } from "./units-1kRzYShk.js";
2
+ import { n as InvalidOptionError } from "./errors-Bvln0H-7.js";
3
+ import { a as inch2Emu } from "./units-internal-Dg95w_YD.js";
4
+ import { t as getSlidesForTableRows } from "./autopage-DkjYZUFD.js";
5
+ //#region src/gen/table/html-dom.ts
6
+ /**
7
+ * Stand-in for `getComputedStyle` on a document that has no view — a document produced by
8
+ * parsing rather than by a browsing context has a `null` `defaultView` and therefore no
9
+ * cascade to compute against. Every property reads as `''`, which every consumer below
10
+ * already treats as "not set", so styling degrades to defaults instead of throwing.
11
+ */
12
+ const NO_COMPUTED_STYLE = { getPropertyValue: () => "" };
13
+ /**
14
+ * Bind a style reader to the document's own view, falling back to {@link NO_COMPUTED_STYLE}.
15
+ * @param {Document | null} doc - the table's owner document
16
+ * @returns {(el: Element) => CSSStyleDeclaration} computed-style reader
17
+ */
18
+ function resolveStyleReader(doc) {
19
+ const view = doc?.defaultView;
20
+ return view ? view.getComputedStyle.bind(view) : () => NO_COMPUTED_STYLE;
21
+ }
22
+ /**
23
+ * Resolve the table element and its style reader from whatever the caller passed.
24
+ *
25
+ * An **element** needs no ambient DOM: the document and its view are reached through
26
+ * `ownerDocument`/`defaultView`. A **string id** does need one, taken from `options.document`
27
+ * when supplied and otherwise from `globalThis.document`; with neither, the error names both
28
+ * remedies rather than surfacing a bare `document is not defined`.
29
+ *
30
+ * This is the one place the public structural element type ({@link TableToSlidesElement},
31
+ * which any DOM satisfies) is narrowed to `lib.dom`'s `Element`, so the rest of the flow can
32
+ * be written against ordinary DOM types. The cast is safe in the only sense that matters here:
33
+ * everything downstream reads standard members and tolerates them being absent — that is what
34
+ * makes the conversion portable in the first place.
35
+ * @param {TableToSlidesElement | string} target - the table element, or the id of one
36
+ * @param {TableToSlidesProps} options - generation options (may carry an explicit `document`)
37
+ * @returns {DomContext} resolved DOM context
38
+ */
39
+ function resolveDomContext(target, options) {
40
+ const asElement = (element) => element;
41
+ if (typeof target !== "string") {
42
+ const table = asElement(target);
43
+ return {
44
+ table,
45
+ getComputedStyle: resolveStyleReader(table.ownerDocument)
46
+ };
47
+ }
48
+ const doc = options.document ?? globalThis.document;
49
+ if (!doc) throw new InvalidOptionError("html/no-document", "tableToSlides: no DOM available to resolve the table id \"" + target + "\" — pass the <table> element itself, or supply a document via `options.document`.");
50
+ const found = doc.getElementById(target);
51
+ if (!found) throw new InvalidOptionError("html/table-not-found", "tableToSlides: Table ID \"" + target + "\" does not exist!");
52
+ const table = asElement(found);
53
+ return {
54
+ table,
55
+ getComputedStyle: resolveStyleReader(table.ownerDocument)
56
+ };
57
+ }
58
+ /**
59
+ * Convert one 0-255 RGB component to its two-digit hex form.
60
+ *
61
+ * Lives here rather than beside the other color code in `gen/drawingml/color.ts` because
62
+ * `getComputedStyle` colors are the only thing in the package that arrives as RGB
63
+ * components — nothing else in the library calls this.
64
+ * @param {number} c - component color
65
+ * @returns {string} hex string
66
+ */
67
+ function componentToHex(c) {
68
+ const hex = c.toString(16);
69
+ return hex.length === 1 ? "0" + hex : hex;
70
+ }
71
+ /**
72
+ * Converts RGB colors from css selectors to Hex for Presentation colors
73
+ * @param {number} r - red value
74
+ * @param {number} g - green value
75
+ * @param {number} b - blue value
76
+ * @returns {string} XML string
77
+ */
78
+ function rgbToHex(r, g, b) {
79
+ return (componentToHex(r) + componentToHex(g) + componentToHex(b)).toUpperCase();
80
+ }
81
+ /**
82
+ * Convert a computed CSS border (width string + color string) from `getComputedStyle` into a
83
+ * pptx `BorderProps`.
84
+ *
85
+ * Preserves *fractional* widths: a hairline CSS border such as `0.5px` must not be rounded to
86
+ * `0pt` and silently vanish — the table serializer (`ptsToEmuLenient`) emits fractional points just
87
+ * fine, so there is no reason to integer-round here. A
88
+ * computed width of `0` (or a non-finite value) yields `{ type: 'none' }` so we never emit a
89
+ * zero-width line.
90
+ * @param {string} widthStr - computed `border-<side>-width`, e.g. `"0.5px"`
91
+ * @param {string} colorStr - computed `border-<side>-color`, e.g. `"rgb(102, 102, 102)"`
92
+ * @returns {BorderProps} border props for the cell side
93
+ */
94
+ function htmlBorderToProps(widthStr, colorStr) {
95
+ const pt = Number(String(widthStr).replace("px", ""));
96
+ if (!isFinite(pt) || pt <= 0) return { type: "none" };
97
+ return {
98
+ width: pt,
99
+ color: cssColorToHex(colorStr) ?? "000000"
100
+ };
101
+ }
102
+ /**
103
+ * Convert a computed CSS color to a pptx hex color, or report that it has none.
104
+ *
105
+ * A browser normalizes every computed color to `rgb()`/`rgba()`, which is why the flow below
106
+ * originally only parsed that form. Outside a browser there is no normalization step, so a
107
+ * computed color comes back however the stylesheet wrote it — `#ff0000` is what happy-dom
108
+ * returns. Feeding that to the `rgb()` parser produced `Number('#ff0000')` → `NaN` → the
109
+ * literal color string `"NANNANNAN"`, an invalid value emitted without complaint.
110
+ *
111
+ * Handles `rgb()`/`rgba()` and `#rgb`/`#rrggbb`. Everything else — a named color, a `color()`
112
+ * function, an empty string — returns `undefined` rather than a guess, so the caller can
113
+ * apply its own default instead of emitting nonsense.
114
+ *
115
+ * A fully transparent color (`alpha === 0`) also returns `undefined`: it states the absence of
116
+ * a color, and pptx has no way to say "transparent" in a solid fill.
117
+ * @param {string} value - computed CSS color, e.g. `"rgb(255, 0, 0)"` or `"#ff0000"`
118
+ * @returns {string | undefined} six-digit uppercase hex, or `undefined` when unparseable
119
+ */
120
+ function cssColorToHex(value) {
121
+ const raw = String(value ?? "").trim();
122
+ const rgbMatch = /^rgba?\(([^)]*)\)$/i.exec(raw);
123
+ if (rgbMatch) {
124
+ const parts = (rgbMatch[1] ?? "").split(/[,/\s]+/).filter((part) => part.length > 0);
125
+ if (parts.length < 3) return void 0;
126
+ const channels = parts.slice(0, 3).map((part) => Number(part.replace("%", "")));
127
+ if (channels.some((channel) => !isFinite(channel))) return void 0;
128
+ if (parts.length > 3 && Number(parts[3]) === 0) return void 0;
129
+ const [r = 0, g = 0, b = 0] = channels.map((channel) => Math.max(0, Math.min(255, Math.round(channel))));
130
+ return rgbToHex(r, g, b);
131
+ }
132
+ const hexMatch = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(raw);
133
+ if (hexMatch) {
134
+ const digits = hexMatch[1] ?? "";
135
+ return (digits.length === 3 ? digits.split("").map((digit) => digit + digit).join("") : digits).toUpperCase();
136
+ }
137
+ }
138
+ /**
139
+ * Resolve a single HTML-table column width for `tableToSlides`.
140
+ *
141
+ * Precedence: an explicit `data-pptx-width` wins outright; otherwise the proportional width
142
+ * derived from the live table is used, raised to `data-pptx-min-width` when that floor is larger.
143
+ *
144
+ * Hidden tables report `offsetWidth` 0 for every cell, which makes `calcWidth` non-finite (a 0/0
145
+ * proportional calc). Fall back to `0` there so an explicit `data-pptx-width` / `data-pptx-min-width`
146
+ * override still drives the column instead of emitting a `NaN` width.
147
+ * @param {number} calcWidth - proportional width derived from `offsetWidth` (may be `NaN` for hidden tables)
148
+ * @param {number} setWidth - `data-pptx-width` override (`0`/`NaN` when absent or invalid)
149
+ * @param {number} minWidth - `data-pptx-min-width` floor (`0`/`NaN` when absent or invalid)
150
+ * @returns {number} resolved column width
151
+ */
152
+ function resolveHtmlColWidth(calcWidth, setWidth, minWidth) {
153
+ const safeCalc = isFinite(calcWidth) ? calcWidth : 0;
154
+ if (isFinite(setWidth) && setWidth > 0) return setWidth;
155
+ return isFinite(minWidth) && minWidth > safeCalc ? minWidth : safeCalc;
156
+ }
157
+ /** A CSS value that is a bare number, or a number with a `px`/`%` unit. Nothing else parses. */
158
+ const CSS_LENGTH = /^\s*(-?(?:\d+\.?\d*|\.\d+))\s*(px|%)?\s*$/i;
159
+ /**
160
+ * Parse a single computed CSS length in **px** to its numeric magnitude.
161
+ *
162
+ * A bare number is read as px (computed styles do not emit one, but an inline `style` read
163
+ * back by a non-browser DOM can). Everything else — `''`, `auto`, `3em`, and deliberately
164
+ * `30%` — is `NaN`: a percentage is meaningful only relative to something, so it cannot be
165
+ * turned into an absolute length here. (Percentages *are* usable as a proportional column
166
+ * basis; that is {@link parseCssWidthBasis}'s job, not this one's.) Callers decide what a
167
+ * `NaN` means for them.
168
+ * @param {string} value - computed CSS value, e.g. `"1.5px"`
169
+ * @returns {number} magnitude in px, or `NaN` when the value is not an absolute px length
170
+ */
171
+ function parseCssPx(value) {
172
+ const match = CSS_LENGTH.exec(String(value ?? ""));
173
+ if (!match || match[2] === "%") return NaN;
174
+ return Number(match[1]);
175
+ }
176
+ /**
177
+ * Parse a whole row of computed CSS widths into a column basis, or reject the set.
178
+ *
179
+ * The result is only ever used *proportionally* (each column's share of the total), so the
180
+ * unit does not have to be absolute — it only has to be the same for every column. Hence:
181
+ *
182
+ * - all `px` (or bare numbers) → their magnitudes
183
+ * - all `%` → their magnitudes; `[25%, 25%, 50%]` is a perfectly good 1:1:2 basis
184
+ * - anything else, or a **mix** of units, or a single unparseable entry (`auto`, `''`,
185
+ * `3em`) → `[]`, meaning "no usable basis"
186
+ *
187
+ * Rejecting the whole set on one bad entry is deliberate: a partial basis would silently
188
+ * give the unparseable columns zero width, which is worse than falling back to an equal
189
+ * split. Negative magnitudes reject for the same reason.
190
+ * @param {readonly string[]} values - computed `width` per column, in column order
191
+ * @returns {number[]} per-column basis, or `[]` when the set is not usable
192
+ */
193
+ function parseCssWidthBasis(values) {
194
+ const basis = [];
195
+ let unit = "";
196
+ for (const value of values) {
197
+ const match = CSS_LENGTH.exec(String(value ?? ""));
198
+ if (!match) return [];
199
+ const magnitude = Number(match[1]);
200
+ if (!isFinite(magnitude) || magnitude < 0) return [];
201
+ const valueUnit = (match[2] ?? "px").toLowerCase();
202
+ if (!unit) unit = valueUnit;
203
+ else if (unit !== valueUnit) return [];
204
+ basis.push(magnitude);
205
+ }
206
+ return basis;
207
+ }
208
+ /**
209
+ * Choose which vector the proportional column math runs on, falling back as bases run out.
210
+ *
211
+ * `offsetWidth` is the ideal basis — it is the width the table actually rendered at — but it
212
+ * requires a layout engine, and reports `0` for every cell both in a hidden table and on any
213
+ * DOM outside a browser. So:
214
+ *
215
+ * 1. **measured** (`offsetWidth`) when it carries any width at all — the browser path, unchanged
216
+ * 2. **CSS widths** when the stylesheet states them (see {@link parseCssWidthBasis})
217
+ * 3. **equal split** otherwise — a basis of all-ones, which the existing proportional math
218
+ * turns into equal columns for free
219
+ *
220
+ * Only the basis changes; `data-pptx-width` / `data-pptx-min-width` overrides still apply
221
+ * downstream via {@link resolveHtmlColWidth} and still win outright.
222
+ *
223
+ * **The first two bases do not measure the same box, and the docs used to imply they did.**
224
+ * `offsetWidth` is the border box; a computed `width` is the content box. Padding alone
225
+ * separates them — `test/browser/harness/table.html` is built so it does, giving 1:1 measured
226
+ * against 2:1 from CSS on one table — so choosing arm 2 over arm 1 can change a table's column
227
+ * *proportions*, not just their precision. That is why the fixture discriminates at all, and why
228
+ * "degrades to computed CSS" was the wrong word for it everywhere it appeared.
229
+ *
230
+ * Left as-is rather than reconciled. Normalizing arm 2 to the border box would need the computed
231
+ * padding and border widths, which the DOMs that reach arm 2 need not resolve either (a `%`
232
+ * padding computes to nothing usable without layout), so it would converge the two only
233
+ * sometimes — and it would collapse the one discriminator the browser lane has, turning
234
+ * `table-widths.spec.mjs` back into a test that passes whether or not the measured arm ran.
235
+ * A caller who needs both runtimes to agree on a column states it with `data-pptx-width`.
236
+ * @param {readonly number[]} measured - per-column `offsetWidth`
237
+ * @param {readonly number[]} cssWidths - per-column CSS width basis (`[]` when unusable)
238
+ * @returns {number[]} the basis vector to run the proportional calc on
239
+ */
240
+ function pickColWidthBasis(measured, cssWidths) {
241
+ const sum = (arr) => arr.reduce((acc, n) => acc + (isFinite(n) ? n : 0), 0);
242
+ if (sum(measured) > 0) return [...measured];
243
+ if (cssWidths.length === measured.length && sum(cssWidths) > 0) return [...cssWidths];
244
+ return measured.map(() => 1);
245
+ }
246
+ /**
247
+ * Read a `colspan`/`rowspan` as the number of grid tracks it covers.
248
+ *
249
+ * Lenient on purpose: `0`, a negative, a fraction, an unparseable attribute and an absent one all
250
+ * mean `1`. A bad span attribute should cost its own cell nothing and the rest of the grid nothing
251
+ * — the alternative, propagating a `NaN` or a negative into the column walk, shifts every
252
+ * subsequent column.
253
+ * @param {unknown} value - the raw attribute value
254
+ * @returns {number} tracks covered, at least 1
255
+ */
256
+ function gridSpan(value) {
257
+ const num = Math.floor(Number(value));
258
+ return isFinite(num) && num > 1 ? num : 1;
259
+ }
260
+ /**
261
+ * Measure the column grid a set of table rows actually occupies.
262
+ *
263
+ * An HTML table is not a rectangle of cells — a row states only the cells it *starts*, so its
264
+ * length says nothing about how many grid columns it fills: a `colspan` fills several, and a
265
+ * `rowspan` started higher up fills one without the row mentioning it. The pptx side has no such
266
+ * model: `<a:tblGrid>` declares N columns and every `<a:tr>` must carry N `<a:tc>`. Something has
267
+ * to translate, and that is a walk of the standard HTML table model — cells pack left to right,
268
+ * skipping columns still held by a rowspan above.
269
+ *
270
+ * Returns the grid width and, per row, how many columns that row reaches. The difference between
271
+ * the two is exactly the padding a ragged row needs; the merge cells for `colspan`/`rowspan` are
272
+ * *not* padding — the table emitter synthesizes those itself, so they are counted as filled here.
273
+ *
274
+ * Spans are read through {@link gridSpan}, so a bad attribute cannot shift the whole grid.
275
+ * @param {readonly (readonly GridCellSpans[])[]} rows - per-row cell spans, in emission order
276
+ * @returns {{ columns: number, filled: number[] }} grid width, and columns reached per row
277
+ */
278
+ function measureGridColumns(rows) {
279
+ const carry = [];
280
+ const filled = [];
281
+ for (const row of rows) {
282
+ let col = 0;
283
+ for (const cell of row) {
284
+ while ((carry[col] ?? 0) > 0) col++;
285
+ const colspan = gridSpan(cell?.colspan);
286
+ for (let idx = 0; idx < colspan; idx++) carry[col + idx] = gridSpan(cell?.rowspan);
287
+ col += colspan;
288
+ }
289
+ while ((carry[col] ?? 0) > 0) col++;
290
+ filled.push(col);
291
+ for (let idx = 0; idx < carry.length; idx++) {
292
+ const held = carry[idx] ?? 0;
293
+ if (held > 0) carry[idx] = held - 1;
294
+ }
295
+ }
296
+ return {
297
+ columns: filled.reduce((max, reached) => reached > max ? reached : max, 0),
298
+ filled
299
+ };
300
+ }
301
+ /**
302
+ * Widen a column-width basis to cover a grid wider than the row it was derived from.
303
+ *
304
+ * The basis comes from one row — the first with cells — but the grid is as wide as the *widest*
305
+ * row, and those need not agree (a single spanning header over a wider body is the common shape).
306
+ * The columns the source row never described get the average of the ones it did, so they read as
307
+ * ordinary columns rather than as a zero-width sliver; with no usable basis at all they get `1`,
308
+ * which the proportional calc downstream turns into an equal split.
309
+ * @param {readonly number[]} basis - per-column basis derived from the width-source row
310
+ * @param {number} columns - the full grid width
311
+ * @returns {number[]} basis covering every grid column
312
+ */
313
+ function extendColBasis(basis, columns) {
314
+ const extended = [...basis];
315
+ const total = extended.reduce((acc, width) => acc + (isFinite(width) ? width : 0), 0);
316
+ const share = extended.length > 0 && total > 0 ? total / extended.length : 1;
317
+ while (extended.length < columns) extended.push(share);
318
+ return extended;
319
+ }
320
+ /**
321
+ * Read a cell's text, with `<br>` surviving as a newline.
322
+ *
323
+ * `innerText` is the right answer in a browser — it is the *rendered* text, so it already
324
+ * collapses whitespace, honors `display: none`, and turns `<br>` into `"\n"`. But it is an
325
+ * HTML-spec extra tied to rendering, and outside a browser it is either absent (jsdom does not
326
+ * implement it, so every cell would come out empty) or present but not actually rendered
327
+ * (happy-dom returns `"ab"` for `a<br>b`, so every cell would come out on one line).
328
+ *
329
+ * So both are computed and the better one wins: a `childNodes` walk that maps `<br>` to a line
330
+ * break and collapses whitespace per line, and `innerText`. `innerText` is preferred unless it
331
+ * dropped a line break the walk found — the one thing that provably makes it not the rendered
332
+ * text. Neither can know about `text-transform` without a browser.
333
+ * @param {TextCell} cell - the table cell element
334
+ * @returns {string} cell text, `<br>`-separated lines joined by `"\n"`
335
+ */
336
+ function readCellText(cell) {
337
+ const lines = [""];
338
+ const append = (text) => {
339
+ lines[lines.length - 1] = (lines[lines.length - 1] ?? "") + text;
340
+ };
341
+ const walk = (node) => {
342
+ if (node.nodeType === 3) {
343
+ append(node.nodeValue ?? "");
344
+ return;
345
+ }
346
+ if (node.nodeType !== 1) return;
347
+ if ((node.nodeName ?? "").toUpperCase() === "BR") {
348
+ lines.push("");
349
+ return;
350
+ }
351
+ const children = node.childNodes;
352
+ for (let idx = 0; idx < (children?.length ?? 0); idx++) {
353
+ const child = children?.[idx];
354
+ if (child) walk(child);
355
+ }
356
+ };
357
+ const children = cell.childNodes;
358
+ for (let idx = 0; idx < (children?.length ?? 0); idx++) {
359
+ const child = children?.[idx];
360
+ if (child) walk(child);
361
+ }
362
+ const walked = lines.map((line) => line.replace(/\s+/g, " ").trim()).join("\n");
363
+ const rendered = cell.innerText;
364
+ if (typeof rendered !== "string") return walked;
365
+ return walked.includes("\n") && !rendered.includes("\n") ? walked : rendered;
366
+ }
367
+ /**
368
+ * Resolve `options.masterTitle` to the slide layout it names, so the auto-pager can take that
369
+ * master's margins.
370
+ *
371
+ * The layout registry is a presentation *internal* — it is not on {@link TableToSlidesHost},
372
+ * and the public `slideLayouts` getter deliberately hides the `_name` this matches on. Rather
373
+ * than widen either, read it defensively: a host that has no registry simply resolves no
374
+ * master, which is what a caller passing a bare `{ addSlide, presLayout }` object should get.
375
+ * The alternative — dropping the lookup on the free-function path — would make `masterTitle`
376
+ * silently do nothing there, which is a trap.
377
+ * @param {TableToSlidesHost} pptx - the presentation the slides are being added to
378
+ * @param {string} masterTitle - the `title` passed to `defineSlideMaster`
379
+ * @returns {SlideLayoutInternal | undefined} the named layout, if this host has one
380
+ */
381
+ function resolveMasterSlide(pptx, masterTitle) {
382
+ if (!masterTitle) return void 0;
383
+ const layouts = pptx._slideLayouts;
384
+ if (!Array.isArray(layouts)) return void 0;
385
+ return layouts.find((layout) => layout._name === masterTitle);
386
+ }
387
+ /**
388
+ * A row's cells, via `HTMLTableRowElement.cells`.
389
+ *
390
+ * Read off the structural shape rather than the `lib.dom` type so a DOM that does not implement
391
+ * the collection degrades to "this row has no cells" instead of throwing — the same tolerance
392
+ * every other DOM read in this file has.
393
+ * @param {Element} row - a `<tr>`
394
+ * @returns {HTMLTableCellElement[]} the row's cells, in document order
395
+ */
396
+ function rowCells(row) {
397
+ const cells = row.cells;
398
+ return cells ? Array.from(cells) : [];
399
+ }
400
+ /**
401
+ * Collect every row of the table, bucketed head → body → foot.
402
+ *
403
+ * `HTMLTableElement.rows` is the right source and a `thead tr`/`tbody tr`/`tfoot tr` query is not,
404
+ * for two reasons the query cannot fix:
405
+ *
406
+ * - **A row need not be in a section.** `<table><tr>…` is valid authored markup, and a table built
407
+ * with `createElement`/`appendChild` has no `<tbody>` at all — the HTML *parser* inserts one, the
408
+ * DOM API does not. Those rows matched none of the three selectors and were silently dropped,
409
+ * which is how a perfectly good table reached `addTable` with zero rows and threw
410
+ * `addTable: Array expected!` (upstream gitbrent/PptxGenJS#1005).
411
+ * - **A descendant query reaches into nested tables.** `tbody tr` matches the rows of a table
412
+ * inside a cell of this one, folding them into the outer table as extra rows.
413
+ *
414
+ * Each row's own parentage settles both questions: it names the section the row belongs to (the
415
+ * head/body/foot split still matters downstream, for repeating header rows on auto-paged slides),
416
+ * and it says whether the row is this table's at all. The ownership check is not redundant with
417
+ * using `rows` — happy-dom's `rows` is itself a descendant walk, so a nested table's rows arrive
418
+ * there too. Checking is what makes the answer the same on every DOM.
419
+ * @param {Element} table - the `<table>` being converted
420
+ * @returns {Array<{ row: Element, part: TablePart }>} rows in emission order
421
+ */
422
+ function collectTableRows(table) {
423
+ const head = [];
424
+ const body = [];
425
+ const foot = [];
426
+ const place = (row) => {
427
+ const parent = row.parentElement;
428
+ const section = (parent?.nodeName ?? "").toUpperCase();
429
+ if ((section === "THEAD" || section === "TBODY" || section === "TFOOT" ? parent?.parentElement : parent) !== table) return;
430
+ if (section === "THEAD") head.push({
431
+ row,
432
+ part: "thead"
433
+ });
434
+ else if (section === "TFOOT") foot.push({
435
+ row,
436
+ part: "tfoot"
437
+ });
438
+ else body.push({
439
+ row,
440
+ part: "tbody"
441
+ });
442
+ };
443
+ const rows = table.rows;
444
+ if (rows) for (let idx = 0; idx < rows.length; idx++) {
445
+ const row = rows[idx];
446
+ if (row) place(row);
447
+ }
448
+ else for (const part of [
449
+ "thead",
450
+ "tbody",
451
+ "tfoot"
452
+ ]) table.querySelectorAll(`${part} tr`).forEach(place);
453
+ return [
454
+ ...head,
455
+ ...body,
456
+ ...foot
457
+ ];
458
+ }
459
+ /**
460
+ * Reproduces an HTML table as a PowerPoint table - including column widths, style, etc. - creates 1 or more slides as needed
461
+ * @param {TableToSlidesHost} pptx - ts-pptx instance
462
+ * @param {TableToSlidesElement | string} target - the table element, or the HTMLElementID of the table
463
+ * @param {TableToSlidesProps} options - array of options (e.g.: tabsize)
464
+ */
465
+ function genTableToSlides(pptx, target, options = {}) {
466
+ const opts = options || {};
467
+ const masterSlide = resolveMasterSlide(pptx, opts.masterTitle);
468
+ opts.slideMargin = opts.slideMargin || opts.slideMargin === 0 ? opts.slideMargin : .5;
469
+ let emuSlideTabW = opts.w || pptx.presLayout.width;
470
+ const arrObjTabHeadRows = [];
471
+ const arrObjTabBodyRows = [];
472
+ const arrObjTabFootRows = [];
473
+ const arrColW = [];
474
+ const arrTabColW = [];
475
+ const arrTabColCssW = [];
476
+ let arrInchMargins = [
477
+ .5,
478
+ .5,
479
+ .5,
480
+ .5
481
+ ];
482
+ let intTabW = 0;
483
+ const ctx = resolveDomContext(target, opts);
484
+ if (masterSlide?._margin) {
485
+ if (Array.isArray(masterSlide._margin)) arrInchMargins = masterSlide._margin;
486
+ else if (!isNaN(masterSlide._margin)) arrInchMargins = [
487
+ masterSlide._margin,
488
+ masterSlide._margin,
489
+ masterSlide._margin,
490
+ masterSlide._margin
491
+ ];
492
+ opts.slideMargin = arrInchMargins;
493
+ } else if (opts?.slideMargin) {
494
+ if (Array.isArray(opts.slideMargin)) arrInchMargins = opts.slideMargin;
495
+ else if (!isNaN(opts.slideMargin)) arrInchMargins = [
496
+ opts.slideMargin,
497
+ opts.slideMargin,
498
+ opts.slideMargin,
499
+ opts.slideMargin
500
+ ];
501
+ }
502
+ emuSlideTabW = (opts.w ? inch2Emu(opts.w) : pptx.presLayout.width) - inch2Emu(arrInchMargins[1] + arrInchMargins[3]);
503
+ if (opts.verbose) {
504
+ console.log("[[VERBOSE MODE]]");
505
+ console.log("|-- `tableToSlides` ----------------------------------------------------|");
506
+ console.log(`| tableProps.h .................................... = ${opts.h}`);
507
+ console.log(`| tableProps.w .................................... = ${opts.w}`);
508
+ console.log(`| pptx.presLayout.width ........................... = ${(pptx.presLayout.width / EMU_PER_INCH).toFixed(1)}`);
509
+ console.log(`| pptx.presLayout.height .......................... = ${(pptx.presLayout.height / EMU_PER_INCH).toFixed(1)}`);
510
+ console.log(`| emuSlideTabW .................................... = ${(emuSlideTabW / EMU_PER_INCH).toFixed(1)}`);
511
+ }
512
+ const domRows = collectTableRows(ctx.table);
513
+ const { columns: intColCnt, filled: arrRowFilled } = measureGridColumns(domRows.map(({ row }) => rowCells(row).map((cell) => ({
514
+ colspan: Number(cell.getAttribute("colspan")),
515
+ rowspan: Number(cell.getAttribute("rowspan"))
516
+ }))));
517
+ const srcRow = domRows.find(({ row }) => rowCells(row).length > 0);
518
+ if (!srcRow) throw new InvalidOptionError("html/table-has-no-cells", "tableToSlides: the table has no cells to convert - expected at least one <tr> with a <td>/<th>.");
519
+ const arrColSrc = [];
520
+ const arrCellSpans = [];
521
+ rowCells(srcRow.row).forEach((cell) => {
522
+ const offsetW = Number(cell.offsetWidth);
523
+ const measured = isFinite(offsetW) ? offsetW : 0;
524
+ arrTabColCssW.push(ctx.getComputedStyle(cell).getPropertyValue("width"));
525
+ const span = gridSpan(cell.getAttribute("colspan"));
526
+ arrCellSpans.push(span);
527
+ for (let idxc = 0; idxc < span; idxc++) {
528
+ arrTabColW.push(Math.round(measured / span));
529
+ arrColSrc.push({
530
+ cell,
531
+ span
532
+ });
533
+ }
534
+ });
535
+ const parsedCellCssW = parseCssWidthBasis(arrTabColCssW);
536
+ const arrCssColW = [];
537
+ if (parsedCellCssW.length === arrCellSpans.length) parsedCellCssW.forEach((cssW, idxC) => {
538
+ const span = arrCellSpans[idxC] ?? 1;
539
+ for (let idxc = 0; idxc < span; idxc++) arrCssColW.push(cssW / span);
540
+ });
541
+ const arrBasisColW = extendColBasis(pickColWidthBasis(arrTabColW, arrCssColW), intColCnt);
542
+ arrBasisColW.forEach((colW) => {
543
+ intTabW += colW;
544
+ });
545
+ arrBasisColW.forEach((colW, idxW) => {
546
+ const intCalcWidth = Number((Number(emuSlideTabW) * (colW / intTabW * 100) / 100 / EMU_PER_INCH).toFixed(2));
547
+ const src = arrColSrc[idxW];
548
+ const intSetWidth = src ? Number(src.cell.getAttribute("data-pptx-width")) / src.span : 0;
549
+ const intMinWidth = src ? Number(src.cell.getAttribute("data-pptx-min-width")) / src.span : 0;
550
+ arrColW.push(resolveHtmlColWidth(intCalcWidth, intSetWidth, intMinWidth));
551
+ });
552
+ if (opts.verbose) console.log(`| arrColW ......................................... = [${arrColW.join(", ")}]`);
553
+ domRows.forEach(({ row, part }, idxRow) => {
554
+ const arrObjTabCells = [];
555
+ rowCells(row).forEach((cell) => {
556
+ const style = ctx.getComputedStyle(cell);
557
+ const textColor = cssColorToHex(style.getPropertyValue("color")) ?? "000000";
558
+ const fillColor = cssColorToHex(style.getPropertyValue("background-color")) ?? "FFFFFF";
559
+ const cellOpts = {
560
+ bold: !!(style.getPropertyValue("font-weight") === "bold" || Number(style.getPropertyValue("font-weight")) >= 500),
561
+ color: textColor,
562
+ fill: { color: fillColor },
563
+ fontSize: Number(style.getPropertyValue("font-size").replace(/[a-z]/gi, ""))
564
+ };
565
+ const fontFace = ((style.getPropertyValue("font-family") || "").split(",")[0] ?? "").replace(/"/g, "").replace("inherit", "").replace("initial", "");
566
+ const colspan = gridSpan(cell.getAttribute("colspan"));
567
+ const rowspan = gridSpan(cell.getAttribute("rowspan"));
568
+ if (fontFace) cellOpts.fontFace = fontFace;
569
+ if (colspan > 1) cellOpts.colspan = colspan;
570
+ if (rowspan > 1) cellOpts.rowspan = rowspan;
571
+ if ([
572
+ "left",
573
+ "center",
574
+ "right",
575
+ "start",
576
+ "end"
577
+ ].includes(style.getPropertyValue("text-align"))) {
578
+ const align = style.getPropertyValue("text-align").replace("start", "left").replace("end", "right");
579
+ cellOpts.align = align === "center" ? "center" : align === "left" ? "left" : align === "right" ? "right" : void 0;
580
+ }
581
+ if ([
582
+ "top",
583
+ "middle",
584
+ "bottom"
585
+ ].includes(style.getPropertyValue("vertical-align"))) {
586
+ const valign = style.getPropertyValue("vertical-align");
587
+ cellOpts.valign = valign === "top" ? "top" : valign === "middle" ? "middle" : valign === "bottom" ? "bottom" : void 0;
588
+ }
589
+ if (style.getPropertyValue("padding-left")) {
590
+ const cellMargin = [
591
+ 0,
592
+ 0,
593
+ 0,
594
+ 0
595
+ ];
596
+ [
597
+ "padding-top",
598
+ "padding-right",
599
+ "padding-bottom",
600
+ "padding-left"
601
+ ].forEach((val, idxs) => {
602
+ const pad = parseCssPx(style.getPropertyValue(val));
603
+ cellMargin[idxs] = isFinite(pad) ? pad / 96 : 0;
604
+ });
605
+ cellOpts.margin = cellMargin;
606
+ }
607
+ if (style.getPropertyValue("border-top-width") || style.getPropertyValue("border-right-width") || style.getPropertyValue("border-bottom-width") || style.getPropertyValue("border-left-width")) {
608
+ const cellBorder = [
609
+ { type: "none" },
610
+ { type: "none" },
611
+ { type: "none" },
612
+ { type: "none" }
613
+ ];
614
+ [
615
+ "top",
616
+ "right",
617
+ "bottom",
618
+ "left"
619
+ ].forEach((val, idxb) => {
620
+ cellBorder[idxb] = htmlBorderToProps(style.getPropertyValue("border-" + val + "-width"), style.getPropertyValue("border-" + val + "-color"));
621
+ });
622
+ cellOpts.border = cellBorder;
623
+ }
624
+ arrObjTabCells.push({
625
+ _type: "tablecell",
626
+ text: readCellText(cell),
627
+ options: cellOpts
628
+ });
629
+ });
630
+ for (let idxPad = arrRowFilled[idxRow] ?? 0; idxPad < intColCnt; idxPad++) arrObjTabCells.push({
631
+ _type: "tablecell",
632
+ text: "",
633
+ options: {}
634
+ });
635
+ if (part === "thead") arrObjTabHeadRows.push(arrObjTabCells);
636
+ else if (part === "tfoot") arrObjTabFootRows.push(arrObjTabCells);
637
+ else arrObjTabBodyRows.push(arrObjTabCells);
638
+ });
639
+ opts._arrObjTabHeadRows = arrObjTabHeadRows;
640
+ opts.colW = arrColW;
641
+ getSlidesForTableRows([
642
+ ...arrObjTabHeadRows,
643
+ ...arrObjTabBodyRows,
644
+ ...arrObjTabFootRows
645
+ ], opts, pptx.presLayout, masterSlide).forEach((slide, idxTr) => {
646
+ const newSlide = pptx.addSlide({ masterTitle: opts.masterTitle || void 0 });
647
+ if (idxTr === 0) opts.y = opts.y || arrInchMargins[0];
648
+ if (idxTr > 0) opts.y = opts.autoPageSlideStartY || arrInchMargins[0];
649
+ if (opts.verbose) console.log(`| opts.autoPageSlideStartY: ${opts.autoPageSlideStartY} / arrInchMargins[0]: ${arrInchMargins[0]} => opts.y = ${opts.y}`);
650
+ newSlide.addTable(slide.rows, {
651
+ x: opts.x || arrInchMargins[3],
652
+ y: opts.y,
653
+ w: Number(emuSlideTabW) / EMU_PER_INCH,
654
+ colW: arrColW,
655
+ autoPage: false
656
+ });
657
+ if (opts.addImage) {
658
+ opts.addImage.options = opts.addImage.options || {};
659
+ if (!opts.addImage.image || !opts.addImage.image.path && !opts.addImage.image.data) warn("html/image-missing-source", "tableToSlides.addImage requires either `path` or `data`");
660
+ else {
661
+ const imageProps = opts.addImage.image.path ? { path: opts.addImage.image.path } : { data: opts.addImage.image.data };
662
+ newSlide.addImage({
663
+ ...imageProps,
664
+ x: opts.addImage.options.x,
665
+ y: opts.addImage.options.y,
666
+ w: opts.addImage.options.w,
667
+ h: opts.addImage.options.h
668
+ });
669
+ }
670
+ }
671
+ if (opts.addShape) newSlide.addShape(opts.addShape.shapeName, opts.addShape.options || {});
672
+ if (opts.addTable) newSlide.addTable(opts.addTable.rows, opts.addTable.options || {});
673
+ if (opts.addText) newSlide.addText(opts.addText.text, opts.addText.options || {});
674
+ });
675
+ }
676
+ //#endregion
677
+ export { genTableToSlides as t };
678
+
679
+ //# sourceMappingURL=html-dom-B0I0QSbU.js.map