@shbernal/ts-pptx 4.0.1 → 4.2.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 (139) hide show
  1. package/README.md +147 -114
  2. package/dist/anim-presets-Qbgh8uut.js +55 -0
  3. package/dist/anim-presets-Qbgh8uut.js.map +1 -0
  4. package/dist/{autopage-odPz7zZO.js → autopage-DqZ37VpL.js} +11 -12
  5. package/dist/autopage-DqZ37VpL.js.map +1 -0
  6. package/dist/{binary-string-CKVAYI3M.js → binary-string-Ba8VM3b9.js} +3 -4
  7. package/dist/binary-string-Ba8VM3b9.js.map +1 -0
  8. package/dist/{body-insets-Dw_BeWD_.js → body-insets-BDHymJvz.js} +7 -9
  9. package/dist/body-insets-BDHymJvz.js.map +1 -0
  10. package/dist/browser.d.ts +24 -16
  11. package/dist/browser.d.ts.map +1 -1
  12. package/dist/browser.js +19 -19
  13. package/dist/browser.js.map +1 -1
  14. package/dist/{image-size-CvN2iCA4.js → chart-metadata-CwiTTcJq.js} +634 -537
  15. package/dist/chart-metadata-CwiTTcJq.js.map +1 -0
  16. package/dist/{clip-Bl01BPcN.d.ts → clip-ISfCMtWA.d.ts} +6 -10
  17. package/dist/clip-ISfCMtWA.d.ts.map +1 -0
  18. package/dist/{notes-Dh12JAbn.js → comments-CKIZYRbZ.js} +530 -401
  19. package/dist/comments-CKIZYRbZ.js.map +1 -0
  20. package/dist/{diagnostics-D8ot4Ei7.d.ts → diagnostics-MoA4MBq5.d.ts} +55 -53
  21. package/dist/diagnostics-MoA4MBq5.d.ts.map +1 -0
  22. package/dist/{embedded-fonts-DarT9aoa.d.ts → embedded-fonts-U7HIIhPO.d.ts} +4 -6
  23. package/dist/embedded-fonts-U7HIIhPO.d.ts.map +1 -0
  24. package/dist/{entry-compose-DCnvBZz2.d.ts → entry-compose-CiakSt-f.d.ts} +62 -81
  25. package/dist/entry-compose-CiakSt-f.d.ts.map +1 -0
  26. package/dist/{errors-C4oENPcu.js → entry-errors-JMthgJ2g.js} +45 -29
  27. package/dist/entry-errors-JMthgJ2g.js.map +1 -0
  28. package/dist/{table-cell-margins-EVg0EwxT.js → enums-BP9wsb9Q.js} +44 -47
  29. package/dist/enums-BP9wsb9Q.js.map +1 -0
  30. package/dist/{enums-BUKTUB6V.d.ts → enums-PcXr3o3J.d.ts} +13 -14
  31. package/dist/{enums-BUKTUB6V.d.ts.map → enums-PcXr3o3J.d.ts.map} +1 -1
  32. package/dist/families.d.ts +24 -26
  33. package/dist/families.d.ts.map +1 -1
  34. package/dist/families.js +3 -4
  35. package/dist/{fit-Cb7naFj6.js → fit-Bpmzep6O.js} +64 -56
  36. package/dist/fit-Bpmzep6O.js.map +1 -0
  37. package/dist/{font-metrics-v5fn7s7e.d.ts → font-metrics-DTppw4t3.d.ts} +62 -38
  38. package/dist/font-metrics-DTppw4t3.d.ts.map +1 -0
  39. package/dist/{grid-DikTjFey.js → grid-DUIMxjfl.js} +23 -26
  40. package/dist/grid-DUIMxjfl.js.map +1 -0
  41. package/dist/{html-dom-Iuog2wdK.js → html-dom-LZCgntnJ.js} +46 -47
  42. package/dist/html-dom-LZCgntnJ.js.map +1 -0
  43. package/dist/html.d.ts +53 -9
  44. package/dist/html.d.ts.map +1 -1
  45. package/dist/html.js +12 -13
  46. package/dist/html.js.map +1 -1
  47. package/dist/index.d.ts +21 -13
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +15 -15
  50. package/dist/index.js.map +1 -1
  51. package/dist/inspect.d.ts +16 -18
  52. package/dist/inspect.d.ts.map +1 -1
  53. package/dist/inspect.js +18 -19
  54. package/dist/inspect.js.map +1 -1
  55. package/dist/{text-anchor-CrYXAfUu.js → lock-attrs-CSQnFDHP.js} +62 -12
  56. package/dist/lock-attrs-CSQnFDHP.js.map +1 -0
  57. package/dist/math.d.ts +2 -4
  58. package/dist/math.d.ts.map +1 -1
  59. package/dist/math.js +12 -13
  60. package/dist/math.js.map +1 -1
  61. package/dist/measure.d.ts +23 -12
  62. package/dist/measure.d.ts.map +1 -1
  63. package/dist/measure.js +3 -3
  64. package/dist/node.d.ts +21 -13
  65. package/dist/node.d.ts.map +1 -1
  66. package/dist/node.js +12 -12
  67. package/dist/node.js.map +1 -1
  68. package/dist/{options-internal-WqgMf720.js → options-internal-BWgmLPdx.js} +4 -5
  69. package/dist/options-internal-BWgmLPdx.js.map +1 -0
  70. package/dist/{package-J49Ck6S2.d.ts → package-CANBWuSm.d.ts} +9 -13
  71. package/dist/package-CANBWuSm.d.ts.map +1 -0
  72. package/dist/{playbtn-CUqQdoDf.js → playbtn-BQZFJir9.js} +4 -5
  73. package/dist/playbtn-BQZFJir9.js.map +1 -0
  74. package/dist/{pres-C6qgOFAa.d.ts → pres-BkEkUAEM.d.ts} +410 -237
  75. package/dist/pres-BkEkUAEM.d.ts.map +1 -0
  76. package/dist/{presentation-DGYlAWhl.js → presentation-4Ggq0_Xa.js} +470 -315
  77. package/dist/presentation-4Ggq0_Xa.js.map +1 -0
  78. package/dist/{presentation-types-8jLQHI4M.d.ts → presentation-types-BgnZR-0a.d.ts} +1119 -468
  79. package/dist/presentation-types-BgnZR-0a.d.ts.map +1 -0
  80. package/dist/read.d.ts +18 -13
  81. package/dist/read.d.ts.map +1 -1
  82. package/dist/read.js +4 -4
  83. package/dist/script.d.ts +116 -65
  84. package/dist/script.d.ts.map +1 -1
  85. package/dist/script.js +2700 -780
  86. package/dist/script.js.map +1 -1
  87. package/dist/{shapes-re0KyQk_.js → shapes-CAPOjU0Q.js} +1564 -491
  88. package/dist/shapes-CAPOjU0Q.js.map +1 -0
  89. package/dist/{slide-Dx2ZeVZ6.d.ts → slide-BmT4kMoO.d.ts} +102 -89
  90. package/dist/slide-BmT4kMoO.d.ts.map +1 -0
  91. package/dist/{slide-size-BalTjFuv.js → slide-size-SkScz1ig.js} +2 -3
  92. package/dist/{slide-size-BalTjFuv.js.map → slide-size-SkScz1ig.js.map} +1 -1
  93. package/dist/{table-dom-BTDP3lEP.js → table-dom-BYga4FYY.js} +4 -5
  94. package/dist/{table-dom-BTDP3lEP.js.map → table-dom-BYga4FYY.js.map} +1 -1
  95. package/dist/{text-sN64Otf_.js → text-CU08xJGs.js} +393 -344
  96. package/dist/text-CU08xJGs.js.map +1 -0
  97. package/dist/{transition-speed-kUEWDWTx.js → transition-speed-CNJ0fSpQ.js} +2 -3
  98. package/dist/{transition-speed-kUEWDWTx.js.map → transition-speed-CNJ0fSpQ.js.map} +1 -1
  99. package/dist/{units-internal-XJlf-hOl.js → units-internal-BR3a4cIi.js} +34 -32
  100. package/dist/units-internal-BR3a4cIi.js.map +1 -0
  101. package/dist/{zip-CKd8ihcf.js → zip-7JTh61LX.js} +8 -10
  102. package/dist/zip-7JTh61LX.js.map +1 -0
  103. package/dist/{zip-C0leZZMj.d.ts → zip-uUjs9Cqx.d.ts} +4 -6
  104. package/dist/zip-uUjs9Cqx.d.ts.map +1 -0
  105. package/dist/zip.d.ts +3 -3
  106. package/dist/zip.js +3 -3
  107. package/dist/{zoom-DPUHh85d.js → zoom-CSSeOwM8.js} +1204 -620
  108. package/dist/zoom-CSSeOwM8.js.map +1 -0
  109. package/package.json +27 -28
  110. package/skills/{ts-pptx-upstream → pptx-ts-upstream}/SKILL.md +37 -84
  111. package/dist/autopage-odPz7zZO.js.map +0 -1
  112. package/dist/binary-string-CKVAYI3M.js.map +0 -1
  113. package/dist/body-insets-Dw_BeWD_.js.map +0 -1
  114. package/dist/clip-Bl01BPcN.d.ts.map +0 -1
  115. package/dist/diagnostics-D8ot4Ei7.d.ts.map +0 -1
  116. package/dist/embedded-fonts-DarT9aoa.d.ts.map +0 -1
  117. package/dist/entry-compose-DCnvBZz2.d.ts.map +0 -1
  118. package/dist/errors-C4oENPcu.js.map +0 -1
  119. package/dist/fit-Cb7naFj6.js.map +0 -1
  120. package/dist/font-metrics-v5fn7s7e.d.ts.map +0 -1
  121. package/dist/grid-DikTjFey.js.map +0 -1
  122. package/dist/html-dom-Iuog2wdK.js.map +0 -1
  123. package/dist/image-size-CvN2iCA4.js.map +0 -1
  124. package/dist/notes-Dh12JAbn.js.map +0 -1
  125. package/dist/options-internal-WqgMf720.js.map +0 -1
  126. package/dist/package-J49Ck6S2.d.ts.map +0 -1
  127. package/dist/playbtn-CUqQdoDf.js.map +0 -1
  128. package/dist/pres-C6qgOFAa.d.ts.map +0 -1
  129. package/dist/presentation-DGYlAWhl.js.map +0 -1
  130. package/dist/presentation-types-8jLQHI4M.d.ts.map +0 -1
  131. package/dist/shapes-re0KyQk_.js.map +0 -1
  132. package/dist/slide-Dx2ZeVZ6.d.ts.map +0 -1
  133. package/dist/table-cell-margins-EVg0EwxT.js.map +0 -1
  134. package/dist/text-anchor-CrYXAfUu.js.map +0 -1
  135. package/dist/text-sN64Otf_.js.map +0 -1
  136. package/dist/units-internal-XJlf-hOl.js.map +0 -1
  137. package/dist/zip-C0leZZMj.d.ts.map +0 -1
  138. package/dist/zip-CKd8ihcf.js.map +0 -1
  139. package/dist/zoom-DPUHh85d.js.map +0 -1
@@ -1,18 +1,19 @@
1
- import { l as warn, n as InvalidOptionError, r as MediaError, t as InternalError, u as warnOnce } from "./errors-C4oENPcu.js";
2
- import { F as DEF_PRES_LAYOUT_NAME, J as SLDNUM_PLACEHOLDER_TEXT, P as DEF_PRES_LAYOUT, R as DEF_SLIDE_MARGIN_IN, U as LEVEL_MARGINS_EMU, V as LAYOUT_IDX_SERIES_BASE, X as MIN_SLIDE_MASTER_ID, a as convertRotationDegrees, n as clampRangedInput, q as SLDNUMFLDID, s as getSmartParseNumber, u as mapStated, v as resolveInsetsEmu } from "./units-internal-XJlf-hOl.js";
3
- import { A as inchesToEmu, D as emuToInches, F as EMU_PER_INCH, T as STANDARD_LAYOUTS } from "./table-cell-margins-EVg0EwxT.js";
4
- import { $t as THEME_REL, Bt as PRES_PROPS_CONTENT_TYPE, Dt as MS_MEDIA_REL, E as EMBEDDED_FONT_SLOTS, Ft as OFFICE_DOCUMENT_REL, Gt as SLIDE_LAYOUT_CONTENT_TYPE, Ht as RELATIONSHIPS_CONTENT_TYPE, Jt as SLIDE_MASTER_REL, Kt as SLIDE_LAYOUT_REL, Nt as NOTES_SLIDE_REL, Qt as THEME_CONTENT_TYPE, Vt as PRES_PROPS_REL, Wt as SLIDE_CONTENT_TYPE, Xt as TABLE_STYLES_CONTENT_TYPE, Yt as SLIDE_REL, Zt as TABLE_STYLES_REL, _t as CORE_PROPS_REL, a as textAlignToken, an as PML_ROOT_NS, at as CHARTEX_REL, bt as EXTENDED_PROPS_CONTENT_TYPE, c as checkEnumOrThrow, cn as decodeBase64ToBytes, d as xsdBool, dn as toMediaDataUri, en as VIDEO_REL, f as xsdBoolIfTrue, gt as CORE_PROPS_CONTENT_TYPE, h as stripHash, ht as COMMENT_AUTHORS_REL, in as OOXML_NS, jt as NOTES_MASTER_REL, l as checkEnumOrWarn, lt as CHART_REL, nn as VIEW_PROPS_REL, nt as TRANSITION_VARIANT_ATTRIBUTES, p as isHexColor, pt as COMMENTS_REL, qt as SLIDE_MASTER_CONTENT_TYPE, rt as AUDIO_REL, sn as bytesToBase64, tn as VIEW_PROPS_CONTENT_TYPE, tt as TRANSITION_TYPES, vt as CUSTOM_PROPS_CONTENT_TYPE, w as DEFAULT_COLOR_MAP, wt as IMAGE_REL, xt as EXTENDED_PROPS_REL, yt as CUSTOM_PROPS_REL, zt as PRESENTATION_MAIN_CONTENT_TYPE } from "./image-size-CvN2iCA4.js";
5
- import { a as avContentType, i as audioExtensionForSubtype, o as dataUriMediaType, s as imageContentType } from "./text-anchor-CrYXAfUu.js";
6
- import { At as getDuplicateObjectNames, C as targetBetweenPptSubparts, Ct as FONT_DATA_CONTENT_TYPE, D as cNvPrEl, Dt as serializeEmbeddedFontLst, Et as flattenEmbeddedFaces, G as clampParaMarginInchesEmu, It as preencodedPath, K as buAutoNumEl, N as spTreeEl, Nt as isHyperlinkRel, Ot as XML_DECL, Pt as isPreencodedPath, R as prstGeomRect, Rt as encodeXmlAttrValue, S as slidePath, St as voidEl, T as targetFromPresentation, Tt as FONT_REL_TYPE, U as clampFontSizeSz, W as clampParaIndentInchesEmu, _ as fontPath, at as fillNamesPaint, b as relsPath, bt as elOrVoid, c as themeFontDefRPr, f as relationshipsPart, g as commentPath, h as SLIDE_MASTER_PATH, ht as createColorElement, i as makeXmlNotesSlide, j as grpXfrmEl, jt as getNewRelId, kt as bracedGuid, l as externalHyperlinkRel, m as PRESENTATION_PATH, n as makeXmlNotesMaster, ot as genXmlColorSelection, p as NOTES_MASTER_PATH, q as buCharEl, s as lvlPPr, t as buildNotesSlideRels, u as relationshipEl, ut as STRETCH_FILL_RECT, v as notesSlidePath, vt as rejectEmptyColor, w as targetFromPptSubpart, wt as FONT_DATA_EXTENSION, x as slideLayoutPath, xt as raw, y as overrideName, yt as el } from "./notes-Dh12JAbn.js";
7
- import { A as renderedSlideObjects, B as pushMediaRel, C as msMediaRid, D as findLayoutPlaceholder, E as placeholderOptionsForSlide, F as warnUnauthoredChild, I as SLIDE_METHOD_FAMILIES, L as composeFamilies, M as shapeIdCount, N as addChildDefinition, O as resolveTextAnchor, P as isGroupableObject, R as familyMethodUnavailable, S as alternateContentEl, T as addTextDefinition, V as resolveImageSource, _ as chartPartNames, a as mediaFamily, b as GROUP_SHAPE_LOCK_ATTRS, c as commentsFamily, d as textFamily, f as shapeFamily, g as makeXmlCore, h as groupFamily, i as model3dFamily, j as resolveObjectNameToId, k as collectSlideShapeIds, l as chartFamily, m as imageFamily, n as tableFamily, o as measureFamily, p as notesFamily, r as oleFamily, s as connectorFamily, t as zoomFamily, u as animationsFamily, v as FMT_SCHEME_XML, w as previewRid, x as genXmlObjectLock, y as resolveImageExtent, z as requirePresentationAuthor } from "./zoom-DPUHh85d.js";
8
- import { n as setOrClear, t as pickDefined } from "./options-internal-WqgMf720.js";
9
- import { b as isFontCollection, g as FontMetricsRegistry, t as applyMeasuredFit, y as parseFontMetrics } from "./fit-Cb7naFj6.js";
10
- import { t as ZipWriter } from "./zip-CKd8ihcf.js";
11
- import { t as transitionSpeedForDuration } from "./transition-speed-kUEWDWTx.js";
12
- import { n as presentationFormatLabel, r as slideSizeType } from "./slide-size-BalTjFuv.js";
13
- //#region src/gen/define/background.ts
14
- /**
15
- * ts-pptx: Background Definition
1
+ import { d as warnOnce, i as MediaError, n as InternalError, r as InvalidOptionError, u as warn } from "./entry-errors-JMthgJ2g.js";
2
+ import { F as DEF_PRES_LAYOUT_NAME, J as SLDNUM_PLACEHOLDER_TEXT, P as DEF_PRES_LAYOUT, R as DEF_SLIDE_MARGIN_IN, S as transparencyToAlpha, U as LEVEL_MARGINS_EMU, V as LAYOUT_IDX_SERIES_BASE, X as MIN_SLIDE_MASTER_ID, a as convertRotationDegrees, n as clampRangedInput, q as SLDNUMFLDID, s as getSmartParseNumber, u as mapStated, v as resolveInsetsEmu } from "./units-internal-BR3a4cIi.js";
3
+ import { A as inchesToEmu, D as emuToInches, F as EMU_PER_INCH, T as STANDARD_LAYOUTS, o as PlaceholderType } from "./enums-BP9wsb9Q.js";
4
+ import { $ as SLIDE_LAYOUT_CONTENT_TYPE, C as COMMENT_AUTHORS_REL, Ct as DEFAULT_COLOR_MAP, D as CUSTOM_PROPS_REL, E as CUSTOM_PROPS_CONTENT_TYPE, F as MS_MEDIA_REL, Gt as TEXT_CAPS_TYPES, J as PRES_PROPS_CONTENT_TYPE, M as IMAGE_REL, O as EXTENDED_PROPS_CONTENT_TYPE, Q as SLIDE_CONTENT_TYPE, T as CORE_PROPS_REL, Tt as EMBEDDED_FONT_SLOTS, U as OFFICE_DOCUMENT_REL, V as NOTES_SLIDE_REL, X as RELATIONSHIPS_CONTENT_TYPE, Xt as TEXT_UNDERLINE_TYPES, Y as PRES_PROPS_REL, Zt as TEXT_VERTICAL, _ as CHART_REL, an as checkEnumOrWarn, at as TABLE_STYLES_REL, cn as xsdBoolIfTrue, ct as VIDEO_REL, d as AUDIO_REL, dn as stripHash, et as SLIDE_LAYOUT_REL, fn as bytesToBase64, ft as OOXML_NS, gn as toMediaDataUri, in as checkEnumOrThrow, it as TABLE_STYLES_CONTENT_TYPE, k as EXTENDED_PROPS_REL, ln as isHexColor, lt as VIEW_PROPS_CONTENT_TYPE, nn as TRANSITION_TYPES, nt as SLIDE_MASTER_REL, ot as THEME_CONTENT_TYPE, p as CHARTEX_REL, pn as decodeBase64ToBytes, pt as PML_ROOT_NS, q as PRESENTATION_MAIN_CONTENT_TYPE, qt as TEXT_SHAPE_TYPES, rn as TRANSITION_VARIANT_ATTRIBUTES, rt as SLIDE_REL, s as textAlignToken, sn as xsdBool, st as THEME_REL, tt as SLIDE_MASTER_CONTENT_TYPE, ut as VIEW_PROPS_REL, w as CORE_PROPS_CONTENT_TYPE, x as COMMENTS_REL, z as NOTES_MASTER_REL } from "./chart-metadata-CwiTTcJq.js";
5
+ import { c as audioExtensionForSubtype, d as imageContentType, l as avContentType, n as GROUP_SHAPE_LOCK_ATTRS, u as dataUriMediaType } from "./lock-attrs-CSQnFDHP.js";
6
+ import { $ as buAutoNumEl, A as cNvPrEl, C as relsPath, Ct as resolveFillKind, D as targetFromPptSubpart, E as targetBetweenPptSubparts, Et as genXmlImageCropRect, Ft as voidEl, H as prstGeomRect, Ht as isPreencodedPath, It as XML_DECL, J as clampFontSizeSz, Kt as encodeXmlAttrValue, L as spTreeEl, Lt as bracedGuid, Mt as el, Nt as elOrVoid, O as targetFromPresentation, Ot as createColorElement, P as grpXfrmEl, Pt as raw, Q as clampParaMarginInchesEmu, Rt as getDuplicateObjectNames, S as overrideName, T as slidePath, Tt as STRETCH_FILL_RECT, Vt as isHyperlinkRel, Wt as preencodedPath, X as clampParaIndentInchesEmu, Y as clampParaIndentEmu, Z as clampParaMarginEmu, _ as PRESENTATION_PATH, _t as flattenEmbeddedFaces, a as makeXmlNotesMaster, b as fontPath, bt as genXmlColorSelection, d as themeFontDefRPr, dt as withLineDefaults, et as buCharEl, f as externalHyperlinkRel, g as NOTES_MASTER_PATH, gt as FONT_REL_TYPE, h as relationshipsPart, ht as FONT_DATA_EXTENSION, i as buildNotesSlideRels, jt as rejectEmptyColor, mt as FONT_DATA_CONTENT_TYPE, p as relationshipEl, q as underlineProps, s as makeXmlNotesSlide, u as lvlPPr, v as SLIDE_MASTER_PATH, vt as serializeEmbeddedFontLst, w as slideLayoutPath, wt as solidPaint, x as notesSlidePath, y as commentPath, yt as fillNamesPaint, zt as getNewRelId } from "./comments-CKIZYRbZ.js";
7
+ import { A as statedPlaceholderBinding, B as composeFamilies, C as msMediaRid, D as checkPlaceholderBinding, E as placeholderOptionsForSlide, F as shapeIdCount, H as requirePresentationAuthor, I as addChildDefinition, L as isGroupableObject, M as collectSlideShapeIds, N as renderedSlideObjects, O as findLayoutPlaceholder, P as resolveObjectNameToId, R as warnUnauthoredChild, S as alternateContentEl, T as addTextDefinition, U as pushMediaRel, V as familyMethodUnavailable, W as resolveImageSource, _ as makeXmlCore, a as mediaFamily, b as resolveImageExtent, c as commentsFamily, d as textFamily, f as shapeFamily, g as makeXmlTableStyles, h as groupFamily, i as model3dFamily, j as resolveTextAnchor, k as layoutPlaceholderName, l as chartFamily, m as imageFamily, n as tableFamily, o as measureFamily, p as notesFamily, r as oleFamily, s as connectorFamily, t as zoomFamily, u as animationsFamily, v as chartPartNames, w as previewRid, x as genXmlObjectLock, y as FMT_SCHEME_XML, z as SLIDE_METHOD_FAMILIES } from "./zoom-CSSeOwM8.js";
8
+ import { n as setOrClear, t as pickDefined } from "./options-internal-BWgmLPdx.js";
9
+ import { b as isFontCollection, g as FontMetricsRegistry, t as applyMeasuredFit, y as parseFontMetrics } from "./fit-Bpmzep6O.js";
10
+ import { t as ZipWriter } from "./zip-7JTh61LX.js";
11
+ import { t as ANIM_PRESET_IDENTITY } from "./anim-presets-Qbgh8uut.js";
12
+ import { t as transitionSpeedForDuration } from "./transition-speed-CNJ0fSpQ.js";
13
+ import { n as presentationFormatLabel, r as slideSizeType } from "./slide-size-SkScz1ig.js";
14
+ // src/gen/define/background.ts
15
+ /**
16
+ * pptx-ts: Background Definition
16
17
  *
17
18
  * Registers an `addBackground()` image as a slide media rel (color backgrounds carry no rel);
18
19
  * the `<p:bg>` XML is emitted later at slide / layout serialize time.
@@ -21,7 +22,7 @@ import { n as presentationFormatLabel, r as slideSizeType } from "./slide-size-B
21
22
  * Adds a background image or color to a slide definition.
22
23
  *
23
24
  * Only an image background does anything here: it is the one that needs a media relationship.
24
- * A colour — object or the bare-string shorthand — carries no rel and is emitted straight from
25
+ * A colour (object or the bare-string shorthand) carries no rel and is emitted straight from
25
26
  * `slide.background` at serialize time.
26
27
  *
27
28
  * The rel id and the part name come from the shared allocators. This used to take the id as
@@ -49,11 +50,12 @@ import { n as presentationFormatLabel, r as slideSizeType } from "./slide-size-B
49
50
  * @param {SlideLayoutInternal} target - slide or layout that the background is set on
50
51
  */
51
52
  function addBackgroundDefinition(props, target) {
52
- if (!(props && typeof props === "object" && (props.path || props.data))) {
53
+ const image = backgroundImage(props);
54
+ if (!image) {
53
55
  delete target._bkgdImgRid;
54
56
  return;
55
57
  }
56
- const source = resolveImageSource(props);
58
+ const source = resolveImageSource(image);
57
59
  if (typeof source === "string") {
58
60
  warn("background/missing-base64-header", "background `data` value lacks a base64 header (ex: 'image/png;base64,...'); ignoring the background image.");
59
61
  delete target._bkgdImgRid;
@@ -87,8 +89,24 @@ function addBackgroundDefinition(props, target) {
87
89
  });
88
90
  target._bkgdImgRid = rId;
89
91
  }
90
- //#endregion
91
- //#region src/slide.ts
92
+ /**
93
+ * The picture a background names, in either of its two spellings: `{ data }` / `{ path }` from
94
+ * `DataOrPathProps`, or `{ image: { data, crop } }` from `ShapeFillProps`, which `BackgroundProps`
95
+ * also extends. The second used to type-check and register nothing, so the background was written
96
+ * as `<a:noFill/>` and its crop was lost with it.
97
+ *
98
+ * The top-level spelling wins, as it always has. The `image` one follows the fill rule that an
99
+ * explicit `type` other than `'image'` wins over a sub-object.
100
+ * @param props - the background option
101
+ * @returns the picture's source, or `null` when the background names none
102
+ */
103
+ function backgroundImage(props) {
104
+ if (!props || typeof props !== "object") return null;
105
+ if (props.path || props.data) return props;
106
+ const image = props.image;
107
+ return resolveFillKind(props) === "image" && image && (image.path || image.data) ? image : null;
108
+ }
109
+ // src/slide.ts
92
110
  /**
93
111
  * Project one authored render-object onto the public {@link SlideObjectInfo}, recursing into a
94
112
  * group's children.
@@ -139,7 +157,7 @@ var SlideBuilder = class {
139
157
  constructor(params) {
140
158
  this.addSlide = params.addSlide;
141
159
  this.getSlide = params.getSlide;
142
- this.getSections = params.getSections ?? (() => []);
160
+ this.getSections = params.getSections;
143
161
  this._name = `Slide ${params.slideNumber}`;
144
162
  this._presLayout = params.presLayout;
145
163
  this._rId = params.slideRId;
@@ -148,7 +166,7 @@ var SlideBuilder = class {
148
166
  this._relsMedia = [];
149
167
  this._setSlideNum = params.setSlideNum;
150
168
  this._slideId = params.slideId;
151
- this._slideLayout = params.slideLayout || null;
169
+ this._slideLayout = params.slideLayout;
152
170
  this._slideNum = params.slideNumber;
153
171
  this._slideObjects = [];
154
172
  this._childAuthors = params.childAuthors;
@@ -157,7 +175,7 @@ var SlideBuilder = class {
157
175
  * `defineSlideMaster` and `addNewSlide.slideNumber` will add {slideNumber} to `this.masterSlide` and `this.slideLayouts`
158
176
  * so, lastly, add to the Slide now.
159
177
  */
160
- this._slideNumberProps = this._slideLayout?._slideNumberProps ? this._slideLayout._slideNumberProps : null;
178
+ this._slideNumberProps = params.slideLayout._slideNumberProps ?? null;
161
179
  }
162
180
  /**
163
181
  * Put this presentation's family methods on the slide.
@@ -269,10 +287,9 @@ var SlideBuilder = class {
269
287
  return emuToInches(this._presLayout.height);
270
288
  }
271
289
  };
272
- //#endregion
273
- //#region src/font-source.ts
290
+ // src/font-source.ts
274
291
  /**
275
- * ts-pptx: getting font bytes out of whatever the caller handed over.
292
+ * pptx-ts: getting font bytes out of whatever the caller handed over.
276
293
  *
277
294
  * `registerFontMetrics` and `embedFont` are two public methods on one class, forty lines apart,
278
295
  * and each resolved its source itself. They agreed on `Uint8Array` and `ArrayBuffer` and
@@ -310,8 +327,11 @@ async function resolveFontBytes(source, runtime, opts, label) {
310
327
  }
311
328
  throw new InvalidOptionError("font/missing-source", `${label} must be a font path, raw bytes, or base64 data`);
312
329
  }
313
- //#endregion
314
- //#region src/gen/define/master.ts
330
+ // src/gen/define/master.ts
331
+ /** Whether a definition's `type` is one of the six friendly ones the `p:ph` writer maps itself. */
332
+ function isFriendlyPlaceholderType(type) {
333
+ return Object.hasOwn(PlaceholderType, type) || Object.values(PlaceholderType).includes(type);
334
+ }
315
335
  /**
316
336
  * Transforms a slide definition to a slide object that is then passed to the XML transformation process.
317
337
  * @param {SlideMasterProps} props - slide definition
@@ -323,40 +343,59 @@ function createSlideMaster(props, target, children) {
323
343
  const tgt = target;
324
344
  if (addChildDefinition(tgt, object, children)) {} else if ("placeholder" in object) {
325
345
  const placeholder = object.placeholder;
326
- const { name, type, ...rawPlaceholderOptions } = placeholder.options;
346
+ const { name, type, idx: statedIdx, ...rawPlaceholderOptions } = placeholder.options;
347
+ checkPlaceholderBinding({
348
+ ...statedIdx === void 0 ? {} : { idx: statedIdx },
349
+ ...isFriendlyPlaceholderType(type) ? {} : { type }
350
+ }, "defineSlideMaster");
327
351
  const placeholderOptions = rawPlaceholderOptions;
328
352
  placeholderOptions.placeholder = name;
329
353
  placeholderOptions._placeholderType = type;
330
- placeholderOptions._placeholderIdx = 100 + idx;
354
+ placeholderOptions._placeholderIdx = statedIdx ?? 100 + idx;
331
355
  const placeholderRun = placeholder.text === void 0 ? {} : { text: placeholder.text };
332
356
  addTextDefinition(tgt, [placeholderRun], placeholderOptions, true);
333
357
  } else warnUnauthoredChild(object, "defineSlideMaster()");
334
358
  });
335
359
  if (props.slideNumber && typeof props.slideNumber === "object") target._slideNumberProps = props.slideNumber;
336
360
  }
337
- //#endregion
338
- //#region src/gen/define/placeholder.ts
361
+ // src/gen/define/placeholder.ts
339
362
  /**
340
363
  * Adds placeholder objects to slide
341
364
  * @param {PresSlideInternal} slide - slide object containing layouts
342
365
  */
343
366
  function addPlaceholdersToSlideLayouts(slide) {
344
367
  if (!slide._slideLayout) return;
345
- (slide._slideLayout._slideObjects || []).forEach((slideLayoutObj) => {
368
+ slide._slideLayout._slideObjects.forEach((slideLayoutObj) => {
346
369
  if (slideLayoutObj._type === "placeholder") {
347
370
  const slideLayoutOptions = slideLayoutObj.options || {};
348
- if (!slide._slideObjects.some((slideObj) => slideObj.options && slideObj.options.placeholder === slideLayoutOptions.placeholder)) addTextDefinition(slide, [{ text: "" }], placeholderOptionsForSlide(slideLayoutOptions), true);
371
+ const fills = (slideObj) => {
372
+ if (layoutPlaceholderName(slideObj.options?.placeholder) === slideLayoutOptions.placeholder) return true;
373
+ const binding = statedPlaceholderBinding(slideObj.options?.placeholder);
374
+ if (!binding || slideLayoutOptions._placeholderIdx === void 0) return false;
375
+ if (binding.idx !== void 0) return binding.idx === slideLayoutOptions._placeholderIdx;
376
+ return slideLayoutOptions._placeholderIdx === 0 && binding.type === slideLayoutOptions._placeholderType;
377
+ };
378
+ if (!slide._slideObjects.some(fills)) {
379
+ addTextDefinition(slide, [{ text: "" }], placeholderOptionsForSlide(slideLayoutOptions), true);
380
+ const seeded = slide._slideObjects.at(-1);
381
+ if (seeded) {
382
+ seeded._seeded = true;
383
+ seeded.text = [];
384
+ const options = seeded.options ?? (seeded.options = {});
385
+ const line = options.line;
386
+ options.line = line?.type === "none" ? { type: "none" } : line && fillNamesPaint(line) ? withLineDefaults(line, "line.color") : { type: "inherit" };
387
+ }
388
+ }
349
389
  }
350
390
  });
351
391
  }
352
- //#endregion
353
- //#region src/media/placeholders.ts
392
+ // src/media/placeholders.ts
354
393
  /**
355
394
  * The base64 placeholder images the library falls back to when a real asset is unavailable.
356
395
  *
357
396
  * Their own module because of where the bundler puts them, not because they group naturally.
358
397
  * The chunker splits by module, so a constant is pulled into every chunk that reaches the module
359
- * declaring it — and these lived in `constants-internal.ts` beside `XML_DECL`, `DEF_FONT_SIZE`
398
+ * declaring it, and these lived in `constants-internal.ts` beside `XML_DECL`, `DEF_FONT_SIZE`
360
399
  * and the rest of the vocabulary all of `src/gen/` imports. The rule that follows: a payload
361
400
  * measured in kilobytes does not share a module with vocabulary, however tidy that grouping reads.
362
401
  *
@@ -368,10 +407,9 @@ function addPlaceholdersToSlideLayouts(slide) {
368
407
  const IMG_BROKEN = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAB3CAYAAAD1oOVhAAAGAUlEQVR4Xu2dT0xcRRzHf7tAYSsc0EBSIq2xEg8mtTGebVzEqOVIolz0siRE4gGTStqKwdpWsXoyGhMuyAVJOHBgqyvLNgonDkabeCBYW/8kTUr0wsJC+Wfm0bfuvn37Znbem9mR9303mJnf/Pb7ed95M7PDI5JIJPYJV5EC7e3t1N/fT62trdqViQCIu+bVgpIHEo/Hqbe3V/sdYVKHyWSSZmZm8ilVA0oeyNjYmEnaVC2Xvr6+qg5fAOJAz4DU1dURGzFSqZRVqtMpAFIGyMjICC0vL9PExIRWKADiAYTNshYWFrRCARAOEFZcCKWtrY0GBgaUTYkBRACIE4rKZwqACALR5RQAqQCIDqcASIVAVDsFQCSAqHQKgEgCUeUUAPEBRIVTAMQnEBvK5OQkbW9vk991CoAEAMQJxc86BUACAhKUUwAkQCBBOAVAAgbi1ykAogCIH6cAiCIgsk4BEIVAZJwCIIqBVLqiBxANQFgXS0tLND4+zl08AogmIG5OSSQS1gGKwgtANAIRcQqAaAbCe6YASBWA2E6xDyeyDUl7+AKQMkDYYevm5mZHabA/Li4uUiaTsYLau8QA4gLE/hU7wajyYtv1hReDAiAOxQcHBymbzark4BkbQKom/X8dp9Npmpqasn4BIAYAYSnYp+4BBEAMUcCwNOCQsAKZnp62NtQOw8WmwT09PUo+ijaHsOMx7GppaaH6+nolH0Z10K2tLVpdXbW6UfV3mNqBdHd3U1NTk2rtlMRfW1uj2dlZAFGirkRQAJEQTWUTAFGprkRsAJEQTWUTAFGprkRsAJEQTWUTAFGprkRsAJEQTWUTAFGprkRsAJEQTWUTAFGprkRsAJEQTWUTAGHqrm8caPzQ0WC1logbeiC7X3xJm0PvUmRzh45cuki1588FAmVn9BO6P3yF9utrqGH0MtW82S8UN9RA9v/4k7InjhcJFTs/TLVXLwmJV67S7vD7tHF5pKi46fYdosdOcOOGG8j1OcqefbFEJD9Q3GCwDhqT31HklS4A8VRgfYM2Op6k3bt/BQJl58J7lPvwg5JYNccepaMry0LPqFA7hCm39+NNyp2J0172b19QysGINj5CsRtpij57musOViH0QPJQXn6J9u7dlYJSFkbrMYolrwvDAJAC+WWdEpQz7FTgECeUCpzi6YxvvqXoM6eEhqnCSgDikEzUKUE7Aw7xuHctKB5OYU3dZlNR9syQdAaAcAYTC0pXF+39c09o2Ik+3EqxVKqiB7hbYAxZkk4pbBaEM+AQofv+wTrFwylBOQNABIGwavdfe4O2pg5elO+86l99nY58/VUF0byrYsjiSFluNlXYrOHcBar7+EogUADEQ0YRGHbzoKAASBkg2+9cpM1rV0tK2QOcXW7bLEFAARAXIF4w2DrDWoeUWaf4hQIgDiA8GPZ2iNfi0Q8UACkAIgrDbrJ385eDxaPLLrEsFAB5oG6lMPJQPLZZZKAACBGVhcG2Q+bmuLu2nk55e4jqPv1IeEoceiBeX7s2zCa5MAqdstl91vfXwaEGsv/rb5TtOFk6tWXOuJGh6KmnhO9sayrMninPx103JBtXblHkice58cINZP4Hyr5wpkgkdiChEmc4FWazLzenNKa/p0jncwDiqcD6BuWePk07t1asatZGoYQzSqA4nFJ7soNiP/+EUyfc25GI2GG53dHPrKo1g/1Cw4pIXLrzO+1c+/wg7tBbFDle/EbQcjFCPWQJCau5EoBoFpzXHYDwFNJcDiCaBed1ByA8hTSXA4hmwXndAQhPIc3lAKJZcF53AMJTSHM5gGgWnNcdgPAU0lwOIJoF53UHIDyFNJcfSiCdnZ0Ui8U0SxlMd7lcjubn561gh+Y1scFIU/0o/3sgeLO12E2k7UXKYumgFoAYdg8ACIAYpoBh6cAhAGKYAoalA4cAiGEKGJYOHAIghilgWDpwCIAYpoBh6cAhAGKYAoalA4cAiGEKGJYOHAIghilgWDpwCIAYpoBh6ZQ4JB6PKzviYthnNy4d9h+1M5mMlVckkUjsG5dhiBMCEMPg/wuOfrZZ/RSywQAAAABJRU5ErkJggg==";
369
408
  /** 1x1 transparent PNG, standing in for an SVG the runtime could not rasterize. */
370
409
  const IMG_SVG_PLACEHOLDER = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
371
- //#endregion
372
- //#region src/gen/media.ts
410
+ // src/gen/media.ts
373
411
  /**
374
- * ts-pptx: Media Methods
412
+ * pptx-ts: Media Methods
375
413
  */
376
414
  function hasEncodingPath(rel) {
377
415
  return typeof rel.path === "string" && rel.path.length > 0 && !isPreencodedPath(rel.path);
@@ -398,14 +436,14 @@ async function writeSvgPreview(rel, runtime, onMediaError) {
398
436
  * Encode Image/Audio/Video into base64
399
437
  * @param {PresSlideInternal | SlideLayoutInternal | SlideMasterInternal} layout - slide, layout or master
400
438
  * @param {RuntimeAdapter} runtime - runtime adapter (Node/browser media loader)
401
- * @param {'throw' | 'placeholder'} onMediaError - failure policy: reject the export (default) or substitute a placeholder and warn
439
+ * @param {'throw' | 'placeholder'} onMediaError - failure policy: reject the export or substitute a placeholder and warn
402
440
  * @return {Promise} promise
403
441
  */
404
- function encodeSlideMediaRels(layout, runtime, onMediaError = "throw") {
442
+ function encodeSlideMediaRels(layout, runtime, onMediaError) {
405
443
  const imageProms = [];
406
444
  const defaultCoverRels = layout._relsMedia.filter((rel) => rel.isDefaultCover && !rel.data);
407
445
  if (defaultCoverRels.length > 0) imageProms.push((async () => {
408
- const { IMG_PLAYBTN } = await import("./playbtn-CUqQdoDf.js");
446
+ const { IMG_PLAYBTN } = await import("./playbtn-BQZFJir9.js");
409
447
  for (const rel of defaultCoverRels) rel.data = IMG_PLAYBTN;
410
448
  return "done";
411
449
  })());
@@ -445,24 +483,23 @@ function encodeSlideMediaRels(layout, runtime, onMediaError = "throw") {
445
483
  });
446
484
  return imageProms;
447
485
  }
448
- //#endregion
449
- //#region src/gen/prepare.ts
486
+ // src/gen/prepare.ts
450
487
  /**
451
- * ts-pptx: the pre-serialization pass
488
+ * pptx-ts: the pre-serialization pass
452
489
  *
453
490
  * Everything that must happen to authored slide state *before* any XML is built. Two callers
454
491
  * reach serialization and both need it:
455
492
  *
456
- * - `package/assemble.ts` — the normal `write`/`stream`/`writeFile` path, building a
493
+ * - `package/assemble.ts`: the normal `write`/`stream`/`writeFile` path, building a
457
494
  * complete `.pptx`.
458
- * - `PresentationCore.extractSlides()` — serializes slide bodies only, for splicing into
495
+ * - `PresentationCore.extractSlides()`: serializes slide bodies only, for splicing into
459
496
  * an already-loaded deck via `Presentation.appendSlides()` (`pptx-ts/read`).
460
497
  *
461
498
  * The second used to reimplement the first inline, kept in step by a comment that said
462
499
  * "exactly as ... does". Nothing failed when they drifted: `extractSlides` would simply emit
463
500
  * slides that did not match what a normal write produces, and no test compares the two.
464
501
  *
465
- * Three steps rather than one combined pass, because the package path has work of its own that
502
+ * Separate steps rather than one combined pass, because the package path has work of its own that
466
503
  * must land between the last two: it de-duplicates identical media across the deck (which needs
467
504
  * the encoded bytes {@link encodeMediaForTargets} produces) and assigns package-unique chart part
468
505
  * filenames, both before any text is measured. Exposing the steps lets `assemble` keep that work
@@ -470,7 +507,9 @@ function encodeSlideMediaRels(layout, runtime, onMediaError = "throw") {
470
507
  *
471
508
  * The order is load-bearing:
472
509
  *
473
- * 1. **Backfill placeholders first**, so a slide carries its layout's placeholder objects before
510
+ * 0. **Register transition sounds**, before media encoding loads their bytes and before
511
+ * backfill, so a slide's rel ids come out the same on both paths.
512
+ * 1. **Backfill placeholders**, so a slide carries its layout's placeholder objects before
474
513
  * anything reads its media. A seeded placeholder can register media of its own, an image fill,
475
514
  * and when backfill ran after encoding that image was never loaded and its rel named a part
476
515
  * that was never written.
@@ -482,7 +521,7 @@ function encodeSlideMediaRels(layout, runtime, onMediaError = "throw") {
482
521
  *
483
522
  * The functions take different target sets on purpose: media lives on layouts and the master as
484
523
  * well as on slides, while placeholder backfill and measured fit apply only to slides.
485
- * `extractSlides` passes only its slides to all three, because it emits no layout or master
524
+ * `extractSlides` passes only its slides to every step, because it emits no layout or master
486
525
  * parts.
487
526
  */
488
527
  /**
@@ -518,10 +557,19 @@ function requireSlideLinksInDeck(slides, layouts = [], master) {
518
557
  part: master
519
558
  }] : []
520
559
  ];
560
+ const refuse = (label, target) => {
561
+ throw new InvalidOptionError("slide/link-past-last-slide", `${label} links to slide ${String(target)}, but the deck has ${slides.length} slide${slides.length === 1 ? "" : "s"}.`);
562
+ };
521
563
  for (const { label, part } of sources) for (const rel of part._rels) {
522
564
  if (!isHyperlinkRel(rel) || rel.data !== "slide") continue;
523
- if (!(Number(rel.Target) > slides.length)) continue;
524
- throw new InvalidOptionError("slide/link-past-last-slide", `${label} links to slide ${String(rel.Target)}, but the deck has ${slides.length} slide${slides.length === 1 ? "" : "s"}.`);
565
+ if (Number(rel.Target) > slides.length) refuse(label, rel.Target);
566
+ }
567
+ for (const slide of slides) for (const obj of slide._slideObjects) {
568
+ if (obj._type !== "notes") continue;
569
+ for (const run of obj.text ?? []) {
570
+ const target = run.options?.hyperlink?.slide;
571
+ if (target !== void 0 && target > slides.length) refuse(`The notes of slide ${slide._slideNum}`, target);
572
+ }
525
573
  }
526
574
  }
527
575
  /**
@@ -536,6 +584,61 @@ async function encodeMediaForTargets(targets, runtime, onMediaError) {
536
584
  await Promise.all(promises);
537
585
  }
538
586
  /**
587
+ * Each slide's registered transition sound.
588
+ *
589
+ * Kept per slide, never on the transition: one transition object can be assigned to several slides,
590
+ * and a relationship id means something only inside the slide part that declares it. Kept across
591
+ * writes as well, so a deck written twice registers each sound once. An entry is reused only while
592
+ * the slide still holds its relationship and still has the same sound assigned.
593
+ */
594
+ const registeredTransitionSounds = /* @__PURE__ */ new WeakMap();
595
+ /**
596
+ * Step 0: register an audio media part + relationship for each slide-transition start sound
597
+ * (`transition.sound` with `data`/`path`), and return the relationship id each slide's
598
+ * `p:sndAc/p:snd r:embed` refers to. Runs before media encoding so the bytes are loaded. Re-export
599
+ * is safe: a slide whose sound is already registered reuses that relationship. The stop-previous
600
+ * form (`sound.stopPrevious`) needs no part and is skipped.
601
+ *
602
+ * Registered here, at write time, rather than when `slide.transition` is assigned. A slide's rel
603
+ * ids and media part names are handed out in registration order, and a transition is often set
604
+ * before the slide's pictures and media are added; registering the sound then would move every
605
+ * later rel id and part name on the slide.
606
+ */
607
+ function registerTransitionSounds(slides) {
608
+ const soundRIds = /* @__PURE__ */ new Map();
609
+ slides.forEach((slide) => {
610
+ const transition = slide.transition;
611
+ if (!transition || !TRANSITION_TYPES.includes(transition.type)) return;
612
+ const sound = transition.sound;
613
+ if (!sound || sound.stopPrevious) return;
614
+ if (!sound.data && !sound.path) return;
615
+ const registered = registeredTransitionSounds.get(slide);
616
+ if (registered && slide._relsMedia.includes(registered.rel) && registered.data === sound.data && registered.path === sound.path) {
617
+ soundRIds.set(slide, registered.rel.rId);
618
+ return;
619
+ }
620
+ const dataMime = dataUriMediaType(sound.data ?? "");
621
+ const pathFile = sound.path ? (sound.path.split("/").pop() ?? "").split("?")[0] ?? "" : "";
622
+ const extn = dataMime?.startsWith("audio/") ? audioExtensionForSubtype(dataMime.slice(6)) : (pathFile.split(".").pop() ?? "wav").toLowerCase();
623
+ const rId = getNewRelId(slide);
624
+ const rel = pushMediaRel(slide, {
625
+ kind: "audio",
626
+ extn,
627
+ type: `audio/${extn}`,
628
+ path: sound.path,
629
+ data: sound.data,
630
+ rId
631
+ });
632
+ registeredTransitionSounds.set(slide, {
633
+ rel,
634
+ data: sound.data,
635
+ path: sound.path
636
+ });
637
+ soundRIds.set(slide, rId);
638
+ });
639
+ return soundRIds;
640
+ }
641
+ /**
539
642
  * Step 1: seed each slide with the layout placeholders it leaves empty.
540
643
  * @param {PresSlideInternal[]} slides - the slides to prepare
541
644
  */
@@ -551,10 +654,9 @@ function backfillPlaceholders(slides) {
551
654
  function bakeMeasuredFit(slides, fontMetrics) {
552
655
  return applyMeasuredFit(slides, fontMetrics);
553
656
  }
554
- //#endregion
555
- //#region src/gen/anim/animation.ts
657
+ // src/gen/anim/animation.ts
556
658
  /**
557
- * Resolved animation target's shape id (`spid`), or `null` when it cannot be resolved — in which
659
+ * Resolved animation target's shape id (`spid`), or `null` when it cannot be resolved. In that
558
660
  * case the effect is dropped, so every `null` warns rather than leaving the animation silently
559
661
  * missing from the deck.
560
662
  *
@@ -566,10 +668,10 @@ function bakeMeasuredFit(slides, fontMetrics) {
566
668
  *
567
669
  * `shapeIndex` is a 0-based index into the top-level objects that RENDER, which is the same
568
670
  * sequence `collectSlideShapeIds` allocates along; group children take ids past that range. It
569
- * counted `_slideObjects` itself, and four of that array's members draw nothing — so `addNotes`
671
+ * counted `_slideObjects` itself, and four of that array's members draw nothing, so `addNotes`
570
672
  * before the first shape shifted every index by one and `shapeIndex: 0` addressed the notes.
571
673
  * An index outside `[0, renderedCount)` would emit a `<p:spTgt spid>` naming no shape on the
572
- * slide — a dangling spid PowerPoint reports as a repair (0x80070570) — so it warns and drops,
674
+ * slide (a dangling spid PowerPoint reports as a repair, 0x80070570), so it warns and drops,
573
675
  * exactly like an unresolvable `objectName` does.
574
676
  * @param shapeIds - the slide's shape ids, from `collectSlideShapeIds`
575
677
  * @param slideObjects - the slide's top-level objects, which a `shapeIndex` addresses
@@ -670,38 +772,23 @@ const ANIM_FLY_AXIS = (direction, axis, spid, dur, next) => {
670
772
  };
671
773
  const ANIM_PRESETS = {
672
774
  fadeIn: {
673
- presetID: 10,
674
- presetClass: "entr",
675
- presetSubtype: 0,
676
- defaultDurationMs: 500,
775
+ ...ANIM_PRESET_IDENTITY.fadeIn,
677
776
  behaviors: (spid, dur, next) => ANIM_SET_VISIBLE(spid, next) + ANIM_FADE("in", spid, dur, next)
678
777
  },
679
778
  flyIn: {
680
- presetID: 2,
681
- presetClass: "entr",
682
- presetSubtype: 4,
683
- defaultDurationMs: 500,
779
+ ...ANIM_PRESET_IDENTITY.flyIn,
684
780
  behaviors: (spid, dur, next) => ANIM_SET_VISIBLE(spid, next) + ANIM_FLY_AXIS("in", "x", spid, dur, next) + ANIM_FLY_AXIS("in", "y", spid, dur, next)
685
781
  },
686
782
  appear: {
687
- presetID: 1,
688
- presetClass: "entr",
689
- presetSubtype: 0,
690
- defaultDurationMs: 500,
783
+ ...ANIM_PRESET_IDENTITY.appear,
691
784
  behaviors: (spid, _dur, next) => ANIM_SET_VISIBLE(spid, next)
692
785
  },
693
786
  wipe: {
694
- presetID: 22,
695
- presetClass: "entr",
696
- presetSubtype: 4,
697
- defaultDurationMs: 500,
787
+ ...ANIM_PRESET_IDENTITY.wipe,
698
788
  behaviors: (spid, dur, next) => ANIM_SET_VISIBLE(spid, next) + ANIM_EFFECT("in", "wipe(down)", spid, dur, next)
699
789
  },
700
790
  grow: {
701
- presetID: 6,
702
- presetClass: "emph",
703
- presetSubtype: 0,
704
- defaultDurationMs: 2e3,
791
+ ...ANIM_PRESET_IDENTITY.grow,
705
792
  behaviors: (spid, dur, next) => el("p:animScale", null, [raw(el("p:cBhvr", null, [raw(voidEl("p:cTn", {
706
793
  id: next(),
707
794
  dur,
@@ -712,10 +799,7 @@ const ANIM_PRESETS = {
712
799
  }))])
713
800
  },
714
801
  spin: {
715
- presetID: 8,
716
- presetClass: "emph",
717
- presetSubtype: 0,
718
- defaultDurationMs: 2e3,
802
+ ...ANIM_PRESET_IDENTITY.spin,
719
803
  behaviors: (spid, dur, next) => el("p:animRot", { by: 216e5 }, [raw(el("p:cBhvr", null, [
720
804
  raw(voidEl("p:cTn", {
721
805
  id: next(),
@@ -727,17 +811,11 @@ const ANIM_PRESETS = {
727
811
  ]))])
728
812
  },
729
813
  fadeOut: {
730
- presetID: 10,
731
- presetClass: "exit",
732
- presetSubtype: 0,
733
- defaultDurationMs: 500,
814
+ ...ANIM_PRESET_IDENTITY.fadeOut,
734
815
  behaviors: (spid, dur, next) => ANIM_FADE("out", spid, dur, next) + ANIM_SET_HIDDEN(spid, dur, next)
735
816
  },
736
817
  flyOut: {
737
- presetID: 2,
738
- presetClass: "exit",
739
- presetSubtype: 4,
740
- defaultDurationMs: 500,
818
+ ...ANIM_PRESET_IDENTITY.flyOut,
741
819
  behaviors: (spid, dur, next) => ANIM_FLY_AXIS("out", "x", spid, dur, next) + ANIM_FLY_AXIS("out", "y", spid, dur, next) + ANIM_SET_HIDDEN(spid, dur, next)
742
820
  }
743
821
  };
@@ -847,8 +925,7 @@ function buildBldList(animations) {
847
925
  }
848
926
  return el("p:bldLst", null, raw(bldPs.join("")));
849
927
  }
850
- //#endregion
851
- //#region src/gen/anim/timing.ts
928
+ // src/gen/anim/timing.ts
852
929
  /**
853
930
  * Build the slide-level `<p:timing>` tree that makes embedded media loop.
854
931
  * - PowerPoint stores playback looping as `repeatCount` on the media node's `<p:cTn>`
@@ -863,7 +940,7 @@ function buildBldList(animations) {
863
940
  function slideTimingToXml(slide) {
864
941
  const loopMedia = slide._slideObjects.filter((obj) => obj._type === "media" && obj.mtype !== "online" && typeof obj.mediaRid === "number" && (obj.loop === true || typeof obj.loopCount === "number" && obj.loopCount > 0));
865
942
  const shapeIds = collectSlideShapeIds(slide._slideObjects);
866
- const animations = (slide._animations ?? []).filter((anim) => hasKnownAnimationPreset(anim)).map((anim) => ({
943
+ const animations = slide._animations.filter((anim) => hasKnownAnimationPreset(anim)).map((anim) => ({
867
944
  anim,
868
945
  spid: resolveAnimationSpid(shapeIds, slide._slideObjects, anim)
869
946
  })).filter((entry) => entry.spid !== null);
@@ -901,8 +978,7 @@ function slideTimingToXml(slide) {
901
978
  nodeType: "tmRoot"
902
979
  }, raw(el("p:childTnLst", null, raw(seq + mediaNodes))))))))), raw(bldLst)]);
903
980
  }
904
- //#endregion
905
- //#region src/gen/anim/transition.ts
981
+ // src/gen/anim/transition.ts
906
982
  /**
907
983
  * The attributes of a transition's type element, from the caller's `variant`.
908
984
  *
@@ -919,7 +995,7 @@ function transitionVariantAttrs(type, variant) {
919
995
  attrs[name] = value;
920
996
  continue;
921
997
  }
922
- warnOnce("transition/unknown-variant", `slide transition \`${type}\` variant \`${name}\` is not an attribute of <p:${type}> and is ignored — ${allowed.length > 0 ? `use ${allowed.join(", ")}` : "it takes none"}.`, {
998
+ warnOnce("transition/unknown-variant", `slide transition \`${type}\` variant \`${name}\` is not an attribute of <p:${type}> and is ignored; ${allowed.length > 0 ? `use ${allowed.join(", ")}` : "it takes none"}.`, {
923
999
  received: name,
924
1000
  valid: allowed
925
1001
  });
@@ -982,9 +1058,8 @@ function slideTransitionToXml(slide, soundRId) {
982
1058
  fallback: el("p:transition", baseAttrs, [raw(typeEl), raw(sndAc)])
983
1059
  });
984
1060
  }
985
- //#endregion
986
- //#region src/gen/slide/object.ts
987
- /** The four axes that make up an explicit group frame. All or nothing — see `givenGroupFrameAxes`. */
1061
+ // src/gen/slide/object.ts
1062
+ /** The four axes that make up an explicit group frame. All or nothing; see `givenGroupFrameAxes`. */
988
1063
  const GROUP_FRAME_AXES = [
989
1064
  "x",
990
1065
  "y",
@@ -996,14 +1071,14 @@ const GROUP_FRAME_AXES = [
996
1071
  *
997
1072
  * A group frame is explicit only when **all four** are given; anything less falls back to
998
1073
  * auto-bounds on every axis. Partial frames used to be honoured per-axis, which let the unset ones
999
- * take the shared per-object defaults (`x=0`, `y=0`, `cx=75%` of the layout width, `cy=0`) — so
1074
+ * take the shared per-object defaults (`x=0`, `y=0`, `cx=75%` of the layout width, `cy=0`), so
1000
1075
  * `addGroup([rect], { x: 5, y: 2 })` emitted a zero-height group whose width was a silent
1001
1076
  * slide-width fraction, and every child of it then re-read as `null` through the read path's
1002
1077
  * degenerate-`chExt` guard.
1003
1078
  *
1004
1079
  * Per-axis fallback was the other candidate, but it cannot mean what it reads like: the writer keeps
1005
1080
  * an identity child space (`chOff/chExt == off/ext`), so a group's frame never moves or scales its
1006
- * children — it only places the selection handle and the rotate pivot. `{ x: 5 }` would leave the
1081
+ * children; it only places the selection handle and the rotate pivot. `{ x: 5 }` would leave the
1007
1082
  * children where they were and put the group's box somewhere they are not. Falling back whole, with
1008
1083
  * a warning, keeps the group box around its content and says so out loud.
1009
1084
  * @param options - the group object's options
@@ -1023,7 +1098,7 @@ const hasCompleteGroupFrame = (options) => givenGroupFrameAxes(options).length =
1023
1098
  * `<a:ext cx>`/`<a:ext cy>` are `ST_PositiveCoordinate` (ECMA-376 Part 1, `CT_PositiveSize2D`), so a
1024
1099
  * negative extent is schema-invalid. PowerPoint rejects the *whole* presentation with "The file or
1025
1100
  * directory is corrupted and unreadable" (0x80070570) and names no shape, part, or slide, while
1026
- * LibreOffice renders the same package happily — so the defect is invisible until the deck reaches
1101
+ * LibreOffice renders the same package happily, so the defect is invisible until the deck reaches
1027
1102
  * PowerPoint.
1028
1103
  *
1029
1104
  * A signed delta is the natural way to write "draw from A to B" (`{ x: x0, y: y0, w: x1 - x0, h: y1 - y0 }`),
@@ -1075,7 +1150,7 @@ function resolveObjectFrame(obj, slide) {
1075
1150
  const layout = slide._presLayout;
1076
1151
  const placeholder = findLayoutPlaceholder(slide._slideLayout, itemOpts.placeholder);
1077
1152
  const phOpts = placeholder?.options ?? {};
1078
- const inherited = (own, ph) => own !== void 0 ? own : ph ?? void 0;
1153
+ const inherited = (own, ph) => own ?? ph ?? void 0;
1079
1154
  const ownX = inherited(itemOpts.x, phOpts.x);
1080
1155
  const ownY = inherited(itemOpts.y, phOpts.y);
1081
1156
  const ownW = inherited(itemOpts.w, phOpts.w);
@@ -1096,7 +1171,7 @@ function resolveObjectFrame(obj, slide) {
1096
1171
  * Whether this part has a real slide number to cache in a `slidenum` field.
1097
1172
  *
1098
1173
  * Only a slide does. A master leaves `_slideNum` null and a layout carries the internal 1000+
1099
- * counter that keeps layout media keys from colliding with slide media — neither is a page
1174
+ * counter that keeps layout media keys from colliding with slide media. Neither is a page
1100
1175
  * number, and neither belongs in the field's cached text.
1101
1176
  */
1102
1177
  function hasRealSlideNumber(slide) {
@@ -1107,7 +1182,7 @@ function hasRealSlideNumber(slide) {
1107
1182
  *
1108
1183
  * Unique `objectName` values are what consumers (e.g. semantic manifests) rely on, so
1109
1184
  * collisions are flagged loudly. Deliberately NOT `warnOnce`: this runs per emit, and two
1110
- * `write()` calls are two separate exports — a caller capturing diagnostics around the second
1185
+ * `write()` calls are two separate exports, and a caller capturing diagnostics around the second
1111
1186
  * would otherwise be told its deck is clean because the first one had already said otherwise. Groups are recursed into: a group's children are
1112
1187
  * `<p:cNvPr>`-named on this same slide, so a child colliding with a top-level object (or with a
1113
1188
  * child of another group) is a collision the Selection Pane shows, and checking only the top
@@ -1126,14 +1201,32 @@ function warnDuplicateObjectNames(slide) {
1126
1201
  * mutually exclusive because a master carrying both a `color` and a `path` must still produce a
1127
1202
  * single `<p:bg>`.
1128
1203
  */
1204
+ /**
1205
+ * The `<a:srcRect>` of a picture background: the crop an `image: { crop }` background states, else
1206
+ * the empty, uncropped form every picture background wrote before it could have one.
1207
+ */
1208
+ function backgroundSrcRect(background) {
1209
+ const image = backgroundImage(background);
1210
+ const crop = image && "crop" in image ? image.crop : void 0;
1211
+ return crop ? genXmlImageCropRect(crop, "background image", "") : voidEl("a:srcRect");
1212
+ }
1213
+ /**
1214
+ * The children of a picture background's `<a:blip>`: its `transparency` as `a:alphaModFix`, as a
1215
+ * shape's picture fill writes it and PowerPoint writes Format Background's picture transparency
1216
+ * (#98), then the `a:lum` every picture background has always carried.
1217
+ */
1218
+ function backgroundBlipEffects(background) {
1219
+ const transparency = typeof background === "object" ? background.transparency : void 0;
1220
+ return (transparency ? voidEl("a:alphaModFix", { amt: transparencyToAlpha(transparency) }) : "") + voidEl("a:lum");
1221
+ }
1129
1222
  function slideBackgroundXml(slide) {
1130
1223
  let strSlideXml = "";
1131
1224
  if (slide._bkgdImgRid) strSlideXml += el("p:bg", null, raw(el("p:bgPr", null, [raw(el("a:blipFill", {
1132
1225
  dpi: "0",
1133
1226
  rotWithShape: "1"
1134
1227
  }, [
1135
- raw(el("a:blip", { "r:embed": `rId${slide._bkgdImgRid}` }, raw(voidEl("a:lum")))),
1136
- raw(voidEl("a:srcRect")),
1228
+ raw(el("a:blip", { "r:embed": `rId${slide._bkgdImgRid}` }, raw(backgroundBlipEffects(slide.background)))),
1229
+ raw(backgroundSrcRect(slide.background)),
1137
1230
  raw(STRETCH_FILL_RECT)
1138
1231
  ])), raw(voidEl("a:effectLst"))])));
1139
1232
  else if (fillNamesPaint(slide.background)) strSlideXml += el("p:bg", null, raw(el("p:bgPr", null, [raw(genXmlColorSelection(slide.background)), raw(voidEl("a:effectLst"))])));
@@ -1144,7 +1237,7 @@ function slideBackgroundXml(slide) {
1144
1237
  * The slide-number placeholder shape.
1145
1238
  *
1146
1239
  * Emitted after every authored object, so its `<p:cNvPr>` id can come from the same monotonic
1147
- * counter the walk has been advancing — the caller allocates it and passes it in.
1240
+ * counter the walk has been advancing; the caller allocates it and passes it in.
1148
1241
  *
1149
1242
  * @param slide - the slide, layout or master being emitted
1150
1243
  * @param snProps - the part's slide-number properties, read but never written: a serializer
@@ -1178,19 +1271,21 @@ function slideNumberPlaceholderXml(slide, snProps, slideNumberId) {
1178
1271
  })))), { openPrefix: " " }) + "</p:spPr>";
1179
1272
  strSlideXml += "<p:txBody>";
1180
1273
  const snInsets = resolveInsetsEmu(snProps.margin);
1181
- strSlideXml += voidEl("a:bodyPr", {
1274
+ const snWarp = checkEnumOrWarn(snProps.textWarp, TEXT_SHAPE_TYPES, "text/invalid-warp", "slideNumber: textWarp");
1275
+ strSlideXml += elOrVoid("a:bodyPr", {
1182
1276
  lIns: snInsets?.l ?? null,
1183
1277
  tIns: snInsets?.t ?? null,
1184
1278
  rIns: snInsets?.r ?? null,
1185
1279
  bIns: snInsets?.b ?? null,
1186
- anchor: resolveTextAnchor(snProps.valign)
1187
- });
1280
+ anchor: resolveTextAnchor(snProps.valign),
1281
+ vert: checkEnumOrWarn(snProps.textDirection, TEXT_VERTICAL, "text/invalid-vertical", "slideNumber: textDirection")
1282
+ }, snWarp ? el("a:prstTxWarp", { prst: snWarp }, raw(voidEl("a:avLst"))) : "");
1188
1283
  let defRPr = "";
1189
1284
  rejectEmptyColor(snProps.color, "slideNumber color");
1190
1285
  if (snProps.fontFace || snProps.fontSize || snProps.color) {
1191
1286
  const face = snProps.fontFace;
1192
1287
  defRPr = el("a:defRPr", { sz: clampFontSizeSz(snProps.fontSize || 12) }, [
1193
- snProps.color ? raw(genXmlColorSelection(snProps.color)) : null,
1288
+ snProps.color ? raw(genXmlColorSelection(solidPaint(snProps.color, snProps.transparency))) : null,
1194
1289
  face ? raw(voidEl("a:latin", { typeface: face })) : null,
1195
1290
  face ? raw(voidEl("a:ea", { typeface: face })) : null,
1196
1291
  face ? raw(voidEl("a:cs", { typeface: face })) : null
@@ -1200,20 +1295,47 @@ function slideNumberPlaceholderXml(slide, snProps, slideNumberId) {
1200
1295
  const snAlignRaw = snProps.align ?? "left";
1201
1296
  const snAlign = snAlignRaw.startsWith("c") ? "ctr" : snAlignRaw.startsWith("r") ? "r" : "l";
1202
1297
  strSlideXml += el("a:p", null, [
1203
- raw(voidEl("a:pPr", { algn: snAlign })),
1298
+ raw(voidEl("a:pPr", {
1299
+ algn: snAlign,
1300
+ marL: paragraphMarginEmu(snProps.paraMarginLeft, clampParaMarginEmu),
1301
+ indent: paragraphMarginEmu(snProps.paraIndent, clampParaIndentEmu)
1302
+ })),
1204
1303
  raw(el("a:fld", {
1205
1304
  id: SLDNUMFLDID,
1206
1305
  type: "slidenum"
1207
- }, [raw(voidEl("a:rPr", {
1208
- b: xsdBool(snProps.bold),
1209
- lang: "en-US"
1210
- })), raw(el("a:t", null, hasRealSlideNumber(slide) ? String(slide._slideNum) : SLDNUM_PLACEHOLDER_TEXT))])),
1306
+ }, [raw(slideNumberRunProperties(snProps)), raw(el("a:t", null, hasRealSlideNumber(slide) ? String(slide._slideNum) : SLDNUM_PLACEHOLDER_TEXT))])),
1211
1307
  raw(voidEl("a:endParaRPr", { lang: "en-US" }))
1212
1308
  ]);
1213
1309
  strSlideXml += "</p:txBody></p:sp>";
1214
1310
  return strSlideXml;
1215
1311
  }
1216
1312
  /**
1313
+ * The slide-number field's own `a:rPr`: `b` and `lang` as it has always written them, then the
1314
+ * character options `addText` writes on a run's `a:rPr` (`genXmlTextRunProperties`). Colour, face
1315
+ * and size stay on the list style's `a:defRPr`, where they were written first.
1316
+ * @param snProps - the part's slide-number properties
1317
+ */
1318
+ function slideNumberRunProperties(snProps) {
1319
+ const underline = underlineProps(snProps.underline);
1320
+ rejectEmptyColor(snProps.highlight, "slideNumber highlight");
1321
+ rejectEmptyColor(underline?.color, "slideNumber underline.color");
1322
+ return elOrVoid("a:rPr", {
1323
+ b: xsdBool(snProps.bold),
1324
+ lang: snProps.lang || "en-US",
1325
+ i: xsdBoolIfTrue(snProps.italic),
1326
+ cap: snProps.caps ? checkEnumOrWarn(snProps.caps, TEXT_CAPS_TYPES, "text/invalid-caps", "slideNumber `caps`") : null,
1327
+ u: underline?.style ? checkEnumOrWarn(underline.style, TEXT_UNDERLINE_TYPES, "text/invalid-underline", "slideNumber `underline.style`") : null
1328
+ }, [
1329
+ snProps.highlight ? el("a:highlight", null, raw(createColorElement(snProps.highlight))) : "",
1330
+ underline?.color ? el("a:uFill", null, raw(genXmlColorSelection(underline.color))) : "",
1331
+ snProps.fontFaceEA ? voidEl("a:ea", { typeface: snProps.fontFaceEA }) : ""
1332
+ ].join(""));
1333
+ }
1334
+ /** `paraMarginLeft` / `paraIndent` in EMU through the attribute's clamp, or `null` to write none. */
1335
+ function paragraphMarginEmu(option, clamp) {
1336
+ return option === void 0 || option === "inherit" ? null : clamp(option);
1337
+ }
1338
+ /**
1217
1339
  * Transforms a slide or slideLayout to resulting XML string - Creates `ppt/slide*.xml`
1218
1340
  * @param slide - slide object created within createSlideObject
1219
1341
  * @param renderers - which renderer emits each shape family; see {@link RendererTable} for why
@@ -1379,8 +1501,8 @@ function slideObjectToXml(slide, renderers) {
1379
1501
  function defaultRelIdStart(slide) {
1380
1502
  let lastRid = 0;
1381
1503
  for (const rel of slide._rels) lastRid = Math.max(lastRid, rel.rId);
1382
- for (const rel of slide._relsChart || []) lastRid = Math.max(lastRid, rel.rId);
1383
- for (const rel of slide._relsMedia || []) lastRid = Math.max(lastRid, rel.rId);
1504
+ for (const rel of slide._relsChart) lastRid = Math.max(lastRid, rel.rId);
1505
+ for (const rel of slide._relsMedia) lastRid = Math.max(lastRid, rel.rId);
1384
1506
  return lastRid + 1;
1385
1507
  }
1386
1508
  /**
@@ -1390,7 +1512,7 @@ function defaultRelIdStart(slide) {
1390
1512
  * `selfPath` is the emitting part's own path, and it is required because a relationship target
1391
1513
  * resolves against the part that states it. An internal slide link used to be written
1392
1514
  * `slide${n}.xml` whatever the part, which is right from `ppt/slides/` and names nothing from
1393
- * `ppt/slideLayouts/` or `ppt/slideMasters/` — so a `hyperlink: { slide }` on an object given to
1515
+ * `ppt/slideLayouts/` or `ppt/slideMasters/`, so a `hyperlink: { slide }` on an object given to
1394
1516
  * `defineSlideMaster({ objects })` wrote a dangling relationship.
1395
1517
  * @param {PresSlideInternal | SlideLayoutInternal | SlideMasterInternal} slide - slide, layout or master whose relations are being transformed
1396
1518
  * @param {{ target: string; type: string }[]} defaultRels - array of default relations
@@ -1405,7 +1527,7 @@ function slideObjectRelationsToXml(slide, defaultRels, selfPath) {
1405
1527
  * told from the first by the first already being there.
1406
1528
  *
1407
1529
  * Two things about the shape. It holds raw Targets rather than escaped ones, because this
1408
- * asks about the model and not about markup — the probe used to substring-scan the emitted
1530
+ * asks about the model and not about markup. The probe used to substring-scan the emitted
1409
1531
  * XML for ` Target="…"`, which forced the caller to escape by hand with the same escaper the
1410
1532
  * builder uses or the two drifted apart, and cost O(rels squared) in string scanning.
1411
1533
  *
@@ -1422,10 +1544,11 @@ function slideObjectRelationsToXml(slide, defaultRels, selfPath) {
1422
1544
  else rels.push(externalHyperlinkRel(rel.rId, rel.Target));
1423
1545
  }
1424
1546
  });
1425
- (slide._relsChart || []).forEach((rel) => {
1547
+ slide._relsChart.forEach((rel) => {
1426
1548
  rels.push(relationshipEl(rel.rId, rel.isChartEx ? CHARTEX_REL : CHART_REL, rel.Target));
1427
1549
  });
1428
- (slide._relsMedia || []).forEach((rel) => {
1550
+ slide._relsMedia.forEach((rel) => {
1551
+ if (rel.chartOwned) return;
1429
1552
  const relType = rel.type.toLowerCase();
1430
1553
  const seen = mediaTargets.has(rel.Target);
1431
1554
  mediaTargets.add(rel.Target);
@@ -1443,10 +1566,9 @@ function slideObjectRelationsToXml(slide, defaultRels, selfPath) {
1443
1566
  });
1444
1567
  return relationshipsPart(rels);
1445
1568
  }
1446
- //#endregion
1447
- //#region src/gen/slide/slide.ts
1569
+ // src/gen/slide/slide.ts
1448
1570
  /**
1449
- * ts-pptx: slide part + slide/layout rels
1571
+ * pptx-ts: slide part + slide/layout rels
1450
1572
  *
1451
1573
  * Emit a slide (`ppt/slides/slideN.xml`) and the relationship files for slides
1452
1574
  * and layouts (`slideN.xml.rels`, `slideLayoutN.xml.rels`).
@@ -1462,7 +1584,7 @@ function slideObjectRelationsToXml(slide, defaultRels, selfPath) {
1462
1584
  function makeXmlSlide(slide, renderers, transitionSoundRId) {
1463
1585
  return XML_DECL + "\r\n" + el("p:sld", {
1464
1586
  ...PML_ROOT_NS,
1465
- show: slide?.hidden ? "0" : null
1587
+ show: slide.hidden ? "0" : null
1466
1588
  }, [
1467
1589
  raw(slideObjectToXml(slide, renderers)),
1468
1590
  raw(el("p:clrMapOvr", null, raw(voidEl("a:masterClrMapping")))),
@@ -1501,7 +1623,7 @@ function makeXmlSlideRel(slides, slideLayouts, slideNumber) {
1501
1623
  target: targetFromPptSubpart(notesSlidePath(slideNumber)),
1502
1624
  type: NOTES_SLIDE_REL
1503
1625
  }];
1504
- if ((slide._comments || []).length > 0) defaultRels.push({
1626
+ if (slide._comments.length > 0) defaultRels.push({
1505
1627
  target: targetFromPptSubpart(commentPath(slideNumber)),
1506
1628
  type: COMMENTS_REL
1507
1629
  });
@@ -1518,10 +1640,9 @@ function getLayoutIdxForSlide(slides, slideLayouts, slideNumber) {
1518
1640
  for (let i = 0; i < slideLayouts.length; i++) if (slideLayouts[i]?._name === slides[slideNumber - 1]?._slideLayout?._name) return i + 1;
1519
1641
  return 1;
1520
1642
  }
1521
- //#endregion
1522
- //#region src/gen/pres/theme.ts
1643
+ // src/gen/pres/theme.ts
1523
1644
  /**
1524
- * ts-pptx: `ppt/theme/theme1.xml`
1645
+ * pptx-ts: `ppt/theme/theme1.xml`
1525
1646
  *
1526
1647
  * Emit the Office theme: the `<a:clrScheme>` (with any caller color overrides
1527
1648
  * over the default Office scheme) plus the fixed font/format schemes.
@@ -1572,13 +1693,13 @@ function buildThemeClrScheme(scheme) {
1572
1693
  }
1573
1694
  /**
1574
1695
  * The per-script theme font fallback table (`a:fontScheme`'s CT_TextFont lists), in OOXML
1575
- * document order. Static Office defaults — no caller data, no escaping risk — unlike the
1696
+ * document order. Static Office defaults (no caller data, no escaping risk), unlike the
1576
1697
  * `<a:latin>`/`<a:ea>`/`<a:cs>` slots above.
1577
1698
  *
1578
1699
  * Forty-one of the forty-seven scripts take the same face in the major and minor lists, so the
1579
1700
  * shared table below is the whole thing and the two override maps hold the six-odd rows that
1580
1701
  * actually differ. Written out twice, ninety-four lines said less than these do, and one row
1581
- * mistyped in one copy produces a theme that opens fine and paints a script in the wrong face —
1702
+ * mistyped in one copy produces a theme that opens fine and paints a script in the wrong face:
1582
1703
  * a difference nobody reviewing a diff of two 47-row tables would see.
1583
1704
  */
1584
1705
  const THEME_FONT_LIST = [
@@ -1703,15 +1824,14 @@ function makeXmlTheme(pres) {
1703
1824
  raw(extLst)
1704
1825
  ]);
1705
1826
  }
1706
- //#endregion
1707
- //#region src/gen/extract-slides.ts
1827
+ // src/gen/extract-slides.ts
1708
1828
  /**
1709
1829
  * Embedded audio/video. addMedia (`gen/define/media.ts`) pushes three consecutive
1710
1830
  * _relsMedia entries per item off `mediaRid`: the ECMA audio/video rel
1711
1831
  * (rId=mediaRid), the MS-2007 `media` rel (mediaRid+1, sharing one Target),
1712
1832
  * and the preview image rel (mediaRid+2). The body references all three; we
1713
1833
  * surface the descriptor so appendSlides can reproduce the rel graph. Online
1714
- * media (external link) is excluded — it has a different rel shape and no part.
1834
+ * media (external link) is excluded: it has a different rel shape and no part.
1715
1835
  */
1716
1836
  function avMediaOf(slide, relByRid) {
1717
1837
  return slide._slideObjects.filter((obj) => obj._type === "media" && obj.mtype !== "online" && typeof obj.mediaRid === "number").map((obj) => {
@@ -1720,8 +1840,8 @@ function avMediaOf(slide, relByRid) {
1720
1840
  const mediaRel = relByRid.get(mediaRid);
1721
1841
  const previewRel = relByRid.get(previewRid(mediaRid));
1722
1842
  if (!mediaRel || !previewRel) return null;
1723
- const mediaBytes = decodeBase64ToBytes(typeof mediaRel.data === "string" ? mediaRel.data : "");
1724
- const previewBytes = decodeBase64ToBytes(typeof previewRel.data === "string" ? previewRel.data : "");
1843
+ const mediaBytes = decodeBase64ToBytes(mediaRel.data);
1844
+ const previewBytes = decodeBase64ToBytes(previewRel.data);
1725
1845
  if (!mediaBytes || !previewBytes) return null;
1726
1846
  const mediaExtn = relExtension(mediaRel, "mp4");
1727
1847
  const previewExtn = relExtension(previewRel, "png");
@@ -1756,9 +1876,8 @@ function relExtension(rel, fallback) {
1756
1876
  * excluded here rather than surfaced twice.
1757
1877
  */
1758
1878
  function imageMediaOf(slide, avPreviewRids) {
1759
- return slide._relsMedia.filter((rel) => rel.type.toLowerCase().includes("image") && !avPreviewRids.has(rel.rId)).map((rel) => {
1760
- const data = rel.data && typeof rel.data === "string" ? rel.data : "";
1761
- const bytes = decodeBase64ToBytes(data);
1879
+ return slide._relsMedia.filter((rel) => rel.type.toLowerCase().includes("image") && !avPreviewRids.has(rel.rId) && !rel.chartOwned).map((rel) => {
1880
+ const bytes = decodeBase64ToBytes(rel.data);
1762
1881
  const extn = relExtension(rel, "png");
1763
1882
  return bytes ? {
1764
1883
  rId: rel.rId,
@@ -1769,6 +1888,23 @@ function imageMediaOf(slide, avPreviewRids) {
1769
1888
  }).filter((m) => m !== null);
1770
1889
  }
1771
1890
  /**
1891
+ * A transition's start sound: the audio part behind `p:sndAc/p:stSnd/p:snd r:embed`, under the
1892
+ * `rId` {@link registerTransitionSounds} gave it. `undefined` when the slide has no start sound.
1893
+ */
1894
+ function transitionSoundOf(rId, relByRid) {
1895
+ const rel = rId === void 0 ? void 0 : relByRid.get(rId);
1896
+ if (!rel || rId === void 0) return void 0;
1897
+ const bytes = decodeBase64ToBytes(rel.data);
1898
+ if (!bytes) return void 0;
1899
+ const extn = relExtension(rel, "wav");
1900
+ return {
1901
+ rId,
1902
+ bytes,
1903
+ extn,
1904
+ contentType: avContentType(extn, "audio")
1905
+ };
1906
+ }
1907
+ /**
1772
1908
  * Online (external-link) video. addMedia type:'online' pushes three rels off
1773
1909
  * mediaRid: the ECMA video rel (mediaRid, External), the MS-2007 media rel
1774
1910
  * (mediaRid+1, External, sharing the link Target), and the poster image rel
@@ -1801,10 +1937,10 @@ function hyperlinksOf(slide) {
1801
1937
  * A Target that does not read as a slide number throws rather than passing `NaN` on to the
1802
1938
  * append path's rel-graph rebuild, where it surfaces as "links to source slide NaN" a whole
1803
1939
  * subsystem later. `addText` writes this Target itself, so the condition is a defect on this
1804
- * side rather than bad input — an {@link InternalError}, like the chart router's own.
1940
+ * side rather than bad input: an {@link InternalError}, like the chart router's own.
1805
1941
  */
1806
- function slideLinksOf(slide) {
1807
- return slide._rels.filter((rel) => isHyperlinkRel(rel) && rel.data === "slide").map((rel) => {
1942
+ function slideLinksOf(rels) {
1943
+ return rels.filter((rel) => isHyperlinkRel(rel) && rel.data === "slide").map((rel) => {
1808
1944
  const sourceSlideNumber = Number(rel.Target);
1809
1945
  if (!Number.isInteger(sourceSlideNumber) || sourceSlideNumber < 1) throw new InternalError("slide/link-target-not-a-number", `Internal slide link rId${rel.rId} targets "${String(rel.Target)}", which is not a slide number`);
1810
1946
  return {
@@ -1823,10 +1959,11 @@ function notesOf(slide) {
1823
1959
  const notes = buildNotesSlideRels(slide);
1824
1960
  return {
1825
1961
  xml: makeXmlNotesSlide(slide, notes),
1826
- hyperlinks: notes.rels.filter((rel) => typeof rel.Target === "string" && rel.Target).map((rel) => ({
1962
+ hyperlinks: notes.rels.filter((rel) => rel.data !== "slide" && typeof rel.Target === "string" && rel.Target).map((rel) => ({
1827
1963
  rId: rel.rId,
1828
1964
  target: rel.Target
1829
- }))
1965
+ })),
1966
+ slideLinks: slideLinksOf(notes.rels)
1830
1967
  };
1831
1968
  }
1832
1969
  /**
@@ -1839,6 +1976,7 @@ async function extractSlides(source, opts = {}) {
1839
1976
  const { presentation } = source;
1840
1977
  const deckSlides = presentation.slides;
1841
1978
  requireSlideLinksInDeck(deckSlides);
1979
+ const transitionSoundRIds = registerTransitionSounds(deckSlides);
1842
1980
  backfillPlaceholders(deckSlides);
1843
1981
  await encodeMediaForTargets(deckSlides, source.runtime, onMediaError);
1844
1982
  const restoreAuthoredFit = bakeMeasuredFit(deckSlides, source.fontMetrics);
@@ -1848,15 +1986,19 @@ async function extractSlides(source, opts = {}) {
1848
1986
  const relByRid = new Map(slide._relsMedia.map((rel) => [rel.rId, rel]));
1849
1987
  const avMedia = avMediaOf(slide, relByRid);
1850
1988
  const notes = notesOf(slide);
1989
+ const soundRId = transitionSoundRIds.get(slide);
1990
+ const transitionSound = transitionSoundOf(soundRId, relByRid);
1851
1991
  return {
1852
- xml: makeXmlSlide(slide, source.renderers),
1992
+ xml: makeXmlSlide(slide, source.renderers, soundRId),
1853
1993
  media: imageMediaOf(slide, new Set(avMedia.map((item) => item.previewRid))),
1854
1994
  hyperlinks: hyperlinksOf(slide),
1855
1995
  charts: source.extract.charts?.(slide) ?? [],
1856
- slideLinks: slideLinksOf(slide),
1996
+ slideLinks: slideLinksOf(slide._rels),
1857
1997
  avMedia,
1858
1998
  onlineMedia: onlineMediaOf(slide, relByRid),
1859
- ...notes ? { notes } : {}
1999
+ ...notes ? { notes } : {},
2000
+ ...transitionSound ? { transitionSound } : {},
2001
+ ...slide._comments.length ? { comments: slide._comments } : {}
1860
2002
  };
1861
2003
  });
1862
2004
  } finally {
@@ -1874,10 +2016,9 @@ async function extractSlides(source, opts = {}) {
1874
2016
  ...notesMaster ? { notesMaster } : {}
1875
2017
  };
1876
2018
  }
1877
- //#endregion
1878
- //#region src/gen/opc/app.ts
2019
+ // src/gen/opc/app.ts
1879
2020
  /**
1880
- * ts-pptx: `docProps/app.xml`
2021
+ * pptx-ts: `docProps/app.xml`
1881
2022
  *
1882
2023
  * Emit the extended-properties part (application, slide/notes counts, heading
1883
2024
  * pairs, titles of parts, company).
@@ -1957,34 +2098,25 @@ function makeXmlApp(slides, company, presLayout) {
1957
2098
  raw(el("AppVersion", null, "16.0000", PROP))
1958
2099
  ], { closePrefix: INDENT_1 });
1959
2100
  }
1960
- //#endregion
1961
- //#region src/gen/opc/content-types.ts
2101
+ // src/gen/opc/content-types.ts
1962
2102
  /**
1963
- * ts-pptx: `[Content_Types].xml`
2103
+ * pptx-ts: `[Content_Types].xml`
1964
2104
  *
1965
2105
  * Emit the package content-types part: Default entries for the media extensions
1966
2106
  * actually used by the deck (plus the font default when faces are embedded) and
1967
2107
  * Override entries for every part the package skeleton writes.
1968
2108
  *
1969
2109
  * Entries that exist only because a construct family put a part in the package arrive as
1970
- * {@link ContentTypeContributions} — already resolved to data and grouped by where they land.
2110
+ * {@link ContentTypeContributions}, already resolved to data and grouped by where they land.
1971
2111
  * The order below is a property of the format, not of whoever assembled that list, so a deck
1972
2112
  * written by a program that links three families and one that links ten emit the same bytes.
1973
2113
  */
1974
2114
  /**
1975
2115
  * Some Override entries have always been emitted with a leading space. It is insignificant
1976
2116
  * whitespace, but it is in the bytes this library has shipped for years, so it is reproduced
1977
- * verbatim rather than normalized (see AGENTS.md "Byte identity": whitespace diffs are a STOP).
2117
+ * verbatim rather than normalized (see "Byte identity" in `docs/contributing/testing.md`: whitespace diffs fail the gate).
1978
2118
  */
1979
2119
  const LEADING_SPACE = { openPrefix: " " };
1980
- /** Nothing beyond the skeleton — the shape a deck written with no construct families contributes. */
1981
- const NO_CONTENT_TYPE_CONTRIBUTIONS = Object.freeze({
1982
- defaults: [],
1983
- presentation: [],
1984
- perSlide: [],
1985
- perLayout: [],
1986
- trailing: []
1987
- });
1988
2120
  function contentDefault(extension, contentType) {
1989
2121
  return voidEl("Default", {
1990
2122
  Extension: extension,
@@ -2007,16 +2139,15 @@ function contributedOverride(entry) {
2007
2139
  * @returns XML
2008
2140
  */
2009
2141
  function makeXmlContTypes(opts) {
2010
- const { slides, slideLayouts, masterSlide, hasCustomProps, embeddedFonts } = opts;
2011
- const contributions = opts.contributions ?? NO_CONTENT_TYPE_CONTRIBUTIONS;
2142
+ const { slides, slideLayouts, masterSlide, hasCustomProps, embeddedFonts, contributions } = opts;
2012
2143
  const parts = [contentDefault("xml", "application/xml"), contentDefault("rels", RELATIONSHIPS_CONTENT_TYPE)];
2013
2144
  const extnTypeMap = /* @__PURE__ */ new Map();
2014
2145
  const ctTargets = [];
2015
- (slides || []).forEach((s) => ctTargets.push(s));
2016
- (slideLayouts || []).forEach((l) => ctTargets.push(l));
2017
- if (masterSlide) ctTargets.push(masterSlide);
2146
+ slides.forEach((s) => ctTargets.push(s));
2147
+ slideLayouts.forEach((l) => ctTargets.push(l));
2148
+ ctTargets.push(masterSlide);
2018
2149
  ctTargets.forEach((target) => {
2019
- (target._relsMedia || []).forEach((rel) => {
2150
+ target._relsMedia.forEach((rel) => {
2020
2151
  if (rel.type === "online" || !rel.extn || !rel.type) return;
2021
2152
  const contentType = rel.type.startsWith("audio/") ? avContentType(rel.extn, "audio") : rel.type.startsWith("video/") ? avContentType(rel.extn, "video") : rel.type;
2022
2153
  if (!extnTypeMap.has(rel.extn)) extnTypeMap.set(rel.extn, contentType);
@@ -2030,7 +2161,7 @@ function makeXmlContTypes(opts) {
2030
2161
  extnTypeMap.set(entry.extension, entry.contentType);
2031
2162
  parts.push(contentDefault(entry.extension, entry.contentType));
2032
2163
  });
2033
- if ((embeddedFonts || []).some((font) => font.faces.some((face) => face.bytes))) parts.push(contentDefault(FONT_DATA_EXTENSION, FONT_DATA_CONTENT_TYPE));
2164
+ if (embeddedFonts.some((font) => font.faces.some((face) => face.bytes))) parts.push(contentDefault(FONT_DATA_EXTENSION, FONT_DATA_CONTENT_TYPE));
2034
2165
  parts.push(override(overrideName(PRESENTATION_PATH), PRESENTATION_MAIN_CONTENT_TYPE));
2035
2166
  contributions.presentation.forEach((entry) => parts.push(contributedOverride(entry)));
2036
2167
  parts.push(override(overrideName(SLIDE_MASTER_PATH), SLIDE_MASTER_CONTENT_TYPE));
@@ -2053,10 +2184,9 @@ function makeXmlContTypes(opts) {
2053
2184
  if (hasCustomProps) parts.push(override("/docProps/custom.xml", CUSTOM_PROPS_CONTENT_TYPE, LEADING_SPACE));
2054
2185
  return XML_DECL + "\r\n" + el("Types", { xmlns: OOXML_NS.ct }, parts.map(raw));
2055
2186
  }
2056
- //#endregion
2057
- //#region src/gen/opc/custom-props.ts
2187
+ // src/gen/opc/custom-props.ts
2058
2188
  /**
2059
- * ts-pptx: `docProps/custom.xml`
2189
+ * pptx-ts: `docProps/custom.xml`
2060
2190
  *
2061
2191
  * Emit the custom-properties part from caller-supplied name/value pairs (bool,
2062
2192
  * date, number and string value types).
@@ -2086,10 +2216,9 @@ function makeXmlCustomProperties(props) {
2086
2216
  }, raw(valueEl(value)))));
2087
2217
  return XML_DECL + "\r\n" + el("Properties", PROPS_NS, properties);
2088
2218
  }
2089
- //#endregion
2090
- //#region src/gen/opc/root-rels.ts
2219
+ // src/gen/opc/root-rels.ts
2091
2220
  /**
2092
- * ts-pptx: `_rels/.rels`
2221
+ * pptx-ts: `_rels/.rels`
2093
2222
  *
2094
2223
  * Emit the package root relationships (app/core/presentation, plus custom-props
2095
2224
  * when present).
@@ -2109,8 +2238,7 @@ function makeXmlRootRels(hasCustomProps) {
2109
2238
  if (hasCustomProps) rels.push(relationshipEl("rId4", CUSTOM_PROPS_REL, "docProps/custom.xml", INDENTED));
2110
2239
  return relationshipsPart(rels, { closePrefix: "\n " });
2111
2240
  }
2112
- //#endregion
2113
- //#region src/gen/pres/presentation-rels.ts
2241
+ // src/gen/pres/presentation-rels.ts
2114
2242
  /**
2115
2243
  * Creates `ppt/_rels/presentation.xml.rels`
2116
2244
  * @param {PresSlideInternal[]} slides - Presenation Slides
@@ -2124,16 +2252,16 @@ function makeXmlRootRels(hasCustomProps) {
2124
2252
  * theme1/tableStyles, then commentAuthors when the deck has any comment, then the embedded
2125
2253
  * fonts.
2126
2254
  *
2127
- * The rels writer and `makeXmlPresentation` both need these numbers — the presentation part
2128
- * names the notesMaster and each embedded face by `r:id` — and each derived them separately,
2255
+ * The rels writer and `makeXmlPresentation` both need these numbers (the presentation part
2256
+ * names the notesMaster and each embedded face by `r:id`), and each derived them separately,
2129
2257
  * one as `slides.length + 2` under a comment saying it copied the other's logic. Inserting a
2130
2258
  * relationship before `notesMaster` then broke the presentation's own reference with no
2131
2259
  * signal.
2132
2260
  * @param slides - the deck's slides, whose count and comments decide the layout
2133
2261
  */
2134
2262
  function presentationFixedRelIds(slides) {
2135
- const first = (slides?.length ?? 0) + 2;
2136
- const hasComments = (slides || []).some((slide) => (slide._comments || []).length > 0);
2263
+ const first = slides.length + 2;
2264
+ const hasComments = slides.some((slide) => slide._comments.length > 0);
2137
2265
  return {
2138
2266
  notesMaster: first,
2139
2267
  presProps: first + 1,
@@ -2145,7 +2273,7 @@ function presentationFixedRelIds(slides) {
2145
2273
  };
2146
2274
  }
2147
2275
  /**
2148
- * The first relationship id free for embedded-font rels — {@link presentationFixedRelIds}'
2276
+ * The first relationship id free for embedded-font rels: {@link presentationFixedRelIds}'
2149
2277
  * `fontStart`, kept as its own name because that is what the two font walks ask for.
2150
2278
  */
2151
2279
  function presentationFontRelStart(slides) {
@@ -2157,13 +2285,12 @@ function makeXmlPresentationRels(slides, embeddedFonts) {
2157
2285
  for (let idx = 1; idx <= slides.length; idx++) rels.push(relationshipEl(idx + 1, SLIDE_REL, targetFromPresentation(slidePath(idx))));
2158
2286
  rels.push(relationshipEl(fixed.notesMaster, NOTES_MASTER_REL, targetFromPresentation(NOTES_MASTER_PATH)), relationshipEl(fixed.presProps, PRES_PROPS_REL, "presProps.xml"), relationshipEl(fixed.viewProps, VIEW_PROPS_REL, "viewProps.xml"), relationshipEl(fixed.theme, THEME_REL, "theme/theme1.xml"), relationshipEl(fixed.tableStyles, TABLE_STYLES_REL, "tableStyles.xml"));
2159
2287
  if (fixed.commentAuthors !== null) rels.push(relationshipEl(fixed.commentAuthors, COMMENT_AUTHORS_REL, "commentAuthors.xml"));
2160
- for (const face of flattenEmbeddedFaces(embeddedFonts || [], presentationFontRelStart(slides))) rels.push(relationshipEl(face.rId, FONT_REL_TYPE, targetFromPresentation(fontPath(face.partIndex))));
2288
+ for (const face of flattenEmbeddedFaces(embeddedFonts, presentationFontRelStart(slides))) rels.push(relationshipEl(face.rId, FONT_REL_TYPE, targetFromPresentation(fontPath(face.partIndex))));
2161
2289
  return relationshipsPart(rels);
2162
2290
  }
2163
- //#endregion
2164
- //#region src/gen/pres/presentation.ts
2291
+ // src/gen/pres/presentation.ts
2165
2292
  /**
2166
- * ts-pptx: `ppt/presentation.xml` + presProps/viewProps
2293
+ * pptx-ts: `ppt/presentation.xml` + presProps/viewProps
2167
2294
  *
2168
2295
  * Emit the presentation part (masters/notes/slide id lists, sizes, embedded-font
2169
2296
  * list, default text styles, sections) and the small presProps/viewProps parts.
@@ -2191,7 +2318,7 @@ function sectionsExtLst(sections) {
2191
2318
  * @return {string} XML
2192
2319
  */
2193
2320
  function makeXmlPresentation(pres) {
2194
- const openTag = `<p:presentation xmlns:a="${OOXML_NS.a}" xmlns:r="${OOXML_NS.r}" xmlns:p="${OOXML_NS.p}" ${pres.rtlMode ? "rtl=\"1\"" : ""} ${(pres.embeddedFonts || []).some((font) => font.faces.some((face) => face.bytes)) ? "embedTrueTypeFonts=\"1\" saveSubsetFonts=\"0\"" : "saveSubsetFonts=\"1\""} autoCompressPictures="0"${pres.firstSlideNum !== 1 ? ` firstSlideNum="${pres.firstSlideNum}"` : ""}>`;
2321
+ const openTag = `<p:presentation xmlns:a="${OOXML_NS.a}" xmlns:r="${OOXML_NS.r}" xmlns:p="${OOXML_NS.p}" ${pres.rtlMode ? "rtl=\"1\"" : ""} ${pres.embeddedFonts.some((font) => font.faces.some((face) => face.bytes)) ? "embedTrueTypeFonts=\"1\" saveSubsetFonts=\"0\"" : "saveSubsetFonts=\"1\""} autoCompressPictures="0"${pres.firstSlideNum !== 1 ? ` firstSlideNum="${pres.firstSlideNum}"` : ""}>`;
2195
2322
  const sldMasterIdLst = el("p:sldMasterIdLst", null, raw(voidEl("p:sldMasterId", {
2196
2323
  id: MIN_SLIDE_MASTER_ID,
2197
2324
  "r:id": "rId1"
@@ -2211,12 +2338,12 @@ function makeXmlPresentation(pres) {
2211
2338
  cx: pres.presLayout.height,
2212
2339
  cy: pres.presLayout.width
2213
2340
  });
2214
- const fonts = pres.embeddedFonts || [];
2341
+ const fonts = pres.embeddedFonts;
2215
2342
  const flat = flattenEmbeddedFaces(fonts, presentationFontRelStart(pres.slides));
2216
2343
  const rIdOf = new Map(flat.map((face) => [`${face.fontIndex}:${face.slot}`, face.rId]));
2217
2344
  const embeddedFontLst = serializeEmbeddedFontLst(fonts, (fontIndex, slot) => rIdOf.get(`${fontIndex}:${slot}`));
2218
2345
  const defaultTextStyle = el("p:defaultTextStyle", null, LEVEL_MARGINS_EMU.map((marL, i) => raw(defaultTextStyleLevel(i + 1, marL))));
2219
- const extLst = pres.sections && pres.sections.length > 0 ? sectionsExtLst(pres.sections) : "";
2346
+ const extLst = pres.sections.length > 0 ? sectionsExtLst(pres.sections) : "";
2220
2347
  return XML_DECL + "\r\n" + openTag + sldMasterIdLst + notesMasterIdLst + sldIdLst + sldSz + notesSz + embeddedFontLst + defaultTextStyle + extLst + "</p:presentation>";
2221
2348
  }
2222
2349
  /**
@@ -2262,23 +2389,9 @@ function makeXmlViewProps() {
2262
2389
  }))
2263
2390
  ]);
2264
2391
  }
2265
- //#endregion
2266
- //#region src/gen/pres/table-styles.ts
2267
- /**
2268
- * Create `ppt/tableStyles.xml`
2269
- * @see: http://openxmldeveloper.org/discussions/formats/f/13/p/2398/8107.aspx
2270
- * @return {string} XML
2271
- */
2272
- function makeXmlTableStyles() {
2273
- return XML_DECL + "\r\n" + voidEl("a:tblStyleLst", {
2274
- "xmlns:a": OOXML_NS.a,
2275
- def: "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}"
2276
- });
2277
- }
2278
- //#endregion
2279
- //#region src/gen/slide/layout.ts
2392
+ // src/gen/slide/layout.ts
2280
2393
  /**
2281
- * ts-pptx: slide-layout part
2394
+ * pptx-ts: slide-layout part
2282
2395
  *
2283
2396
  * Emit a slide layout (`ppt/slideLayouts/slideLayoutN.xml`) from a layout object.
2284
2397
  */
@@ -2297,10 +2410,9 @@ function makeXmlLayout(layout, renderers) {
2297
2410
  childPrefix: "\n "
2298
2411
  });
2299
2412
  }
2300
- //#endregion
2301
- //#region src/gen/slide/master.ts
2413
+ // src/gen/slide/master.ts
2302
2414
  /**
2303
- * ts-pptx: slide-master parts
2415
+ * pptx-ts: slide-master parts
2304
2416
  *
2305
2417
  * The slide master (`slideMaster1.xml`) and its `.rels`, plus the master text
2306
2418
  * styles (`<p:txStyles>`): per-level default values mirroring the built-in
@@ -2436,6 +2548,7 @@ function masterAlignAttr(align) {
2436
2548
  /** Build the bullet element for a master level: caller override wins over the level default. */
2437
2549
  function masterBulletXml(bulletOverride, base) {
2438
2550
  if (bulletOverride === false) return voidEl("a:buNone");
2551
+ if (bulletOverride === true && (base === "none" || base === void 0)) return masterBulletXml({}, base);
2439
2552
  if (bulletOverride && typeof bulletOverride === "object") {
2440
2553
  const font = bulletOverride.fontFace ? voidEl("a:buFont", { typeface: bulletOverride.fontFace }) : "";
2441
2554
  if (bulletOverride.type === "number") {
@@ -2534,7 +2647,7 @@ function makeXmlMasterTxStyles(textStyles) {
2534
2647
  * The built-in Office master's `<p:txStyles>`, used verbatim when `defineSlideMaster({ textStyles })`
2535
2648
  * was never called. Deliberately NOT built via `masterLevelXml`: a configured title level always
2536
2649
  * carries `marL="0"` (from `MASTER_TITLE_DEFAULT.marL`), but the true unconfigured default titleStyle
2537
- * has no `marL`/`indent` attribute at all — a pre-existing asymmetry between the two paths, preserved
2650
+ * has no `marL`/`indent` attribute at all: a pre-existing asymmetry between the two paths, preserved
2538
2651
  * exactly rather than unified.
2539
2652
  */
2540
2653
  function makeXmlMasterDefaultTxStyles() {
@@ -2624,8 +2737,7 @@ function makeXmlMasterRel(masterSlide, slideLayouts) {
2624
2737
  });
2625
2738
  return slideObjectRelationsToXml(masterSlide, defaultRels, SLIDE_MASTER_PATH);
2626
2739
  }
2627
- //#endregion
2628
- //#region src/package/parts/shared.ts
2740
+ // src/package/parts/shared.ts
2629
2741
  /**
2630
2742
  * The contributors in emission order. Sorting here rather than trusting the caller's array is the
2631
2743
  * whole point of {@link PartContributor.order}; the sort is stable, so two families that share a
@@ -2636,7 +2748,7 @@ function orderedContributors(contributors) {
2636
2748
  }
2637
2749
  /**
2638
2750
  * Ask every contributor for its content-type entries and flatten them into the slot-keyed data
2639
- * `makeXmlContTypes` emits. Takes the contributors already ordered — within a slot the entries
2751
+ * `makeXmlContTypes` emits. Takes the contributors already ordered; within a slot the entries
2640
2752
  * come out in the order they are collected here.
2641
2753
  */
2642
2754
  function collectContentTypes(contributors, pres) {
@@ -2668,15 +2780,14 @@ function collectContentTypes(contributors, pres) {
2668
2780
  trailing
2669
2781
  };
2670
2782
  }
2671
- //#endregion
2672
- //#region src/package/assemble.ts
2783
+ // src/package/assemble.ts
2673
2784
  /**
2674
2785
  * Package assembly: turn an authored presentation's internal state into the full set of
2675
2786
  * OOXML package parts and hand the bytes to the ZIP writer. This is the write-side
2676
- * "packaging" layer — `[Content_Types].xml`, the `_rels` graph, docProps, theme, the
2677
- * per-slide/layout/master parts, and media rels — split out of the authoring class
2678
- * (`TsPptx`) so that class stays a façade over slide authoring. The parts a construct
2679
- * family adds on top of that skeleton — charts, comments, notes — come from the part
2787
+ * "packaging" layer (`[Content_Types].xml`, the `_rels` graph, docProps, theme, the
2788
+ * per-slide/layout/master parts, and media rels), split out of the authoring class
2789
+ * (`PptxTs`) so that class stays a façade over slide authoring. The parts a construct
2790
+ * family adds on top of that skeleton (charts, comments, notes) come from the part
2680
2791
  * contributors it is handed, so nothing here names a family.
2681
2792
  *
2682
2793
  * The entry point `writePackage` takes a structural {@link PackageSource} that the
@@ -2717,7 +2828,7 @@ const ALREADY_COMPRESSED_MEDIA_EXTN = /* @__PURE__ */ new Set([
2717
2828
  "oga"
2718
2829
  ]);
2719
2830
  /**
2720
- * Extensions whose payload is itself a ZIP archive — the embedded OPC packages an OLE object can
2831
+ * Extensions whose payload is itself a ZIP archive: the embedded OPC packages an OLE object can
2721
2832
  * carry (`addOleObject`). Deflating a zip inside a zip buys nothing, so these are STOREd like the
2722
2833
  * already-compressed media above. No media/image rel ever uses one of these extensions, so decks
2723
2834
  * without an OLE object are unaffected.
@@ -2731,61 +2842,6 @@ const ZIP_CONTAINER_EXTN = /* @__PURE__ */ new Set([
2731
2842
  "pptm"
2732
2843
  ]);
2733
2844
  /**
2734
- * Each slide's registered transition sound.
2735
- *
2736
- * Kept per slide, never on the transition: one transition object can be assigned to several slides,
2737
- * and a relationship id means something only inside the slide part that declares it. Kept across
2738
- * writes as well, so a deck written twice registers each sound once. An entry is reused only while
2739
- * the slide still holds its relationship and still has the same sound assigned.
2740
- */
2741
- const registeredTransitionSounds = /* @__PURE__ */ new WeakMap();
2742
- /**
2743
- * Register an audio media part + relationship for each slide-transition start sound
2744
- * (`transition.sound` with `data`/`path`), and return the relationship id each slide's
2745
- * `p:sndAc/p:snd r:embed` refers to. Runs before media encoding so the bytes are loaded. Re-export
2746
- * is safe: a slide whose sound is already registered reuses that relationship. The stop-previous
2747
- * form (`sound.stopPrevious`) needs no part and is skipped.
2748
- *
2749
- * Registered here, at write time, rather than when `slide.transition` is assigned. A slide's rel
2750
- * ids and media part names are handed out in registration order, and a transition is often set
2751
- * before the slide's pictures and media are added; registering the sound then would move every
2752
- * later rel id and part name on the slide.
2753
- */
2754
- function registerTransitionSounds(slides) {
2755
- const soundRIds = /* @__PURE__ */ new Map();
2756
- slides.forEach((slide) => {
2757
- const transition = slide.transition;
2758
- if (!transition || !TRANSITION_TYPES.includes(transition.type)) return;
2759
- const sound = transition.sound;
2760
- if (!sound || sound.stopPrevious) return;
2761
- if (!sound.data && !sound.path) return;
2762
- const registered = registeredTransitionSounds.get(slide);
2763
- if (registered && slide._relsMedia.includes(registered.rel) && registered.data === sound.data && registered.path === sound.path) {
2764
- soundRIds.set(slide, registered.rel.rId);
2765
- return;
2766
- }
2767
- const dataMime = dataUriMediaType(sound.data ?? "");
2768
- const pathFile = sound.path ? (sound.path.split("/").pop() ?? "").split("?")[0] ?? "" : "";
2769
- const extn = dataMime?.startsWith("audio/") ? audioExtensionForSubtype(dataMime.slice(6)) : (pathFile.split(".").pop() ?? "wav").toLowerCase();
2770
- const rId = getNewRelId(slide);
2771
- const rel = pushMediaRel(slide, {
2772
- kind: "audio",
2773
- extn,
2774
- type: `audio/${extn}`,
2775
- path: sound.path,
2776
- data: sound.data,
2777
- rId
2778
- });
2779
- registeredTransitionSounds.set(slide, {
2780
- rel,
2781
- data: sound.data,
2782
- path: sound.path
2783
- });
2784
- soundRIds.set(slide, rId);
2785
- });
2786
- return soundRIds;
2787
- }
2788
- /**
2789
2845
  * Write the media parts one target's rels point at. Images are how every deck carries a picture,
2790
2846
  * so this stays on the core path; the chart half of what used to be one function is a part
2791
2847
  * contributor (`parts/chart.ts`) and runs just before it, per target, so part order is unchanged.
@@ -2804,8 +2860,7 @@ function createMediaParts(slide, zip, written) {
2804
2860
  if (rel.type !== "online" && rel.type !== "hyperlink") {
2805
2861
  const path = rel.Target.replace("..", "ppt");
2806
2862
  if (written.has(path)) return;
2807
- const data = rel.data && typeof rel.data === "string" ? rel.data : "";
2808
- const bytes = decodeBase64ToBytes(data);
2863
+ const bytes = decodeBase64ToBytes(rel.data);
2809
2864
  if (!bytes) return;
2810
2865
  const extn = (rel.extn || rel.Target.split(".").pop() || "").toLowerCase();
2811
2866
  zip.add(path, bytes, { store: ALREADY_COMPRESSED_MEDIA_EXTN.has(extn) || ZIP_CONTAINER_EXTN.has(extn) });
@@ -2819,7 +2874,7 @@ function createMediaParts(slide, zip, written) {
2819
2874
  * media de-dup, chart-part-id assignment, and measured-fit passes before the synchronous
2820
2875
  * XML pass reads slide state. The bytes are the same the ZIP writer would compress; splitting the
2821
2876
  * assembly from the zip lets the byte-identity harness (and a future parts API) read parts
2822
- * directly. Only `onMediaError` is meaningful here — compression/output shape are zip concerns
2877
+ * directly. Only `onMediaError` is meaningful here; compression/output shape are zip concerns
2823
2878
  * handled by {@link zipPackageParts}.
2824
2879
  */
2825
2880
  async function buildPackageParts(source, props) {
@@ -2838,16 +2893,16 @@ async function buildPackageParts(source, props) {
2838
2893
  ];
2839
2894
  return await encodeMediaForTargets(mediaTargets, source.runtime, onMediaError).then(async () => {
2840
2895
  const canonicalMediaTargets = /* @__PURE__ */ new Map();
2841
- for (const target of mediaTargets) for (const rel of target._relsMedia || []) {
2842
- if (rel.type === "online" || rel.type === "hyperlink" || typeof rel.data !== "string" || !rel.data) continue;
2896
+ for (const target of mediaTargets) for (const rel of target._relsMedia) {
2897
+ if (rel.type === "online" || rel.type === "hyperlink" || !rel.data) continue;
2843
2898
  if (rel.oleRelType) continue;
2844
- const key = (rel.extn || "") + "\0" + rel.data;
2899
+ const key = rel.extn + "\0" + rel.data;
2845
2900
  const canonical = canonicalMediaTargets.get(key);
2846
2901
  if (canonical) rel.Target = canonical;
2847
2902
  else canonicalMediaTargets.set(key, rel.Target);
2848
2903
  }
2849
2904
  let chartPartIdx = 0;
2850
- for (const target of mediaTargets) for (const rel of target._relsChart || []) {
2905
+ for (const target of mediaTargets) for (const rel of target._relsChart) {
2851
2906
  const chartId = ++chartPartIdx;
2852
2907
  rel.globalId = chartId;
2853
2908
  const { fileName, target } = chartPartNames(chartId, rel.isChartEx);
@@ -2876,7 +2931,7 @@ async function buildPackageParts(source, props) {
2876
2931
  zip.add("ppt/theme/theme2.xml", themeXml);
2877
2932
  zip.add(PRESENTATION_PATH, makeXmlPresentation(pres));
2878
2933
  zip.add("ppt/presProps.xml", makeXmlPresProps());
2879
- zip.add("ppt/tableStyles.xml", makeXmlTableStyles());
2934
+ zip.add("ppt/tableStyles.xml", makeXmlTableStyles(contributors.flatMap((contributor) => contributor.parts?.tableStyles?.(pres) ?? [])));
2880
2935
  zip.add("ppt/viewProps.xml", makeXmlViewProps());
2881
2936
  pres.slideLayouts.forEach((layout, idx) => {
2882
2937
  zip.add(slideLayoutPath(idx + 1), makeXmlLayout(layout, source.renderers));
@@ -2909,7 +2964,7 @@ async function buildPackageParts(source, props) {
2909
2964
  }
2910
2965
  /**
2911
2966
  * Zip an ordered list of package parts into the output shape `props.outputType` selects. Re-adds
2912
- * each part to a fresh {@link ZipWriter} in order — preserving each part's `store` hint — so the
2967
+ * each part to a fresh {@link ZipWriter} in order (preserving each part's `store` hint), so the
2913
2968
  * archive is byte-identical to assembling straight into one writer. Compression and output type
2914
2969
  * are the only zip-level knobs; part assembly happened in {@link buildPackageParts}.
2915
2970
  */
@@ -2928,23 +2983,22 @@ async function zipPackageParts(parts, props) {
2928
2983
  async function writePackage(source, props) {
2929
2984
  return await zipPackageParts(await buildPackageParts(source, props), props);
2930
2985
  }
2931
- //#endregion
2932
- //#region src/presentation.ts
2986
+ // src/presentation.ts
2933
2987
  /**
2934
- * ts-pptx: the presentation core
2988
+ * pptx-ts: the presentation core
2935
2989
  *
2936
2990
  * Home of {@link PresentationCore}, the class every entry point subclasses with a runtime
2937
2991
  * adapter (`index.ts`, `node.ts`, `browser.ts`). See the class doc below for the layout of
2938
2992
  * its body and where package assembly actually happens.
2939
2993
  *
2940
- * Units: this library takes user coordinates in inches and converts to EMU on output — see
2994
+ * Units: this library takes user coordinates in inches and converts to EMU on output. See
2941
2995
  * `units.ts` for the conversions and `STANDARD_LAYOUTS` for the built-in slide sizes.
2942
2996
  *
2943
2997
  * Derived from PptxGenJS (C) 2015-present Brent Ely (MIT), which in turn took some code from
2944
2998
  * the OfficeGen project (Copyright 2013 Ziv Barber). This project is likewise MIT; the full
2945
2999
  * license text is in `LICENSE` at the repository root.
2946
3000
  */
2947
- const VERSION = "4.0.1";
3001
+ const VERSION = "4.2.0";
2948
3002
  function standardLayoutToPresLayout(layout) {
2949
3003
  return {
2950
3004
  name: layout.name,
@@ -2973,8 +3027,8 @@ function layoutSideInches(value, side) {
2973
3027
  return clampRangedInput(Number(value), 1, 56, "layout/size-out-of-range", `defineLayout ${side} (inches)`, "coord/non-finite");
2974
3028
  }
2975
3029
  /**
2976
- * Main presentation class and package export flow — the public entry point consumers
2977
- * instantiate (`new TsPptx()`). Owns presentation-level state and metadata, collects
3030
+ * Main presentation class and package export flow: the public entry point consumers
3031
+ * instantiate (`new PptxTs()`). Owns presentation-level state and metadata, collects
2978
3032
  * slides, and drives `write`/`writeFile`/`stream`. The actual OOXML string building is
2979
3033
  * delegated to the `gen-*` modules; runtime file/stream output goes through the injected
2980
3034
  * `RuntimeAdapter`. Enums (`AlignH`, `ChartType`, `ShapeType`, …) are imported from the
@@ -2983,12 +3037,12 @@ function layoutSideInches(value, side) {
2983
3037
  * Rough layout of the class body:
2984
3038
  * - Metadata accessors layout / author / company / title / theme / sections getters+setters
2985
3039
  * - Private slide helpers addNewSlide, getSlide, setSlideNumber
2986
- * - Public export methods stream / write / writeFile — delegate packaging to `writePackage`
3040
+ * - Public export methods stream / write / writeFile; delegate packaging to `writePackage`
2987
3041
  * - Public authoring methods addSlide / defineLayout / defineSlideMaster / defineTableStyle
2988
3042
  *
2989
3043
  * Package assembly (`[Content_Types].xml`, the rels graph, per-part XML, the ZIP) lives in
2990
3044
  * `package/assemble.ts`; this class provides the deck state via {@link PresentationCore.packageSource}.
2991
- * The live-DOM `tableToSlides()` is NOT here — it is added by the browser entry subclass
3045
+ * The live-DOM `tableToSlides()` is NOT here; it is added by the browser entry subclass
2992
3046
  * (`browser.ts`) so it stays out of the Node build and out of the core chunk.
2993
3047
  */
2994
3048
  var PresentationCore = class {
@@ -3017,7 +3071,7 @@ var PresentationCore = class {
3017
3071
  return this._layout;
3018
3072
  }
3019
3073
  /**
3020
- * ts-pptx Library Version
3074
+ * pptx-ts Library Version
3021
3075
  */
3022
3076
  _version = VERSION;
3023
3077
  get version() {
@@ -3124,7 +3178,7 @@ var PresentationCore = class {
3124
3178
  /**
3125
3179
  * slide layout definition objects, used for generating slide layout files.
3126
3180
  * `protected` (not `private`) so the browser entry subclass can resolve
3127
- * `tableToSlides({ masterTitle })` against them — that method lives on the
3181
+ * `tableToSlides({ masterTitle })` against them. That method lives on the
3128
3182
  * browser build only (it reads a live DOM), see `browser.ts`.
3129
3183
  */
3130
3184
  _slideLayouts;
@@ -3153,7 +3207,7 @@ var PresentationCore = class {
3153
3207
  LAYOUTS;
3154
3208
  /**
3155
3209
  * The built-in default layout, or a throw. `LAYOUTS` is a string-keyed map, so the lookup is
3156
- * typed as possibly-missing at every call site even though the constructor registers it — and
3210
+ * typed as possibly-missing at every call site even though the constructor registers it, and
3157
3211
  * the two callers that need it were spelling the same guard and the same message.
3158
3212
  */
3159
3213
  #requireDefaultLayout() {
@@ -3191,11 +3245,11 @@ var PresentationCore = class {
3191
3245
  LAYOUT_16x10: standardLayoutToPresLayout(STANDARD_LAYOUTS.LAYOUT_16x10),
3192
3246
  LAYOUT_WIDE: standardLayoutToPresLayout(STANDARD_LAYOUTS.LAYOUT_WIDE)
3193
3247
  };
3194
- this._author = "ts-pptx";
3195
- this._company = "ts-pptx";
3248
+ this._author = "pptx-ts";
3249
+ this._company = "pptx-ts";
3196
3250
  this._revision = "1";
3197
- this._subject = "ts-pptx Presentation";
3198
- this._title = "ts-pptx Presentation";
3251
+ this._subject = "pptx-ts Presentation";
3252
+ this._title = "pptx-ts Presentation";
3199
3253
  const defLayout = this.#requireDefaultLayout();
3200
3254
  this._presLayout = {
3201
3255
  name: defLayout.name,
@@ -3255,7 +3309,7 @@ var PresentationCore = class {
3255
3309
  */
3256
3310
  getSections = () => this._sections;
3257
3311
  /**
3258
- * Enables the `Slide` class to set ts-pptx [Presentation] master/layout slidenumbers
3312
+ * Enables the `Slide` class to set pptx-ts [Presentation] master/layout slidenumbers
3259
3313
  * @param {SlideNumberProps} slideNum - slide number config
3260
3314
  */
3261
3315
  setSlideNumber = (slideNum) => {
@@ -3281,14 +3335,14 @@ var PresentationCore = class {
3281
3335
  * Author + serialize this presentation's slides as injectable descriptors,
3282
3336
  * WITHOUT producing a `.pptx` package. Runs the same media-encode, placeholder
3283
3337
  * backfill, and measured-fit passes `write()` uses, then serializes each slide
3284
- * body and resolves its image media to decoded bytes — so a loaded deck can
3338
+ * body and resolves its image media to decoded bytes, so a loaded deck can
3285
3339
  * splice the slides in via `Presentation.appendSlides()` (see `pptx-ts/read`)
3286
3340
  * while keeping its own masters/layouts/theme byte-identical.
3287
3341
  *
3288
3342
  * Returns the deck's slide size (EMU, for the destination size check) and one
3289
3343
  * descriptor per authored slide. Charts, audio/video media, online media, and
3290
3344
  * internal slide-to-slide hyperlinks are surfaced as descriptors alongside the
3291
- * serialized body — this method does not resolve them into a package; the append
3345
+ * serialized body. This method does not resolve them into a package; the append
3292
3346
  * path consumes each descriptor to reserve the parts and rebuild the rel graph.
3293
3347
  */
3294
3348
  extractSlides = async (opts = {}) => extractSlides({
@@ -3391,11 +3445,11 @@ var PresentationCore = class {
3391
3445
  }
3392
3446
  /**
3393
3447
  * Measure how tall text wraps at a given width, using the **same** calibrated wrap
3394
- * model the export-time autofit bake uses — so a layout-time prediction matches the
3448
+ * model the export-time autofit bake uses, so a layout-time prediction matches the
3395
3449
  * value `fit:'shrink'`/`'resize'` would bake. Synchronous: register the face's metrics
3396
- * first with {@link TsPptx.registerFontMetrics} (lookup is sync).
3450
+ * first with {@link PptxTs.registerFontMetrics} (lookup is sync).
3397
3451
  *
3398
- * Lets a consumer size its own geometry before export — grow a card to fit its text,
3452
+ * Lets a consumer size its own geometry before export: grow a card to fit its text,
3399
3453
  * reflow a grid, or detect overflow at layout time. Heights err **tall** (conservative),
3400
3454
  * so the returned height is ≥ what PowerPoint/LibreOffice render.
3401
3455
  * @param {string | TextProps[]} text - a string or run array (per-run options override the defaults in `opts`)
@@ -3410,7 +3464,7 @@ var PresentationCore = class {
3410
3464
  /**
3411
3465
  * Convenience overflow check: `true` if `text` does not fit a box of inner size
3412
3466
  * `wIn`×`hIn` (inches) at full size. Because the model errs tall, this is a
3413
- * **conservative** (slightly over-reporting) check — appropriate for a build-time
3467
+ * **conservative** (slightly over-reporting) check, appropriate for a build-time
3414
3468
  * warning, not a hard gate. An unmeasurable (unnamed theme-default) face reports
3415
3469
  * `false` (no overflow) so the linter does not false-positive on faces it cannot measure.
3416
3470
  * @param {string | TextProps[]} text - a string or run array
@@ -3422,13 +3476,13 @@ var PresentationCore = class {
3422
3476
  }
3423
3477
  /**
3424
3478
  * Compute the per-cell geometry of a table laid out at `opts.x`/`y`/`w`, without
3425
- * adding it to a slide — so a consumer can place images or shapes precisely over
3479
+ * adding it to a slide, so a consumer can place images or shapes precisely over
3426
3480
  * cells. Column widths (cell `x`/`w`) are exact, derived from the same logic the
3427
3481
  * writer uses. Row heights (`y`/`h`) are exact when pinned by `rowH` (array or
3428
3482
  * scalar) or table `h`; an auto-height row is estimated with the same conservative
3429
- * (tall) text model as {@link TsPptx.measureText} and flagged `heightExact:false`
3430
- * (register the cell font via {@link TsPptx.registerFontMetrics} for an exact
3431
- * estimate). Geometry is for a single, un-paginated table — `autoPage` paging is
3483
+ * (tall) text model as {@link PptxTs.measureText} and flagged `heightExact:false`
3484
+ * (register the cell font via {@link PptxTs.registerFontMetrics} for an exact
3485
+ * estimate). Geometry is for a single, un-paginated table; `autoPage` paging is
3432
3486
  * not modeled.
3433
3487
  * @param {TableRow[]} rows - the same `rows` passed to `slide.addTable`
3434
3488
  * @param {TableProps} opts - the same table options (`x`, `y`, `w`, `colW`, `rowH`, `h`, …)
@@ -3458,7 +3512,9 @@ var PresentationCore = class {
3458
3512
  /**
3459
3513
  * Export the current Presentation as ZIP content with the selected type
3460
3514
  * @param {WriteProps} props output properties
3461
- * @returns {Promise<string | ArrayBuffer | Blob | Uint8Array>} file content in selected type
3515
+ * @returns file content in the selected type: a `Blob` when `outputType` is left out, else
3516
+ * the {@link WriteOutputMap} entry for it
3517
+ * @example const bytes = await pptx.write({ outputType: 'uint8array' }) // Uint8Array, no cast
3462
3518
  */
3463
3519
  async write(props) {
3464
3520
  return await writePackage(this.packageSource(), pickDefined(props ?? {}, [
@@ -3468,7 +3524,7 @@ var PresentationCore = class {
3468
3524
  ]));
3469
3525
  }
3470
3526
  /**
3471
- * Assemble the current Presentation into its OOXML package parts WITHOUT zipping — the raw
3527
+ * Assemble the current Presentation into its OOXML package parts WITHOUT zipping: the raw
3472
3528
  * `path → bytes` contents `write()` would compress into the `.pptx`. Use this to stream parts
3473
3529
  * into a custom container, inspect individual parts, or feed a pipeline that does its own
3474
3530
  * archiving. To produce a real `.pptx` file, use {@link write} / {@link writeFile} instead.
@@ -3647,10 +3703,9 @@ var PresentationCore = class {
3647
3703
  if (newLayout._slideNumberProps && !this._masterSlide._slideNumberProps) this._masterSlide._slideNumberProps = newLayout._slideNumberProps;
3648
3704
  }
3649
3705
  };
3650
- //#endregion
3651
- //#region src/families/core.ts
3706
+ // src/families/core.ts
3652
3707
  /**
3653
- * ts-pptx: the construct families every presentation carries
3708
+ * pptx-ts: the construct families every presentation carries
3654
3709
  *
3655
3710
  * The core tier. A composed presentation gets these whether it asks for them or not, so
3656
3711
  * `createPresentation()` with no families still authors text, shapes, images, groups and speaker
@@ -3669,10 +3724,9 @@ const CORE_CONSTRUCT_FAMILIES = [
3669
3724
  groupFamily,
3670
3725
  notesFamily
3671
3726
  ];
3672
- //#endregion
3673
- //#region src/entry-compose.ts
3727
+ // src/entry-compose.ts
3674
3728
  /**
3675
- * ts-pptx: composing a presentation from the construct families it actually needs
3729
+ * pptx-ts: composing a presentation from the construct families it actually needs
3676
3730
  *
3677
3731
  * The body behind each entry's `createPresentation`, and the types that describe what comes back.
3678
3732
  * It lives beside `entry-surface.ts` for the same reason that does: `index.ts`, `node.ts` and
@@ -3683,7 +3737,7 @@ const CORE_CONSTRUCT_FAMILIES = [
3683
3737
  * three-condition treatment `.` gets, so three more source entries, three more `dist` files, three
3684
3738
  * more budget rows and a second place for the entries to diverge -- and it would buy nothing that
3685
3739
  * export-level shaking does not already give. `pptx-ts` is an intermediate module in a consumer's
3686
- * graph: a program that imports only `createPresentation` drops the `TsPptx` class beside it and,
3740
+ * graph: a program that imports only `createPresentation` drops the `PptxTs` class beside it and,
3687
3741
  * with it, every family that class names.
3688
3742
  */
3689
3743
  /**
@@ -3697,13 +3751,12 @@ const CORE_CONSTRUCT_FAMILIES = [
3697
3751
  function composePresentation(runtime, opts) {
3698
3752
  return new PresentationCore(runtime, [...CORE_CONSTRUCT_FAMILIES, ...opts?.use ?? []]);
3699
3753
  }
3700
- //#endregion
3701
- //#region src/entry-families.ts
3754
+ // src/entry-families.ts
3702
3755
  /**
3703
- * ts-pptx: the construct families the three main entries are composed with
3756
+ * pptx-ts: the construct families the three main entries are composed with
3704
3757
  *
3705
3758
  * The full authoring surface, as a list: `index.ts`, `node.ts` and `browser.ts` each compose every
3706
- * family, so `TsPptx` authors everything it ever has; the browser entry adds the table family's
3759
+ * family, so `PptxTs` authors everything it ever has; the browser entry adds the table family's
3707
3760
  * live-DOM half on top, because that one needs a `document` to read. The families a *composed*
3708
3761
  * presentation gets without asking are the shorter list in `families/core.ts`.
3709
3762
  *
@@ -3737,14 +3790,13 @@ const ALL_CONSTRUCT_FAMILIES = /* @__PURE__ */ Object.freeze([
3737
3790
  tableFamily,
3738
3791
  zoomFamily
3739
3792
  ]);
3740
- //#endregion
3741
- //#region src/runtime/fetch-media.ts
3793
+ // src/runtime/fetch-media.ts
3742
3794
  /**
3743
3795
  * The parts of a runtime adapter that are not runtime-specific.
3744
3796
  *
3745
3797
  * `fetch`, `btoa` and `TextEncoder` exist in Node, in every browser, and in the edge/worker
3746
3798
  * runtimes the neutral adapter serves, so the remote-load paths were the same code in all three
3747
- * adapters — byte-identical in two of them and an inline branch in the third. What is genuinely
3799
+ * adapters: byte-identical in two of them and an inline branch in the third. What is genuinely
3748
3800
  * per-runtime stays in each adapter: `node:fs`, `FileReader`, the canvas rasterizer, the
3749
3801
  * `<a download>` writer.
3750
3802
  *
@@ -3753,13 +3805,13 @@ const ALL_CONSTRUCT_FAMILIES = /* @__PURE__ */ Object.freeze([
3753
3805
  /**
3754
3806
  * Whether `source` names something to `fetch` rather than something to read from a filesystem.
3755
3807
  *
3756
- * A real scheme test, not `source.startsWith('http')`. That prefix matched a *relative path* —
3757
- * `httpdocs/logo.png`, `http-icons/x.svg` — and sent it to `fetch`, which then failed with a
3808
+ * A real scheme test, not `source.startsWith('http')`. That prefix matched a *relative path*
3809
+ * (`httpdocs/logo.png`, `http-icons/x.svg`) and sent it to `fetch`, which then failed with a
3758
3810
  * fetch error naming a file that exists on disk. It was also case-sensitive, so
3759
3811
  * `HTTPS://example.com/f.ttf` missed and went to `fs.readFile`.
3760
3812
  *
3761
3813
  * Only `http:` and `https:` count. A `file:` or `data:` URL falls through to the adapter's
3762
- * local branch, where it fails — the same as before this was tightened, and deliberately left
3814
+ * local branch, where it fails, the same as before this was tightened, and deliberately left
3763
3815
  * that way: making either work is a feature with its own decisions (what a `file:` URL means
3764
3816
  * off Node, whether a `data:` URL should bypass loading entirely), not a side effect of fixing
3765
3817
  * a prefix test.
@@ -3775,7 +3827,7 @@ async function fetchFontBytes(source) {
3775
3827
  return new Uint8Array(await response.arrayBuffer());
3776
3828
  }
3777
3829
  /**
3778
- * Fetch a media file as raw base64 (no `data:` prefix) — see {@link RuntimeAdapter.loadMedia}
3830
+ * Fetch a media file as raw base64 (no `data:` prefix). See {@link RuntimeAdapter.loadMedia}
3779
3831
  * for why both encodings are accepted.
3780
3832
  *
3781
3833
  * Encodes through `bytesToBase64` rather than `Buffer`, so the same function serves a runtime
@@ -3800,16 +3852,15 @@ async function placeholderSvgPreview(rel) {
3800
3852
  rel.data = IMG_SVG_PLACEHOLDER;
3801
3853
  return null;
3802
3854
  }
3803
- //#endregion
3804
- //#region src/clip.ts
3855
+ // src/clip.ts
3805
3856
  /**
3806
- * ts-pptx: named clip silhouettes for images
3857
+ * pptx-ts: named clip silhouettes for images
3807
3858
  *
3808
- * A `ClipShape` is declarative data — a named silhouette plus its options — and {@link clipPath}
3859
+ * A `ClipShape` is declarative data (a named silhouette plus its options), and {@link clipPath}
3809
3860
  * resolves it to the freeform `points` path `addImage` emits as the `<a:custGeom>` clip mask on
3810
3861
  * the `<p:pic>`. Paired with `sizing: { type: 'cover' }` (an `<a:srcRect>` crop) it reproduces
3811
- * what a PowerPoint *picture placeholder* does — a layout `custGeom` clipping an inherited
3812
- * blipFill — but standalone, with no placeholder involved.
3862
+ * what a PowerPoint *picture placeholder* does (a layout `custGeom` clipping an inherited
3863
+ * blipFill), but standalone, with no placeholder involved.
3813
3864
  *
3814
3865
  * These are silhouettes recurring often enough in real decks to be worth naming; hand-authored
3815
3866
  * `points` remain available for anything else. Nothing here is theme- or brand-aware: which
@@ -3939,7 +3990,7 @@ function halfDisc(w, h, flat, preset) {
3939
3990
  * to `addImage({ points })`.
3940
3991
  *
3941
3992
  * `w`/`h` are the image box's size **in inches** and must match the `w`/`h` the picture is drawn
3942
- * at — the path is emitted in the box's own coordinate space, not normalized (see the module
3993
+ * at, because the path is emitted in the box's own coordinate space, not normalized (see the module
3943
3994
  * note above).
3944
3995
  * @param {ClipShape} shape - the named silhouette and its options
3945
3996
  * @param {number} w - image box width (inches)
@@ -3961,8 +4012,7 @@ function clipPath(shape, w, h) {
3961
4012
  case "half-disc": return halfDisc(w, h, checkEnumOrThrow(shape.flat, FLAT_SIDES, "clipPath `flat`", "clip/invalid-shape"), checkEnumOrThrow(shape.preset ?? "deep", HALF_DISC_PRESETS, "clipPath `preset`", "clip/invalid-shape"));
3962
4013
  }
3963
4014
  }
3964
- //#endregion
3965
- //#region src/types/text.ts
4015
+ // src/types/text.ts
3966
4016
  /** Factory for a single inline text run. Prevents `as never` casts when building mixed-style run arrays. */
3967
4017
  function textRun(text, options) {
3968
4018
  return options !== void 0 ? {
@@ -3974,7 +4024,6 @@ function textRun(text, options) {
3974
4024
  function textRuns(runs) {
3975
4025
  return runs;
3976
4026
  }
3977
- //#endregion
3978
4027
  export { fetchMediaBase64 as a, ALL_CONSTRUCT_FAMILIES as c, IMG_SVG_PLACEHOLDER as d, fetchFontBytes as i, composePresentation as l, textRuns as n, isRemote as o, clipPath as r, placeholderSvgPreview as s, textRun as t, PresentationCore as u };
3979
4028
 
3980
- //# sourceMappingURL=text-sN64Otf_.js.map
4029
+ //# sourceMappingURL=text-CU08xJGs.js.map