@shbernal/ts-pptx 3.2.0 → 3.3.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 (71) hide show
  1. package/README.md +19 -20
  2. package/dist/{autopage-QQyfRtoK.js → autopage-D1sqK6j7.js} +16 -15
  3. package/dist/{autopage-QQyfRtoK.js.map → autopage-D1sqK6j7.js.map} +1 -1
  4. package/dist/browser.d.ts +6 -6
  5. package/dist/browser.js +5 -6
  6. package/dist/browser.js.map +1 -1
  7. package/dist/{diagnostics-Chjz_4_Q.d.ts → diagnostics-B2yWpcjB.d.ts} +31 -14
  8. package/dist/{diagnostics-Chjz_4_Q.d.ts.map → diagnostics-B2yWpcjB.d.ts.map} +1 -1
  9. package/dist/{diagnostics-DYQC7xLV.js → diagnostics-H1QzgRSw.js} +0 -0
  10. package/dist/{diagnostics-DYQC7xLV.js.map → diagnostics-H1QzgRSw.js.map} +1 -1
  11. package/dist/{enums-CoXnxiCI.d.ts → enums-CvRYniUA.d.ts} +13 -3
  12. package/dist/{enums-CoXnxiCI.d.ts.map → enums-CvRYniUA.d.ts.map} +1 -1
  13. package/dist/{errors-BVLCFr-t.d.ts → errors-fB1KDJKQ.d.ts} +5 -5
  14. package/dist/{errors-BVLCFr-t.d.ts.map → errors-fB1KDJKQ.d.ts.map} +1 -1
  15. package/dist/{fit-DhMbQG5O.js → fit-CYJ1jkQI.js} +0 -0
  16. package/dist/fit-CYJ1jkQI.js.map +1 -0
  17. package/dist/{html-dom-DajBuobe.js → html-dom-CzewEdE1.js} +4 -5
  18. package/dist/{html-dom-DajBuobe.js.map → html-dom-CzewEdE1.js.map} +1 -1
  19. package/dist/html.d.ts +3 -3
  20. package/dist/html.js +1 -1
  21. package/dist/index.d.ts +6 -6
  22. package/dist/index.js +5 -6
  23. package/dist/index.js.map +1 -1
  24. package/dist/inspect.d.ts +1 -1
  25. package/dist/inspect.js +4 -4
  26. package/dist/math.d.ts +1 -1
  27. package/dist/measure.d.ts +96 -6
  28. package/dist/measure.d.ts.map +1 -1
  29. package/dist/measure.js +2 -2
  30. package/dist/node.d.ts +6 -6
  31. package/dist/node.js +4 -5
  32. package/dist/node.js.map +1 -1
  33. package/dist/notes-SCvpJz0l.js +1646 -0
  34. package/dist/notes-SCvpJz0l.js.map +1 -0
  35. package/dist/{pres-C-EU3SkN.d.ts → pres-Cb-1xDne.d.ts} +4 -4
  36. package/dist/{pres-C-EU3SkN.d.ts.map → pres-Cb-1xDne.d.ts.map} +1 -1
  37. package/dist/{presentation-CxnUVhK9.js → presentation-DeKTJGHf.js} +1002 -422
  38. package/dist/presentation-DeKTJGHf.js.map +1 -0
  39. package/dist/{presentation-Osx1cDdJ.d.ts → presentation-types-B50aOUFI.d.ts} +679 -11
  40. package/dist/presentation-types-B50aOUFI.d.ts.map +1 -0
  41. package/dist/read.d.ts +4 -5
  42. package/dist/read.d.ts.map +1 -1
  43. package/dist/read.js +2 -2
  44. package/dist/script.d.ts +2 -2
  45. package/dist/script.d.ts.map +1 -1
  46. package/dist/script.js +0 -0
  47. package/dist/script.js.map +1 -1
  48. package/dist/{shapes-DoJBPrwN.js → shapes-BXVgsk0F.js} +3 -3
  49. package/dist/{shapes-DoJBPrwN.js.map → shapes-BXVgsk0F.js.map} +1 -1
  50. package/dist/{slide-BTfn1Pyp.d.ts → slide-DWw9UoI6.d.ts} +39 -3
  51. package/dist/slide-DWw9UoI6.d.ts.map +1 -0
  52. package/dist/{st-enums-evQG2A7Z.js → st-enums-bVnezkMJ.js} +33 -12
  53. package/dist/{st-enums-evQG2A7Z.js.map → st-enums-bVnezkMJ.js.map} +1 -1
  54. package/dist/{text-C4P9MNdf.js → text-uM-zGton.js} +219 -1668
  55. package/dist/text-uM-zGton.js.map +1 -0
  56. package/dist/{units-internal-CR8s8YQa.js → units-internal-CGf4VrXy.js} +156 -4
  57. package/dist/units-internal-CGf4VrXy.js.map +1 -0
  58. package/dist/zip.d.ts +2 -2
  59. package/package.json +36 -27
  60. package/dist/embedded-fonts-MaynAHa9.js +0 -94
  61. package/dist/embedded-fonts-MaynAHa9.js.map +0 -1
  62. package/dist/fit-DhMbQG5O.js.map +0 -1
  63. package/dist/presentation-CxnUVhK9.js.map +0 -1
  64. package/dist/presentation-Osx1cDdJ.d.ts.map +0 -1
  65. package/dist/presentation-types-Dj4riuDs.d.ts +0 -538
  66. package/dist/presentation-types-Dj4riuDs.d.ts.map +0 -1
  67. package/dist/slide-BTfn1Pyp.d.ts.map +0 -1
  68. package/dist/text-C4P9MNdf.js.map +0 -1
  69. package/dist/units-C7QM1KgH.js +0 -144
  70. package/dist/units-C7QM1KgH.js.map +0 -1
  71. package/dist/units-internal-CR8s8YQa.js.map +0 -1
@@ -1,11 +1,10 @@
1
- import { n as warn, r as warnOnce } from "./diagnostics-DYQC7xLV.js";
1
+ import { n as warn, r as warnOnce } from "./diagnostics-H1QzgRSw.js";
2
2
  import { n as InvalidOptionError, o as UnsupportedFeatureError, r as MediaError, t as InternalError } from "./errors-Bvln0H-7.js";
3
- import { a as FIXED_PCT_PER_PERCENT, d as emuToInches, g as pixelsToEmu, i as EMU_PER_POINT, l as STANDARD_LAYOUTS, m as inchesToEmu, r as EMU_PER_INCH, s as PERCENT_SCALE, t as ANGLE_UNITS_PER_DEGREE, v as ptToHundredths } from "./units-C7QM1KgH.js";
4
- import { $ as connectorPresetFor, C as DEF_PRES_LAYOUT_NAME, D as DEF_TEXT_GLOW, E as DEF_SLIDE_MARGIN_IN, F as PIECHART_COLORS, G as PlaceholderType, I as REGEX_HEX_COLOR, K as SchemeColor, L as SLDNUMFLDID, M as LAYOUT_IDX_SERIES_BASE, N as LETTERS, O as DEF_TEXT_SHADOW, Q as asChartType, R as XML_DECL, S as DEF_PRES_LAYOUT, T as DEF_SHAPE_SHADOW, Z as VALID_SHAPE_PRESETS, _ as DEF_CELL_BORDER, a as lineWidthToEmu, b as DEF_CHART_GRIDLINE, c as resolveTableColWidthsEmu, d as AXIS_ID_CATEGORY_PRIMARY, et as isChartExType, f as AXIS_ID_CATEGORY_SECONDARY, g as BARCHART_COLORS, h as AXIS_ID_VALUE_SECONDARY, i as inch2Emu, k as IMG_BROKEN, l as transparencyToAlpha, m as AXIS_ID_VALUE_PRIMARY, n as convertRotationDegrees, o as marginToEmu, p as AXIS_ID_SERIES_PRIMARY, r as getSmartParseNumber, s as opacityToAlpha, t as convertArcAngle, u as valToPts, v as DEF_CELL_MARGIN_IN, x as DEF_FONT_COLOR, y as DEF_CHART_BORDER } from "./units-internal-CR8s8YQa.js";
5
- import { E as svgMarkupToDataUri, S as getImageSizeFromBase64, T as decodeBase64ToBytes, a as PRESET_MATERIALS, g as PACKAGE_REL_NS, h as OFFICE_REL, i as PRESET_LINE_DASHES, n as LIGHT_RIGS, r as LIGHT_RIG_DIRECTIONS, s as TEXT_HORZ_OVERFLOW, t as BEVEL_PRESETS, u as checkEnumOrWarn, x as fitSrcRectPercents } from "./st-enums-evQG2A7Z.js";
6
- import { t as getSlidesForTableRows } from "./autopage-QQyfRtoK.js";
7
- import { _ as parseFontMetrics, m as FontMetricsRegistry, n as measureText, r as computeTableLayout, t as applyMeasuredFit } from "./fit-DhMbQG5O.js";
8
- import { a as flattenEmbeddedFaces, i as FONT_REL_TYPE, n as FONT_DATA_CONTENT_TYPE, o as serializeEmbeddedFontLst, r as FONT_DATA_EXTENSION, t as EMBEDDED_FONT_SLOTS } from "./embedded-fonts-MaynAHa9.js";
3
+ import { $ as connectorPresetFor, C as DEF_PRES_LAYOUT_NAME, E as DEF_SLIDE_MARGIN_IN, F as PIECHART_COLORS, G as PlaceholderType, I as REGEX_HEX_COLOR, K as SchemeColor, L as SLDNUMFLDID, M as LAYOUT_IDX_SERIES_BASE, N as LETTERS, O as DEF_TEXT_SHADOW, Q as asChartType, R as XML_DECL, S as DEF_PRES_LAYOUT, T as DEF_SHAPE_SHADOW, U as ChartType, Z as VALID_SHAPE_PRESETS, _ as DEF_CELL_BORDER, _t as pixelsToEmu, a as lineWidthToEmu, at as EMU_PER_POINT, b as DEF_CHART_GRIDLINE, c as resolveTableColWidthsEmu, ct as PERCENT_SCALE, d as AXIS_ID_CATEGORY_PRIMARY, et as isChartExType, f as AXIS_ID_CATEGORY_SECONDARY, ft as emuToInches, g as BARCHART_COLORS, h as AXIS_ID_VALUE_SECONDARY, ht as inchesToEmu, i as inch2Emu, it as EMU_PER_INCH, k as IMG_BROKEN, m as AXIS_ID_VALUE_PRIMARY, n as convertRotationDegrees, o as marginToEmu, ot as FIXED_PCT_PER_PERCENT, p as AXIS_ID_SERIES_PRIMARY, r as getSmartParseNumber, t as convertArcAngle, tt as isChartType, u as valToPts, ut as STANDARD_LAYOUTS, v as DEF_CELL_MARGIN_IN, x as DEF_FONT_COLOR, y as DEF_CHART_BORDER, yt as ptToHundredths } from "./units-internal-CGf4VrXy.js";
4
+ import { A as getNewRelId, C as createColorElement, D as encodeXmlAttrValue, E as voidEl, M as isHyperlinkRel, N as validateObjectName, O as encodeXmlEntities, S as createShadowEffectLst, T as raw, _ as FONT_DATA_EXTENSION, a as makeXmlNotesSlideRel, b as serializeEmbeddedFontLst, c as genXmlParagraphProperties, d as clampFontSizeSz, f as genXmlColorSelection, g as FONT_DATA_CONTENT_TYPE, h as EMBEDDED_FONT_SLOTS, i as makeXmlNotesSlide, j as getUuid, k as getDuplicateObjectNames, l as groupRunsIntoLines, m as genXmlPatternFill, n as makeXmlNotesMaster, p as genXmlGradientFill, r as makeXmlNotesMasterRel, s as genXmlNormAutofit, t as buildNotesSlideRels, u as renderTextParagraphsXml, v as FONT_REL_TYPE, w as el, x as correctShadowOptions, y as flattenEmbeddedFaces } from "./notes-SCvpJz0l.js";
5
+ import { E as svgMarkupToDataUri, S as getImageSizeFromBase64, T as decodeBase64ToBytes, a as PRESET_MATERIALS, g as PACKAGE_REL_NS, h as OFFICE_REL, i as PRESET_LINE_DASHES, n as LIGHT_RIGS, r as LIGHT_RIG_DIRECTIONS, s as TEXT_HORZ_OVERFLOW, t as BEVEL_PRESETS, u as checkEnumOrWarn, x as fitSrcRectPercents } from "./st-enums-bVnezkMJ.js";
6
+ import { t as getSlidesForTableRows } from "./autopage-D1sqK6j7.js";
7
+ import { _ as parseFontMetrics, m as FontMetricsRegistry, n as measureText, r as computeTableLayout, t as applyMeasuredFit, v as isFontCollection } from "./fit-CYJ1jkQI.js";
9
8
  import { ZipWriter } from "./zip.js";
10
9
  //#region src/media/content-type.ts
11
10
  /**
@@ -164,400 +163,6 @@ function addBackgroundDefinition(props, target) {
164
163
  }
165
164
  }
166
165
  //#endregion
167
- //#region src/gen/utils.ts
168
- /**
169
- * ts-pptx: shared generator helpers.
170
- *
171
- * The small cross-cutting pieces the OOXML writers all need and that belong to
172
- * no single part:
173
- * - XML text encodeXmlEntities
174
- * - Identifiers & naming getUuid, validateObjectName, getDuplicateObjectNames
175
- * - Slide relationships getNewRelId, isHyperlinkRel
176
- *
177
- * DrawingML fragment builders moved to `gen/drawingml/{color,effect,fill,line}.ts`;
178
- * unit conversion to `units-internal.ts` (over the public primitives in `units.ts`);
179
- * base64/image-header decoding and media content types to `media/`.
180
- */
181
- /**
182
- * Basic UUID Generator Adapted
183
- * @link https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript#answer-2117523
184
- * @param {string} uuidFormat - UUID format
185
- * @returns {string} UUID
186
- */
187
- function getUuid(uuidFormat) {
188
- return uuidFormat.replace(/[xy]/g, function(c) {
189
- const r = Math.random() * 16 | 0;
190
- return (c === "x" ? r : r & 3 | 8).toString(16);
191
- });
192
- }
193
- /**
194
- * Replace special XML characters with HTML-encoded strings.
195
- *
196
- * ELEMENT TEXT ONLY. A literal tab, carriage return or line feed is left alone here because in
197
- * character data it *is* the content (`<a:t>` line breaks depend on it). Inside an attribute value
198
- * the same three characters are destroyed by the parser rather than preserved — see
199
- * {@link encodeXmlAttrValue}, which every attribute-emitting path must use instead.
200
- * @param {string | number} xml - value to encode (numbers are stringified, as callers pass counts/sizes)
201
- * @returns {string} escaped XML
202
- */
203
- function encodeXmlEntities(xml) {
204
- if (typeof xml === "undefined" || xml == null) return "";
205
- const cc = String.fromCharCode;
206
- const illegalXmlCharsRe = new RegExp(`[${cc(0)}-${cc(8)}${cc(11)}${cc(12)}${cc(14)}-${cc(31)}${cc(127)}]`, "g");
207
- return xml.toString().replace(illegalXmlCharsRe, "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
208
- }
209
- /**
210
- * Escape a value destined for an XML **attribute**.
211
- *
212
- * Everything {@link encodeXmlEntities} does, plus the three whitespace characters that survive in
213
- * element text but not in an attribute: XML 1.0 section 3.3.3 requires a parser to normalise a
214
- * literal tab, carriage return or line feed inside an attribute value to a single space *before any
215
- * consumer sees it*. Carrying one across therefore requires a character reference, so a caller's
216
- * `objectName: 'Abschnitts-\nuberschrift'` reads back with its line break rather than a space.
217
- *
218
- * Deliberately a separate function rather than a widening of `encodeXmlEntities`: that helper also
219
- * escapes element text, where a raw newline is meaningful content emitted as-is, and escaping it
220
- * there would change bytes across every text-bearing part in the package.
221
- * @param {string | number} xml - value to encode (numbers are stringified, as callers pass ids/sizes)
222
- * @returns {string} escaped XML, safe to place between attribute quotes
223
- */
224
- function encodeXmlAttrValue(xml) {
225
- return encodeXmlEntities(xml).replace(/\t/g, "&#9;").replace(/\n/g, "&#10;").replace(/\r/g, "&#13;");
226
- }
227
- /**
228
- * Practical maximum length for a `p:cNvPr` object name. PowerPoint does not
229
- * enforce a hard spec limit, but very long names are a strong signal of a bug
230
- * and are unwieldy in the Selection Pane.
231
- */
232
- const MAX_OBJECT_NAME_LENGTH = 255;
233
- /**
234
- * Validate a user-supplied object name and warn (does not throw) when the value
235
- * cannot be preserved as a stable PowerPoint Selection Pane identity. This keeps
236
- * semantic-identity bugs visible at generation time without breaking existing
237
- * decks that pass loose names.
238
- * - Empty/whitespace-only names provide no usable identity.
239
- * - Control characters are stripped by `encodeXmlEntities`, silently changing
240
- * the stored name.
241
- * - Excessively long names may not round-trip through PowerPoint/consumers.
242
- * @param {string} name - the raw (pre-encoding) object name
243
- * @param {string} kind - object kind for the warning message (e.g. 'text')
244
- * @returns {string} the name unchanged (validation only)
245
- */
246
- function validateObjectName(name, kind) {
247
- if (typeof name !== "string") return name;
248
- if (name.trim().length === 0) {
249
- warn("object-name/empty", `${kind} objectName is empty or whitespace-only; it will not provide a stable Selection Pane identity.`);
250
- return name;
251
- }
252
- const cc = String.fromCharCode;
253
- if (new RegExp(`[${cc(0)}-${cc(8)}${cc(11)}${cc(12)}${cc(14)}-${cc(31)}${cc(127)}]`).test(name)) warn("object-name/control-characters", `${kind} objectName "${name}" contains control characters that will be stripped, changing the stored name.`);
254
- if (name.length > MAX_OBJECT_NAME_LENGTH) warn("object-name/too-long", `${kind} objectName exceeds ${MAX_OBJECT_NAME_LENGTH} characters and may not be preserved by PowerPoint.`);
255
- return name;
256
- }
257
- /**
258
- * Return object names that appear more than once in the given list. Used to warn
259
- * when duplicate Selection Pane identities would be emitted on a single slide,
260
- * which breaks consumers (e.g. semantic manifests) that rely on unique names.
261
- * @param {string[]} names - object names emitted on one slide
262
- * @returns {string[]} the duplicated names (each listed once)
263
- */
264
- function getDuplicateObjectNames(names) {
265
- const seen = /* @__PURE__ */ new Set();
266
- const dupes = /* @__PURE__ */ new Set();
267
- names.forEach((name) => {
268
- if (typeof name !== "string" || name.length === 0) return;
269
- if (seen.has(name)) dupes.add(name);
270
- else seen.add(name);
271
- });
272
- return Array.from(dupes);
273
- }
274
- /**
275
- * Get a new rel ID (rId) for charts, media, etc.
276
- *
277
- * Counting is the normal case: a slide's rels are minted in order, so one past the
278
- * total is free. It is not enough on its own, because a rel can arrive on a slide
279
- * carrying an id minted somewhere else — auto-paging re-registers a repeated header
280
- * row's hyperlink on each overflow slide under the id stamped on the shared
281
- * hyperlink object (`gen/define/hyperlinks.ts`). That id is then held but not
282
- * counted-to, so the next mint could hand out the same number and the part would
283
- * declare two `Relationship` elements sharing an `Id` — invalid OPC, and PowerPoint
284
- * resolves such a reference to whichever comes first. Hence the scan: start where
285
- * the count says, then step over anything the slide already holds.
286
- * @param {PresSlideInternal} target - the slide to use
287
- * @returns {number} the lowest free rId at or above the current rel count plus 1
288
- */
289
- function getNewRelId(target) {
290
- const held = /* @__PURE__ */ new Set();
291
- target._rels.forEach((rel) => held.add(rel.rId));
292
- target._relsChart.forEach((rel) => held.add(rel.rId));
293
- target._relsMedia.forEach((rel) => held.add(rel.rId));
294
- let rId = target._rels.length + target._relsChart.length + target._relsMedia.length + 1;
295
- while (held.has(rId)) rId++;
296
- return rId;
297
- }
298
- /**
299
- * Whether a slide relationship is a hyperlink (external URL or internal slide
300
- * link). The relationship `type` is stringly-typed (`'hyperlink'`, `'online'`,
301
- * mixed-case variants), so this centralizes the case-insensitive predicate that
302
- * was duplicated across the slide-rels writer (`gen/slide/slide.ts`) and the inspect path
303
- * (presentation.ts). For an internal slide-to-slide link, `rel.data === 'slide'` and
304
- * `rel.Target` is the 1-based target slide number.
305
- * @param {{ type: string }} rel - a slide relationship
306
- * @returns {boolean} true if the rel is any kind of hyperlink
307
- */
308
- function isHyperlinkRel(rel) {
309
- return rel.type.toLowerCase().includes("hyperlink");
310
- }
311
- //#endregion
312
- //#region src/gen/oxml/el.ts
313
- /**
314
- * ts-pptx: XML element builder (write-side substrate)
315
- *
316
- * The write path historically concatenated template strings and called
317
- * `encodeXmlEntities` by hand at every interpolation — the source of escaping,
318
- * attribute-order and child-sequence bugs. This builder centralizes escaping
319
- * while reproducing today's exact byte layout, so a migration onto it can be
320
- * gated on strict byte-identity (`pnpm run byte-identity:check`) rather than on
321
- * looser schema equivalence.
322
- *
323
- * Mirrors `src/read/oxml/dom.ts` on the write side.
324
- *
325
- * Two rules make byte-identity achievable:
326
- *
327
- * 1. Self-closing is decided by ARITY, never by child value. `el()` always emits
328
- * a paired tag; `voidEl()` always self-closes. This matters because
329
- * `encodeXmlEntities(undefined)` returns `''`, so a value-based rule would
330
- * turn today's `<dc:title></dc:title>` into `<dc:title/>` — a silent byte
331
- * regression on every optional-text element in the tree.
332
- *
333
- * Escaping is split to match: attribute values go through `encodeXmlAttrValue`
334
- * (which adds the `&#9;`/`&#10;`/`&#13;` character references an XML parser
335
- * would otherwise normalise to a space), text children through
336
- * `encodeXmlEntities`, where those same characters are content.
337
- *
338
- * 2. Whitespace is explicit, per element, via `fmt`. Most emitted parts are flat
339
- * (no `fmt` needed at all); the pretty-printed ones carry indentation that is
340
- * not always depth-regular, so it is described rather than derived. Quirks
341
- * like a misindented closing tag stay visible in the call site instead of
342
- * being hidden inside a template literal.
343
- */
344
- /** Wrap already-serialized XML so it is interpolated verbatim. */
345
- function raw(xml) {
346
- return { raw: xml };
347
- }
348
- function isRaw(child) {
349
- return typeof child === "object" && child !== null && typeof child.raw === "string";
350
- }
351
- function openTag(name, attrs, fmt) {
352
- let out = (fmt?.openPrefix ?? "") + "<" + name;
353
- for (const [key, value] of Object.entries(attrs ?? {})) {
354
- if (value === void 0 || value === null) continue;
355
- out += " " + key + "=\"" + encodeXmlAttrValue(value) + "\"";
356
- }
357
- return out;
358
- }
359
- /**
360
- * Paired element: `<name …>children</name>` — emitted even when `children` is
361
- * empty or `undefined`. Text children are escaped; `raw()` children are not.
362
- */
363
- function el(name, attrs, children, fmt) {
364
- let out = openTag(name, attrs, fmt) + ">";
365
- const childPrefix = fmt?.childPrefix ?? "";
366
- for (const child of Array.isArray(children) ? children : [children]) {
367
- if (child === void 0 || child === null) continue;
368
- out += childPrefix + (isRaw(child) ? child.raw : encodeXmlEntities(child));
369
- }
370
- return out + (fmt?.closePrefix ?? "") + "</" + name + ">";
371
- }
372
- /**
373
- * Self-closing element: `<name …/>`. `fmt.closePrefix` goes before the `/>` — a
374
- * handful of emitters write `<a:avLst />` with a space there, and that space is
375
- * byte-significant like any other.
376
- */
377
- function voidEl(name, attrs, fmt) {
378
- return openTag(name, attrs, fmt) + (fmt?.closePrefix ?? "") + "/>";
379
- }
380
- //#endregion
381
- //#region src/gen/drawingml/color.ts
382
- /**
383
- * ts-pptx: DrawingML color elements
384
- *
385
- * Emit the bare `<a:srgbClr>` / `<a:schemeClr>` color element that every other
386
- * color context builds on (solid fills, gradient stops, effect alphas, line
387
- * fills, highlights, patterns).
388
- *
389
- * The RGB->hex helpers this module used to carry now live in
390
- * `gen/table/html-dom.ts`, their only caller: they are reachable exclusively from
391
- * the browser-only `tableToSlides` path, so keeping them here made them permanent
392
- * dead weight in the Node chunk's coverage report (33% functions on a file that is
393
- * otherwise fully exercised) while html-dom.ts is coverage-excluded wholesale.
394
- */
395
- /**
396
- * Opening delimiter of an `<a:alpha>` child, for detecting one a caller already
397
- * supplied. A prefix rather than a whole element: the value is the caller's, and
398
- * this asks only whether the element is present.
399
- */
400
- const ALPHA_TAG = "<a:alpha";
401
- /**
402
- * Emit a bare DrawingML color element — either `<a:schemeClr>` (scheme color) or `<a:srgbClr>`
403
- * (hex RGB). This is a low-level primitive: it is the shared building block for every color
404
- * context (solid fills, gradient stops, `<a:alpha>` on shadows/glow, line fills, highlight,
405
- * underline, patterns, …), so it is intentionally NOT limited to solid fills and cannot be
406
- * folded into `genXmlColorSelection`.
407
- *
408
- * When you specifically want a *solid fill*, prefer `genXmlColorSelection` (`fill.ts`), which
409
- * wraps this in `<a:solidFill>…</a:solidFill>` and also handles `alpha`/`transparency` and the
410
- * gradient/pattern/image fill types — reach for `createColorElement` directly only when you
411
- * need the raw color element (e.g. inside `<a:ln>`, `<a:gs>`, an effect, or a highlight).
412
- * @param {string|SCHEME_COLORS} colorStr - hex RGB (e.g. "FFFF00") or a scheme color constant (e.g. SchemeColor.ACCENT1)
413
- * @param {string} [innerElements] - additional elements that adjust the color, nested inside the color element
414
- * @returns {string} XML string
415
- */
416
- function createColorElement(colorStr, innerElements) {
417
- if (typeof colorStr !== "string") {
418
- warn("color/not-a-string", `createColorElement: expected a string color value, got ${typeof colorStr}. "${DEF_FONT_COLOR}" used instead.`);
419
- colorStr = DEF_FONT_COLOR;
420
- }
421
- let colorVal = (colorStr || "").replace("#", "");
422
- if (/^[0-9a-fA-F]{8}$/.test(colorVal)) {
423
- if (!innerElements?.includes(ALPHA_TAG)) {
424
- const alphaHex = colorVal.slice(6, 8);
425
- innerElements = voidEl("a:alpha", { val: Math.round(parseInt(alphaHex, 16) / 255 * PERCENT_SCALE) }) + (innerElements || "");
426
- }
427
- colorVal = colorVal.slice(0, 6);
428
- }
429
- if (!REGEX_HEX_COLOR.test(colorVal) && !Object.values(SchemeColor).includes(colorVal)) {
430
- warn("color/invalid-value", `"${colorVal}" is not a valid scheme color or hex RGB! "${DEF_FONT_COLOR}" used instead. Only provide 6-digit RGB or 'SchemeColor' values!`);
431
- colorVal = DEF_FONT_COLOR;
432
- }
433
- const isHex = REGEX_HEX_COLOR.test(colorVal);
434
- const name = isHex ? "a:srgbClr" : "a:schemeClr";
435
- const attrs = { val: isHex ? colorVal.toUpperCase() : colorVal };
436
- return innerElements ? el(name, attrs, raw(innerElements)) : voidEl(name, attrs);
437
- }
438
- //#endregion
439
- //#region src/gen/drawingml/effect.ts
440
- /**
441
- * ts-pptx: DrawingML effects (glow and shadow)
442
- *
443
- * Emit the `<a:glow>` / `<a:outerShdw>` / `<a:innerShdw>` children of an
444
- * `<a:effectLst>`, and normalize the user-facing `shadow` options into the
445
- * internal shape every emit site reads.
446
- * @see http://officeopenxml.com/drwSp-effects.php
447
- */
448
- /**
449
- * Creates `a:glow` element
450
- * @param {TextGlowProps} options glow properties
451
- * @param {TextGlowProps} defaults defaults for unspecified properties in `opts`
452
- * @see http://officeopenxml.com/drwSp-effects.php
453
- * { size: 8, color: 'FFFFFF', opacity: 0.75 };
454
- */
455
- function createGlowElement(options, defaults) {
456
- const opts = {
457
- ...defaults,
458
- ...options
459
- };
460
- const size = Math.round(opts.size * EMU_PER_POINT);
461
- const color = opts.color || "000000";
462
- const opacity = opacityToAlpha(opts.opacity ?? 0);
463
- return el("a:glow", { rad: size }, raw(createColorElement(color, alpha(opacity))));
464
- }
465
- /** The `<a:alpha>` child both effects hang off their color element. */
466
- function alpha(value) {
467
- return voidEl("a:alpha", { val: value });
468
- }
469
- /**
470
- * Creates an `a:outerShdw`/`a:innerShdw` element for a text run, shape, image, or chart.
471
- * Returns the shadow element only (no wrapping `a:effectLst`) so callers can either combine
472
- * it with other effects (e.g. glow) inside one `a:effectLst`, or wrap a lone shadow via
473
- * {@link createShadowEffectLst}.
474
- *
475
- * Colors go through {@link createColorElement}, so scheme colors (e.g. `accent1`) are honored
476
- * — earlier per-site copies hardcoded `a:srgbClr` and silently emitted invalid OOXML for them.
477
- * @param {ShadowPropsInternal} options shadow properties
478
- * @param {ShadowPropsInternal} defaults defaults for unspecified properties in `options`
479
- * @see http://officeopenxml.com/drwSp-effects.php
480
- * @returns {string} XML string, or '' when type is 'none'
481
- */
482
- function createShadowElement(options, defaults) {
483
- const opts = {
484
- ...defaults,
485
- ...options
486
- };
487
- if (opts.type === "none") return "";
488
- const type = opts.type || "outer";
489
- const blur = valToPts(opts.blur ?? 0);
490
- const offset = valToPts(opts.offset ?? 0);
491
- const angle = Math.round((opts.angle ?? 0) * ANGLE_UNITS_PER_DEGREE);
492
- const opacity = Math.round((opts._alpha ?? .75) * PERCENT_SCALE);
493
- const color = opts.color || "000000";
494
- const attrs = {
495
- ...type === "outer" ? {
496
- sx: 1e5,
497
- sy: 1e5,
498
- kx: 0,
499
- ky: 0,
500
- algn: "bl",
501
- rotWithShape: opts.rotateWithShape ? 1 : 0
502
- } : {},
503
- blurRad: blur,
504
- dist: offset,
505
- dir: angle
506
- };
507
- return el(`a:${type}Shdw`, attrs, raw(createColorElement(color, alpha(opacity))));
508
- }
509
- /**
510
- * Wraps a lone shadow in the `a:effectLst` that CT_ShapeProperties (shapes/images) and chart
511
- * marker/data-point properties require. Returns a self-closing `<a:effectLst/>` when there is
512
- * no shadow (missing/non-object options, or `type: 'none'`), matching the "no effects" element
513
- * PowerPoint emits. Use this instead of {@link createShadowElement} when the shadow is the only
514
- * effect; use `createShadowElement` directly when combining with other effects (e.g. glow).
515
- * @param {ShadowPropsInternal} options shadow properties
516
- * @param {ShadowPropsInternal} defaults defaults for unspecified properties in `options`
517
- * @returns {string} `<a:effectLst>…</a:effectLst>` or `<a:effectLst/>`
518
- */
519
- function createShadowEffectLst(options, defaults) {
520
- if (!options || typeof options !== "object") return voidEl("a:effectLst");
521
- const inner = createShadowElement(options, defaults);
522
- return inner ? el("a:effectLst", null, raw(inner)) : voidEl("a:effectLst");
523
- }
524
- /**
525
- * Checks shadow options passed by user and performs corrections if needed.
526
- * @param {ShadowProps} ShadowProps - shadow options
527
- */
528
- function correctShadowOptions(ShadowProps) {
529
- if (!ShadowProps || typeof ShadowProps !== "object") return;
530
- const corrected = ShadowProps;
531
- if (corrected.type !== "outer" && corrected.type !== "inner" && corrected.type !== "none") {
532
- warn("shadow/invalid-type", "shadow.type options are `outer`, `inner` or `none`.");
533
- corrected.type = "outer";
534
- }
535
- if (corrected.angle) {
536
- if (isNaN(Number(corrected.angle)) || corrected.angle < 0 || corrected.angle > 359) {
537
- warn("shadow/angle-out-of-range", "shadow.angle can only be 0-359");
538
- corrected.angle = 270;
539
- }
540
- corrected.angle = Math.round(Number(corrected.angle));
541
- }
542
- if (corrected.transparency !== void 0) {
543
- const pct = Number(corrected.transparency);
544
- if (isNaN(pct) || pct < 0 || pct > 100) warn("shadow/transparency-out-of-range", "shadow.transparency can only be 0-100");
545
- else corrected._alpha = 1 - pct / 100;
546
- }
547
- if (corrected.color) {
548
- if (corrected.color.startsWith("#")) {
549
- warn("shadow/color-has-hash", "shadow.color should not include hash (#) character, , e.g. \"FF0000\"");
550
- corrected.color = corrected.color.replace("#", "");
551
- }
552
- if (/^[0-9a-fA-F]{8}$/.test(corrected.color)) {
553
- const alphaHex = corrected.color.slice(6, 8);
554
- if (corrected._alpha === void 0) corrected._alpha = parseInt(alphaHex, 16) / 255;
555
- corrected.color = corrected.color.slice(0, 6);
556
- }
557
- }
558
- return corrected;
559
- }
560
- //#endregion
561
166
  //#region src/gen/define/chart.ts
562
167
  /**
563
168
  * ts-pptx: Chart Definition
@@ -937,6 +542,7 @@ function addChartDefinition(target, type, data, opt) {
937
542
  }
938
543
  const options = copyChartOptions(tmpOpt && typeof tmpOpt === "object" ? tmpOpt : {});
939
544
  const callerSetBarGapWidthPct = typeof options.barGapWidthPct === "number" && !isNaN(options.barGapWidthPct);
545
+ for (const name of tmpTypes ? tmpTypes.map((sub) => sub.type) : [type]) if (!isChartType(name)) throw new InvalidOptionError("chart/unknown-type", `addChart: "${String(name)}" is not a chart type. Valid types are: ${Object.values(ChartType).join(", ")}.`, { detail: { type: name } });
940
546
  options._type = tmpTypes ?? asChartType(type);
941
547
  options.x = typeof options.x !== "undefined" && options.x != null && !isNaN(Number(options.x)) ? options.x : 1;
942
548
  options.y = typeof options.y !== "undefined" && options.y != null && !isNaN(Number(options.y)) ? options.y : 1;
@@ -1409,17 +1015,19 @@ function addImageDefinition(target, opt) {
1409
1015
  });
1410
1016
  newObject.imageRid = imageRelId;
1411
1017
  }
1412
- if (typeof objHyperlink === "object") if (!objHyperlink.url && !objHyperlink.slide) throw new InvalidOptionError("hyperlink/missing-target", "addImage: `hyperlink` requires either `url` or `slide`");
1413
- else {
1414
- imageRelId++;
1415
- target._rels.push({
1416
- type: "hyperlink",
1417
- data: objHyperlink.slide ? "slide" : "dummy",
1418
- rId: imageRelId,
1419
- Target: objHyperlink.url ? objHyperlink.url : String(objHyperlink.slide)
1420
- });
1421
- objHyperlink._rId = imageRelId;
1422
- newObject.hyperlink = objHyperlink;
1018
+ if (typeof objHyperlink === "object") {
1019
+ if (!objHyperlink.url && !objHyperlink.slide) throw new InvalidOptionError("hyperlink/missing-target", "addImage: `hyperlink` requires either `url` or `slide`");
1020
+ else {
1021
+ imageRelId++;
1022
+ target._rels.push({
1023
+ type: "hyperlink",
1024
+ data: objHyperlink.slide ? "slide" : "dummy",
1025
+ rId: imageRelId,
1026
+ Target: objHyperlink.url ? objHyperlink.url : String(objHyperlink.slide)
1027
+ });
1028
+ objHyperlink._rId = imageRelId;
1029
+ newObject.hyperlink = objHyperlink;
1030
+ }
1423
1031
  }
1424
1032
  target._slideObjects.push(newObject);
1425
1033
  }
@@ -1580,10 +1188,14 @@ function addTextDefinition(target, text, opts, isPlaceholder) {
1580
1188
  itemOpts._bodyProp.vert = itemOpts.textDirection ?? itemOpts.vert;
1581
1189
  itemOpts._bodyProp.wrap = typeof itemOpts.wrap === "boolean" ? itemOpts.wrap : true;
1582
1190
  itemOpts._bodyProp.prstTxWarp = itemOpts.textWarp;
1583
- if (itemOpts.columns !== void 0) if (typeof itemOpts.columns !== "number" || isNaN(itemOpts.columns) || itemOpts.columns < 1 || itemOpts.columns > 16) warn("text/invalid-columns", "text `columns` must be a number 1-16 (ignoring value)");
1584
- else itemOpts._bodyProp.numCol = Math.round(itemOpts.columns);
1585
- if (itemOpts.columnSpacing !== void 0) if (typeof itemOpts.columnSpacing !== "number" || isNaN(itemOpts.columnSpacing) || itemOpts.columnSpacing < 0) warn("text/invalid-column-spacing", "text `columnSpacing` must be a number >= 0 (ignoring value)");
1586
- else itemOpts._bodyProp.spcCol = valToPts(itemOpts.columnSpacing);
1191
+ if (itemOpts.columns !== void 0) {
1192
+ if (typeof itemOpts.columns !== "number" || isNaN(itemOpts.columns) || itemOpts.columns < 1 || itemOpts.columns > 16) warn("text/invalid-columns", "text `columns` must be a number 1-16 (ignoring value)");
1193
+ else itemOpts._bodyProp.numCol = Math.round(itemOpts.columns);
1194
+ }
1195
+ if (itemOpts.columnSpacing !== void 0) {
1196
+ if (typeof itemOpts.columnSpacing !== "number" || isNaN(itemOpts.columnSpacing) || itemOpts.columnSpacing < 0) warn("text/invalid-column-spacing", "text `columnSpacing` must be a number >= 0 (ignoring value)");
1197
+ else itemOpts._bodyProp.spcCol = valToPts(itemOpts.columnSpacing);
1198
+ }
1587
1199
  if (typeof itemOpts.underline === "boolean" && itemOpts.underline === true) itemOpts.underline = { style: "sng" };
1588
1200
  {
1589
1201
  const align = (itemOpts.align || "").toLowerCase();
@@ -2123,6 +1735,7 @@ function addNotesDefinition(target, notes) {
2123
1735
  }
2124
1736
  //#endregion
2125
1737
  //#region src/gen/define/ole.ts
1738
+ const OD$1 = "application/vnd.openxmlformats-officedocument.";
2126
1739
  /** Rel type for an embedded OPC package (an Office file, itself a zip). */
2127
1740
  const PACKAGE_REL = OFFICE_REL + "package";
2128
1741
  /** Rel type for a generic OLE-server blob — a compound-file `.bin` (ECMA-376 Part 1 §15.2.10). */
@@ -2134,7 +1747,7 @@ const OLE_OBJECT_REL = OFFICE_REL + "oleObject";
2134
1747
  */
2135
1748
  const OLE_FORMATS = {
2136
1749
  xlsx: {
2137
- contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1750
+ contentType: OD$1 + "spreadsheetml.sheet",
2138
1751
  relType: PACKAGE_REL,
2139
1752
  progId: "Excel.Sheet.12",
2140
1753
  name: "Worksheet"
@@ -2146,7 +1759,7 @@ const OLE_FORMATS = {
2146
1759
  name: "Worksheet"
2147
1760
  },
2148
1761
  docx: {
2149
- contentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1762
+ contentType: OD$1 + "wordprocessingml.document",
2150
1763
  relType: PACKAGE_REL,
2151
1764
  progId: "Word.Document.12",
2152
1765
  name: "Document"
@@ -2158,7 +1771,7 @@ const OLE_FORMATS = {
2158
1771
  name: "Document"
2159
1772
  },
2160
1773
  pptx: {
2161
- contentType: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
1774
+ contentType: OD$1 + "presentationml.presentation",
2162
1775
  relType: PACKAGE_REL,
2163
1776
  progId: "PowerPoint.Show.12",
2164
1777
  name: "Presentation"
@@ -2172,7 +1785,7 @@ const OLE_FORMATS = {
2172
1785
  };
2173
1786
  /** Fallback for any payload that is not one of the {@link OLE_FORMATS} Office packages. */
2174
1787
  const BIN_FORMAT = {
2175
- contentType: "application/vnd.openxmlformats-officedocument.oleObject",
1788
+ contentType: OD$1 + "oleObject",
2176
1789
  relType: OLE_OBJECT_REL,
2177
1790
  progId: "Package",
2178
1791
  name: "Object"
@@ -3095,140 +2708,6 @@ var SlideBuilder = class {
3095
2708
  }
3096
2709
  };
3097
2710
  //#endregion
3098
- //#region src/gen/drawingml/fill.ts
3099
- /**
3100
- * ts-pptx: DrawingML fills
3101
- *
3102
- * Emit the fill group a shape, cell, chart element, or line paint can carry:
3103
- * `<a:solidFill>`, `<a:gradFill>`, `<a:pattFill>`, `<a:blipFill>`.
3104
- * {@link genXmlColorSelection} is the dispatch every caller should reach for;
3105
- * the per-kind builders are exported for the few sites that know their fill kind.
3106
- */
3107
- function boolToXml(value) {
3108
- return value ? "1" : "0";
3109
- }
3110
- function normalizeGradientAngle(angle) {
3111
- const degrees = angle ?? 0;
3112
- if (typeof degrees !== "number" || !Number.isFinite(degrees)) throw new InvalidOptionError("gradient/angle-non-finite", "Gradient angle must be a finite number.");
3113
- return convertRotationDegrees((degrees % 360 + 360) % 360);
3114
- }
3115
- function alphaFromTransparency(transparency) {
3116
- return transparency ? voidEl("a:alpha", { val: transparencyToAlpha(transparency) }) : "";
3117
- }
3118
- function normalizeGradientStops(stops) {
3119
- if (!Array.isArray(stops) || stops.length < 2) throw new InvalidOptionError("gradient/too-few-stops", "Gradient fill requires at least two stops.");
3120
- return stops.map((stop) => {
3121
- if (!stop || typeof stop.position !== "number" || !Number.isFinite(stop.position)) throw new InvalidOptionError("gradient/stop-position-non-finite", "Gradient stop position must be a finite number from 0 to 100.");
3122
- if (stop.position < 0 || stop.position > 100) throw new InvalidOptionError("gradient/stop-position-out-of-range", "Gradient stop position must be from 0 to 100.");
3123
- return stop;
3124
- }).sort((a, b) => a.position - b.position);
3125
- }
3126
- /**
3127
- * Create a native DrawingML gradient fill.
3128
- * @param {GradientFillProps} gradient gradient fill options
3129
- * @returns XML string
3130
- */
3131
- function genXmlGradientFill(gradient) {
3132
- if (!gradient || gradient.kind !== "linear" && gradient.kind !== "radial") throw new UnsupportedFeatureError("gradient/type-unsupported", "Gradient fill currently supports only linear and radial gradients.");
3133
- if (typeof gradient.rotateWithShape !== "undefined" && typeof gradient.rotateWithShape !== "boolean") throw new InvalidOptionError("gradient/rotate-with-shape-not-boolean", "Gradient rotateWithShape must be a boolean.");
3134
- const stops = normalizeGradientStops(gradient.stops);
3135
- const rotWithShape = gradient.rotateWithShape ?? true;
3136
- const gsLst = el("a:gsLst", null, stops.map((stop) => raw(el("a:gs", { pos: Math.round(stop.position * FIXED_PCT_PER_PERCENT) }, raw(createColorElement(stop.color, alphaFromTransparency(stop.transparency)))))));
3137
- let shape;
3138
- if (gradient.kind === "radial") {
3139
- const cx = Math.max(0, Math.min(100, gradient.center?.x ?? 50));
3140
- const cy = Math.max(0, Math.min(100, gradient.center?.y ?? 50));
3141
- shape = el("a:path", { path: "circle" }, raw(voidEl("a:fillToRect", {
3142
- l: Math.round(cx * FIXED_PCT_PER_PERCENT),
3143
- t: Math.round(cy * FIXED_PCT_PER_PERCENT),
3144
- r: Math.round((100 - cx) * FIXED_PCT_PER_PERCENT),
3145
- b: Math.round((100 - cy) * FIXED_PCT_PER_PERCENT)
3146
- })));
3147
- } else {
3148
- if (typeof gradient.scaled !== "undefined" && typeof gradient.scaled !== "boolean") throw new InvalidOptionError("gradient/scaled-not-boolean", "Gradient scaled must be a boolean.");
3149
- shape = voidEl("a:lin", {
3150
- ang: normalizeGradientAngle(gradient.angle),
3151
- scaled: typeof gradient.scaled === "boolean" ? boolToXml(gradient.scaled) : void 0
3152
- });
3153
- }
3154
- return el("a:gradFill", { rotWithShape: boolToXml(rotWithShape) }, [raw(gsLst), raw(shape)]);
3155
- }
3156
- /**
3157
- * Create a native DrawingML pattern fill.
3158
- * @param {PatternFillProps} pattern pattern fill options
3159
- * @returns XML string
3160
- */
3161
- function genXmlPatternFill(pattern) {
3162
- if (!pattern) throw new InvalidOptionError("pattern-fill/missing-pattern", "Pattern fill requires a pattern object.");
3163
- const fgColor = pattern.fgColor ?? "000000";
3164
- const bgColor = pattern.bgColor ?? "FFFFFF";
3165
- return el("a:pattFill", { prst: pattern.preset }, [raw(el("a:fgClr", null, raw(createColorElement(fgColor)))), raw(el("a:bgClr", null, raw(createColorElement(bgColor))))]);
3166
- }
3167
- /**
3168
- * Create a native DrawingML picture (image) fill.
3169
- * The media relationship is registered when the object is added; this only emits
3170
- * the `<a:blipFill>` referencing the pre-resolved rId.
3171
- * @param {ShapeFillProps} props fill props (must carry a resolved `_imgRid`)
3172
- * @returns XML string
3173
- */
3174
- function genXmlImageFill(props) {
3175
- if (!props || typeof props._imgRid !== "number") {
3176
- warn("image-fill/unresolved-media", "image fill is missing its resolved media reference; falling back to no fill. Provide `image: { path }` or `image: { data }`.");
3177
- return voidEl("a:noFill");
3178
- }
3179
- const alpha = props.transparency;
3180
- const blipInner = alpha ? voidEl("a:alphaModFix", { amt: Math.round((100 - alpha) * FIXED_PCT_PER_PERCENT) }) : "";
3181
- return el("a:blipFill", {
3182
- dpi: 0,
3183
- rotWithShape: 1
3184
- }, [
3185
- raw(el("a:blip", { "r:embed": `rId${props._imgRid}` }, raw(blipInner))),
3186
- raw(voidEl("a:srcRect")),
3187
- raw(el("a:stretch", null, raw(voidEl("a:fillRect"))))
3188
- ]);
3189
- }
3190
- /**
3191
- * Create color selection
3192
- * @param {Color | ShapeFillProps | ShapeLineProps} props fill props
3193
- * @returns XML string
3194
- */
3195
- function genXmlColorSelection(props) {
3196
- let fillType = "solid";
3197
- let colorVal = "";
3198
- let internalElements = "";
3199
- let outText = "";
3200
- if (props) {
3201
- if (typeof props === "string") colorVal = props;
3202
- else {
3203
- if (props.type) fillType = props.type;
3204
- if (props.color) colorVal = props.color;
3205
- internalElements += alphaFromTransparency(props.transparency);
3206
- }
3207
- switch (fillType) {
3208
- case "none":
3209
- outText += voidEl("a:noFill");
3210
- break;
3211
- case "inherit":
3212
- outText += "";
3213
- break;
3214
- case "solid":
3215
- outText += el("a:solidFill", null, raw(createColorElement(colorVal, internalElements)));
3216
- break;
3217
- case "gradient":
3218
- outText += genXmlGradientFill(typeof props === "string" ? void 0 : props.gradient);
3219
- break;
3220
- case "pattern":
3221
- outText += genXmlPatternFill(typeof props === "string" ? void 0 : props.pattern);
3222
- break;
3223
- case "image":
3224
- outText += genXmlImageFill(typeof props === "string" ? void 0 : props);
3225
- break;
3226
- default: outText += "";
3227
- }
3228
- }
3229
- return outText;
3230
- }
3231
- //#endregion
3232
2711
  //#region src/gen/drawingml/line.ts
3233
2712
  /** Every key `BorderProps` defines. Keep in step with the interface in `types/style.ts`. */
3234
2713
  const BORDER_KEYS = [
@@ -3480,13 +2959,14 @@ function genXmlTitle(opts, chartX, chartY) {
3480
2959
  * @return {string} XML
3481
2960
  */
3482
2961
  function createGridLineElement(glOpts) {
3483
- return el("c:majorGridlines", null, raw(el("c:spPr", null, raw(el("a:ln", {
2962
+ const line = el("a:ln", {
3484
2963
  w: valToPts(glOpts.size || DEF_CHART_GRIDLINE.size || 1),
3485
2964
  cap: createLineCap(glOpts.cap || DEF_CHART_GRIDLINE.cap)
3486
2965
  }, [raw(" " + el("a:solidFill", null, raw(createColorElement(glOpts.color || DEF_GRIDLINE_COLOR)))), raw(" " + voidEl("a:prstDash", { val: glOpts.style || DEF_CHART_GRIDLINE.style }) + voidEl("a:round"))], {
3487
2966
  openPrefix: " ",
3488
2967
  closePrefix: " "
3489
- })), {
2968
+ });
2969
+ return el("c:majorGridlines", null, raw(el("c:spPr", null, raw(line), {
3490
2970
  openPrefix: " ",
3491
2971
  closePrefix: " "
3492
2972
  })));
@@ -3580,10 +3060,11 @@ function createSerLinesElement(opt) {
3580
3060
  if (!opt) return "";
3581
3061
  if (opt === true) return "<c:serLines/>";
3582
3062
  if (opt.style === "none") return "";
3583
- return el("c:serLines", null, raw(el("c:spPr", null, raw(el("a:ln", {
3063
+ const line = el("a:ln", {
3584
3064
  w: valToPts(opt.size || DEF_CHART_GRIDLINE.size || 1),
3585
3065
  cap: createLineCap(opt.cap || DEF_CHART_GRIDLINE.cap)
3586
- }, [raw(el("a:solidFill", null, raw(createColorElement(opt.color || DEF_GRIDLINE_COLOR)))), raw(voidEl("a:prstDash", { val: opt.style || DEF_CHART_GRIDLINE.style }) + voidEl("a:round"))])))));
3066
+ }, [raw(el("a:solidFill", null, raw(createColorElement(opt.color || DEF_GRIDLINE_COLOR)))), raw(voidEl("a:prstDash", { val: opt.style || DEF_CHART_GRIDLINE.style }) + voidEl("a:round"))]);
3067
+ return el("c:serLines", null, raw(el("c:spPr", null, raw(line))));
3587
3068
  }
3588
3069
  /**
3589
3070
  * Build the `<c:leaderLines>` element for pie/doughnut data labels.
@@ -3601,7 +3082,9 @@ function createSerLinesElement(opt) {
3601
3082
  function createLeaderLinesElement(opts) {
3602
3083
  if (!opts.showLeaderLines) return "";
3603
3084
  if (!opts.leaderLineColor && opts.leaderLineSize == null) return "";
3604
- return `<c:leaderLines><c:spPr><a:ln w="${valToPts(opts.leaderLineSize ?? .75)}" cap="flat">${genXmlColorSelection(opts.leaderLineColor || "808080")}<a:prstDash val="solid"/><a:round/></a:ln><a:effectLst/></c:spPr></c:leaderLines>`;
3085
+ const w = valToPts(opts.leaderLineSize ?? .75);
3086
+ const color = opts.leaderLineColor || "808080";
3087
+ return `<c:leaderLines><c:spPr><a:ln w="${w}" cap="flat">${genXmlColorSelection(color)}<a:prstDash val="solid"/><a:round/></a:ln><a:effectLst/></c:spPr></c:leaderLines>`;
3605
3088
  }
3606
3089
  /**
3607
3090
  * Build a single custom `<c:dLbl>` (rich-text data label) overriding one data point's label.
@@ -3677,8 +3160,10 @@ function makeSeriesDataPointsXml(chartType, obj, opts, varyColors) {
3677
3160
  ...pattern,
3678
3161
  fgColor: fillColor
3679
3162
  } : pattern);
3680
- else if (fillColor) if (chartType === "bar3D") xml += `<a:ln>${genXmlColorSelection(fillColor)}</a:ln>`;
3681
- else xml += genXmlColorSelection(fillColor);
3163
+ else if (fillColor) {
3164
+ if (chartType === "bar3D") xml += `<a:ln>${genXmlColorSelection(fillColor)}</a:ln>`;
3165
+ else xml += genXmlColorSelection(fillColor);
3166
+ }
3682
3167
  if (border) xml += createChartBorderLine(border);
3683
3168
  }
3684
3169
  xml += createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW);
@@ -5089,9 +4574,10 @@ function makeXmlCharts(rel) {
5089
4574
  usesSecondaryCatAxis = usesSecondaryCatAxis || (options.secondaryCatAxis ?? false);
5090
4575
  const subType = asChartType(type.type);
5091
4576
  const usesValueXAxis = subType === "scatter" || subType === "bubble" || subType === "bubble3D";
5092
- if (options.secondaryCatAxis) if (usesValueXAxis) secondaryCatAxisValType = subType;
5093
- else secondaryCatAxisHasCategoryChart = true;
5094
- else if (usesValueXAxis) primaryCatAxisValType = subType;
4577
+ if (options.secondaryCatAxis) {
4578
+ if (usesValueXAxis) secondaryCatAxisValType = subType;
4579
+ else secondaryCatAxisHasCategoryChart = true;
4580
+ } else if (usesValueXAxis) primaryCatAxisValType = subType;
5095
4581
  else primaryCatAxisHasCategoryChart = true;
5096
4582
  strXml += makeChartType(subType, type.data, options, valAxisId, catAxisId);
5097
4583
  });
@@ -5182,7 +4668,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
5182
4668
  case "surface": return makeSurfacePlot(chartType, data, opts, valAxisId, catAxisId, valFmtCode);
5183
4669
  case "doughnut":
5184
4670
  case "pie": return makePiePlot(chartType, data, opts, valFmtCode);
5185
- default: return "";
4671
+ default: throw new InternalError("chart/type-not-routed", `makeChartType: "${String(chartType)}" has no <c:...Chart> plot — the chartEx catalog is built by makeXmlChartEx, and a newly added classic type needs an arm here`, { detail: { chartType } });
5186
4672
  }
5187
4673
  }
5188
4674
  //#endregion
@@ -5245,7 +4731,8 @@ function makeChartExData(rel) {
5245
4731
  formatCode: "General"
5246
4732
  }, raw(valPts)))]);
5247
4733
  const data = el("cx:data", { id: 0 }, [raw(catDim), raw(valDim)]);
5248
- return el("cx:chartData", null, [raw(voidEl("cx:externalData", { "r:id": "rId1" })), raw(data)]);
4734
+ const externalData = voidEl("cx:externalData", { "r:id": "rId1" });
4735
+ return el("cx:chartData", null, [raw(externalData), raw(data)]);
5249
4736
  }
5250
4737
  /**
5251
4738
  * The `Sheet1` cell holding the series name (the numeric dimension's header) — column immediately
@@ -5264,7 +4751,8 @@ const A_NS = "http://schemas.openxmlformats.org/drawingml/2006/main";
5264
4751
  const R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
5265
4752
  /**
5266
4753
  * Map a chartEx {@link ChartType} to its `<cx:series layoutId>` token (the CT_SeriesLayout value
5267
- * PowerPoint keys the chart geometry on). Returns `''` for a non-chartEx type.
4754
+ * PowerPoint keys the chart geometry on). Throws on anything it does not own — see the `default`
4755
+ * arm for which members those are and why reaching it is a routing bug.
5268
4756
  */
5269
4757
  function chartExLayoutId(type) {
5270
4758
  switch (type) {
@@ -5275,7 +4763,7 @@ function chartExLayoutId(type) {
5275
4763
  case "histogram": return "clusteredColumn";
5276
4764
  case "boxWhisker": return "boxWhisker";
5277
4765
  case "regionMap": return "regionMap";
5278
- default: return "";
4766
+ default: throw new InternalError("chart/type-not-routed", `chartExLayoutId: "${String(type)}" has no <cx:series> layoutId — classic chart types belong to makeXmlCharts, pareto is built by makeParetoSeries, and a newly added chartEx type needs an arm here`, { detail: { chartType: type } });
5279
4767
  }
5280
4768
  }
5281
4769
  /**
@@ -5353,11 +4841,12 @@ function makeBoxWhiskerLayoutPr(stats) {
5353
4841
  * renders blank if the names don't match PowerPoint's geography database.
5354
4842
  */
5355
4843
  function makeGeographyLayoutPr(geo) {
5356
- return el("cx:layoutPr", null, raw(voidEl("cx:geography", {
4844
+ const geography = voidEl("cx:geography", {
5357
4845
  cultureLanguage: geo?.cultureLanguage || "en-US",
5358
4846
  cultureRegion: geo?.cultureRegion || "US",
5359
4847
  attribution: "Powered by Bing"
5360
- })));
4848
+ });
4849
+ return el("cx:layoutPr", null, raw(geography));
5361
4850
  }
5362
4851
  /** The `<cx:tx>` series-name cell/value block (shared by every layout that names its series). */
5363
4852
  function makeChartExSeriesName(rel) {
@@ -5403,8 +4892,10 @@ function makeChartExSeries(rel) {
5403
4892
  const type = opts._type;
5404
4893
  if (type === "pareto") return makeParetoSeries(rel);
5405
4894
  let layoutPr = "";
5406
- if (type === "waterfall" && Array.isArray(opts.subtotals) && opts.subtotals.length > 0) layoutPr = el("cx:layoutPr", null, raw(el("cx:subtotals", null, raw(opts.subtotals.map((idx) => voidEl("cx:idx", { val: idx })).join("")))));
5407
- else if (type === "treemap") layoutPr = el("cx:layoutPr", null, raw(voidEl("cx:parentLabelLayout", { val: "overlapping" })));
4895
+ if (type === "waterfall" && Array.isArray(opts.subtotals) && opts.subtotals.length > 0) {
4896
+ const idxs = opts.subtotals.map((idx) => voidEl("cx:idx", { val: idx })).join("");
4897
+ layoutPr = el("cx:layoutPr", null, raw(el("cx:subtotals", null, raw(idxs))));
4898
+ } else if (type === "treemap") layoutPr = el("cx:layoutPr", null, raw(voidEl("cx:parentLabelLayout", { val: "overlapping" })));
5408
4899
  else if (type === "histogram") layoutPr = el("cx:layoutPr", null, raw(makeChartExBinning(opts.binning)));
5409
4900
  else if (type === "boxWhisker") layoutPr = makeBoxWhiskerLayoutPr(opts.statistics);
5410
4901
  else if (type === "regionMap") layoutPr = makeGeographyLayoutPr(opts.geography);
@@ -5473,15 +4964,16 @@ function makeChartExTitle(rel) {
5473
4964
  const color = rel.opts.titleColor || "000000";
5474
4965
  const face = rel.opts.titleFontFace || "Calibri";
5475
4966
  const rPr = el("a:defRPr", null, [raw(genXmlColorSelection(color)), raw(createChartTextFonts(face))]);
4967
+ const rich = el("cx:rich", null, [
4968
+ raw(voidEl("a:bodyPr")),
4969
+ raw(voidEl("a:lstStyle")),
4970
+ raw(el("a:p", null, [raw(el("a:pPr", null, raw(rPr))), raw(el("a:r", null, [raw(voidEl("a:rPr", { lang: rel.opts.lang || "en-US" })), raw(el("a:t", null, rel.opts.title || "Chart Title"))]))]))
4971
+ ]);
5476
4972
  return el("cx:title", {
5477
4973
  pos: "t",
5478
4974
  align: "ctr",
5479
4975
  overlay: 0
5480
- }, raw(el("cx:tx", null, raw(el("cx:rich", null, [
5481
- raw(voidEl("a:bodyPr")),
5482
- raw(voidEl("a:lstStyle")),
5483
- raw(el("a:p", null, [raw(el("a:pPr", null, raw(rPr))), raw(el("a:r", null, [raw(voidEl("a:rPr", { lang: rel.opts.lang || "en-US" })), raw(el("a:t", null, rel.opts.title || "Chart Title"))]))]))
5484
- ])))));
4976
+ }, raw(el("cx:tx", null, raw(rich))));
5485
4977
  }
5486
4978
  /**
5487
4979
  * Assemble the full `<cx:chartSpace>` for a chartEx chart part.
@@ -5492,7 +4984,8 @@ function makeChartExTitle(rel) {
5492
4984
  */
5493
4985
  function makeXmlChartEx(rel) {
5494
4986
  const type = rel.opts._type;
5495
- const plotArea = el("cx:plotArea", null, [raw(el("cx:plotAreaRegion", null, raw(makeChartExSeries(rel)))), raw(makeChartExAxes(type))]);
4987
+ const plotAreaRegion = el("cx:plotAreaRegion", null, raw(makeChartExSeries(rel)));
4988
+ const plotArea = el("cx:plotArea", null, [raw(plotAreaRegion), raw(makeChartExAxes(type))]);
5496
4989
  const legend = rel.opts.showLegend ? voidEl("cx:legend", {
5497
4990
  pos: chartExLegendPos(rel.opts.legendPos),
5498
4991
  align: "ctr",
@@ -5546,6 +5039,8 @@ function makeChartExStyleXml() {
5546
5039
  }
5547
5040
  //#endregion
5548
5041
  //#region src/gen/chart/embed-xlsx.ts
5042
+ /** MS chart-extension relationship types (chartEx style + color-style sidecar parts). */
5043
+ const MS_CHART_REL = "http://schemas.microsoft.com/office/2011/relationships/";
5549
5044
  function relationship$2(id, type, target) {
5550
5045
  return voidEl("Relationship", {
5551
5046
  Id: id,
@@ -5783,8 +5278,8 @@ function buildChartRelsXml(embeddingTarget) {
5783
5278
  function buildChartExRelsXml(embeddingTarget, colorsTarget, styleTarget) {
5784
5279
  return XML_DECL + relationships([
5785
5280
  relationship$2("rId1", OFFICE_REL + "package", embeddingTarget),
5786
- relationship$2("rId2", "http://schemas.microsoft.com/office/2011/relationships/chartColorStyle", colorsTarget),
5787
- relationship$2("rId3", "http://schemas.microsoft.com/office/2011/relationships/chartStyle", styleTarget)
5281
+ relationship$2("rId2", MS_CHART_REL + "chartColorStyle", colorsTarget),
5282
+ relationship$2("rId3", MS_CHART_REL + "chartStyle", styleTarget)
5788
5283
  ]);
5789
5284
  }
5790
5285
  /**
@@ -6254,6 +5749,7 @@ function buildAnimationSeq(animations, next) {
6254
5749
  fill: "hold"
6255
5750
  }, [raw(el("p:stCondLst", null, raw(voidEl("p:cond", { delay: "indefinite" })))), raw(el("p:childTnLst", null, raw(subs)))])));
6256
5751
  };
5752
+ const clickGroups = groups.map(emitGroup).join("");
6257
5753
  return el("p:seq", {
6258
5754
  concurrent: 1,
6259
5755
  nextAc: "seek"
@@ -6262,7 +5758,7 @@ function buildAnimationSeq(animations, next) {
6262
5758
  id: 2,
6263
5759
  dur: "indefinite",
6264
5760
  nodeType: "mainSeq"
6265
- }, raw(el("p:childTnLst", null, raw(groups.map(emitGroup).join("")))))),
5761
+ }, raw(el("p:childTnLst", null, raw(clickGroups))))),
6266
5762
  raw(el("p:prevCondLst", null, raw(el("p:cond", {
6267
5763
  evt: "onPrev",
6268
5764
  delay: 0
@@ -6310,7 +5806,8 @@ function slideTimingToXml(slide) {
6310
5806
  const mediaNode = (obj, nodeId) => {
6311
5807
  const spid = slide._slideObjects.indexOf(obj) + 2;
6312
5808
  const repeatCount = obj.loop === true ? "indefinite" : String(Math.round(obj.loopCount * 1e3));
6313
- return el(obj.mtype === "audio" ? "p:audio" : "p:video", null, raw(el("p:cMediaNode", null, [raw(el("p:cTn", {
5809
+ const mediaTag = obj.mtype === "audio" ? "p:audio" : "p:video";
5810
+ return el(mediaTag, null, raw(el("p:cMediaNode", null, [raw(el("p:cTn", {
6314
5811
  id: nodeId,
6315
5812
  repeatCount,
6316
5813
  fill: "hold",
@@ -6320,12 +5817,13 @@ function slideTimingToXml(slide) {
6320
5817
  if (animations.length === 0) {
6321
5818
  if (loopMedia.length === 0) return "";
6322
5819
  let nodeId = 1;
5820
+ const mediaNodes = loopMedia.map((obj) => mediaNode(obj, nodeId += 1)).join("");
6323
5821
  return el("p:timing", null, raw(el("p:tnLst", null, raw(el("p:par", null, raw(el("p:cTn", {
6324
5822
  id: 1,
6325
5823
  dur: "indefinite",
6326
5824
  restart: "never",
6327
5825
  nodeType: "tmRoot"
6328
- }, raw(el("p:childTnLst", null, raw(loopMedia.map((obj) => mediaNode(obj, nodeId += 1)).join("")))))))))));
5826
+ }, raw(el("p:childTnLst", null, raw(mediaNodes))))))))));
6329
5827
  }
6330
5828
  let id = 2;
6331
5829
  const next = () => id += 1;
@@ -6394,62 +5892,6 @@ function slideTransitionToXml(slide) {
6394
5892
  }, [raw(typeEl), raw(sndAc)])))), raw(el("mc:Fallback", null, raw(el("p:transition", baseAttrs, [raw(typeEl), raw(sndAc)]))))]);
6395
5893
  }
6396
5894
  //#endregion
6397
- //#region src/gen/drawingml/clamp.ts
6398
- /**
6399
- * ts-pptx: DrawingML value clamps
6400
- *
6401
- * Clamp font/character/line spacing values (points) into their ST_* schema
6402
- * ranges and convert to the hundredths-of-a-point units the attributes expect.
6403
- * Out-of-range values make PowerPoint report the package as needing repair.
6404
- */
6405
- /**
6406
- * Clamp a font size (points) into ST_TextFontSize (1-4000pt) and return it in
6407
- * hundredths of a point for the `sz` attribute. Out-of-range sizes make
6408
- * PowerPoint report the package as needing repair (e.g. `sz` > 400000 or < 100).
6409
- */
6410
- function clampFontSizeSz(fontSizePts) {
6411
- const raw = ptToHundredths(fontSizePts);
6412
- const clamped = Math.min(4e5, Math.max(100, raw));
6413
- if (clamped !== raw) warnOnce("font/size-out-of-range", `fontSize ${fontSizePts} is outside the valid range 1-4000pt; using ${clamped / 100}.`);
6414
- return clamped;
6415
- }
6416
- /** Clamp character spacing (points) into ST_TextPoint (-4000..4000pt); returns hundredths for the `spc` attribute. */
6417
- function clampCharSpacingSpc(charSpacingPts) {
6418
- const raw = ptToHundredths(charSpacingPts);
6419
- const clamped = Math.min(4e5, Math.max(-4e5, raw));
6420
- if (clamped !== raw) warnOnce("text/char-spacing-out-of-range", `charSpacing ${charSpacingPts} is outside the valid range -4000..4000pt; using ${clamped / 100}.`);
6421
- return clamped;
6422
- }
6423
- /**
6424
- * Clamp a paragraph's left margin (points) into ST_TextMargin (0..4032pt, i.e. 0..51206400 EMU);
6425
- * returns EMU for `a:pPr/@marL`. The type is unsigned — a negative margin is not a narrower one,
6426
- * it is a value PowerPoint reports as needing repair.
6427
- */
6428
- function clampParaMarginEmu(marginPts) {
6429
- const raw = valToPts(marginPts);
6430
- const clamped = Math.min(51206400, Math.max(0, raw));
6431
- if (clamped !== raw) warnOnce("text/paragraph-margin-out-of-range", `paraMarginLeft ${marginPts} is outside the valid range 0-4032pt; using ${clamped / EMU_PER_POINT}.`);
6432
- return clamped;
6433
- }
6434
- /**
6435
- * Clamp a first-line indent (points) into ST_TextIndent (-4032..4032pt); returns EMU for
6436
- * `a:pPr/@indent`. Unlike the margin this one is signed: a negative indent is the hanging
6437
- * indent every bulleted paragraph uses.
6438
- */
6439
- function clampParaIndentEmu(indentPts) {
6440
- const raw = valToPts(indentPts);
6441
- const clamped = Math.min(51206400, Math.max(-51206400, raw));
6442
- if (clamped !== raw) warnOnce("text/paragraph-indent-out-of-range", `paraIndent ${indentPts} is outside the valid range -4032..4032pt; using ${clamped / EMU_PER_POINT}.`);
6443
- return clamped;
6444
- }
6445
- /** Clamp line spacing (points) into ST_TextSpacingPoint (0..1584pt); returns hundredths for `<a:spcPts val>`. */
6446
- function clampLineSpacingPts(lineSpacingPts) {
6447
- const raw = ptToHundredths(lineSpacingPts);
6448
- const clamped = Math.min(158400, Math.max(0, raw));
6449
- if (clamped !== raw) warnOnce("text/line-spacing-out-of-range", `lineSpacing ${lineSpacingPts} is outside the valid range 0-1584pt; using ${clamped / 100}.`);
6450
- return clamped;
6451
- }
6452
- //#endregion
6453
5895
  //#region src/gen/drawingml/locks.ts
6454
5896
  const SHAPE_LOCK_ATTRS = [
6455
5897
  "noGrp",
@@ -6585,430 +6027,6 @@ function genXmlShapeLine(ln) {
6585
6027
  ]);
6586
6028
  }
6587
6029
  //#endregion
6588
- //#region src/gen/drawingml/math.ts
6589
- /**
6590
- * ts-pptx: DrawingML native equations (OMML)
6591
- *
6592
- * Wrap raw OMML markup into the `<a14:m>` marker PowerPoint uses for editable
6593
- * equations — as a standalone display paragraph (`genXmlMathParagraph`) or as an
6594
- * inline run that flows between the surrounding `<a:r>` runs (`genXmlInlineMath`).
6595
- */
6596
- const M_NS = "http://schemas.openxmlformats.org/officeDocument/2006/math";
6597
- const A14_NS = "http://schemas.microsoft.com/office/drawing/2010/main";
6598
- /** The `<a14:m>` marker, carrying both namespace declarations so the supplied OMML needs none. */
6599
- function a14Math(mathXml) {
6600
- return el("a14:m", {
6601
- "xmlns:a14": A14_NS,
6602
- "xmlns:m": M_NS
6603
- }, raw(mathXml));
6604
- }
6605
- /**
6606
- * Build a native-equation paragraph (`<a:p>`) from raw OMML.
6607
- *
6608
- * PowerPoint stores an editable equation inside a text body as an `<a14:m>` marker wrapping
6609
- * `<m:oMathPara><m:oMath>…`. We declare both the `a14` (drawing-2010) and `m` (math) namespaces
6610
- * on the `<a14:m>` element so the supplied OMML needs no namespace declarations of its own, then
6611
- * accept three input shapes: a full `<m:oMathPara>`, a full `<m:oMath>`, or the inner OMML
6612
- * (children of `<m:oMath>`). A trailing `<a:endParaRPr>` matches what PowerPoint authors.
6613
- *
6614
- * @param {string} omml - raw OMML markup for the equation
6615
- * @returns {string} an `<a:p>` math paragraph
6616
- */
6617
- function genXmlMathParagraph(omml) {
6618
- const trimmed = (omml || "").trim();
6619
- const paraPr = el("m:oMathParaPr", null, raw(voidEl("m:jc", { "m:val": "centerGroup" })));
6620
- return el("a:p", null, [a14Math(trimmed.startsWith("<m:oMathPara") ? trimmed : trimmed.includes("<m:oMath") ? el("m:oMathPara", null, [paraPr, trimmed].map(raw)) : el("m:oMathPara", null, [paraPr, el("m:oMath", null, raw(trimmed))].map(raw))), voidEl("a:endParaRPr", { lang: "en-US" })].map(raw));
6621
- }
6622
- /**
6623
- * Build an INLINE native-equation run (dn-inline-math) from raw OMML.
6624
- *
6625
- * Unlike the display form ({@link genXmlMathParagraph}), an inline equation is *not* its own
6626
- * paragraph: PowerPoint authors an `<a14:m>` marker wrapping a bare `<m:oMath>` (no `<m:oMathPara>`,
6627
- * no `<m:oMathParaPr>`/`<m:jc>`) that flows between the surrounding `<a:r>` runs in one `<a:p>` —
6628
- * pinned by the `math-omml-inline.pptx` oracle. The `mc:AlternateContent` envelope stays at the
6629
- * shape level (see `objectHasMath`), so nothing wraps the run itself. We declare the `a14` and `m`
6630
- * namespaces on the `<a14:m>` element so the supplied OMML needs none of its own, and accept the
6631
- * same three input shapes as the display helper: a full `<m:oMathPara>` (its inner `<m:oMath>` is
6632
- * unwrapped, since a paragraph block cannot flow inline), a full `<m:oMath>`, or the inner OMML.
6633
- *
6634
- * @param {string} omml - raw OMML markup for the equation
6635
- * @returns {string} an `<a14:m>` inline equation run
6636
- */
6637
- function genXmlInlineMath(omml) {
6638
- const trimmed = (omml || "").trim();
6639
- const oMathMatch = trimmed.match(/<m:oMath[\s>][\s\S]*<\/m:oMath>/);
6640
- return a14Math(oMathMatch ? oMathMatch[0] : el("m:oMath", null, raw(trimmed)));
6641
- }
6642
- //#endregion
6643
- //#region src/gen/drawingml/text-run.ts
6644
- /** The 2018 hyperlink-color extension namespace, written on the `<ahyp:hlinkClr>` element itself. */
6645
- const AHYP_NS = "http://schemas.microsoft.com/office/drawing/2018/hyperlinkcolor";
6646
- /**
6647
- * `<a:buChar>` cannot go through the element builder. Its `char` attribute carries a *pre-escaped*
6648
- * numeric character reference (`&#x2022;` — see `BulletType`), and the builder escapes every
6649
- * attribute value, which would emit `&amp;#x2022;` and render that text literally as the bullet.
6650
- * There is no raw-attribute escape hatch, so this one stays a template.
6651
- */
6652
- function buChar(char) {
6653
- return `<a:buChar char="${char}"/>`;
6654
- }
6655
- /**
6656
- * `<a:extLst>` marking a hyperlink whose color should follow the text color rather than the
6657
- * theme's hyperlink color. The leading spaces are byte-significant: this block was authored as
6658
- * indented string concatenation and that indentation reaches the file, so it is described here
6659
- * with `openPrefix`/`closePrefix` rather than being tidied away.
6660
- */
6661
- const HLINK_TEXT_COLOR_EXT = el("a:extLst", null, raw(el("a:ext", { uri: "{A12FA001-AC4F-418D-AE19-62706E023703}" }, raw(voidEl("ahyp:hlinkClr", {
6662
- "xmlns:ahyp": AHYP_NS,
6663
- val: "tx"
6664
- }, { openPrefix: " " })), {
6665
- openPrefix: " ",
6666
- closePrefix: " "
6667
- })), {
6668
- openPrefix: " ",
6669
- closePrefix: " "
6670
- });
6671
- /**
6672
- * Whether a `bullet` value makes the paragraph DRAW a bullet (a glyph, a number or a picture).
6673
- *
6674
- * Two call sites downstream of the emitter act on "this paragraph has a bullet" rather than on
6675
- * "this paragraph says something about its bullet", and both were plain truthiness tests until
6676
- * `'inherit'` — a truthy string that draws nothing — joined the union. Left as truthiness they
6677
- * would have broken the line grouping (every run starting its own paragraph) and eaten a real
6678
- * leading `•` from text that emits no `a:buChar` to duplicate it.
6679
- * @param {TextPropsOptions['bullet']} bullet - the option value
6680
- * @return {boolean} true when bullet markup is emitted
6681
- */
6682
- function emitsBulletMarkup(bullet) {
6683
- return !!bullet && bullet !== "inherit";
6684
- }
6685
- /**
6686
- * One paragraph margin attribute's value in EMU, or `null` when the attribute is not written.
6687
- *
6688
- * Three inputs, because the option has three states and so does the attribute: an explicit
6689
- * number is clamped and written, `'inherit'` writes nothing, and an absent option falls back to
6690
- * whatever the `bullet` arm decided — which is also `null` for `bullet: 'inherit'`, the one arm
6691
- * that states nothing on its own.
6692
- * @param {number|'inherit'|undefined} option - `paraMarginLeft` / `paraIndent` as given
6693
- * @param {number|null} fallbackEmu - the bullet arm's default, `null` for no attribute
6694
- * @param {Function} clamp - the attribute's ST_* range clamp (points in, EMU out)
6695
- * @return {number|null} the EMU value to write, or null to omit the attribute
6696
- */
6697
- function resolveParagraphMargin(option, fallbackEmu, clamp) {
6698
- if (option === void 0) return fallbackEmu;
6699
- if (option === "inherit") return null;
6700
- return clamp(option);
6701
- }
6702
- /**
6703
- * Generate XML Paragraph Properties
6704
- * @param {SlideObject|TextProps} textObj - text object
6705
- * @param {boolean} isDefault - array of default relations
6706
- * @return {string} XML
6707
- */
6708
- function genXmlParagraphProperties(textObj, isDefault) {
6709
- const opts = textObj.options ?? {};
6710
- let strXmlBullet = "";
6711
- let strXmlBulletColor = "";
6712
- let strXmlLnSpc = "";
6713
- let strXmlParaSpc = "";
6714
- let strXmlTabStops = "";
6715
- const tag = isDefault ? "a:lvl1pPr" : "a:pPr";
6716
- let bulletMarL = valToPts(27);
6717
- let defaultMarL = null;
6718
- let defaultIndent = null;
6719
- let indentBeforeMarL = false;
6720
- let paragraphPropXml = `<${tag}${opts.rtlMode ? " rtl=\"1\" " : ""}`;
6721
- {
6722
- if (opts.align) switch (opts.align) {
6723
- case "left":
6724
- paragraphPropXml += " algn=\"l\"";
6725
- break;
6726
- case "right":
6727
- paragraphPropXml += " algn=\"r\"";
6728
- break;
6729
- case "center":
6730
- paragraphPropXml += " algn=\"ctr\"";
6731
- break;
6732
- case "justify":
6733
- paragraphPropXml += " algn=\"just\"";
6734
- break;
6735
- default: paragraphPropXml += "";
6736
- }
6737
- if (opts.lineSpacing) strXmlLnSpc = el("a:lnSpc", null, raw(voidEl("a:spcPts", { val: clampLineSpacingPts(opts.lineSpacing) })));
6738
- else if (opts.lineSpacingMultiple) strXmlLnSpc = el("a:lnSpc", null, raw(voidEl("a:spcPct", { val: Math.round(opts.lineSpacingMultiple * PERCENT_SCALE) })));
6739
- if (opts.indentLevel && !isNaN(Number(opts.indentLevel)) && opts.indentLevel > 0) paragraphPropXml += ` lvl="${opts.indentLevel}"`;
6740
- if (opts.paraSpaceBefore && !isNaN(Number(opts.paraSpaceBefore)) && opts.paraSpaceBefore > 0) strXmlParaSpc += el("a:spcBef", null, raw(voidEl("a:spcPts", { val: ptToHundredths(opts.paraSpaceBefore) })));
6741
- if (opts.paraSpaceAfter && !isNaN(Number(opts.paraSpaceAfter)) && opts.paraSpaceAfter > 0) strXmlParaSpc += el("a:spcAft", null, raw(voidEl("a:spcPts", { val: ptToHundredths(opts.paraSpaceAfter) })));
6742
- if (typeof opts.bullet === "object") {
6743
- const bulletImage = opts.bullet.image;
6744
- const isPictureBullet = !!(bulletImage && (bulletImage.path || bulletImage.data));
6745
- if (opts.bullet?.indent) bulletMarL = valToPts(opts.bullet.indent);
6746
- defaultMarL = opts.indentLevel && opts.indentLevel > 0 ? bulletMarL + bulletMarL * opts.indentLevel : bulletMarL;
6747
- defaultIndent = -bulletMarL;
6748
- if (opts.bullet.color && !isPictureBullet) strXmlBulletColor = el("a:buClr", null, raw(createColorElement(opts.bullet.color)));
6749
- let bulletSizePct;
6750
- if (opts.bullet.size !== void 0) {
6751
- const bulletSize = Number(opts.bullet.size);
6752
- if (isNaN(bulletSize) || bulletSize < 25 || bulletSize > 400) warn("bullet/size-out-of-range", "`bullet.size` must be a percentage between 25 and 400!");
6753
- else bulletSizePct = Math.round(bulletSize * FIXED_PCT_PER_PERCENT);
6754
- }
6755
- const strXmlBulletSize = bulletSizePct === void 0 ? "" : voidEl("a:buSzPct", { val: bulletSizePct });
6756
- const strXmlBulletFont = opts.bullet.fontFace ? voidEl("a:buFont", { typeface: opts.bullet.fontFace }) : "";
6757
- if (isPictureBullet) if (opts.bullet._rId) {
6758
- const svgExt = opts.bullet._rIdSvg ? el("a:extLst", null, raw(el("a:ext", { uri: "{96DAC541-7B7A-43D3-8B79-37D633B846F1}" }, raw(voidEl("asvg:svgBlip", {
6759
- "xmlns:asvg": "http://schemas.microsoft.com/office/drawing/2016/SVG/main",
6760
- "r:embed": `rId${opts.bullet._rIdSvg}`
6761
- }))))) : "";
6762
- strXmlBullet = strXmlBulletSize + el("a:buBlip", null, raw(svgExt ? el("a:blip", { "r:embed": `rId${opts.bullet._rId}` }, raw(svgExt)) : voidEl("a:blip", { "r:embed": `rId${opts.bullet._rId}` })));
6763
- } else {
6764
- warn("bullet/image-embed-failed", "picture `bullet.image` could not be embedded; using a default bullet glyph");
6765
- strXmlBullet = strXmlBulletSize + strXmlBulletFont + buChar("&#x2022;");
6766
- }
6767
- else if (opts.bullet.type && opts.bullet.type.toString().toLowerCase() === "number") strXmlBullet = strXmlBulletSize + (strXmlBulletFont || voidEl("a:buFont", { typeface: "+mj-lt" })) + voidEl("a:buAutoNum", {
6768
- type: opts.bullet.numberType || "arabicPeriod",
6769
- startAt: opts.bullet.numberStartAt || "1"
6770
- });
6771
- else if (opts.bullet.characterCode) {
6772
- let bulletCode = `&#x${opts.bullet.characterCode};`;
6773
- if (!/^[0-9A-Fa-f]{4}$/.test(opts.bullet.characterCode)) {
6774
- warn("bullet/invalid-character-code", "`bullet.characterCode` should be a 4-digit unicode character (ex: 22AB)!");
6775
- bulletCode = "&#x2022;";
6776
- }
6777
- strXmlBullet = strXmlBulletSize + strXmlBulletFont + buChar(bulletCode);
6778
- } else strXmlBullet = strXmlBulletSize + strXmlBulletFont + buChar("&#x2022;");
6779
- } else if (opts.bullet === "inherit") {} else if (opts.bullet) {
6780
- defaultMarL = opts.indentLevel && opts.indentLevel > 0 ? bulletMarL + bulletMarL * opts.indentLevel : bulletMarL;
6781
- defaultIndent = -bulletMarL;
6782
- strXmlBullet = buChar("&#x2022;");
6783
- } else if (!opts.bullet) {
6784
- defaultMarL = 0;
6785
- defaultIndent = 0;
6786
- indentBeforeMarL = true;
6787
- strXmlBullet = voidEl("a:buNone");
6788
- }
6789
- const marLEmu = resolveParagraphMargin(opts.paraMarginLeft, defaultMarL, clampParaMarginEmu);
6790
- const indentEmu = resolveParagraphMargin(opts.paraIndent, defaultIndent, clampParaIndentEmu);
6791
- const marLAttr = marLEmu === null ? "" : ` marL="${marLEmu}"`;
6792
- const indentAttr = indentEmu === null ? "" : ` indent="${indentEmu}"`;
6793
- paragraphPropXml += indentBeforeMarL ? indentAttr + marLAttr : marLAttr + indentAttr;
6794
- if (opts.tabStops && Array.isArray(opts.tabStops)) strXmlTabStops = el("a:tabLst", null, raw(opts.tabStops.map((stop) => voidEl("a:tab", {
6795
- pos: inch2Emu(stop.position || 1),
6796
- algn: stop.alignment || "l"
6797
- })).join("")));
6798
- paragraphPropXml += ">" + strXmlLnSpc + strXmlParaSpc + strXmlBulletColor + strXmlBullet + strXmlTabStops;
6799
- if (isDefault) paragraphPropXml += genXmlTextRunProperties(opts, true);
6800
- paragraphPropXml += "</" + tag + ">";
6801
- }
6802
- return paragraphPropXml;
6803
- }
6804
- /**
6805
- * Generate XML Text Run Properties (`a:rPr`)
6806
- * @param {ObjectOptions|TextPropsOptions} opts - text options
6807
- * @param {boolean} isDefault - whether these are the default text run properties
6808
- * @return {string} XML
6809
- */
6810
- function genXmlTextRunProperties(opts, isDefault) {
6811
- let runProps = "";
6812
- const runPropsTag = isDefault ? "a:defRPr" : "a:rPr";
6813
- const underline = typeof opts.underline === "object" && opts.underline?.style ? opts.underline.style : opts.hyperlink ? "sng" : null;
6814
- const baseline = opts.baseline ? Math.round(opts.baseline * 50) : opts.subscript ? -4e4 : opts.superscript ? 3e4 : null;
6815
- const attrs = {
6816
- lang: opts.lang ? opts.lang : "en-US",
6817
- altLang: opts.lang ? "en-US" : null,
6818
- sz: opts.fontSize ? clampFontSizeSz(opts.fontSize) : null,
6819
- b: opts?.bold ? "1" : null,
6820
- i: opts?.italic ? "1" : null,
6821
- strike: opts?.strike ? typeof opts.strike === "string" ? opts.strike : "sngStrike" : null,
6822
- cap: opts?.caps ? opts.caps : null,
6823
- u: underline,
6824
- baseline,
6825
- spc: opts.charSpacing ? clampCharSpacingSpc(opts.charSpacing) : null,
6826
- kern: opts.charSpacing ? 0 : null,
6827
- dirty: "0"
6828
- };
6829
- const hasShadow = !!opts.shadow && opts.shadow.type !== "none";
6830
- if (opts.color || opts.fontFace || opts.outline || opts.glow || hasShadow || typeof opts.underline === "object" && opts.underline.color) {
6831
- if (opts.outline && typeof opts.outline === "object") runProps += el("a:ln", { w: lineWidthToEmu(opts.outline.size || .75) }, raw(genXmlColorSelection(opts.outline.color || "FFFFFF")));
6832
- if (opts.color) runProps += genXmlColorSelection({
6833
- color: opts.color,
6834
- transparency: opts.transparency
6835
- });
6836
- if (opts.glow || hasShadow) runProps += el("a:effectLst", null, [opts.glow ? raw(createGlowElement(opts.glow, DEF_TEXT_GLOW)) : null, hasShadow ? raw(createShadowElement(opts.shadow, DEF_TEXT_SHADOW)) : null]);
6837
- if (opts.highlight) runProps += el("a:highlight", null, raw(createColorElement(opts.highlight)));
6838
- if (typeof opts.underline === "object" && opts.underline.color) runProps += el("a:uFill", null, raw(genXmlColorSelection(opts.underline.color)));
6839
- if (opts.fontFace) {
6840
- runProps += voidEl("a:latin", {
6841
- typeface: opts.fontFace,
6842
- pitchFamily: "34",
6843
- charset: "0"
6844
- });
6845
- if (opts.fontFaceEA) runProps += voidEl("a:ea", { typeface: opts.fontFaceEA });
6846
- runProps += voidEl("a:cs", { typeface: opts.fontFace });
6847
- }
6848
- }
6849
- if (opts.hyperlink) {
6850
- if (typeof opts.hyperlink !== "object") throw new InvalidOptionError("hyperlink/not-an-object", "text `hyperlink` option should be an object. Ex: `hyperlink:{url:'https://github.com'}` ");
6851
- else if (!opts.hyperlink.url && !opts.hyperlink.slide && !opts.hyperlink.action) throw new InvalidOptionError("hyperlink/missing-target", "text `hyperlink` requires either `url`, `slide`, or `action`");
6852
- else if (opts.hyperlink.url || opts.hyperlink.slide) {
6853
- const linkAttrs = opts.hyperlink.url ? {
6854
- "r:id": `rId${opts.hyperlink._rId}`,
6855
- invalidUrl: "",
6856
- action: "",
6857
- tgtFrame: "",
6858
- tooltip: opts.hyperlink.tooltip ?? "",
6859
- history: "1",
6860
- highlightClick: "0",
6861
- endSnd: "0"
6862
- } : {
6863
- "r:id": `rId${opts.hyperlink._rId}`,
6864
- action: "ppaction://hlinksldjump",
6865
- tooltip: opts.hyperlink.tooltip ?? ""
6866
- };
6867
- runProps += opts.color ? el("a:hlinkClick", linkAttrs, raw(HLINK_TEXT_COLOR_EXT)) : voidEl("a:hlinkClick", linkAttrs);
6868
- }
6869
- }
6870
- return el(runPropsTag, attrs, raw(runProps));
6871
- }
6872
- /**
6873
- * Build textBody text runs [`<a:r></a:r>`] for paragraphs [`<a:p>`]
6874
- * @param {TextProps} textObj - Text object
6875
- * @return {string} XML string
6876
- */
6877
- function genXmlTextRun(textObj) {
6878
- if (textObj.text === void 0 || textObj.text === null) return "";
6879
- return el("a:r", null, [raw(genXmlTextRunProperties(textObj.options ?? {}, false)), raw(el("a:t", null, String(textObj.text)))]);
6880
- }
6881
- /**
6882
- * Builds `<a:normAutofit>` with explicit fontScale/lnSpcReduction for "shrink text on overflow"
6883
- * @param {TextFitShrinkProps} fit - shrink fit options
6884
- * @return {string} XML string (`<a:normAutofit .../>`)
6885
- * @see ECMA-376 CT_TextNormAutofit (attributes in 1000ths of a percent)
6886
- */
6887
- function genXmlNormAutofit(fit) {
6888
- const pct = (val, name) => {
6889
- if (val === void 0 || val === null) return null;
6890
- if (typeof val !== "number" || isNaN(val) || val < 0 || val > 100) {
6891
- warn("text/invalid-fit-percentage", `fit.${name} must be a number between 0 and 100 (percent); received ${String(val)} - attribute ignored.`);
6892
- return null;
6893
- }
6894
- return Math.round(val * FIXED_PCT_PER_PERCENT);
6895
- };
6896
- return voidEl("a:normAutofit", {
6897
- fontScale: pct(fit.fontScale, "fontScale"),
6898
- lnSpcReduction: pct(fit.lnSpcReduction, "lnSpcReduction")
6899
- });
6900
- }
6901
- /**
6902
- * Group the flat run list into paragraphs (lines): a new line starts on a display-math run,
6903
- * an alignment change, or a bullet, and closes after a breakLine. Returns the per-line run arrays.
6904
- */
6905
- function groupRunsIntoLines(arrTextObjects, opts) {
6906
- const arrLines = [];
6907
- let arrTexts = [];
6908
- arrTextObjects.forEach((textObj, idx) => {
6909
- if (textObj.math && !textObj.inline) {
6910
- if (arrTexts.length > 0) {
6911
- arrLines.push(arrTexts);
6912
- arrTexts = [];
6913
- }
6914
- arrLines.push([textObj]);
6915
- return;
6916
- }
6917
- if (arrTexts.length > 0 && (textObj.options.align || opts.align)) {
6918
- if (textObj.options.align !== arrTextObjects[idx - 1]?.options.align) {
6919
- arrLines.push(arrTexts);
6920
- arrTexts = [];
6921
- }
6922
- } else if (arrTexts.length > 0 && emitsBulletMarkup(textObj.options.bullet)) {
6923
- arrLines.push(arrTexts);
6924
- arrTexts = [];
6925
- textObj.options.breakLine = false;
6926
- }
6927
- arrTexts.push(textObj);
6928
- if (arrTexts.length > 0 && textObj.options.breakLine) {
6929
- if (idx + 1 < arrTextObjects.length) {
6930
- arrLines.push(arrTexts);
6931
- arrTexts = [];
6932
- }
6933
- }
6934
- if (idx + 1 === arrTextObjects.length) arrLines.push(arrTexts);
6935
- });
6936
- return arrLines;
6937
- }
6938
- /**
6939
- * Render each grouped line to an `<a:p>` paragraph: paragraph props, inherited run options,
6940
- * text runs (and inline/display math), and the closing endParaRPr.
6941
- */
6942
- function renderTextParagraphsXml(arrLines, slideObj, opts) {
6943
- let strSlideXml = "";
6944
- arrLines.forEach((line) => {
6945
- const firstRun = line[0];
6946
- if (line.length === 1 && firstRun?.math && !firstRun.inline) {
6947
- strSlideXml += genXmlMathParagraph(firstRun.math);
6948
- return;
6949
- }
6950
- let reqsClosingFontSize = false;
6951
- let paraXml = "";
6952
- let paragraphPropEmitted = false;
6953
- line.forEach((textObj, idx) => {
6954
- textObj.options._lineIdx = idx;
6955
- if (idx > 0 && textObj.options.softBreakBefore) paraXml += voidEl("a:br");
6956
- textObj.options.align = textObj.options.align || opts.align;
6957
- textObj.options.lineSpacing = textObj.options.lineSpacing || opts.lineSpacing;
6958
- textObj.options.lineSpacingMultiple = textObj.options.lineSpacingMultiple || opts.lineSpacingMultiple;
6959
- textObj.options.indentLevel = textObj.options.indentLevel || opts.indentLevel;
6960
- textObj.options.paraSpaceBefore = textObj.options.paraSpaceBefore || opts.paraSpaceBefore;
6961
- textObj.options.paraSpaceAfter = textObj.options.paraSpaceAfter || opts.paraSpaceAfter;
6962
- textObj.options.paraMarginLeft = textObj.options.paraMarginLeft ?? opts.paraMarginLeft;
6963
- textObj.options.paraIndent = textObj.options.paraIndent ?? opts.paraIndent;
6964
- if (!paragraphPropEmitted) {
6965
- paragraphPropEmitted = true;
6966
- paraXml += genXmlParagraphProperties(textObj, false).replace("<a:pPr></a:pPr>", "");
6967
- }
6968
- const textOptions = textObj.options;
6969
- Object.entries(opts).filter(([key]) => !(textObj.options.hyperlink && key === "color")).forEach(([key, val]) => {
6970
- if (key !== "bullet" && !textOptions[key]) textOptions[key] = val;
6971
- });
6972
- let _textRunObj = textObj;
6973
- if (idx === 0 && emitsBulletMarkup(line[0]?.options.bullet) && typeof textObj.text === "string") {
6974
- const _stripped = textObj.text.replace(/^[\u2022\u25E6\u25AA\u25AB\u25CF\u25CB\u2023\u2043\u2219]\s*/, "");
6975
- if (_stripped !== textObj.text) _textRunObj = {
6976
- text: _stripped,
6977
- options: textObj.options
6978
- };
6979
- }
6980
- const isEmptyBreakArtifact = _textRunObj.text === "" && line.length > 1;
6981
- if (_textRunObj.math) paraXml += genXmlInlineMath(_textRunObj.math);
6982
- else if (!isEmptyBreakArtifact) paraXml += genXmlTextRun(_textRunObj);
6983
- if (!textObj.text && opts.fontSize || textObj.options.fontSize) {
6984
- reqsClosingFontSize = true;
6985
- opts.fontSize = opts.fontSize || textObj.options.fontSize;
6986
- }
6987
- });
6988
- const sizedAttrs = () => ({
6989
- lang: opts.lang || "en-US",
6990
- sz: opts.fontSize ? clampFontSizeSz(opts.fontSize) : null,
6991
- dirty: "0"
6992
- });
6993
- if (slideObj._type === "tablecell" && (opts.fontSize || opts.fontFace)) if (opts.fontFace) paraXml += el("a:endParaRPr", sizedAttrs(), [
6994
- raw(voidEl("a:latin", {
6995
- typeface: opts.fontFace,
6996
- charset: "0"
6997
- })),
6998
- opts.fontFaceEA ? raw(voidEl("a:ea", { typeface: opts.fontFaceEA })) : null,
6999
- raw(voidEl("a:cs", { typeface: opts.fontFace }))
7000
- ]);
7001
- else paraXml += voidEl("a:endParaRPr", sizedAttrs());
7002
- else if (reqsClosingFontSize) paraXml += voidEl("a:endParaRPr", sizedAttrs());
7003
- else paraXml += voidEl("a:endParaRPr", {
7004
- lang: opts.lang || "en-US",
7005
- dirty: "0"
7006
- });
7007
- strSlideXml += el("a:p", null, raw(paraXml));
7008
- });
7009
- return strSlideXml;
7010
- }
7011
- //#endregion
7012
6030
  //#region src/gen/drawingml/text-body.ts
7013
6031
  /**
7014
6032
  * ts-pptx: DrawingML text body
@@ -7514,29 +6532,32 @@ function genXmlCustGeom(options, cx, cy, layout) {
7514
6532
  if (cxnEls.length) cxnLst = el("a:cxnLst", null, cxnEls.map(raw));
7515
6533
  }
7516
6534
  let ahLst = voidEl("a:ahLst", null, SPACE_BEFORE_SLASH);
7517
- if (options.adjustHandles?.length) ahLst = el("a:ahLst", null, options.adjustHandles.map((h) => {
7518
- const pos = raw(voidEl("a:pos", {
7519
- x: adjCoord(h.x, "X"),
7520
- y: adjCoord(h.y, "Y")
7521
- }));
7522
- if ("gdRefR" in h || "minR" in h || "maxR" in h || "gdRefAng" in h || "minAng" in h || "maxAng" in h) return el("a:ahPolar", {
7523
- gdRefR: h.gdRefR,
7524
- minR: adjCoord(h.minR, "X"),
7525
- maxR: adjCoord(h.maxR, "X"),
7526
- gdRefAng: h.gdRefAng,
7527
- minAng: h.minAng == null ? void 0 : convertRotationDegrees(h.minAng),
7528
- maxAng: h.maxAng == null ? void 0 : convertRotationDegrees(h.maxAng)
7529
- }, pos);
7530
- const xy = h;
7531
- return el("a:ahXY", {
7532
- gdRefX: xy.gdRefX,
7533
- minX: adjCoord(xy.minX, "X"),
7534
- maxX: adjCoord(xy.maxX, "X"),
7535
- gdRefY: xy.gdRefY,
7536
- minY: adjCoord(xy.minY, "Y"),
7537
- maxY: adjCoord(xy.maxY, "Y")
7538
- }, pos);
7539
- }).map(raw));
6535
+ if (options.adjustHandles?.length) {
6536
+ const ahEls = options.adjustHandles.map((h) => {
6537
+ const pos = raw(voidEl("a:pos", {
6538
+ x: adjCoord(h.x, "X"),
6539
+ y: adjCoord(h.y, "Y")
6540
+ }));
6541
+ if ("gdRefR" in h || "minR" in h || "maxR" in h || "gdRefAng" in h || "minAng" in h || "maxAng" in h) return el("a:ahPolar", {
6542
+ gdRefR: h.gdRefR,
6543
+ minR: adjCoord(h.minR, "X"),
6544
+ maxR: adjCoord(h.maxR, "X"),
6545
+ gdRefAng: h.gdRefAng,
6546
+ minAng: h.minAng == null ? void 0 : convertRotationDegrees(h.minAng),
6547
+ maxAng: h.maxAng == null ? void 0 : convertRotationDegrees(h.maxAng)
6548
+ }, pos);
6549
+ const xy = h;
6550
+ return el("a:ahXY", {
6551
+ gdRefX: xy.gdRefX,
6552
+ minX: adjCoord(xy.minX, "X"),
6553
+ maxX: adjCoord(xy.maxX, "X"),
6554
+ gdRefY: xy.gdRefY,
6555
+ minY: adjCoord(xy.minY, "Y"),
6556
+ maxY: adjCoord(xy.maxY, "Y")
6557
+ }, pos);
6558
+ });
6559
+ ahLst = el("a:ahLst", null, ahEls.map(raw));
6560
+ }
7540
6561
  return el("a:custGeom", null, [
7541
6562
  voidEl("a:avLst", null, SPACE_BEFORE_SLASH),
7542
6563
  gdLst,
@@ -7600,11 +6621,15 @@ const ImageSizingXml = {
7600
6621
  ].filter(Boolean).join(", ");
7601
6622
  throw new InvalidOptionError("image/crop-window-overflows", `addImage sizing.type 'crop': crop window overflows image bounds — ${over}. Ensure x≥0, y≥0, x+w≤w, y+h≤h.`);
7602
6623
  }
6624
+ const lPerc = Math.round(1e5 * (l / imgSize.w));
6625
+ const rPerc = Math.round(1e5 * (r / imgSize.w));
6626
+ const tPerc = Math.round(1e5 * (t / imgSize.h));
6627
+ const bPerc = Math.round(1e5 * (b / imgSize.h));
7603
6628
  return voidEl("a:srcRect", {
7604
- l: Math.round(1e5 * (l / imgSize.w)),
7605
- r: Math.round(1e5 * (r / imgSize.w)),
7606
- t: Math.round(1e5 * (t / imgSize.h)),
7607
- b: Math.round(1e5 * (b / imgSize.h))
6629
+ l: lPerc,
6630
+ r: rPerc,
6631
+ t: tPerc,
6632
+ b: bPerc
7608
6633
  }) + STRETCH;
7609
6634
  }
7610
6635
  };
@@ -8966,7 +7991,11 @@ function slideObjectToXml(slide) {
8966
7991
  strSlideXml += "</p:grpSp>";
8967
7992
  break;
8968
7993
  }
8969
- default: strSlideXml += "";
7994
+ case "notes": break;
7995
+ case "tablecell": break;
7996
+ case "hyperlink": break;
7997
+ case "online": break;
7998
+ default: slideItemObj._type;
8970
7999
  }
8971
8000
  return strSlideXml;
8972
8001
  };
@@ -9061,17 +8090,19 @@ function slideObjectRelationsToXml(slide, defaultRels) {
9061
8090
  const hasTarget = (target) => rels.some((xml) => xml.includes(` Target="${target}"`));
9062
8091
  slide._rels.forEach((rel) => {
9063
8092
  lastRid = Math.max(lastRid, rel.rId);
9064
- if (isHyperlinkRel(rel)) if (rel.data === "slide") rels.push(voidEl("Relationship", {
9065
- Id: `rId${rel.rId}`,
9066
- Type: OFFICE_REL + "slide",
9067
- Target: `slide${rel.Target}.xml`
9068
- }));
9069
- else rels.push(voidEl("Relationship", {
9070
- Id: `rId${rel.rId}`,
9071
- Type: OFFICE_REL + "hyperlink",
9072
- Target: rel.Target,
9073
- TargetMode: "External"
9074
- }));
8093
+ if (isHyperlinkRel(rel)) {
8094
+ if (rel.data === "slide") rels.push(voidEl("Relationship", {
8095
+ Id: `rId${rel.rId}`,
8096
+ Type: OFFICE_REL + "slide",
8097
+ Target: `slide${rel.Target}.xml`
8098
+ }));
8099
+ else rels.push(voidEl("Relationship", {
8100
+ Id: `rId${rel.rId}`,
8101
+ Type: OFFICE_REL + "hyperlink",
8102
+ Target: rel.Target,
8103
+ TargetMode: "External"
8104
+ }));
8105
+ }
9075
8106
  });
9076
8107
  (slide._relsChart || []).forEach((rel) => {
9077
8108
  lastRid = Math.max(lastRid, rel.rId);
@@ -9182,527 +8213,6 @@ function getLayoutIdxForSlide(slides, slideLayouts, slideNumber) {
9182
8213
  return 1;
9183
8214
  }
9184
8215
  //#endregion
9185
- //#region src/gen/slide/notes.ts
9186
- /**
9187
- * Collect the speaker-notes runs for a slide (flattened across any number of `addNotes()` calls).
9188
- * @param {PresSlideInternal} slide - the slide object
9189
- * @return {TextProps[]} notes text runs in document order
9190
- */
9191
- function getNotesRuns(slide) {
9192
- const runs = [];
9193
- slide._slideObjects.forEach((obj) => {
9194
- if (obj._type === "notes" && obj.text) runs.push(...obj.text);
9195
- });
9196
- return runs;
9197
- }
9198
- /**
9199
- * Build (and cache) the hyperlink relationships for a slide's notes part (`notesSlideN.xml.rels`).
9200
- *
9201
- * Notes rels use their own namespace, independent of `slide._rels` (which serialize to
9202
- * `slideN.xml.rels`). The notes part always reserves rId1=notesMaster and rId2=slide, so
9203
- * dynamic hyperlink rels are allocated starting at rId3. Each notes hyperlink run is tagged
9204
- * with its `_rId` so the body serializer and the rels file agree.
9205
- *
9206
- * Idempotent: the result is cached on `slide._relsNotes` and reused by both callers.
9207
- * Only external `url` hyperlinks are supported; `slide` targets are ignored with a warning.
9208
- * @param {PresSlideInternal} slide - the slide object
9209
- * @return {SlideRel[]} notes hyperlink relationships
9210
- */
9211
- function buildNotesSlideRels(slide) {
9212
- if (slide._relsNotes) return slide._relsNotes;
9213
- const NOTES_REL_RESERVED = 2;
9214
- const rels = [];
9215
- let lastRid = NOTES_REL_RESERVED;
9216
- getNotesRuns(slide).forEach((run) => {
9217
- const hyperlink = run.options?.hyperlink;
9218
- if (!hyperlink) return;
9219
- if (!hyperlink.url) {
9220
- if (hyperlink.slide) warn("notes/hyperlink-slide-unsupported", "notes hyperlinks support `url` only (ignoring `slide` target)");
9221
- if (run.options) delete run.options.hyperlink;
9222
- return;
9223
- }
9224
- lastRid++;
9225
- hyperlink._rId = lastRid;
9226
- rels.push({
9227
- type: "hyperlink",
9228
- data: "dummy",
9229
- rId: lastRid,
9230
- Target: hyperlink.url
9231
- });
9232
- });
9233
- slide._relsNotes = rels;
9234
- return rels;
9235
- }
9236
- /**
9237
- * Build the `<p:txBody>` paragraphs for the notes placeholder.
9238
- * Runs are split into `<a:p>` paragraphs on newlines; each run is serialized with the standard
9239
- * text-run generator so inline formatting and `<a:hlinkClick>` markup are emitted consistently.
9240
- * @param {PresSlideInternal} slide - the slide object
9241
- * @return {string} XML string of `<a:p>` paragraphs
9242
- */
9243
- function genXmlNotesParagraphs(slide) {
9244
- const paragraphs = [[]];
9245
- getNotesRuns(slide).forEach((run) => {
9246
- String(run.text ?? "").split("\n").forEach((segment, idx) => {
9247
- if (idx > 0) paragraphs.push([]);
9248
- const text = segment.replace(/\r/g, "");
9249
- if (text !== "") paragraphs[paragraphs.length - 1]?.push({
9250
- text,
9251
- options: run.options || {}
9252
- });
9253
- });
9254
- });
9255
- return paragraphs.map((runs) => el("a:p", null, [raw(runs.map((run) => genXmlTextRun(run)).join("")), raw(voidEl("a:endParaRPr", {
9256
- lang: "en-US",
9257
- dirty: 0
9258
- }))])).join("");
9259
- }
9260
- /**
9261
- * Generate XML for Notes Master (notesMaster1.xml)
9262
- * @returns {string} XML
9263
- */
9264
- function makeXmlNotesMaster() {
9265
- const xfrmOffExt = (x, y, cx, cy) => el("a:xfrm", null, [raw(voidEl("a:off", {
9266
- x,
9267
- y
9268
- })), raw(voidEl("a:ext", {
9269
- cx,
9270
- cy
9271
- }))]);
9272
- const prstGeomRect = el("a:prstGeom", { prst: "rect" }, raw(voidEl("a:avLst")));
9273
- const spLocks = (attrs) => el("p:cNvSpPr", null, raw(voidEl("a:spLocks", attrs)));
9274
- const nvPr = (phAttrs) => el("p:nvPr", null, raw(voidEl("p:ph", phAttrs)));
9275
- const bodyPr = (attrs) => voidEl("a:bodyPr", attrs);
9276
- const lstStyleLvl1 = (algn) => el("a:lstStyle", null, raw(el("a:lvl1pPr", { algn }, raw(voidEl("a:defRPr", { sz: 1200 })))));
9277
- const emptyPara = el("a:p", null, raw(voidEl("a:endParaRPr", { lang: "en-US" })));
9278
- const header = el("p:sp", null, [
9279
- raw(el("p:nvSpPr", null, [
9280
- raw(voidEl("p:cNvPr", {
9281
- id: 2,
9282
- name: "Header Placeholder 1"
9283
- })),
9284
- raw(spLocks({ noGrp: 1 })),
9285
- raw(nvPr({
9286
- type: "hdr",
9287
- sz: "quarter"
9288
- }))
9289
- ])),
9290
- raw(el("p:spPr", null, [raw(xfrmOffExt(0, 0, 2971800, 458788)), raw(prstGeomRect)])),
9291
- raw(el("p:txBody", null, [
9292
- raw(bodyPr({
9293
- vert: "horz",
9294
- lIns: 91440,
9295
- tIns: 45720,
9296
- rIns: 91440,
9297
- bIns: 45720,
9298
- rtlCol: 0
9299
- })),
9300
- raw(lstStyleLvl1("l")),
9301
- raw(emptyPara)
9302
- ]))
9303
- ]);
9304
- const date = el("p:sp", null, [
9305
- raw(el("p:nvSpPr", null, [
9306
- raw(voidEl("p:cNvPr", {
9307
- id: 3,
9308
- name: "Date Placeholder 2"
9309
- })),
9310
- raw(spLocks({ noGrp: 1 })),
9311
- raw(nvPr({
9312
- type: "dt",
9313
- idx: 1
9314
- }))
9315
- ])),
9316
- raw(el("p:spPr", null, [raw(xfrmOffExt(3884613, 0, 2971800, 458788)), raw(prstGeomRect)])),
9317
- raw(el("p:txBody", null, [
9318
- raw(bodyPr({
9319
- vert: "horz",
9320
- lIns: 91440,
9321
- tIns: 45720,
9322
- rIns: 91440,
9323
- bIns: 45720,
9324
- rtlCol: 0
9325
- })),
9326
- raw(lstStyleLvl1("r")),
9327
- raw(el("a:p", null, [raw(el("a:fld", {
9328
- id: "{5282F153-3F37-0F45-9E97-73ACFA13230C}",
9329
- type: "datetimeFigureOut"
9330
- }, [raw(voidEl("a:rPr", { lang: "en-US" })), raw(el("a:t", null, "7/23/19"))])), raw(voidEl("a:endParaRPr", { lang: "en-US" }))]))
9331
- ]))
9332
- ]);
9333
- const slideImg = el("p:sp", null, [
9334
- raw(el("p:nvSpPr", null, [
9335
- raw(voidEl("p:cNvPr", {
9336
- id: 4,
9337
- name: "Slide Image Placeholder 3"
9338
- })),
9339
- raw(spLocks({
9340
- noGrp: 1,
9341
- noRot: 1,
9342
- noChangeAspect: 1
9343
- })),
9344
- raw(nvPr({
9345
- type: "sldImg",
9346
- idx: 2
9347
- }))
9348
- ])),
9349
- raw(el("p:spPr", null, [
9350
- raw(xfrmOffExt(685800, 1143e3, 5486400, 3086100)),
9351
- raw(prstGeomRect),
9352
- raw(voidEl("a:noFill")),
9353
- raw(el("a:ln", { w: 12700 }, raw(el("a:solidFill", null, raw(voidEl("a:prstClr", { val: "black" }))))))
9354
- ])),
9355
- raw(el("p:txBody", null, [
9356
- raw(bodyPr({
9357
- vert: "horz",
9358
- lIns: 91440,
9359
- tIns: 45720,
9360
- rIns: 91440,
9361
- bIns: 45720,
9362
- rtlCol: 0,
9363
- anchor: "ctr"
9364
- })),
9365
- raw(voidEl("a:lstStyle")),
9366
- raw(emptyPara)
9367
- ]))
9368
- ]);
9369
- const notesPlaceholder = el("p:sp", null, [
9370
- raw(el("p:nvSpPr", null, [
9371
- raw(voidEl("p:cNvPr", {
9372
- id: 5,
9373
- name: "Notes Placeholder 4"
9374
- })),
9375
- raw(spLocks({ noGrp: 1 })),
9376
- raw(nvPr({
9377
- type: "body",
9378
- sz: "quarter",
9379
- idx: 3
9380
- }))
9381
- ])),
9382
- raw(el("p:spPr", null, [raw(xfrmOffExt(685800, 4400550, 5486400, 3600450)), raw(prstGeomRect)])),
9383
- raw(el("p:txBody", null, [
9384
- raw(bodyPr({
9385
- vert: "horz",
9386
- lIns: 91440,
9387
- tIns: 45720,
9388
- rIns: 91440,
9389
- bIns: 45720,
9390
- rtlCol: 0
9391
- })),
9392
- raw(voidEl("a:lstStyle")),
9393
- raw([
9394
- "Click to edit Master text styles",
9395
- "Second level",
9396
- "Third level",
9397
- "Fourth level",
9398
- "Fifth level"
9399
- ].map((text, lvl) => el("a:p", null, [raw(voidEl("a:pPr", { lvl })), raw(el("a:r", null, [raw(voidEl("a:rPr", { lang: "en-US" })), raw(el("a:t", null, text))]))])).join(""))
9400
- ]))
9401
- ]);
9402
- const footer = el("p:sp", null, [
9403
- raw(el("p:nvSpPr", null, [
9404
- raw(voidEl("p:cNvPr", {
9405
- id: 6,
9406
- name: "Footer Placeholder 5"
9407
- })),
9408
- raw(spLocks({ noGrp: 1 })),
9409
- raw(nvPr({
9410
- type: "ftr",
9411
- sz: "quarter",
9412
- idx: 4
9413
- }))
9414
- ])),
9415
- raw(el("p:spPr", null, [raw(xfrmOffExt(0, 8685213, 2971800, 458787)), raw(prstGeomRect)])),
9416
- raw(el("p:txBody", null, [
9417
- raw(bodyPr({
9418
- vert: "horz",
9419
- lIns: 91440,
9420
- tIns: 45720,
9421
- rIns: 91440,
9422
- bIns: 45720,
9423
- rtlCol: 0,
9424
- anchor: "b"
9425
- })),
9426
- raw(lstStyleLvl1("l")),
9427
- raw(emptyPara)
9428
- ]))
9429
- ]);
9430
- const slideNum = el("p:sp", null, [
9431
- raw(el("p:nvSpPr", null, [
9432
- raw(voidEl("p:cNvPr", {
9433
- id: 7,
9434
- name: "Slide Number Placeholder 6"
9435
- })),
9436
- raw(spLocks({ noGrp: 1 })),
9437
- raw(nvPr({
9438
- type: "sldNum",
9439
- sz: "quarter",
9440
- idx: 5
9441
- }))
9442
- ])),
9443
- raw(el("p:spPr", null, [raw(xfrmOffExt(3884613, 8685213, 2971800, 458787)), raw(prstGeomRect)])),
9444
- raw(el("p:txBody", null, [
9445
- raw(bodyPr({
9446
- vert: "horz",
9447
- lIns: 91440,
9448
- tIns: 45720,
9449
- rIns: 91440,
9450
- bIns: 45720,
9451
- rtlCol: 0,
9452
- anchor: "b"
9453
- })),
9454
- raw(lstStyleLvl1("r")),
9455
- raw(el("a:p", null, [raw(el("a:fld", {
9456
- id: "{CE5E9CC1-C706-0F49-92D6-E571CC5EEA8F}",
9457
- type: "slidenum"
9458
- }, [raw(voidEl("a:rPr", { lang: "en-US" })), raw(el("a:t", null, "‹#›"))])), raw(voidEl("a:endParaRPr", { lang: "en-US" }))]))
9459
- ]))
9460
- ]);
9461
- const spTree = el("p:spTree", null, [
9462
- raw(el("p:nvGrpSpPr", null, [
9463
- raw(voidEl("p:cNvPr", {
9464
- id: 1,
9465
- name: ""
9466
- })),
9467
- raw(voidEl("p:cNvGrpSpPr")),
9468
- raw(voidEl("p:nvPr"))
9469
- ])),
9470
- raw(el("p:grpSpPr", null, raw(el("a:xfrm", null, [
9471
- raw(voidEl("a:off", {
9472
- x: 0,
9473
- y: 0
9474
- })),
9475
- raw(voidEl("a:ext", {
9476
- cx: 0,
9477
- cy: 0
9478
- })),
9479
- raw(voidEl("a:chOff", {
9480
- x: 0,
9481
- y: 0
9482
- })),
9483
- raw(voidEl("a:chExt", {
9484
- cx: 0,
9485
- cy: 0
9486
- }))
9487
- ])))),
9488
- raw(header),
9489
- raw(date),
9490
- raw(slideImg),
9491
- raw(notesPlaceholder),
9492
- raw(footer),
9493
- raw(slideNum)
9494
- ]);
9495
- const extLst = el("p:extLst", null, raw(el("p:ext", { uri: "{BB962C8B-B14F-4D97-AF65-F5344CB8AC3E}" }, raw(voidEl("p14:creationId", {
9496
- "xmlns:p14": "http://schemas.microsoft.com/office/powerpoint/2010/main",
9497
- val: 1024086991
9498
- })))));
9499
- const cSld = el("p:cSld", null, [
9500
- raw(el("p:bg", null, raw(el("p:bgRef", { idx: 1001 }, raw(voidEl("a:schemeClr", { val: "bg1" })))))),
9501
- raw(spTree),
9502
- raw(extLst)
9503
- ]);
9504
- const clrMap = voidEl("p:clrMap", {
9505
- bg1: "lt1",
9506
- tx1: "dk1",
9507
- bg2: "lt2",
9508
- tx2: "dk2",
9509
- accent1: "accent1",
9510
- accent2: "accent2",
9511
- accent3: "accent3",
9512
- accent4: "accent4",
9513
- accent5: "accent5",
9514
- accent6: "accent6",
9515
- hlink: "hlink",
9516
- folHlink: "folHlink"
9517
- });
9518
- const notesStyleLevel = (n, marL) => el(`a:lvl${n}pPr`, {
9519
- marL,
9520
- algn: "l",
9521
- defTabSz: 914400,
9522
- rtl: 0,
9523
- eaLnBrk: 1,
9524
- latinLnBrk: 0,
9525
- hangingPunct: 1
9526
- }, raw(el("a:defRPr", {
9527
- sz: 1200,
9528
- kern: 1200
9529
- }, [
9530
- raw(el("a:solidFill", null, raw(voidEl("a:schemeClr", { val: "tx1" })))),
9531
- raw(voidEl("a:latin", { typeface: "+mn-lt" })),
9532
- raw(voidEl("a:ea", { typeface: "+mn-ea" })),
9533
- raw(voidEl("a:cs", { typeface: "+mn-cs" }))
9534
- ])));
9535
- const notesStyle = el("p:notesStyle", null, raw([
9536
- 0,
9537
- 457200,
9538
- 914400,
9539
- 1371600,
9540
- 1828800,
9541
- 2286e3,
9542
- 2743200,
9543
- 3200400,
9544
- 3657600
9545
- ].map((marL, i) => notesStyleLevel(i + 1, marL)).join("")));
9546
- return XML_DECL + "\r\n" + el("p:notesMaster", {
9547
- "xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
9548
- "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
9549
- "xmlns:p": "http://schemas.openxmlformats.org/presentationml/2006/main"
9550
- }, [
9551
- raw(cSld),
9552
- raw(clrMap),
9553
- raw(notesStyle)
9554
- ]);
9555
- }
9556
- /**
9557
- * Creates Notes Slide (`ppt/notesSlides/notesSlide1.xml`)
9558
- * @param {PresSlideInternal} slide - the slide object to transform into XML
9559
- * @return {string} XML
9560
- */
9561
- function makeXmlNotesSlide(slide) {
9562
- buildNotesSlideRels(slide);
9563
- const slideImgPh = el("p:sp", null, [raw(el("p:nvSpPr", null, [
9564
- raw(voidEl("p:cNvPr", {
9565
- id: 2,
9566
- name: "Slide Image Placeholder 1"
9567
- })),
9568
- raw(el("p:cNvSpPr", null, raw(voidEl("a:spLocks", {
9569
- noGrp: 1,
9570
- noRot: 1,
9571
- noChangeAspect: 1
9572
- })))),
9573
- raw(el("p:nvPr", null, raw(voidEl("p:ph", { type: "sldImg" }))))
9574
- ])), raw(voidEl("p:spPr"))]);
9575
- const notesPh = el("p:sp", null, [
9576
- raw(el("p:nvSpPr", null, [
9577
- raw(voidEl("p:cNvPr", {
9578
- id: 3,
9579
- name: "Notes Placeholder 2"
9580
- })),
9581
- raw(el("p:cNvSpPr", null, raw(voidEl("a:spLocks", { noGrp: 1 })))),
9582
- raw(el("p:nvPr", null, raw(voidEl("p:ph", {
9583
- type: "body",
9584
- idx: 1
9585
- }))))
9586
- ])),
9587
- raw(voidEl("p:spPr")),
9588
- raw(el("p:txBody", null, [
9589
- raw(voidEl("a:bodyPr")),
9590
- raw(voidEl("a:lstStyle")),
9591
- raw(genXmlNotesParagraphs(slide))
9592
- ]))
9593
- ]);
9594
- const slideNumPh = el("p:sp", null, [
9595
- raw(el("p:nvSpPr", null, [
9596
- raw(voidEl("p:cNvPr", {
9597
- id: 4,
9598
- name: "Slide Number Placeholder 3"
9599
- })),
9600
- raw(el("p:cNvSpPr", null, raw(voidEl("a:spLocks", { noGrp: 1 })))),
9601
- raw(el("p:nvPr", null, raw(voidEl("p:ph", {
9602
- type: "sldNum",
9603
- sz: "quarter",
9604
- idx: 10
9605
- }))))
9606
- ])),
9607
- raw(voidEl("p:spPr")),
9608
- raw(el("p:txBody", null, [
9609
- raw(voidEl("a:bodyPr")),
9610
- raw(voidEl("a:lstStyle")),
9611
- raw(el("a:p", null, [raw(el("a:fld", {
9612
- id: SLDNUMFLDID,
9613
- type: "slidenum"
9614
- }, [raw(voidEl("a:rPr", { lang: "en-US" })), raw(el("a:t", null, slide._slideNum))])), raw(voidEl("a:endParaRPr", { lang: "en-US" }))]))
9615
- ]))
9616
- ]);
9617
- const spTree = el("p:spTree", null, [
9618
- raw(el("p:nvGrpSpPr", null, [
9619
- raw(voidEl("p:cNvPr", {
9620
- id: 1,
9621
- name: ""
9622
- })),
9623
- raw(voidEl("p:cNvGrpSpPr")),
9624
- raw(voidEl("p:nvPr"))
9625
- ])),
9626
- raw(el("p:grpSpPr", null, raw(el("a:xfrm", null, [
9627
- raw(voidEl("a:off", {
9628
- x: 0,
9629
- y: 0
9630
- })),
9631
- raw(voidEl("a:ext", {
9632
- cx: 0,
9633
- cy: 0
9634
- })),
9635
- raw(voidEl("a:chOff", {
9636
- x: 0,
9637
- y: 0
9638
- })),
9639
- raw(voidEl("a:chExt", {
9640
- cx: 0,
9641
- cy: 0
9642
- }))
9643
- ])))),
9644
- raw(slideImgPh),
9645
- raw(notesPh),
9646
- raw(slideNumPh)
9647
- ]);
9648
- const extLst = el("p:extLst", null, raw(el("p:ext", { uri: "{BB962C8B-B14F-4D97-AF65-F5344CB8AC3E}" }, raw(voidEl("p14:creationId", {
9649
- "xmlns:p14": "http://schemas.microsoft.com/office/powerpoint/2010/main",
9650
- val: 1024086991
9651
- })))));
9652
- const cSld = el("p:cSld", null, [raw(spTree), raw(extLst)]);
9653
- return XML_DECL + "\r\n" + el("p:notes", {
9654
- "xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
9655
- "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
9656
- "xmlns:p": "http://schemas.openxmlformats.org/presentationml/2006/main"
9657
- }, [raw(cSld), raw(el("p:clrMapOvr", null, raw(voidEl("a:masterClrMapping"))))]);
9658
- }
9659
- /**
9660
- * Generates XML string for a notes-slide relation file (`ppt/notesSlides/_rels/notesSlideN.xml.rels`).
9661
- * rId1=notesMaster and rId2=slide are always reserved; any notes hyperlink rels follow (rId3+).
9662
- * @param {PresSlideInternal} slide - the slide whose notes part is being related
9663
- * @param {number} slideNumber - 1-indexed slide number the notes part belongs to
9664
- * @return {string} XML
9665
- */
9666
- function makeXmlNotesSlideRel(slide, slideNumber) {
9667
- const hlinkRels = buildNotesSlideRels(slide).map((rel) => voidEl("Relationship", {
9668
- Id: `rId${rel.rId}`,
9669
- Type: OFFICE_REL + "hyperlink",
9670
- Target: rel.Target,
9671
- TargetMode: "External"
9672
- })).join("");
9673
- return XML_DECL + el("Relationships", { xmlns: PACKAGE_REL_NS }, [
9674
- raw(voidEl("Relationship", {
9675
- Id: "rId1",
9676
- Type: OFFICE_REL + "notesMaster",
9677
- Target: "../notesMasters/notesMaster1.xml"
9678
- })),
9679
- raw(voidEl("Relationship", {
9680
- Id: "rId2",
9681
- Type: OFFICE_REL + "slide",
9682
- Target: `../slides/slide${slideNumber}.xml`
9683
- })),
9684
- raw(hlinkRels)
9685
- ], {
9686
- openPrefix: "\n ",
9687
- childPrefix: "\n ",
9688
- closePrefix: ""
9689
- });
9690
- }
9691
- /**
9692
- * Creates `ppt/notesMasters/_rels/notesMaster1.xml.rels`
9693
- * @return {string} XML
9694
- */
9695
- function makeXmlNotesMasterRel() {
9696
- return XML_DECL + "\r\n" + el("Relationships", { xmlns: PACKAGE_REL_NS }, raw(voidEl("Relationship", {
9697
- Id: "rId1",
9698
- Type: OFFICE_REL + "theme",
9699
- Target: "../theme/theme2.xml"
9700
- })), {
9701
- childPrefix: "\n ",
9702
- closePrefix: "\n "
9703
- });
9704
- }
9705
- //#endregion
9706
8216
  //#region src/gen/pres/theme.ts
9707
8217
  /**
9708
8218
  * ts-pptx: `ppt/theme/theme1.xml`
@@ -9994,11 +8504,14 @@ function makeXmlApp(slides, company) {
9994
8504
  * actually used by the deck (plus xlsx/font defaults when present) and Override
9995
8505
  * entries for every written part.
9996
8506
  */
9997
- const CT_CHART = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml";
8507
+ /** Content-type prefixes; spelled out per part below so each entry stays greppable by its suffix. */
8508
+ const OD = "application/vnd.openxmlformats-officedocument.";
8509
+ const PKG = "application/vnd.openxmlformats-package.";
8510
+ const CT_CHART = OD + "drawingml.chart+xml";
9998
8511
  const CT_CHARTEX = "application/vnd.ms-office.chartex+xml";
9999
8512
  const CT_CHARTEX_STYLE = "application/vnd.ms-office.chartstyle+xml";
10000
8513
  const CT_CHARTEX_COLORS = "application/vnd.ms-office.chartcolorstyle+xml";
10001
- const CT_THEME = "application/vnd.openxmlformats-officedocument.theme+xml";
8514
+ const CT_THEME = OD + "theme+xml";
10002
8515
  /**
10003
8516
  * Some Override entries have always been emitted with a leading space. It is insignificant
10004
8517
  * whitespace, but it is in the bytes this library has shipped for years, so it is reproduced
@@ -10038,7 +8551,7 @@ function chartOverrides(rel, fmt) {
10038
8551
  * @returns XML
10039
8552
  */
10040
8553
  function makeXmlContTypes(slides, slideLayouts, masterSlide, hasCustomProps, embeddedFonts) {
10041
- const parts = [contentDefault("xml", "application/xml"), contentDefault("rels", "application/vnd.openxmlformats-package.relationships+xml")];
8554
+ const parts = [contentDefault("xml", "application/xml"), contentDefault("rels", PKG + "relationships+xml")];
10042
8555
  const extnTypeMap = /* @__PURE__ */ new Map();
10043
8556
  const ctTargets = [];
10044
8557
  (slides || []).forEach((s) => ctTargets.push(s));
@@ -10056,45 +8569,45 @@ function makeXmlContTypes(slides, slideLayouts, masterSlide, hasCustomProps, emb
10056
8569
  extnTypeMap.forEach((type, extn) => {
10057
8570
  parts.push(contentDefault(extn, type));
10058
8571
  });
10059
- if (ctHasChart && !extnTypeMap.has("xlsx")) parts.push(contentDefault("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
8572
+ if (ctHasChart && !extnTypeMap.has("xlsx")) parts.push(contentDefault("xlsx", OD + "spreadsheetml.sheet"));
10060
8573
  if ((embeddedFonts || []).some((font) => font.faces.some((face) => face.bytes))) parts.push(contentDefault(FONT_DATA_EXTENSION, FONT_DATA_CONTENT_TYPE));
10061
- parts.push(override("/ppt/presentation.xml", "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"));
10062
- parts.push(override("/ppt/notesMasters/notesMaster1.xml", "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml"));
10063
- parts.push(override("/ppt/slideMasters/slideMaster1.xml", "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"));
8574
+ parts.push(override("/ppt/presentation.xml", OD + "presentationml.presentation.main+xml"));
8575
+ parts.push(override("/ppt/notesMasters/notesMaster1.xml", OD + "presentationml.notesMaster+xml"));
8576
+ parts.push(override("/ppt/slideMasters/slideMaster1.xml", OD + "presentationml.slideMaster+xml"));
10064
8577
  slides.forEach((slide, idx) => {
10065
- parts.push(override(`/ppt/slides/slide${idx + 1}.xml`, "application/vnd.openxmlformats-officedocument.presentationml.slide+xml"));
8578
+ parts.push(override(`/ppt/slides/slide${idx + 1}.xml`, OD + "presentationml.slide+xml"));
10066
8579
  slide._relsChart.forEach((rel) => {
10067
8580
  parts.push(...chartOverrides(rel));
10068
8581
  });
10069
8582
  });
10070
- parts.push(override("/ppt/presProps.xml", "application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"));
10071
- parts.push(override("/ppt/viewProps.xml", "application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml"));
8583
+ parts.push(override("/ppt/presProps.xml", OD + "presentationml.presProps+xml"));
8584
+ parts.push(override("/ppt/viewProps.xml", OD + "presentationml.viewProps+xml"));
10072
8585
  parts.push(override("/ppt/theme/theme1.xml", CT_THEME));
10073
8586
  parts.push(override("/ppt/theme/theme2.xml", CT_THEME));
10074
- parts.push(override("/ppt/tableStyles.xml", "application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml"));
8587
+ parts.push(override("/ppt/tableStyles.xml", OD + "presentationml.tableStyles+xml"));
10075
8588
  slideLayouts.forEach((layout, idx) => {
10076
- parts.push(override(`/ppt/slideLayouts/slideLayout${idx + 1}.xml`, "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"));
8589
+ parts.push(override(`/ppt/slideLayouts/slideLayout${idx + 1}.xml`, OD + "presentationml.slideLayout+xml"));
10077
8590
  (layout._relsChart || []).forEach((rel) => {
10078
8591
  parts.push(...chartOverrides(rel, LEADING_SPACE));
10079
8592
  });
10080
8593
  });
10081
8594
  slides.forEach((_slide, idx) => {
10082
- parts.push(override(`/ppt/notesSlides/notesSlide${idx + 1}.xml`, "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml"));
8595
+ parts.push(override(`/ppt/notesSlides/notesSlide${idx + 1}.xml`, OD + "presentationml.notesSlide+xml"));
10083
8596
  });
10084
8597
  let hasAnyComment = false;
10085
8598
  slides.forEach((slide, idx) => {
10086
8599
  if ((slide._comments || []).length > 0) {
10087
8600
  hasAnyComment = true;
10088
- parts.push(override(`/ppt/comments/comment${idx + 1}.xml`, "application/vnd.openxmlformats-officedocument.presentationml.comments+xml"));
8601
+ parts.push(override(`/ppt/comments/comment${idx + 1}.xml`, OD + "presentationml.comments+xml"));
10089
8602
  }
10090
8603
  });
10091
- if (hasAnyComment) parts.push(override("/ppt/commentAuthors.xml", "application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml"));
8604
+ if (hasAnyComment) parts.push(override("/ppt/commentAuthors.xml", OD + "presentationml.commentAuthors+xml"));
10092
8605
  masterSlide?._relsChart.forEach((rel) => {
10093
8606
  parts.push(...chartOverrides(rel, LEADING_SPACE));
10094
8607
  });
10095
- parts.push(override("/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml", LEADING_SPACE));
10096
- parts.push(override("/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml", LEADING_SPACE));
10097
- if (hasCustomProps) parts.push(override("/docProps/custom.xml", "application/vnd.openxmlformats-officedocument.custom-properties+xml", LEADING_SPACE));
8608
+ parts.push(override("/docProps/core.xml", PKG + "core-properties+xml", LEADING_SPACE));
8609
+ parts.push(override("/docProps/app.xml", OD + "extended-properties+xml", LEADING_SPACE));
8610
+ if (hasCustomProps) parts.push(override("/docProps/custom.xml", OD + "custom-properties+xml", LEADING_SPACE));
10098
8611
  return XML_DECL + "\r\n" + el("Types", { xmlns: "http://schemas.openxmlformats.org/package/2006/content-types" }, parts.map(raw));
10099
8612
  }
10100
8613
  //#endregion
@@ -10643,7 +9156,7 @@ function masterAlignAttr(align) {
10643
9156
  case "right": return "r";
10644
9157
  case "center": return "ctr";
10645
9158
  case "justify": return "just";
10646
- default: return "";
9159
+ case void 0: return "";
10647
9160
  }
10648
9161
  }
10649
9162
  /** Build the bullet element for a master level: caller override wins over the level default. */
@@ -10651,10 +9164,14 @@ function masterBulletXml(bulletOverride, base) {
10651
9164
  if (bulletOverride === false) return voidEl("a:buNone");
10652
9165
  if (bulletOverride && typeof bulletOverride === "object") {
10653
9166
  const font = bulletOverride.fontFace ? voidEl("a:buFont", { typeface: bulletOverride.fontFace }) : "";
10654
- if (bulletOverride.type === "number") return font + voidEl("a:buAutoNum", {
10655
- type: bulletOverride.numberType || "arabicPeriod",
10656
- startAt: typeof bulletOverride.numberStartAt === "number" ? Math.round(bulletOverride.numberStartAt) : null
10657
- });
9167
+ if (bulletOverride.type === "number") {
9168
+ const type = bulletOverride.numberType || "arabicPeriod";
9169
+ const startAt = typeof bulletOverride.numberStartAt === "number" ? Math.round(bulletOverride.numberStartAt) : null;
9170
+ return font + voidEl("a:buAutoNum", {
9171
+ type,
9172
+ startAt
9173
+ });
9174
+ }
10658
9175
  const char = bulletOverride.characterCode ? `&#x${bulletOverride.characterCode};` : "•";
10659
9176
  return `${bulletOverride.fontFace ? font : voidEl("a:buFont", {
10660
9177
  typeface: "Arial",
@@ -10676,8 +9193,10 @@ function masterLevelXml(levelNum, base, levelOverride = {}) {
10676
9193
  const indentEmu = typeof levelOverride.indent === "number" && !isNaN(levelOverride.indent) ? inch2Emu(levelOverride.indent) : base.indent;
10677
9194
  const algn = levelOverride.align && masterAlignAttr(levelOverride.align) || base.algn;
10678
9195
  let sz = base.sz;
10679
- if (typeof levelOverride.fontSize === "number") if (isNaN(levelOverride.fontSize) || levelOverride.fontSize <= 0) warn("master/invalid-text-style-font-size", `master textStyles fontSize "${levelOverride.fontSize}" is invalid; keeping default ${base.sz / 100}pt.`);
10680
- else sz = ptToHundredths(levelOverride.fontSize);
9196
+ if (typeof levelOverride.fontSize === "number") {
9197
+ if (isNaN(levelOverride.fontSize) || levelOverride.fontSize <= 0) warn("master/invalid-text-style-font-size", `master textStyles fontSize "${levelOverride.fontSize}" is invalid; keeping default ${base.sz / 100}pt.`);
9198
+ else sz = ptToHundredths(levelOverride.fontSize);
9199
+ }
10681
9200
  const colorXml = levelOverride.color ? createColorElement(levelOverride.color) : voidEl("a:schemeClr", { val: "tx1" });
10682
9201
  const latinXml = levelOverride.fontFace ? voidEl("a:latin", { typeface: levelOverride.fontFace }) : voidEl("a:latin", { typeface: `+${base.font}-lt` });
10683
9202
  return el(`a:lvl${levelNum}pPr`, {
@@ -11060,8 +9579,7 @@ async function zipPackageParts(parts, props) {
11060
9579
  const zip = new ZipWriter();
11061
9580
  for (const part of parts) zip.add(part.path, part.data, { store: part.store });
11062
9581
  const compression = props.compression !== false;
11063
- if (props.outputType === "STREAM") return await zip.generate("nodebuffer", { compression });
11064
- else if (props.outputType) return await zip.generate(props.outputType, { compression });
9582
+ if (props.outputType) return await zip.generate(props.outputType, { compression });
11065
9583
  else return await zip.generate("blob", { compression });
11066
9584
  }
11067
9585
  /**
@@ -11088,7 +9606,7 @@ async function writePackage(source, props) {
11088
9606
  * the OfficeGen project (Copyright 2013 Ziv Barber). This project is likewise MIT; the full
11089
9607
  * license text is in `LICENSE` at the repository root.
11090
9608
  */
11091
- const VERSION = "3.2.0";
9609
+ const VERSION = "3.3.0";
11092
9610
  function standardLayoutToPresLayout(layout) {
11093
9611
  return {
11094
9612
  name: layout.name,
@@ -11469,11 +9987,23 @@ var PresentationCore = class {
11469
9987
  rId: rel.rId,
11470
9988
  sourceSlideNumber: Number(rel.Target)
11471
9989
  }));
11472
- const charts = (slide._relsChart || []).map((rel) => ({
11473
- rId: rel.rId,
11474
- chartXml: makeXmlCharts(rel),
11475
- embeddingBytes: buildEmbeddedWorksheet(rel)
11476
- }));
9990
+ const charts = (slide._relsChart || []).map((rel) => {
9991
+ const base = {
9992
+ rId: rel.rId,
9993
+ embeddingBytes: buildEmbeddedWorksheet(rel)
9994
+ };
9995
+ return rel.isChartEx ? {
9996
+ ...base,
9997
+ chartXml: makeXmlChartEx(rel),
9998
+ chartEx: {
9999
+ styleXml: makeChartExStyleXml(),
10000
+ colorsXml: makeChartExColorsXml()
10001
+ }
10002
+ } : {
10003
+ ...base,
10004
+ chartXml: makeXmlCharts(rel)
10005
+ };
10006
+ });
11477
10007
  const notes = slide._slideObjects.some((obj) => obj._type === "notes") ? {
11478
10008
  xml: makeXmlNotesSlide(slide),
11479
10009
  hyperlinks: buildNotesSlideRels(slide).filter((rel) => typeof rel.Target === "string" && rel.Target).map((rel) => ({
@@ -11512,19 +10042,36 @@ var PresentationCore = class {
11512
10042
  * Without registered metrics, `fit:'shrink'` keeps its current behavior (a bare
11513
10043
  * `<a:normAutofit/>` that only PowerPoint recomputes on edit). Register the same
11514
10044
  * face once per weight/style you use; bold/italic advances differ.
10045
+ *
10046
+ * **Font collections** (`.ttc`/`.otc`, how MS Gothic, Yu Gothic, SimSun, Microsoft
10047
+ * YaHei and Cambria ship on Windows) hold several fonts in one file, so one has to be
10048
+ * chosen. With no `font` option, `face` is used as the selector, which is usually what
10049
+ * you meant; a `face` that names no font in the collection throws rather than falling
10050
+ * back to the first, since measuring the wrong member is invisible downstream. Pass
10051
+ * `font` when the deck-side name differs from the name inside the file, and use
10052
+ * `listFontFaces` from `ts-pptx/measure` to see what a file holds.
11515
10053
  * @param {string} face - font family name as used in `fontFace` (e.g. 'Aptos')
11516
- * @param {string | Uint8Array | ArrayBuffer} source - font file path/URL (Node/web) or raw TTF/OTF bytes
11517
- * @param {object} [opts] - variant flags; advances differ per weight/style
10054
+ * @param {string | Uint8Array | ArrayBuffer} source - font file path/URL (Node/web) or raw TTF/OTF/TTC bytes
10055
+ * @param {object} [opts] - variant flags (advances differ per weight/style) and collection selection
10056
+ * @param {boolean} [opts.bold] - these are the bold advances
10057
+ * @param {boolean} [opts.italic] - these are the italic advances
10058
+ * @param {number | string} [opts.font] - which font inside a collection: 0-based index, or a name
11518
10059
  * @example await pptx.registerFontMetrics('Aptos', '/usr/share/fonts/Aptos.ttf')
11519
10060
  * @example await pptx.registerFontMetrics('Aptos', aptosBoldBytes, { bold: true })
10061
+ * @example await pptx.registerFontMetrics('MS PGothic', 'C:/Windows/Fonts/msgothic.ttc')
10062
+ * @example await pptx.registerFontMetrics('Cambria Math', 'C:/Windows/Fonts/cambria.ttc', { font: 1 })
11520
10063
  */
11521
10064
  async registerFontMetrics(face, source, opts) {
11522
10065
  let bytes;
11523
10066
  if (typeof source === "string") bytes = await this._runtime.loadFontData(source);
11524
10067
  else if (source instanceof Uint8Array) bytes = source;
11525
10068
  else bytes = new Uint8Array(source);
11526
- const metrics = await parseFontMetrics(bytes);
11527
- this._fontMetrics.set(face, metrics, opts);
10069
+ const font = opts?.font ?? (isFontCollection(bytes) ? face : void 0);
10070
+ const metrics = await parseFontMetrics(bytes, font === void 0 ? void 0 : { font });
10071
+ this._fontMetrics.set(face, metrics, {
10072
+ bold: opts?.bold,
10073
+ italic: opts?.italic
10074
+ });
11528
10075
  }
11529
10076
  /**
11530
10077
  * Embed a font face so the deck renders with it even on machines that do not
@@ -11580,7 +10127,7 @@ var PresentationCore = class {
11580
10127
  * Measure how tall text wraps at a given width, using the **same** calibrated wrap
11581
10128
  * model the export-time autofit bake uses — so a layout-time prediction matches the
11582
10129
  * value `fit:'shrink'`/`'resize'` would bake. Synchronous: register the face's metrics
11583
- * first with {@link PresentationCore.registerFontMetrics} (lookup is sync).
10130
+ * first with {@link TsPptx.registerFontMetrics} (lookup is sync).
11584
10131
  *
11585
10132
  * Lets a consumer size its own geometry before export — grow a card to fit its text,
11586
10133
  * reflow a grid, or detect overflow at layout time. Heights err **tall** (conservative),
@@ -11614,8 +10161,8 @@ var PresentationCore = class {
11614
10161
  * cells. Column widths (cell `x`/`w`) are exact, derived from the same logic the
11615
10162
  * writer uses. Row heights (`y`/`h`) are exact when pinned by `rowH` (array or
11616
10163
  * scalar) or table `h`; an auto-height row is estimated with the same conservative
11617
- * (tall) text model as {@link PresentationCore.measureText} and flagged `heightExact:false`
11618
- * (register the cell font via {@link PresentationCore.registerFontMetrics} for an exact
10164
+ * (tall) text model as {@link TsPptx.measureText} and flagged `heightExact:false`
10165
+ * (register the cell font via {@link TsPptx.registerFontMetrics} for an exact
11619
10166
  * estimate). Geometry is for a single, un-paginated table — `autoPage` paging is
11620
10167
  * not modeled.
11621
10168
  * @param {TableRow[]} rows - the same `rows` passed to `slide.addTable`
@@ -11630,14 +10177,18 @@ var PresentationCore = class {
11630
10177
  return computeTableLayout(rows, opts, this._presLayout, this._fontMetrics);
11631
10178
  }
11632
10179
  /**
11633
- * Export the current Presentation to stream
10180
+ * Export the current presentation as portable ZIP bytes. Unlike the former
10181
+ * `stream()` method, this does not imply incremental output or convert the
10182
+ * archive to a Node `Buffer`: package assembly is synchronous and the returned
10183
+ * `Uint8Array` works unchanged in Node, browsers, and workers.
11634
10184
  * @param {WriteBaseProps} props - output properties
11635
- * @returns {Promise<string | ArrayBuffer | Blob | Uint8Array>} file stream
10185
+ * @returns {Promise<Uint8Array>} complete `.pptx` archive bytes
11636
10186
  */
11637
- async stream(props) {
10187
+ async toBytes(props) {
11638
10188
  return await writePackage(this.packageSource(), {
11639
10189
  compression: props?.compression,
11640
- outputType: "STREAM"
10190
+ outputType: "uint8array",
10191
+ onMediaError: props?.onMediaError
11641
10192
  });
11642
10193
  }
11643
10194
  /**
@@ -11976,4 +10527,4 @@ function textRuns(runs) {
11976
10527
  //#endregion
11977
10528
  export { PresentationCore as i, textRuns as n, clipPath as r, textRun as t };
11978
10529
 
11979
- //# sourceMappingURL=text-C4P9MNdf.js.map
10530
+ //# sourceMappingURL=text-uM-zGton.js.map