@shbernal/ts-pptx 2.0.0 → 3.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 (85) hide show
  1. package/README.md +31 -2
  2. package/dist/{autopage-tmfjRzL4.js → autopage-QQyfRtoK.js} +4 -5
  3. package/dist/{autopage-tmfjRzL4.js.map → autopage-QQyfRtoK.js.map} +1 -1
  4. package/dist/browser.d.ts +14 -6
  5. package/dist/browser.d.ts.map +1 -1
  6. package/dist/browser.js +14 -6
  7. package/dist/browser.js.map +1 -1
  8. package/dist/{diagnostics-CQ0JWc9m.d.ts → diagnostics-PMINsVP6.d.ts} +97 -8
  9. package/dist/diagnostics-PMINsVP6.d.ts.map +1 -0
  10. package/dist/{enums-D3aF4QGb.d.ts → enums-CoXnxiCI.d.ts} +6 -5
  11. package/dist/{enums-D3aF4QGb.d.ts.map → enums-CoXnxiCI.d.ts.map} +1 -1
  12. package/dist/{errors-B22h8Qd0.d.ts → errors-BeUEkNWL.d.ts} +26 -4
  13. package/dist/errors-BeUEkNWL.d.ts.map +1 -0
  14. package/dist/{errors-DGE9zh3U.js → errors-Bvln0H-7.js} +21 -2
  15. package/dist/errors-Bvln0H-7.js.map +1 -0
  16. package/dist/{fit-BSWY0AYt.js → fit-DhMbQG5O.js} +0 -0
  17. package/dist/fit-DhMbQG5O.js.map +1 -0
  18. package/dist/{html-dom-BQLUntho.js → html-dom-DajBuobe.js} +20 -6
  19. package/dist/html-dom-DajBuobe.js.map +1 -0
  20. package/dist/html.d.ts +3 -3
  21. package/dist/html.d.ts.map +1 -1
  22. package/dist/html.js +7 -4
  23. package/dist/html.js.map +1 -1
  24. package/dist/index.d.ts +6 -6
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +6 -6
  27. package/dist/index.js.map +1 -1
  28. package/dist/inspect.d.ts +1 -1
  29. package/dist/inspect.js +4 -4
  30. package/dist/inspect.js.map +1 -1
  31. package/dist/math.d.ts +1 -1
  32. package/dist/math.d.ts.map +1 -1
  33. package/dist/math.js +98 -2
  34. package/dist/math.js.map +1 -1
  35. package/dist/measure.d.ts +3 -3
  36. package/dist/measure.js +2 -2
  37. package/dist/node.d.ts +14 -6
  38. package/dist/node.d.ts.map +1 -1
  39. package/dist/node.js +13 -5
  40. package/dist/node.js.map +1 -1
  41. package/dist/package-CaOLXSNi.d.ts.map +1 -1
  42. package/dist/{pres-GU-jjBEx.d.ts → pres-BgvxhQ_m.d.ts} +3 -3
  43. package/dist/{pres-GU-jjBEx.d.ts.map → pres-BgvxhQ_m.d.ts.map} +1 -1
  44. package/dist/{presentation-MJe9WDi4.js → presentation-BP1B8PCF.js} +863 -343
  45. package/dist/presentation-BP1B8PCF.js.map +1 -0
  46. package/dist/{presentation-CVaTZw1V.d.ts → presentation-DyIRDKkp.d.ts} +742 -595
  47. package/dist/presentation-DyIRDKkp.d.ts.map +1 -0
  48. package/dist/{presentation-types-BVw0EknS.d.ts → presentation-types-Dj4riuDs.d.ts} +2 -2
  49. package/dist/{presentation-types-BVw0EknS.d.ts.map → presentation-types-Dj4riuDs.d.ts.map} +1 -1
  50. package/dist/read.d.ts +5 -5
  51. package/dist/read.d.ts.map +1 -1
  52. package/dist/read.js +3 -3
  53. package/dist/script.d.ts +2 -2
  54. package/dist/script.d.ts.map +1 -1
  55. package/dist/script.js +0 -0
  56. package/dist/script.js.map +1 -1
  57. package/dist/{shapes-CaOgR0i1.js → shapes-DMThLsZN.js} +689 -1032
  58. package/dist/shapes-DMThLsZN.js.map +1 -0
  59. package/dist/{slide-BBlElWr9.d.ts → slide-BRAtT8Dl.d.ts} +197 -22
  60. package/dist/slide-BRAtT8Dl.d.ts.map +1 -0
  61. package/dist/{image-size-C8uYE8GJ.js → st-enums-evQG2A7Z.js} +233 -2
  62. package/dist/st-enums-evQG2A7Z.js.map +1 -0
  63. package/dist/{text-B1ZrPEeK.js → text-B6PbMFih.js} +725 -319
  64. package/dist/text-B6PbMFih.js.map +1 -0
  65. package/dist/{units-B-JUKwCS.js → units-C7QM1KgH.js} +2 -2
  66. package/dist/{units-B-JUKwCS.js.map → units-C7QM1KgH.js.map} +1 -1
  67. package/dist/{units-internal-7JcMfYti.js → units-internal-CR8s8YQa.js} +4 -3
  68. package/dist/units-internal-CR8s8YQa.js.map +1 -0
  69. package/dist/zip.d.ts +2 -2
  70. package/dist/zip.js +1 -1
  71. package/dist/zip.js.map +1 -1
  72. package/package.json +40 -29
  73. package/skills/ts-pptx-upstream/SKILL.md +247 -0
  74. package/dist/diagnostics-CQ0JWc9m.d.ts.map +0 -1
  75. package/dist/errors-B22h8Qd0.d.ts.map +0 -1
  76. package/dist/errors-DGE9zh3U.js.map +0 -1
  77. package/dist/fit-BSWY0AYt.js.map +0 -1
  78. package/dist/html-dom-BQLUntho.js.map +0 -1
  79. package/dist/image-size-C8uYE8GJ.js.map +0 -1
  80. package/dist/presentation-CVaTZw1V.d.ts.map +0 -1
  81. package/dist/presentation-MJe9WDi4.js.map +0 -1
  82. package/dist/shapes-CaOgR0i1.js.map +0 -1
  83. package/dist/slide-BBlElWr9.d.ts.map +0 -1
  84. package/dist/text-B1ZrPEeK.js.map +0 -1
  85. package/dist/units-internal-7JcMfYti.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shbernal/ts-pptx",
3
- "version": "2.0.0",
3
+ "version": "3.1.0",
4
4
  "author": {
5
5
  "name": "shbernal",
6
6
  "url": "https://github.com/shbernal/"
@@ -72,24 +72,25 @@
72
72
  }
73
73
  },
74
74
  "files": [
75
- "dist"
75
+ "dist",
76
+ "skills"
76
77
  ],
77
78
  "types": "./dist/index.d.ts",
78
79
  "scripts": {
79
80
  "build": "tsdown",
80
81
  "build:dev": "tsdown --config tsdown.dev.config.ts",
81
- "prepare": "lefthook install",
82
+ "prepare": "node scripts/install-hooks.mjs",
82
83
  "start": "pnpm run watch",
83
84
  "watch": "tsdown --watch",
84
85
  "watch:dev": "tsdown --config tsdown.dev.config.ts --watch",
85
86
  "ensure-dist": "node scripts/ensure-dist.mjs",
86
- "verify": "pnpm run ensure-dist && pnpm run typecheck && pnpm run typecheck:scripts && pnpm run typecheck:test && pnpm run backlog:validate && pnpm run raw-xml:check && vitest run",
87
- "verify:full": "pnpm run verify && pnpm run package:lint && pnpm run test:package",
88
- "check:static": "pnpm run ensure-dist && pnpm run lint && pnpm run format:check && pnpm run typecheck && pnpm run typecheck:scripts && pnpm run typecheck:test && pnpm run backlog:validate && pnpm run raw-xml:check",
89
- "check:package": "pnpm run ensure-dist && pnpm run package:lint && pnpm run test:package",
87
+ "verify": "pnpm run ensure-dist && pnpm run typecheck && pnpm run typecheck:scripts && pnpm run typecheck:test && pnpm run raw-xml:check && pnpm run path-refs:check && pnpm run docs:build && vitest run",
88
+ "verify:full": "pnpm run verify && pnpm run script:roundtrip:all && pnpm run package:lint && pnpm run test:package && pnpm run bundle-size:check",
89
+ "check:static": "pnpm run ensure-dist && pnpm run lint && pnpm run format:check && pnpm run typecheck && pnpm run typecheck:scripts && pnpm run typecheck:test && pnpm run raw-xml:check && pnpm run path-refs:check && pnpm run docs:build",
90
+ "check:package": "pnpm run ensure-dist && pnpm run package:lint && pnpm run test:package && pnpm run bundle-size:check",
90
91
  "test": "pnpm run ensure-dist && vitest run",
91
92
  "test:unit": "pnpm run ensure-dist && vitest run test/regression",
92
- "test:schema": "pnpm run ensure-dist && vitest run test/schema-validation.test.mjs",
93
+ "test:schema": "pnpm run ensure-dist && vitest run test/schema-validation.test.js",
93
94
  "schema:versions": "pnpm run ensure-dist && node scripts/ooxml-version-probe.mjs",
94
95
  "test:read": "pnpm run ensure-dist && vitest run test/read",
95
96
  "test:read:emit": "pnpm run ensure-dist && node scripts/read-emit-roundtrip.mjs",
@@ -97,41 +98,50 @@
97
98
  "read:census": "pnpm run ensure-dist && node scripts/read-blindness-census.mjs",
98
99
  "read:append-ceiling": "pnpm run ensure-dist && node scripts/append-ceiling.mjs",
99
100
  "script:roundtrip": "pnpm run ensure-dist && node scripts/script-roundtrip.mjs",
101
+ "script:roundtrip:a": "pnpm run ensure-dist && node scripts/script-roundtrip.mjs --tier a",
102
+ "script:roundtrip:all": "pnpm run script:roundtrip && pnpm run script:roundtrip:a",
103
+ "script:census": "pnpm run ensure-dist && node scripts/note-census.mjs",
100
104
  "test:watch": "vitest --watch test/regression",
101
105
  "test:coverage": "pnpm run ensure-dist && vitest run --coverage",
102
106
  "coverage:probe": "pnpm run ensure-dist && vitest run --coverage --coverage.reportsDirectory=coverage/probe --coverage.reporter=json --coverage.reporter=json-summary --coverage.reporter=text-summary --coverage.thresholds.statements=0 --coverage.thresholds.branches=0 --coverage.thresholds.functions=0 --coverage.thresholds.lines=0",
107
+ "coverage:merge": "node scripts/coverage-merge.mjs",
108
+ "coverage:gate": "pnpm run coverage:merge && node scripts/coverage-gate.mjs",
103
109
  "test:package": "node scripts/package-smoke.mjs",
110
+ "test:browser": "pnpm run ensure-dist && pnpm run demos:build:vite && playwright test",
104
111
  "demos:build": "pnpm run ensure-dist && pnpm --dir demos/showcases run build",
112
+ "demos:build:vite": "pnpm --dir demos/vite-demo run build",
105
113
  "test:com": "pnpm run ensure-dist && node scripts/powerpoint-com-smoke.mjs",
106
114
  "typecheck": "tsc -p tsconfig.json --noEmit",
107
115
  "typecheck:scripts": "tsc -p tsconfig.scripts.json --noEmit",
108
116
  "typecheck:test": "tsc -p tsconfig.test.json --noEmit",
109
- "lint": "eslint . --no-warn-ignored",
117
+ "lint": "oxlint .",
110
118
  "lint:fix": "pnpm run lint --fix",
111
- "format:run": "prettier \"src/**/*.ts\" package.json pnpm-workspace.yaml typedoc.docs.json tsconfig*.json docs/tsconfig.json docs/.vitepress/config.mts eslint.config.mjs tsdown.config.ts tsdown.dev.config.ts vitest.config.ts \"scripts/**/*.mjs\" \"test/**/*.mjs\" \"test/**/*.js\" \".github/**/*.yml\"",
119
+ "format:run": "oxfmt \"src/**/*.ts\" package.json tools/api-docs/package.json .oxfmtrc.jsonc pnpm-workspace.yaml typedoc.docs.json tsconfig.base.json tsconfig.json tsconfig.scripts.json tsconfig.test.json docs/tsconfig.json docs/.vitepress/config.mts .oxlintrc.jsonc tsdown.config.ts tsdown.dev.config.ts vitest.config.ts playwright.config.ts \"scripts/**/*.mjs\" \"test/**/*.mjs\" \"test/**/*.js\" \".github/**/*.yml\"",
112
120
  "format": "pnpm run format:run --write",
113
121
  "format:check": "pnpm run format:run --check",
114
122
  "package:lint": "node scripts/package-lint.mjs",
115
- "backlog": "node scripts/backlog-ledger.mjs",
116
- "backlog:list": "node scripts/backlog-ledger.mjs list",
117
- "backlog:validate": "node scripts/backlog-ledger.mjs validate",
123
+ "bundle-size:check": "pnpm run ensure-dist && node scripts/bundle-size-ratchet.mjs",
124
+ "bundle-size:freeze": "pnpm run ensure-dist && node scripts/bundle-size-ratchet.mjs --freeze",
125
+ "bundle-size:list": "pnpm run ensure-dist && node scripts/bundle-size-ratchet.mjs --list",
118
126
  "raw-xml:check": "node scripts/raw-xml-ratchet.mjs",
119
127
  "raw-xml:freeze": "node scripts/raw-xml-ratchet.mjs --freeze",
120
128
  "raw-xml:list": "node scripts/raw-xml-ratchet.mjs --list",
129
+ "path-refs:check": "node scripts/path-refs.mjs",
130
+ "path-refs:list": "node scripts/path-refs.mjs --list",
121
131
  "byte-identity:baseline": "node scripts/byte-identity.mjs baseline",
122
132
  "byte-identity:check": "node scripts/byte-identity.mjs check",
123
133
  "prepack": "pnpm run build",
124
134
  "check-validator": "node tools/ooxml-validator/check-updates.js",
125
- "docs:init": "node scripts/run-python.mjs scripts/docs-init.py",
126
- "docs:list": "node scripts/run-python.mjs scripts/docs-list.py",
127
- "docs:new": "node scripts/run-python.mjs scripts/docs-new.py",
135
+ "docs:init": "node scripts/docs-init.mjs",
136
+ "docs:list": "node scripts/docs-list.mjs",
137
+ "docs:new": "node scripts/docs-new.mjs",
128
138
  "docs:api": "node scripts/docs-api.mjs",
129
139
  "docs:llms": "node scripts/generate-llms-docs.mjs",
130
- "docs:index": "node scripts/run-python.mjs scripts/docs-index.py",
131
- "docs:check": "pnpm run docs:api && node scripts/run-python.mjs scripts/docs-check.py",
140
+ "docs:index": "node scripts/docs-index.mjs",
141
+ "docs:check": "pnpm run docs:api && node scripts/docs-check.mjs",
132
142
  "docs:prepare": "pnpm run docs:api && pnpm run docs:llms && pnpm run docs:index",
133
143
  "docs:dev": "pnpm run docs:prepare && vitepress dev docs",
134
- "docs:build": "pnpm run docs:prepare && node scripts/run-python.mjs scripts/docs-check.py && vitepress build docs",
144
+ "docs:build": "pnpm run docs:prepare && node scripts/docs-check.mjs && vitepress build docs && node scripts/docs-check.mjs --dist",
135
145
  "docs:preview": "vitepress preview docs"
136
146
  },
137
147
  "dependencies": {
@@ -157,28 +167,29 @@
157
167
  "devDependencies": {
158
168
  "@arethetypeswrong/cli": "^0.18.5",
159
169
  "@arethetypeswrong/core": "^0.18.5",
160
- "@eslint/js": "^10.0.1",
170
+ "@playwright/test": "^1.62.1",
161
171
  "@types/node": "^26.1.1",
162
172
  "@types/opentype.js": "^1.3.10",
163
- "@typescript-eslint/eslint-plugin": "^8.64.0",
164
- "@typescript-eslint/parser": "^8.64.0",
165
173
  "@vitest/coverage-v8": "^4.1.10",
166
- "eslint": "^10.7.0",
167
- "eslint-config-prettier": "^10.1.8",
174
+ "ast-v8-to-istanbul": "^1.0.5",
175
+ "esbuild": "^0.28.1",
168
176
  "fast-xml-parser": "^5.10.1",
169
177
  "happy-dom": "^20.11.1",
178
+ "istanbul-lib-coverage": "^3.2.2",
179
+ "istanbul-lib-report": "^3.0.1",
180
+ "istanbul-reports": "^3.2.0",
170
181
  "jszip": "^3.10.1",
171
182
  "lefthook": "^2.1.10",
172
183
  "mathml2omml": "^0.5.0",
173
- "prettier": "^3.9.5",
184
+ "oxfmt": "^0.62.0",
185
+ "oxlint": "^1.77.0",
186
+ "oxlint-tsgolint": "7.0.2001",
174
187
  "publint": "^0.3.21",
175
188
  "temml": "^0.13.3",
176
189
  "tsdown": "^0.22.9",
177
190
  "tslib": "^2.8.1",
178
- "typedoc": "^0.28.20",
179
- "typedoc-plugin-markdown": "^4.12.0",
180
- "typescript": "^6.0.3",
181
- "typescript-eslint": "^8.64.0",
191
+ "typescript": "^7.0.2",
192
+ "typescript-6": "npm:typescript@6.0.3",
182
193
  "vitepress": "^1.6.4",
183
194
  "vitest": "^4.1.10",
184
195
  "yaml": "^2.9.0"
@@ -0,0 +1,247 @@
1
+ ---
2
+ name: ts-pptx-upstream
3
+ description: Report a ts-pptx (@shbernal/ts-pptx) bug, gap, or wrong output to its GitHub tracker from a project that depends on it. Use when ts-pptx throws an InternalError or an error whose message points at the issue tracker, when it cannot read a .pptx that opens cleanly in PowerPoint, when it writes a deck PowerPoint repairs or renders wrong, when a construct does not survive a round trip, when its types block correct code, or whenever you are about to write a workaround for ts-pptx behaving incorrectly. Filing the bug is the fix; the workaround is the stopgap.
4
+ ---
5
+
6
+ # Reporting a ts-pptx problem upstream
7
+
8
+ You are in a project that *uses* `@shbernal/ts-pptx`, not the project that builds it.
9
+ This skill is how a defect you hit here becomes a permanent regression test there.
10
+
11
+ The library's maintainers turn reports with a minimal reproduction into fixtures and
12
+ regression cases, which is the only mechanism that guarantees a bug is never
13
+ reintroduced. A report without a reproduction is a wish; a report with one is a fix.
14
+
15
+ **The single most valuable thing you can do is reduce the failure to a script that
16
+ builds its own deck.** Everything below is in service of that.
17
+
18
+ ## 1. Decide whether it is actually ours
19
+
20
+ Every failure ts-pptx raises is a `TsPptxError` in one of five classes, and the class
21
+ already answers *whose problem is this*. Catch it and read `err.name` and `err.code`
22
+ — `code` is a stable `area/condition` string and is API; the message is not, so never
23
+ branch on its wording.
24
+
25
+ | class | whose bug | report it? |
26
+ | ------------------------- | ---------------- | ------------------------------------------------------------------- |
27
+ | `InternalError` | **ts-pptx** | Always. The library says so itself in the message. |
28
+ | `PackageReadError` | usually the file | Only if the file opens **cleanly in PowerPoint**. Then it's our gap. |
29
+ | `MediaError` | usually the asset| Only if the image/font/AV loads fine elsewhere. |
30
+ | `UnsupportedFeatureError` | nobody's, yet | If PowerPoint can express it, this is a feature request worth filing.|
31
+ | `InvalidOptionError` | usually you | Only if the deck it refused is one PowerPoint can express. |
32
+
33
+ **The supported bar is "the output opens cleanly in Microsoft PowerPoint."** That is
34
+ the project's own stated standard, and it is the test to apply in both directions: a
35
+ file PowerPoint opens but ts-pptx rejects is our gap, and a file ts-pptx writes that
36
+ PowerPoint repairs is our bug.
37
+
38
+ Not every defect throws. These are ours too, and are worth reporting:
39
+
40
+ - Output PowerPoint opens with a **repair prompt** (`0x80070570` and friends), or
41
+ renders differently than intended.
42
+ - A **round trip that loses a construct**: read a deck, write it back, something is
43
+ gone or changed. If the loss surfaces as a conversion fidelity note, capture its
44
+ **construct key** (e.g. `table.rowAuto`) — the form asks for it and it is the
45
+ fastest thing a maintainer can search on.
46
+ - The **read side cannot see what the write side authors** — a property ts-pptx emits
47
+ but has no accessor to read back. This is the strongest case a gap can make, and it
48
+ has its own form (see step 5).
49
+ - A `Diagnostic` that fires when it should not, does not fire when it should, or
50
+ carries the wrong `code`.
51
+ - Types that make correct code fail to compile, or admit code that throws at runtime.
52
+ - Documented behaviour that does not match observed behaviour.
53
+
54
+ Two things are **out of active maintenance scope** and will usually be closed — check
55
+ before spending effort on a reproduction:
56
+
57
+ - Live-DOM / browser-layout features, i.e. anything whose answer comes from a
58
+ *rendered* page (real `offsetWidth` after layout, the resolved cascade). Converting
59
+ an HTML `<table>` is *not* in this category.
60
+ - Third-party office-suite interop quirks that appear only after the file has been
61
+ round-tripped through another application, when the package ts-pptx wrote is itself
62
+ valid OOXML.
63
+
64
+ Issues and pull requests in those two areas are still welcome; just say so in the
65
+ report so nobody triages it as a regression.
66
+
67
+ Also: ts-pptx is **ESM, Node-first**. Reports about `require()`, a CJS build, or an
68
+ IIFE/global browser bundle will be closed — those are deliberately not supported.
69
+
70
+ ## 2. Collect the facts
71
+
72
+ ```bash
73
+ node -p "require('@shbernal/ts-pptx/package.json').version" # exact installed version
74
+ node -v # runtime (>=24 required)
75
+ ```
76
+
77
+ Capture, verbatim: `err.name`, `err.code`, the full message, `err.detail` if present,
78
+ and the stack. Do not paraphrase the message — the throw site is often identifiable
79
+ from its exact text, and `detail` is the structured context the site chose to attach.
80
+
81
+ If the failure is about how the file *renders* or whether PowerPoint repairs it, note
82
+ the PowerPoint version and platform too. For an API-shape report it is meaningless;
83
+ for a render report it is load-bearing.
84
+
85
+ ## 3. Build a minimal reproduction
86
+
87
+ **Never attach or paste the user's deck.** A presentation in a real project carries
88
+ client names, unreleased strategy, pricing, and internal logos. Treat every `.pptx`
89
+ in this repo as confidential unless the user tells you otherwise, and never upload one
90
+ to a public tracker — including "just a screenshot of the slide". This is the same rule
91
+ the project already applies to its own filings: describe a downstream consumer's need
92
+ anonymously, never its name, its deck or client names, or a path from its tree.
93
+
94
+ Instead, write a self-contained script that *constructs* its input and fails:
95
+
96
+ ```ts
97
+ import TsPptx from '@shbernal/ts-pptx'
98
+
99
+ // build the smallest deck that shows the problem
100
+ const pptx = new TsPptx()
101
+ const slide = pptx.addSlide()
102
+ slide.addText('x', { x: 1, y: 1, w: 8, h: 1 /* the option that triggers it */ })
103
+
104
+ const bytes = await pptx.write({ outputType: 'uint8array' })
105
+ // ...then whatever exposes the defect: read it back, inspect it, open it.
106
+ ```
107
+
108
+ For a read-side or round-trip defect, generate the input with the write side first, so
109
+ the script still owns everything it touches:
110
+
111
+ ```ts
112
+ import { Presentation } from '@shbernal/ts-pptx/read'
113
+
114
+ const pres = await Presentation.load(bytes) // bytes built above, not the user's file
115
+ // ...the accessor that returns the wrong thing, or throws
116
+ ```
117
+
118
+ Then cut it down: remove slides, shapes, options, and styling one at a time, re-running
119
+ after each cut, until removing anything more makes the failure disappear. What is left
120
+ is the report.
121
+
122
+ If the failure only reproduces with a *specific deck* you cannot share, do not ask for
123
+ permission to share it — file without the file. Say exactly that in the report and
124
+ describe the structural feature you believe is responsible (a grouped shape, a chart
125
+ part, a custom table style, a particular namespace prefix, a layout inherited from a
126
+ master). A maintainer can usually author a fixture from that description in PowerPoint;
127
+ they can never unsee an attachment.
128
+
129
+ If you need a file attached, build one: reproduce the *structure* you suspect in a deck
130
+ you generate, with invented values. A synthesized file is always safe to attach. A
131
+ redacted one is not yours to judge — redaction fails quietly, and a public tracker is
132
+ permanent.
133
+
134
+ ## 4. Check it is not already filed
135
+
136
+ ```bash
137
+ gh issue list --repo shbernal/ts-pptx --state all --limit 20 --search "<distinctive phrase>"
138
+ ```
139
+
140
+ Search the error `code` (`table/invalid-border`, `oxml/node-has-no-document`) or the
141
+ distinctive part of the message — not your description of it. Codes are stable and
142
+ searchable in a way prose is not. If an open issue matches, add your reproduction as a
143
+ comment instead of opening a duplicate; if a closed one matches, reopen the conversation
144
+ there with your version and Node version.
145
+
146
+ ## 5. Pick the form, then file it
147
+
148
+ **File it yourself. Do not stop to ask for permission.** A report whose reproduction
149
+ builds its own deck carries nothing of the user's into the tracker, so there is nothing
150
+ for them to weigh; interrupting them to approve a synthetic script is a question with
151
+ only one sensible answer. A duplicate or a thin report is a small cost to a maintainer.
152
+ A bug that is never filed because the moment passed costs everyone, permanently.
153
+
154
+ This rests entirely on step 3 holding: the reproduction constructs its own input, and no
155
+ file from this project is attached. When you cannot manage a self-contained reproduction,
156
+ the answer is a thinner report — the prose description from step 3 — not a question and
157
+ not an attachment.
158
+
159
+ Three forms; pick by what you are reporting:
160
+
161
+ | form | for |
162
+ | ------------------ | ------------------------------------------------------------------------- |
163
+ | `agent-report.yml` | you hit this while using the library in another project — start here |
164
+ | `bug.yml` | wrong output, a repair prompt, a regression, a fidelity limit |
165
+ | `api-gap.yml` | the OOXML carries it but no accessor reaches it |
166
+
167
+ If none fits, file a blank issue rather than bending one of them — blank issues are
168
+ enabled deliberately.
169
+
170
+ `gh` defaults to the *current* repository — which here is the consumer's, not ts-pptx's.
171
+ Always pass `--repo shbernal/ts-pptx` explicitly, or you will file the bug into the wrong
172
+ tracker.
173
+
174
+ ```bash
175
+ gh issue create --repo shbernal/ts-pptx \
176
+ --title "<InternalError|reads|writes|round-trip|types>: <one specific symptom>" \
177
+ --label agent-reported \
178
+ --body-file .tmp/ts-pptx-report.md
179
+ ```
180
+
181
+ The web form (`agent-report.yml`) is what the error message links to; `gh` does not apply
182
+ issue forms, so mirror its sections in the body file so both routes land the same shape:
183
+
184
+ ````markdown
185
+ ### ts-pptx version
186
+ <x.y.z>
187
+
188
+ ### Node.js version
189
+ <vXX.Y.Z>
190
+
191
+ ### Operating system
192
+ <Windows 11 x64 / macOS 15 arm64 / Ubuntu 24.04 x64>
193
+
194
+ ### PowerPoint version
195
+ <only if the report depends on how PowerPoint renders or repairs the file>
196
+
197
+ ### Error class and code
198
+ <InternalError / oxml/node-has-no-document> (or: no error thrown — wrong output)
199
+
200
+ ### What happened
201
+ <observed>
202
+
203
+ ### What should have happened
204
+ <expected, and why you believe that — an ECMA-376 clause, what PowerPoint itself does
205
+ with the same input, or what the docs promise>
206
+
207
+ ### Minimal reproduction
208
+ ```js
209
+ <the script from step 3>
210
+ ```
211
+
212
+ ### Error output
213
+ ```
214
+ <verbatim message, detail, and stack>
215
+ ```
216
+
217
+ ### Attached file
218
+ <none / synthesized — describe how it was generated>
219
+ ````
220
+
221
+ Tell the user the issue number and URL once it exists — after the fact, as a result, not
222
+ as a request. They should be able to read what you filed and close it if they disagree.
223
+
224
+ ## 6. Then, and only then, write the workaround
225
+
226
+ Filing does not unblock the user. Once the issue is open, implement the workaround in
227
+ this project and leave a comment pointing at the issue, so the stopgap is removable when
228
+ the fix ships:
229
+
230
+ ```ts
231
+ // Workaround for ts-pptx#<N> — remove once fixed upstream.
232
+ ```
233
+
234
+ ## If `gh` is unavailable
235
+
236
+ Print the assembled report and this URL, and ask the user to paste it in:
237
+
238
+ <https://github.com/shbernal/ts-pptx/issues/new?template=agent-report.yml>
239
+
240
+ ## Keeping this skill current
241
+
242
+ This file ships inside the package, so the copy in `node_modules` always matches the
243
+ installed version. If it was installed into an agent directory, refresh it with:
244
+
245
+ ```bash
246
+ npx skills update ts-pptx-upstream
247
+ ```
@@ -1 +0,0 @@
1
- {"version":3,"file":"diagnostics-CQ0JWc9m.d.ts","names":[],"sources":["../src/types/zoom.ts","../src/types/internal.ts","../src/runtime/types.ts","../src/units.ts","../src/presentation.ts","../src/diagnostics.ts"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors-B22h8Qd0.d.ts","names":[],"sources":["../src/codes.ts","../src/errors.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAgCY;;;;;;;KAuHA;;;;;;KAiIA;;;;;;KAsBA;;;;;;;KA8BA;;;;;;KAiBA;;KAWA,YACX,yBAAyB,8BAA8B,uBAAuB,iBAAiB;;KAGpF,aAAa,iBAAiB;;;;UCvUzB,2BAA2B;;;;;WAKlC,SAAS,SAAS;;;;;;;;;cAUf,oBAAoB;;WAEvB,MAAM;;WAGN,SAAS,SAAS;;;;;;EAOf,YAAA,MAAM,WAAW,iBAAiB,UAAU;;;;;;;;;;cAmB5C,2BAA2B;WACtB,MAAM;EACX,YAAA,MAAM,wBAAwB,iBAAiB,UAAU;;;;;;;;;;;cAczD,gCAAgC;WAC3B,MAAM;EACX,YAAA,MAAM,6BAA6B,iBAAiB,UAAU;;;;;;;;;;cAa9D,yBAAyB;WACpB,MAAM;EACX,YAAA,MAAM,sBAAsB,iBAAiB,UAAU;;;;;;;;;cAYvD,mBAAmB;WACd,MAAM;EACX,YAAA,MAAM,gBAAgB,iBAAiB,UAAU;;;;;;;;;cAYjD,sBAAsB;WACjB,MAAM;EACX,YAAA,MAAM,mBAAmB,iBAAiB,UAAU"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors-DGE9zh3U.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * The library's error taxonomy.\n *\n * Every failure ts-pptx raises is a {@link TsPptxError} carrying a stable {@link ErrorCode}, so a\n * consumer can tell *\"you passed a bad coordinate\"* from *\"this font file is corrupt\"* from\n * *\"these bytes are not a package\"* without matching on message substrings. The classes are a\n * deliberately flat, coarse bucket — five of them — and the code carries the specificity. Do not\n * add a class per throw site; add a code.\n *\n * **The class and the code are API. The message is not.** Branch on `instanceof` and on `code`;\n * the wording behind them is free to improve in any release. Every error remains an\n * `instanceof Error`, so existing `catch` blocks keep working.\n *\n * Which class to reach for:\n *\n * | class | the failure is | who fixes it |\n * |---|---|---|\n * | {@link InvalidOptionError} | the caller passed something unusable | the caller's code |\n * | {@link UnsupportedFeatureError} | a well-formed request this build/runtime/shape cannot express | the caller's expectations, or the environment |\n * | {@link PackageReadError} | the input bytes are not a readable package | the input file |\n * | {@link MediaError} | a referenced image/font/AV resource would not load or decode | the resource |\n * | {@link InternalError} | an invariant of the library itself did not hold | ts-pptx (file a bug) |\n *\n * @see `docs/errors.md` for the consumer-facing contract.\n * @see `codes.ts` for the code vocabulary, which is shared with the diagnostic (warning) surface.\n */\n\nimport type {\n\tErrorCode,\n\tInternalErrorCode,\n\tInvalidOptionErrorCode,\n\tMediaErrorCode,\n\tPackageReadErrorCode,\n\tUnsupportedFeatureErrorCode,\n} from './codes.js'\n\n/** Extra context accepted by every {@link TsPptxError} constructor. */\nexport interface TsPptxErrorOptions extends ErrorOptions {\n\t/**\n\t * Structured context for the condition, when a site has any to give. Mirrors `Diagnostic.detail`\n\t * so the same condition looks the same whether it was warned or thrown.\n\t */\n\treadonly detail?: Readonly<Record<string, unknown>>\n}\n\n/**\n * Base class for every error ts-pptx throws.\n *\n * Catch this to catch anything from the library; narrow with `instanceof` on a subclass, or branch\n * on {@link TsPptxError.code}, to react to something specific. It is never thrown directly — every\n * site picks one of the five subclasses.\n */\nexport class TsPptxError extends Error {\n\t/** Stable condition identifier — see {@link ErrorCode}. Branch on this, not on the text. */\n\treadonly code: ErrorCode\n\n\t/** Structured context for the condition, when the throwing site had any to give. */\n\treadonly detail?: Readonly<Record<string, unknown>>\n\n\t/**\n\t * @param code - the stable condition identifier\n\t * @param message - explanation for a human reading a stack trace; not part of the contract\n\t * @param options - standard `cause`, plus optional structured `detail`\n\t */\n\tconstructor(code: ErrorCode, message: string, options?: TsPptxErrorOptions) {\n\t\tsuper(message, options)\n\t\t// `name` drives how a stack trace and `console.log` label the error, and the default from\n\t\t// `Error` would say \"Error\" for every subclass. Derive it from the constructor so a subclass\n\t\t// never has to remember to set it.\n\t\tthis.name = new.target.name\n\t\tthis.code = code\n\t\tif (options?.detail !== undefined) this.detail = options.detail\n\t}\n}\n\n/**\n * The caller passed something the library cannot use — a non-finite coordinate, an unknown enum\n * value, a required option left out, a value outside its legal range.\n *\n * This is the error behind the project's throw-rather-than-coerce policy: emitting a degenerate\n * result (a zero-size shape, a silently dropped option) hides the mistake, so the library refuses\n * instead. Reaching one means the calling code needs to change.\n */\nexport class InvalidOptionError extends TsPptxError {\n\tdeclare readonly code: InvalidOptionErrorCode\n\tconstructor(code: InvalidOptionErrorCode, message: string, options?: TsPptxErrorOptions) {\n\t\tsuper(code, message, options)\n\t}\n}\n\n/**\n * The request is well-formed but cannot be expressed here: an optional peer dependency is not\n * installed, the runtime lacks a capability, or the shape/feature has no OOXML representation the\n * library emits.\n *\n * Distinct from {@link InvalidOptionError} because the caller did nothing wrong — the answer is\n * \"not here\", not \"not like that\" — and the fix is usually to install something or pick a different\n * approach rather than to correct a value.\n */\nexport class UnsupportedFeatureError extends TsPptxError {\n\tdeclare readonly code: UnsupportedFeatureErrorCode\n\tconstructor(code: UnsupportedFeatureErrorCode, message: string, options?: TsPptxErrorOptions) {\n\t\tsuper(code, message, options)\n\t}\n}\n\n/**\n * The bytes handed to the library are not a package it can read, or a part inside one is\n * structurally malformed — a missing `[Content_Types].xml`, a relationship pointing at nothing, a\n * slide part with no root element.\n *\n * Always about *input*. A problem with something the library is being asked to produce is an\n * {@link InvalidOptionError}.\n */\nexport class PackageReadError extends TsPptxError {\n\tdeclare readonly code: PackageReadErrorCode\n\tconstructor(code: PackageReadErrorCode, message: string, options?: TsPptxErrorOptions) {\n\t\tsuper(code, message, options)\n\t}\n}\n\n/**\n * A referenced image, font, or audio/video resource could not be fetched, read, or decoded.\n *\n * Kept apart from {@link PackageReadError} because the failure is in a resource the deck *points\n * at*, not in the package structure — a broken URL, an unreadable path, a corrupt font file — and\n * a consumer typically retries or substitutes rather than rejecting the deck.\n */\nexport class MediaError extends TsPptxError {\n\tdeclare readonly code: MediaErrorCode\n\tconstructor(code: MediaErrorCode, message: string, options?: TsPptxErrorOptions) {\n\t\tsuper(code, message, options)\n\t}\n}\n\n/**\n * An invariant the library maintains itself did not hold.\n *\n * No consumer input should be able to produce one, which is the whole reason it is a separate\n * class: seeing it means the bug is in ts-pptx, and the useful response is to file it rather than\n * to keep adjusting the input.\n */\nexport class InternalError extends TsPptxError {\n\tdeclare readonly code: InternalErrorCode\n\tconstructor(code: InternalErrorCode, message: string, options?: TsPptxErrorOptions) {\n\t\tsuper(code, message, options)\n\t}\n}\n"],"mappings":";;;;;;;;AAoDA,IAAa,cAAb,cAAiC,MAAM;;CAEtC;;CAGA;;;;;;CAOA,YAAY,MAAiB,SAAiB,SAA8B;EAC3E,MAAM,SAAS,OAAO;EAItB,KAAK,OAAO,WAAW;EACvB,KAAK,OAAO;EACZ,IAAI,SAAS,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;CAC1D;AACD;;;;;;;;;AAUA,IAAa,qBAAb,cAAwC,YAAY;CAEnD,YAAY,MAA8B,SAAiB,SAA8B;EACxF,MAAM,MAAM,SAAS,OAAO;CAC7B;AACD;;;;;;;;;;AAWA,IAAa,0BAAb,cAA6C,YAAY;CAExD,YAAY,MAAmC,SAAiB,SAA8B;EAC7F,MAAM,MAAM,SAAS,OAAO;CAC7B;AACD;;;;;;;;;AAUA,IAAa,mBAAb,cAAsC,YAAY;CAEjD,YAAY,MAA4B,SAAiB,SAA8B;EACtF,MAAM,MAAM,SAAS,OAAO;CAC7B;AACD;;;;;;;;AASA,IAAa,aAAb,cAAgC,YAAY;CAE3C,YAAY,MAAsB,SAAiB,SAA8B;EAChF,MAAM,MAAM,SAAS,OAAO;CAC7B;AACD;;;;;;;;AASA,IAAa,gBAAb,cAAmC,YAAY;CAE9C,YAAY,MAAyB,SAAiB,SAA8B;EACnF,MAAM,MAAM,SAAS,OAAO;CAC7B;AACD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"fit-BSWY0AYt.js","names":[],"sources":["../src/measure/font-metrics.ts","../src/measure/text-fit.ts","../src/measure/paragraphs.ts","../src/measure/table-fit.ts","../src/measure/fit.ts"],"sourcesContent":["/**\n * PptxGenJS: Write-side font metrics\n *\n * Provides per-glyph advance widths for measured text fit (see `text-fit.ts`).\n * The library has no write-side font registration otherwise; consumers register\n * a face's font file via `pptx.registerFontMetrics(...)` and the measured-fit\n * pass uses these advances to compute a baked `fontScale` for `fit: 'shrink'`.\n *\n * Width is summed from **raw `hmtx` advances with no kerning/shaping** (GPOS/GSUB\n * are deliberately ignored): kerning almost always narrows a line, so raw advances\n * over-estimate width — the conservative direction (shrink a touch too much, never\n * overflow). See `PLAN-measured-text-fit.md` §1.\n */\n\nimport { UnsupportedFeatureError } from '../errors.js'\nimport type { MetricsResolver } from './text-fit.js'\n\n/** Per-(face,bold,italic) advance-width source backed by a parsed font file. */\nexport interface FontMetrics {\n\t/** Font design units per em (advances are in these units). */\n\treadonly unitsPerEm: number\n\t/**\n\t * Advance width of `text` at `sizePt`, in points. Sums raw glyph advances\n\t * (no kerning) and adds `charSpacingPt` after every character, matching how\n\t * PowerPoint widens runs with character spacing.\n\t */\n\tadvanceWidthPt: (text: string, sizePt: number, charSpacingPt?: number) => number\n\t/**\n\t * True if this face's cmap maps code point `cp` to a real glyph (not the\n\t * `.notdef` fallback). Use it to flag source code points the face cannot\n\t * render. Authoritative only for file-backed metrics; the unregistered-font\n\t * heuristic has no cmap and reports every code point as covered.\n\t */\n\thasCodepoint: (cp: number) => boolean\n}\n\n/** Minimal slice of an opentype.js `Font` that `OpentypeFontMetrics` relies on. */\ninterface OpentypeFontLike {\n\tunitsPerEm: number\n\tcharToGlyph: (ch: string) => { advanceWidth?: number } | undefined\n\thasChar: (ch: string) => boolean\n}\n\nclass OpentypeFontMetrics implements FontMetrics {\n\tprivate readonly font: OpentypeFontLike\n\tconstructor(font: OpentypeFontLike) {\n\t\tthis.font = font\n\t}\n\n\tget unitsPerEm(): number {\n\t\treturn this.font.unitsPerEm\n\t}\n\n\tadvanceWidthPt(text: string, sizePt: number, charSpacingPt = 0): number {\n\t\tif (!text) return 0\n\t\t// Sum raw per-glyph `hmtx` advances via cmap (charToGlyph) — deliberately\n\t\t// NOT getAdvanceWidth(), which runs GPOS/GSUB shaping (kerning narrows lines,\n\t\t// and some lookups even throw). Raw advances over-estimate width, the\n\t\t// conservative direction. `for..of` iterates code points (astral-safe).\n\t\tconst scale = sizePt / this.font.unitsPerEm\n\t\tlet width = 0\n\t\tlet count = 0\n\t\tfor (const ch of text) {\n\t\t\tconst glyph = this.font.charToGlyph(ch)\n\t\t\twidth += (glyph?.advanceWidth ?? 0) * scale\n\t\t\tcount++\n\t\t}\n\t\tif (charSpacingPt) width += charSpacingPt * count\n\t\treturn width\n\t}\n\n\thasCodepoint(cp: number): boolean {\n\t\t// opentype's charToGlyph() falls back to .notdef for uncovered code points,\n\t\t// so it can't answer coverage; hasChar() keys on the cmap (glyph index > 0).\n\t\treturn this.font.hasChar(String.fromCodePoint(cp))\n\t}\n}\n\nconst HEURISTIC_UNITS_PER_EM = 1000\n\n/**\n * Advance width of an ASCII/Unicode code point as a fraction of em, for the\n * unregistered-font fallback. Values are biased to **over-estimate** a typical\n * proportional Latin face: over-estimating width is the conservative direction for\n * both solvers (shrink a touch too much / grow a touch too tall, never overflow).\n * Non-Latin code points are treated as full-em (safe for CJK, which is otherwise\n * out of scope; slightly over-wide for accented Latin, which is harmless).\n */\nfunction heuristicCharRatio(ch: string): number {\n\tconst cp = ch.codePointAt(0) ?? 0\n\tif (cp > 0x2e7f) return 1.0 // CJK / full-width and beyond\n\tif (cp > 0xff) return 0.62 // Latin-Extended / accented / misc symbols\n\tif (ch === ' ' || ch === '\\t') return 0.3\n\tif (\"ijl.,'`!|:;\".includes(ch)) return 0.3\n\tif ('ftr()[]{}/\\\\I'.includes(ch)) return 0.4\n\tif (ch === 'm' || ch === 'w') return 0.9\n\tif (ch === 'M' || ch === 'W') return 0.98\n\tif (ch === '@' || ch === '%') return 1.0\n\tif (ch >= 'A' && ch <= 'Z') return 0.72\n\treturn 0.58 // lowercase, digits, default punctuation\n}\n\nclass HeuristicFontMetrics implements FontMetrics {\n\treadonly unitsPerEm = HEURISTIC_UNITS_PER_EM\n\tadvanceWidthPt(text: string, sizePt: number, charSpacingPt = 0): number {\n\t\tif (!text) return 0\n\t\tlet width = 0\n\t\tlet count = 0\n\t\tfor (const ch of text) {\n\t\t\twidth += heuristicCharRatio(ch) * sizePt\n\t\t\tcount++\n\t\t}\n\t\tif (charSpacingPt) width += charSpacingPt * count\n\t\treturn width\n\t}\n\n\thasCodepoint(): boolean {\n\t\treturn true // no cmap to consult; never fabricate a 'missing' glyph\n\t}\n}\n\nlet heuristicSingleton: FontMetrics | undefined\n\n/**\n * Shared, font-independent `FontMetrics` used when a named face has no registered\n * metrics but the deck has opted into measured fit (some other face is registered).\n * Approximate but conservative — see {@link heuristicCharRatio}. A deck with no\n * registered metrics at all does not engage measured fit, so this never fires there.\n */\nexport function getHeuristicFontMetrics(): FontMetrics {\n\tif (!heuristicSingleton) heuristicSingleton = new HeuristicFontMetrics()\n\treturn heuristicSingleton\n}\n\n/** Parse a font file (TTF/OTF) into a `FontMetrics`. Lazily imports opentype.js. */\nexport async function parseFontMetrics(data: Uint8Array): Promise<FontMetrics> {\n\t// Dynamic import keeps opentype.js off the critical/browser path until a\n\t// consumer actually registers metrics.\n\tconst mod = (await import('opentype.js')) as unknown as {\n\t\tparse?: (buf: ArrayBuffer) => OpentypeFontLike\n\t\tdefault?: { parse: (buf: ArrayBuffer) => OpentypeFontLike }\n\t}\n\tconst parse = mod.parse ?? mod.default?.parse\n\tif (typeof parse !== 'function')\n\t\tthrow new UnsupportedFeatureError('font/opentype-unavailable', 'opentype.js: parse() not found in module exports')\n\t// opentype.parse needs a standalone ArrayBuffer view of exactly the font bytes.\n\tconst buf = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer\n\treturn new OpentypeFontMetrics(parse(buf))\n}\n\n/**\n * Registry of `FontMetrics` keyed by `(face, bold, italic)`. Variant advances\n * differ, so bold/italic are stored separately; lookup falls back to the regular\n * variant (then any registered variant of the face) so a deck that only registers\n * the regular weight still measures bold runs approximately rather than not at all.\n */\nexport class FontMetricsRegistry {\n\tprivate readonly map = new Map<string, FontMetrics>()\n\n\tprivate key(face: string, bold: boolean, italic: boolean): string {\n\t\treturn `${face.toLowerCase()}\u0000${bold ? 1 : 0}\u0000${italic ? 1 : 0}`\n\t}\n\n\tset(face: string, metrics: FontMetrics, opts?: { bold?: boolean; italic?: boolean }): void {\n\t\tthis.map.set(this.key(face, !!opts?.bold, !!opts?.italic), metrics)\n\t}\n\n\t/** True if any variant of `face` has registered metrics. */\n\thasFace(face: string): boolean {\n\t\tconst prefix = `${face.toLowerCase()}\u0000`\n\t\tfor (const k of this.map.keys()) if (k.startsWith(prefix)) return true\n\t\treturn false\n\t}\n\n\t/**\n\t * Coverage of code point `cp` for `face`, read from the registered font's cmap:\n\t * `true`/`false` when a variant of `face` is registered (see {@link get} for the\n\t * exact→regular→any-variant fallback), or `undefined` when the face has **no**\n\t * registered metrics — \"unknown\", deliberately distinct from a `false`\n\t * \"registered but not covered\". A coverage audit must not treat an unregistered\n\t * face as fully covered, so callers get `undefined` here rather than the per-face\n\t * {@link FontMetrics.hasCodepoint} boolean (which, for the heuristic fallback,\n\t * always reports covered). Register the face via {@link parseFontMetrics} +\n\t * {@link set} first.\n\t */\n\thasCodepoint(\n\t\tface: string | undefined | null,\n\t\tcp: number,\n\t\topts?: { bold?: boolean; italic?: boolean }\n\t): boolean | undefined {\n\t\treturn this.get(face, opts?.bold, opts?.italic)?.hasCodepoint(cp)\n\t}\n\n\t/** Resolve metrics for a run, falling back from exact variant → regular → any variant. */\n\tget(face: string | undefined | null, bold = false, italic = false): FontMetrics | undefined {\n\t\t// A run with no explicit face inherits the theme font, which we cannot resolve\n\t\t// reliably here — treat as unregistered so measured fit degrades gracefully.\n\t\tif (typeof face !== 'string' || face.length === 0) return undefined\n\t\tconst exact = this.map.get(this.key(face, bold, italic))\n\t\tif (exact) return exact\n\t\tconst regular = this.map.get(this.key(face, false, false))\n\t\tif (regular) return regular\n\t\tconst prefix = `${face.toLowerCase()}\u0000`\n\t\tfor (const [k, v] of this.map) if (k.startsWith(prefix)) return v\n\t\treturn undefined\n\t}\n\n\tget size(): number {\n\t\treturn this.map.size\n\t}\n}\n\n/**\n * A {@link MetricsResolver} backed by a registry: exact metrics for a registered face, otherwise\n * the shared heuristic fallback (with `onHeuristic` told which face it stood in for). A run with\n * no `fontFace` at all resolves to `undefined`, which the callers treat as unmeasurable.\n */\nexport function makeRegistryResolver(\n\tregistry: FontMetricsRegistry,\n\tonHeuristic?: (face: string) => void\n): MetricsResolver {\n\treturn (run) => {\n\t\tconst exact = registry.get(run.fontFace, run.bold, run.italic)\n\t\tif (exact) return exact\n\t\tif (typeof run.fontFace === 'string' && run.fontFace.length > 0) {\n\t\t\tonHeuristic?.(run.fontFace)\n\t\t\treturn getHeuristicFontMetrics()\n\t\t}\n\t\treturn undefined\n\t}\n}\n","/**\n * ts-pptx: Measured text fit (line-break simulator + shrink solver)\n *\n * Computes a baked `fontScale` for `fit: 'shrink'` so overflowing text renders\n * pre-shrunk in headless renderers (and on plain file-open) without a manual\n * edit/resize. See `docs/measured-text-fit.md`.\n *\n * Calibration: every constant here is pinned against PowerPoint-authored fixtures\n * (`test/read/fixtures/autofit-calibration.json`). The model errs **conservative**\n * — it over-estimates width (raw advances, no kerning) and uses the calibrated\n * line pitch — so the computed `fontScale` is ≤ PowerPoint's and the text never\n * overflows in either PowerPoint or LibreOffice.\n */\n\nimport type { FontMetrics } from './font-metrics.js'\n\n/**\n * PowerPoint single-spacing line pitch as a multiple of font size.\n *\n * Calibrated from `autofit-calibration.json` (Deck 1, `autofit-line-metrics`):\n * the per-line `cy` delta is a **font-independent** 1.2117× at 12/18/32 pt across\n * {Aptos, Aptos SemiBold, Calibri, Tahoma, Arial}, and PowerPoint vs LibreOffice\n * agree to ≤ 0.041 pt. See `test/read/fixtures/README.md` → \"Findings\" #1–#2.\n */\nexport const SINGLE_LINE_PITCH = 1.2117\n\n/** PowerPoint's `fontScale` search grid: discrete 2.5% steps (Findings #3). */\nexport const FONT_SCALE_STEP_PCT = 2.5\n\n/** Floor for the shrink search (see `docs/measured-text-fit.md` → Solvers). */\nexport const MIN_FONT_SCALE_PCT = 25\n\n/**\n * Conservative safety factors applied by the shrink solver (not by the pure\n * `measureHeightPt`). Raw `hmtx` advances summed without shaping still pack a line\n * a little tighter than PowerPoint, which lays text out at a device DPI and rounds\n * each glyph advance up — so PowerPoint wraps marginally earlier and its fitted\n * box is marginally taller. Inflating measured width and height by these factors\n * makes the computed `fontScale` ≤ PowerPoint's across the calibration oracle\n * (`test/read/autofit-calibration-oracle.test.mjs`: 0 violations over the Aptos\n * cases). They err on the side of shrinking slightly too much, never overflowing.\n */\nexport const WIDTH_SAFETY_FACTOR = 1.03\nexport const HEIGHT_SAFETY_FACTOR = 1.04\n\n/** One text run with the inputs measurement needs. */\nexport interface FitRun {\n\ttext: string\n\tsizePt: number\n\tbold?: boolean\n\titalic?: boolean\n\tfontFace?: string\n\t/** Character spacing in points (PowerPoint `spc`/1200). */\n\tcharSpacingPt?: number\n}\n\n/** One paragraph: its runs plus vertical-rhythm options. */\nexport interface FitParagraph {\n\truns: FitRun[]\n\t/** Line spacing as a percent of single (100 = single). Ignored if `lineSpacingPts` set. */\n\tlineSpacingPct?: number\n\t/** Exact line spacing in points (overrides `lineSpacingPct`). */\n\tlineSpacingPts?: number\n\tspaceBeforePts?: number\n\tspaceAfterPts?: number\n}\n\n/** Inner box (shape minus insets) the text must fit, in points. */\nexport interface FitBox {\n\tinnerWidthPt: number\n\tinnerHeightPt: number\n\t/**\n\t * `false` when the frame does not wrap (`wrap:false` / `wrap=\"none\"`): text lays\n\t * out as one line per paragraph (no width-wrapping), and the shrink solver must\n\t * additionally keep the widest line within `innerWidthPt`. A too-wide line\n\t * overflows HORIZONTALLY — `normAutofit`'s vertical shrink never catches it, so\n\t * without this check a non-wrapping line is never shrunk and spills out of the box\n\t * in PowerPoint. Defaults to wrapping (`true`), so table cells and `measureText`\n\t * are unaffected.\n\t */\n\twrap?: boolean\n}\n\n/** Resolve the metrics for a run, or `undefined` if the face is not registered. */\nexport type MetricsResolver = (run: FitRun) => FontMetrics | undefined\n\nexport interface ShrinkResult {\n\tfontScalePct: number\n\tlnSpcReductionPct: number\n}\n\nexport type ShrinkOutcome =\n\t| { kind: 'fits' } // already fits at 100% — no shrink needed\n\t| { kind: 'unmeasurable' } // a run's face has no registered metrics — caller degrades\n\t| { kind: 'shrink'; result: ShrinkResult }\n\ninterface WordToken {\n\tkind: 'word'\n\tw: number\n\tcharWidths: number[]\n}\ntype Token = WordToken | { kind: 'space'; w: number } | { kind: 'newline'; w: 0 }\n\nconst isWhitespace = (ch: string): boolean => ch === ' ' || ch === '\\t' || ch === ' ' || /\\s/.test(ch)\n\n/**\n * Tokenize one paragraph at a given `fontScalePct`. Words can span run boundaries\n * (a break is only allowed at whitespace). Returns `null` if any run's face has no\n * registered metrics (the paragraph cannot be measured).\n */\nfunction tokenizeParagraph(\n\tpara: FitParagraph,\n\tresolve: MetricsResolver,\n\tfontScalePct: number,\n\twidthSafety: number\n): Token[] | null {\n\tconst tokens: Token[] = []\n\tlet curWord: WordToken | null = null\n\tconst flushWord = (): void => {\n\t\tif (curWord) {\n\t\t\ttokens.push(curWord)\n\t\t\tcurWord = null\n\t\t}\n\t}\n\tfor (const run of para.runs) {\n\t\tconst metrics = resolve(run)\n\t\tif (!metrics) return null\n\t\tconst scaledSize = run.sizePt * (fontScalePct / 100)\n\t\tconst charSpacingPt = run.charSpacingPt ?? 0\n\t\tfor (const ch of run.text ?? '') {\n\t\t\tif (ch === '\\n') {\n\t\t\t\tflushWord()\n\t\t\t\ttokens.push({ kind: 'newline', w: 0 })\n\t\t\t} else if (isWhitespace(ch)) {\n\t\t\t\tflushWord()\n\t\t\t\t// Tabs are approximated as a single space-width gap (documented P1 gap).\n\t\t\t\tconst w = metrics.advanceWidthPt(ch === '\\t' ? ' ' : ch, scaledSize, charSpacingPt) * widthSafety\n\t\t\t\ttokens.push({ kind: 'space', w })\n\t\t\t} else {\n\t\t\t\tconst w = metrics.advanceWidthPt(ch, scaledSize, charSpacingPt) * widthSafety\n\t\t\t\tif (!curWord) curWord = { kind: 'word', w: 0, charWidths: [] }\n\t\t\t\tcurWord.w += w\n\t\t\t\tcurWord.charWidths.push(w)\n\t\t\t}\n\t\t}\n\t}\n\tflushWord()\n\treturn tokens\n}\n\n/** Greedy `wrap=square` line layout for a tokenized paragraph. */\ninterface LineLayout {\n\tlines: number\n\t/** Width (points) of the widest laid-out line — for natural/widest-line measurement. */\n\tmaxLineWidthPt: number\n}\n\n/**\n * Greedy `wrap=square` line layout: the wrapped line count plus the widest\n * laid-out line width. `maxLineWidthPt` tracks the running line width and records\n * it whenever a line completes (a wrap or the final line), so callers can measure\n * the actual text extent, not just the box width. Trailing whitespace counts\n * toward width (Findings #7), which slightly over-reports — the conservative\n * direction, consistent with the wrap model erring wide.\n */\nfunction countLines(tokens: Token[], innerWidthPt: number): LineLayout {\n\tlet lines = 1\n\tlet lineW = 0\n\tlet maxLineWidthPt = 0\n\tconst finishLine = (): void => {\n\t\tif (lineW > maxLineWidthPt) maxLineWidthPt = lineW\n\t}\n\tfor (const tok of tokens) {\n\t\tif (tok.kind === 'newline') {\n\t\t\tfinishLine()\n\t\t\tlines++\n\t\t\tlineW = 0\n\t\t} else if (tok.kind === 'space') {\n\t\t\t// Trailing whitespace counts toward line width (Findings #7); if it\n\t\t\t// overflows, the line wraps and the space is consumed at the break.\n\t\t\tif (lineW + tok.w > innerWidthPt && lineW > 0) {\n\t\t\t\tfinishLine()\n\t\t\t\tlines++\n\t\t\t\tlineW = 0\n\t\t\t} else {\n\t\t\t\tlineW += tok.w\n\t\t\t}\n\t\t} else if (tok.w <= innerWidthPt) {\n\t\t\tif (lineW + tok.w > innerWidthPt && lineW > 0) {\n\t\t\t\tfinishLine()\n\t\t\t\tlines++\n\t\t\t\tlineW = tok.w\n\t\t\t} else {\n\t\t\t\tlineW += tok.w\n\t\t\t}\n\t\t} else {\n\t\t\t// Over-long unbreakable word → character-wrap (Findings #7).\n\t\t\tif (lineW > 0) {\n\t\t\t\tfinishLine()\n\t\t\t\tlines++\n\t\t\t\tlineW = 0\n\t\t\t}\n\t\t\tfor (const cw of tok.charWidths) {\n\t\t\t\tif (lineW + cw > innerWidthPt && lineW > 0) {\n\t\t\t\t\tfinishLine()\n\t\t\t\t\tlines++\n\t\t\t\t\tlineW = cw\n\t\t\t\t} else {\n\t\t\t\t\tlineW += cw\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfinishLine()\n\treturn { lines, maxLineWidthPt }\n}\n\n/** Max run size in a paragraph (line height follows the tallest run — Findings #7). */\nfunction maxRunSizePt(para: FitParagraph): number {\n\tlet max = 0\n\tfor (const run of para.runs) if (run.sizePt > max) max = run.sizePt\n\treturn max\n}\n\n/** Laid-out result of {@link measureLayout}: total height (points) + wrapped line count. */\nexport interface LayoutResult {\n\theightPt: number\n\tlineCount: number\n\t/**\n\t * Width (points) of the widest laid-out line across all paragraphs. Useful for\n\t * tightening a box to the actual text extent or measuring a natural (single-line)\n\t * width. Carries the same `widthSafety` inflation as the wrap decision, so a box\n\t * set to this width will not re-wrap. Conservative (slightly wide).\n\t */\n\twidestLineWidthPt: number\n}\n\n/**\n * Lay out `paragraphs` at a given scale/reduction and return the total height\n * (points) and wrapped line count, or `null` if any paragraph is unmeasurable.\n * Errs tall (conservative): includes space-before/after and the calibrated line\n * pitch. The single source of truth for both {@link measureHeightPt} (the solvers)\n * and the public layout-time measure API.\n */\nexport function measureLayout(\n\tparagraphs: FitParagraph[],\n\tinnerWidthPt: number,\n\tresolve: MetricsResolver,\n\tfontScalePct: number,\n\tlnSpcReductionPct: number,\n\twidthSafety = 1\n): LayoutResult | null {\n\tif (innerWidthPt <= 0) return null\n\tlet total = 0\n\tlet lineCount = 0\n\tlet widestLineWidthPt = 0\n\tfor (const para of paragraphs) {\n\t\tconst tokens = tokenizeParagraph(para, resolve, fontScalePct, widthSafety)\n\t\tif (tokens === null) return null\n\t\tconst { lines, maxLineWidthPt } = countLines(tokens, innerWidthPt)\n\t\tlineCount += lines\n\t\tif (maxLineWidthPt > widestLineWidthPt) widestLineWidthPt = maxLineWidthPt\n\t\tconst scaledMax = maxRunSizePt(para) * (fontScalePct / 100)\n\t\tlet lineHeight =\n\t\t\tpara.lineSpacingPts != null && para.lineSpacingPts > 0\n\t\t\t\t? para.lineSpacingPts\n\t\t\t\t: SINGLE_LINE_PITCH * scaledMax * ((para.lineSpacingPct ?? 100) / 100)\n\t\tlineHeight *= 1 - lnSpcReductionPct / 100\n\t\ttotal += lines * lineHeight + (para.spaceBeforePts ?? 0) + (para.spaceAfterPts ?? 0)\n\t}\n\treturn { heightPt: total, lineCount, widestLineWidthPt }\n}\n\n/**\n * Total laid-out height (points) at a given scale/reduction, or `null` if any\n * paragraph is unmeasurable. Thin wrapper over {@link measureLayout}.\n */\nexport function measureHeightPt(\n\tparagraphs: FitParagraph[],\n\tinnerWidthPt: number,\n\tresolve: MetricsResolver,\n\tfontScalePct: number,\n\tlnSpcReductionPct: number,\n\twidthSafety = 1\n): number | null {\n\tconst result = measureLayout(paragraphs, innerWidthPt, resolve, fontScalePct, lnSpcReductionPct, widthSafety)\n\treturn result === null ? null : result.heightPt\n}\n\n/**\n * Largest `fontScale` (on PowerPoint's 2.5% grid) at which the text fits the box.\n * Width and height both re-measured per scale. Conservative: because the model\n * over-estimates, the returned scale is ≤ PowerPoint's and the text never overflows.\n *\n * `lnSpcReduction` is left at 0 in P1 — dropping `fontScale` alone is provably\n * conservative (PowerPoint trades line-spacing reduction to keep the font *larger*,\n * so a reduction-free fit is always ≤ PowerPoint's scale). The calibration data for\n * the reduction ramp (Findings #4) is recorded for a future refinement.\n */\nexport type ResizeOutcome =\n\t| { kind: 'unmeasurable' } // a run's face has no registered metrics — caller degrades\n\t| { kind: 'resize'; neededInnerHeightPt: number }\n\n/**\n * Inner height (points) the text needs at full size — the value to bake into the\n * shape's `a:ext/@cy` for `fit: 'resize'` (`spAutoFit`). Unlike shrink, resize has\n * **no safety net**: an under-estimate overflows (there is no text scaling\n * fallback), so this errs **tall** — width is inflated (earlier wrap ⇒ more lines)\n * and the laid-out height by the calibrated height factor, so the computed `cy` is\n * ≥ PowerPoint's and ≥ the LibreOffice-rendered height across the resize oracle.\n */\n/**\n * Width used for wrap layout: the inner box width when wrapping, or unbounded for a\n * non-wrapping frame (`wrap:false`) so each paragraph lays out as a single line. A\n * non-wrapping frame's width constraint is enforced separately, against the widest\n * laid-out line — see {@link solveShrink}.\n */\nfunction layoutWidthPt(box: FitBox): number {\n\treturn box.wrap === false ? Infinity : box.innerWidthPt\n}\n\nexport function solveResize(paragraphs: FitParagraph[], box: FitBox, resolve: MetricsResolver): ResizeOutcome {\n\tconst h = measureHeightPt(paragraphs, layoutWidthPt(box), resolve, 100, 0, WIDTH_SAFETY_FACTOR)\n\tif (h === null) return { kind: 'unmeasurable' }\n\treturn { kind: 'resize', neededInnerHeightPt: h * HEIGHT_SAFETY_FACTOR }\n}\n\nexport function solveShrink(paragraphs: FitParagraph[], box: FitBox, resolve: MetricsResolver): ShrinkOutcome {\n\tconst width = layoutWidthPt(box)\n\tconst noWrap = box.wrap === false\n\t// Inflate measured width (earlier wrap) and height by the calibrated safety\n\t// factors so the fit threshold is conservative against PowerPoint. For a\n\t// non-wrapping frame the single line always fits the box HEIGHT, so height alone\n\t// never triggers a shrink: also require the widest (unwrapped) line to fit the box\n\t// WIDTH. `widestLineWidthPt` already carries the WIDTH_SAFETY_FACTOR inflation, so\n\t// comparing it straight to `innerWidthPt` stays on the conservative (shrink-more) side.\n\tconst fits = (scale: number): boolean | null => {\n\t\tconst layout = measureLayout(paragraphs, width, resolve, scale, 0, WIDTH_SAFETY_FACTOR)\n\t\tif (layout === null) return null\n\t\tif (layout.heightPt * HEIGHT_SAFETY_FACTOR > box.innerHeightPt) return false\n\t\tif (noWrap && layout.widestLineWidthPt > box.innerWidthPt) return false\n\t\treturn true\n\t}\n\n\tconst at100 = fits(100)\n\tif (at100 === null) return { kind: 'unmeasurable' }\n\tif (at100) return { kind: 'fits' }\n\n\tfor (let scale = 100 - FONT_SCALE_STEP_PCT; scale >= MIN_FONT_SCALE_PCT; scale -= FONT_SCALE_STEP_PCT) {\n\t\tif (fits(scale)) return { kind: 'shrink', result: { fontScalePct: scale, lnSpcReductionPct: 0 } }\n\t}\n\t// Even at the floor it overflows our conservative model — bake the floor as best effort.\n\treturn { kind: 'shrink', result: { fontScalePct: MIN_FONT_SCALE_PCT, lnSpcReductionPct: 0 } }\n}\n","/**\n * ts-pptx: turning a slide text object into the simulator's inputs\n *\n * The lower half of the measured-fit pass: normalize a text object's runs into `FitParagraph`s,\n * resolve its body insets and box, and work out how a height change is shared between `off.y` and\n * `ext.cy` for a given anchor. Everything here is about *reading* an authored object; the solving\n * and the rewriting live in the siblings (`table-fit.ts`, `fit.ts`).\n */\n\nimport { DEF_FONT_SIZE } from '../constants-internal.js'\nimport { EMU_PER_POINT } from '../units.js'\nimport { getSmartParseNumber, marginToEmu } from '../units-internal.js'\nimport type { FitBox, FitParagraph, FitRun } from './text-fit.js'\nimport { TextAnchor } from '../enums.js'\nimport type { ObjectOptions, TextProps, TextPropsOptions } from '../types/index.js'\nimport type { SlideObject, PresSlideInternal } from '../types/internal.js'\n\n// PowerPoint's default text-frame insets (EMU): l/r = 0.1in, t/b = 0.05in.\nconst DEF_INS_LR_EMU = 91440\nconst DEF_INS_TB_EMU = 45720\n\nconst CRLF_RE = /\\r*\\n/g\n\nexport type RunOpts = TextPropsOptions & ObjectOptions\n\n/** Normalize `slideObj.text` (string | TextProps | TextProps[]) to a run list. */\nexport function normalizeRuns(obj: SlideObject): TextProps[] {\n\tconst opts = obj.options ?? {}\n\tconst text = obj.text as unknown\n\tif (text == null) return []\n\tif (typeof text === 'string' || typeof text === 'number') return [{ text: String(text), options: opts }]\n\tif (!Array.isArray(text) && typeof text === 'object' && 'text' in text) {\n\t\tconst t = text as TextProps\n\t\treturn [{ text: t.text, options: t.options ?? opts }]\n\t}\n\tif (Array.isArray(text)) return (text as TextProps[]).map((t) => ({ text: t.text, options: t.options ?? opts }))\n\treturn []\n}\n\n/** Build a measurable `FitParagraph[]` from a text object, or null if not measurable. */\nexport function extractParagraphs(obj: SlideObject): FitParagraph[] | null {\n\tconst opts = (obj.options ?? {}) as RunOpts\n\tconst runs = normalizeRuns(obj)\n\tif (runs.length === 0) return null\n\treturn buildFitParagraphs(runs, opts)\n}\n\n/**\n * Convert a run list (+ box-level default options) into a measurable\n * `FitParagraph[]`, or null if empty. The single converter shared by the\n * export-time pass ({@link extractParagraphs}) and the public layout-time\n * `measureText` API, so a layout-time prediction and the baked export never drift.\n */\nexport function buildFitParagraphs(runs: TextProps[], opts: RunOpts): FitParagraph[] | null {\n\tif (runs.length === 0) return null\n\n\t// Expand \"\\n\" inside a run into separate pieces, flagging the paragraph break\n\t// after each (mirrors `gen/slide/objects/table.ts` STEP 4). `breakLine` ends a paragraph too.\n\tinterface Piece {\n\t\ttext: string\n\t\toptions: RunOpts\n\t\tbreakAfter: boolean\n\t}\n\tconst pieces: Piece[] = []\n\tfor (const run of runs) {\n\t\tconst ro = (run.options ?? opts) as RunOpts\n\t\tconst raw = String(run.text ?? '').replace(CRLF_RE, '\\n')\n\t\tif (raw.includes('\\n')) {\n\t\t\tconst lines = raw.split('\\n')\n\t\t\tlines.forEach((line, i) => {\n\t\t\t\tconst isLast = i === lines.length - 1\n\t\t\t\tpieces.push({ text: line, options: ro, breakAfter: isLast ? !!ro.breakLine : true })\n\t\t\t})\n\t\t} else {\n\t\t\tpieces.push({ text: raw, options: ro, breakAfter: !!ro.breakLine })\n\t\t}\n\t}\n\n\tconst toRun = (p: Piece): FitRun => {\n\t\tconst ro = p.options\n\t\tconst sizePt = Number(ro.fontSize ?? opts.fontSize ?? DEF_FONT_SIZE)\n\t\treturn {\n\t\t\ttext: p.text,\n\t\t\tsizePt: Number.isFinite(sizePt) && sizePt > 0 ? sizePt : DEF_FONT_SIZE,\n\t\t\tbold: !!(ro.bold ?? opts.bold),\n\t\t\titalic: !!(ro.italic ?? opts.italic),\n\t\t\tfontFace: ro.fontFace ?? opts.fontFace,\n\t\t\tcharSpacingPt: (ro.charSpacing ?? opts.charSpacing) || undefined,\n\t\t}\n\t}\n\tconst toPara = (runsForPara: FitRun[], paraOpts: RunOpts): FitParagraph => {\n\t\tconst lineSpacing = paraOpts.lineSpacing ?? opts.lineSpacing\n\t\tconst lineSpacingMultiple = paraOpts.lineSpacingMultiple ?? opts.lineSpacingMultiple\n\t\treturn {\n\t\t\truns: runsForPara,\n\t\t\tlineSpacingPts: typeof lineSpacing === 'number' && lineSpacing > 0 ? lineSpacing : undefined,\n\t\t\tlineSpacingPct:\n\t\t\t\ttypeof lineSpacingMultiple === 'number' && lineSpacingMultiple > 0 ? lineSpacingMultiple * 100 : 100,\n\t\t\tspaceBeforePts: Number(paraOpts.paraSpaceBefore ?? opts.paraSpaceBefore ?? 0) || 0,\n\t\t\tspaceAfterPts: Number(paraOpts.paraSpaceAfter ?? opts.paraSpaceAfter ?? 0) || 0,\n\t\t}\n\t}\n\n\tconst paras: FitParagraph[] = []\n\tlet cur: FitRun[] = []\n\tlet curParaOpts: RunOpts = opts\n\tfor (const piece of pieces) {\n\t\tcur.push(toRun(piece))\n\t\tcurParaOpts = piece.options\n\t\tif (piece.breakAfter) {\n\t\t\tparas.push(toPara(cur, curParaOpts))\n\t\t\tcur = []\n\t\t}\n\t}\n\tif (cur.length > 0) paras.push(toPara(cur, curParaOpts))\n\treturn paras.length > 0 ? paras : null\n}\n\nexport interface InsetsEmu {\n\tlIns: number\n\trIns: number\n\ttIns: number\n\tbIns: number\n}\n\n/** Resolve text-frame insets (EMU): explicit `_bodyProp` (from `inset`) → `margin` → PowerPoint defaults. */\nexport function resolveInsetsEmu(opts: RunOpts): InsetsEmu {\n\tconst bp = opts._bodyProp ?? {}\n\tconst margin = opts.margin\n\tlet lIns = bp.lIns\n\tlet rIns = bp.rIns\n\tlet tIns = bp.tIns\n\tlet bIns = bp.bIns\n\tif (lIns == null && rIns == null && tIns == null && bIns == null && margin != null) {\n\t\tif (Array.isArray(margin)) {\n\t\t\ttIns = marginToEmu(margin[0] ?? 0)\n\t\t\trIns = marginToEmu(margin[1] ?? 0)\n\t\t\tbIns = marginToEmu(margin[2] ?? 0)\n\t\t\tlIns = marginToEmu(margin[3] ?? 0)\n\t\t} else if (typeof margin === 'number') {\n\t\t\tlIns = rIns = tIns = bIns = marginToEmu(margin)\n\t\t}\n\t}\n\treturn {\n\t\tlIns: lIns ?? DEF_INS_LR_EMU,\n\t\trIns: rIns ?? DEF_INS_LR_EMU,\n\t\ttIns: tIns ?? DEF_INS_TB_EMU,\n\t\tbIns: bIns ?? DEF_INS_TB_EMU,\n\t}\n}\n\n/**\n * Resolve the inner box (shape minus insets) in points; null if degenerate.\n *\n * Reads the object's own `opts.w/h` with no ancestor walk — correct only because a group keeps an\n * identity child coordinate space (`chOff/chExt == off/ext`, see the `gen/slide/object.ts` group renderer and\n * `docs/groups.md`), so a group never scales its children and a grouped text box's authored w/h is its\n * true rendered size. `applyMeasuredFit` reaches here for grouped text via its explicit group descent\n * (`measureObject`). If `addGroup` ever authored a scaled group (non-identity `chExt`), this would need\n * the same ancestor-scale composition `Shape.absoluteFrame` performs on the read path.\n */\nexport function computeBox(obj: SlideObject, presLayout: PresSlideInternal['_presLayout']): FitBox | null {\n\tconst opts = (obj.options ?? {}) as RunOpts\n\tconst wEmu = getSmartParseNumber(opts.w, 'X', presLayout)\n\tconst hEmu = getSmartParseNumber(opts.h, 'Y', presLayout)\n\tif (!(wEmu > 0) || !(hEmu > 0)) return null\n\n\tconst { lIns, rIns, tIns, bIns } = resolveInsetsEmu(opts)\n\n\t// wrap=none lays text out one line per paragraph (no width-wrapping, handled by\n\t// the solver via the wrap flag), but the box width is still a real constraint:\n\t// solveShrink enforces it against the widest line so an over-wide non-wrapping\n\t// line shrinks to fit horizontally instead of spilling out of the box.\n\tconst wrap = (opts._bodyProp ?? {}).wrap !== false\n\tconst innerWidthPt = (wEmu - lIns - rIns) / EMU_PER_POINT\n\tconst innerHeightPt = (hEmu - tIns - bIns) / EMU_PER_POINT\n\tif (!(innerHeightPt > 0)) return null\n\tif (!(innerWidthPt > 0)) return null\n\treturn { innerWidthPt, innerHeightPt, wrap }\n}\n\n/** Vertical-anchor share of a height change that moves the box top up (`off.y` shift). */\nexport function anchorTopShareOfDelta(opts: RunOpts): number {\n\t// `_bodyProp.anchor` is the resolved valign ('t' | 'ctr' | 'b'); default 'ctr'.\n\tconst anchor = (opts._bodyProp ?? {}).anchor\n\tif (anchor === TextAnchor.t) return 0 // grow downward — top fixed\n\tif (anchor === TextAnchor.b) return 1 // grow upward — bottom fixed\n\treturn 0.5 // centered growth (default)\n}\n\n// --- Table cells -----------------------------------------------------------\n// PowerPoint has no text-autofit for table cells (`a:tcPr` carries no autofit and\n// the app ignores `normAutofit` inside a cell — rows auto-grow instead). So a cell's\n// `fit:'shrink'` is honored by baking a *reduced literal font size* onto its runs,\n// which both PowerPoint and LibreOffice render identically with no edit/resize.\n\n/** Text/format options that a cell inherits from the table when it sets none itself (mirrors `gen/slide/objects/table.ts`). */\n","/**\n * ts-pptx: measuring a table's row heights and cell fits\n *\n * A table is measured cell by cell against its resolved grid: each cell inherits the table's text\n * options where it sets none, gets its own margins resolved to EMU insets, and is laid out inside\n * the column span it actually occupies. A merged region is measured once, at its origin, across\n * the columns it covers — which is what `walkTableGrid` exists to hand out.\n */\n\nimport { DEF_CELL_MARGIN_IN, DEF_FONT_SIZE, LINEH_MODIFIER } from '../constants-internal.js'\nimport { EMU_PER_POINT, emuToInches } from '../units.js'\nimport { getSmartParseNumber, inch2Emu, marginToEmu, resolveTableColWidthsEmu } from '../units-internal.js'\nimport { measureLayout, WIDTH_SAFETY_FACTOR, HEIGHT_SAFETY_FACTOR } from './text-fit.js'\nimport { makeRegistryResolver, type FontMetricsRegistry } from './font-metrics.js'\nimport { extractParagraphs, type RunOpts } from './paragraphs.js'\nimport type {\n\tMargin,\n\tPresLayout,\n\tTableCell,\n\tTableCellLayout,\n\tTableCellProps,\n\tTableLayoutResult,\n\tTableProps,\n} from '../types/index.js'\nimport type { SlideObject } from '../types/internal.js'\n\nexport const CELL_INHERIT_KEYS = [\n\t'fontFace',\n\t'fontSize',\n\t'bold',\n\t'italic',\n\t'charSpacing',\n\t'align',\n\t'lineSpacing',\n\t'lineSpacingMultiple',\n\t'valign',\n\t'margin',\n] as const\n\n/** Effective cell options: the cell's own values, with table-level values filled in where unset. */\nexport function effectiveCellOpts(cellOpts: TableCellProps, tableOpts: RunOpts): RunOpts {\n\tconst merged = { ...cellOpts } as RunOpts\n\tfor (const k of CELL_INHERIT_KEYS) {\n\t\tif (merged[k] === undefined && tableOpts[k] !== undefined) (merged as Record<string, unknown>)[k] = tableOpts[k]\n\t}\n\treturn merged\n}\n\nexport interface CellInsetsEmu {\n\tmarL: number\n\tmarR: number\n\tmarT: number\n\tmarB: number\n}\n\n/** Resolve a cell's margins to EMU insets, mirroring `gen/slide/objects/table.ts` (array is `[T,R,B,L]`, inches; see `marginToEmu`). */\nexport function resolveCellInsetsEmu(margin: Margin | undefined): CellInsetsEmu {\n\tlet m: Margin = margin === 0 || margin ? margin : DEF_CELL_MARGIN_IN\n\tif (typeof m === 'number') m = [m, m, m, m]\n\tif (!Array.isArray(m) || m.length !== 4 || m.some((v) => typeof v !== 'number' || !isFinite(v)))\n\t\tm = DEF_CELL_MARGIN_IN\n\tconst arr = m\n\treturn {\n\t\tmarT: marginToEmu(arr[0]),\n\t\tmarR: marginToEmu(arr[1]),\n\t\tmarB: marginToEmu(arr[2]),\n\t\tmarL: marginToEmu(arr[3]),\n\t}\n}\n\n/**\n * Bake a reduced font size onto a cell's runs by factor `f` (< 1). Clones every\n * options object before mutating: a plain-string cell shares the table's `opt`\n * object (`gen/define/`), so in-place mutation would corrupt every other such cell.\n */\nexport function scaleCellFontSizes(cell: TableCell, eff: RunOpts, f: number): void {\n\tconst shrink = (sizePt: number): number => Math.floor(sizePt * f * 10) / 10 // floor: stay on the conservative (smaller) side\n\tconst baseSize = Number(eff.fontSize ?? DEF_FONT_SIZE)\n\tcell.options = { ...(cell.options ?? {}), fontSize: shrink(baseSize) }\n\tif (Array.isArray(cell.text)) {\n\t\tcell.text = cell.text.map((run) =>\n\t\t\trun && typeof run === 'object' && typeof run.options?.fontSize === 'number'\n\t\t\t\t? { ...run, options: { ...run.options, fontSize: shrink(run.options.fontSize) } }\n\t\t\t\t: run\n\t\t)\n\t}\n}\n\n/** Grid column count of a table (sums the first row's colspans), mirroring `gen/slide/objects/table.ts`. */\nexport function tableColCount(rows: TableCell[][]): number {\n\tconst first = rows[0]\n\treturn first ? first.reduce((n, c) => n + (Number(c?.options?.colspan) || 1), 0) : 0\n}\n\n/** A placed (non-merged origin) cell yielded by {@link walkTableGrid}. */\nexport interface GridPlacement {\n\tcell: TableCell\n\t/** Zero-based grid row of the cell's top-left origin. */\n\trow: number\n\t/** Zero-based grid column of the cell's top-left origin. */\n\tcol: number\n\t/** Rows spanned, clamped to the available rows. */\n\trowSpan: number\n\t/** Columns spanned, clamped to the grid width. */\n\tcolSpan: number\n}\n\n/**\n * Walk a table's cell grid in row-major order, resolving each authored cell to its\n * grid origin (`row`/`col`) and clamped colspan/rowspan. Tracks rowspan occupancy so\n * a cell never lands beneath one spanned from above (grid build). This is\n * the single traversal shared by the measured-fit shrink pass and\n * {@link computeTableLayout}, so cell placement cannot drift between them.\n */\nexport function* walkTableGrid(rows: TableCell[][], numCols: number): Generator<GridPlacement> {\n\t// occupied[c] = rows still covered by a rowspan started above (incl. current row).\n\tconst occupied = new Array<number>(numCols).fill(0)\n\tfor (let r = 0; r < rows.length; r++) {\n\t\tconst row = rows[r]\n\t\tif (!row) continue\n\t\tlet col = 0\n\t\tfor (const cell of row) {\n\t\t\twhile (col < numCols && (occupied[col] ?? 0) > 0) col++\n\t\t\tif (col >= numCols) break\n\t\t\tconst colspan = Math.max(1, Number(cell?.options?.colspan) || 1)\n\t\t\tconst rowspan = Math.min(Math.max(1, Number(cell?.options?.rowspan) || 1), rows.length - r)\n\t\t\tconst colStart = col\n\t\t\tconst colEnd = Math.min(colStart + colspan, numCols)\n\t\t\tfor (let c = colStart; c < colEnd; c++) occupied[c] = rowspan\n\t\t\tcol = colEnd\n\t\t\tyield { cell, row: r, col: colStart, rowSpan: rowspan, colSpan: colEnd - colStart }\n\t\t}\n\t\tfor (let c = 0; c < numCols; c++) {\n\t\t\tconst cur = occupied[c] ?? 0\n\t\t\tif (cur > 0) occupied[c] = cur - 1\n\t\t}\n\t}\n}\n\n/**\n * Compute per-cell geometry (inches) for a table laid out at `opts.x`/`y`/`w` — the\n * engine behind `pptx.tableLayout()`. Column widths come from the same\n * {@link resolveTableColWidthsEmu} the writer uses, so cell x/width are exact. Row\n * heights are exact when pinned by `rowH` (array or scalar) or table `h`; otherwise\n * each auto-height row is estimated with the same conservative (tall) text model as\n * {@link measureText} and flagged `heightExact: false`. Single-slide only —\n * `autoPage` paging across slides is not modeled. Rowspan cells do not drive a row's\n * estimated height (mirrors `gen/table/autopage.ts`, which exempts them from line-height growth).\n */\nexport function computeTableLayout(\n\trows: TableCell[][],\n\topts: TableProps,\n\tpresLayout: PresLayout,\n\tregistry: FontMetricsRegistry\n): TableLayoutResult {\n\tconst empty: TableLayoutResult = { cells: [], widthIn: 0, heightIn: 0, heightExact: true }\n\tif (!rows || rows.length === 0 || !rows[0]) return empty\n\tconst numRows = rows.length\n\tconst numCols = tableColCount(rows)\n\tif (!(numCols > 0)) return empty\n\t// Reuse TableProps as a bag of inheritable text props; only CELL_INHERIT_KEYS are\n\t// read from it. Route through `unknown` because TableProps.columns (per-column cell\n\t// styling, TableCellProps[]) collides by name with TextPropsOptions.columns (text\n\t// column count, number) — the two never overlap here since `columns` isn't inherited.\n\tconst o = opts as unknown as RunOpts\n\n\tconst tableXEmu = opts.x != null ? getSmartParseNumber(opts.x, 'X', presLayout) : 0\n\tconst tableYEmu = opts.y != null ? getSmartParseNumber(opts.y, 'Y', presLayout) : 0\n\tconst cxEmu =\n\t\topts.w != null ? getSmartParseNumber(opts.w, 'X', presLayout) : getSmartParseNumber('75%', 'X', presLayout)\n\tconst colWidthsEmu = resolveTableColWidthsEmu(opts.colW, cxEmu, numCols)\n\n\t// Prefix-sum column x offsets (length numCols+1): colXEmu[c] = left edge of column c.\n\tconst colXEmu = new Array<number>(numCols + 1).fill(0)\n\tfor (let c = 0; c < numCols; c++) colXEmu[c + 1] = (colXEmu[c] ?? 0) + (colWidthsEmu[c] ?? 0)\n\n\tconst tableHeightEmu = opts.h != null ? getSmartParseNumber(opts.h, 'Y', presLayout) : 0\n\t// Explicit row height (EMU) or null when the row is auto-height. An array `rowH`\n\t// slot that is missing/NaN falls back to an even split of table `h` if one is set.\n\tconst explicitRowHEmu = (r: number): number | null => {\n\t\tif (Array.isArray(opts.rowH)) {\n\t\t\tconst v = opts.rowH[r]\n\t\t\tif (typeof v === 'number' && isFinite(v)) return inch2Emu(v)\n\t\t\treturn tableHeightEmu > 0 ? Math.round(tableHeightEmu / numRows) : null\n\t\t}\n\t\tif (opts.rowH != null && !isNaN(Number(opts.rowH))) return inch2Emu(Number(opts.rowH))\n\t\tif (tableHeightEmu > 0) return Math.round(tableHeightEmu / numRows)\n\t\treturn null\n\t}\n\tconst resolve = makeRegistryResolver(registry)\n\tconst defLineEmu = inch2Emu((DEF_FONT_SIZE * LINEH_MODIFIER) / 100)\n\n\t// Estimate a cell's content height (EMU) at its authored size, conservative/tall,\n\t// with a one-line floor. Mirrors measureText's inflated wrap + height safety.\n\tconst estimateContentHeightEmu = (cell: TableCell, eff: RunOpts, innerWidthPt: number): number => {\n\t\tconst fontSizePt = Number(eff.fontSize ?? DEF_FONT_SIZE) || DEF_FONT_SIZE\n\t\tconst oneLineEmu = inch2Emu((fontSizePt * LINEH_MODIFIER) / 100)\n\t\tif (!(innerWidthPt > 0)) return oneLineEmu\n\t\tconst paragraphs = extractParagraphs({ text: cell.text, options: eff } as unknown as SlideObject)\n\t\tif (!paragraphs) return oneLineEmu\n\t\tconst layout = measureLayout(paragraphs, innerWidthPt, resolve, 100, 0, WIDTH_SAFETY_FACTOR)\n\t\tif (layout === null) return oneLineEmu\n\t\treturn Math.max(Math.round(layout.heightPt * HEIGHT_SAFETY_FACTOR * EMU_PER_POINT), oneLineEmu)\n\t}\n\n\t// PASS 1: place every origin cell and resolve each row's height.\n\tinterface Placed {\n\t\tp: GridPlacement\n\t\tcolStart: number\n\t\tcolEnd: number\n\t}\n\tconst placed: Placed[] = []\n\tconst rowHeightsEmu = new Array<number>(numRows).fill(0)\n\tconst rowExact = new Array<boolean>(numRows).fill(false)\n\tfor (let r = 0; r < numRows; r++) {\n\t\tconst ex = explicitRowHEmu(r)\n\t\tif (ex != null) {\n\t\t\trowHeightsEmu[r] = ex\n\t\t\trowExact[r] = true\n\t\t}\n\t}\n\n\tfor (const p of walkTableGrid(rows, numCols)) {\n\t\tconst colStart = p.col\n\t\tconst colEnd = p.col + p.colSpan\n\t\tplaced.push({ p, colStart, colEnd })\n\n\t\t// Only single-row, auto-height cells drive a row's estimated height.\n\t\tif (p.rowSpan === 1 && !rowExact[p.row]) {\n\t\t\tlet widthEmu = 0\n\t\t\tfor (let c = colStart; c < colEnd; c++) widthEmu += colWidthsEmu[c] ?? 0\n\t\t\tconst eff = effectiveCellOpts(p.cell?.options ?? {}, o)\n\t\t\tconst ins = resolveCellInsetsEmu(eff.margin)\n\t\t\tconst innerWidthPt = (widthEmu - ins.marL - ins.marR) / EMU_PER_POINT\n\t\t\tconst contentEmu = estimateContentHeightEmu(p.cell, eff, innerWidthPt) + ins.marT + ins.marB\n\t\t\tif (contentEmu > (rowHeightsEmu[p.row] ?? 0)) rowHeightsEmu[p.row] = contentEmu\n\t\t}\n\t}\n\n\t// A row touched only by rowspans (no single-row cell, no explicit height) still\n\t// needs a non-zero height: give it one default line.\n\tfor (let r = 0; r < numRows; r++) if ((rowHeightsEmu[r] ?? 0) <= 0) rowHeightsEmu[r] = defLineEmu\n\n\t// Prefix-sum row y offsets.\n\tconst rowYEmu = new Array<number>(numRows + 1).fill(0)\n\tfor (let r = 0; r < numRows; r++) rowYEmu[r + 1] = (rowYEmu[r] ?? 0) + (rowHeightsEmu[r] ?? 0)\n\n\t// PASS 2: emit one rect per origin cell.\n\tconst cells: TableCellLayout[] = placed.map(({ p, colStart, colEnd }) => {\n\t\tconst rowEnd = p.row + p.rowSpan\n\t\tlet heightExact = true\n\t\tfor (let rr = p.row; rr < rowEnd; rr++) if (!rowExact[rr]) heightExact = false\n\t\treturn {\n\t\t\trow: p.row,\n\t\t\tcol: p.col,\n\t\t\trowSpan: p.rowSpan,\n\t\t\tcolSpan: p.colSpan,\n\t\t\txIn: emuToInches(tableXEmu + (colXEmu[colStart] ?? 0)),\n\t\t\tyIn: emuToInches(tableYEmu + (rowYEmu[p.row] ?? 0)),\n\t\t\twIn: emuToInches((colXEmu[colEnd] ?? 0) - (colXEmu[colStart] ?? 0)),\n\t\t\thIn: emuToInches((rowYEmu[rowEnd] ?? 0) - (rowYEmu[p.row] ?? 0)),\n\t\t\theightExact,\n\t\t}\n\t})\n\n\treturn {\n\t\tcells,\n\t\twidthIn: emuToInches(colXEmu[numCols] ?? 0),\n\t\theightIn: emuToInches(rowYEmu[numRows] ?? 0),\n\t\theightExact: rowExact.every(Boolean),\n\t}\n}\n\n/**\n * Build the `MetricsResolver` both the export pass and `measureText` use, so they\n * agree run-for-run: exact registered metrics → conservative heuristic for any\n * **named** face without exact metrics → `undefined` only for an unnamed\n * (theme-default) face that cannot be guessed. `onHeuristic` is called with each\n * named face that fell back to the heuristic (for the export pass's warn-once).\n */\n","/**\n * ts-pptx: Measured-fit serialization pass\n *\n * Bridges slide text objects to the line-break simulator / shrink solver\n * (`text-fit.ts`). Runs during async export, BEFORE the `gen/` emitter builds the body:\n * for each text box with `fit: 'shrink'` and a registered font, it computes the\n * `fontScale` PowerPoint would have baked and rewrites `options.fit` to the\n * explicit object form (`{ type:'shrink', fontScale }`) so `genXmlNormAutofit`\n * emits `<a:normAutofit fontScale=…/>`. Without metrics it leaves the bare flag\n * untouched (current behavior) and warns once. See `docs/measured-text-fit.md`.\n */\n\nimport { SlideObjectType } from '../enums.js'\nimport { EMU_PER_POINT, POINTS_PER_INCH } from '../units.js'\nimport { getSmartParseNumber, inch2Emu, resolveTableColWidthsEmu } from '../units-internal.js'\nimport { warn } from '../diagnostics.js'\nimport { makeRegistryResolver, type FontMetricsRegistry } from './font-metrics.js'\nimport {\n\tsolveShrink,\n\tsolveResize,\n\tmeasureLayout,\n\tWIDTH_SAFETY_FACTOR,\n\tHEIGHT_SAFETY_FACTOR,\n\ttype FitBox,\n\ttype FitParagraph,\n} from './text-fit.js'\nimport {\n\tanchorTopShareOfDelta,\n\tbuildFitParagraphs,\n\tcomputeBox,\n\textractParagraphs,\n\tresolveInsetsEmu,\n\ttype RunOpts,\n} from './paragraphs.js'\nimport {\n\teffectiveCellOpts,\n\tresolveCellInsetsEmu,\n\tscaleCellFontSizes,\n\ttableColCount,\n\twalkTableGrid,\n} from './table-fit.js'\nimport type { MeasureTextOptions, TextMeasurement, TextProps } from '../types/index.js'\nimport type { SlideObject, PresSlideInternal } from '../types/internal.js'\n\n/** Map the public {@link MeasureTextOptions} onto the internal run-option shape. */\nfunction measureOptsToRunOpts(opts: MeasureTextOptions): RunOpts {\n\treturn {\n\t\tfontSize: opts.fontSize,\n\t\tfontFace: opts.fontFace,\n\t\tbold: opts.bold,\n\t\titalic: opts.italic,\n\t\tcharSpacing: opts.charSpacing,\n\t\tlineSpacing: opts.lineSpacing,\n\t\tlineSpacingMultiple: opts.lineSpacingMultiple,\n\t\tparaSpaceBefore: opts.paraSpaceBefore,\n\t\tparaSpaceAfter: opts.paraSpaceAfter,\n\t}\n}\n\n/** A fresh result each call: `approximatedFaces` is caller-owned, so it cannot be shared. */\nconst unmeasurable = (): TextMeasurement => ({\n\theightIn: 0,\n\tlineCount: 0,\n\twidestLineIn: 0,\n\tmeasurable: false,\n\tapproximatedFaces: [],\n\tfitsBox: () => false,\n\tshrinkScaleFor: () => 100,\n})\n\n/**\n * Layout-time text measurement against registered metrics — the public engine\n * behind `pptx.measureText()`. Uses the **same** calibrated wrap model, resolver\n * semantics, and conservative safety factors as the export-time bake\n * ({@link applyMeasuredFit} / {@link solveResize} / {@link solveShrink}), so a\n * layout-time prediction matches the value the export would bake **for any deck\n * that opted into measured fit (i.e. registered at least one face)**.\n *\n * With an **empty** registry the two intentionally diverge: {@link applyMeasuredFit}\n * treats \"no metrics\" as \"not opted in\" and bakes nothing, while this returns\n * heuristic numbers so the API is useful with zero setup. Check\n * `approximatedFaces` if that distinction matters.\n *\n * Synchronous: assumes metrics are pre-registered (lookup is sync). A named face\n * with no exact metrics silently uses the conservative heuristic (same as export)\n * and is reported in `approximatedFaces`; an unnamed theme-default face returns\n * `measurable: false`.\n */\nexport function measureText(\n\tregistry: FontMetricsRegistry,\n\ttext: string | TextProps[],\n\topts: MeasureTextOptions\n): TextMeasurement {\n\tconst runs: TextProps[] =\n\t\ttypeof text === 'string' || typeof text === 'number' ? [{ text: String(text) }] : Array.isArray(text) ? text : []\n\tconst paragraphs = buildFitParagraphs(runs, measureOptsToRunOpts(opts))\n\tif (!paragraphs) return unmeasurable()\n\n\tconst inset = opts.insetIn ?? 0\n\tconst innerWidthPt = (opts.wIn - 2 * inset) * POINTS_PER_INCH\n\tconst heuristicFaces = new Set<string>()\n\tconst resolve = makeRegistryResolver(registry, (face) => heuristicFaces.add(face))\n\n\t// Conservative (tall) layout at full size, mirroring solveResize: inflate width\n\t// (earlier wrap) by WIDTH_SAFETY and the height by HEIGHT_SAFETY. The resolver runs\n\t// during layout, so `heuristicFaces` is only populated once this returns.\n\tconst layout = measureLayout(paragraphs, innerWidthPt, resolve, 100, 0, WIDTH_SAFETY_FACTOR)\n\tif (layout === null) return unmeasurable()\n\tconst heightPt = layout.heightPt * HEIGHT_SAFETY_FACTOR\n\tconst heightIn = heightPt / POINTS_PER_INCH\n\n\treturn {\n\t\theightIn,\n\t\tlineCount: layout.lineCount,\n\t\twidestLineIn: layout.widestLineWidthPt / POINTS_PER_INCH,\n\t\tmeasurable: true,\n\t\t// Snapshot: shrinkScaleFor() re-enters the same resolver, so the live set must not leak.\n\t\tapproximatedFaces: [...heuristicFaces],\n\t\t// Mirrors solveShrink's fit check at scale 100 (height already inflated).\n\t\tfitsBox: (hIn: number) => heightPt <= hIn * POINTS_PER_INCH,\n\t\tshrinkScaleFor: (hIn: number) => {\n\t\t\tconst box: FitBox = { innerWidthPt, innerHeightPt: hIn * POINTS_PER_INCH }\n\t\t\tconst outcome = solveShrink(paragraphs, box, resolve)\n\t\t\tif (outcome.kind === 'shrink') return outcome.result.fontScalePct\n\t\t\treturn 100 // 'fits' (or, defensively, 'unmeasurable') → no shrink\n\t\t},\n\t}\n}\n\n/**\n * Apply measured fit across every slide. For each text box that opts in via\n * `fit:'shrink'` or `fit:'resize'` and whose font has registered metrics, this\n * bakes the computed result before the sync XML pass reads it:\n * - `'shrink'` → rewrites `options.fit` to the object form so `<a:normAutofit\n * fontScale=…/>` is emitted (text renders pre-shrunk).\n * - `'resize'` → rewrites `options.h` (and `options.y` per vertical anchor) so the\n * shape's `a:ext/@cy` is the height the text needs; the `<a:spAutoFit/>` marker is\n * left in place (the renderer draws the baked `cy`).\n *\n * Safe to call with an empty registry (no-op). Warns once if any opted-in box could\n * not be measured (missing metrics) so overflow is not silently ignored.\n */\nexport function applyMeasuredFit(slides: PresSlideInternal[], registry: FontMetricsRegistry): void {\n\tif (registry.size === 0) return\n\n\t// A deck that registered *some* metrics has opted into measured fit, so a named\n\t// face we have no exact metrics for falls back to the conservative heuristic rather\n\t// than degrading to the bare flag — overflow still self-corrects, just less precisely.\n\t// An unnamed (theme-default) face stays unmeasurable: we cannot guess which face it is.\n\tconst heuristicFaces = new Set<string>()\n\tconst resolve = makeRegistryResolver(registry, (face) => heuristicFaces.add(face))\n\tconst unmeasuredShrink = new Set<string>()\n\tconst unmeasuredResize = new Set<string>()\n\n\tconst collectUnmeasured = (paragraphs: FitParagraph[], into: Set<string>): void => {\n\t\tfor (const para of paragraphs)\n\t\t\tfor (const run of para.runs) if (!resolve(run)) into.add(run.fontFace ?? '(theme default)')\n\t}\n\n\t/**\n\t * Bake measured shrink into a table's cells. Walks the cell grid (accounting for\n\t * colspan/rowspan) to size each cell's box from its column widths and row heights,\n\t * then lowers the run font sizes of any `fit:'shrink'` cell that overflows. Cells\n\t * in auto-height rows (no fixed `rowH`/table `h`) are skipped — the row grows instead.\n\t */\n\tconst measureTableCells = (tableObj: SlideObject, layout: PresSlideInternal['_presLayout']): void => {\n\t\tconst rows = tableObj.arrTabRows\n\t\tif (!rows || rows.length === 0 || !rows[0]) return\n\t\tconst tableOpts = (tableObj.options ?? {}) as RunOpts\n\t\tconst numRows = rows.length\n\t\tconst numCols = tableColCount(rows)\n\t\tif (!(numCols > 0)) return\n\n\t\tconst cxEmu =\n\t\t\ttableOpts.w != null ? getSmartParseNumber(tableOpts.w, 'X', layout) : getSmartParseNumber('75%', 'X', layout)\n\t\tconst colWidthsEmu = resolveTableColWidthsEmu(tableOpts.colW, cxEmu, numCols)\n\t\tconst tableHeightEmu =\n\t\t\ttableOpts.h != null\n\t\t\t\t? getSmartParseNumber(tableOpts.h, 'Y', layout)\n\t\t\t\t: typeof tableOpts.cy === 'number'\n\t\t\t\t\t? tableOpts.cy\n\t\t\t\t\t: 0\n\t\tconst rowHeightEmu = (rIdx: number): number => {\n\t\t\tif (Array.isArray(tableOpts.rowH) && tableOpts.rowH[rIdx]) return inch2Emu(Number(tableOpts.rowH[rIdx]))\n\t\t\tif (tableOpts.rowH != null && !Array.isArray(tableOpts.rowH) && !isNaN(Number(tableOpts.rowH)))\n\t\t\t\treturn inch2Emu(Number(tableOpts.rowH))\n\t\t\tif (tableHeightEmu > 0) return Math.round(tableHeightEmu / numRows)\n\t\t\treturn 0 // auto-height row → grows to fit, no shrink\n\t\t}\n\n\t\tfor (const { cell, row: r, col: colStart, colSpan, rowSpan } of walkTableGrid(rows, numCols)) {\n\t\t\tconst colEnd = colStart + colSpan\n\t\t\tconst cellOpts = cell?.options ?? {}\n\t\t\tconst fit = cellOpts.fit ?? (tableOpts.fit === 'shrink' ? 'shrink' : undefined)\n\t\t\tif (fit !== 'shrink') continue\n\n\t\t\tlet widthEmu = 0\n\t\t\tfor (let c = colStart; c < colEnd; c++) widthEmu += colWidthsEmu[c] ?? 0\n\t\t\tlet heightEmu = 0\n\t\t\tlet autoHeight = false\n\t\t\tfor (let rr = r; rr < r + rowSpan; rr++) {\n\t\t\t\tconst h = rowHeightEmu(rr)\n\t\t\t\tif (h <= 0) {\n\t\t\t\t\tautoHeight = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\theightEmu += h\n\t\t\t}\n\t\t\tif (autoHeight) continue\n\n\t\t\tconst eff = effectiveCellOpts(cellOpts, tableOpts)\n\t\t\tconst ins = resolveCellInsetsEmu(eff.margin)\n\t\t\tconst innerWidthPt = (widthEmu - ins.marL - ins.marR) / EMU_PER_POINT\n\t\t\tconst innerHeightPt = (heightEmu - ins.marT - ins.marB) / EMU_PER_POINT\n\t\t\tif (!(innerWidthPt > 0) || !(innerHeightPt > 0)) continue\n\n\t\t\tconst paragraphs = extractParagraphs({ text: cell.text, options: eff } as unknown as SlideObject)\n\t\t\tif (!paragraphs) continue\n\t\t\tconst box: FitBox = { innerWidthPt, innerHeightPt }\n\t\t\tconst outcome = solveShrink(paragraphs, box, resolve)\n\t\t\tif (outcome.kind === 'shrink') {\n\t\t\t\tconst f = outcome.result.fontScalePct / 100\n\t\t\t\tif (f < 1) scaleCellFontSizes(cell, eff, f)\n\t\t\t} else if (outcome.kind === 'unmeasurable') {\n\t\t\t\tcollectUnmeasured(paragraphs, unmeasuredShrink)\n\t\t\t}\n\t\t\t// 'fits' → leave the authored size; the text already fits.\n\t\t}\n\t}\n\n\tconst measureObject = (obj: SlideObject, layout: PresSlideInternal['_presLayout']): void => {\n\t\t// Groups keep their children outside slide._slideObjects. Descend through every\n\t\t// nesting level so grouping remains a visual/editability operation and does not\n\t\t// disable the export-time fit pass for contained text.\n\t\tif (obj._type === SlideObjectType.group) {\n\t\t\tfor (const child of obj._groupObjects ?? []) measureObject(child, layout)\n\t\t\treturn\n\t\t}\n\t\tif (obj._type === SlideObjectType.table) {\n\t\t\tmeasureTableCells(obj, layout)\n\t\t\treturn\n\t\t}\n\t\tif (obj._type !== SlideObjectType.text) return\n\t\t// Only the bare string forms opt into measurement; an explicit object form is\n\t\t// already baked by the caller, and 'none' is a no-op.\n\t\tconst options = obj.options\n\t\tif (!options) return\n\t\tconst fit = options.fit\n\t\tif (fit !== 'shrink' && fit !== 'resize') return\n\n\t\tconst paragraphs = extractParagraphs(obj)\n\t\tif (!paragraphs) return\n\t\tconst box = computeBox(obj, layout)\n\t\tif (!box) return\n\n\t\tif (fit === 'shrink') {\n\t\t\tconst outcome = solveShrink(paragraphs, box, resolve)\n\t\t\tif (outcome.kind === 'shrink') {\n\t\t\t\tconst { fontScalePct, lnSpcReductionPct } = outcome.result\n\t\t\t\toptions.fit = {\n\t\t\t\t\ttype: 'shrink',\n\t\t\t\t\tfontScale: fontScalePct,\n\t\t\t\t\tlnSpcReduction: lnSpcReductionPct || undefined,\n\t\t\t\t}\n\t\t\t} else if (outcome.kind === 'unmeasurable') {\n\t\t\t\tcollectUnmeasured(paragraphs, unmeasuredShrink)\n\t\t\t}\n\t\t\t// 'fits' → leave the bare flag; the text already fits, so no scale is needed.\n\t\t} else {\n\t\t\tconst outcome = solveResize(paragraphs, box, resolve)\n\t\t\tif (outcome.kind === 'resize') {\n\t\t\t\tconst opts = obj.options as RunOpts\n\t\t\t\tconst { tIns, bIns } = resolveInsetsEmu(opts)\n\t\t\t\tconst oldHeightEmu = getSmartParseNumber(opts.h, 'Y', layout)\n\t\t\t\tconst newHeightEmu = Math.round(outcome.neededInnerHeightPt * EMU_PER_POINT) + tIns + bIns\n\t\t\t\t// Shift the box origin so growth/shrink honors the vertical anchor; `off.y`\n\t\t\t\t// moves up by the anchor's share of the height delta (0 / half / full for t / ctr / b).\n\t\t\t\tconst oldYEmu = getSmartParseNumber(opts.y, 'Y', layout)\n\t\t\t\tconst shiftEmu = Math.round((newHeightEmu - oldHeightEmu) * anchorTopShareOfDelta(opts))\n\t\t\t\topts.h = `${newHeightEmu}emu`\n\t\t\t\tif (shiftEmu !== 0) opts.y = `${oldYEmu - shiftEmu}emu`\n\t\t\t} else {\n\t\t\t\tcollectUnmeasured(paragraphs, unmeasuredResize)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const slide of slides) {\n\t\tfor (const obj of slide._slideObjects ?? []) measureObject(obj, slide._presLayout)\n\t}\n\n\tif (unmeasuredShrink.size > 0) {\n\t\twarn(\n\t\t\t'measure/shrink-unmeasured',\n\t\t\t`fit:'shrink' could not be measured for font(s) [${[...unmeasuredShrink].join(', ')}] — ` +\n\t\t\t\t'no registered metrics. Emitting bare <a:normAutofit/> (text will not pre-shrink in headless renders). ' +\n\t\t\t\t'Call pptx.registerFontMetrics(face, fontFilePathOrBytes) to enable measured fit.'\n\t\t)\n\t}\n\tif (unmeasuredResize.size > 0) {\n\t\twarn(\n\t\t\t'measure/resize-unmeasured',\n\t\t\t`fit:'resize' could not be measured for font(s) [${[...unmeasuredResize].join(', ')}] — ` +\n\t\t\t\t'no registered metrics. Emitting bare <a:spAutoFit/> with the authored height (box will not auto-grow in headless renders). ' +\n\t\t\t\t'Call pptx.registerFontMetrics(face, fontFilePathOrBytes) to enable measured fit.'\n\t\t)\n\t}\n\tif (heuristicFaces.size > 0) {\n\t\twarn(\n\t\t\t'measure/heuristic-metrics',\n\t\t\t`Note: measured fit used a conservative average-advance estimate for font(s) [${[...heuristicFaces].join(', ')}] — ` +\n\t\t\t\t'no exact metrics registered. Fit is approximate (may shrink/grow more than necessary). ' +\n\t\t\t\t'Call pptx.registerFontMetrics(face, fontFilePathOrBytes) for an exact fit.'\n\t\t)\n\t}\n}\n"],"mappings":"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"}