pptx-ts 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +147 -114
  2. package/dist/{autopage-odPz7zZO.js → autopage-D4kyVp11.js} +5 -5
  3. package/dist/{autopage-odPz7zZO.js.map → autopage-D4kyVp11.js.map} +1 -1
  4. package/dist/{body-insets-Dw_BeWD_.js → body-insets-kVHmjGc6.js} +2 -2
  5. package/dist/{body-insets-Dw_BeWD_.js.map → body-insets-kVHmjGc6.js.map} +1 -1
  6. package/dist/browser.d.ts +7 -7
  7. package/dist/browser.js +5 -5
  8. package/dist/{clip-Bl01BPcN.d.ts → clip-DHDlnHUq.d.ts} +2 -2
  9. package/dist/{clip-Bl01BPcN.d.ts.map → clip-DHDlnHUq.d.ts.map} +1 -1
  10. package/dist/{diagnostics-D8ot4Ei7.d.ts → diagnostics-DiMmBU8P.d.ts} +6 -6
  11. package/dist/{diagnostics-D8ot4Ei7.d.ts.map → diagnostics-DiMmBU8P.d.ts.map} +1 -1
  12. package/dist/{embedded-fonts-DarT9aoa.d.ts → embedded-fonts-CI_YGW2d.d.ts} +2 -2
  13. package/dist/{embedded-fonts-DarT9aoa.d.ts.map → embedded-fonts-CI_YGW2d.d.ts.map} +1 -1
  14. package/dist/{entry-compose-DCnvBZz2.d.ts → entry-compose-6hF9aTmT.d.ts} +19 -17
  15. package/dist/{entry-compose-DCnvBZz2.d.ts.map → entry-compose-6hF9aTmT.d.ts.map} +1 -1
  16. package/dist/{enums-BUKTUB6V.d.ts → enums-DQRpVOLh.d.ts} +3 -3
  17. package/dist/{enums-BUKTUB6V.d.ts.map → enums-DQRpVOLh.d.ts.map} +1 -1
  18. package/dist/{errors-C4oENPcu.js → errors-Da2nXCJm.js} +6 -6
  19. package/dist/{errors-C4oENPcu.js.map → errors-Da2nXCJm.js.map} +1 -1
  20. package/dist/families.d.ts +6 -6
  21. package/dist/families.js +2 -2
  22. package/dist/{fit-Cb7naFj6.js → fit-CcXbg__l.js} +10 -10
  23. package/dist/{fit-Cb7naFj6.js.map → fit-CcXbg__l.js.map} +1 -1
  24. package/dist/{font-metrics-v5fn7s7e.d.ts → font-metrics-CBGmhLo0.d.ts} +6 -6
  25. package/dist/{font-metrics-v5fn7s7e.d.ts.map → font-metrics-CBGmhLo0.d.ts.map} +1 -1
  26. package/dist/{grid-DikTjFey.js → grid-9B2w5EaY.js} +2 -2
  27. package/dist/{grid-DikTjFey.js.map → grid-9B2w5EaY.js.map} +1 -1
  28. package/dist/{html-dom-Iuog2wdK.js → html-dom-DvJmuHt2.js} +10 -10
  29. package/dist/html-dom-DvJmuHt2.js.map +1 -0
  30. package/dist/html.d.ts +3 -3
  31. package/dist/html.js +2 -2
  32. package/dist/{image-size-CvN2iCA4.js → image-size-DwDbakcl.js} +64 -5
  33. package/dist/image-size-DwDbakcl.js.map +1 -0
  34. package/dist/index.d.ts +7 -7
  35. package/dist/index.js +3 -3
  36. package/dist/inspect.d.ts +4 -4
  37. package/dist/inspect.js +4 -4
  38. package/dist/inspect.js.map +1 -1
  39. package/dist/math.d.ts +1 -1
  40. package/dist/math.js +1 -1
  41. package/dist/measure.d.ts +3 -3
  42. package/dist/measure.js +2 -2
  43. package/dist/node.d.ts +7 -7
  44. package/dist/node.js +3 -3
  45. package/dist/{notes-Dh12JAbn.js → notes-BcT571R4.js} +19 -18
  46. package/dist/notes-BcT571R4.js.map +1 -0
  47. package/dist/options-internal-WqgMf720.js.map +1 -1
  48. package/dist/{package-J49Ck6S2.d.ts → package-KoEgVwII.d.ts} +6 -2
  49. package/dist/{package-J49Ck6S2.d.ts.map → package-KoEgVwII.d.ts.map} +1 -1
  50. package/dist/{pres-C6qgOFAa.d.ts → pres-BA9_lJbY.d.ts} +32 -12
  51. package/dist/pres-BA9_lJbY.d.ts.map +1 -0
  52. package/dist/{presentation-DGYlAWhl.js → presentation-Cyl74rq2.js} +70 -35
  53. package/dist/presentation-Cyl74rq2.js.map +1 -0
  54. package/dist/{presentation-types-8jLQHI4M.d.ts → presentation-types-B3VQH4Bz.d.ts} +19 -15
  55. package/dist/presentation-types-B3VQH4Bz.d.ts.map +1 -0
  56. package/dist/read.d.ts +6 -6
  57. package/dist/read.js +3 -3
  58. package/dist/script.d.ts +2 -2
  59. package/dist/script.d.ts.map +1 -1
  60. package/dist/script.js +11 -11
  61. package/dist/script.js.map +1 -1
  62. package/dist/{shapes-re0KyQk_.js → shapes-BcyFdZpp.js} +14 -10
  63. package/dist/shapes-BcyFdZpp.js.map +1 -0
  64. package/dist/{slide-Dx2ZeVZ6.d.ts → slide-BqPtyKmb.d.ts} +12 -7
  65. package/dist/slide-BqPtyKmb.d.ts.map +1 -0
  66. package/dist/{table-cell-margins-EVg0EwxT.js → table-cell-margins-PDshga8T.js} +4 -4
  67. package/dist/{table-cell-margins-EVg0EwxT.js.map → table-cell-margins-PDshga8T.js.map} +1 -1
  68. package/dist/{table-dom-BTDP3lEP.js → table-dom-CVbvUfgh.js} +3 -3
  69. package/dist/{table-dom-BTDP3lEP.js.map → table-dom-CVbvUfgh.js.map} +1 -1
  70. package/dist/{text-KnLOtEEu.js → text-BvFFMMFQ.js} +38 -36
  71. package/dist/{text-KnLOtEEu.js.map → text-BvFFMMFQ.js.map} +1 -1
  72. package/dist/{text-anchor-CrYXAfUu.js → text-anchor-BgLPbwbL.js} +2 -2
  73. package/dist/{text-anchor-CrYXAfUu.js.map → text-anchor-BgLPbwbL.js.map} +1 -1
  74. package/dist/{units-internal-XJlf-hOl.js → units-internal-DFcUYjh3.js} +11 -6
  75. package/dist/units-internal-DFcUYjh3.js.map +1 -0
  76. package/dist/{zip-C0leZZMj.d.ts → zip-CTll_dr0.d.ts} +2 -2
  77. package/dist/{zip-C0leZZMj.d.ts.map → zip-CTll_dr0.d.ts.map} +1 -1
  78. package/dist/{zip-CKd8ihcf.js → zip-CYZ0F6BV.js} +2 -2
  79. package/dist/{zip-CKd8ihcf.js.map → zip-CYZ0F6BV.js.map} +1 -1
  80. package/dist/zip.d.ts +2 -2
  81. package/dist/zip.js +2 -2
  82. package/dist/{zoom-cX9sllpH.js → zoom-BoNyOlPm.js} +66 -52
  83. package/dist/zoom-BoNyOlPm.js.map +1 -0
  84. package/package.json +39 -31
  85. package/skills/ts-pptx-upstream/SKILL.md +18 -18
  86. package/dist/html-dom-Iuog2wdK.js.map +0 -1
  87. package/dist/image-size-CvN2iCA4.js.map +0 -1
  88. package/dist/notes-Dh12JAbn.js.map +0 -1
  89. package/dist/pres-C6qgOFAa.d.ts.map +0 -1
  90. package/dist/presentation-DGYlAWhl.js.map +0 -1
  91. package/dist/presentation-types-8jLQHI4M.d.ts.map +0 -1
  92. package/dist/shapes-re0KyQk_.js.map +0 -1
  93. package/dist/slide-Dx2ZeVZ6.d.ts.map +0 -1
  94. package/dist/units-internal-XJlf-hOl.js.map +0 -1
  95. package/dist/zoom-cX9sllpH.js.map +0 -1
package/README.md CHANGED
@@ -1,102 +1,147 @@
1
- # ts-pptx
1
+ <div align="center">
2
2
 
3
- [![npm](https://img.shields.io/npm/v/pptx-ts)](https://www.npmjs.com/package/pptx-ts)
4
- [![weekly downloads](https://img.shields.io/npm/dw/pptx-ts.svg?label=npm%20downloads&logo=npm)](https://www.npmjs.com/package/pptx-ts)
5
- [![total downloads](https://img.shields.io/npm/dt/pptx-ts.svg?label=npm%20total%20downloads&logo=npm)](https://www.npmjs.com/package/pptx-ts)
6
- [![CI](https://github.com/shbernal/ts-pptx/actions/workflows/ci.yml/badge.svg)](https://github.com/shbernal/ts-pptx/actions/workflows/ci.yml)
7
- [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](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
- **Write a program, get a PowerPoint file.**
8
+ [![npm][npm-badge]][npm]
9
+ [![Downloads][downloads-badge]][npm]
10
+ [![CI][ci-badge]][ci]
11
+ [![License][license-badge]][license]
10
12
 
11
- A `.pptx` is a zip full of XML. ts-pptx writes that zip for you, so you describe
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
- Reach for it when a deck has to be built from data that changes: a monthly report,
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
- ## Install
17
+ ---
22
18
 
23
- ```bash
24
- pnpm add pptx-ts
25
- ```
19
+ </div>
26
20
 
27
- `@shbernal/ts-pptx` is the same package under its first name, so install one or the other.
28
- [Installation](docs/getting-started/installation.md) covers npm, CommonJS and the optional
29
- math dependencies.
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
- ## Quick start
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
- ```ts
34
- import TsPptx from "pptx-ts"
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
- const pptx = new TsPptx()
37
- const slide = pptx.addSlide()
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.addText("Hello from ts-pptx", {
40
- x: 1,
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
- await pptx.writeFile({ fileName: "example.pptx" })
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
- That is the whole shape of it. Make a presentation, add a slide, put things on the
52
- slide, write the file. Positions are in inches by default, so `x: 1, y: 1` is an inch
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
- ## What you can put on a slide
89
+ 2. Run it. Node 24 strips the types itself, so there is no build step.
58
90
 
59
- Text and rich paragraphs. Tables, including ones that spill onto as many slides as
60
- they need. Shapes and connectors between them. Pictures, SVGs, video and audio.
61
- Charts, with a real embedded workbook behind them, so double-clicking a chart in
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
- Two features worth knowing about by name:
95
+ 3. Open `hello.pptx`.
67
96
 
68
- - **[An HTML table becomes slides](docs/html-tables.md).** Point `tableToSlides` at a
69
- `<table>` you already have and it comes out as a PowerPoint table, paged across
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
- ## Reading decks, not only writing them
100
+ ## Read and edit decks too
76
101
 
77
- Writing is half of it. ts-pptx also opens a `.pptx` you already have, which is
78
- unusual: the library it descends from generates decks and does not read them.
102
+ PptxGenJS, the library pptx-ts descends from, only writes.
103
+ pptx-ts also opens a `.pptx` you already have.
79
104
 
80
- - **[Look inside one](docs/reference/pptx-inspection.md)** and get slide count, size,
81
- parts, media and fonts, without loading the whole thing into a model.
82
- - **[Edit one](docs/reading/read-and-edit.md)**. Open a deck, change the text on slide
83
- four, save it back. Parts you did not touch come out byte for byte as they went in.
84
- - **[Turn one into code](docs/reference/pptx-to-script.md)**. Point it at a deck and
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
- ## Where it runs
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
- | Runtime | Load it with | `writeFile` |
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
- | A browser app built with a bundler | `import` | downloads the file |
95
- | A browser page with no build step | `import TsPptx from "https://esm.sh/pptx-ts/browser"` in a module script | downloads the file |
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 and how each runtime
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-pptx is an independent derivative of
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-09-21 by building the same 22 deck intents with each library and reading
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-pptx emitted 21 of 22, pptxgenjs 10 of 22. Nothing in the
114
- corpus is emitted by pptxgenjs and not by ts-pptx.
115
- - **Schema validity:** of the decks each library built, 21 of 21 ts-pptx decks and 0 of 10
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 10,539,687 times a month, against 1,780 for
118
- ts-pptx. If a large installed base matters to you more than the differences above, use
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-09-15 for ts-pptx and 2025-06-26
121
- for pptxgenjs. Last npm publish, 2026-08-29 and 2025-06-26.
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
- ## Documentation
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
- Open an issue: <https://github.com/shbernal/ts-pptx/issues>. Errors the library knows
148
- are its own fault print that link themselves.
149
-
150
- If an agent writes most of your code, install the `ts-pptx-upstream` skill that ships inside
151
- the package. It turns a library defect the agent hits into a filed issue with a small
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
- Name the runtimes you use. [CONTRIBUTING.md](CONTRIBUTING.md) covers what the skill does
159
- with a report and how to refresh it after a version bump.
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
- ## License
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
- [MIT](LICENSE)
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-C4oENPcu.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-XJlf-hOl.js";
3
- import { F as EMU_PER_INCH } from "./table-cell-margins-EVg0EwxT.js";
4
- import { a as resolveSpan, n as tableColCount, r as walkTableGrid, s as withCheckedSpans } from "./grid-DikTjFey.js";
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-odPz7zZO.js.map
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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"}