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