@shbernal/ts-pptx 3.1.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 (73) hide show
  1. package/README.md +55 -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-PMINsVP6.d.ts → diagnostics-B2yWpcjB.d.ts} +31 -14
  8. package/dist/{diagnostics-PMINsVP6.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-BeUEkNWL.d.ts → errors-fB1KDJKQ.d.ts} +5 -5
  14. package/dist/{errors-BeUEkNWL.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-BgvxhQ_m.d.ts → pres-Cb-1xDne.d.ts} +4 -4
  36. package/dist/{pres-BgvxhQ_m.d.ts.map → pres-Cb-1xDne.d.ts.map} +1 -1
  37. package/dist/{presentation-BP1B8PCF.js → presentation-DeKTJGHf.js} +1096 -440
  38. package/dist/presentation-DeKTJGHf.js.map +1 -0
  39. package/dist/{presentation-DyIRDKkp.d.ts → presentation-types-B50aOUFI.d.ts} +804 -39
  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 +32 -4
  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-DMThLsZN.js → shapes-BXVgsk0F.js} +81 -63
  49. package/dist/shapes-BXVgsk0F.js.map +1 -0
  50. package/dist/{slide-BRAtT8Dl.d.ts → slide-DWw9UoI6.d.ts} +130 -11
  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-B6PbMFih.js → text-uM-zGton.js} +242 -1611
  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 +40 -28
  60. package/skills/ts-pptx-upstream/SKILL.md +107 -14
  61. package/dist/embedded-fonts-MaynAHa9.js +0 -94
  62. package/dist/embedded-fonts-MaynAHa9.js.map +0 -1
  63. package/dist/fit-DhMbQG5O.js.map +0 -1
  64. package/dist/presentation-BP1B8PCF.js.map +0 -1
  65. package/dist/presentation-DyIRDKkp.d.ts.map +0 -1
  66. package/dist/presentation-types-Dj4riuDs.d.ts +0 -538
  67. package/dist/presentation-types-Dj4riuDs.d.ts.map +0 -1
  68. package/dist/shapes-DMThLsZN.js.map +0 -1
  69. package/dist/slide-BRAtT8Dl.d.ts.map +0 -1
  70. package/dist/text-B6PbMFih.js.map +0 -1
  71. package/dist/units-C7QM1KgH.js +0 -144
  72. package/dist/units-C7QM1KgH.js.map +0 -1
  73. 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,137 +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 "solid":
3212
- outText += el("a:solidFill", null, raw(createColorElement(colorVal, internalElements)));
3213
- break;
3214
- case "gradient":
3215
- outText += genXmlGradientFill(typeof props === "string" ? void 0 : props.gradient);
3216
- break;
3217
- case "pattern":
3218
- outText += genXmlPatternFill(typeof props === "string" ? void 0 : props.pattern);
3219
- break;
3220
- case "image":
3221
- outText += genXmlImageFill(typeof props === "string" ? void 0 : props);
3222
- break;
3223
- default: outText += "";
3224
- }
3225
- }
3226
- return outText;
3227
- }
3228
- //#endregion
3229
2711
  //#region src/gen/drawingml/line.ts
3230
2712
  /** Every key `BorderProps` defines. Keep in step with the interface in `types/style.ts`. */
3231
2713
  const BORDER_KEYS = [
@@ -3477,13 +2959,14 @@ function genXmlTitle(opts, chartX, chartY) {
3477
2959
  * @return {string} XML
3478
2960
  */
3479
2961
  function createGridLineElement(glOpts) {
3480
- return el("c:majorGridlines", null, raw(el("c:spPr", null, raw(el("a:ln", {
2962
+ const line = el("a:ln", {
3481
2963
  w: valToPts(glOpts.size || DEF_CHART_GRIDLINE.size || 1),
3482
2964
  cap: createLineCap(glOpts.cap || DEF_CHART_GRIDLINE.cap)
3483
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"))], {
3484
2966
  openPrefix: " ",
3485
2967
  closePrefix: " "
3486
- })), {
2968
+ });
2969
+ return el("c:majorGridlines", null, raw(el("c:spPr", null, raw(line), {
3487
2970
  openPrefix: " ",
3488
2971
  closePrefix: " "
3489
2972
  })));
@@ -3577,10 +3060,11 @@ function createSerLinesElement(opt) {
3577
3060
  if (!opt) return "";
3578
3061
  if (opt === true) return "<c:serLines/>";
3579
3062
  if (opt.style === "none") return "";
3580
- return el("c:serLines", null, raw(el("c:spPr", null, raw(el("a:ln", {
3063
+ const line = el("a:ln", {
3581
3064
  w: valToPts(opt.size || DEF_CHART_GRIDLINE.size || 1),
3582
3065
  cap: createLineCap(opt.cap || DEF_CHART_GRIDLINE.cap)
3583
- }, [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))));
3584
3068
  }
3585
3069
  /**
3586
3070
  * Build the `<c:leaderLines>` element for pie/doughnut data labels.
@@ -3598,7 +3082,9 @@ function createSerLinesElement(opt) {
3598
3082
  function createLeaderLinesElement(opts) {
3599
3083
  if (!opts.showLeaderLines) return "";
3600
3084
  if (!opts.leaderLineColor && opts.leaderLineSize == null) return "";
3601
- 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>`;
3602
3088
  }
3603
3089
  /**
3604
3090
  * Build a single custom `<c:dLbl>` (rich-text data label) overriding one data point's label.
@@ -3674,8 +3160,10 @@ function makeSeriesDataPointsXml(chartType, obj, opts, varyColors) {
3674
3160
  ...pattern,
3675
3161
  fgColor: fillColor
3676
3162
  } : pattern);
3677
- else if (fillColor) if (chartType === "bar3D") xml += `<a:ln>${genXmlColorSelection(fillColor)}</a:ln>`;
3678
- 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
+ }
3679
3167
  if (border) xml += createChartBorderLine(border);
3680
3168
  }
3681
3169
  xml += createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW);
@@ -4825,6 +4313,27 @@ function makeSurfaceScene(opts) {
4825
4313
  * {@link ./data-refs}.
4826
4314
  */
4827
4315
  /**
4316
+ * Whether a chart-area/plot-area `fill` says anything the fill dispatch can act on.
4317
+ *
4318
+ * `c:spPr` is `a:CT_ShapeProperties` — the same optional `EG_FillProperties` group the
4319
+ * shape path writes into — so gradient, pattern and an omitted fill child are all legal
4320
+ * here. The gate used to be `fill?.color`, which meant every spelling carrying no colour
4321
+ * (`gradient`, `pattern`, `image`, `inherit`) fell to the no-fill arm and did nothing.
4322
+ *
4323
+ * The gate cannot simply be `fill != null`. `normalizeChartOptions` defaults
4324
+ * `plotArea.fill` to `{}`, so every chart in existence arrives here with a fill object,
4325
+ * and a presence check would paint each one a default grey. The same is true of the one
4326
+ * input a caller can write by hand: `{ transparency: 50 }` alone is not a fill, since
4327
+ * there is no colour for the alpha to apply to. Both stay no-fill.
4328
+ *
4329
+ * A payload with no `type` (`{ gradient }`, `{ pattern }`) is likewise not stated, which
4330
+ * matches the shape path: only `line` infers its type from a bare `gradient`, fills have
4331
+ * always wanted `type: 'gradient'` spelled out.
4332
+ */
4333
+ function isStatedFill(fill) {
4334
+ return Boolean(fill?.color || fill?.type);
4335
+ }
4336
+ /**
4828
4337
  * Build the chartSpace/chart header: chartSpace open, title (or autoTitleDeleted),
4829
4338
  * optional 3D view, and the plotArea open with optional manual layout.
4830
4339
  */
@@ -4980,7 +4489,7 @@ function makeChartPlotAreaPropsXml(rel) {
4980
4489
  strXml += "</c:dTable>";
4981
4490
  }
4982
4491
  strXml += " <c:spPr>";
4983
- strXml += plotArea.fill?.color ? genXmlColorSelection(plotArea.fill) : "<a:noFill/>";
4492
+ strXml += isStatedFill(plotArea.fill) ? genXmlColorSelection(plotArea.fill) : "<a:noFill/>";
4984
4493
  strXml += plotArea.border ? `<a:ln w="${valToPts(resolveBorderWidth(plotArea.border, 1))}" cap="flat">${genXmlColorSelection({
4985
4494
  color: plotArea.border.color ?? "363636",
4986
4495
  transparency: plotArea.border.transparency
@@ -5065,9 +4574,10 @@ function makeXmlCharts(rel) {
5065
4574
  usesSecondaryCatAxis = usesSecondaryCatAxis || (options.secondaryCatAxis ?? false);
5066
4575
  const subType = asChartType(type.type);
5067
4576
  const usesValueXAxis = subType === "scatter" || subType === "bubble" || subType === "bubble3D";
5068
- if (options.secondaryCatAxis) if (usesValueXAxis) secondaryCatAxisValType = subType;
5069
- else secondaryCatAxisHasCategoryChart = true;
5070
- else if (usesValueXAxis) primaryCatAxisValType = subType;
4577
+ if (options.secondaryCatAxis) {
4578
+ if (usesValueXAxis) secondaryCatAxisValType = subType;
4579
+ else secondaryCatAxisHasCategoryChart = true;
4580
+ } else if (usesValueXAxis) primaryCatAxisValType = subType;
5071
4581
  else primaryCatAxisHasCategoryChart = true;
5072
4582
  strXml += makeChartType(subType, type.data, options, valAxisId, catAxisId);
5073
4583
  });
@@ -5079,7 +4589,7 @@ function makeXmlCharts(rel) {
5079
4589
  if (rel.opts._type === "scatter") strXml += "<c:showDLblsOverMax val=\"1\"/>";
5080
4590
  strXml += "</c:chart>";
5081
4591
  strXml += "<c:spPr>";
5082
- strXml += chartArea.fill?.color ? genXmlColorSelection(chartArea.fill) : "<a:noFill/>";
4592
+ strXml += isStatedFill(chartArea.fill) ? genXmlColorSelection(chartArea.fill) : "<a:noFill/>";
5083
4593
  strXml += chartArea.border ? `<a:ln w="${valToPts(resolveBorderWidth(chartArea.border, 1))}" cap="flat">${genXmlColorSelection({
5084
4594
  color: chartArea.border.color ?? "363636",
5085
4595
  transparency: chartArea.border.transparency
@@ -5158,7 +4668,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
5158
4668
  case "surface": return makeSurfacePlot(chartType, data, opts, valAxisId, catAxisId, valFmtCode);
5159
4669
  case "doughnut":
5160
4670
  case "pie": return makePiePlot(chartType, data, opts, valFmtCode);
5161
- 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 } });
5162
4672
  }
5163
4673
  }
5164
4674
  //#endregion
@@ -5221,7 +4731,8 @@ function makeChartExData(rel) {
5221
4731
  formatCode: "General"
5222
4732
  }, raw(valPts)))]);
5223
4733
  const data = el("cx:data", { id: 0 }, [raw(catDim), raw(valDim)]);
5224
- 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)]);
5225
4736
  }
5226
4737
  /**
5227
4738
  * The `Sheet1` cell holding the series name (the numeric dimension's header) — column immediately
@@ -5240,7 +4751,8 @@ const A_NS = "http://schemas.openxmlformats.org/drawingml/2006/main";
5240
4751
  const R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
5241
4752
  /**
5242
4753
  * Map a chartEx {@link ChartType} to its `<cx:series layoutId>` token (the CT_SeriesLayout value
5243
- * 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.
5244
4756
  */
5245
4757
  function chartExLayoutId(type) {
5246
4758
  switch (type) {
@@ -5251,7 +4763,7 @@ function chartExLayoutId(type) {
5251
4763
  case "histogram": return "clusteredColumn";
5252
4764
  case "boxWhisker": return "boxWhisker";
5253
4765
  case "regionMap": return "regionMap";
5254
- 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 } });
5255
4767
  }
5256
4768
  }
5257
4769
  /**
@@ -5329,11 +4841,12 @@ function makeBoxWhiskerLayoutPr(stats) {
5329
4841
  * renders blank if the names don't match PowerPoint's geography database.
5330
4842
  */
5331
4843
  function makeGeographyLayoutPr(geo) {
5332
- return el("cx:layoutPr", null, raw(voidEl("cx:geography", {
4844
+ const geography = voidEl("cx:geography", {
5333
4845
  cultureLanguage: geo?.cultureLanguage || "en-US",
5334
4846
  cultureRegion: geo?.cultureRegion || "US",
5335
4847
  attribution: "Powered by Bing"
5336
- })));
4848
+ });
4849
+ return el("cx:layoutPr", null, raw(geography));
5337
4850
  }
5338
4851
  /** The `<cx:tx>` series-name cell/value block (shared by every layout that names its series). */
5339
4852
  function makeChartExSeriesName(rel) {
@@ -5379,8 +4892,10 @@ function makeChartExSeries(rel) {
5379
4892
  const type = opts._type;
5380
4893
  if (type === "pareto") return makeParetoSeries(rel);
5381
4894
  let layoutPr = "";
5382
- 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("")))));
5383
- 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" })));
5384
4899
  else if (type === "histogram") layoutPr = el("cx:layoutPr", null, raw(makeChartExBinning(opts.binning)));
5385
4900
  else if (type === "boxWhisker") layoutPr = makeBoxWhiskerLayoutPr(opts.statistics);
5386
4901
  else if (type === "regionMap") layoutPr = makeGeographyLayoutPr(opts.geography);
@@ -5449,15 +4964,16 @@ function makeChartExTitle(rel) {
5449
4964
  const color = rel.opts.titleColor || "000000";
5450
4965
  const face = rel.opts.titleFontFace || "Calibri";
5451
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
+ ]);
5452
4972
  return el("cx:title", {
5453
4973
  pos: "t",
5454
4974
  align: "ctr",
5455
4975
  overlay: 0
5456
- }, raw(el("cx:tx", null, raw(el("cx:rich", null, [
5457
- raw(voidEl("a:bodyPr")),
5458
- raw(voidEl("a:lstStyle")),
5459
- 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"))]))]))
5460
- ])))));
4976
+ }, raw(el("cx:tx", null, raw(rich))));
5461
4977
  }
5462
4978
  /**
5463
4979
  * Assemble the full `<cx:chartSpace>` for a chartEx chart part.
@@ -5468,7 +4984,8 @@ function makeChartExTitle(rel) {
5468
4984
  */
5469
4985
  function makeXmlChartEx(rel) {
5470
4986
  const type = rel.opts._type;
5471
- 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))]);
5472
4989
  const legend = rel.opts.showLegend ? voidEl("cx:legend", {
5473
4990
  pos: chartExLegendPos(rel.opts.legendPos),
5474
4991
  align: "ctr",
@@ -5522,6 +5039,8 @@ function makeChartExStyleXml() {
5522
5039
  }
5523
5040
  //#endregion
5524
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/";
5525
5044
  function relationship$2(id, type, target) {
5526
5045
  return voidEl("Relationship", {
5527
5046
  Id: id,
@@ -5759,8 +5278,8 @@ function buildChartRelsXml(embeddingTarget) {
5759
5278
  function buildChartExRelsXml(embeddingTarget, colorsTarget, styleTarget) {
5760
5279
  return XML_DECL + relationships([
5761
5280
  relationship$2("rId1", OFFICE_REL + "package", embeddingTarget),
5762
- relationship$2("rId2", "http://schemas.microsoft.com/office/2011/relationships/chartColorStyle", colorsTarget),
5763
- 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)
5764
5283
  ]);
5765
5284
  }
5766
5285
  /**
@@ -6230,6 +5749,7 @@ function buildAnimationSeq(animations, next) {
6230
5749
  fill: "hold"
6231
5750
  }, [raw(el("p:stCondLst", null, raw(voidEl("p:cond", { delay: "indefinite" })))), raw(el("p:childTnLst", null, raw(subs)))])));
6232
5751
  };
5752
+ const clickGroups = groups.map(emitGroup).join("");
6233
5753
  return el("p:seq", {
6234
5754
  concurrent: 1,
6235
5755
  nextAc: "seek"
@@ -6238,7 +5758,7 @@ function buildAnimationSeq(animations, next) {
6238
5758
  id: 2,
6239
5759
  dur: "indefinite",
6240
5760
  nodeType: "mainSeq"
6241
- }, raw(el("p:childTnLst", null, raw(groups.map(emitGroup).join("")))))),
5761
+ }, raw(el("p:childTnLst", null, raw(clickGroups))))),
6242
5762
  raw(el("p:prevCondLst", null, raw(el("p:cond", {
6243
5763
  evt: "onPrev",
6244
5764
  delay: 0
@@ -6286,7 +5806,8 @@ function slideTimingToXml(slide) {
6286
5806
  const mediaNode = (obj, nodeId) => {
6287
5807
  const spid = slide._slideObjects.indexOf(obj) + 2;
6288
5808
  const repeatCount = obj.loop === true ? "indefinite" : String(Math.round(obj.loopCount * 1e3));
6289
- 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", {
6290
5811
  id: nodeId,
6291
5812
  repeatCount,
6292
5813
  fill: "hold",
@@ -6296,12 +5817,13 @@ function slideTimingToXml(slide) {
6296
5817
  if (animations.length === 0) {
6297
5818
  if (loopMedia.length === 0) return "";
6298
5819
  let nodeId = 1;
5820
+ const mediaNodes = loopMedia.map((obj) => mediaNode(obj, nodeId += 1)).join("");
6299
5821
  return el("p:timing", null, raw(el("p:tnLst", null, raw(el("p:par", null, raw(el("p:cTn", {
6300
5822
  id: 1,
6301
5823
  dur: "indefinite",
6302
5824
  restart: "never",
6303
5825
  nodeType: "tmRoot"
6304
- }, raw(el("p:childTnLst", null, raw(loopMedia.map((obj) => mediaNode(obj, nodeId += 1)).join("")))))))))));
5826
+ }, raw(el("p:childTnLst", null, raw(mediaNodes))))))))));
6305
5827
  }
6306
5828
  let id = 2;
6307
5829
  const next = () => id += 1;
@@ -6370,40 +5892,6 @@ function slideTransitionToXml(slide) {
6370
5892
  }, [raw(typeEl), raw(sndAc)])))), raw(el("mc:Fallback", null, raw(el("p:transition", baseAttrs, [raw(typeEl), raw(sndAc)]))))]);
6371
5893
  }
6372
5894
  //#endregion
6373
- //#region src/gen/drawingml/clamp.ts
6374
- /**
6375
- * ts-pptx: DrawingML value clamps
6376
- *
6377
- * Clamp font/character/line spacing values (points) into their ST_* schema
6378
- * ranges and convert to the hundredths-of-a-point units the attributes expect.
6379
- * Out-of-range values make PowerPoint report the package as needing repair.
6380
- */
6381
- /**
6382
- * Clamp a font size (points) into ST_TextFontSize (1-4000pt) and return it in
6383
- * hundredths of a point for the `sz` attribute. Out-of-range sizes make
6384
- * PowerPoint report the package as needing repair (e.g. `sz` > 400000 or < 100).
6385
- */
6386
- function clampFontSizeSz(fontSizePts) {
6387
- const raw = ptToHundredths(fontSizePts);
6388
- const clamped = Math.min(4e5, Math.max(100, raw));
6389
- if (clamped !== raw) warnOnce("font/size-out-of-range", `fontSize ${fontSizePts} is outside the valid range 1-4000pt; using ${clamped / 100}.`);
6390
- return clamped;
6391
- }
6392
- /** Clamp character spacing (points) into ST_TextPoint (-4000..4000pt); returns hundredths for the `spc` attribute. */
6393
- function clampCharSpacingSpc(charSpacingPts) {
6394
- const raw = ptToHundredths(charSpacingPts);
6395
- const clamped = Math.min(4e5, Math.max(-4e5, raw));
6396
- if (clamped !== raw) warnOnce("text/char-spacing-out-of-range", `charSpacing ${charSpacingPts} is outside the valid range -4000..4000pt; using ${clamped / 100}.`);
6397
- return clamped;
6398
- }
6399
- /** Clamp line spacing (points) into ST_TextSpacingPoint (0..1584pt); returns hundredths for `<a:spcPts val>`. */
6400
- function clampLineSpacingPts(lineSpacingPts) {
6401
- const raw = ptToHundredths(lineSpacingPts);
6402
- const clamped = Math.min(158400, Math.max(0, raw));
6403
- if (clamped !== raw) warnOnce("text/line-spacing-out-of-range", `lineSpacing ${lineSpacingPts} is outside the valid range 0-1584pt; using ${clamped / 100}.`);
6404
- return clamped;
6405
- }
6406
- //#endregion
6407
5895
  //#region src/gen/drawingml/locks.ts
6408
5896
  const SHAPE_LOCK_ATTRS = [
6409
5897
  "noGrp",
@@ -6539,396 +6027,6 @@ function genXmlShapeLine(ln) {
6539
6027
  ]);
6540
6028
  }
6541
6029
  //#endregion
6542
- //#region src/gen/drawingml/math.ts
6543
- /**
6544
- * ts-pptx: DrawingML native equations (OMML)
6545
- *
6546
- * Wrap raw OMML markup into the `<a14:m>` marker PowerPoint uses for editable
6547
- * equations — as a standalone display paragraph (`genXmlMathParagraph`) or as an
6548
- * inline run that flows between the surrounding `<a:r>` runs (`genXmlInlineMath`).
6549
- */
6550
- const M_NS = "http://schemas.openxmlformats.org/officeDocument/2006/math";
6551
- const A14_NS = "http://schemas.microsoft.com/office/drawing/2010/main";
6552
- /** The `<a14:m>` marker, carrying both namespace declarations so the supplied OMML needs none. */
6553
- function a14Math(mathXml) {
6554
- return el("a14:m", {
6555
- "xmlns:a14": A14_NS,
6556
- "xmlns:m": M_NS
6557
- }, raw(mathXml));
6558
- }
6559
- /**
6560
- * Build a native-equation paragraph (`<a:p>`) from raw OMML.
6561
- *
6562
- * PowerPoint stores an editable equation inside a text body as an `<a14:m>` marker wrapping
6563
- * `<m:oMathPara><m:oMath>…`. We declare both the `a14` (drawing-2010) and `m` (math) namespaces
6564
- * on the `<a14:m>` element so the supplied OMML needs no namespace declarations of its own, then
6565
- * accept three input shapes: a full `<m:oMathPara>`, a full `<m:oMath>`, or the inner OMML
6566
- * (children of `<m:oMath>`). A trailing `<a:endParaRPr>` matches what PowerPoint authors.
6567
- *
6568
- * @param {string} omml - raw OMML markup for the equation
6569
- * @returns {string} an `<a:p>` math paragraph
6570
- */
6571
- function genXmlMathParagraph(omml) {
6572
- const trimmed = (omml || "").trim();
6573
- const paraPr = el("m:oMathParaPr", null, raw(voidEl("m:jc", { "m:val": "centerGroup" })));
6574
- 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));
6575
- }
6576
- /**
6577
- * Build an INLINE native-equation run (dn-inline-math) from raw OMML.
6578
- *
6579
- * Unlike the display form ({@link genXmlMathParagraph}), an inline equation is *not* its own
6580
- * paragraph: PowerPoint authors an `<a14:m>` marker wrapping a bare `<m:oMath>` (no `<m:oMathPara>`,
6581
- * no `<m:oMathParaPr>`/`<m:jc>`) that flows between the surrounding `<a:r>` runs in one `<a:p>` —
6582
- * pinned by the `math-omml-inline.pptx` oracle. The `mc:AlternateContent` envelope stays at the
6583
- * shape level (see `objectHasMath`), so nothing wraps the run itself. We declare the `a14` and `m`
6584
- * namespaces on the `<a14:m>` element so the supplied OMML needs none of its own, and accept the
6585
- * same three input shapes as the display helper: a full `<m:oMathPara>` (its inner `<m:oMath>` is
6586
- * unwrapped, since a paragraph block cannot flow inline), a full `<m:oMath>`, or the inner OMML.
6587
- *
6588
- * @param {string} omml - raw OMML markup for the equation
6589
- * @returns {string} an `<a14:m>` inline equation run
6590
- */
6591
- function genXmlInlineMath(omml) {
6592
- const trimmed = (omml || "").trim();
6593
- const oMathMatch = trimmed.match(/<m:oMath[\s>][\s\S]*<\/m:oMath>/);
6594
- return a14Math(oMathMatch ? oMathMatch[0] : el("m:oMath", null, raw(trimmed)));
6595
- }
6596
- //#endregion
6597
- //#region src/gen/drawingml/text-run.ts
6598
- /** The 2018 hyperlink-color extension namespace, written on the `<ahyp:hlinkClr>` element itself. */
6599
- const AHYP_NS = "http://schemas.microsoft.com/office/drawing/2018/hyperlinkcolor";
6600
- /**
6601
- * `<a:buChar>` cannot go through the element builder. Its `char` attribute carries a *pre-escaped*
6602
- * numeric character reference (`&#x2022;` — see `BulletType`), and the builder escapes every
6603
- * attribute value, which would emit `&amp;#x2022;` and render that text literally as the bullet.
6604
- * There is no raw-attribute escape hatch, so this one stays a template.
6605
- */
6606
- function buChar(char) {
6607
- return `<a:buChar char="${char}"/>`;
6608
- }
6609
- /**
6610
- * `<a:extLst>` marking a hyperlink whose color should follow the text color rather than the
6611
- * theme's hyperlink color. The leading spaces are byte-significant: this block was authored as
6612
- * indented string concatenation and that indentation reaches the file, so it is described here
6613
- * with `openPrefix`/`closePrefix` rather than being tidied away.
6614
- */
6615
- const HLINK_TEXT_COLOR_EXT = el("a:extLst", null, raw(el("a:ext", { uri: "{A12FA001-AC4F-418D-AE19-62706E023703}" }, raw(voidEl("ahyp:hlinkClr", {
6616
- "xmlns:ahyp": AHYP_NS,
6617
- val: "tx"
6618
- }, { openPrefix: " " })), {
6619
- openPrefix: " ",
6620
- closePrefix: " "
6621
- })), {
6622
- openPrefix: " ",
6623
- closePrefix: " "
6624
- });
6625
- /**
6626
- * Generate XML Paragraph Properties
6627
- * @param {SlideObject|TextProps} textObj - text object
6628
- * @param {boolean} isDefault - array of default relations
6629
- * @return {string} XML
6630
- */
6631
- function genXmlParagraphProperties(textObj, isDefault) {
6632
- const opts = textObj.options ?? {};
6633
- let strXmlBullet = "";
6634
- let strXmlBulletColor = "";
6635
- let strXmlLnSpc = "";
6636
- let strXmlParaSpc = "";
6637
- let strXmlTabStops = "";
6638
- const tag = isDefault ? "a:lvl1pPr" : "a:pPr";
6639
- let bulletMarL = valToPts(27);
6640
- let paragraphPropXml = `<${tag}${opts.rtlMode ? " rtl=\"1\" " : ""}`;
6641
- if (opts.align) switch (opts.align) {
6642
- case "left":
6643
- paragraphPropXml += " algn=\"l\"";
6644
- break;
6645
- case "right":
6646
- paragraphPropXml += " algn=\"r\"";
6647
- break;
6648
- case "center":
6649
- paragraphPropXml += " algn=\"ctr\"";
6650
- break;
6651
- case "justify":
6652
- paragraphPropXml += " algn=\"just\"";
6653
- break;
6654
- default: paragraphPropXml += "";
6655
- }
6656
- if (opts.lineSpacing) strXmlLnSpc = el("a:lnSpc", null, raw(voidEl("a:spcPts", { val: clampLineSpacingPts(opts.lineSpacing) })));
6657
- else if (opts.lineSpacingMultiple) strXmlLnSpc = el("a:lnSpc", null, raw(voidEl("a:spcPct", { val: Math.round(opts.lineSpacingMultiple * PERCENT_SCALE) })));
6658
- if (opts.indentLevel && !isNaN(Number(opts.indentLevel)) && opts.indentLevel > 0) paragraphPropXml += ` lvl="${opts.indentLevel}"`;
6659
- if (opts.paraSpaceBefore && !isNaN(Number(opts.paraSpaceBefore)) && opts.paraSpaceBefore > 0) strXmlParaSpc += el("a:spcBef", null, raw(voidEl("a:spcPts", { val: ptToHundredths(opts.paraSpaceBefore) })));
6660
- if (opts.paraSpaceAfter && !isNaN(Number(opts.paraSpaceAfter)) && opts.paraSpaceAfter > 0) strXmlParaSpc += el("a:spcAft", null, raw(voidEl("a:spcPts", { val: ptToHundredths(opts.paraSpaceAfter) })));
6661
- if (typeof opts.bullet === "object") {
6662
- const bulletImage = opts.bullet.image;
6663
- const isPictureBullet = !!(bulletImage && (bulletImage.path || bulletImage.data));
6664
- if (opts.bullet?.indent) bulletMarL = valToPts(opts.bullet.indent);
6665
- if (opts.bullet.color && !isPictureBullet) strXmlBulletColor = el("a:buClr", null, raw(createColorElement(opts.bullet.color)));
6666
- let bulletSizePct;
6667
- if (opts.bullet.size !== void 0) {
6668
- const bulletSize = Number(opts.bullet.size);
6669
- if (isNaN(bulletSize) || bulletSize < 25 || bulletSize > 400) warn("bullet/size-out-of-range", "`bullet.size` must be a percentage between 25 and 400!");
6670
- else bulletSizePct = Math.round(bulletSize * FIXED_PCT_PER_PERCENT);
6671
- }
6672
- const strXmlBulletSize = bulletSizePct === void 0 ? "" : voidEl("a:buSzPct", { val: bulletSizePct });
6673
- const strXmlBulletFont = opts.bullet.fontFace ? voidEl("a:buFont", { typeface: opts.bullet.fontFace }) : "";
6674
- const bulletIndentAttrs = () => ` marL="${opts.indentLevel && opts.indentLevel > 0 ? bulletMarL + bulletMarL * opts.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
6675
- if (isPictureBullet) {
6676
- paragraphPropXml += bulletIndentAttrs();
6677
- if (opts.bullet._rId) {
6678
- const svgExt = opts.bullet._rIdSvg ? el("a:extLst", null, raw(el("a:ext", { uri: "{96DAC541-7B7A-43D3-8B79-37D633B846F1}" }, raw(voidEl("asvg:svgBlip", {
6679
- "xmlns:asvg": "http://schemas.microsoft.com/office/drawing/2016/SVG/main",
6680
- "r:embed": `rId${opts.bullet._rIdSvg}`
6681
- }))))) : "";
6682
- 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}` })));
6683
- } else {
6684
- warn("bullet/image-embed-failed", "picture `bullet.image` could not be embedded; using a default bullet glyph");
6685
- strXmlBullet = strXmlBulletSize + strXmlBulletFont + buChar("&#x2022;");
6686
- }
6687
- } else if (opts.bullet.type && opts.bullet.type.toString().toLowerCase() === "number") {
6688
- paragraphPropXml += bulletIndentAttrs();
6689
- strXmlBullet = strXmlBulletSize + (strXmlBulletFont || voidEl("a:buFont", { typeface: "+mj-lt" })) + voidEl("a:buAutoNum", {
6690
- type: opts.bullet.numberType || "arabicPeriod",
6691
- startAt: opts.bullet.numberStartAt || "1"
6692
- });
6693
- } else if (opts.bullet.characterCode) {
6694
- let bulletCode = `&#x${opts.bullet.characterCode};`;
6695
- if (!/^[0-9A-Fa-f]{4}$/.test(opts.bullet.characterCode)) {
6696
- warn("bullet/invalid-character-code", "`bullet.characterCode` should be a 4-digit unicode character (ex: 22AB)!");
6697
- bulletCode = "&#x2022;";
6698
- }
6699
- paragraphPropXml += bulletIndentAttrs();
6700
- strXmlBullet = strXmlBulletSize + strXmlBulletFont + buChar(bulletCode);
6701
- } else {
6702
- paragraphPropXml += bulletIndentAttrs();
6703
- strXmlBullet = strXmlBulletSize + strXmlBulletFont + buChar("&#x2022;");
6704
- }
6705
- } else if (opts.bullet) {
6706
- paragraphPropXml += ` marL="${opts.indentLevel && opts.indentLevel > 0 ? bulletMarL + bulletMarL * opts.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
6707
- strXmlBullet = buChar("&#x2022;");
6708
- } else if (!opts.bullet) {
6709
- paragraphPropXml += " indent=\"0\" marL=\"0\"";
6710
- strXmlBullet = voidEl("a:buNone");
6711
- }
6712
- if (opts.tabStops && Array.isArray(opts.tabStops)) strXmlTabStops = el("a:tabLst", null, raw(opts.tabStops.map((stop) => voidEl("a:tab", {
6713
- pos: inch2Emu(stop.position || 1),
6714
- algn: stop.alignment || "l"
6715
- })).join("")));
6716
- paragraphPropXml += ">" + strXmlLnSpc + strXmlParaSpc + strXmlBulletColor + strXmlBullet + strXmlTabStops;
6717
- if (isDefault) paragraphPropXml += genXmlTextRunProperties(opts, true);
6718
- paragraphPropXml += "</" + tag + ">";
6719
- return paragraphPropXml;
6720
- }
6721
- /**
6722
- * Generate XML Text Run Properties (`a:rPr`)
6723
- * @param {ObjectOptions|TextPropsOptions} opts - text options
6724
- * @param {boolean} isDefault - whether these are the default text run properties
6725
- * @return {string} XML
6726
- */
6727
- function genXmlTextRunProperties(opts, isDefault) {
6728
- let runProps = "";
6729
- const runPropsTag = isDefault ? "a:defRPr" : "a:rPr";
6730
- const underline = typeof opts.underline === "object" && opts.underline?.style ? opts.underline.style : opts.hyperlink ? "sng" : null;
6731
- const baseline = opts.baseline ? Math.round(opts.baseline * 50) : opts.subscript ? -4e4 : opts.superscript ? 3e4 : null;
6732
- const attrs = {
6733
- lang: opts.lang ? opts.lang : "en-US",
6734
- altLang: opts.lang ? "en-US" : null,
6735
- sz: opts.fontSize ? clampFontSizeSz(opts.fontSize) : null,
6736
- b: opts?.bold ? "1" : null,
6737
- i: opts?.italic ? "1" : null,
6738
- strike: opts?.strike ? typeof opts.strike === "string" ? opts.strike : "sngStrike" : null,
6739
- cap: opts?.caps ? opts.caps : null,
6740
- u: underline,
6741
- baseline,
6742
- spc: opts.charSpacing ? clampCharSpacingSpc(opts.charSpacing) : null,
6743
- kern: opts.charSpacing ? 0 : null,
6744
- dirty: "0"
6745
- };
6746
- const hasShadow = !!opts.shadow && opts.shadow.type !== "none";
6747
- if (opts.color || opts.fontFace || opts.outline || opts.glow || hasShadow || typeof opts.underline === "object" && opts.underline.color) {
6748
- if (opts.outline && typeof opts.outline === "object") runProps += el("a:ln", { w: lineWidthToEmu(opts.outline.size || .75) }, raw(genXmlColorSelection(opts.outline.color || "FFFFFF")));
6749
- if (opts.color) runProps += genXmlColorSelection({
6750
- color: opts.color,
6751
- transparency: opts.transparency
6752
- });
6753
- 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]);
6754
- if (opts.highlight) runProps += el("a:highlight", null, raw(createColorElement(opts.highlight)));
6755
- if (typeof opts.underline === "object" && opts.underline.color) runProps += el("a:uFill", null, raw(genXmlColorSelection(opts.underline.color)));
6756
- if (opts.fontFace) {
6757
- runProps += voidEl("a:latin", {
6758
- typeface: opts.fontFace,
6759
- pitchFamily: "34",
6760
- charset: "0"
6761
- });
6762
- if (opts.fontFaceEA) runProps += voidEl("a:ea", { typeface: opts.fontFaceEA });
6763
- runProps += voidEl("a:cs", { typeface: opts.fontFace });
6764
- }
6765
- }
6766
- if (opts.hyperlink) {
6767
- 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'}` ");
6768
- 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`");
6769
- else if (opts.hyperlink.url || opts.hyperlink.slide) {
6770
- const linkAttrs = opts.hyperlink.url ? {
6771
- "r:id": `rId${opts.hyperlink._rId}`,
6772
- invalidUrl: "",
6773
- action: "",
6774
- tgtFrame: "",
6775
- tooltip: opts.hyperlink.tooltip ?? "",
6776
- history: "1",
6777
- highlightClick: "0",
6778
- endSnd: "0"
6779
- } : {
6780
- "r:id": `rId${opts.hyperlink._rId}`,
6781
- action: "ppaction://hlinksldjump",
6782
- tooltip: opts.hyperlink.tooltip ?? ""
6783
- };
6784
- runProps += opts.color ? el("a:hlinkClick", linkAttrs, raw(HLINK_TEXT_COLOR_EXT)) : voidEl("a:hlinkClick", linkAttrs);
6785
- }
6786
- }
6787
- return el(runPropsTag, attrs, raw(runProps));
6788
- }
6789
- /**
6790
- * Build textBody text runs [`<a:r></a:r>`] for paragraphs [`<a:p>`]
6791
- * @param {TextProps} textObj - Text object
6792
- * @return {string} XML string
6793
- */
6794
- function genXmlTextRun(textObj) {
6795
- if (textObj.text === void 0 || textObj.text === null) return "";
6796
- return el("a:r", null, [raw(genXmlTextRunProperties(textObj.options ?? {}, false)), raw(el("a:t", null, String(textObj.text)))]);
6797
- }
6798
- /**
6799
- * Builds `<a:normAutofit>` with explicit fontScale/lnSpcReduction for "shrink text on overflow"
6800
- * @param {TextFitShrinkProps} fit - shrink fit options
6801
- * @return {string} XML string (`<a:normAutofit .../>`)
6802
- * @see ECMA-376 CT_TextNormAutofit (attributes in 1000ths of a percent)
6803
- */
6804
- function genXmlNormAutofit(fit) {
6805
- const pct = (val, name) => {
6806
- if (val === void 0 || val === null) return null;
6807
- if (typeof val !== "number" || isNaN(val) || val < 0 || val > 100) {
6808
- warn("text/invalid-fit-percentage", `fit.${name} must be a number between 0 and 100 (percent); received ${String(val)} - attribute ignored.`);
6809
- return null;
6810
- }
6811
- return Math.round(val * FIXED_PCT_PER_PERCENT);
6812
- };
6813
- return voidEl("a:normAutofit", {
6814
- fontScale: pct(fit.fontScale, "fontScale"),
6815
- lnSpcReduction: pct(fit.lnSpcReduction, "lnSpcReduction")
6816
- });
6817
- }
6818
- /**
6819
- * Group the flat run list into paragraphs (lines): a new line starts on a display-math run,
6820
- * an alignment change, or a bullet, and closes after a breakLine. Returns the per-line run arrays.
6821
- */
6822
- function groupRunsIntoLines(arrTextObjects, opts) {
6823
- const arrLines = [];
6824
- let arrTexts = [];
6825
- arrTextObjects.forEach((textObj, idx) => {
6826
- if (textObj.math && !textObj.inline) {
6827
- if (arrTexts.length > 0) {
6828
- arrLines.push(arrTexts);
6829
- arrTexts = [];
6830
- }
6831
- arrLines.push([textObj]);
6832
- return;
6833
- }
6834
- if (arrTexts.length > 0 && (textObj.options.align || opts.align)) {
6835
- if (textObj.options.align !== arrTextObjects[idx - 1]?.options.align) {
6836
- arrLines.push(arrTexts);
6837
- arrTexts = [];
6838
- }
6839
- } else if (arrTexts.length > 0 && textObj.options.bullet) {
6840
- arrLines.push(arrTexts);
6841
- arrTexts = [];
6842
- textObj.options.breakLine = false;
6843
- }
6844
- arrTexts.push(textObj);
6845
- if (arrTexts.length > 0 && textObj.options.breakLine) {
6846
- if (idx + 1 < arrTextObjects.length) {
6847
- arrLines.push(arrTexts);
6848
- arrTexts = [];
6849
- }
6850
- }
6851
- if (idx + 1 === arrTextObjects.length) arrLines.push(arrTexts);
6852
- });
6853
- return arrLines;
6854
- }
6855
- /**
6856
- * Render each grouped line to an `<a:p>` paragraph: paragraph props, inherited run options,
6857
- * text runs (and inline/display math), and the closing endParaRPr.
6858
- */
6859
- function renderTextParagraphsXml(arrLines, slideObj, opts) {
6860
- let strSlideXml = "";
6861
- arrLines.forEach((line) => {
6862
- const firstRun = line[0];
6863
- if (line.length === 1 && firstRun?.math && !firstRun.inline) {
6864
- strSlideXml += genXmlMathParagraph(firstRun.math);
6865
- return;
6866
- }
6867
- let reqsClosingFontSize = false;
6868
- let paraXml = "";
6869
- let paragraphPropXml = `<a:pPr ${line[0]?.options?.rtlMode ? " rtl=\"1\" " : ""}`;
6870
- let paragraphPropEmitted = false;
6871
- line.forEach((textObj, idx) => {
6872
- textObj.options._lineIdx = idx;
6873
- if (idx > 0 && textObj.options.softBreakBefore) paraXml += voidEl("a:br");
6874
- textObj.options.align = textObj.options.align || opts.align;
6875
- textObj.options.lineSpacing = textObj.options.lineSpacing || opts.lineSpacing;
6876
- textObj.options.lineSpacingMultiple = textObj.options.lineSpacingMultiple || opts.lineSpacingMultiple;
6877
- textObj.options.indentLevel = textObj.options.indentLevel || opts.indentLevel;
6878
- textObj.options.paraSpaceBefore = textObj.options.paraSpaceBefore || opts.paraSpaceBefore;
6879
- textObj.options.paraSpaceAfter = textObj.options.paraSpaceAfter || opts.paraSpaceAfter;
6880
- if (!paragraphPropEmitted) {
6881
- paragraphPropXml = genXmlParagraphProperties(textObj, false);
6882
- const cleaned = paragraphPropXml.replace("<a:pPr></a:pPr>", "");
6883
- if (cleaned) {
6884
- paraXml += cleaned;
6885
- paragraphPropEmitted = true;
6886
- }
6887
- }
6888
- const textOptions = textObj.options;
6889
- Object.entries(opts).filter(([key]) => !(textObj.options.hyperlink && key === "color")).forEach(([key, val]) => {
6890
- if (key !== "bullet" && !textOptions[key]) textOptions[key] = val;
6891
- });
6892
- let _textRunObj = textObj;
6893
- if (idx === 0 && line[0]?.options.bullet && typeof textObj.text === "string") {
6894
- const _stripped = textObj.text.replace(/^[\u2022\u25E6\u25AA\u25AB\u25CF\u25CB\u2023\u2043\u2219]\s*/, "");
6895
- if (_stripped !== textObj.text) _textRunObj = {
6896
- text: _stripped,
6897
- options: textObj.options
6898
- };
6899
- }
6900
- const isEmptyBreakArtifact = _textRunObj.text === "" && line.length > 1;
6901
- if (_textRunObj.math) paraXml += genXmlInlineMath(_textRunObj.math);
6902
- else if (!isEmptyBreakArtifact) paraXml += genXmlTextRun(_textRunObj);
6903
- if (!textObj.text && opts.fontSize || textObj.options.fontSize) {
6904
- reqsClosingFontSize = true;
6905
- opts.fontSize = opts.fontSize || textObj.options.fontSize;
6906
- }
6907
- });
6908
- const sizedAttrs = () => ({
6909
- lang: opts.lang || "en-US",
6910
- sz: opts.fontSize ? clampFontSizeSz(opts.fontSize) : null,
6911
- dirty: "0"
6912
- });
6913
- if (slideObj._type === "tablecell" && (opts.fontSize || opts.fontFace)) if (opts.fontFace) paraXml += el("a:endParaRPr", sizedAttrs(), [
6914
- raw(voidEl("a:latin", {
6915
- typeface: opts.fontFace,
6916
- charset: "0"
6917
- })),
6918
- opts.fontFaceEA ? raw(voidEl("a:ea", { typeface: opts.fontFaceEA })) : null,
6919
- raw(voidEl("a:cs", { typeface: opts.fontFace }))
6920
- ]);
6921
- else paraXml += voidEl("a:endParaRPr", sizedAttrs());
6922
- else if (reqsClosingFontSize) paraXml += voidEl("a:endParaRPr", sizedAttrs());
6923
- else paraXml += voidEl("a:endParaRPr", {
6924
- lang: opts.lang || "en-US",
6925
- dirty: "0"
6926
- });
6927
- strSlideXml += el("a:p", null, raw(paraXml));
6928
- });
6929
- return strSlideXml;
6930
- }
6931
- //#endregion
6932
6030
  //#region src/gen/drawingml/text-body.ts
6933
6031
  /**
6934
6032
  * ts-pptx: DrawingML text body
@@ -7434,29 +6532,32 @@ function genXmlCustGeom(options, cx, cy, layout) {
7434
6532
  if (cxnEls.length) cxnLst = el("a:cxnLst", null, cxnEls.map(raw));
7435
6533
  }
7436
6534
  let ahLst = voidEl("a:ahLst", null, SPACE_BEFORE_SLASH);
7437
- if (options.adjustHandles?.length) ahLst = el("a:ahLst", null, options.adjustHandles.map((h) => {
7438
- const pos = raw(voidEl("a:pos", {
7439
- x: adjCoord(h.x, "X"),
7440
- y: adjCoord(h.y, "Y")
7441
- }));
7442
- if ("gdRefR" in h || "minR" in h || "maxR" in h || "gdRefAng" in h || "minAng" in h || "maxAng" in h) return el("a:ahPolar", {
7443
- gdRefR: h.gdRefR,
7444
- minR: adjCoord(h.minR, "X"),
7445
- maxR: adjCoord(h.maxR, "X"),
7446
- gdRefAng: h.gdRefAng,
7447
- minAng: h.minAng == null ? void 0 : convertRotationDegrees(h.minAng),
7448
- maxAng: h.maxAng == null ? void 0 : convertRotationDegrees(h.maxAng)
7449
- }, pos);
7450
- const xy = h;
7451
- return el("a:ahXY", {
7452
- gdRefX: xy.gdRefX,
7453
- minX: adjCoord(xy.minX, "X"),
7454
- maxX: adjCoord(xy.maxX, "X"),
7455
- gdRefY: xy.gdRefY,
7456
- minY: adjCoord(xy.minY, "Y"),
7457
- maxY: adjCoord(xy.maxY, "Y")
7458
- }, pos);
7459
- }).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
+ }
7460
6561
  return el("a:custGeom", null, [
7461
6562
  voidEl("a:avLst", null, SPACE_BEFORE_SLASH),
7462
6563
  gdLst,
@@ -7520,11 +6621,15 @@ const ImageSizingXml = {
7520
6621
  ].filter(Boolean).join(", ");
7521
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.`);
7522
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));
7523
6628
  return voidEl("a:srcRect", {
7524
- l: Math.round(1e5 * (l / imgSize.w)),
7525
- r: Math.round(1e5 * (r / imgSize.w)),
7526
- t: Math.round(1e5 * (t / imgSize.h)),
7527
- b: Math.round(1e5 * (b / imgSize.h))
6629
+ l: lPerc,
6630
+ r: rPerc,
6631
+ t: tPerc,
6632
+ b: bPerc
7528
6633
  }) + STRETCH;
7529
6634
  }
7530
6635
  };
@@ -8886,7 +7991,11 @@ function slideObjectToXml(slide) {
8886
7991
  strSlideXml += "</p:grpSp>";
8887
7992
  break;
8888
7993
  }
8889
- default: strSlideXml += "";
7994
+ case "notes": break;
7995
+ case "tablecell": break;
7996
+ case "hyperlink": break;
7997
+ case "online": break;
7998
+ default: slideItemObj._type;
8890
7999
  }
8891
8000
  return strSlideXml;
8892
8001
  };
@@ -8981,17 +8090,19 @@ function slideObjectRelationsToXml(slide, defaultRels) {
8981
8090
  const hasTarget = (target) => rels.some((xml) => xml.includes(` Target="${target}"`));
8982
8091
  slide._rels.forEach((rel) => {
8983
8092
  lastRid = Math.max(lastRid, rel.rId);
8984
- if (isHyperlinkRel(rel)) if (rel.data === "slide") rels.push(voidEl("Relationship", {
8985
- Id: `rId${rel.rId}`,
8986
- Type: OFFICE_REL + "slide",
8987
- Target: `slide${rel.Target}.xml`
8988
- }));
8989
- else rels.push(voidEl("Relationship", {
8990
- Id: `rId${rel.rId}`,
8991
- Type: OFFICE_REL + "hyperlink",
8992
- Target: rel.Target,
8993
- TargetMode: "External"
8994
- }));
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
+ }
8995
8106
  });
8996
8107
  (slide._relsChart || []).forEach((rel) => {
8997
8108
  lastRid = Math.max(lastRid, rel.rId);
@@ -9102,527 +8213,6 @@ function getLayoutIdxForSlide(slides, slideLayouts, slideNumber) {
9102
8213
  return 1;
9103
8214
  }
9104
8215
  //#endregion
9105
- //#region src/gen/slide/notes.ts
9106
- /**
9107
- * Collect the speaker-notes runs for a slide (flattened across any number of `addNotes()` calls).
9108
- * @param {PresSlideInternal} slide - the slide object
9109
- * @return {TextProps[]} notes text runs in document order
9110
- */
9111
- function getNotesRuns(slide) {
9112
- const runs = [];
9113
- slide._slideObjects.forEach((obj) => {
9114
- if (obj._type === "notes" && obj.text) runs.push(...obj.text);
9115
- });
9116
- return runs;
9117
- }
9118
- /**
9119
- * Build (and cache) the hyperlink relationships for a slide's notes part (`notesSlideN.xml.rels`).
9120
- *
9121
- * Notes rels use their own namespace, independent of `slide._rels` (which serialize to
9122
- * `slideN.xml.rels`). The notes part always reserves rId1=notesMaster and rId2=slide, so
9123
- * dynamic hyperlink rels are allocated starting at rId3. Each notes hyperlink run is tagged
9124
- * with its `_rId` so the body serializer and the rels file agree.
9125
- *
9126
- * Idempotent: the result is cached on `slide._relsNotes` and reused by both callers.
9127
- * Only external `url` hyperlinks are supported; `slide` targets are ignored with a warning.
9128
- * @param {PresSlideInternal} slide - the slide object
9129
- * @return {SlideRel[]} notes hyperlink relationships
9130
- */
9131
- function buildNotesSlideRels(slide) {
9132
- if (slide._relsNotes) return slide._relsNotes;
9133
- const NOTES_REL_RESERVED = 2;
9134
- const rels = [];
9135
- let lastRid = NOTES_REL_RESERVED;
9136
- getNotesRuns(slide).forEach((run) => {
9137
- const hyperlink = run.options?.hyperlink;
9138
- if (!hyperlink) return;
9139
- if (!hyperlink.url) {
9140
- if (hyperlink.slide) warn("notes/hyperlink-slide-unsupported", "notes hyperlinks support `url` only (ignoring `slide` target)");
9141
- if (run.options) delete run.options.hyperlink;
9142
- return;
9143
- }
9144
- lastRid++;
9145
- hyperlink._rId = lastRid;
9146
- rels.push({
9147
- type: "hyperlink",
9148
- data: "dummy",
9149
- rId: lastRid,
9150
- Target: hyperlink.url
9151
- });
9152
- });
9153
- slide._relsNotes = rels;
9154
- return rels;
9155
- }
9156
- /**
9157
- * Build the `<p:txBody>` paragraphs for the notes placeholder.
9158
- * Runs are split into `<a:p>` paragraphs on newlines; each run is serialized with the standard
9159
- * text-run generator so inline formatting and `<a:hlinkClick>` markup are emitted consistently.
9160
- * @param {PresSlideInternal} slide - the slide object
9161
- * @return {string} XML string of `<a:p>` paragraphs
9162
- */
9163
- function genXmlNotesParagraphs(slide) {
9164
- const paragraphs = [[]];
9165
- getNotesRuns(slide).forEach((run) => {
9166
- String(run.text ?? "").split("\n").forEach((segment, idx) => {
9167
- if (idx > 0) paragraphs.push([]);
9168
- const text = segment.replace(/\r/g, "");
9169
- if (text !== "") paragraphs[paragraphs.length - 1]?.push({
9170
- text,
9171
- options: run.options || {}
9172
- });
9173
- });
9174
- });
9175
- return paragraphs.map((runs) => el("a:p", null, [raw(runs.map((run) => genXmlTextRun(run)).join("")), raw(voidEl("a:endParaRPr", {
9176
- lang: "en-US",
9177
- dirty: 0
9178
- }))])).join("");
9179
- }
9180
- /**
9181
- * Generate XML for Notes Master (notesMaster1.xml)
9182
- * @returns {string} XML
9183
- */
9184
- function makeXmlNotesMaster() {
9185
- const xfrmOffExt = (x, y, cx, cy) => el("a:xfrm", null, [raw(voidEl("a:off", {
9186
- x,
9187
- y
9188
- })), raw(voidEl("a:ext", {
9189
- cx,
9190
- cy
9191
- }))]);
9192
- const prstGeomRect = el("a:prstGeom", { prst: "rect" }, raw(voidEl("a:avLst")));
9193
- const spLocks = (attrs) => el("p:cNvSpPr", null, raw(voidEl("a:spLocks", attrs)));
9194
- const nvPr = (phAttrs) => el("p:nvPr", null, raw(voidEl("p:ph", phAttrs)));
9195
- const bodyPr = (attrs) => voidEl("a:bodyPr", attrs);
9196
- const lstStyleLvl1 = (algn) => el("a:lstStyle", null, raw(el("a:lvl1pPr", { algn }, raw(voidEl("a:defRPr", { sz: 1200 })))));
9197
- const emptyPara = el("a:p", null, raw(voidEl("a:endParaRPr", { lang: "en-US" })));
9198
- const header = el("p:sp", null, [
9199
- raw(el("p:nvSpPr", null, [
9200
- raw(voidEl("p:cNvPr", {
9201
- id: 2,
9202
- name: "Header Placeholder 1"
9203
- })),
9204
- raw(spLocks({ noGrp: 1 })),
9205
- raw(nvPr({
9206
- type: "hdr",
9207
- sz: "quarter"
9208
- }))
9209
- ])),
9210
- raw(el("p:spPr", null, [raw(xfrmOffExt(0, 0, 2971800, 458788)), raw(prstGeomRect)])),
9211
- raw(el("p:txBody", null, [
9212
- raw(bodyPr({
9213
- vert: "horz",
9214
- lIns: 91440,
9215
- tIns: 45720,
9216
- rIns: 91440,
9217
- bIns: 45720,
9218
- rtlCol: 0
9219
- })),
9220
- raw(lstStyleLvl1("l")),
9221
- raw(emptyPara)
9222
- ]))
9223
- ]);
9224
- const date = el("p:sp", null, [
9225
- raw(el("p:nvSpPr", null, [
9226
- raw(voidEl("p:cNvPr", {
9227
- id: 3,
9228
- name: "Date Placeholder 2"
9229
- })),
9230
- raw(spLocks({ noGrp: 1 })),
9231
- raw(nvPr({
9232
- type: "dt",
9233
- idx: 1
9234
- }))
9235
- ])),
9236
- raw(el("p:spPr", null, [raw(xfrmOffExt(3884613, 0, 2971800, 458788)), raw(prstGeomRect)])),
9237
- raw(el("p:txBody", null, [
9238
- raw(bodyPr({
9239
- vert: "horz",
9240
- lIns: 91440,
9241
- tIns: 45720,
9242
- rIns: 91440,
9243
- bIns: 45720,
9244
- rtlCol: 0
9245
- })),
9246
- raw(lstStyleLvl1("r")),
9247
- raw(el("a:p", null, [raw(el("a:fld", {
9248
- id: "{5282F153-3F37-0F45-9E97-73ACFA13230C}",
9249
- type: "datetimeFigureOut"
9250
- }, [raw(voidEl("a:rPr", { lang: "en-US" })), raw(el("a:t", null, "7/23/19"))])), raw(voidEl("a:endParaRPr", { lang: "en-US" }))]))
9251
- ]))
9252
- ]);
9253
- const slideImg = el("p:sp", null, [
9254
- raw(el("p:nvSpPr", null, [
9255
- raw(voidEl("p:cNvPr", {
9256
- id: 4,
9257
- name: "Slide Image Placeholder 3"
9258
- })),
9259
- raw(spLocks({
9260
- noGrp: 1,
9261
- noRot: 1,
9262
- noChangeAspect: 1
9263
- })),
9264
- raw(nvPr({
9265
- type: "sldImg",
9266
- idx: 2
9267
- }))
9268
- ])),
9269
- raw(el("p:spPr", null, [
9270
- raw(xfrmOffExt(685800, 1143e3, 5486400, 3086100)),
9271
- raw(prstGeomRect),
9272
- raw(voidEl("a:noFill")),
9273
- raw(el("a:ln", { w: 12700 }, raw(el("a:solidFill", null, raw(voidEl("a:prstClr", { val: "black" }))))))
9274
- ])),
9275
- raw(el("p:txBody", null, [
9276
- raw(bodyPr({
9277
- vert: "horz",
9278
- lIns: 91440,
9279
- tIns: 45720,
9280
- rIns: 91440,
9281
- bIns: 45720,
9282
- rtlCol: 0,
9283
- anchor: "ctr"
9284
- })),
9285
- raw(voidEl("a:lstStyle")),
9286
- raw(emptyPara)
9287
- ]))
9288
- ]);
9289
- const notesPlaceholder = el("p:sp", null, [
9290
- raw(el("p:nvSpPr", null, [
9291
- raw(voidEl("p:cNvPr", {
9292
- id: 5,
9293
- name: "Notes Placeholder 4"
9294
- })),
9295
- raw(spLocks({ noGrp: 1 })),
9296
- raw(nvPr({
9297
- type: "body",
9298
- sz: "quarter",
9299
- idx: 3
9300
- }))
9301
- ])),
9302
- raw(el("p:spPr", null, [raw(xfrmOffExt(685800, 4400550, 5486400, 3600450)), raw(prstGeomRect)])),
9303
- raw(el("p:txBody", null, [
9304
- raw(bodyPr({
9305
- vert: "horz",
9306
- lIns: 91440,
9307
- tIns: 45720,
9308
- rIns: 91440,
9309
- bIns: 45720,
9310
- rtlCol: 0
9311
- })),
9312
- raw(voidEl("a:lstStyle")),
9313
- raw([
9314
- "Click to edit Master text styles",
9315
- "Second level",
9316
- "Third level",
9317
- "Fourth level",
9318
- "Fifth level"
9319
- ].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(""))
9320
- ]))
9321
- ]);
9322
- const footer = el("p:sp", null, [
9323
- raw(el("p:nvSpPr", null, [
9324
- raw(voidEl("p:cNvPr", {
9325
- id: 6,
9326
- name: "Footer Placeholder 5"
9327
- })),
9328
- raw(spLocks({ noGrp: 1 })),
9329
- raw(nvPr({
9330
- type: "ftr",
9331
- sz: "quarter",
9332
- idx: 4
9333
- }))
9334
- ])),
9335
- raw(el("p:spPr", null, [raw(xfrmOffExt(0, 8685213, 2971800, 458787)), raw(prstGeomRect)])),
9336
- raw(el("p:txBody", null, [
9337
- raw(bodyPr({
9338
- vert: "horz",
9339
- lIns: 91440,
9340
- tIns: 45720,
9341
- rIns: 91440,
9342
- bIns: 45720,
9343
- rtlCol: 0,
9344
- anchor: "b"
9345
- })),
9346
- raw(lstStyleLvl1("l")),
9347
- raw(emptyPara)
9348
- ]))
9349
- ]);
9350
- const slideNum = el("p:sp", null, [
9351
- raw(el("p:nvSpPr", null, [
9352
- raw(voidEl("p:cNvPr", {
9353
- id: 7,
9354
- name: "Slide Number Placeholder 6"
9355
- })),
9356
- raw(spLocks({ noGrp: 1 })),
9357
- raw(nvPr({
9358
- type: "sldNum",
9359
- sz: "quarter",
9360
- idx: 5
9361
- }))
9362
- ])),
9363
- raw(el("p:spPr", null, [raw(xfrmOffExt(3884613, 8685213, 2971800, 458787)), raw(prstGeomRect)])),
9364
- raw(el("p:txBody", null, [
9365
- raw(bodyPr({
9366
- vert: "horz",
9367
- lIns: 91440,
9368
- tIns: 45720,
9369
- rIns: 91440,
9370
- bIns: 45720,
9371
- rtlCol: 0,
9372
- anchor: "b"
9373
- })),
9374
- raw(lstStyleLvl1("r")),
9375
- raw(el("a:p", null, [raw(el("a:fld", {
9376
- id: "{CE5E9CC1-C706-0F49-92D6-E571CC5EEA8F}",
9377
- type: "slidenum"
9378
- }, [raw(voidEl("a:rPr", { lang: "en-US" })), raw(el("a:t", null, "‹#›"))])), raw(voidEl("a:endParaRPr", { lang: "en-US" }))]))
9379
- ]))
9380
- ]);
9381
- const spTree = el("p:spTree", null, [
9382
- raw(el("p:nvGrpSpPr", null, [
9383
- raw(voidEl("p:cNvPr", {
9384
- id: 1,
9385
- name: ""
9386
- })),
9387
- raw(voidEl("p:cNvGrpSpPr")),
9388
- raw(voidEl("p:nvPr"))
9389
- ])),
9390
- raw(el("p:grpSpPr", null, raw(el("a:xfrm", null, [
9391
- raw(voidEl("a:off", {
9392
- x: 0,
9393
- y: 0
9394
- })),
9395
- raw(voidEl("a:ext", {
9396
- cx: 0,
9397
- cy: 0
9398
- })),
9399
- raw(voidEl("a:chOff", {
9400
- x: 0,
9401
- y: 0
9402
- })),
9403
- raw(voidEl("a:chExt", {
9404
- cx: 0,
9405
- cy: 0
9406
- }))
9407
- ])))),
9408
- raw(header),
9409
- raw(date),
9410
- raw(slideImg),
9411
- raw(notesPlaceholder),
9412
- raw(footer),
9413
- raw(slideNum)
9414
- ]);
9415
- const extLst = el("p:extLst", null, raw(el("p:ext", { uri: "{BB962C8B-B14F-4D97-AF65-F5344CB8AC3E}" }, raw(voidEl("p14:creationId", {
9416
- "xmlns:p14": "http://schemas.microsoft.com/office/powerpoint/2010/main",
9417
- val: 1024086991
9418
- })))));
9419
- const cSld = el("p:cSld", null, [
9420
- raw(el("p:bg", null, raw(el("p:bgRef", { idx: 1001 }, raw(voidEl("a:schemeClr", { val: "bg1" })))))),
9421
- raw(spTree),
9422
- raw(extLst)
9423
- ]);
9424
- const clrMap = voidEl("p:clrMap", {
9425
- bg1: "lt1",
9426
- tx1: "dk1",
9427
- bg2: "lt2",
9428
- tx2: "dk2",
9429
- accent1: "accent1",
9430
- accent2: "accent2",
9431
- accent3: "accent3",
9432
- accent4: "accent4",
9433
- accent5: "accent5",
9434
- accent6: "accent6",
9435
- hlink: "hlink",
9436
- folHlink: "folHlink"
9437
- });
9438
- const notesStyleLevel = (n, marL) => el(`a:lvl${n}pPr`, {
9439
- marL,
9440
- algn: "l",
9441
- defTabSz: 914400,
9442
- rtl: 0,
9443
- eaLnBrk: 1,
9444
- latinLnBrk: 0,
9445
- hangingPunct: 1
9446
- }, raw(el("a:defRPr", {
9447
- sz: 1200,
9448
- kern: 1200
9449
- }, [
9450
- raw(el("a:solidFill", null, raw(voidEl("a:schemeClr", { val: "tx1" })))),
9451
- raw(voidEl("a:latin", { typeface: "+mn-lt" })),
9452
- raw(voidEl("a:ea", { typeface: "+mn-ea" })),
9453
- raw(voidEl("a:cs", { typeface: "+mn-cs" }))
9454
- ])));
9455
- const notesStyle = el("p:notesStyle", null, raw([
9456
- 0,
9457
- 457200,
9458
- 914400,
9459
- 1371600,
9460
- 1828800,
9461
- 2286e3,
9462
- 2743200,
9463
- 3200400,
9464
- 3657600
9465
- ].map((marL, i) => notesStyleLevel(i + 1, marL)).join("")));
9466
- return XML_DECL + "\r\n" + el("p:notesMaster", {
9467
- "xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
9468
- "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
9469
- "xmlns:p": "http://schemas.openxmlformats.org/presentationml/2006/main"
9470
- }, [
9471
- raw(cSld),
9472
- raw(clrMap),
9473
- raw(notesStyle)
9474
- ]);
9475
- }
9476
- /**
9477
- * Creates Notes Slide (`ppt/notesSlides/notesSlide1.xml`)
9478
- * @param {PresSlideInternal} slide - the slide object to transform into XML
9479
- * @return {string} XML
9480
- */
9481
- function makeXmlNotesSlide(slide) {
9482
- buildNotesSlideRels(slide);
9483
- const slideImgPh = el("p:sp", null, [raw(el("p:nvSpPr", null, [
9484
- raw(voidEl("p:cNvPr", {
9485
- id: 2,
9486
- name: "Slide Image Placeholder 1"
9487
- })),
9488
- raw(el("p:cNvSpPr", null, raw(voidEl("a:spLocks", {
9489
- noGrp: 1,
9490
- noRot: 1,
9491
- noChangeAspect: 1
9492
- })))),
9493
- raw(el("p:nvPr", null, raw(voidEl("p:ph", { type: "sldImg" }))))
9494
- ])), raw(voidEl("p:spPr"))]);
9495
- const notesPh = el("p:sp", null, [
9496
- raw(el("p:nvSpPr", null, [
9497
- raw(voidEl("p:cNvPr", {
9498
- id: 3,
9499
- name: "Notes Placeholder 2"
9500
- })),
9501
- raw(el("p:cNvSpPr", null, raw(voidEl("a:spLocks", { noGrp: 1 })))),
9502
- raw(el("p:nvPr", null, raw(voidEl("p:ph", {
9503
- type: "body",
9504
- idx: 1
9505
- }))))
9506
- ])),
9507
- raw(voidEl("p:spPr")),
9508
- raw(el("p:txBody", null, [
9509
- raw(voidEl("a:bodyPr")),
9510
- raw(voidEl("a:lstStyle")),
9511
- raw(genXmlNotesParagraphs(slide))
9512
- ]))
9513
- ]);
9514
- const slideNumPh = el("p:sp", null, [
9515
- raw(el("p:nvSpPr", null, [
9516
- raw(voidEl("p:cNvPr", {
9517
- id: 4,
9518
- name: "Slide Number Placeholder 3"
9519
- })),
9520
- raw(el("p:cNvSpPr", null, raw(voidEl("a:spLocks", { noGrp: 1 })))),
9521
- raw(el("p:nvPr", null, raw(voidEl("p:ph", {
9522
- type: "sldNum",
9523
- sz: "quarter",
9524
- idx: 10
9525
- }))))
9526
- ])),
9527
- raw(voidEl("p:spPr")),
9528
- raw(el("p:txBody", null, [
9529
- raw(voidEl("a:bodyPr")),
9530
- raw(voidEl("a:lstStyle")),
9531
- raw(el("a:p", null, [raw(el("a:fld", {
9532
- id: SLDNUMFLDID,
9533
- type: "slidenum"
9534
- }, [raw(voidEl("a:rPr", { lang: "en-US" })), raw(el("a:t", null, slide._slideNum))])), raw(voidEl("a:endParaRPr", { lang: "en-US" }))]))
9535
- ]))
9536
- ]);
9537
- const spTree = el("p:spTree", null, [
9538
- raw(el("p:nvGrpSpPr", null, [
9539
- raw(voidEl("p:cNvPr", {
9540
- id: 1,
9541
- name: ""
9542
- })),
9543
- raw(voidEl("p:cNvGrpSpPr")),
9544
- raw(voidEl("p:nvPr"))
9545
- ])),
9546
- raw(el("p:grpSpPr", null, raw(el("a:xfrm", null, [
9547
- raw(voidEl("a:off", {
9548
- x: 0,
9549
- y: 0
9550
- })),
9551
- raw(voidEl("a:ext", {
9552
- cx: 0,
9553
- cy: 0
9554
- })),
9555
- raw(voidEl("a:chOff", {
9556
- x: 0,
9557
- y: 0
9558
- })),
9559
- raw(voidEl("a:chExt", {
9560
- cx: 0,
9561
- cy: 0
9562
- }))
9563
- ])))),
9564
- raw(slideImgPh),
9565
- raw(notesPh),
9566
- raw(slideNumPh)
9567
- ]);
9568
- const extLst = el("p:extLst", null, raw(el("p:ext", { uri: "{BB962C8B-B14F-4D97-AF65-F5344CB8AC3E}" }, raw(voidEl("p14:creationId", {
9569
- "xmlns:p14": "http://schemas.microsoft.com/office/powerpoint/2010/main",
9570
- val: 1024086991
9571
- })))));
9572
- const cSld = el("p:cSld", null, [raw(spTree), raw(extLst)]);
9573
- return XML_DECL + "\r\n" + el("p:notes", {
9574
- "xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
9575
- "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
9576
- "xmlns:p": "http://schemas.openxmlformats.org/presentationml/2006/main"
9577
- }, [raw(cSld), raw(el("p:clrMapOvr", null, raw(voidEl("a:masterClrMapping"))))]);
9578
- }
9579
- /**
9580
- * Generates XML string for a notes-slide relation file (`ppt/notesSlides/_rels/notesSlideN.xml.rels`).
9581
- * rId1=notesMaster and rId2=slide are always reserved; any notes hyperlink rels follow (rId3+).
9582
- * @param {PresSlideInternal} slide - the slide whose notes part is being related
9583
- * @param {number} slideNumber - 1-indexed slide number the notes part belongs to
9584
- * @return {string} XML
9585
- */
9586
- function makeXmlNotesSlideRel(slide, slideNumber) {
9587
- const hlinkRels = buildNotesSlideRels(slide).map((rel) => voidEl("Relationship", {
9588
- Id: `rId${rel.rId}`,
9589
- Type: OFFICE_REL + "hyperlink",
9590
- Target: rel.Target,
9591
- TargetMode: "External"
9592
- })).join("");
9593
- return XML_DECL + el("Relationships", { xmlns: PACKAGE_REL_NS }, [
9594
- raw(voidEl("Relationship", {
9595
- Id: "rId1",
9596
- Type: OFFICE_REL + "notesMaster",
9597
- Target: "../notesMasters/notesMaster1.xml"
9598
- })),
9599
- raw(voidEl("Relationship", {
9600
- Id: "rId2",
9601
- Type: OFFICE_REL + "slide",
9602
- Target: `../slides/slide${slideNumber}.xml`
9603
- })),
9604
- raw(hlinkRels)
9605
- ], {
9606
- openPrefix: "\n ",
9607
- childPrefix: "\n ",
9608
- closePrefix: ""
9609
- });
9610
- }
9611
- /**
9612
- * Creates `ppt/notesMasters/_rels/notesMaster1.xml.rels`
9613
- * @return {string} XML
9614
- */
9615
- function makeXmlNotesMasterRel() {
9616
- return XML_DECL + "\r\n" + el("Relationships", { xmlns: PACKAGE_REL_NS }, raw(voidEl("Relationship", {
9617
- Id: "rId1",
9618
- Type: OFFICE_REL + "theme",
9619
- Target: "../theme/theme2.xml"
9620
- })), {
9621
- childPrefix: "\n ",
9622
- closePrefix: "\n "
9623
- });
9624
- }
9625
- //#endregion
9626
8216
  //#region src/gen/pres/theme.ts
9627
8217
  /**
9628
8218
  * ts-pptx: `ppt/theme/theme1.xml`
@@ -9914,11 +8504,14 @@ function makeXmlApp(slides, company) {
9914
8504
  * actually used by the deck (plus xlsx/font defaults when present) and Override
9915
8505
  * entries for every written part.
9916
8506
  */
9917
- 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";
9918
8511
  const CT_CHARTEX = "application/vnd.ms-office.chartex+xml";
9919
8512
  const CT_CHARTEX_STYLE = "application/vnd.ms-office.chartstyle+xml";
9920
8513
  const CT_CHARTEX_COLORS = "application/vnd.ms-office.chartcolorstyle+xml";
9921
- const CT_THEME = "application/vnd.openxmlformats-officedocument.theme+xml";
8514
+ const CT_THEME = OD + "theme+xml";
9922
8515
  /**
9923
8516
  * Some Override entries have always been emitted with a leading space. It is insignificant
9924
8517
  * whitespace, but it is in the bytes this library has shipped for years, so it is reproduced
@@ -9958,7 +8551,7 @@ function chartOverrides(rel, fmt) {
9958
8551
  * @returns XML
9959
8552
  */
9960
8553
  function makeXmlContTypes(slides, slideLayouts, masterSlide, hasCustomProps, embeddedFonts) {
9961
- 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")];
9962
8555
  const extnTypeMap = /* @__PURE__ */ new Map();
9963
8556
  const ctTargets = [];
9964
8557
  (slides || []).forEach((s) => ctTargets.push(s));
@@ -9976,45 +8569,45 @@ function makeXmlContTypes(slides, slideLayouts, masterSlide, hasCustomProps, emb
9976
8569
  extnTypeMap.forEach((type, extn) => {
9977
8570
  parts.push(contentDefault(extn, type));
9978
8571
  });
9979
- 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"));
9980
8573
  if ((embeddedFonts || []).some((font) => font.faces.some((face) => face.bytes))) parts.push(contentDefault(FONT_DATA_EXTENSION, FONT_DATA_CONTENT_TYPE));
9981
- parts.push(override("/ppt/presentation.xml", "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"));
9982
- parts.push(override("/ppt/notesMasters/notesMaster1.xml", "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml"));
9983
- 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"));
9984
8577
  slides.forEach((slide, idx) => {
9985
- 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"));
9986
8579
  slide._relsChart.forEach((rel) => {
9987
8580
  parts.push(...chartOverrides(rel));
9988
8581
  });
9989
8582
  });
9990
- parts.push(override("/ppt/presProps.xml", "application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"));
9991
- 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"));
9992
8585
  parts.push(override("/ppt/theme/theme1.xml", CT_THEME));
9993
8586
  parts.push(override("/ppt/theme/theme2.xml", CT_THEME));
9994
- parts.push(override("/ppt/tableStyles.xml", "application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml"));
8587
+ parts.push(override("/ppt/tableStyles.xml", OD + "presentationml.tableStyles+xml"));
9995
8588
  slideLayouts.forEach((layout, idx) => {
9996
- 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"));
9997
8590
  (layout._relsChart || []).forEach((rel) => {
9998
8591
  parts.push(...chartOverrides(rel, LEADING_SPACE));
9999
8592
  });
10000
8593
  });
10001
8594
  slides.forEach((_slide, idx) => {
10002
- 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"));
10003
8596
  });
10004
8597
  let hasAnyComment = false;
10005
8598
  slides.forEach((slide, idx) => {
10006
8599
  if ((slide._comments || []).length > 0) {
10007
8600
  hasAnyComment = true;
10008
- 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"));
10009
8602
  }
10010
8603
  });
10011
- 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"));
10012
8605
  masterSlide?._relsChart.forEach((rel) => {
10013
8606
  parts.push(...chartOverrides(rel, LEADING_SPACE));
10014
8607
  });
10015
- parts.push(override("/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml", LEADING_SPACE));
10016
- parts.push(override("/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml", LEADING_SPACE));
10017
- 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));
10018
8611
  return XML_DECL + "\r\n" + el("Types", { xmlns: "http://schemas.openxmlformats.org/package/2006/content-types" }, parts.map(raw));
10019
8612
  }
10020
8613
  //#endregion
@@ -10563,7 +9156,7 @@ function masterAlignAttr(align) {
10563
9156
  case "right": return "r";
10564
9157
  case "center": return "ctr";
10565
9158
  case "justify": return "just";
10566
- default: return "";
9159
+ case void 0: return "";
10567
9160
  }
10568
9161
  }
10569
9162
  /** Build the bullet element for a master level: caller override wins over the level default. */
@@ -10571,10 +9164,14 @@ function masterBulletXml(bulletOverride, base) {
10571
9164
  if (bulletOverride === false) return voidEl("a:buNone");
10572
9165
  if (bulletOverride && typeof bulletOverride === "object") {
10573
9166
  const font = bulletOverride.fontFace ? voidEl("a:buFont", { typeface: bulletOverride.fontFace }) : "";
10574
- if (bulletOverride.type === "number") return font + voidEl("a:buAutoNum", {
10575
- type: bulletOverride.numberType || "arabicPeriod",
10576
- startAt: typeof bulletOverride.numberStartAt === "number" ? Math.round(bulletOverride.numberStartAt) : null
10577
- });
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
+ }
10578
9175
  const char = bulletOverride.characterCode ? `&#x${bulletOverride.characterCode};` : "•";
10579
9176
  return `${bulletOverride.fontFace ? font : voidEl("a:buFont", {
10580
9177
  typeface: "Arial",
@@ -10596,8 +9193,10 @@ function masterLevelXml(levelNum, base, levelOverride = {}) {
10596
9193
  const indentEmu = typeof levelOverride.indent === "number" && !isNaN(levelOverride.indent) ? inch2Emu(levelOverride.indent) : base.indent;
10597
9194
  const algn = levelOverride.align && masterAlignAttr(levelOverride.align) || base.algn;
10598
9195
  let sz = base.sz;
10599
- 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.`);
10600
- 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
+ }
10601
9200
  const colorXml = levelOverride.color ? createColorElement(levelOverride.color) : voidEl("a:schemeClr", { val: "tx1" });
10602
9201
  const latinXml = levelOverride.fontFace ? voidEl("a:latin", { typeface: levelOverride.fontFace }) : voidEl("a:latin", { typeface: `+${base.font}-lt` });
10603
9202
  return el(`a:lvl${levelNum}pPr`, {
@@ -10980,8 +9579,7 @@ async function zipPackageParts(parts, props) {
10980
9579
  const zip = new ZipWriter();
10981
9580
  for (const part of parts) zip.add(part.path, part.data, { store: part.store });
10982
9581
  const compression = props.compression !== false;
10983
- if (props.outputType === "STREAM") return await zip.generate("nodebuffer", { compression });
10984
- else if (props.outputType) return await zip.generate(props.outputType, { compression });
9582
+ if (props.outputType) return await zip.generate(props.outputType, { compression });
10985
9583
  else return await zip.generate("blob", { compression });
10986
9584
  }
10987
9585
  /**
@@ -11008,7 +9606,7 @@ async function writePackage(source, props) {
11008
9606
  * the OfficeGen project (Copyright 2013 Ziv Barber). This project is likewise MIT; the full
11009
9607
  * license text is in `LICENSE` at the repository root.
11010
9608
  */
11011
- const VERSION = "3.1.0";
9609
+ const VERSION = "3.3.0";
11012
9610
  function standardLayoutToPresLayout(layout) {
11013
9611
  return {
11014
9612
  name: layout.name,
@@ -11389,11 +9987,23 @@ var PresentationCore = class {
11389
9987
  rId: rel.rId,
11390
9988
  sourceSlideNumber: Number(rel.Target)
11391
9989
  }));
11392
- const charts = (slide._relsChart || []).map((rel) => ({
11393
- rId: rel.rId,
11394
- chartXml: makeXmlCharts(rel),
11395
- embeddingBytes: buildEmbeddedWorksheet(rel)
11396
- }));
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
+ });
11397
10007
  const notes = slide._slideObjects.some((obj) => obj._type === "notes") ? {
11398
10008
  xml: makeXmlNotesSlide(slide),
11399
10009
  hyperlinks: buildNotesSlideRels(slide).filter((rel) => typeof rel.Target === "string" && rel.Target).map((rel) => ({
@@ -11432,19 +10042,36 @@ var PresentationCore = class {
11432
10042
  * Without registered metrics, `fit:'shrink'` keeps its current behavior (a bare
11433
10043
  * `<a:normAutofit/>` that only PowerPoint recomputes on edit). Register the same
11434
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.
11435
10053
  * @param {string} face - font family name as used in `fontFace` (e.g. 'Aptos')
11436
- * @param {string | Uint8Array | ArrayBuffer} source - font file path/URL (Node/web) or raw TTF/OTF bytes
11437
- * @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
11438
10059
  * @example await pptx.registerFontMetrics('Aptos', '/usr/share/fonts/Aptos.ttf')
11439
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 })
11440
10063
  */
11441
10064
  async registerFontMetrics(face, source, opts) {
11442
10065
  let bytes;
11443
10066
  if (typeof source === "string") bytes = await this._runtime.loadFontData(source);
11444
10067
  else if (source instanceof Uint8Array) bytes = source;
11445
10068
  else bytes = new Uint8Array(source);
11446
- const metrics = await parseFontMetrics(bytes);
11447
- 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
+ });
11448
10075
  }
11449
10076
  /**
11450
10077
  * Embed a font face so the deck renders with it even on machines that do not
@@ -11500,7 +10127,7 @@ var PresentationCore = class {
11500
10127
  * Measure how tall text wraps at a given width, using the **same** calibrated wrap
11501
10128
  * model the export-time autofit bake uses — so a layout-time prediction matches the
11502
10129
  * value `fit:'shrink'`/`'resize'` would bake. Synchronous: register the face's metrics
11503
- * first with {@link PresentationCore.registerFontMetrics} (lookup is sync).
10130
+ * first with {@link TsPptx.registerFontMetrics} (lookup is sync).
11504
10131
  *
11505
10132
  * Lets a consumer size its own geometry before export — grow a card to fit its text,
11506
10133
  * reflow a grid, or detect overflow at layout time. Heights err **tall** (conservative),
@@ -11534,8 +10161,8 @@ var PresentationCore = class {
11534
10161
  * cells. Column widths (cell `x`/`w`) are exact, derived from the same logic the
11535
10162
  * writer uses. Row heights (`y`/`h`) are exact when pinned by `rowH` (array or
11536
10163
  * scalar) or table `h`; an auto-height row is estimated with the same conservative
11537
- * (tall) text model as {@link PresentationCore.measureText} and flagged `heightExact:false`
11538
- * (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
11539
10166
  * estimate). Geometry is for a single, un-paginated table — `autoPage` paging is
11540
10167
  * not modeled.
11541
10168
  * @param {TableRow[]} rows - the same `rows` passed to `slide.addTable`
@@ -11550,14 +10177,18 @@ var PresentationCore = class {
11550
10177
  return computeTableLayout(rows, opts, this._presLayout, this._fontMetrics);
11551
10178
  }
11552
10179
  /**
11553
- * 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.
11554
10184
  * @param {WriteBaseProps} props - output properties
11555
- * @returns {Promise<string | ArrayBuffer | Blob | Uint8Array>} file stream
10185
+ * @returns {Promise<Uint8Array>} complete `.pptx` archive bytes
11556
10186
  */
11557
- async stream(props) {
10187
+ async toBytes(props) {
11558
10188
  return await writePackage(this.packageSource(), {
11559
10189
  compression: props?.compression,
11560
- outputType: "STREAM"
10190
+ outputType: "uint8array",
10191
+ onMediaError: props?.onMediaError
11561
10192
  });
11562
10193
  }
11563
10194
  /**
@@ -11896,4 +10527,4 @@ function textRuns(runs) {
11896
10527
  //#endregion
11897
10528
  export { PresentationCore as i, textRuns as n, clipPath as r, textRun as t };
11898
10529
 
11899
- //# sourceMappingURL=text-B6PbMFih.js.map
10530
+ //# sourceMappingURL=text-uM-zGton.js.map