pptx-ts 4.0.1 → 4.1.1-next.17.g4636d72
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.
- package/README.md +147 -114
- package/dist/{autopage-odPz7zZO.js → autopage-D4kyVp11.js} +5 -5
- package/dist/{autopage-odPz7zZO.js.map → autopage-D4kyVp11.js.map} +1 -1
- package/dist/{body-insets-Dw_BeWD_.js → body-insets-kVHmjGc6.js} +2 -2
- package/dist/{body-insets-Dw_BeWD_.js.map → body-insets-kVHmjGc6.js.map} +1 -1
- package/dist/browser.d.ts +7 -7
- package/dist/browser.js +5 -5
- package/dist/{clip-Bl01BPcN.d.ts → clip-BkzLGLTj.d.ts} +2 -2
- package/dist/{clip-Bl01BPcN.d.ts.map → clip-BkzLGLTj.d.ts.map} +1 -1
- package/dist/{diagnostics-D8ot4Ei7.d.ts → diagnostics-UEnuaGE5.d.ts} +8 -8
- package/dist/{diagnostics-D8ot4Ei7.d.ts.map → diagnostics-UEnuaGE5.d.ts.map} +1 -1
- package/dist/{embedded-fonts-DarT9aoa.d.ts → embedded-fonts-DQaUrNYv.d.ts} +2 -2
- package/dist/{embedded-fonts-DarT9aoa.d.ts.map → embedded-fonts-DQaUrNYv.d.ts.map} +1 -1
- package/dist/{entry-compose-DCnvBZz2.d.ts → entry-compose-BM9vmPPw.d.ts} +19 -17
- package/dist/{entry-compose-DCnvBZz2.d.ts.map → entry-compose-BM9vmPPw.d.ts.map} +1 -1
- package/dist/{enums-BUKTUB6V.d.ts → enums-DQRpVOLh.d.ts} +3 -3
- package/dist/{enums-BUKTUB6V.d.ts.map → enums-DQRpVOLh.d.ts.map} +1 -1
- package/dist/{errors-C4oENPcu.js → errors-Da2nXCJm.js} +6 -6
- package/dist/{errors-C4oENPcu.js.map → errors-Da2nXCJm.js.map} +1 -1
- package/dist/families.d.ts +6 -6
- package/dist/families.js +2 -2
- package/dist/{fit-Cb7naFj6.js → fit-CcXbg__l.js} +10 -10
- package/dist/{fit-Cb7naFj6.js.map → fit-CcXbg__l.js.map} +1 -1
- package/dist/{font-metrics-v5fn7s7e.d.ts → font-metrics-CUnCHR3S.d.ts} +6 -6
- package/dist/{font-metrics-v5fn7s7e.d.ts.map → font-metrics-CUnCHR3S.d.ts.map} +1 -1
- package/dist/{grid-DikTjFey.js → grid-9B2w5EaY.js} +2 -2
- package/dist/{grid-DikTjFey.js.map → grid-9B2w5EaY.js.map} +1 -1
- package/dist/{html-dom-Iuog2wdK.js → html-dom-DvJmuHt2.js} +10 -10
- package/dist/html-dom-DvJmuHt2.js.map +1 -0
- package/dist/html.d.ts +3 -3
- package/dist/html.js +2 -2
- package/dist/{image-size-CvN2iCA4.js → image-size-DwDbakcl.js} +64 -5
- package/dist/image-size-DwDbakcl.js.map +1 -0
- package/dist/index.d.ts +7 -7
- package/dist/index.js +3 -3
- package/dist/inspect.d.ts +4 -4
- package/dist/inspect.js +4 -4
- package/dist/inspect.js.map +1 -1
- package/dist/{text-anchor-CrYXAfUu.js → lock-attrs-Bi6NVbej.js} +55 -3
- package/dist/lock-attrs-Bi6NVbej.js.map +1 -0
- package/dist/math.d.ts +1 -1
- package/dist/math.js +1 -1
- package/dist/measure.d.ts +3 -3
- package/dist/measure.js +2 -2
- package/dist/node.d.ts +7 -7
- package/dist/node.js +3 -3
- package/dist/{notes-Dh12JAbn.js → notes-CRu_Ypml.js} +38 -22
- package/dist/notes-CRu_Ypml.js.map +1 -0
- package/dist/options-internal-WqgMf720.js.map +1 -1
- package/dist/{package-J49Ck6S2.d.ts → package-KoEgVwII.d.ts} +6 -2
- package/dist/{package-J49Ck6S2.d.ts.map → package-KoEgVwII.d.ts.map} +1 -1
- package/dist/{pres-C6qgOFAa.d.ts → pres-C6tEDikt.d.ts} +59 -14
- package/dist/pres-C6tEDikt.d.ts.map +1 -0
- package/dist/{presentation-DGYlAWhl.js → presentation-DW83laic.js} +70 -35
- package/dist/presentation-DW83laic.js.map +1 -0
- package/dist/{presentation-types-8jLQHI4M.d.ts → presentation-types-NkPl7zfU.d.ts} +154 -47
- package/dist/presentation-types-NkPl7zfU.d.ts.map +1 -0
- package/dist/read.d.ts +7 -7
- package/dist/read.js +3 -3
- package/dist/script.d.ts +2 -2
- package/dist/script.d.ts.map +1 -1
- package/dist/script.js +315 -55
- package/dist/script.js.map +1 -1
- package/dist/{shapes-re0KyQk_.js → shapes-B3bO4ono.js} +192 -54
- package/dist/shapes-B3bO4ono.js.map +1 -0
- package/dist/{slide-Dx2ZeVZ6.d.ts → slide-D1uABOLy.d.ts} +16 -7
- package/dist/slide-D1uABOLy.d.ts.map +1 -0
- package/dist/{table-cell-margins-EVg0EwxT.js → table-cell-margins-PDshga8T.js} +4 -4
- package/dist/{table-cell-margins-EVg0EwxT.js.map → table-cell-margins-PDshga8T.js.map} +1 -1
- package/dist/{table-dom-BTDP3lEP.js → table-dom-CVbvUfgh.js} +3 -3
- package/dist/{table-dom-BTDP3lEP.js.map → table-dom-CVbvUfgh.js.map} +1 -1
- package/dist/{text-sN64Otf_.js → text-8DZ4esMQ.js} +39 -37
- package/dist/text-8DZ4esMQ.js.map +1 -0
- package/dist/{units-internal-XJlf-hOl.js → units-internal-DFcUYjh3.js} +11 -6
- package/dist/units-internal-DFcUYjh3.js.map +1 -0
- package/dist/{zip-C0leZZMj.d.ts → zip-CTll_dr0.d.ts} +2 -2
- package/dist/{zip-C0leZZMj.d.ts.map → zip-CTll_dr0.d.ts.map} +1 -1
- package/dist/{zip-CKd8ihcf.js → zip-CYZ0F6BV.js} +2 -2
- package/dist/{zip-CKd8ihcf.js.map → zip-CYZ0F6BV.js.map} +1 -1
- package/dist/zip.d.ts +2 -2
- package/dist/zip.js +2 -2
- package/dist/{zoom-DPUHh85d.js → zoom-Bjd8B4U8.js} +159 -99
- package/dist/zoom-Bjd8B4U8.js.map +1 -0
- package/package.json +40 -32
- package/skills/ts-pptx-upstream/SKILL.md +18 -18
- package/dist/html-dom-Iuog2wdK.js.map +0 -1
- package/dist/image-size-CvN2iCA4.js.map +0 -1
- package/dist/notes-Dh12JAbn.js.map +0 -1
- package/dist/pres-C6qgOFAa.d.ts.map +0 -1
- package/dist/presentation-DGYlAWhl.js.map +0 -1
- package/dist/presentation-types-8jLQHI4M.d.ts.map +0 -1
- package/dist/shapes-re0KyQk_.js.map +0 -1
- package/dist/slide-Dx2ZeVZ6.d.ts.map +0 -1
- package/dist/text-anchor-CrYXAfUu.js.map +0 -1
- package/dist/text-sN64Otf_.js.map +0 -1
- package/dist/units-internal-XJlf-hOl.js.map +0 -1
- package/dist/zoom-DPUHh85d.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,102 +1,147 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
[](LICENSE)
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/readme/banner/banner-dark.svg">
|
|
5
|
+
<img alt="pptx-ts: write a program, get a PowerPoint file" src="assets/readme/banner/banner-light.svg">
|
|
6
|
+
</picture>
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
[![npm][npm-badge]][npm]
|
|
9
|
+
[![Downloads][downloads-badge]][npm]
|
|
10
|
+
[![CI][ci-badge]][ci]
|
|
11
|
+
[![License][license-badge]][license]
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
slides in TypeScript and a `.pptx` comes out the other end. PowerPoint never runs, no
|
|
13
|
-
Office licence is involved, and nothing has to be installed on the machine doing the
|
|
14
|
-
writing. The file opens cleanly in desktop PowerPoint, and Keynote, LibreOffice Impress
|
|
15
|
-
and Google Slides import it on a best-effort basis.
|
|
13
|
+
---
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
one deck per customer, a hundred decks per night, or a download button on a page that
|
|
19
|
-
hands the user a deck built from what they are looking at.
|
|
15
|
+
[Install](#install) • [Quickstart](#quickstart) • [Read and edit](#read-and-edit-decks-too) • [Compare](#how-this-compares-with-pptxgenjs) • [Docs](https://shbernal.github.io/pptx-ts/)
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
---
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
pnpm add pptx-ts
|
|
25
|
-
```
|
|
19
|
+
</div>
|
|
26
20
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
<p align="center">
|
|
22
|
+
<picture>
|
|
23
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/readme/demo/demo-dark.svg">
|
|
24
|
+
<img alt="A 28-line TypeScript program runs and writes report.pptx, a slide with a revenue bar chart" src="assets/readme/demo/demo-light.svg">
|
|
25
|
+
</picture>
|
|
26
|
+
</p>
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
pptx-ts writes `.pptx` files from TypeScript.
|
|
29
|
+
You describe slides in code, and a deck comes out that opens cleanly in desktop PowerPoint.
|
|
30
|
+
PowerPoint never runs, so there is no Office licence and nothing to install on the machine doing the writing.
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
- Text, tables that page across slides, shapes, connectors, pictures, SVG, video and audio.
|
|
33
|
+
- Native charts backed by an embedded workbook, so PowerPoint's "Edit Data" works.
|
|
34
|
+
- Slide masters, layouts, sections, speaker notes, gradients and an image clipped to a shape.
|
|
35
|
+
- LaTeX maths, embedded spreadsheets and 3D models.
|
|
36
|
+
- [An HTML `<table>` becomes paged slides](docs/html-tables.md), in the browser or under Node.
|
|
37
|
+
- [Text measured against the real font](docs/text-fit.md), so a box shrinks or grows to fit before the file is written.
|
|
38
|
+
- Opens existing decks too: [inspect](docs/reference/pptx-inspection.md), [edit](docs/reading/read-and-edit.md), or [turn one into code](docs/reference/pptx-to-script.md).
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
<p align="center">
|
|
41
|
+
<img alt="Nine slides from the two showcase decks: a consulting cover, a cost waterfall, a dark pitch-deck cover over a night sky, KPI cards, a pipeline diagram with connectors, a business-case table with Harvey balls, a terminal mock, a 2x2 prioritisation matrix and an area chart" src="assets/readme/gallery/gallery.webp">
|
|
42
|
+
</p>
|
|
38
43
|
|
|
39
|
-
slide
|
|
40
|
-
|
|
41
|
-
y: 1,
|
|
42
|
-
w: 8,
|
|
43
|
-
h: 1,
|
|
44
|
-
fontSize: 24,
|
|
45
|
-
color: "363636",
|
|
46
|
-
})
|
|
44
|
+
pptx-ts wrote every slide above, and LibreOffice rendered them.
|
|
45
|
+
They come from the two [showcase decks](www/demos/decks/README.md).
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
## Why pptx-ts?
|
|
48
|
+
|
|
49
|
+
Use it when a deck has to be built from data that changes:
|
|
50
|
+
|
|
51
|
+
- a monthly report
|
|
52
|
+
- one deck per customer
|
|
53
|
+
- a hundred decks every night
|
|
54
|
+
- a download button that hands the user a deck built from what they are looking at
|
|
55
|
+
|
|
56
|
+
Every deck has to open cleanly in desktop PowerPoint.
|
|
57
|
+
Keynote, LibreOffice Impress and Google Slides import it on a best-effort basis.
|
|
58
|
+
|
|
59
|
+
## Quickstart
|
|
60
|
+
|
|
61
|
+
1. Install pptx-ts on your project:
|
|
62
|
+
|
|
63
|
+
[![Node ≥ 24][node-badge]][node]
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pnpm add pptx-ts
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
2. Save this as `hello.mts`:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import TsPptx from "pptx-ts"
|
|
73
|
+
|
|
74
|
+
const pptx = new TsPptx()
|
|
75
|
+
const slide = pptx.addSlide()
|
|
76
|
+
|
|
77
|
+
slide.addText("Hello from pptx-ts", {
|
|
78
|
+
x: 1,
|
|
79
|
+
y: 1,
|
|
80
|
+
w: 8,
|
|
81
|
+
h: 1,
|
|
82
|
+
fontSize: 24,
|
|
83
|
+
color: "363636",
|
|
84
|
+
})
|
|
50
85
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
in from the top-left corner of a 10 by 5.625 inch slide.
|
|
54
|
-
[Your first deck](docs/getting-started/first-deck.md) builds a bigger one from data, with a
|
|
55
|
-
table, a chart and speaker notes.
|
|
86
|
+
await pptx.writeFile({ fileName: "hello.pptx" })
|
|
87
|
+
```
|
|
56
88
|
|
|
57
|
-
|
|
89
|
+
2. Run it. Node 24 strips the types itself, so there is no build step.
|
|
58
90
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
PowerPoint opens its data the way it does for a chart a human made. Speaker notes,
|
|
63
|
-
sections, slide masters and layouts, gradients, an image clipped to a shape, a
|
|
64
|
-
spreadsheet embedded as an object, a 3D model, and LaTeX maths.
|
|
91
|
+
```bash
|
|
92
|
+
node hello.mts
|
|
93
|
+
```
|
|
65
94
|
|
|
66
|
-
|
|
95
|
+
3. Open `hello.pptx`.
|
|
67
96
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
slides. Works in a browser and under Node.
|
|
71
|
-
- **[Text that has to fit](docs/text-fit.md).** ts-pptx can measure the text
|
|
72
|
-
against the real font and shrink or grow the box before it writes the file, instead
|
|
73
|
-
of leaving you to guess at font sizes.
|
|
97
|
+
Positions are in inches, from the top-left corner of a 10 by 5.625 inch slide.
|
|
98
|
+
[Your first deck](docs/getting-started/first-deck.md) builds a four-slide report from an array, with a table, a chart and speaker notes.
|
|
74
99
|
|
|
75
|
-
##
|
|
100
|
+
## Read and edit decks too
|
|
76
101
|
|
|
77
|
-
|
|
78
|
-
|
|
102
|
+
PptxGenJS, the library pptx-ts descends from, only writes.
|
|
103
|
+
pptx-ts also opens a `.pptx` you already have.
|
|
79
104
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
-
|
|
83
|
-
|
|
84
|
-
-
|
|
85
|
-
get TypeScript that rebuilds it. Anything it could not express is reported to you
|
|
86
|
-
rather than dropped in silence. It is the fastest way to learn the API: build a
|
|
87
|
-
slide by hand in PowerPoint, then read the script for it.
|
|
105
|
+
| Entry point | What it does |
|
|
106
|
+
| --- | --- |
|
|
107
|
+
| `pptx-ts/inspect` | Slide count, size, parts, media and fonts, without loading the whole deck. [Guide](docs/reference/pptx-inspection.md) |
|
|
108
|
+
| `pptx-ts/read` | Open a deck, change the text on slide four, save it. Parts you did not touch keep their original bytes. [Guide](docs/reading/read-and-edit.md) |
|
|
109
|
+
| `pptx-ts/script` | Print TypeScript that rebuilds a deck, plus a note for each construct it could not carry. [Guide](docs/reference/pptx-to-script.md) |
|
|
88
110
|
|
|
89
|
-
|
|
111
|
+
```ts
|
|
112
|
+
import { readFile, writeFile } from "node:fs/promises"
|
|
113
|
+
import { Presentation } from "pptx-ts/read"
|
|
114
|
+
|
|
115
|
+
const deck = await Presentation.load(await readFile("report.pptx"))
|
|
116
|
+
const title = deck.slides[0]?.placeholder("title")
|
|
117
|
+
if (title) title.text = "Q3 review"
|
|
118
|
+
|
|
119
|
+
await writeFile("report.pptx", await deck.save())
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The script printer is the quickest way to learn the API.
|
|
123
|
+
Build a slide by hand in PowerPoint, then read the code for it.
|
|
124
|
+
|
|
125
|
+
## How it works
|
|
126
|
+
|
|
127
|
+
<p align="center">
|
|
128
|
+
<picture>
|
|
129
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/readme/how-it-works/loop-dark.svg">
|
|
130
|
+
<img alt="pptx-ts writes a .pptx from your code; pptx-ts/read loads and saves it, pptx-ts/inspect lists its contents, and pptx-ts/script turns it back into TypeScript" src="assets/readme/how-it-works/loop-light.svg">
|
|
131
|
+
</picture>
|
|
132
|
+
</p>
|
|
90
133
|
|
|
91
|
-
|
|
134
|
+
A `.pptx` is a zip of XML parts.
|
|
135
|
+
pptx-ts builds those parts from your calls and zips them, with no Office process involved.
|
|
136
|
+
|
|
137
|
+
| Runtime | Import | `writeFile` |
|
|
92
138
|
| --- | --- | --- |
|
|
93
139
|
| Node.js 24 or later | `import`, or `require()` with the class on `.default` | writes to disk |
|
|
94
|
-
|
|
|
95
|
-
|
|
|
140
|
+
| Browser, with a bundler | `import` | downloads the file |
|
|
141
|
+
| Browser, no build step | `import TsPptx from "https://esm.sh/pptx-ts/browser"` | downloads the file |
|
|
96
142
|
| Deno, Bun, edge workers | `import` | throws; use `toBytes()` |
|
|
97
143
|
|
|
98
|
-
[Where it runs](docs/getting-started/runtime.md) lists every entry point
|
|
99
|
-
loads the one ESM build.
|
|
144
|
+
[Where it runs](docs/getting-started/runtime.md) lists every entry point.
|
|
100
145
|
|
|
101
146
|
<!-- comparison:start -->
|
|
102
147
|
<!-- GENERATED REGION. Do not edit by hand.
|
|
@@ -105,20 +150,20 @@ loads the one ESM build.
|
|
|
105
150
|
|
|
106
151
|
## How this compares with PptxGenJS
|
|
107
152
|
|
|
108
|
-
ts
|
|
153
|
+
pptx-ts is an independent derivative of
|
|
109
154
|
[PptxGenJS](https://github.com/gitbrent/PptxGenJS), detached at its v4.0.1. Both were
|
|
110
|
-
measured on 2026-
|
|
155
|
+
measured on 2026-10-11 by building the same 22 deck intents with each library and reading
|
|
111
156
|
the bytes that came out.
|
|
112
157
|
|
|
113
|
-
- **Construct coverage:** ts
|
|
114
|
-
corpus is emitted by pptxgenjs and not by ts
|
|
115
|
-
- **Schema validity:** of the decks each library built, 21 of 21 ts
|
|
158
|
+
- **Construct coverage:** pptx-ts emitted 21 of 22, pptxgenjs 10 of 22. Nothing in the
|
|
159
|
+
corpus is emitted by pptxgenjs and not by pptx-ts.
|
|
160
|
+
- **Schema validity:** of the decks each library built, 21 of 21 pptx-ts decks and 0 of 10
|
|
116
161
|
pptxgenjs decks validate with no error against the Open XML SDK.
|
|
117
|
-
- **Adoption:** pptxgenjs is downloaded
|
|
118
|
-
ts
|
|
162
|
+
- **Adoption:** pptxgenjs is downloaded 14,641,405 times a month, against 1,915 for
|
|
163
|
+
pptx-ts. If a large installed base matters to you more than the differences above, use
|
|
119
164
|
pptxgenjs.
|
|
120
|
-
- **Activity:** last commit on the default branch, 2026-
|
|
121
|
-
for pptxgenjs. Last npm publish, 2026-
|
|
165
|
+
- **Activity:** last commit on the default branch, 2026-10-10 for pptx-ts and 2025-06-26
|
|
166
|
+
for pptxgenjs. Last npm publish, 2026-09-21 and 2025-06-26.
|
|
122
167
|
|
|
123
168
|
Where the two libraries part company is on the [comparison page](docs/comparison.md), and
|
|
124
169
|
[how it was measured](docs/comparison-method.md) has every full table. Every intent as
|
|
@@ -127,40 +172,28 @@ PptxGenJS](docs/comparison-syntax.md).
|
|
|
127
172
|
|
|
128
173
|
<!-- comparison:end -->
|
|
129
174
|
|
|
130
|
-
##
|
|
131
|
-
|
|
132
|
-
The documentation site, with the generated API reference, is at
|
|
133
|
-
**<https://shbernal.github.io/ts-pptx/>**. The [demos page](https://shbernal.github.io/ts-pptx/demos)
|
|
134
|
-
builds a quarterly review deck in your browser and previews the slides.
|
|
135
|
-
|
|
136
|
-
- Start with the [Introduction](docs/getting-started/introduction.md),
|
|
137
|
-
[Your first deck](docs/getting-started/first-deck.md) and
|
|
138
|
-
[Core concepts](docs/getting-started/concepts.md)
|
|
139
|
-
- [Tables](docs/tables.md), [connectors](docs/connectors.md), [groups](docs/groups.md) and
|
|
140
|
-
[HTML tables to slides](docs/html-tables.md)
|
|
141
|
-
- [Smaller bundles](docs/bundle-size.md) for a browser program that composes only what it uses
|
|
142
|
-
- [Errors and warnings](docs/errors-and-warnings.md) and
|
|
143
|
-
[troubleshooting](docs/troubleshooting.md)
|
|
144
|
-
|
|
145
|
-
## Something wrong, or missing?
|
|
175
|
+
## Learn more
|
|
146
176
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
reproduction, instead of a silent workaround:
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
npx skills add ./node_modules/pptx-ts -s '*' -a claude-code -a codex -a universal -y
|
|
156
|
-
```
|
|
177
|
+
- [Documentation site](https://shbernal.github.io/pptx-ts/), with the API reference
|
|
178
|
+
- [Live demo](https://shbernal.github.io/pptx-ts/demos): build a strategy consulting deck and a startup pitch deck in your browser
|
|
179
|
+
- [Core concepts](docs/getting-started/concepts.md), [tables](docs/tables.md), [connectors](docs/connectors.md) and [groups](docs/groups.md)
|
|
180
|
+
- [Smaller browser bundles](docs/bundle-size.md)
|
|
181
|
+
- [Errors and warnings](docs/errors-and-warnings.md) and [troubleshooting](docs/troubleshooting.md)
|
|
157
182
|
|
|
158
|
-
|
|
159
|
-
|
|
183
|
+
Found a bug? [Open an issue](https://github.com/shbernal/pptx-ts/issues).
|
|
184
|
+
Errors that are the library's fault print that link themselves.
|
|
160
185
|
|
|
161
|
-
|
|
186
|
+
If an agent writes most of your code, install the `ts-pptx-upstream` skill that ships in the package.
|
|
187
|
+
It files a library defect the agent hits as an issue with a small reproduction, instead of a silent workaround.
|
|
188
|
+
[CONTRIBUTING.md](CONTRIBUTING.md#the-skill-that-files-the-issue-for-you) has the install command.
|
|
162
189
|
|
|
163
|
-
Copyright (c) 2015-2022 Brent Ely.
|
|
164
|
-
Modifications copyright (c) 2026 shbernal.
|
|
165
190
|
|
|
166
|
-
[
|
|
191
|
+
[npm]: https://www.npmjs.com/package/pptx-ts
|
|
192
|
+
[npm-badge]: https://img.shields.io/npm/v/pptx-ts?style=for-the-badge&logo=npm&logoColor=e9855c&label=npm&labelColor=1b1b1f&color=b4451f
|
|
193
|
+
[downloads-badge]: https://img.shields.io/npm/dm/pptx-ts?style=for-the-badge&label=downloads&labelColor=1b1b1f&color=b4451f
|
|
194
|
+
[ci]: https://github.com/shbernal/pptx-ts/actions/workflows/ci.yml
|
|
195
|
+
[ci-badge]: https://img.shields.io/github/actions/workflow/status/shbernal/pptx-ts/ci.yml?branch=main&style=for-the-badge&logo=githubactions&logoColor=e9855c&label=CI&labelColor=1b1b1f&color=b4451f
|
|
196
|
+
[node]: package.json
|
|
197
|
+
[node-badge]: https://img.shields.io/badge/node-%E2%89%A524-b4451f?style=for-the-badge&logo=nodedotjs&logoColor=e9855c&labelColor=1b1b1f
|
|
198
|
+
[license]: LICENSE
|
|
199
|
+
[license-badge]: https://img.shields.io/github/license/shbernal/pptx-ts?style=for-the-badge&labelColor=1b1b1f&color=b4451f
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { l as warn } from "./errors-
|
|
2
|
-
import { C as usableTableWidthEmu, R as DEF_SLIDE_MARGIN_IN, _ as resolveCellMarginsInches, b as resolveTableColWidthsEmu, c as inch2Emu, d as marginToEmu, m as pinnedRowHeightInches, s as getSmartParseNumber, t as autoPageLineHeightEmu, y as resolveSlideMarginsInches } from "./units-internal-
|
|
3
|
-
import { F as EMU_PER_INCH } from "./table-cell-margins-
|
|
4
|
-
import { a as resolveSpan, n as tableColCount, r as walkTableGrid, s as withCheckedSpans } from "./grid-
|
|
1
|
+
import { l as warn } from "./errors-Da2nXCJm.js";
|
|
2
|
+
import { C as usableTableWidthEmu, R as DEF_SLIDE_MARGIN_IN, _ as resolveCellMarginsInches, b as resolveTableColWidthsEmu, c as inch2Emu, d as marginToEmu, m as pinnedRowHeightInches, s as getSmartParseNumber, t as autoPageLineHeightEmu, y as resolveSlideMarginsInches } from "./units-internal-DFcUYjh3.js";
|
|
3
|
+
import { F as EMU_PER_INCH } from "./table-cell-margins-PDshga8T.js";
|
|
4
|
+
import { a as resolveSpan, n as tableColCount, r as walkTableGrid, s as withCheckedSpans } from "./grid-9B2w5EaY.js";
|
|
5
5
|
//#region src/gen/table/autopage.ts
|
|
6
6
|
/**
|
|
7
7
|
* A cell for the pager's working grid, carrying the source cell's options only when it has some.
|
|
@@ -533,4 +533,4 @@ function getSlidesForTableRows(rows = [], tableProps = {}, presLayout, masterSli
|
|
|
533
533
|
//#endregion
|
|
534
534
|
export { getSlidesForTableRows as t };
|
|
535
535
|
|
|
536
|
-
//# sourceMappingURL=autopage-
|
|
536
|
+
//# sourceMappingURL=autopage-D4kyVp11.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autopage-odPz7zZO.js","names":[],"sources":["../src/gen/table/autopage.ts"],"sourcesContent":["/**\n * ts-pptx: Table auto-paging core\n *\n * The DOM-independent heart of table generation: given rows that overflow a slide,\n * split them across as many slides as needed, measuring wrapped text to compute line\n * counts and row heights. `getSlidesForTableRows` is the in-memory core (fully\n * Node-testable); `parseTextToLines` is its private cell-wrapping helper.\n */\n\nimport { SlideObjectType } from '../../enums.js'\nimport { DEF_FONT_SIZE, DEF_SLIDE_MARGIN_IN } from '../../constants-internal.js'\nimport type { PresLayout, TableRowSlide, TableCellProps } from '../../types/index.js'\nimport type {\n\tSlideLayoutInternal,\n\tTableCellInternal,\n\tTableRowInternal,\n\tTableToSlidesPropsInternal,\n} from '../../types/internal.js'\nimport {\n\tautoPageLineHeightEmu,\n\tgetSmartParseNumber,\n\tinch2Emu,\n\tmarginToEmu,\n\tpinnedRowHeightInches,\n\tresolveCellMarginsInches,\n\tresolveSlideMarginsInches,\n\tresolveTableColWidthsEmu,\n\tusableTableWidthEmu,\n} from '../../units-internal.js'\nimport { warn } from '../../diagnostics.js'\nimport { type GridPlacement, tableColCount, walkTableGrid } from './grid.js'\nimport { resolveSpan, withCheckedSpans } from './spans.js'\nimport { EMU_PER_INCH, POINTS_PER_INCH } from '../../units.js'\n\ntype AutoPageCell = TableCellInternal & {\n\t_lineHeight: number\n\t_lines: TableCellInternal[][]\n\toptions: TableCellProps\n\ttext: TableCellInternal[]\n}\n\n/**\n * A cell for the pager's working grid, carrying the source cell's options only when it has some.\n *\n * Every one of these used to be an inline `{ …, options: cell.options }`, which put an `options`\n * key holding `undefined` on a cell whose source had none. Readers cannot see the difference —\n * they all go through `cell.options?.…` — but a `TableCellProps` bag *is* spread in\n * `gen/define/table.ts` (`{ ...colDef, ...headerRow, ...cell.options }`), where the two states\n * are not the same thing. So the model keeps one spelling of absent.\n * @param text - the cell's content, a string or the run list the pager is accumulating\n * @param options - the source cell's options, if it had any\n */\nfunction workingCell(text: string | TableCellInternal[], options: TableCellProps | undefined): TableCellInternal {\n\treturn options === undefined\n\t\t? { _type: SlideObjectType.tablecell, text }\n\t\t: { _type: SlideObjectType.tablecell, text, options }\n}\n\n// ===== Cell text wrapping =====\n\n/**\n * Break cell text into lines based upon table column width (e.g.: Magic Happens Here(tm))\n *\n * `charWeight` arrives as an argument rather than being read off `cell.options` because the\n * caller used to *stamp* it there: the table's weight was written onto the caller's own cell\n * options bag, or `delete`d from it when the table stated none. That mutated caller input --\n * which the chart path has a dedicated guard against -- and it made the option's precedence\n * differ by call site: the row-height probe read the cell's own value while the main loop had\n * just overwritten it with the table's. Both callers now resolve it the same way, and the more\n * specific statement wins, as it does everywhere else in this codebase.\n *\n * `fontSizePt` arrives resolved for the same reason. This read `cell.options.fontSize` or the\n * library default and never the table's `fontSize`, while the row's line height and the emitted\n * `sz` both used the table's. A table of 24pt text was wrapped as if it were 12pt, so the pager\n * counted about half the lines and rows ran off the bottom of each page.\n * @param {TableCellInternal} cell - table cell\n * @param {number} colWidth - table column width (inches)\n * @param {number} fontSizePt - resolved font size: the cell's, else the table's, else the default\n * @param {number} charWeight - resolved `autoPageCharWeight`: the cell's, else the table's, else 0\n * @param {boolean} [verbose] - dump the four wrapping stages; carries `addTable({ verbose })`\n * down, which is the only stage of the pager that flag did not reach\n * @return {TableRowInternal[]} - cell's text objects grouped into lines\n */\nfunction parseTextToLines(\n\tcell: TableCellInternal,\n\tcolWidth: number,\n\tfontSizePt: number,\n\tcharWeight: number,\n\tverbose?: boolean\n): TableCellInternal[][] {\n\t// FYI: CPL = Width / (font-size / font-constant)\n\t// FYI: CHAR:2.3, colWidth:10, fontSize:12 => CPL=138, (actual chars per line in PPT)=145 [14.5 CPI]\n\t// FYI: CHAR:2.3, colWidth:7 , fontSize:12 => CPL= 97, (actual chars per line in PPT)=100 [14.3 CPI]\n\t// FYI: CHAR:2.3, colWidth:9 , fontSize:16 => CPL= 96, (actual chars per line in PPT)=84 [ 9.3 CPI]\n\tconst FOCO = 2.3 + charWeight // Character Constant\n\t// `colWidth` is inches, so the column's width in points is `colWidth * 72`. This used to be\n\t// spelled `(colWidth / EMU_PER_POINT) * EMU_PER_INCH`, which is the same 72 with two EMU\n\t// constants that cancel -- except that the detour through EMU is not exact in binary floating\n\t// point, and `Math.floor` takes the hit. Over the 200,000 widths from 0.001in to 200in the two\n\t// forms disagree on 51, always by one point low: 6.625in is exactly 477 points, and the old\n\t// form floored 476.9999... to 476.\n\tconst CPL = Math.floor(colWidth * POINTS_PER_INCH) / (fontSizePt / FOCO) // Chars-Per-Line\n\n\t// The number the four dumps below exist to explain: every wrap decision in step 4 is\n\t// `strCurrLine.length + word.length > CPL`, so a wrap that looks wrong is almost always\n\t// a CPL that is wrong, and CPL is not recoverable from the lines themselves.\n\tif (verbose) console.log(`[0/4] colWidth=${colWidth}in fontSize=${fontSizePt} FOCO=${FOCO} CPL=${CPL}`)\n\n\tconst parsedLines: TableCellInternal[][] = []\n\tlet inputCells: TableCellInternal[] = []\n\tconst inputLines1: TableCellInternal[][] = []\n\tconst inputLines2: TableCellInternal[][] = []\n\n\t/**\n\t * EX INPUTS: `cell.text`\n\t * - string....: \"Account Name Column\"\n\t * - object....: { text:\"Account Name Column\" }\n\t * - object[]..: [{ text:\"Account Name\", options:{ bold:true } }, { text:\" Column\" }]\n\t * - object[]..: [{ text:\"Account Name\", options:{ breakLine:true } }, { text:\"Input\" }]\n\t */\n\n\t/**\n\t * EX OUTPUTS:\n\t * - string....: [{ text:\"Account Name Column\" }]\n\t * - object....: [{ text:\"Account Name Column\" }]\n\t * - object[]..: [{ text:\"Account Name\", options:{ breakLine:true } }, { text:\"Input\" }]\n\t * - object[]..: [{ text:\"Account Name\", options:{ breakLine:true } }, { text:\"Input\" }]\n\t */\n\n\t// STEP 1: Ensure inputCells is an array of TableCells\n\tif (cell.text && cell.text.toString().trim().length === 0) {\n\t\t// Allow a single space/whitespace as cell text (user-requested feature)\n\t\tinputCells.push({ _type: SlideObjectType.tablecell, text: ' ' })\n\t} else if (typeof cell.text === 'number' || typeof cell.text === 'string') {\n\t\tinputCells.push({ _type: SlideObjectType.tablecell, text: (cell.text || '').toString().trim() })\n\t} else if (Array.isArray(cell.text)) {\n\t\tinputCells = cell.text\n\t}\n\tif (verbose) {\n\t\tconsole.log('[1/4] inputCells')\n\t\tinputCells.forEach((cell, idx) => console.log(`[1/4] [${idx + 1}] cell: ${JSON.stringify(cell)}`))\n\t}\n\n\t// STEP 2: Group table cells into lines based on \"\\n\" or `breakLine` prop\n\t/**\n\t * - EX: `[{ text:\"Input Output\" }, { text:\"Extra\" }]` == 1 line\n\t * - EX: `[{ text:\"Input\" }, { text:\"Output\", options:{ breakLine:true } }]` == 1 line\n\t * - EX: `[{ text:\"Input\\nOutput\" }]` == 2 lines\n\t * - EX: `[{ text:\"Input\", options:{ breakLine:true } }, { text:\"Output\" }]` == 2 lines\n\t */\n\tlet newLine: TableCellInternal[] = []\n\tinputCells.forEach((cell) => {\n\t\tif (typeof cell.text !== 'string') return\n\n\t\tif (cell.text.includes('\\n')) {\n\t\t\t// Each non-last \\n-split part ends with a forced paragraph break; the last\n\t\t\t// part accumulates in newLine so subsequent runs stay on the same paragraph.\n\t\t\tconst parts = cell.text.split('\\n')\n\t\t\tparts.forEach((part, partIdx) => {\n\t\t\t\tconst isLastPart = partIdx === parts.length - 1\n\t\t\t\tif (isLastPart) {\n\t\t\t\t\tnewLine.push(workingCell(part, cell.options))\n\t\t\t\t} else {\n\t\t\t\t\tnewLine.push({\n\t\t\t\t\t\t_type: SlideObjectType.tablecell,\n\t\t\t\t\t\ttext: part,\n\t\t\t\t\t\toptions: { ...cell.options, breakLine: true },\n\t\t\t\t\t})\n\t\t\t\t\tinputLines1.push(newLine)\n\t\t\t\t\tnewLine = []\n\t\t\t\t}\n\t\t\t})\n\t\t} else {\n\t\t\tnewLine.push(workingCell(cell.text.trim(), cell.options))\n\t\t}\n\n\t\tif (cell.options?.breakLine) {\n\t\t\tif (verbose) console.log(`inputCells: new line > ${JSON.stringify(newLine)}`)\n\t\t\tinputLines1.push(newLine)\n\t\t\tnewLine = []\n\t\t}\n\t})\n\t// Flush remaining buffer after all cells are processed\n\tif (newLine.length > 0) {\n\t\tinputLines1.push(newLine)\n\t\tnewLine = []\n\t}\n\tif (verbose) {\n\t\tconsole.log(`[2/4] inputLines1 (${inputLines1.length})`)\n\t\tinputLines1.forEach((line, idx) => console.log(`[2/4] [${idx + 1}] line: ${JSON.stringify(line)}`))\n\t}\n\n\t// STEP 3: Tokenize every text object into words. All runs of one logical paragraph\n\t// are flattened into a single token list so step 4 tracks column position across\n\t// styled-run boundaries (fixes independent-reset bug for rich-text cells).\n\tinputLines1.forEach((line) => {\n\t\tconst lineTokens: TableCellInternal[] = []\n\t\tline.forEach((cell) => {\n\t\t\tconst cellTextStr = String(cell.text) // force convert to string (compiled JS is better with this than a cast)\n\t\t\tconst lineWords = cellTextStr.split(' ')\n\n\t\t\tlineWords.forEach((word, idx) => {\n\t\t\t\tconst cellProps = { ...cell.options }\n\t\t\t\t// IMPORTANT: Handle `breakLine` prop - we cannot apply to each word - only apply to very last word!\n\t\t\t\tif (cellProps?.breakLine) cellProps.breakLine = idx + 1 === lineWords.length\n\t\t\t\tlineTokens.push({\n\t\t\t\t\t_type: SlideObjectType.tablecell,\n\t\t\t\t\ttext: word + (idx + 1 < lineWords.length ? ' ' : ''),\n\t\t\t\t\toptions: cellProps,\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\tinputLines2.push(lineTokens)\n\t})\n\tif (verbose) {\n\t\tconsole.log(`[3/4] inputLines2 (${inputLines2.length})`)\n\t\tinputLines2.forEach((line) => console.log(`[3/4] line: ${JSON.stringify(line)}`))\n\t}\n\n\t// STEP 4: Group cells/words into lines based upon space consumed by word letters\n\tinputLines2.forEach((line) => {\n\t\tlet lineCells: TableCellInternal[] = []\n\t\tlet strCurrLine = ''\n\n\t\tline.forEach((word) => {\n\t\t\tconst wordText = String(word.text || '')\n\t\t\t// A: create new line when horizontal space is exhausted.\n\t\t\t// `lineCells.length > 0` is the same guard the flush below carries, and for the same\n\t\t\t// reason: on the first word `strCurrLine` is `''`, so a word wider than the column on\n\t\t\t// its own fires this branch with nothing buffered and used to push an EMPTY line. The\n\t\t\t// cell's text survived -- the pager concatenates lines back together -- but its height\n\t\t\t// did not, because `_lines.length` is what prices the row. Twelve single-word rows in a\n\t\t\t// 0.4in column paged onto two slides when the word was long and one when it was short.\n\t\t\t// The word still overflows its column: nothing here breaks inside a word, which is the\n\t\t\t// stated behaviour rather than a gap (`docs/contributing/design/tables.md`). A hard\n\t\t\t// break would have to land at the `CPL` estimate, which is not where the renderer wraps,\n\t\t\t// and would then have to keep `_lines.length` and the emitted text in step -- the exact\n\t\t\t// pairing the phantom-line bug above got wrong.\n\t\t\tif (strCurrLine.length + wordText.length > CPL) {\n\t\t\t\tif (lineCells.length > 0) parsedLines.push(lineCells)\n\t\t\t\tlineCells = []\n\t\t\t\tstrCurrLine = ''\n\t\t\t}\n\n\t\t\t// B: add current word to line cells\n\t\t\tlineCells.push(word)\n\n\t\t\t// C: add current word to `strCurrLine` which we use to keep track of line's char length\n\t\t\tstrCurrLine += wordText\n\t\t})\n\n\t\t// Flush buffer: Only create a line when there's text to avoid empty row\n\t\tif (lineCells.length > 0) parsedLines.push(lineCells)\n\t})\n\tif (verbose) {\n\t\tconsole.log(`[4/4] parsedLines (${parsedLines.length})`)\n\t\tparsedLines.forEach((line, idx) => console.log(`[4/4] [Line ${idx + 1}]:\\n${JSON.stringify(line)}`))\n\t\tconsole.log('...............................................\\n\\n')\n\t}\n\n\t// Done:\n\treturn parsedLines\n}\n\n/**\n * The font size in points one auto-paged cell is measured at: the cell's own `fontSize`,\n * else the table's, else the library default. Pass `null` for the cell to ask the same\n * question of the table alone.\n */\nfunction resolveCellFontSize(\n\tcellOpts: TableCellProps | undefined | null,\n\ttableOpts: TableToSlidesPropsInternal\n): number {\n\tconst size = cellOpts?.fontSize ?? tableOpts.fontSize\n\treturn typeof size === 'number' ? size : DEF_FONT_SIZE\n}\n\n/**\n * The `autoPageCharWeight` a cell wraps at: its own, else the table's, else none.\n *\n * The sibling of {@link resolveCellFontSize}, and named for the same reason -- the two call\n * sites of {@link parseTextToLines} had resolved this one differently. The row-height probe\n * read the cell's own value; the main loop stamped the table's over it first, and `delete`d the\n * cell's when the table stated none, so a weight set on a cell alone was accepted and dropped.\n * The precedence here is the one every other paired option in this codebase uses.\n * @param cellOpts - the cell's own options, if any\n * @param tableOpts - the table's options\n */\nfunction resolveCellCharWeight(\n\tcellOpts: TableCellProps | undefined | null,\n\ttableOpts: TableToSlidesPropsInternal\n): number {\n\tconst weight = cellOpts?.autoPageCharWeight ?? tableOpts.autoPageCharWeight\n\treturn typeof weight === 'number' ? weight : 0\n}\n\n// ===== Auto-page engine =====\n// The in-memory core (fully Node-testable). Internally signposted by the\n// `// STEP 1..7` markers below: margins → column count → widths → the main\n// row-iteration/overflow loop (STEP 6) → final-slide flush (STEP 7).\n\n/**\n * The largest top and bottom cell margin in one row, in EMU.\n *\n * A cell's own `margin` wins over the table's when it states one, and a stated `0` counts as\n * stating one. Both sites that read this gated on truthiness and on `Array.isArray`, so a cell\n * asking for `margin: [0, …]` fell through to the table's margin instead of taking its own, and\n * a scalar `margin: 0.2` was not seen at all -- while the emitter broadcasts a scalar to four\n * sides. `resolveCellMarginsInches` is the resolver the emitter itself reads.\n * @param row - the row's cells\n * @param tableProps - the table's options, for the fallback margin\n * @returns the row's top and bottom margin allowance in EMU\n */\nfunction rowMarginsEmu(\n\trow: TableRowInternal,\n\ttableProps: TableToSlidesPropsInternal\n): { topEmu: number; btmEmu: number } {\n\tlet topEmu = 0\n\tlet btmEmu = 0\n\trow.forEach((cell) => {\n\t\tconst stated = cell?.options?.margin ?? tableProps.margin\n\t\tif (stated === undefined || stated === null) return\n\t\tconst [top, , btm] = resolveCellMarginsInches(stated)\n\t\tif (marginToEmu(top) > topEmu) topEmu = marginToEmu(top)\n\t\tif (marginToEmu(btm) > btmEmu) btmEmu = marginToEmu(btm)\n\t})\n\treturn { topEmu, btmEmu }\n}\n\n/** One row, measured: each cell's wrapped lines and what the row costs a page. */\ninterface MeasuredRow {\n\t/** One working cell per source cell, in row order, carrying its lines and its line height. */\n\tcells: AutoPageCell[]\n\t/** The row's tallest top margin plus its tallest bottom margin, in EMU. */\n\tmarginsEmu: number\n\t/** The tallest line height among the cells that do not span rows, in EMU. */\n\tlineHeightEmu: number\n\t/** The whole row: its margins plus its tallest cell's line count at `lineHeightEmu`. */\n\theightEmu: number\n}\n\n/**\n * Takes an array of table rows and breaks into an array of slides, which contain the calculated amount of table rows that fit on that slide\n * @param {TableCellInternal[][]} tableRows - table rows\n * @param {TableToSlidesPropsInternal} tableProps - table2slides properties\n * @param {PresLayout} presLayout - presentation layout\n * @param {SlideLayoutInternal} masterSlide - master slide\n * @return {TableRowSlide[]} array of table rows\n */\nexport function getSlidesForTableRows(\n\trows: TableCellInternal[][] = [],\n\ttableProps: TableToSlidesPropsInternal = {},\n\tpresLayout: PresLayout,\n\tmasterSlide?: SlideLayoutInternal | null\n): TableRowSlide[] {\n\t// `MAX_TABLE_SPAN` guards the two allocations a span decides, and its own module says it\n\t// covers both paths -- but `tableToSlides` reaches this function directly, without the\n\t// definer that applies the check, and its own `gridSpan` has no ceiling. So a\n\t// `colspan=\"4000000000\"` in a source table reached the column walk and the per-column depth\n\t// array, which is the allocation that takes the host process down with no exception to catch.\n\t// Checking here rather than at the call site keeps the one entry point guarded whoever calls\n\t// it; a grid whose spans are all fine is passed through by identity.\n\tconst tableRows = withCheckedSpans(rows)\n\tlet arrInchMargins = DEF_SLIDE_MARGIN_IN\n\tlet emuSlideTabW: number\n\tlet colWidthsIn: number[] = []\n\tlet emuSlideTabH = EMU_PER_INCH * 1\n\tlet emuTabCurrH = 0\n\tlet numCols = 0\n\tlet warnedNoTabH = false\n\tconst tableRowSlides: TableRowSlide[] = []\n\tconst tablePropX = getSmartParseNumber(tableProps.x, 'X', presLayout)\n\tconst tablePropY = getSmartParseNumber(tableProps.y, 'Y', presLayout)\n\tconst tablePropW = getSmartParseNumber(tableProps.w, 'X', presLayout)\n\tconst tablePropH = getSmartParseNumber(tableProps.h, 'Y', presLayout)\n\tlet tableCalcW: number = tablePropW\n\n\t/**\n\t * Where this page's table starts, in EMU from the top of the slide.\n\t *\n\t * There used to be three rules for two cases. The first page and \"every page after it\"\n\t * were handled together, then a block gated on `tableRowSlides.length > 1` overrode the\n\t * result -- so despite its own comment it began on the THIRD page, and its first arm\n\t * recomputed exactly what had just been computed. Page 2 therefore got a different usable\n\t * height from pages 3 and up: with `y` above the top margin and an explicit `h`, a\n\t * 60-row table paged 9 / 7 / 9 / 9 / 9 / 9 / 8, which is the same \"pages disagree about\n\t * how many identical rows fit\" symptom the margin-reset note in `calcSlideTabH` records.\n\t *\n\t * It is a function rather than two expressions because the too-small-height fallback in\n\t * `calcSlideTabH` had spelled the continuation arm its own way and drifted the same way\n\t * twice over: `autoPageSlideStartY || margin` discarded an explicit `0` that the main rule\n\t * honoured, and the `Math.min` clause below was missing outright. Two readings of one rule\n\t * is what the paragraph above is about; there is now one.\n\t */\n\tfunction startYEmu(): number {\n\t\t// A stated `y: 0` is the top edge of the slide, not a missing `y`: tested for truthiness, it\n\t\t// was budgeted from the top margin while `addTable` placed the page at 0.\n\t\tconst statedY = tableProps.y !== undefined && tableProps.y !== null\n\t\tif (tableRowSlides.length === 0) return statedY ? tablePropY : inch2Emu(arrInchMargins[0])\n\t\tif (typeof tableProps.autoPageSlideStartY === 'number') return inch2Emu(tableProps.autoPageSlideStartY)\n\t\t// RULE: after the first page the table starts at the top margin rather than at its\n\t\t// own `y` -- unless `y` is ABOVE the margin, in which case paging must not push the\n\t\t// table down and lose the space. Whichever is higher on the slide wins.\n\t\treturn inch2Emu(statedY ? Math.min(tablePropY / EMU_PER_INCH, arrInchMargins[0]) : arrInchMargins[0])\n\t}\n\n\tfunction calcSlideTabH(): void {\n\t\tconst emuStartY = startYEmu()\n\t\temuSlideTabH = (tablePropH || presLayout.height) - emuStartY - inch2Emu(arrInchMargins[2])\n\t\t// EXPLICIT-H FIX: an explicit `h` is the table's height (an extent), not a bottom\n\t\t// coordinate, so -- unlike `presLayout.height` -- the start-Y must not be subtracted from\n\t\t// it. Otherwise a table that begins mid-slide gets a tiny page while the pages that do\n\t\t// clamp to `h` render normally. Applied on every page: the floor used to reach the first\n\t\t// page and pages three and up, leaving page two as the one that could shrink below `h`.\n\t\tif (tablePropH && emuSlideTabH < tablePropH) emuSlideTabH = tablePropH\n\n\t\t// GUARD: a non-positive height, or one too small to fit even a single line of the base font,\n\t\t// means no row ever fits. That previously emitted degenerate empty overflow pages (rows:[]),\n\t\t// which made the recursive addTable throw \"Array expected\", or — with `h` smaller than one\n\t\t// line — placed one row per slide forever. Ignore the unusable height, fall back to the full\n\t\t// slide area between margins, and warn once rather than emit a broken table.\n\t\tconst emuBaseLineH = autoPageLineHeightEmu(\n\t\t\tresolveCellFontSize(null, tableProps),\n\t\t\ttableProps.autoPageLineWeight || 0\n\t\t)\n\t\tif (emuSlideTabH < emuBaseLineH) {\n\t\t\tconst fallbackH = presLayout.height - startYEmu() - inch2Emu(arrInchMargins[2])\n\t\t\tif (!warnedNoTabH) {\n\t\t\t\twarn(\n\t\t\t\t\t'table/autopage-height-too-small',\n\t\t\t\t\t'addTable/autoPage: the table height (`h`) leaves no room to paginate; ignoring it and using the slide height. Increase `h` or decrease `y`.'\n\t\t\t\t)\n\t\t\t\twarnedNoTabH = true\n\t\t\t}\n\t\t\temuSlideTabH = fallbackH > 0 ? fallbackH : presLayout.height\n\t\t}\n\t}\n\n\tif (tableProps.verbose) {\n\t\tconsole.log('[[VERBOSE MODE]]')\n\t\tconsole.log('|-- TABLE PROPS --------------------------------------------------------|')\n\t\tconsole.log(`| presLayout.width ................................ = ${(presLayout.width / EMU_PER_INCH).toFixed(1)}`)\n\t\tconsole.log(\n\t\t\t`| presLayout.height ............................... = ${(presLayout.height / EMU_PER_INCH).toFixed(1)}`\n\t\t)\n\t\tconsole.log(\n\t\t\t`| tableProps.x .................................... = ${typeof tableProps.x === 'number' ? (tableProps.x / EMU_PER_INCH).toFixed(1) : tableProps.x}`\n\t\t)\n\t\tconsole.log(\n\t\t\t`| tableProps.y .................................... = ${typeof tableProps.y === 'number' ? (tableProps.y / EMU_PER_INCH).toFixed(1) : tableProps.y}`\n\t\t)\n\t\tconsole.log(\n\t\t\t`| tableProps.w .................................... = ${typeof tableProps.w === 'number' ? (tableProps.w / EMU_PER_INCH).toFixed(1) : tableProps.w}`\n\t\t)\n\t\tconsole.log(\n\t\t\t`| tableProps.h .................................... = ${typeof tableProps.h === 'number' ? (tableProps.h / EMU_PER_INCH).toFixed(1) : tableProps.h}`\n\t\t)\n\t\tconsole.log(\n\t\t\t`| tableProps.slideMargin .......................... = ${tableProps.slideMargin ? String(tableProps.slideMargin) : ''}`\n\t\t)\n\t\tconsole.log(`| tableProps.margin ............................... = ${String(tableProps.margin)}`)\n\t\tconsole.log(`| tableProps.colW ................................. = ${String(tableProps.colW)}`)\n\t\tconsole.log(`| tableProps.autoPageSlideStartY .................. = ${tableProps.autoPageSlideStartY}`)\n\t\tconsole.log(`| tableProps.autoPageCharWeight ................... = ${tableProps.autoPageCharWeight}`)\n\t\tconsole.log('|-- CALCULATIONS -------------------------------------------------------|')\n\t\tconsole.log(`| tablePropX ...................................... = ${tablePropX / EMU_PER_INCH}`)\n\t\tconsole.log(`| tablePropY ...................................... = ${tablePropY / EMU_PER_INCH}`)\n\t\tconsole.log(`| tablePropW ...................................... = ${tablePropW / EMU_PER_INCH}`)\n\t\tconsole.log(`| tablePropH ...................................... = ${tablePropH / EMU_PER_INCH}`)\n\t\tconsole.log(`| tableCalcW ...................................... = ${tableCalcW / EMU_PER_INCH}`)\n\t}\n\n\t// STEP 1: Calculate margins\n\t{\n\t\t// The caller's value goes through unaltered, including an absent one: `resolveSlideMarginsInches`\n\t\t// owns all three tiers, and defaulting to `DEF_SLIDE_MARGIN_IN[0]` here handed it a stated\n\t\t// caller margin that no caller had stated, which suppressed the master's own `margin`.\n\t\tarrInchMargins = resolveSlideMarginsInches(masterSlide?._margin, tableProps.slideMargin)\n\n\t\tif (tableProps.verbose)\n\t\t\tconsole.log(`| arrInchMargins .................................. = [${arrInchMargins.join(', ')}]`)\n\t}\n\n\t// STEP 2: Calculate number of columns\n\t{\n\t\tnumCols = tableColCount(tableRows)\n\t\tif (tableProps.verbose) console.log(`| numCols ......................................... = ${numCols}`)\n\t}\n\n\t// Where every cell actually lands. The pager asks the grid two questions -- which columns a\n\t// cell spans, so its text is wrapped against the right width, and whether a rowspan opened\n\t// on an earlier row is still open here, so a page break does not split a merged group -- and\n\t// it used to answer both with its own occupancy array, once forwards through the row and\n\t// once again after it. `walkTableGrid` is the traversal the measured-fit pass and the\n\t// emitter's own grid build follow, so the widths this pager wraps against are the widths the\n\t// cells are finally given.\n\tconst placements: GridPlacement[][] = tableRows.map(() => [])\n\t/**\n\t * How many of each row's grid columns are held by a rowspan opened ABOVE it, accumulated as\n\t * the placements arrive rather than searched for afterwards.\n\t *\n\t * The question used to be answered by scanning every placement in the table, once per row,\n\t * which is quadratic in a table of any width. Two columns of short text and no spans at all:\n\t * 1000 rows took 55ms, 2000 took 115ms, 4000 took 333ms and 8000 took 1036ms. The same four\n\t * now take 24ms, 19ms, 40ms and 83ms -- so it was pure overhead, paid in full by the long\n\t * report table that is the whole reason this code exists.\n\t *\n\t * Filling forward instead is bounded by the placement's own `rowSpan`, so the total work is\n\t * the sum of all rowspans. That is safe to expand without a further check because\n\t * `walkTableGrid` has already clamped each `rowSpan` to `rows.length - r`: a span cannot\n\t * reach past the last row however large the caller wrote it, so the sum is bounded by the\n\t * grid rather than by anything the caller states.\n\t *\n\t * The fill starts at `row + 1`. A `rowSpan` of 1 must contribute nothing -- starting at\n\t * `row` would have every ordinary row counting itself as covered, and the\n\t * `coveredFromAbove(iRow) === numCols` reader below would then fire on all of them.\n\t */\n\tconst coveredCols: number[] = Array.from({ length: tableRows.length }, () => 0)\n\tfor (const placement of walkTableGrid(tableRows, numCols)) {\n\t\tplacements[placement.row]?.push(placement)\n\t\tconst last = Math.min(placement.row + placement.rowSpan, coveredCols.length)\n\t\tfor (let r = placement.row + 1; r < last; r++) coveredCols[r] = (coveredCols[r] ?? 0) + placement.colSpan\n\t}\n\t/** How many of `iRow`'s grid columns are held by a rowspan opened above it. */\n\tconst coveredFromAbove = (iRow: number): number => coveredCols[iRow] ?? 0\n\t/** Whether a rowspan opened above `iRow` is still covering it. */\n\tconst spannedFromAbove = (iRow: number): boolean => coveredFromAbove(iRow) > 0\n\n\t// STEP 3: Calculate width using tableProps.colW if possible. Only an array reaches here:\n\t// `addTableDefinition` turns a single `colW` into `w` before paging, and `tableToSlides` always\n\t// passes one width per column.\n\tif (!tablePropW && Array.isArray(tableProps.colW)) {\n\t\t// The `0` seed is what `colW: []` needs: an empty array is truthy, so it reached an\n\t\t// unseeded `reduce` and threw a raw `TypeError` — a failure this library did not raise as\n\t\t// a `TsPptxError`. Seeded, it totals `0`, which falls through to `usableTableWidthEmu`\n\t\t// exactly where `colW: undefined` lands. That is the reading intended: an empty array is a\n\t\t// caller stating no columns, not a table of width zero.\n\t\ttableCalcW = tableProps.colW.reduce((p, n) => p + n, 0) * EMU_PER_INCH\n\t\tif (tableProps.verbose)\n\t\t\tconsole.log(`| tableCalcW ...................................... = ${tableCalcW / EMU_PER_INCH}`)\n\t}\n\n\t// STEP 4: Calculate usable width now that total usable space is known (`emuSlideTabW`)\n\t{\n\t\temuSlideTabW = tableCalcW || usableTableWidthEmu(presLayout, tablePropX, arrInchMargins)\n\t\tif (tableProps.verbose)\n\t\t\tconsole.log(`| emuSlideTabW .................................... = ${(emuSlideTabW / EMU_PER_INCH).toFixed(1)}`)\n\t}\n\n\t// STEP 5: Resolve the column grid. `resolveTableColWidthsEmu` is the same resolver the\n\t// table emitter and the measured-fit pass read, so the widths this pager wraps text\n\t// against are the widths the emitted `<a:gridCol>`s carry. It also normalizes the array\n\t// to `numCols`: a short `colW` used to leave the trailing columns measuring against a\n\t// zero width. Inches, because that is the unit each paged table's `colW` takes; the grid\n\t// rides out on every `TableRowSlide` rather than being written back onto the caller's bag.\n\tcolWidthsIn = resolveTableColWidthsEmu(tableProps.colW, emuSlideTabW, numCols).map(\n\t\t(emu: number) => emu / EMU_PER_INCH\n\t)\n\n\t// Resolve a row's explicit height (inches) from the original `rowH` *array*, keyed by original\n\t// row index. A single-number `rowH` is left to propagate via table options (it applies uniformly,\n\t// so it needs no per-row remapping); only the array form is index-sensitive after pagination.\n\t// Whether a slot pins its row is `pinnedRowHeightInches`, the same rule the writer applies to\n\t// the array this builds — a `0` accepted here and rejected there is how the two readings drift.\n\tconst resolveRowH = (origRowIdx: number): number | undefined =>\n\t\tArray.isArray(tableProps.rowH) ? (pinnedRowHeightInches(tableProps.rowH[origRowIdx]) ?? undefined) : undefined\n\n\t/**\n\t * Wrap each of row `iRow`'s cells against the grid columns it covers, and price the row.\n\t *\n\t * The one row measure. The repeated header rows used to be priced by a second copy of it with its\n\t * own column cursor, which ignored the columns a rowspan holds -- the fault `walkTableGrid`\n\t * fixed for the main loop -- and skipped the line-height exemption for a cell that spans rows,\n\t * so a header row inside a rowspan was measured against the wrong columns.\n\t * @param iRow - the row's index in `tableRows`\n\t * @param verbose - dump the wrapping stages, as `addTable({ verbose })` asks\n\t */\n\tfunction measureRow(iRow: number, verbose?: boolean): MeasuredRow {\n\t\tconst row = tableRows[iRow] ?? []\n\t\tconst { topEmu, btmEmu } = rowMarginsEmu(row, tableProps)\n\t\t// Cells are keyed to grid columns, not to their position in the row: a colspan earlier in\n\t\t// the row shifts every later cell, and a rowspan opened above skips columns entirely.\n\t\t// Measuring a cell against `colW[iCell]` wrapped its text to another column's width.\n\t\tconst rowPlacements = placements[iRow] ?? []\n\t\tconst cells = row.map((cell, iCell): AutoPageCell => {\n\t\t\t// A row longer than the grid runs off the end of it: those cells are placed nowhere\n\t\t\t// and measure against no column, which is what the cursor did by clamping.\n\t\t\tconst placed = rowPlacements[iCell]\n\t\t\tconst colStart = placed ? placed.col : numCols\n\t\t\tconst cellColspan = placed ? placed.colSpan : resolveSpan(cell.options?.colspan, 'colspan')\n\t\t\t// A spanning cell is as wide as the columns it covers; the seed keeps a row longer than\n\t\t\t// the grid from throwing on an empty slice.\n\t\t\tconst totalColW = colWidthsIn.slice(colStart, colStart + cellColspan).reduce((prev, curr) => prev + curr, 0)\n\t\t\tconst fontSizePt = resolveCellFontSize(cell.options, tableProps)\n\t\t\treturn {\n\t\t\t\t_type: SlideObjectType.tablecell,\n\t\t\t\t_lines: parseTextToLines(cell, totalColW, fontSizePt, resolveCellCharWeight(cell.options, tableProps), verbose),\n\t\t\t\t// A cell that spans rows adds no line height to this one (or a page could break where\n\t\t\t\t// nothing needed it to). The grid's clamped span decides, not the option: `rowspan: 1`\n\t\t\t\t// is a valid \"no span\" that `withCheckedSpans` passes through, and testing the option\n\t\t\t\t// priced every such row at its margins alone.\n\t\t\t\t_lineHeight:\n\t\t\t\t\tplaced && placed.rowSpan > 1 ? 0 : autoPageLineHeightEmu(fontSizePt, tableProps.autoPageLineWeight || 0),\n\t\t\t\ttext: [],\n\t\t\t\toptions: cell.options || {},\n\t\t\t}\n\t\t})\n\t\tconst lineHeightEmu = cells.reduce((max, cell) => Math.max(max, cell._lineHeight), 0)\n\t\tconst maxLines = cells.reduce((max, cell) => Math.max(max, cell._lines.length), 0)\n\t\tconst marginsEmu = topEmu + btmEmu\n\t\treturn { cells, marginsEmu, lineHeightEmu, heightEmu: marginsEmu + maxLines * lineHeightEmu }\n\t}\n\n\t// What the repeated header rows cost a continuation page. Computed once: the rows and the\n\t// grid are the same on every page, so re-measuring per page would only be slower. Both callers\n\t// pass the header rows as the leading rows of `tableRows`, so each is measured at its own index,\n\t// against the grid placements the body rows use.\n\tconst repeatHeaderRows =\n\t\ttableProps.autoPageRepeatHeader && tableProps._arrObjTabHeadRows ? tableProps._arrObjTabHeadRows : []\n\tconst repeatHeaderHeightsEmu = repeatHeaderRows.map((_row, headIdx) => measureRow(headIdx).heightEmu)\n\n\t/**\n\t * The last row of the rowspan group that row `iRow` opens: the furthest row any of its spans\n\t * reaches, extended by every span a covered row opens in turn. `walkTableGrid` has clamped each\n\t * span to the table, so the walk ends inside it.\n\t * @param iRow - the row that opens the group\n\t */\n\tfunction rowSpanGroupEnd(iRow: number): number {\n\t\tlet end = iRow\n\t\tfor (let r = iRow; r <= end; r++) {\n\t\t\tfor (const placement of placements[r] ?? []) end = Math.max(end, r + placement.rowSpan - 1)\n\t\t}\n\t\treturn end\n\t}\n\n\t// STEP 6: **MAIN** Iterate over rows, add table content, create new slides as rows overflow\n\tlet newTableRowSlide: TableRowSlide = {\n\t\trows: [] as TableRowInternal[],\n\t\trowH: [] as Array<number | undefined>,\n\t\tcolW: colWidthsIn,\n\t\ty: startYEmu() / EMU_PER_INCH,\n\t}\n\t/** Rows placed on the working page other than its repeated header rows. */\n\tlet bodyRowsOnPage = 0\n\tlet warnedTallRowSpan = false\n\n\t/**\n\t * Close the working page and open the next: push the page when it has rows, re-derive the page\n\t * height, charge `chargedEmu` to the new page, then repeat the header rows onto it.\n\t * @param chargedEmu - what the new page already owes before its header rows: the margins of a\n\t * row that is starting over here, or nothing\n\t */\n\tfunction startNewPage(chargedEmu: number): void {\n\t\t// Never push an empty page: a row that does not fit yet has no content here, and an empty\n\t\t// `rows` slide crashes the recursive addTable.\n\t\tif (newTableRowSlide.rows.length > 0) tableRowSlides.push(newTableRowSlide)\n\t\t// Decided after the push: the page just closed is what makes this one a continuation.\n\t\tnewTableRowSlide = {\n\t\t\trows: [],\n\t\t\trowH: [] as Array<number | undefined>,\n\t\t\tcolW: colWidthsIn,\n\t\t\ty: startYEmu() / EMU_PER_INCH,\n\t\t}\n\t\tbodyRowsOnPage = 0\n\t\t// The page height depends on which page this is, so it is re-derived for the new one.\n\t\tcalcSlideTabH()\n\t\t// ORDER IS LOAD-BEARING. The charge used to be added and then wiped by the reset, so the first\n\t\t// row of every continuation slide was the only row in the table that paid no margin. That let\n\t\t// a continuation slide accept one row more than it had room for, and the extra row hung off\n\t\t// the bottom of the slide (upstream gitbrent/PptxGenJS#1200). The symptom is easy to miss\n\t\t// because the pager is *self*-consistent per slide: it only shows up as the first slide and\n\t\t// the continuation slides disagreeing about how many identical rows fit the identical space,\n\t\t// which is what test/regression/table/table-autopage-continuation-budget.test.js pins.\n\t\temuTabCurrH = chargedEmu\n\t\tif (tableProps.verbose)\n\t\t\tconsole.log(\n\t\t\t\t`| SLIDE [${tableRowSlides.length}]: emuSlideTabH ...... = ${(emuSlideTabH / EMU_PER_INCH).toFixed(1)} `\n\t\t\t)\n\t\trepeatHeaderRows.forEach((headRow, headIdx) => {\n\t\t\tnewTableRowSlide.rows.push([...headRow])\n\t\t\t// Repeated header rows are the original leading rows, so carry their configured height.\n\t\t\tnewTableRowSlide.rowH?.push(resolveRowH(headIdx))\n\t\t\temuTabCurrH += repeatHeaderHeightsEmu[headIdx] ?? 0\n\t\t})\n\t}\n\n\ttableRows.forEach((row, iRow) => {\n\t\t// A: Row variables — detect active rowspan at the start of this row so we can\n\t\t// suppress page breaks that would split a rowspan group across slides.\n\t\tconst hasActiveRowSpan = spannedFromAbove(iRow)\n\t\t// B: Create new row in data model\n\t\tlet currTableRow: TableRowInternal = []\n\t\trow.forEach((cell) => currTableRow.push(workingCell([], cell.options)))\n\n\t\t// C: Calc usable vertical space/table height. Set default value first, adjust below when necessary.\n\t\tcalcSlideTabH()\n\t\tif (tableProps.verbose && iRow === 0)\n\t\t\tconsole.log(\n\t\t\t\t`| SLIDE [${tableRowSlides.length}]: emuSlideTabH ...... = ${(emuSlideTabH / EMU_PER_INCH).toFixed(1)} `\n\t\t\t)\n\n\t\t// C-2: A row that opens a rowspan is kept with the rows the span covers. Breaking a page\n\t\t// partway through it left the partial row as the page's last row still carrying `rowSpan`,\n\t\t// so the emitter wrote a `rowSpan=\"2\"` with no row under it -- the state PowerPoint reports\n\t\t// as corrupt. The whole group is priced before any of its lines is taken: when it does not\n\t\t// fit under rows already on the page, the page breaks before it, and a group taller than a\n\t\t// page is emitted whole on a page of its own.\n\t\tconst startsRowSpan = !hasActiveRowSpan && (placements[iRow] ?? []).some((placement) => placement.rowSpan > 1)\n\t\tif (startsRowSpan) {\n\t\t\tlet groupHeightEmu = 0\n\t\t\tfor (let r = iRow; r <= rowSpanGroupEnd(iRow); r++) groupHeightEmu += measureRow(r).heightEmu\n\t\t\tif (bodyRowsOnPage > 0 && emuTabCurrH + groupHeightEmu > emuSlideTabH) startNewPage(0)\n\t\t\tif (emuTabCurrH + groupHeightEmu > emuSlideTabH && !warnedTallRowSpan) {\n\t\t\t\twarn(\n\t\t\t\t\t'table/autopage-rowspan-too-tall',\n\t\t\t\t\t'addTable/autoPage: a group of rows joined by a rowspan is taller than a page; it is kept on one page and runs past its bottom. Reduce the span or the text in it.'\n\t\t\t\t)\n\t\t\t\twarnedTallRowSpan = true\n\t\t\t}\n\t\t}\n\t\tconst keepRowTogether = hasActiveRowSpan || startsRowSpan\n\n\t\t// D: --==[[ BUILD DATA SET ]]==-- (split each cell's text into lines, set its line height)\n\t\tconst {\n\t\t\tcells: rowCellLines,\n\t\t\tmarginsEmu: rowMarginsTotalEmu,\n\t\t\tlineHeightEmu: emuLineMaxH,\n\t\t} = measureRow(iRow, tableProps.verbose)\n\t\temuTabCurrH += rowMarginsTotalEmu // Start row height with margins\n\n\t\t/** E: --==[[ PAGE DATA SET ]]==--\n\t\t * Add text one-line-a-time to this row's cells until: lines are exhausted OR table height limit is hit\n\t\t *\n\t\t * Design:\n\t\t * - Building cells L-to-R/loop style wont work as one could be 100 lines and another 1 line\n\t\t * - Therefore, build the whole row, one-line-at-a-time, across each table columns\n\t\t * - Then, when the vertical size limit is hit is by any of the cells, make a new slide and continue adding any remaining lines\n\t\t *\n\t\t * Implementation:\n\t\t * - `rowCellLines` is an array of cells, one for each column in the table, with each cell containing an array of lines\n\t\t *\n\t\t * Sample Data:\n\t\t * - `rowCellLines` ..: [ TableCellInternal, TableCellInternal, TableCellInternal ]\n\t\t * - `TableCellInternal` .....: { _type: 'tablecell', _lines: TableCellInternal[], _lineHeight: 10 }\n\t\t * - `_lines` ........: [ {_type: 'tablecell', text: 'cell-1,line-1', options: {…}}, {_type: 'tablecell', text: 'cell-1,line-2', options: {…}} }\n\t\t * - `_lines` is TableCellInternal[] (the 1-N words in the line)\n\t\t * {\n\t\t * _lines: [{ text:'cell-1,line-1' }, { text:'cell-1,line-2' }], // TOTAL-CELL-HEIGHT = 2\n\t\t * _lines: [{ text:'cell-2,line-1' }, { text:'cell-2,line-2' }], // TOTAL-CELL-HEIGHT = 2\n\t\t * _lines: [{ text:'cell-3,line-1' }, { text:'cell-3,line-2' }, { text:'cell-3,line-3' }, { text:'cell-3,line-4' }], // TOTAL-CELL-HEIGHT = 4\n\t\t * }\n\t\t *\n\t\t * Example: 2 rows, with the firstrow overflowing onto a new slide\n\t\t * SLIDE 1:\n\t\t * |--------|--------|--------|--------|\n\t\t * | line-1 | line-1 | line-1 | line-1 |\n\t\t * | | | line-2 | |\n\t\t * | | | line-3 | |\n\t\t * |--------|--------|--------|--------|\n\t\t *\n\t\t * SLIDE 2:\n\t\t * |--------|--------|--------|--------|\n\t\t * | | | line-4 | |\n\t\t * |--------|--------|--------|--------|\n\t\t * | line-1 | line-1 | line-1 | line-1 |\n\t\t * |--------|--------|--------|--------|\n\t\t */\n\t\tif (tableProps.verbose) console.log(`\\n| SLIDE [${tableRowSlides.length}]: ROW [${iRow}]: START...`)\n\t\tlet currCellIdx = 0\n\t\tlet isDone = false\n\t\twhile (!isDone) {\n\t\t\tconst srcCell = rowCellLines[currCellIdx]\n\t\t\tif (!srcCell) break\n\t\t\tlet tgtCell = currTableRow[currCellIdx] // NOTE: may be redefined below (a new row may be created, thus changing this value)\n\n\t\t\t// 2: create a new slide if there is insufficient room for the current row,\n\t\t\t// but never break inside a rowspan group — keep spanned rows together.\n\t\t\tif (emuTabCurrH + emuLineMaxH > emuSlideTabH && !keepRowTogether) {\n\t\t\t\tif (tableProps.verbose) {\n\t\t\t\t\tconsole.log('\\n|-----------------------------------------------------------------------|')\n\t\t\t\t\t// prettier-ignore\n\t\t\t\t\tconsole.log(`|-- NEW SLIDE CREATED (currTabH+currLineH > maxH) => ${(emuTabCurrH / EMU_PER_INCH).toFixed(2)} + ${(srcCell._lineHeight / EMU_PER_INCH).toFixed(2)} > ${emuSlideTabH / EMU_PER_INCH}`)\n\t\t\t\t\tconsole.log('|-----------------------------------------------------------------------|\\n\\n')\n\t\t\t\t}\n\n\t\t\t\t// A: add current row slide or it will be lost (only if it has rows and text)\n\t\t\t\tif (\n\t\t\t\t\tcurrTableRow.length > 0 &&\n\t\t\t\t\tcurrTableRow.map((cell) => (Array.isArray(cell.text) ? cell.text.length : 0)).reduce((p, n) => p + n) > 0\n\t\t\t\t) {\n\t\t\t\t\tnewTableRowSlide.rows.push(currTableRow)\n\t\t\t\t\tnewTableRowSlide.rowH?.push(resolveRowH(iRow))\n\t\t\t\t}\n\n\t\t\t\t// B: open the next page. The row is starting over there, so it owes its cell margins\n\t\t\t\t// again.\n\t\t\t\tstartNewPage(rowMarginsTotalEmu)\n\n\t\t\t\t// C: reset working/curr row\n\t\t\t\tcurrTableRow = []\n\t\t\t\trow.forEach((cell) => currTableRow.push(workingCell([], cell.options)))\n\n\t\t\t\ttgtCell = currTableRow[currCellIdx]\n\t\t\t}\n\n\t\t\t// 3: set array of words that comprise this line\n\t\t\tconst currLine = srcCell._lines.shift()\n\n\t\t\t// 4: create new line by adding all words from curr line (or add empty if there are no words to avoid \"needs repair\" issue triggered when cells have null content)\n\t\t\tif (tgtCell && Array.isArray(tgtCell.text)) {\n\t\t\t\tif (currLine) tgtCell.text = tgtCell.text.concat(currLine)\n\t\t\t\telse if (tgtCell.text.length === 0)\n\t\t\t\t\ttgtCell.text = tgtCell.text.concat({ _type: SlideObjectType.tablecell, text: '' })\n\t\t\t\t// IMPORTANT: ^^^ add empty if there are no words to avoid \"needs repair\" issue triggered when cells have null content\n\t\t\t}\n\n\t\t\t// 5: increase table height by the curr line height (if we're on the last column)\n\t\t\tif (currCellIdx === rowCellLines.length - 1) emuTabCurrH += emuLineMaxH\n\n\t\t\t// 6: advance column/cell index (or circle back to first one to continue adding lines)\n\t\t\tcurrCellIdx = currCellIdx < rowCellLines.length - 1 ? currCellIdx + 1 : 0\n\n\t\t\t// 7: WIP: done?\n\t\t\tconst brent = rowCellLines.map((cell) => cell._lines.length).reduce((prev, next) => prev + next)\n\t\t\tif (brent === 0) isDone = true\n\t\t}\n\n\t\t// F: Flush/capture row buffer before it resets at the top of this loop.\n\t\t//\n\t\t// A row that states no cells is kept only when every one of its grid columns is held by a\n\t\t// rowspan from above. That is a real row -- a source row states only the cells it *starts*,\n\t\t// so one entirely covered from above states none at all, which is what `<tr></tr>` between\n\t\t// two spanned rows means -- and the emitter fills it with `vMerge` continuations. Dropping\n\t\t// it moved every later row up one while the emitter went on synthesizing that grid row's\n\t\t// continuations, so they landed in the next row's `<a:tr>`: a 2-column table came out with\n\t\t// a 4-cell row.\n\t\t//\n\t\t// An empty row NOT covered that way is a different thing and is still dropped: nothing\n\t\t// would fill it, and a `<a:tr>` carrying fewer `<a:tc>` than the grid has columns is the\n\t\t// malformation PowerPoint offers to repair.\n\t\tif (currTableRow.length > 0 || (row.length === 0 && coveredFromAbove(iRow) === numCols)) {\n\t\t\tnewTableRowSlide.rows.push(currTableRow)\n\t\t\tnewTableRowSlide.rowH?.push(resolveRowH(iRow))\n\t\t\tbodyRowsOnPage++\n\t\t}\n\n\t\tif (tableProps.verbose) {\n\t\t\tconsole.log(\n\t\t\t\t`- SLIDE [${tableRowSlides.length}]: ROW [${iRow}]: ...COMPLETE ...... emuTabCurrH = ${(emuTabCurrH / EMU_PER_INCH).toFixed(2)} ( emuSlideTabH = ${(\n\t\t\t\t\temuSlideTabH / EMU_PER_INCH\n\t\t\t\t).toFixed(2)} )`\n\t\t\t)\n\t\t}\n\t})\n\n\t// STEP 7: Flush buffer / add final slide (skip an empty trailing buffer; always keep at least\n\t// one slide so a non-empty table is never reduced to zero pages)\n\tif (newTableRowSlide.rows.length > 0 || tableRowSlides.length === 0) tableRowSlides.push(newTableRowSlide)\n\n\tif (tableProps.verbose) {\n\t\tconsole.log('\\n|================================================|')\n\t\tconsole.log(`| FINAL: tableRowSlides.length = ${tableRowSlides.length}`)\n\t\ttableRowSlides.forEach((slide) => console.log(slide))\n\t\tconsole.log('|================================================|\\n\\n')\n\t}\n\n\t// LAST:\n\treturn tableRowSlides\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"autopage-D4kyVp11.js","names":[],"sources":["../src/gen/table/autopage.ts"],"sourcesContent":["/**\n * pptx-ts: Table auto-paging core\n *\n * The DOM-independent heart of table generation: given rows that overflow a slide,\n * split them across as many slides as needed, measuring wrapped text to compute line\n * counts and row heights. `getSlidesForTableRows` is the in-memory core (fully\n * Node-testable); `parseTextToLines` is its private cell-wrapping helper.\n */\n\nimport { SlideObjectType } from '../../enums.js'\nimport { DEF_FONT_SIZE, DEF_SLIDE_MARGIN_IN } from '../../constants-internal.js'\nimport type { PresLayout, TableRowSlide, TableCellProps } from '../../types/index.js'\nimport type {\n\tSlideLayoutInternal,\n\tTableCellInternal,\n\tTableRowInternal,\n\tTableToSlidesPropsInternal,\n} from '../../types/internal.js'\nimport {\n\tautoPageLineHeightEmu,\n\tgetSmartParseNumber,\n\tinch2Emu,\n\tmarginToEmu,\n\tpinnedRowHeightInches,\n\tresolveCellMarginsInches,\n\tresolveSlideMarginsInches,\n\tresolveTableColWidthsEmu,\n\tusableTableWidthEmu,\n} from '../../units-internal.js'\nimport { warn } from '../../diagnostics.js'\nimport { type GridPlacement, tableColCount, walkTableGrid } from './grid.js'\nimport { resolveSpan, withCheckedSpans } from './spans.js'\nimport { EMU_PER_INCH, POINTS_PER_INCH } from '../../units.js'\n\ntype AutoPageCell = TableCellInternal & {\n\t_lineHeight: number\n\t_lines: TableCellInternal[][]\n\toptions: TableCellProps\n\ttext: TableCellInternal[]\n}\n\n/**\n * A cell for the pager's working grid, carrying the source cell's options only when it has some.\n *\n * Every one of these used to be an inline `{ …, options: cell.options }`, which put an `options`\n * key holding `undefined` on a cell whose source had none. Readers cannot see the difference —\n * they all go through `cell.options?.…` — but a `TableCellProps` bag *is* spread in\n * `gen/define/table.ts` (`{ ...colDef, ...headerRow, ...cell.options }`), where the two states\n * are not the same thing. So the model keeps one spelling of absent.\n * @param text - the cell's content, a string or the run list the pager is accumulating\n * @param options - the source cell's options, if it had any\n */\nfunction workingCell(text: string | TableCellInternal[], options: TableCellProps | undefined): TableCellInternal {\n\treturn options === undefined\n\t\t? { _type: SlideObjectType.tablecell, text }\n\t\t: { _type: SlideObjectType.tablecell, text, options }\n}\n\n// ===== Cell text wrapping =====\n\n/**\n * Break cell text into lines based upon table column width (e.g.: Magic Happens Here(tm))\n *\n * `charWeight` arrives as an argument rather than being read off `cell.options` because the\n * caller used to *stamp* it there: the table's weight was written onto the caller's own cell\n * options bag, or `delete`d from it when the table stated none. That mutated caller input --\n * which the chart path has a dedicated guard against -- and it made the option's precedence\n * differ by call site: the row-height probe read the cell's own value while the main loop had\n * just overwritten it with the table's. Both callers now resolve it the same way, and the more\n * specific statement wins, as it does everywhere else in this codebase.\n *\n * `fontSizePt` arrives resolved for the same reason. This read `cell.options.fontSize` or the\n * library default and never the table's `fontSize`, while the row's line height and the emitted\n * `sz` both used the table's. A table of 24pt text was wrapped as if it were 12pt, so the pager\n * counted about half the lines and rows ran off the bottom of each page.\n * @param {TableCellInternal} cell - table cell\n * @param {number} colWidth - table column width (inches)\n * @param {number} fontSizePt - resolved font size: the cell's, else the table's, else the default\n * @param {number} charWeight - resolved `autoPageCharWeight`: the cell's, else the table's, else 0\n * @param {boolean} [verbose] - dump the four wrapping stages; carries `addTable({ verbose })`\n * down, which is the only stage of the pager that flag did not reach\n * @return {TableRowInternal[]} - cell's text objects grouped into lines\n */\nfunction parseTextToLines(\n\tcell: TableCellInternal,\n\tcolWidth: number,\n\tfontSizePt: number,\n\tcharWeight: number,\n\tverbose?: boolean\n): TableCellInternal[][] {\n\t// FYI: CPL = Width / (font-size / font-constant)\n\t// FYI: CHAR:2.3, colWidth:10, fontSize:12 => CPL=138, (actual chars per line in PPT)=145 [14.5 CPI]\n\t// FYI: CHAR:2.3, colWidth:7 , fontSize:12 => CPL= 97, (actual chars per line in PPT)=100 [14.3 CPI]\n\t// FYI: CHAR:2.3, colWidth:9 , fontSize:16 => CPL= 96, (actual chars per line in PPT)=84 [ 9.3 CPI]\n\tconst FOCO = 2.3 + charWeight // Character Constant\n\t// `colWidth` is inches, so the column's width in points is `colWidth * 72`. This used to be\n\t// spelled `(colWidth / EMU_PER_POINT) * EMU_PER_INCH`, which is the same 72 with two EMU\n\t// constants that cancel -- except that the detour through EMU is not exact in binary floating\n\t// point, and `Math.floor` takes the hit. Over the 200,000 widths from 0.001in to 200in the two\n\t// forms disagree on 51, always by one point low: 6.625in is exactly 477 points, and the old\n\t// form floored 476.9999... to 476.\n\tconst CPL = Math.floor(colWidth * POINTS_PER_INCH) / (fontSizePt / FOCO) // Chars-Per-Line\n\n\t// The number the four dumps below exist to explain: every wrap decision in step 4 is\n\t// `strCurrLine.length + word.length > CPL`, so a wrap that looks wrong is almost always\n\t// a CPL that is wrong, and CPL is not recoverable from the lines themselves.\n\tif (verbose) console.log(`[0/4] colWidth=${colWidth}in fontSize=${fontSizePt} FOCO=${FOCO} CPL=${CPL}`)\n\n\tconst parsedLines: TableCellInternal[][] = []\n\tlet inputCells: TableCellInternal[] = []\n\tconst inputLines1: TableCellInternal[][] = []\n\tconst inputLines2: TableCellInternal[][] = []\n\n\t/**\n\t * EX INPUTS: `cell.text`\n\t * - string....: \"Account Name Column\"\n\t * - object....: { text:\"Account Name Column\" }\n\t * - object[]..: [{ text:\"Account Name\", options:{ bold:true } }, { text:\" Column\" }]\n\t * - object[]..: [{ text:\"Account Name\", options:{ breakLine:true } }, { text:\"Input\" }]\n\t */\n\n\t/**\n\t * EX OUTPUTS:\n\t * - string....: [{ text:\"Account Name Column\" }]\n\t * - object....: [{ text:\"Account Name Column\" }]\n\t * - object[]..: [{ text:\"Account Name\", options:{ breakLine:true } }, { text:\"Input\" }]\n\t * - object[]..: [{ text:\"Account Name\", options:{ breakLine:true } }, { text:\"Input\" }]\n\t */\n\n\t// STEP 1: Ensure inputCells is an array of TableCells\n\tif (cell.text && cell.text.toString().trim().length === 0) {\n\t\t// Allow a single space/whitespace as cell text (user-requested feature)\n\t\tinputCells.push({ _type: SlideObjectType.tablecell, text: ' ' })\n\t} else if (typeof cell.text === 'number' || typeof cell.text === 'string') {\n\t\tinputCells.push({ _type: SlideObjectType.tablecell, text: (cell.text || '').toString().trim() })\n\t} else if (Array.isArray(cell.text)) {\n\t\tinputCells = cell.text\n\t}\n\tif (verbose) {\n\t\tconsole.log('[1/4] inputCells')\n\t\tinputCells.forEach((cell, idx) => console.log(`[1/4] [${idx + 1}] cell: ${JSON.stringify(cell)}`))\n\t}\n\n\t// STEP 2: Group table cells into lines based on \"\\n\" or `breakLine` prop\n\t/**\n\t * - EX: `[{ text:\"Input Output\" }, { text:\"Extra\" }]` == 1 line\n\t * - EX: `[{ text:\"Input\" }, { text:\"Output\", options:{ breakLine:true } }]` == 1 line\n\t * - EX: `[{ text:\"Input\\nOutput\" }]` == 2 lines\n\t * - EX: `[{ text:\"Input\", options:{ breakLine:true } }, { text:\"Output\" }]` == 2 lines\n\t */\n\tlet newLine: TableCellInternal[] = []\n\tinputCells.forEach((cell) => {\n\t\tif (typeof cell.text !== 'string') return\n\n\t\tif (cell.text.includes('\\n')) {\n\t\t\t// Each non-last \\n-split part ends with a forced paragraph break; the last\n\t\t\t// part accumulates in newLine so subsequent runs stay on the same paragraph.\n\t\t\tconst parts = cell.text.split('\\n')\n\t\t\tparts.forEach((part, partIdx) => {\n\t\t\t\tconst isLastPart = partIdx === parts.length - 1\n\t\t\t\tif (isLastPart) {\n\t\t\t\t\tnewLine.push(workingCell(part, cell.options))\n\t\t\t\t} else {\n\t\t\t\t\tnewLine.push({\n\t\t\t\t\t\t_type: SlideObjectType.tablecell,\n\t\t\t\t\t\ttext: part,\n\t\t\t\t\t\toptions: { ...cell.options, breakLine: true },\n\t\t\t\t\t})\n\t\t\t\t\tinputLines1.push(newLine)\n\t\t\t\t\tnewLine = []\n\t\t\t\t}\n\t\t\t})\n\t\t} else {\n\t\t\tnewLine.push(workingCell(cell.text.trim(), cell.options))\n\t\t}\n\n\t\tif (cell.options?.breakLine) {\n\t\t\tif (verbose) console.log(`inputCells: new line > ${JSON.stringify(newLine)}`)\n\t\t\tinputLines1.push(newLine)\n\t\t\tnewLine = []\n\t\t}\n\t})\n\t// Flush remaining buffer after all cells are processed\n\tif (newLine.length > 0) {\n\t\tinputLines1.push(newLine)\n\t\tnewLine = []\n\t}\n\tif (verbose) {\n\t\tconsole.log(`[2/4] inputLines1 (${inputLines1.length})`)\n\t\tinputLines1.forEach((line, idx) => console.log(`[2/4] [${idx + 1}] line: ${JSON.stringify(line)}`))\n\t}\n\n\t// STEP 3: Tokenize every text object into words. All runs of one logical paragraph\n\t// are flattened into a single token list so step 4 tracks column position across\n\t// styled-run boundaries (fixes independent-reset bug for rich-text cells).\n\tinputLines1.forEach((line) => {\n\t\tconst lineTokens: TableCellInternal[] = []\n\t\tline.forEach((cell) => {\n\t\t\tconst cellTextStr = String(cell.text) // force convert to string (compiled JS is better with this than a cast)\n\t\t\tconst lineWords = cellTextStr.split(' ')\n\n\t\t\tlineWords.forEach((word, idx) => {\n\t\t\t\tconst cellProps = { ...cell.options }\n\t\t\t\t// IMPORTANT: Handle `breakLine` prop - we cannot apply to each word - only apply to very last word!\n\t\t\t\tif (cellProps?.breakLine) cellProps.breakLine = idx + 1 === lineWords.length\n\t\t\t\tlineTokens.push({\n\t\t\t\t\t_type: SlideObjectType.tablecell,\n\t\t\t\t\ttext: word + (idx + 1 < lineWords.length ? ' ' : ''),\n\t\t\t\t\toptions: cellProps,\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\tinputLines2.push(lineTokens)\n\t})\n\tif (verbose) {\n\t\tconsole.log(`[3/4] inputLines2 (${inputLines2.length})`)\n\t\tinputLines2.forEach((line) => console.log(`[3/4] line: ${JSON.stringify(line)}`))\n\t}\n\n\t// STEP 4: Group cells/words into lines based upon space consumed by word letters\n\tinputLines2.forEach((line) => {\n\t\tlet lineCells: TableCellInternal[] = []\n\t\tlet strCurrLine = ''\n\n\t\tline.forEach((word) => {\n\t\t\tconst wordText = String(word.text || '')\n\t\t\t// A: create new line when horizontal space is exhausted.\n\t\t\t// `lineCells.length > 0` is the same guard the flush below carries, and for the same\n\t\t\t// reason: on the first word `strCurrLine` is `''`, so a word wider than the column on\n\t\t\t// its own fires this branch with nothing buffered and used to push an EMPTY line. The\n\t\t\t// cell's text survived -- the pager concatenates lines back together -- but its height\n\t\t\t// did not, because `_lines.length` is what prices the row. Twelve single-word rows in a\n\t\t\t// 0.4in column paged onto two slides when the word was long and one when it was short.\n\t\t\t// The word still overflows its column: nothing here breaks inside a word, which is the\n\t\t\t// stated behaviour rather than a gap (`docs/contributing/design/tables.md`). A hard\n\t\t\t// break would have to land at the `CPL` estimate, which is not where the renderer wraps,\n\t\t\t// and would then have to keep `_lines.length` and the emitted text in step -- the exact\n\t\t\t// pairing the phantom-line bug above got wrong.\n\t\t\tif (strCurrLine.length + wordText.length > CPL) {\n\t\t\t\tif (lineCells.length > 0) parsedLines.push(lineCells)\n\t\t\t\tlineCells = []\n\t\t\t\tstrCurrLine = ''\n\t\t\t}\n\n\t\t\t// B: add current word to line cells\n\t\t\tlineCells.push(word)\n\n\t\t\t// C: add current word to `strCurrLine` which we use to keep track of line's char length\n\t\t\tstrCurrLine += wordText\n\t\t})\n\n\t\t// Flush buffer: Only create a line when there's text to avoid empty row\n\t\tif (lineCells.length > 0) parsedLines.push(lineCells)\n\t})\n\tif (verbose) {\n\t\tconsole.log(`[4/4] parsedLines (${parsedLines.length})`)\n\t\tparsedLines.forEach((line, idx) => console.log(`[4/4] [Line ${idx + 1}]:\\n${JSON.stringify(line)}`))\n\t\tconsole.log('...............................................\\n\\n')\n\t}\n\n\t// Done:\n\treturn parsedLines\n}\n\n/**\n * The font size in points one auto-paged cell is measured at: the cell's own `fontSize`,\n * else the table's, else the library default. Pass `null` for the cell to ask the same\n * question of the table alone.\n */\nfunction resolveCellFontSize(\n\tcellOpts: TableCellProps | undefined | null,\n\ttableOpts: TableToSlidesPropsInternal\n): number {\n\tconst size = cellOpts?.fontSize ?? tableOpts.fontSize\n\treturn typeof size === 'number' ? size : DEF_FONT_SIZE\n}\n\n/**\n * The `autoPageCharWeight` a cell wraps at: its own, else the table's, else none.\n *\n * The sibling of {@link resolveCellFontSize}, and named for the same reason -- the two call\n * sites of {@link parseTextToLines} had resolved this one differently. The row-height probe\n * read the cell's own value; the main loop stamped the table's over it first, and `delete`d the\n * cell's when the table stated none, so a weight set on a cell alone was accepted and dropped.\n * The precedence here is the one every other paired option in this codebase uses.\n * @param cellOpts - the cell's own options, if any\n * @param tableOpts - the table's options\n */\nfunction resolveCellCharWeight(\n\tcellOpts: TableCellProps | undefined | null,\n\ttableOpts: TableToSlidesPropsInternal\n): number {\n\tconst weight = cellOpts?.autoPageCharWeight ?? tableOpts.autoPageCharWeight\n\treturn typeof weight === 'number' ? weight : 0\n}\n\n// ===== Auto-page engine =====\n// The in-memory core (fully Node-testable). Internally signposted by the\n// `// STEP 1..7` markers below: margins → column count → widths → the main\n// row-iteration/overflow loop (STEP 6) → final-slide flush (STEP 7).\n\n/**\n * The largest top and bottom cell margin in one row, in EMU.\n *\n * A cell's own `margin` wins over the table's when it states one, and a stated `0` counts as\n * stating one. Both sites that read this gated on truthiness and on `Array.isArray`, so a cell\n * asking for `margin: [0, …]` fell through to the table's margin instead of taking its own, and\n * a scalar `margin: 0.2` was not seen at all -- while the emitter broadcasts a scalar to four\n * sides. `resolveCellMarginsInches` is the resolver the emitter itself reads.\n * @param row - the row's cells\n * @param tableProps - the table's options, for the fallback margin\n * @returns the row's top and bottom margin allowance in EMU\n */\nfunction rowMarginsEmu(\n\trow: TableRowInternal,\n\ttableProps: TableToSlidesPropsInternal\n): { topEmu: number; btmEmu: number } {\n\tlet topEmu = 0\n\tlet btmEmu = 0\n\trow.forEach((cell) => {\n\t\tconst stated = cell?.options?.margin ?? tableProps.margin\n\t\tif (stated === undefined || stated === null) return\n\t\tconst [top, , btm] = resolveCellMarginsInches(stated)\n\t\tif (marginToEmu(top) > topEmu) topEmu = marginToEmu(top)\n\t\tif (marginToEmu(btm) > btmEmu) btmEmu = marginToEmu(btm)\n\t})\n\treturn { topEmu, btmEmu }\n}\n\n/** One row, measured: each cell's wrapped lines and what the row costs a page. */\ninterface MeasuredRow {\n\t/** One working cell per source cell, in row order, carrying its lines and its line height. */\n\tcells: AutoPageCell[]\n\t/** The row's tallest top margin plus its tallest bottom margin, in EMU. */\n\tmarginsEmu: number\n\t/** The tallest line height among the cells that do not span rows, in EMU. */\n\tlineHeightEmu: number\n\t/** The whole row: its margins plus its tallest cell's line count at `lineHeightEmu`. */\n\theightEmu: number\n}\n\n/**\n * Takes an array of table rows and breaks into an array of slides, which contain the calculated amount of table rows that fit on that slide\n * @param {TableCellInternal[][]} tableRows - table rows\n * @param {TableToSlidesPropsInternal} tableProps - table2slides properties\n * @param {PresLayout} presLayout - presentation layout\n * @param {SlideLayoutInternal} masterSlide - master slide\n * @return {TableRowSlide[]} array of table rows\n */\nexport function getSlidesForTableRows(\n\trows: TableCellInternal[][] = [],\n\ttableProps: TableToSlidesPropsInternal = {},\n\tpresLayout: PresLayout,\n\tmasterSlide?: SlideLayoutInternal | null\n): TableRowSlide[] {\n\t// `MAX_TABLE_SPAN` guards the two allocations a span decides, and its own module says it\n\t// covers both paths -- but `tableToSlides` reaches this function directly, without the\n\t// definer that applies the check, and its own `gridSpan` has no ceiling. So a\n\t// `colspan=\"4000000000\"` in a source table reached the column walk and the per-column depth\n\t// array, which is the allocation that takes the host process down with no exception to catch.\n\t// Checking here rather than at the call site keeps the one entry point guarded whoever calls\n\t// it; a grid whose spans are all fine is passed through by identity.\n\tconst tableRows = withCheckedSpans(rows)\n\tlet arrInchMargins = DEF_SLIDE_MARGIN_IN\n\tlet emuSlideTabW: number\n\tlet colWidthsIn: number[] = []\n\tlet emuSlideTabH = EMU_PER_INCH * 1\n\tlet emuTabCurrH = 0\n\tlet numCols = 0\n\tlet warnedNoTabH = false\n\tconst tableRowSlides: TableRowSlide[] = []\n\tconst tablePropX = getSmartParseNumber(tableProps.x, 'X', presLayout)\n\tconst tablePropY = getSmartParseNumber(tableProps.y, 'Y', presLayout)\n\tconst tablePropW = getSmartParseNumber(tableProps.w, 'X', presLayout)\n\tconst tablePropH = getSmartParseNumber(tableProps.h, 'Y', presLayout)\n\tlet tableCalcW: number = tablePropW\n\n\t/**\n\t * Where this page's table starts, in EMU from the top of the slide.\n\t *\n\t * There used to be three rules for two cases. The first page and \"every page after it\"\n\t * were handled together, then a block gated on `tableRowSlides.length > 1` overrode the\n\t * result -- so despite its own comment it began on the THIRD page, and its first arm\n\t * recomputed exactly what had just been computed. Page 2 therefore got a different usable\n\t * height from pages 3 and up: with `y` above the top margin and an explicit `h`, a\n\t * 60-row table paged 9 / 7 / 9 / 9 / 9 / 9 / 8, which is the same \"pages disagree about\n\t * how many identical rows fit\" symptom the margin-reset note in `calcSlideTabH` records.\n\t *\n\t * It is a function rather than two expressions because the too-small-height fallback in\n\t * `calcSlideTabH` had spelled the continuation arm its own way and drifted the same way\n\t * twice over: `autoPageSlideStartY || margin` discarded an explicit `0` that the main rule\n\t * honoured, and the `Math.min` clause below was missing outright. Two readings of one rule\n\t * is what the paragraph above is about; there is now one.\n\t */\n\tfunction startYEmu(): number {\n\t\t// A stated `y: 0` is the top edge of the slide, not a missing `y`: tested for truthiness, it\n\t\t// was budgeted from the top margin while `addTable` placed the page at 0.\n\t\tconst statedY = tableProps.y !== undefined && tableProps.y !== null\n\t\tif (tableRowSlides.length === 0) return statedY ? tablePropY : inch2Emu(arrInchMargins[0])\n\t\tif (typeof tableProps.autoPageSlideStartY === 'number') return inch2Emu(tableProps.autoPageSlideStartY)\n\t\t// RULE: after the first page the table starts at the top margin rather than at its\n\t\t// own `y` -- unless `y` is ABOVE the margin, in which case paging must not push the\n\t\t// table down and lose the space. Whichever is higher on the slide wins.\n\t\treturn inch2Emu(statedY ? Math.min(tablePropY / EMU_PER_INCH, arrInchMargins[0]) : arrInchMargins[0])\n\t}\n\n\tfunction calcSlideTabH(): void {\n\t\tconst emuStartY = startYEmu()\n\t\temuSlideTabH = (tablePropH || presLayout.height) - emuStartY - inch2Emu(arrInchMargins[2])\n\t\t// EXPLICIT-H FIX: an explicit `h` is the table's height (an extent), not a bottom\n\t\t// coordinate, so -- unlike `presLayout.height` -- the start-Y must not be subtracted from\n\t\t// it. Otherwise a table that begins mid-slide gets a tiny page while the pages that do\n\t\t// clamp to `h` render normally. Applied on every page: the floor used to reach the first\n\t\t// page and pages three and up, leaving page two as the one that could shrink below `h`.\n\t\tif (tablePropH && emuSlideTabH < tablePropH) emuSlideTabH = tablePropH\n\n\t\t// GUARD: a non-positive height, or one too small to fit even a single line of the base font,\n\t\t// means no row ever fits. That previously emitted degenerate empty overflow pages (rows:[]),\n\t\t// which made the recursive addTable throw \"Array expected\", or — with `h` smaller than one\n\t\t// line — placed one row per slide forever. Ignore the unusable height, fall back to the full\n\t\t// slide area between margins, and warn once rather than emit a broken table.\n\t\tconst emuBaseLineH = autoPageLineHeightEmu(\n\t\t\tresolveCellFontSize(null, tableProps),\n\t\t\ttableProps.autoPageLineWeight || 0\n\t\t)\n\t\tif (emuSlideTabH < emuBaseLineH) {\n\t\t\tconst fallbackH = presLayout.height - startYEmu() - inch2Emu(arrInchMargins[2])\n\t\t\tif (!warnedNoTabH) {\n\t\t\t\twarn(\n\t\t\t\t\t'table/autopage-height-too-small',\n\t\t\t\t\t'addTable/autoPage: the table height (`h`) leaves no room to paginate; ignoring it and using the slide height. Increase `h` or decrease `y`.'\n\t\t\t\t)\n\t\t\t\twarnedNoTabH = true\n\t\t\t}\n\t\t\temuSlideTabH = fallbackH > 0 ? fallbackH : presLayout.height\n\t\t}\n\t}\n\n\tif (tableProps.verbose) {\n\t\tconsole.log('[[VERBOSE MODE]]')\n\t\tconsole.log('|-- TABLE PROPS --------------------------------------------------------|')\n\t\tconsole.log(`| presLayout.width ................................ = ${(presLayout.width / EMU_PER_INCH).toFixed(1)}`)\n\t\tconsole.log(\n\t\t\t`| presLayout.height ............................... = ${(presLayout.height / EMU_PER_INCH).toFixed(1)}`\n\t\t)\n\t\tconsole.log(\n\t\t\t`| tableProps.x .................................... = ${typeof tableProps.x === 'number' ? (tableProps.x / EMU_PER_INCH).toFixed(1) : tableProps.x}`\n\t\t)\n\t\tconsole.log(\n\t\t\t`| tableProps.y .................................... = ${typeof tableProps.y === 'number' ? (tableProps.y / EMU_PER_INCH).toFixed(1) : tableProps.y}`\n\t\t)\n\t\tconsole.log(\n\t\t\t`| tableProps.w .................................... = ${typeof tableProps.w === 'number' ? (tableProps.w / EMU_PER_INCH).toFixed(1) : tableProps.w}`\n\t\t)\n\t\tconsole.log(\n\t\t\t`| tableProps.h .................................... = ${typeof tableProps.h === 'number' ? (tableProps.h / EMU_PER_INCH).toFixed(1) : tableProps.h}`\n\t\t)\n\t\tconsole.log(\n\t\t\t`| tableProps.slideMargin .......................... = ${tableProps.slideMargin ? String(tableProps.slideMargin) : ''}`\n\t\t)\n\t\tconsole.log(`| tableProps.margin ............................... = ${String(tableProps.margin)}`)\n\t\tconsole.log(`| tableProps.colW ................................. = ${String(tableProps.colW)}`)\n\t\tconsole.log(`| tableProps.autoPageSlideStartY .................. = ${tableProps.autoPageSlideStartY}`)\n\t\tconsole.log(`| tableProps.autoPageCharWeight ................... = ${tableProps.autoPageCharWeight}`)\n\t\tconsole.log('|-- CALCULATIONS -------------------------------------------------------|')\n\t\tconsole.log(`| tablePropX ...................................... = ${tablePropX / EMU_PER_INCH}`)\n\t\tconsole.log(`| tablePropY ...................................... = ${tablePropY / EMU_PER_INCH}`)\n\t\tconsole.log(`| tablePropW ...................................... = ${tablePropW / EMU_PER_INCH}`)\n\t\tconsole.log(`| tablePropH ...................................... = ${tablePropH / EMU_PER_INCH}`)\n\t\tconsole.log(`| tableCalcW ...................................... = ${tableCalcW / EMU_PER_INCH}`)\n\t}\n\n\t// STEP 1: Calculate margins\n\t{\n\t\t// The caller's value goes through unaltered, including an absent one: `resolveSlideMarginsInches`\n\t\t// owns all three tiers, and defaulting to `DEF_SLIDE_MARGIN_IN[0]` here handed it a stated\n\t\t// caller margin that no caller had stated, which suppressed the master's own `margin`.\n\t\tarrInchMargins = resolveSlideMarginsInches(masterSlide?._margin, tableProps.slideMargin)\n\n\t\tif (tableProps.verbose)\n\t\t\tconsole.log(`| arrInchMargins .................................. = [${arrInchMargins.join(', ')}]`)\n\t}\n\n\t// STEP 2: Calculate number of columns\n\t{\n\t\tnumCols = tableColCount(tableRows)\n\t\tif (tableProps.verbose) console.log(`| numCols ......................................... = ${numCols}`)\n\t}\n\n\t// Where every cell actually lands. The pager asks the grid two questions -- which columns a\n\t// cell spans, so its text is wrapped against the right width, and whether a rowspan opened\n\t// on an earlier row is still open here, so a page break does not split a merged group -- and\n\t// it used to answer both with its own occupancy array, once forwards through the row and\n\t// once again after it. `walkTableGrid` is the traversal the measured-fit pass and the\n\t// emitter's own grid build follow, so the widths this pager wraps against are the widths the\n\t// cells are finally given.\n\tconst placements: GridPlacement[][] = tableRows.map(() => [])\n\t/**\n\t * How many of each row's grid columns are held by a rowspan opened ABOVE it, accumulated as\n\t * the placements arrive rather than searched for afterwards.\n\t *\n\t * The question used to be answered by scanning every placement in the table, once per row,\n\t * which is quadratic in a table of any width. Two columns of short text and no spans at all:\n\t * 1000 rows took 55ms, 2000 took 115ms, 4000 took 333ms and 8000 took 1036ms. The same four\n\t * now take 24ms, 19ms, 40ms and 83ms -- so it was pure overhead, paid in full by the long\n\t * report table that is the whole reason this code exists.\n\t *\n\t * Filling forward instead is bounded by the placement's own `rowSpan`, so the total work is\n\t * the sum of all rowspans. That is safe to expand without a further check because\n\t * `walkTableGrid` has already clamped each `rowSpan` to `rows.length - r`: a span cannot\n\t * reach past the last row however large the caller wrote it, so the sum is bounded by the\n\t * grid rather than by anything the caller states.\n\t *\n\t * The fill starts at `row + 1`. A `rowSpan` of 1 must contribute nothing -- starting at\n\t * `row` would have every ordinary row counting itself as covered, and the\n\t * `coveredFromAbove(iRow) === numCols` reader below would then fire on all of them.\n\t */\n\tconst coveredCols: number[] = Array.from({ length: tableRows.length }, () => 0)\n\tfor (const placement of walkTableGrid(tableRows, numCols)) {\n\t\tplacements[placement.row]?.push(placement)\n\t\tconst last = Math.min(placement.row + placement.rowSpan, coveredCols.length)\n\t\tfor (let r = placement.row + 1; r < last; r++) coveredCols[r] = (coveredCols[r] ?? 0) + placement.colSpan\n\t}\n\t/** How many of `iRow`'s grid columns are held by a rowspan opened above it. */\n\tconst coveredFromAbove = (iRow: number): number => coveredCols[iRow] ?? 0\n\t/** Whether a rowspan opened above `iRow` is still covering it. */\n\tconst spannedFromAbove = (iRow: number): boolean => coveredFromAbove(iRow) > 0\n\n\t// STEP 3: Calculate width using tableProps.colW if possible. Only an array reaches here:\n\t// `addTableDefinition` turns a single `colW` into `w` before paging, and `tableToSlides` always\n\t// passes one width per column.\n\tif (!tablePropW && Array.isArray(tableProps.colW)) {\n\t\t// The `0` seed is what `colW: []` needs: an empty array is truthy, so it reached an\n\t\t// unseeded `reduce` and threw a raw `TypeError` — a failure this library did not raise as\n\t\t// a `TsPptxError`. Seeded, it totals `0`, which falls through to `usableTableWidthEmu`\n\t\t// exactly where `colW: undefined` lands. That is the reading intended: an empty array is a\n\t\t// caller stating no columns, not a table of width zero.\n\t\ttableCalcW = tableProps.colW.reduce((p, n) => p + n, 0) * EMU_PER_INCH\n\t\tif (tableProps.verbose)\n\t\t\tconsole.log(`| tableCalcW ...................................... = ${tableCalcW / EMU_PER_INCH}`)\n\t}\n\n\t// STEP 4: Calculate usable width now that total usable space is known (`emuSlideTabW`)\n\t{\n\t\temuSlideTabW = tableCalcW || usableTableWidthEmu(presLayout, tablePropX, arrInchMargins)\n\t\tif (tableProps.verbose)\n\t\t\tconsole.log(`| emuSlideTabW .................................... = ${(emuSlideTabW / EMU_PER_INCH).toFixed(1)}`)\n\t}\n\n\t// STEP 5: Resolve the column grid. `resolveTableColWidthsEmu` is the same resolver the\n\t// table emitter and the measured-fit pass read, so the widths this pager wraps text\n\t// against are the widths the emitted `<a:gridCol>`s carry. It also normalizes the array\n\t// to `numCols`: a short `colW` used to leave the trailing columns measuring against a\n\t// zero width. Inches, because that is the unit each paged table's `colW` takes; the grid\n\t// rides out on every `TableRowSlide` rather than being written back onto the caller's bag.\n\tcolWidthsIn = resolveTableColWidthsEmu(tableProps.colW, emuSlideTabW, numCols).map(\n\t\t(emu: number) => emu / EMU_PER_INCH\n\t)\n\n\t// Resolve a row's explicit height (inches) from the original `rowH` *array*, keyed by original\n\t// row index. A single-number `rowH` is left to propagate via table options (it applies uniformly,\n\t// so it needs no per-row remapping); only the array form is index-sensitive after pagination.\n\t// Whether a slot pins its row is `pinnedRowHeightInches`, the same rule the writer applies to\n\t// the array this builds — a `0` accepted here and rejected there is how the two readings drift.\n\tconst resolveRowH = (origRowIdx: number): number | undefined =>\n\t\tArray.isArray(tableProps.rowH) ? (pinnedRowHeightInches(tableProps.rowH[origRowIdx]) ?? undefined) : undefined\n\n\t/**\n\t * Wrap each of row `iRow`'s cells against the grid columns it covers, and price the row.\n\t *\n\t * The one row measure. The repeated header rows used to be priced by a second copy of it with its\n\t * own column cursor, which ignored the columns a rowspan holds -- the fault `walkTableGrid`\n\t * fixed for the main loop -- and skipped the line-height exemption for a cell that spans rows,\n\t * so a header row inside a rowspan was measured against the wrong columns.\n\t * @param iRow - the row's index in `tableRows`\n\t * @param verbose - dump the wrapping stages, as `addTable({ verbose })` asks\n\t */\n\tfunction measureRow(iRow: number, verbose?: boolean): MeasuredRow {\n\t\tconst row = tableRows[iRow] ?? []\n\t\tconst { topEmu, btmEmu } = rowMarginsEmu(row, tableProps)\n\t\t// Cells are keyed to grid columns, not to their position in the row: a colspan earlier in\n\t\t// the row shifts every later cell, and a rowspan opened above skips columns entirely.\n\t\t// Measuring a cell against `colW[iCell]` wrapped its text to another column's width.\n\t\tconst rowPlacements = placements[iRow] ?? []\n\t\tconst cells = row.map((cell, iCell): AutoPageCell => {\n\t\t\t// A row longer than the grid runs off the end of it: those cells are placed nowhere\n\t\t\t// and measure against no column, which is what the cursor did by clamping.\n\t\t\tconst placed = rowPlacements[iCell]\n\t\t\tconst colStart = placed ? placed.col : numCols\n\t\t\tconst cellColspan = placed ? placed.colSpan : resolveSpan(cell.options?.colspan, 'colspan')\n\t\t\t// A spanning cell is as wide as the columns it covers; the seed keeps a row longer than\n\t\t\t// the grid from throwing on an empty slice.\n\t\t\tconst totalColW = colWidthsIn.slice(colStart, colStart + cellColspan).reduce((prev, curr) => prev + curr, 0)\n\t\t\tconst fontSizePt = resolveCellFontSize(cell.options, tableProps)\n\t\t\treturn {\n\t\t\t\t_type: SlideObjectType.tablecell,\n\t\t\t\t_lines: parseTextToLines(cell, totalColW, fontSizePt, resolveCellCharWeight(cell.options, tableProps), verbose),\n\t\t\t\t// A cell that spans rows adds no line height to this one (or a page could break where\n\t\t\t\t// nothing needed it to). The grid's clamped span decides, not the option: `rowspan: 1`\n\t\t\t\t// is a valid \"no span\" that `withCheckedSpans` passes through, and testing the option\n\t\t\t\t// priced every such row at its margins alone.\n\t\t\t\t_lineHeight:\n\t\t\t\t\tplaced && placed.rowSpan > 1 ? 0 : autoPageLineHeightEmu(fontSizePt, tableProps.autoPageLineWeight || 0),\n\t\t\t\ttext: [],\n\t\t\t\toptions: cell.options || {},\n\t\t\t}\n\t\t})\n\t\tconst lineHeightEmu = cells.reduce((max, cell) => Math.max(max, cell._lineHeight), 0)\n\t\tconst maxLines = cells.reduce((max, cell) => Math.max(max, cell._lines.length), 0)\n\t\tconst marginsEmu = topEmu + btmEmu\n\t\treturn { cells, marginsEmu, lineHeightEmu, heightEmu: marginsEmu + maxLines * lineHeightEmu }\n\t}\n\n\t// What the repeated header rows cost a continuation page. Computed once: the rows and the\n\t// grid are the same on every page, so re-measuring per page would only be slower. Both callers\n\t// pass the header rows as the leading rows of `tableRows`, so each is measured at its own index,\n\t// against the grid placements the body rows use.\n\tconst repeatHeaderRows =\n\t\ttableProps.autoPageRepeatHeader && tableProps._arrObjTabHeadRows ? tableProps._arrObjTabHeadRows : []\n\tconst repeatHeaderHeightsEmu = repeatHeaderRows.map((_row, headIdx) => measureRow(headIdx).heightEmu)\n\n\t/**\n\t * The last row of the rowspan group that row `iRow` opens: the furthest row any of its spans\n\t * reaches, extended by every span a covered row opens in turn. `walkTableGrid` has clamped each\n\t * span to the table, so the walk ends inside it.\n\t * @param iRow - the row that opens the group\n\t */\n\tfunction rowSpanGroupEnd(iRow: number): number {\n\t\tlet end = iRow\n\t\tfor (let r = iRow; r <= end; r++) {\n\t\t\tfor (const placement of placements[r] ?? []) end = Math.max(end, r + placement.rowSpan - 1)\n\t\t}\n\t\treturn end\n\t}\n\n\t// STEP 6: **MAIN** Iterate over rows, add table content, create new slides as rows overflow\n\tlet newTableRowSlide: TableRowSlide = {\n\t\trows: [] as TableRowInternal[],\n\t\trowH: [] as Array<number | undefined>,\n\t\tcolW: colWidthsIn,\n\t\ty: startYEmu() / EMU_PER_INCH,\n\t}\n\t/** Rows placed on the working page other than its repeated header rows. */\n\tlet bodyRowsOnPage = 0\n\tlet warnedTallRowSpan = false\n\n\t/**\n\t * Close the working page and open the next: push the page when it has rows, re-derive the page\n\t * height, charge `chargedEmu` to the new page, then repeat the header rows onto it.\n\t * @param chargedEmu - what the new page already owes before its header rows: the margins of a\n\t * row that is starting over here, or nothing\n\t */\n\tfunction startNewPage(chargedEmu: number): void {\n\t\t// Never push an empty page: a row that does not fit yet has no content here, and an empty\n\t\t// `rows` slide crashes the recursive addTable.\n\t\tif (newTableRowSlide.rows.length > 0) tableRowSlides.push(newTableRowSlide)\n\t\t// Decided after the push: the page just closed is what makes this one a continuation.\n\t\tnewTableRowSlide = {\n\t\t\trows: [],\n\t\t\trowH: [] as Array<number | undefined>,\n\t\t\tcolW: colWidthsIn,\n\t\t\ty: startYEmu() / EMU_PER_INCH,\n\t\t}\n\t\tbodyRowsOnPage = 0\n\t\t// The page height depends on which page this is, so it is re-derived for the new one.\n\t\tcalcSlideTabH()\n\t\t// ORDER IS LOAD-BEARING. The charge used to be added and then wiped by the reset, so the first\n\t\t// row of every continuation slide was the only row in the table that paid no margin. That let\n\t\t// a continuation slide accept one row more than it had room for, and the extra row hung off\n\t\t// the bottom of the slide (upstream gitbrent/PptxGenJS#1200). The symptom is easy to miss\n\t\t// because the pager is *self*-consistent per slide: it only shows up as the first slide and\n\t\t// the continuation slides disagreeing about how many identical rows fit the identical space,\n\t\t// which is what test/regression/table/table-autopage-continuation-budget.test.ts pins.\n\t\temuTabCurrH = chargedEmu\n\t\tif (tableProps.verbose)\n\t\t\tconsole.log(\n\t\t\t\t`| SLIDE [${tableRowSlides.length}]: emuSlideTabH ...... = ${(emuSlideTabH / EMU_PER_INCH).toFixed(1)} `\n\t\t\t)\n\t\trepeatHeaderRows.forEach((headRow, headIdx) => {\n\t\t\tnewTableRowSlide.rows.push([...headRow])\n\t\t\t// Repeated header rows are the original leading rows, so carry their configured height.\n\t\t\tnewTableRowSlide.rowH?.push(resolveRowH(headIdx))\n\t\t\temuTabCurrH += repeatHeaderHeightsEmu[headIdx] ?? 0\n\t\t})\n\t}\n\n\ttableRows.forEach((row, iRow) => {\n\t\t// A: Row variables — detect active rowspan at the start of this row so we can\n\t\t// suppress page breaks that would split a rowspan group across slides.\n\t\tconst hasActiveRowSpan = spannedFromAbove(iRow)\n\t\t// B: Create new row in data model\n\t\tlet currTableRow: TableRowInternal = []\n\t\trow.forEach((cell) => currTableRow.push(workingCell([], cell.options)))\n\n\t\t// C: Calc usable vertical space/table height. Set default value first, adjust below when necessary.\n\t\tcalcSlideTabH()\n\t\tif (tableProps.verbose && iRow === 0)\n\t\t\tconsole.log(\n\t\t\t\t`| SLIDE [${tableRowSlides.length}]: emuSlideTabH ...... = ${(emuSlideTabH / EMU_PER_INCH).toFixed(1)} `\n\t\t\t)\n\n\t\t// C-2: A row that opens a rowspan is kept with the rows the span covers. Breaking a page\n\t\t// partway through it left the partial row as the page's last row still carrying `rowSpan`,\n\t\t// so the emitter wrote a `rowSpan=\"2\"` with no row under it -- the state PowerPoint reports\n\t\t// as corrupt. The whole group is priced before any of its lines is taken: when it does not\n\t\t// fit under rows already on the page, the page breaks before it, and a group taller than a\n\t\t// page is emitted whole on a page of its own.\n\t\tconst startsRowSpan = !hasActiveRowSpan && (placements[iRow] ?? []).some((placement) => placement.rowSpan > 1)\n\t\tif (startsRowSpan) {\n\t\t\tlet groupHeightEmu = 0\n\t\t\tfor (let r = iRow; r <= rowSpanGroupEnd(iRow); r++) groupHeightEmu += measureRow(r).heightEmu\n\t\t\tif (bodyRowsOnPage > 0 && emuTabCurrH + groupHeightEmu > emuSlideTabH) startNewPage(0)\n\t\t\tif (emuTabCurrH + groupHeightEmu > emuSlideTabH && !warnedTallRowSpan) {\n\t\t\t\twarn(\n\t\t\t\t\t'table/autopage-rowspan-too-tall',\n\t\t\t\t\t'addTable/autoPage: a group of rows joined by a rowspan is taller than a page; it is kept on one page and runs past its bottom. Reduce the span or the text in it.'\n\t\t\t\t)\n\t\t\t\twarnedTallRowSpan = true\n\t\t\t}\n\t\t}\n\t\tconst keepRowTogether = hasActiveRowSpan || startsRowSpan\n\n\t\t// D: --==[[ BUILD DATA SET ]]==-- (split each cell's text into lines, set its line height)\n\t\tconst {\n\t\t\tcells: rowCellLines,\n\t\t\tmarginsEmu: rowMarginsTotalEmu,\n\t\t\tlineHeightEmu: emuLineMaxH,\n\t\t} = measureRow(iRow, tableProps.verbose)\n\t\temuTabCurrH += rowMarginsTotalEmu // Start row height with margins\n\n\t\t/** E: --==[[ PAGE DATA SET ]]==--\n\t\t * Add text one-line-a-time to this row's cells until: lines are exhausted OR table height limit is hit\n\t\t *\n\t\t * Design:\n\t\t * - Building cells L-to-R/loop style wont work as one could be 100 lines and another 1 line\n\t\t * - Therefore, build the whole row, one-line-at-a-time, across each table columns\n\t\t * - Then, when the vertical size limit is hit is by any of the cells, make a new slide and continue adding any remaining lines\n\t\t *\n\t\t * Implementation:\n\t\t * - `rowCellLines` is an array of cells, one for each column in the table, with each cell containing an array of lines\n\t\t *\n\t\t * Sample Data:\n\t\t * - `rowCellLines` ..: [ TableCellInternal, TableCellInternal, TableCellInternal ]\n\t\t * - `TableCellInternal` .....: { _type: 'tablecell', _lines: TableCellInternal[], _lineHeight: 10 }\n\t\t * - `_lines` ........: [ {_type: 'tablecell', text: 'cell-1,line-1', options: {…}}, {_type: 'tablecell', text: 'cell-1,line-2', options: {…}} }\n\t\t * - `_lines` is TableCellInternal[] (the 1-N words in the line)\n\t\t * {\n\t\t * _lines: [{ text:'cell-1,line-1' }, { text:'cell-1,line-2' }], // TOTAL-CELL-HEIGHT = 2\n\t\t * _lines: [{ text:'cell-2,line-1' }, { text:'cell-2,line-2' }], // TOTAL-CELL-HEIGHT = 2\n\t\t * _lines: [{ text:'cell-3,line-1' }, { text:'cell-3,line-2' }, { text:'cell-3,line-3' }, { text:'cell-3,line-4' }], // TOTAL-CELL-HEIGHT = 4\n\t\t * }\n\t\t *\n\t\t * Example: 2 rows, with the firstrow overflowing onto a new slide\n\t\t * SLIDE 1:\n\t\t * |--------|--------|--------|--------|\n\t\t * | line-1 | line-1 | line-1 | line-1 |\n\t\t * | | | line-2 | |\n\t\t * | | | line-3 | |\n\t\t * |--------|--------|--------|--------|\n\t\t *\n\t\t * SLIDE 2:\n\t\t * |--------|--------|--------|--------|\n\t\t * | | | line-4 | |\n\t\t * |--------|--------|--------|--------|\n\t\t * | line-1 | line-1 | line-1 | line-1 |\n\t\t * |--------|--------|--------|--------|\n\t\t */\n\t\tif (tableProps.verbose) console.log(`\\n| SLIDE [${tableRowSlides.length}]: ROW [${iRow}]: START...`)\n\t\tlet currCellIdx = 0\n\t\tlet isDone = false\n\t\twhile (!isDone) {\n\t\t\tconst srcCell = rowCellLines[currCellIdx]\n\t\t\tif (!srcCell) break\n\t\t\tlet tgtCell = currTableRow[currCellIdx] // NOTE: may be redefined below (a new row may be created, thus changing this value)\n\n\t\t\t// 2: create a new slide if there is insufficient room for the current row,\n\t\t\t// but never break inside a rowspan group — keep spanned rows together.\n\t\t\tif (emuTabCurrH + emuLineMaxH > emuSlideTabH && !keepRowTogether) {\n\t\t\t\tif (tableProps.verbose) {\n\t\t\t\t\tconsole.log('\\n|-----------------------------------------------------------------------|')\n\t\t\t\t\t// prettier-ignore\n\t\t\t\t\tconsole.log(`|-- NEW SLIDE CREATED (currTabH+currLineH > maxH) => ${(emuTabCurrH / EMU_PER_INCH).toFixed(2)} + ${(srcCell._lineHeight / EMU_PER_INCH).toFixed(2)} > ${emuSlideTabH / EMU_PER_INCH}`)\n\t\t\t\t\tconsole.log('|-----------------------------------------------------------------------|\\n\\n')\n\t\t\t\t}\n\n\t\t\t\t// A: add current row slide or it will be lost (only if it has rows and text)\n\t\t\t\tif (\n\t\t\t\t\tcurrTableRow.length > 0 &&\n\t\t\t\t\tcurrTableRow.map((cell) => (Array.isArray(cell.text) ? cell.text.length : 0)).reduce((p, n) => p + n) > 0\n\t\t\t\t) {\n\t\t\t\t\tnewTableRowSlide.rows.push(currTableRow)\n\t\t\t\t\tnewTableRowSlide.rowH?.push(resolveRowH(iRow))\n\t\t\t\t}\n\n\t\t\t\t// B: open the next page. The row is starting over there, so it owes its cell margins\n\t\t\t\t// again.\n\t\t\t\tstartNewPage(rowMarginsTotalEmu)\n\n\t\t\t\t// C: reset working/curr row\n\t\t\t\tcurrTableRow = []\n\t\t\t\trow.forEach((cell) => currTableRow.push(workingCell([], cell.options)))\n\n\t\t\t\ttgtCell = currTableRow[currCellIdx]\n\t\t\t}\n\n\t\t\t// 3: set array of words that comprise this line\n\t\t\tconst currLine = srcCell._lines.shift()\n\n\t\t\t// 4: create new line by adding all words from curr line (or add empty if there are no words to avoid \"needs repair\" issue triggered when cells have null content)\n\t\t\tif (tgtCell && Array.isArray(tgtCell.text)) {\n\t\t\t\tif (currLine) tgtCell.text = tgtCell.text.concat(currLine)\n\t\t\t\telse if (tgtCell.text.length === 0)\n\t\t\t\t\ttgtCell.text = tgtCell.text.concat({ _type: SlideObjectType.tablecell, text: '' })\n\t\t\t\t// IMPORTANT: ^^^ add empty if there are no words to avoid \"needs repair\" issue triggered when cells have null content\n\t\t\t}\n\n\t\t\t// 5: increase table height by the curr line height (if we're on the last column)\n\t\t\tif (currCellIdx === rowCellLines.length - 1) emuTabCurrH += emuLineMaxH\n\n\t\t\t// 6: advance column/cell index (or circle back to first one to continue adding lines)\n\t\t\tcurrCellIdx = currCellIdx < rowCellLines.length - 1 ? currCellIdx + 1 : 0\n\n\t\t\t// 7: WIP: done?\n\t\t\tconst brent = rowCellLines.map((cell) => cell._lines.length).reduce((prev, next) => prev + next)\n\t\t\tif (brent === 0) isDone = true\n\t\t}\n\n\t\t// F: Flush/capture row buffer before it resets at the top of this loop.\n\t\t//\n\t\t// A row that states no cells is kept only when every one of its grid columns is held by a\n\t\t// rowspan from above. That is a real row -- a source row states only the cells it *starts*,\n\t\t// so one entirely covered from above states none at all, which is what `<tr></tr>` between\n\t\t// two spanned rows means -- and the emitter fills it with `vMerge` continuations. Dropping\n\t\t// it moved every later row up one while the emitter went on synthesizing that grid row's\n\t\t// continuations, so they landed in the next row's `<a:tr>`: a 2-column table came out with\n\t\t// a 4-cell row.\n\t\t//\n\t\t// An empty row NOT covered that way is a different thing and is still dropped: nothing\n\t\t// would fill it, and a `<a:tr>` carrying fewer `<a:tc>` than the grid has columns is the\n\t\t// malformation PowerPoint offers to repair.\n\t\tif (currTableRow.length > 0 || (row.length === 0 && coveredFromAbove(iRow) === numCols)) {\n\t\t\tnewTableRowSlide.rows.push(currTableRow)\n\t\t\tnewTableRowSlide.rowH?.push(resolveRowH(iRow))\n\t\t\tbodyRowsOnPage++\n\t\t}\n\n\t\tif (tableProps.verbose) {\n\t\t\tconsole.log(\n\t\t\t\t`- SLIDE [${tableRowSlides.length}]: ROW [${iRow}]: ...COMPLETE ...... emuTabCurrH = ${(emuTabCurrH / EMU_PER_INCH).toFixed(2)} ( emuSlideTabH = ${(\n\t\t\t\t\temuSlideTabH / EMU_PER_INCH\n\t\t\t\t).toFixed(2)} )`\n\t\t\t)\n\t\t}\n\t})\n\n\t// STEP 7: Flush buffer / add final slide (skip an empty trailing buffer; always keep at least\n\t// one slide so a non-empty table is never reduced to zero pages)\n\tif (newTableRowSlide.rows.length > 0 || tableRowSlides.length === 0) tableRowSlides.push(newTableRowSlide)\n\n\tif (tableProps.verbose) {\n\t\tconsole.log('\\n|================================================|')\n\t\tconsole.log(`| FINAL: tableRowSlides.length = ${tableRowSlides.length}`)\n\t\ttableRowSlides.forEach((slide) => console.log(slide))\n\t\tconsole.log('|================================================|\\n\\n')\n\t}\n\n\t// LAST:\n\treturn tableRowSlides\n}\n"],"mappings":"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"}
|