@shbernal/ts-pptx 2.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +31 -2
  2. package/dist/{autopage-tmfjRzL4.js → autopage-QQyfRtoK.js} +4 -5
  3. package/dist/{autopage-tmfjRzL4.js.map → autopage-QQyfRtoK.js.map} +1 -1
  4. package/dist/browser.d.ts +14 -6
  5. package/dist/browser.d.ts.map +1 -1
  6. package/dist/browser.js +14 -6
  7. package/dist/browser.js.map +1 -1
  8. package/dist/{diagnostics-CQ0JWc9m.d.ts → diagnostics-PMINsVP6.d.ts} +97 -8
  9. package/dist/diagnostics-PMINsVP6.d.ts.map +1 -0
  10. package/dist/{enums-D3aF4QGb.d.ts → enums-CoXnxiCI.d.ts} +6 -5
  11. package/dist/{enums-D3aF4QGb.d.ts.map → enums-CoXnxiCI.d.ts.map} +1 -1
  12. package/dist/{errors-B22h8Qd0.d.ts → errors-BeUEkNWL.d.ts} +26 -4
  13. package/dist/errors-BeUEkNWL.d.ts.map +1 -0
  14. package/dist/{errors-DGE9zh3U.js → errors-Bvln0H-7.js} +21 -2
  15. package/dist/errors-Bvln0H-7.js.map +1 -0
  16. package/dist/{fit-BSWY0AYt.js → fit-DhMbQG5O.js} +0 -0
  17. package/dist/fit-DhMbQG5O.js.map +1 -0
  18. package/dist/{html-dom-BQLUntho.js → html-dom-DajBuobe.js} +20 -6
  19. package/dist/html-dom-DajBuobe.js.map +1 -0
  20. package/dist/html.d.ts +3 -3
  21. package/dist/html.d.ts.map +1 -1
  22. package/dist/html.js +7 -4
  23. package/dist/html.js.map +1 -1
  24. package/dist/index.d.ts +6 -6
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +6 -6
  27. package/dist/index.js.map +1 -1
  28. package/dist/inspect.d.ts +1 -1
  29. package/dist/inspect.js +4 -4
  30. package/dist/inspect.js.map +1 -1
  31. package/dist/math.d.ts +1 -1
  32. package/dist/math.d.ts.map +1 -1
  33. package/dist/math.js +98 -2
  34. package/dist/math.js.map +1 -1
  35. package/dist/measure.d.ts +3 -3
  36. package/dist/measure.js +2 -2
  37. package/dist/node.d.ts +14 -6
  38. package/dist/node.d.ts.map +1 -1
  39. package/dist/node.js +13 -5
  40. package/dist/node.js.map +1 -1
  41. package/dist/package-CaOLXSNi.d.ts.map +1 -1
  42. package/dist/{pres-GU-jjBEx.d.ts → pres-BgvxhQ_m.d.ts} +3 -3
  43. package/dist/{pres-GU-jjBEx.d.ts.map → pres-BgvxhQ_m.d.ts.map} +1 -1
  44. package/dist/{presentation-MJe9WDi4.js → presentation-BP1B8PCF.js} +863 -343
  45. package/dist/presentation-BP1B8PCF.js.map +1 -0
  46. package/dist/{presentation-CVaTZw1V.d.ts → presentation-DyIRDKkp.d.ts} +742 -595
  47. package/dist/presentation-DyIRDKkp.d.ts.map +1 -0
  48. package/dist/{presentation-types-BVw0EknS.d.ts → presentation-types-Dj4riuDs.d.ts} +2 -2
  49. package/dist/{presentation-types-BVw0EknS.d.ts.map → presentation-types-Dj4riuDs.d.ts.map} +1 -1
  50. package/dist/read.d.ts +5 -5
  51. package/dist/read.d.ts.map +1 -1
  52. package/dist/read.js +3 -3
  53. package/dist/script.d.ts +2 -2
  54. package/dist/script.d.ts.map +1 -1
  55. package/dist/script.js +0 -0
  56. package/dist/script.js.map +1 -1
  57. package/dist/{shapes-CaOgR0i1.js → shapes-DMThLsZN.js} +689 -1032
  58. package/dist/shapes-DMThLsZN.js.map +1 -0
  59. package/dist/{slide-BBlElWr9.d.ts → slide-BRAtT8Dl.d.ts} +197 -22
  60. package/dist/slide-BRAtT8Dl.d.ts.map +1 -0
  61. package/dist/{image-size-C8uYE8GJ.js → st-enums-evQG2A7Z.js} +233 -2
  62. package/dist/st-enums-evQG2A7Z.js.map +1 -0
  63. package/dist/{text-B1ZrPEeK.js → text-B6PbMFih.js} +725 -319
  64. package/dist/text-B6PbMFih.js.map +1 -0
  65. package/dist/{units-B-JUKwCS.js → units-C7QM1KgH.js} +2 -2
  66. package/dist/{units-B-JUKwCS.js.map → units-C7QM1KgH.js.map} +1 -1
  67. package/dist/{units-internal-7JcMfYti.js → units-internal-CR8s8YQa.js} +4 -3
  68. package/dist/units-internal-CR8s8YQa.js.map +1 -0
  69. package/dist/zip.d.ts +2 -2
  70. package/dist/zip.js +1 -1
  71. package/dist/zip.js.map +1 -1
  72. package/package.json +40 -29
  73. package/skills/ts-pptx-upstream/SKILL.md +247 -0
  74. package/dist/diagnostics-CQ0JWc9m.d.ts.map +0 -1
  75. package/dist/errors-B22h8Qd0.d.ts.map +0 -1
  76. package/dist/errors-DGE9zh3U.js.map +0 -1
  77. package/dist/fit-BSWY0AYt.js.map +0 -1
  78. package/dist/html-dom-BQLUntho.js.map +0 -1
  79. package/dist/image-size-C8uYE8GJ.js.map +0 -1
  80. package/dist/presentation-CVaTZw1V.d.ts.map +0 -1
  81. package/dist/presentation-MJe9WDi4.js.map +0 -1
  82. package/dist/shapes-CaOgR0i1.js.map +0 -1
  83. package/dist/slide-BBlElWr9.d.ts.map +0 -1
  84. package/dist/text-B1ZrPEeK.js.map +0 -1
  85. package/dist/units-internal-7JcMfYti.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import { n as warn, r as warnOnce } from "./diagnostics-DYQC7xLV.js";
2
- import { n as InvalidOptionError, o as UnsupportedFeatureError, r as MediaError, t as InternalError } from "./errors-DGE9zh3U.js";
3
- import { a as FIXED_PCT_PER_PERCENT, d as emuToInches, g as pixelsToEmu, i as EMU_PER_POINT, l as STANDARD_LAYOUTS, m as inchesToEmu, r as EMU_PER_INCH, s as PERCENT_SCALE, t as ANGLE_UNITS_PER_DEGREE, v as ptToHundredths } from "./units-B-JUKwCS.js";
4
- import { $ as connectorPresetFor, C as DEF_PRES_LAYOUT_NAME, D as DEF_TEXT_GLOW, E as DEF_SLIDE_MARGIN_IN, F as PIECHART_COLORS, G as PlaceholderType, I as REGEX_HEX_COLOR, K as SchemeColor, L as SLDNUMFLDID, M as LAYOUT_IDX_SERIES_BASE, N as LETTERS, O as DEF_TEXT_SHADOW, Q as asChartType, R as XML_DECL, S as DEF_PRES_LAYOUT, T as DEF_SHAPE_SHADOW, Z as VALID_SHAPE_PRESETS, _ as DEF_CELL_BORDER, a as lineWidthToEmu, b as DEF_CHART_GRIDLINE, c as resolveTableColWidthsEmu, d as AXIS_ID_CATEGORY_PRIMARY, et as isChartExType, f as AXIS_ID_CATEGORY_SECONDARY, g as BARCHART_COLORS, h as AXIS_ID_VALUE_SECONDARY, i as inch2Emu, k as IMG_BROKEN, l as transparencyToAlpha, m as AXIS_ID_VALUE_PRIMARY, n as convertRotationDegrees, o as marginToEmu, p as AXIS_ID_SERIES_PRIMARY, r as getSmartParseNumber, s as opacityToAlpha, t as convertArcAngle, u as valToPts, v as DEF_CELL_MARGIN_IN, x as DEF_FONT_COLOR, y as DEF_CHART_BORDER } from "./units-internal-7JcMfYti.js";
5
- import { a as decodeBase64ToBytes, n as getImageSizeFromBase64, o as svgMarkupToDataUri, t as fitSrcRectPercents } from "./image-size-C8uYE8GJ.js";
6
- import { t as getSlidesForTableRows } from "./autopage-tmfjRzL4.js";
7
- import { _ as parseFontMetrics, m as FontMetricsRegistry, n as measureText, r as computeTableLayout, t as applyMeasuredFit } from "./fit-BSWY0AYt.js";
2
+ import { n as InvalidOptionError, o as UnsupportedFeatureError, r as MediaError, t as InternalError } from "./errors-Bvln0H-7.js";
3
+ import { a as FIXED_PCT_PER_PERCENT, d as emuToInches, g as pixelsToEmu, i as EMU_PER_POINT, l as STANDARD_LAYOUTS, m as inchesToEmu, r as EMU_PER_INCH, s as PERCENT_SCALE, t as ANGLE_UNITS_PER_DEGREE, v as ptToHundredths } from "./units-C7QM1KgH.js";
4
+ import { $ as connectorPresetFor, C as DEF_PRES_LAYOUT_NAME, D as DEF_TEXT_GLOW, E as DEF_SLIDE_MARGIN_IN, F as PIECHART_COLORS, G as PlaceholderType, I as REGEX_HEX_COLOR, K as SchemeColor, L as SLDNUMFLDID, M as LAYOUT_IDX_SERIES_BASE, N as LETTERS, O as DEF_TEXT_SHADOW, Q as asChartType, R as XML_DECL, S as DEF_PRES_LAYOUT, T as DEF_SHAPE_SHADOW, Z as VALID_SHAPE_PRESETS, _ as DEF_CELL_BORDER, a as lineWidthToEmu, b as DEF_CHART_GRIDLINE, c as resolveTableColWidthsEmu, d as AXIS_ID_CATEGORY_PRIMARY, et as isChartExType, f as AXIS_ID_CATEGORY_SECONDARY, g as BARCHART_COLORS, h as AXIS_ID_VALUE_SECONDARY, i as inch2Emu, k as IMG_BROKEN, l as transparencyToAlpha, m as AXIS_ID_VALUE_PRIMARY, n as convertRotationDegrees, o as marginToEmu, p as AXIS_ID_SERIES_PRIMARY, r as getSmartParseNumber, s as opacityToAlpha, t as convertArcAngle, u as valToPts, v as DEF_CELL_MARGIN_IN, x as DEF_FONT_COLOR, y as DEF_CHART_BORDER } from "./units-internal-CR8s8YQa.js";
5
+ import { E as svgMarkupToDataUri, S as getImageSizeFromBase64, T as decodeBase64ToBytes, a as PRESET_MATERIALS, g as PACKAGE_REL_NS, h as OFFICE_REL, i as PRESET_LINE_DASHES, n as LIGHT_RIGS, r as LIGHT_RIG_DIRECTIONS, s as TEXT_HORZ_OVERFLOW, t as BEVEL_PRESETS, u as checkEnumOrWarn, x as fitSrcRectPercents } from "./st-enums-evQG2A7Z.js";
6
+ import { t as getSlidesForTableRows } from "./autopage-QQyfRtoK.js";
7
+ import { _ as parseFontMetrics, m as FontMetricsRegistry, n as measureText, r as computeTableLayout, t as applyMeasuredFit } from "./fit-DhMbQG5O.js";
8
8
  import { a as flattenEmbeddedFaces, i as FONT_REL_TYPE, n as FONT_DATA_CONTENT_TYPE, o as serializeEmbeddedFontLst, r as FONT_DATA_EXTENSION, t as EMBEDDED_FONT_SLOTS } from "./embedded-fonts-MaynAHa9.js";
9
9
  import { ZipWriter } from "./zip.js";
10
10
  //#region src/media/content-type.ts
@@ -1326,7 +1326,7 @@ function addImageDefinition(target, opt) {
1326
1326
  let defWidth = intWidth;
1327
1327
  let defHeight = intHeight;
1328
1328
  let szAuto;
1329
- if ((!intWidth || !intHeight) && strImgExtn !== "svg") {
1329
+ if ((!intWidth || !intHeight) && !(strImgExtn === "svg" && !intWidth && !intHeight)) {
1330
1330
  const natural = strImageData ? getImageSizeFromBase64(strImageData) : null;
1331
1331
  if (natural) {
1332
1332
  if (!intWidth && !intHeight) {
@@ -1961,23 +1961,6 @@ function addMediaDefinition(target, opt) {
1961
1961
  target._slideObjects.push(slideData);
1962
1962
  }
1963
1963
  //#endregion
1964
- //#region src/gen/define/notes.ts
1965
- /**
1966
- * Adds Notes to a slide.
1967
- * @param {PresSlideInternal} `target` slide object
1968
- * @param {string | NotesProps | NotesProps[]} `notes` plain text, or rich runs (inline formatting / hyperlinks)
1969
- */
1970
- function addNotesDefinition(target, notes) {
1971
- const runs = typeof notes === "string" ? [{ text: notes }] : (Array.isArray(notes) ? notes : [notes]).map((run) => ({
1972
- text: run.text,
1973
- options: run.options
1974
- }));
1975
- target._slideObjects.push({
1976
- _type: "notes",
1977
- text: runs
1978
- });
1979
- }
1980
- //#endregion
1981
1964
  //#region src/gen/define/preview-image.ts
1982
1965
  /**
1983
1966
  * Register a preview/cover image as a slide media rel (deduped like `addImage`) and return its rId.
@@ -2010,11 +1993,140 @@ function registerPreviewImage(target, cover) {
2010
1993
  return rId;
2011
1994
  }
2012
1995
  //#endregion
1996
+ //#region src/gen/define/model3d.ts
1997
+ /** OPC content type for a glTF binary, as PowerPoint spells it in `[Content_Types].xml`. */
1998
+ const GLB_CONTENT_TYPE = "model/gltf.binary";
1999
+ /** `.rels` `Type` URI for the embedded 3D model part. Note `2017/06` — not the namespace's `2017`. */
2000
+ const MODEL3D_REL_TYPE = "http://schemas.microsoft.com/office/2017/06/relationships/model3d";
2001
+ /** Fixed-point denominator for `am3d` linear values: `am3d:up@dy="36000000"` is the unit vector. */
2002
+ const AM3D_UNIT = 36e6;
2003
+ /** Denominator of every `am3d` rational (`meterPerModelUnit`, `scale`, `illuminance`, `intensity`). */
2004
+ const AM3D_RATIO_DEN = 1e6;
2005
+ /** `am3d:perspective@fov` is in 60000ths of a degree, like every other DrawingML angle. */
2006
+ const FOV_PER_DEGREE = 6e4;
2007
+ /**
2008
+ * The camera PowerPoint wrote for the 2×2×2 cube fixture, in caller units (metres / degrees).
2009
+ * `z` is `sqrt(3)/2 / sin(22.5°)` — the bounding sphere of a 1-metre cube framed at a 45° fov.
2010
+ */
2011
+ const DEFAULT_CAMERA = {
2012
+ pos: {
2013
+ x: 0,
2014
+ y: 0,
2015
+ z: 2.2630334
2016
+ },
2017
+ lookAt: {
2018
+ x: 0,
2019
+ y: 0,
2020
+ z: 0
2021
+ },
2022
+ up: {
2023
+ x: 0,
2024
+ y: 1,
2025
+ z: 0
2026
+ },
2027
+ fov: 45
2028
+ };
2029
+ /** `am3d:meterPerModelUnit` for the cube fixture: 1 / (its 2-unit largest extent). */
2030
+ const DEFAULT_METER_PER_MODEL_UNIT = .5;
2031
+ /** Reject a non-finite camera component rather than letting `NaN` reach the XML as `"NaN"`. */
2032
+ function toAm3dUnits(point, fallback, label) {
2033
+ const src = point ?? fallback;
2034
+ for (const axis of [
2035
+ "x",
2036
+ "y",
2037
+ "z"
2038
+ ]) if (!Number.isFinite(src[axis])) throw new InvalidOptionError("model3d/invalid-camera", `addModel3d(): camera.${label}.${axis} must be a finite number, got ${String(src[axis])}.`);
2039
+ return {
2040
+ x: Math.round(src.x * AM3D_UNIT),
2041
+ y: Math.round(src.y * AM3D_UNIT),
2042
+ z: Math.round(src.z * AM3D_UNIT)
2043
+ };
2044
+ }
2045
+ /** Resolve `camera` + `meterPerModelUnit` into the wire units {@link Model3dInternal} carries. */
2046
+ function resolveCamera(camera, meterPerModelUnit) {
2047
+ const fovDeg = camera?.fov ?? DEFAULT_CAMERA.fov;
2048
+ if (!Number.isFinite(fovDeg) || fovDeg <= 0 || fovDeg >= 180) throw new InvalidOptionError("model3d/invalid-fov", `addModel3d(): camera.fov must be greater than 0 and less than 180 degrees, got ${String(fovDeg)}.`);
2049
+ const scale = meterPerModelUnit ?? DEFAULT_METER_PER_MODEL_UNIT;
2050
+ if (!Number.isFinite(scale) || scale <= 0) throw new InvalidOptionError("model3d/invalid-scale", `addModel3d(): meterPerModelUnit must be a finite number greater than 0, got ${String(scale)}.`);
2051
+ const up = toAm3dUnits(camera?.up, DEFAULT_CAMERA.up, "up");
2052
+ return {
2053
+ pos: toAm3dUnits(camera?.pos, DEFAULT_CAMERA.pos, "pos"),
2054
+ lookAt: toAm3dUnits(camera?.lookAt, DEFAULT_CAMERA.lookAt, "lookAt"),
2055
+ up: {
2056
+ dx: up.x,
2057
+ dy: up.y,
2058
+ dz: up.z
2059
+ },
2060
+ fov: Math.round(fovDeg * FOV_PER_DEGREE),
2061
+ meterPerModelUnitN: Math.round(scale * AM3D_RATIO_DEN)
2062
+ };
2063
+ }
2064
+ /**
2065
+ * Adds an embedded 3D model to a slide definition.
2066
+ * @param {PresSlideInternal} target - slide the model will be added to
2067
+ * @param {Model3dProps} opt - 3D model options
2068
+ */
2069
+ function addModel3dDefinition(target, opt) {
2070
+ const strData = opt.data || "";
2071
+ const strPath = opt.path || "";
2072
+ if (!strPath && !strData) throw new InvalidOptionError("model3d/missing-source", "addModel3d(): either `data` or `path` are required!");
2073
+ const camera = resolveCamera(opt.camera, opt.meterPerModelUnit);
2074
+ const nameIdx = nextObjectNameIdx(target, "model3d");
2075
+ const objectName = opt.objectName ? encodeXmlAttrValue(validateObjectName(opt.objectName, "model3d")) : `3D Model ${nameIdx + 1}`;
2076
+ const modelRid = getNewRelId(target);
2077
+ const mediaSlideKey = target._slideNum == null ? "sm" : target._slideNum >= 1e3 ? `sl-${target._slideNum}` : target._slideNum;
2078
+ target._relsMedia.push({
2079
+ path: strPath || "preencoded.glb",
2080
+ type: GLB_CONTENT_TYPE,
2081
+ extn: "glb",
2082
+ data: strData,
2083
+ rId: modelRid,
2084
+ model3dRelType: MODEL3D_REL_TYPE,
2085
+ Target: `../media/model3d-${mediaSlideKey}-${target._relsMedia.length + 1}.glb`
2086
+ });
2087
+ if (!opt.preview?.path && !opt.preview?.data) warn("model3d/preview-missing", "addModel3d(): no `preview` image supplied, so a gray placeholder is embedded. PowerPoint 2019+ draws the live model over it, but thumbnails, PDF export and older viewers show the placeholder.");
2088
+ const previewRid = registerPreviewImage(target, opt.preview);
2089
+ const slideData = {
2090
+ _type: "model3d",
2091
+ options: {
2092
+ x: opt.x ?? 0,
2093
+ y: opt.y ?? 0,
2094
+ w: opt.w ?? 4,
2095
+ h: opt.h ?? 3,
2096
+ objectName,
2097
+ ...opt.altText ? { altText: opt.altText } : {}
2098
+ },
2099
+ model3d: {
2100
+ modelRid,
2101
+ previewRid,
2102
+ ...camera
2103
+ }
2104
+ };
2105
+ target._slideObjects.push(slideData);
2106
+ }
2107
+ //#endregion
2108
+ //#region src/gen/define/notes.ts
2109
+ /**
2110
+ * Adds Notes to a slide.
2111
+ * @param {PresSlideInternal} `target` slide object
2112
+ * @param {string | NotesProps | NotesProps[]} `notes` plain text, or rich runs (inline formatting / hyperlinks)
2113
+ */
2114
+ function addNotesDefinition(target, notes) {
2115
+ const runs = typeof notes === "string" ? [{ text: notes }] : (Array.isArray(notes) ? notes : [notes]).map((run) => ({
2116
+ text: run.text,
2117
+ options: run.options
2118
+ }));
2119
+ target._slideObjects.push({
2120
+ _type: "notes",
2121
+ text: runs
2122
+ });
2123
+ }
2124
+ //#endregion
2013
2125
  //#region src/gen/define/ole.ts
2014
2126
  /** Rel type for an embedded OPC package (an Office file, itself a zip). */
2015
- const PACKAGE_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package";
2127
+ const PACKAGE_REL = OFFICE_REL + "package";
2016
2128
  /** Rel type for a generic OLE-server blob — a compound-file `.bin` (ECMA-376 Part 1 §15.2.10). */
2017
- const OLE_OBJECT_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject";
2129
+ const OLE_OBJECT_REL = OFFICE_REL + "oleObject";
2018
2130
  /**
2019
2131
  * Embeddable payload formats, keyed by file extension. The six Office extensions are OPC packages
2020
2132
  * and keep their own extension in `ppt/embeddings/`; everything else is embedded as a generic OLE
@@ -2220,7 +2332,7 @@ function applyTableHeaderColumnSugar(tableRows, opt) {
2220
2332
  options: {}
2221
2333
  } : {
2222
2334
  ...cell,
2223
- options: { ...cell.options || {} }
2335
+ options: { ...cell.options }
2224
2336
  };
2225
2337
  const colDef = cols ? cols[colCursor] : void 0;
2226
2338
  colCursor += cellObj.options?.colspan || 1;
@@ -2805,6 +2917,24 @@ var SlideBuilder = class {
2805
2917
  return this;
2806
2918
  }
2807
2919
  /**
2920
+ * Add an embedded 3D model (PowerPoint's Insert ▸ 3D Models) to the Slide.
2921
+ *
2922
+ * The `.glb` bytes ship inside the `.pptx`, so PowerPoint 2019+ renders the model live and lets
2923
+ * the viewer orbit it. Everything else — older PowerPoint, thumbnails, PDF export — draws the
2924
+ * `preview` picture instead; without one a neutral gray placeholder is embedded.
2925
+ *
2926
+ * Most models also need `meterPerModelUnit`: ts-pptx does not parse the `.glb`, so it cannot
2927
+ * measure the bounding box PowerPoint would have scaled the scene by. Set it to
2928
+ * `1 / <largest bounding-box dimension in model units>`.
2929
+ * @param {Model3dProps} options - 3D model options
2930
+ * @return {SlideBuilder} this Slide
2931
+ * @example slide.addModel3d({ path: 'engine.glb', preview: { path: 'engine.png' }, x: 1, y: 1, w: 6, h: 4 })
2932
+ */
2933
+ addModel3d(options) {
2934
+ addModel3dDefinition(this, options);
2935
+ return this;
2936
+ }
2937
+ /**
2808
2938
  * Add speaker notes to Slide
2809
2939
  * @param {string | NotesProps | NotesProps[]} notes - notes text, or rich runs with inline
2810
2940
  * formatting / hyperlinks. A plain string is the single-run case; pass run objects to add
@@ -3075,6 +3205,9 @@ function genXmlColorSelection(props) {
3075
3205
  internalElements += alphaFromTransparency(props.transparency);
3076
3206
  }
3077
3207
  switch (fillType) {
3208
+ case "none":
3209
+ outText += voidEl("a:noFill");
3210
+ break;
3078
3211
  case "solid":
3079
3212
  outText += el("a:solidFill", null, raw(createColorElement(colorVal, internalElements)));
3080
3213
  break;
@@ -3104,23 +3237,6 @@ const BORDER_KEYS = [
3104
3237
  "cap"
3105
3238
  ];
3106
3239
  /**
3107
- * The `ST_PresetLineDashVal` tokens `BorderProps.dashType` accepts — the same set
3108
- * `ShapeLineProps.dashType` does, so a caller who knows one knows the other.
3109
- */
3110
- const BORDER_DASH_VALUES = [
3111
- "solid",
3112
- "dot",
3113
- "dash",
3114
- "lgDash",
3115
- "dashDot",
3116
- "lgDashDot",
3117
- "lgDashDotDot",
3118
- "sysDash",
3119
- "sysDot",
3120
- "sysDashDot",
3121
- "sysDashDotDot"
3122
- ];
3123
- /**
3124
3240
  * Resolve the `a:prstDash/@val` a border should emit.
3125
3241
  *
3126
3242
  * `BorderProps.type` is a three-way switch, so on its own it can only say "dashed" and
@@ -3136,14 +3252,7 @@ const BORDER_DASH_VALUES = [
3136
3252
  */
3137
3253
  function resolveBorderDash(border) {
3138
3254
  const fromType = border.type === "dash" ? "sysDash" : "solid";
3139
- const dash = border.dashType;
3140
- if (dash === void 0 || dash === null) return fromType;
3141
- if (BORDER_DASH_VALUES.includes(dash)) return dash;
3142
- warnOnce("border/invalid-dash-type", `border: dashType \`${String(dash)}\` is not a valid value and is ignored — use one of ${BORDER_DASH_VALUES.join(", ")}.`, {
3143
- received: dash,
3144
- valid: BORDER_DASH_VALUES
3145
- });
3146
- return fromType;
3255
+ return checkEnumOrWarn(border.dashType, PRESET_LINE_DASHES, "border/invalid-dash-type", "border: dashType") ?? fromType;
3147
3256
  }
3148
3257
  /**
3149
3258
  * Report an authored key that is not part of `BorderProps`, which the library would
@@ -3585,7 +3694,34 @@ function makeSeriesDataPointsXml(chartType, obj, opts, varyColors) {
3585
3694
  * gridlines, optional axis title -- but PowerPoint requires the child elements in a
3586
3695
  * fixed order that differs per axis, so they stay as three separate builders rather
3587
3696
  * than one parameterized one. Called by {@link ./chart-xml}'s axis region.
3588
- */
3697
+ *
3698
+ * Sub-blocks are shared where they can be shared *exactly*: {@link axisLineSpPr} covers
3699
+ * `<c:spPr>`, which differs between the three only by indent width. `<c:txPr>` is
3700
+ * deliberately left duplicated — its inherited indentation is irregular *within* each copy
3701
+ * as well as between them (the category axis closes `</a:defRPr>` at three spaces, the value
3702
+ * axis at six), so parameterising it would take about as many indent arguments as it has
3703
+ * lines and would hide the quirks rather than share the structure.
3704
+ */
3705
+ /**
3706
+ * The `<c:spPr>` axis-line block, identical in shape on all three axes.
3707
+ *
3708
+ * The only thing that differed between the three copies was how far each was indented — two
3709
+ * spaces on the category and series axes, one on the value axis — and that irregularity is
3710
+ * inherited, not meaningful. It is reproduced here rather than normalised because inter-element
3711
+ * whitespace being inert does not make changing it free: byte-identity is the gate that proves
3712
+ * an emitter refactor changed nothing, and it cannot tell a "harmless" whitespace edit from a
3713
+ * real one. `indent` keeps the quirk visible at the call site.
3714
+ * @param {number} indent - leading spaces on the `<c:spPr>` line; children step out by 2 and 4
3715
+ * @param {number} widthEmu - line width for `a:ln@w`
3716
+ * @param {boolean | undefined} show - false emits `<a:noFill/>` instead of a colour
3717
+ * @param {string | undefined} color - line colour, defaulting to {@link DEF_GRIDLINE_COLOR}
3718
+ * @param {string} dash - `a:prstDash@val`
3719
+ * @return {string} XML `<c:spPr>`
3720
+ */
3721
+ function axisLineSpPr(indent, widthEmu, show, color, dash) {
3722
+ const at = (extra) => " ".repeat(indent + extra);
3723
+ return `${at(0)}<c:spPr>${at(2)}<a:ln w="${widthEmu}" cap="flat">` + (!show ? "<a:noFill/>" : genXmlColorSelection(color || DEF_GRIDLINE_COLOR)) + `${at(4)}<a:prstDash val="${dash}"/>${at(4)}<a:round/>${at(2)}</a:ln>${at(0)}</c:spPr>`;
3724
+ }
3589
3725
  function makeCatAxis(opts, axisId, valAxisId) {
3590
3726
  let strXml = "";
3591
3727
  const usesValueAxisForCategories = opts._type === "scatter" || opts._type === "bubble" || opts._type === "bubble3D";
@@ -3594,7 +3730,7 @@ function makeCatAxis(opts, axisId, valAxisId) {
3594
3730
  else strXml += "<c:" + (opts.catLabelFormatCode ? "dateAx" : "catAx") + ">";
3595
3731
  strXml += " <c:axId val=\"" + axisId + "\"/>";
3596
3732
  strXml += " <c:scaling>";
3597
- strXml += "<c:orientation val=\"" + (opts.catAxisOrientation || (opts.barDir === "col" ? "minMax" : "minMax")) + "\"/>";
3733
+ strXml += "<c:orientation val=\"" + (opts.catAxisOrientation || "minMax") + "\"/>";
3598
3734
  if (opts.catAxisMaxVal || opts.catAxisMaxVal === 0) strXml += `<c:max val="${opts.catAxisMaxVal}"/>`;
3599
3735
  if (opts.catAxisMinVal || opts.catAxisMinVal === 0) strXml += `<c:min val="${opts.catAxisMinVal}"/>`;
3600
3736
  strXml += "</c:scaling>";
@@ -3627,13 +3763,7 @@ function makeCatAxis(opts, axisId, valAxisId) {
3627
3763
  strXml += " <c:minorTickMark val=\"" + (opts.catAxisMinorTickMark || "none") + "\"/>";
3628
3764
  strXml += " <c:tickLblPos val=\"" + (opts.catAxisLabelPos || (opts.barDir === "col" ? "low" : "nextTo")) + "\"/>";
3629
3765
  }
3630
- strXml += " <c:spPr>";
3631
- strXml += ` <a:ln w="${opts.catAxisLineSize ? valToPts(opts.catAxisLineSize) : EMU_PER_POINT}" cap="flat">`;
3632
- strXml += !opts.catAxisLineShow ? "<a:noFill/>" : genXmlColorSelection(opts.catAxisLineColor || DEF_GRIDLINE_COLOR);
3633
- strXml += " <a:prstDash val=\"" + (opts.catAxisLineStyle || "solid") + "\"/>";
3634
- strXml += " <a:round/>";
3635
- strXml += " </a:ln>";
3636
- strXml += " </c:spPr>";
3766
+ strXml += axisLineSpPr(2, opts.catAxisLineSize ? valToPts(opts.catAxisLineSize) : EMU_PER_POINT, opts.catAxisLineShow, opts.catAxisLineColor, opts.catAxisLineStyle || "solid");
3637
3767
  strXml += " <c:txPr>";
3638
3768
  if (opts.catAxisLabelRotate) strXml += `<a:bodyPr rot="${convertRotationDegrees(opts.catAxisLabelRotate)}"/>`;
3639
3769
  else strXml += "<a:bodyPr/>";
@@ -3697,7 +3827,7 @@ function makeValAxis(opts, valAxisId) {
3697
3827
  strXml += " <c:axId val=\"" + valAxisId + "\"/>";
3698
3828
  strXml += " <c:scaling>";
3699
3829
  if (opts.valAxisLogScaleBase) strXml += `<c:logBase val="${opts.valAxisLogScaleBase}"/>`;
3700
- strXml += "<c:orientation val=\"" + (opts.valAxisOrientation || (opts.barDir === "col" ? "minMax" : "minMax")) + "\"/>";
3830
+ strXml += "<c:orientation val=\"" + (opts.valAxisOrientation || "minMax") + "\"/>";
3701
3831
  if (opts.valAxisMaxVal || opts.valAxisMaxVal === 0) strXml += `<c:max val="${opts.valAxisMaxVal}"/>`;
3702
3832
  if (opts.valAxisMinVal || opts.valAxisMinVal === 0) strXml += `<c:min val="${opts.valAxisMinVal}"/>`;
3703
3833
  strXml += " </c:scaling>";
@@ -3724,13 +3854,7 @@ function makeValAxis(opts, valAxisId) {
3724
3854
  strXml += " <c:minorTickMark val=\"" + (opts.valAxisMinorTickMark || "none") + "\"/>";
3725
3855
  strXml += " <c:tickLblPos val=\"" + (opts.valAxisLabelPos || (opts.barDir === "col" ? "nextTo" : "low")) + "\"/>";
3726
3856
  }
3727
- strXml += " <c:spPr>";
3728
- strXml += ` <a:ln w="${opts.valAxisLineSize ? valToPts(opts.valAxisLineSize) : EMU_PER_POINT}" cap="flat">`;
3729
- strXml += !opts.valAxisLineShow ? "<a:noFill/>" : genXmlColorSelection(opts.valAxisLineColor || DEF_GRIDLINE_COLOR);
3730
- strXml += " <a:prstDash val=\"" + (opts.valAxisLineStyle || "solid") + "\"/>";
3731
- strXml += " <a:round/>";
3732
- strXml += " </a:ln>";
3733
- strXml += " </c:spPr>";
3857
+ strXml += axisLineSpPr(1, opts.valAxisLineSize ? valToPts(opts.valAxisLineSize) : EMU_PER_POINT, opts.valAxisLineShow, opts.valAxisLineColor, opts.valAxisLineStyle || "solid");
3734
3858
  strXml += " <c:txPr>";
3735
3859
  strXml += ` <a:bodyPr${opts.valAxisLabelRotate ? " rot=\"" + convertRotationDegrees(opts.valAxisLabelRotate).toString() + "\"" : ""}/>`;
3736
3860
  strXml += " <a:lstStyle/>";
@@ -3765,7 +3889,7 @@ function makeSerAxis(opts, axisId, valAxisId) {
3765
3889
  let strXml = "";
3766
3890
  strXml += "<c:serAx>";
3767
3891
  strXml += " <c:axId val=\"" + axisId + "\"/>";
3768
- strXml += " <c:scaling><c:orientation val=\"" + (opts.serAxisOrientation || (opts.barDir === "col" ? "minMax" : "minMax")) + "\"/></c:scaling>";
3892
+ strXml += " <c:scaling><c:orientation val=\"" + (opts.serAxisOrientation || "minMax") + "\"/></c:scaling>";
3769
3893
  strXml += " <c:delete val=\"" + (opts.serAxisHidden ? "1" : "0") + "\"/>";
3770
3894
  strXml += " <c:axPos val=\"" + (opts.barDir === "col" ? "b" : "l") + "\"/>";
3771
3895
  strXml += opts.serGridLine && opts.serGridLine.style !== "none" ? createGridLineElement(opts.serGridLine) : "";
@@ -3783,13 +3907,7 @@ function makeSerAxis(opts, axisId, valAxisId) {
3783
3907
  strXml += " <c:majorTickMark val=\"out\"/>";
3784
3908
  strXml += " <c:minorTickMark val=\"none\"/>";
3785
3909
  strXml += ` <c:tickLblPos val="${opts.serAxisLabelPos || (opts.barDir === "col" ? "low" : "nextTo")}"/>`;
3786
- strXml += " <c:spPr>";
3787
- strXml += " <a:ln w=\"12700\" cap=\"flat\">";
3788
- strXml += !opts.serAxisLineShow ? "<a:noFill/>" : genXmlColorSelection(opts.serAxisLineColor || DEF_GRIDLINE_COLOR);
3789
- strXml += " <a:prstDash val=\"solid\"/>";
3790
- strXml += " <a:round/>";
3791
- strXml += " </a:ln>";
3792
- strXml += " </c:spPr>";
3910
+ strXml += axisLineSpPr(2, EMU_PER_POINT, opts.serAxisLineShow, opts.serAxisLineColor, "solid");
3793
3911
  strXml += " <c:txPr>";
3794
3912
  strXml += " <a:bodyPr/>";
3795
3913
  strXml += " <a:lstStyle/>";
@@ -5139,7 +5257,8 @@ function chartExLayoutId(type) {
5139
5257
  /**
5140
5258
  * A deterministic `uniqueId` GUID for a `<cx:series>`, derived from the chart's package-global id.
5141
5259
  * PowerPoint stamps a random GUID here; deriving it keeps output byte-stable (the project pins
5142
- * chart determinism — see `docs/backlog.yml` `fork-chart-counter-nondeterminism`). `seriesIdx`
5260
+ * chart determinism — the package-wide id this derives from is assigned at write time in
5261
+ * `package/assemble.ts`, STEP 2). `seriesIdx`
5143
5262
  * distinguishes multiple series within one chart (pareto emits two), varying the 4th GUID group so
5144
5263
  * every series still gets a distinct id.
5145
5264
  */
@@ -5383,9 +5502,15 @@ function makeXmlChartEx(rel) {
5383
5502
  * template — analogous to the default theme/worksheet boilerplate the library already embeds. They
5384
5503
  * reference theme scheme colors, so they adapt to the deck theme without per-chart customization.
5385
5504
  */
5386
- /** The `cs:colorStyle` payload for a chartEx chart's `colors{N}.xml` part (theme-accent cycle). */
5505
+ /**
5506
+ * The `cs:colorStyle` payload for a chartEx chart's `colors{N}.xml` part (theme-accent cycle).
5507
+ * @raw-xml-asset Captured verbatim from PowerPoint; not built here, so not a migration target.
5508
+ */
5387
5509
  const CHARTEX_COLORS_BODY = "<cs:colorStyle xmlns:cs=\"http://schemas.microsoft.com/office/drawing/2012/chartStyle\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" meth=\"cycle\" id=\"10\"><a:schemeClr val=\"accent1\"/><a:schemeClr val=\"accent2\"/><a:schemeClr val=\"accent3\"/><a:schemeClr val=\"accent4\"/><a:schemeClr val=\"accent5\"/><a:schemeClr val=\"accent6\"/><cs:variation/><cs:variation><a:lumMod val=\"60000\"/></cs:variation><cs:variation><a:lumMod val=\"80000\"/><a:lumOff val=\"20000\"/></cs:variation><cs:variation><a:lumMod val=\"80000\"/></cs:variation><cs:variation><a:lumMod val=\"60000\"/><a:lumOff val=\"40000\"/></cs:variation><cs:variation><a:lumMod val=\"50000\"/></cs:variation><cs:variation><a:lumMod val=\"70000\"/><a:lumOff val=\"30000\"/></cs:variation><cs:variation><a:lumMod val=\"70000\"/></cs:variation><cs:variation><a:lumMod val=\"50000\"/><a:lumOff val=\"50000\"/></cs:variation></cs:colorStyle>";
5388
- /** The `cs:chartStyle` payload for a chartEx chart's `style{N}.xml` part (PowerPoint default). */
5510
+ /**
5511
+ * The `cs:chartStyle` payload for a chartEx chart's `style{N}.xml` part (PowerPoint default).
5512
+ * @raw-xml-asset Captured verbatim from PowerPoint; not built here, so not a migration target.
5513
+ */
5389
5514
  const CHARTEX_STYLE_BODY = "<cs:chartStyle xmlns:cs=\"http://schemas.microsoft.com/office/drawing/2012/chartStyle\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" id=\"395\"><cs:axisTitle><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></cs:fontRef><cs:defRPr sz=\"1197\"/></cs:axisTitle><cs:categoryAxis><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></cs:fontRef><cs:spPr><a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"15000\"/><a:lumOff val=\"85000\"/></a:schemeClr></a:solidFill><a:round/></a:ln></cs:spPr><cs:defRPr sz=\"1197\"/></cs:categoryAxis><cs:chartArea mods=\"allowNoFillOverride allowNoLineOverride\"><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:solidFill><a:schemeClr val=\"bg1\"/></a:solidFill><a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"15000\"/><a:lumOff val=\"85000\"/></a:schemeClr></a:solidFill><a:round/></a:ln></cs:spPr><cs:defRPr sz=\"1330\"/></cs:chartArea><cs:dataLabel><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></cs:fontRef><cs:defRPr sz=\"1197\"/></cs:dataLabel><cs:dataLabelCallout><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"dk1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></cs:fontRef><cs:spPr><a:solidFill><a:schemeClr val=\"lt1\"/></a:solidFill><a:ln><a:solidFill><a:schemeClr val=\"dk1\"><a:lumMod val=\"25000\"/><a:lumOff val=\"75000\"/></a:schemeClr></a:solidFill></a:ln></cs:spPr><cs:defRPr sz=\"1197\"/><cs:bodyPr rot=\"0\" spcFirstLastPara=\"1\" vertOverflow=\"clip\" horzOverflow=\"clip\" vert=\"horz\" wrap=\"square\" lIns=\"36576\" tIns=\"18288\" rIns=\"36576\" bIns=\"18288\" anchor=\"ctr\" anchorCtr=\"1\"><a:spAutoFit/></cs:bodyPr></cs:dataLabelCallout><cs:dataPoint><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"><cs:styleClr val=\"auto\"/></cs:fillRef><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill></cs:spPr></cs:dataPoint><cs:dataPoint3D><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"><cs:styleClr val=\"auto\"/></cs:fillRef><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill></cs:spPr></cs:dataPoint3D><cs:dataPointLine><cs:lnRef idx=\"0\"><cs:styleClr val=\"auto\"/></cs:lnRef><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:ln w=\"28575\" cap=\"rnd\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:round/></a:ln></cs:spPr></cs:dataPointLine><cs:dataPointMarker><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"><cs:styleClr val=\"auto\"/></cs:fillRef><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:ln w=\"9525\"><a:solidFill><a:schemeClr val=\"lt1\"/></a:solidFill></a:ln></cs:spPr></cs:dataPointMarker><cs:dataPointMarkerLayout symbol=\"circle\" size=\"5\"/><cs:dataPointWireframe><cs:lnRef idx=\"0\"><cs:styleClr val=\"auto\"/></cs:lnRef><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:ln w=\"28575\" cap=\"rnd\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:round/></a:ln></cs:spPr></cs:dataPointWireframe><cs:dataTable><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></cs:fontRef><cs:spPr><a:ln w=\"9525\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"15000\"/><a:lumOff val=\"85000\"/></a:schemeClr></a:solidFill></a:ln></cs:spPr><cs:defRPr sz=\"1197\"/></cs:dataTable><cs:downBar><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"dk1\"/></cs:fontRef><cs:spPr><a:solidFill><a:schemeClr val=\"dk1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></a:solidFill><a:ln w=\"9525\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></a:solidFill></a:ln></cs:spPr></cs:downBar><cs:dropLine><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"35000\"/><a:lumOff val=\"65000\"/></a:schemeClr></a:solidFill><a:round/></a:ln></cs:spPr></cs:dropLine><cs:errorBar><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></a:solidFill><a:round/></a:ln></cs:spPr></cs:errorBar><cs:floor><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef></cs:floor><cs:gridlineMajor><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"15000\"/><a:lumOff val=\"85000\"/></a:schemeClr></a:solidFill><a:round/></a:ln></cs:spPr></cs:gridlineMajor><cs:gridlineMinor><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"15000\"/><a:lumOff val=\"85000\"/></a:schemeClr></a:solidFill><a:round/></a:ln></cs:spPr></cs:gridlineMinor><cs:hiLoLine><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"75000\"/><a:lumOff val=\"25000\"/></a:schemeClr></a:solidFill><a:round/></a:ln></cs:spPr></cs:hiLoLine><cs:leaderLine><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"35000\"/><a:lumOff val=\"65000\"/></a:schemeClr></a:solidFill><a:round/></a:ln></cs:spPr></cs:leaderLine><cs:legend><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></cs:fontRef><cs:defRPr sz=\"1197\"/></cs:legend><cs:plotArea mods=\"allowNoFillOverride allowNoLineOverride\"><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef></cs:plotArea><cs:plotArea3D mods=\"allowNoFillOverride allowNoLineOverride\"><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef></cs:plotArea3D><cs:seriesAxis><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></cs:fontRef><cs:spPr><a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"15000\"/><a:lumOff val=\"85000\"/></a:schemeClr></a:solidFill><a:round/></a:ln></cs:spPr><cs:defRPr sz=\"1197\"/></cs:seriesAxis><cs:seriesLine><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:ln w=\"9525\" cap=\"flat\"><a:solidFill><a:srgbClr val=\"D9D9D9\"/></a:solidFill><a:round/></a:ln></cs:spPr></cs:seriesLine><cs:title><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></cs:fontRef><cs:defRPr sz=\"1862\"/></cs:title><cs:trendline><cs:lnRef idx=\"0\"><cs:styleClr val=\"auto\"/></cs:lnRef><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef><cs:spPr><a:ln w=\"19050\" cap=\"rnd\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"sysDash\"/></a:ln></cs:spPr></cs:trendline><cs:trendlineLabel><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></cs:fontRef><cs:defRPr sz=\"1197\"/></cs:trendlineLabel><cs:upBar><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"dk1\"/></cs:fontRef><cs:spPr><a:solidFill><a:schemeClr val=\"lt1\"/></a:solidFill><a:ln w=\"9525\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"15000\"/><a:lumOff val=\"85000\"/></a:schemeClr></a:solidFill></a:ln></cs:spPr></cs:upBar><cs:valueAxis><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></cs:fontRef><cs:defRPr sz=\"1197\"/></cs:valueAxis><cs:wall><cs:lnRef idx=\"0\"/><cs:fillRef idx=\"0\"/><cs:effectRef idx=\"0\"/><cs:fontRef idx=\"minor\"><a:schemeClr val=\"tx1\"/></cs:fontRef></cs:wall></cs:chartStyle>";
5390
5515
  /** Build the `ppt/charts/colors{N}.xml` part body. */
5391
5516
  function makeChartExColorsXml() {
@@ -5397,7 +5522,6 @@ function makeChartExStyleXml() {
5397
5522
  }
5398
5523
  //#endregion
5399
5524
  //#region src/gen/chart/embed-xlsx.ts
5400
- const PACKAGE_REL_NS$2 = "http://schemas.openxmlformats.org/package/2006/relationships";
5401
5525
  function relationship$2(id, type, target) {
5402
5526
  return voidEl("Relationship", {
5403
5527
  Id: id,
@@ -5407,7 +5531,7 @@ function relationship$2(id, type, target) {
5407
5531
  }
5408
5532
  /** `<Relationships>` wrapper shared by the workbook's `.rels` parts (all flat, no indent). */
5409
5533
  function relationships(rels) {
5410
- return el("Relationships", { xmlns: PACKAGE_REL_NS$2 }, rels.map(raw));
5534
+ return el("Relationships", { xmlns: PACKAGE_REL_NS }, rels.map(raw));
5411
5535
  }
5412
5536
  /**
5413
5537
  * Build the chart's embedded Excel workbook as a standalone OPC package and
@@ -5426,22 +5550,22 @@ function buildEmbeddedWorksheet(chartObject) {
5426
5550
  const IS_MULTI_CAT_AXES = (data[0]?.labels?.length ?? 0) > 1;
5427
5551
  zipExcel.add("[Content_Types].xml", XML_DECL + "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"> <Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/> <Default Extension=\"xml\" ContentType=\"application/xml\"/> <Override PartName=\"/xl/workbook.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/> <Override PartName=\"/xl/worksheets/sheet1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/> <Override PartName=\"/xl/theme/theme1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.theme+xml\"/> <Override PartName=\"/xl/styles.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml\"/> <Override PartName=\"/xl/sharedStrings.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml\"/> <Override PartName=\"/xl/tables/table1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml\"/> <Override PartName=\"/docProps/core.xml\" ContentType=\"application/vnd.openxmlformats-package.core-properties+xml\"/> <Override PartName=\"/docProps/app.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.extended-properties+xml\"/></Types>\n");
5428
5552
  zipExcel.add("_rels/.rels", XML_DECL + relationships([
5429
- relationship$2("rId1", "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties", "docProps/core.xml"),
5430
- relationship$2("rId2", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties", "docProps/app.xml"),
5431
- relationship$2("rId3", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", "xl/workbook.xml")
5553
+ relationship$2("rId1", PACKAGE_REL_NS + "/metadata/core-properties", "docProps/core.xml"),
5554
+ relationship$2("rId2", OFFICE_REL + "extended-properties", "docProps/app.xml"),
5555
+ relationship$2("rId3", OFFICE_REL + "officeDocument", "xl/workbook.xml")
5432
5556
  ]) + "\n");
5433
5557
  zipExcel.add("docProps/app.xml", XML_DECL + "<Properties xmlns=\"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties\" xmlns:vt=\"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes\"><Application>Microsoft Macintosh Excel</Application><DocSecurity>0</DocSecurity><ScaleCrop>false</ScaleCrop><HeadingPairs><vt:vector size=\"2\" baseType=\"variant\"><vt:variant><vt:lpstr>Worksheets</vt:lpstr></vt:variant><vt:variant><vt:i4>1</vt:i4></vt:variant></vt:vector></HeadingPairs><TitlesOfParts><vt:vector size=\"1\" baseType=\"lpstr\"><vt:lpstr>Sheet1</vt:lpstr></vt:vector></TitlesOfParts><Company></Company><LinksUpToDate>false</LinksUpToDate><SharedDoc>false</SharedDoc><HyperlinksChanged>false</HyperlinksChanged><AppVersion>16.0300</AppVersion></Properties>\n");
5434
5558
  zipExcel.add("docProps/core.xml", XML_DECL + "<cp:coreProperties xmlns:cp=\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:dcterms=\"http://purl.org/dc/terms/\" xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><dc:creator>TsPptx</dc:creator><cp:lastModifiedBy>TsPptx</cp:lastModifiedBy><dcterms:created xsi:type=\"dcterms:W3CDTF\">" + (/* @__PURE__ */ new Date()).toISOString() + "</dcterms:created><dcterms:modified xsi:type=\"dcterms:W3CDTF\">" + (/* @__PURE__ */ new Date()).toISOString() + "</dcterms:modified></cp:coreProperties>");
5435
5559
  zipExcel.add("xl/_rels/workbook.xml.rels", XML_DECL + relationships([
5436
- relationship$2("rId3", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles", "styles.xml"),
5437
- relationship$2("rId2", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme", "theme/theme1.xml"),
5438
- relationship$2("rId1", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet", "worksheets/sheet1.xml"),
5439
- relationship$2("rId4", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings", "sharedStrings.xml")
5560
+ relationship$2("rId3", OFFICE_REL + "styles", "styles.xml"),
5561
+ relationship$2("rId2", OFFICE_REL + "theme", "theme/theme1.xml"),
5562
+ relationship$2("rId1", OFFICE_REL + "worksheet", "worksheets/sheet1.xml"),
5563
+ relationship$2("rId4", OFFICE_REL + "sharedStrings", "sharedStrings.xml")
5440
5564
  ]));
5441
5565
  zipExcel.add("xl/styles.xml", XML_DECL + "<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><numFmts count=\"1\"><numFmt numFmtId=\"0\" formatCode=\"General\"/></numFmts><fonts count=\"4\"><font><sz val=\"9\"/><color indexed=\"8\"/><name val=\"Geneva\"/></font><font><sz val=\"9\"/><color indexed=\"8\"/><name val=\"Geneva\"/></font><font><sz val=\"10\"/><color indexed=\"8\"/><name val=\"Geneva\"/></font><font><sz val=\"18\"/><color indexed=\"8\"/><name val=\"Arial\"/></font></fonts><fills count=\"2\"><fill><patternFill patternType=\"none\"/></fill><fill><patternFill patternType=\"gray125\"/></fill></fills><borders count=\"1\"><border><left/><right/><top/><bottom/><diagonal/></border></borders><dxfs count=\"0\"/><tableStyles count=\"0\"/><colors><indexedColors><rgbColor rgb=\"ff000000\"/><rgbColor rgb=\"ffffffff\"/><rgbColor rgb=\"ffff0000\"/><rgbColor rgb=\"ff00ff00\"/><rgbColor rgb=\"ff0000ff\"/><rgbColor rgb=\"ffffff00\"/><rgbColor rgb=\"ffff00ff\"/><rgbColor rgb=\"ff00ffff\"/><rgbColor rgb=\"ff000000\"/><rgbColor rgb=\"ffffffff\"/><rgbColor rgb=\"ff878787\"/><rgbColor rgb=\"fff9f9f9\"/></indexedColors></colors></styleSheet>\n");
5442
5566
  zipExcel.add("xl/theme/theme1.xml", XML_DECL + "<a:theme xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" name=\"Office Theme\"><a:themeElements><a:clrScheme name=\"Office\"><a:dk1><a:sysClr val=\"windowText\" lastClr=\"000000\"/></a:dk1><a:lt1><a:sysClr val=\"window\" lastClr=\"FFFFFF\"/></a:lt1><a:dk2><a:srgbClr val=\"44546A\"/></a:dk2><a:lt2><a:srgbClr val=\"E7E6E6\"/></a:lt2><a:accent1><a:srgbClr val=\"4472C4\"/></a:accent1><a:accent2><a:srgbClr val=\"ED7D31\"/></a:accent2><a:accent3><a:srgbClr val=\"A5A5A5\"/></a:accent3><a:accent4><a:srgbClr val=\"FFC000\"/></a:accent4><a:accent5><a:srgbClr val=\"5B9BD5\"/></a:accent5><a:accent6><a:srgbClr val=\"70AD47\"/></a:accent6><a:hlink><a:srgbClr val=\"0563C1\"/></a:hlink><a:folHlink><a:srgbClr val=\"954F72\"/></a:folHlink></a:clrScheme><a:fontScheme name=\"Office\"><a:majorFont><a:latin typeface=\"Calibri Light\" panose=\"020F0302020204030204\"/><a:ea typeface=\"\"/><a:cs typeface=\"\"/><a:font script=\"Jpan\" typeface=\"Yu Gothic Light\"/><a:font script=\"Hang\" typeface=\"맑은 고딕\"/><a:font script=\"Hans\" typeface=\"DengXian Light\"/><a:font script=\"Hant\" typeface=\"新細明體\"/><a:font script=\"Arab\" typeface=\"Times New Roman\"/><a:font script=\"Hebr\" typeface=\"Times New Roman\"/><a:font script=\"Thai\" typeface=\"Tahoma\"/><a:font script=\"Ethi\" typeface=\"Nyala\"/><a:font script=\"Beng\" typeface=\"Vrinda\"/><a:font script=\"Gujr\" typeface=\"Shruti\"/><a:font script=\"Khmr\" typeface=\"MoolBoran\"/><a:font script=\"Knda\" typeface=\"Tunga\"/><a:font script=\"Guru\" typeface=\"Raavi\"/><a:font script=\"Cans\" typeface=\"Euphemia\"/><a:font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/><a:font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/><a:font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/><a:font script=\"Thaa\" typeface=\"MV Boli\"/><a:font script=\"Deva\" typeface=\"Mangal\"/><a:font script=\"Telu\" typeface=\"Gautami\"/><a:font script=\"Taml\" typeface=\"Latha\"/><a:font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/><a:font script=\"Orya\" typeface=\"Kalinga\"/><a:font script=\"Mlym\" typeface=\"Kartika\"/><a:font script=\"Laoo\" typeface=\"DokChampa\"/><a:font script=\"Sinh\" typeface=\"Iskoola Pota\"/><a:font script=\"Mong\" typeface=\"Mongolian Baiti\"/><a:font script=\"Viet\" typeface=\"Times New Roman\"/><a:font script=\"Uigh\" typeface=\"Microsoft Uighur\"/><a:font script=\"Geor\" typeface=\"Sylfaen\"/></a:majorFont><a:minorFont><a:latin typeface=\"Calibri\" panose=\"020F0502020204030204\"/><a:ea typeface=\"\"/><a:cs typeface=\"\"/><a:font script=\"Jpan\" typeface=\"Yu Gothic\"/><a:font script=\"Hang\" typeface=\"맑은 고딕\"/><a:font script=\"Hans\" typeface=\"DengXian\"/><a:font script=\"Hant\" typeface=\"新細明體\"/><a:font script=\"Arab\" typeface=\"Arial\"/><a:font script=\"Hebr\" typeface=\"Arial\"/><a:font script=\"Thai\" typeface=\"Tahoma\"/><a:font script=\"Ethi\" typeface=\"Nyala\"/><a:font script=\"Beng\" typeface=\"Vrinda\"/><a:font script=\"Gujr\" typeface=\"Shruti\"/><a:font script=\"Khmr\" typeface=\"DaunPenh\"/><a:font script=\"Knda\" typeface=\"Tunga\"/><a:font script=\"Guru\" typeface=\"Raavi\"/><a:font script=\"Cans\" typeface=\"Euphemia\"/><a:font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/><a:font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/><a:font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/><a:font script=\"Thaa\" typeface=\"MV Boli\"/><a:font script=\"Deva\" typeface=\"Mangal\"/><a:font script=\"Telu\" typeface=\"Gautami\"/><a:font script=\"Taml\" typeface=\"Latha\"/><a:font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/><a:font script=\"Orya\" typeface=\"Kalinga\"/><a:font script=\"Mlym\" typeface=\"Kartika\"/><a:font script=\"Laoo\" typeface=\"DokChampa\"/><a:font script=\"Sinh\" typeface=\"Iskoola Pota\"/><a:font script=\"Mong\" typeface=\"Mongolian Baiti\"/><a:font script=\"Viet\" typeface=\"Arial\"/><a:font script=\"Uigh\" typeface=\"Microsoft Uighur\"/><a:font script=\"Geor\" typeface=\"Sylfaen\"/></a:minorFont></a:fontScheme><a:fmtScheme name=\"Office\"><a:fillStyleLst><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"110000\"/><a:satMod val=\"105000\"/><a:tint val=\"67000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"105000\"/><a:satMod val=\"103000\"/><a:tint val=\"73000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"105000\"/><a:satMod val=\"109000\"/><a:tint val=\"81000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:satMod val=\"103000\"/><a:lumMod val=\"102000\"/><a:tint val=\"94000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:satMod val=\"110000\"/><a:lumMod val=\"100000\"/><a:shade val=\"100000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"99000\"/><a:satMod val=\"120000\"/><a:shade val=\"78000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill></a:fillStyleLst><a:lnStyleLst><a:ln w=\"6350\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln><a:ln w=\"12700\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln><a:ln w=\"19050\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad=\"57150\" dist=\"19050\" dir=\"5400000\" algn=\"ctr\" rotWithShape=\"0\"><a:srgbClr val=\"000000\"><a:alpha val=\"63000\"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:solidFill><a:schemeClr val=\"phClr\"><a:tint val=\"95000\"/><a:satMod val=\"170000\"/></a:schemeClr></a:solidFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:tint val=\"93000\"/><a:satMod val=\"150000\"/><a:shade val=\"98000\"/><a:lumMod val=\"102000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:tint val=\"98000\"/><a:satMod val=\"130000\"/><a:shade val=\"90000\"/><a:lumMod val=\"103000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:shade val=\"63000\"/><a:satMod val=\"120000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements><a:objectDefaults/><a:extraClrSchemeLst/><a:extLst><a:ext uri=\"{05A4C25C-085E-4340-85A3-A5531E510DB2}\"><thm15:themeFamily xmlns:thm15=\"http://schemas.microsoft.com/office/thememl/2012/main\" name=\"Office Theme\" id=\"{62F939B6-93AF-4DB8-9C6B-D6C7DFDC589F}\" vid=\"{4A3C46E8-61CC-4603-A589-7422A47A8E4A}\"/></a:ext></a:extLst></a:theme>");
5443
5567
  zipExcel.add("xl/workbook.xml", XML_DECL + "<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" mc:Ignorable=\"x15\" xmlns:x15=\"http://schemas.microsoft.com/office/spreadsheetml/2010/11/main\"><fileVersion appName=\"xl\" lastEdited=\"7\" lowestEdited=\"6\" rupBuild=\"10507\"/><workbookPr/><bookViews><workbookView xWindow=\"0\" yWindow=\"500\" windowWidth=\"20960\" windowHeight=\"15960\"/></bookViews><sheets><sheet name=\"Sheet1\" sheetId=\"1\" r:id=\"rId1\"/></sheets><calcPr calcId=\"0\" concurrentCalc=\"0\"/></workbook>\n");
5444
- zipExcel.add("xl/worksheets/_rels/sheet1.xml.rels", XML_DECL + relationships([relationship$2("rId1", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table", "../tables/table1.xml")]) + "\n");
5568
+ zipExcel.add("xl/worksheets/_rels/sheet1.xml.rels", XML_DECL + relationships([relationship$2("rId1", OFFICE_REL + "table", "../tables/table1.xml")]) + "\n");
5445
5569
  zipExcel.add("xl/sharedStrings.xml", buildXlsxSharedStrings(chartObject, data, intBubbleCols, IS_MULTI_CAT_AXES));
5446
5570
  zipExcel.add("xl/tables/table1.xml", buildXlsxTable(chartObject, data, intBubbleCols));
5447
5571
  zipExcel.add("xl/worksheets/sheet1.xml", buildXlsxSheet(chartObject, data, intBubbleCols, IS_MULTI_CAT_AXES));
@@ -5622,7 +5746,7 @@ function buildXlsxSheet(chartObject, data, intBubbleCols, IS_MULTI_CAT_AXES) {
5622
5746
  * @return {string} the chart part's `.rels` XML
5623
5747
  */
5624
5748
  function buildChartRelsXml(embeddingTarget) {
5625
- return XML_DECL + relationships([relationship$2("rId1", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package", embeddingTarget)]);
5749
+ return XML_DECL + relationships([relationship$2("rId1", OFFICE_REL + "package", embeddingTarget)]);
5626
5750
  }
5627
5751
  /**
5628
5752
  * Build the `.rels` for a chartEx chart part: the embedded workbook (rId1, via `<cx:externalData>`)
@@ -5634,7 +5758,7 @@ function buildChartRelsXml(embeddingTarget) {
5634
5758
  */
5635
5759
  function buildChartExRelsXml(embeddingTarget, colorsTarget, styleTarget) {
5636
5760
  return XML_DECL + relationships([
5637
- relationship$2("rId1", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package", embeddingTarget),
5761
+ relationship$2("rId1", OFFICE_REL + "package", embeddingTarget),
5638
5762
  relationship$2("rId2", "http://schemas.microsoft.com/office/2011/relationships/chartColorStyle", colorsTarget),
5639
5763
  relationship$2("rId3", "http://schemas.microsoft.com/office/2011/relationships/chartStyle", styleTarget)
5640
5764
  ]);
@@ -5752,6 +5876,63 @@ function encodeSlideMediaRels(layout, runtime, onMediaError = "throw") {
5752
5876
  return imageProms;
5753
5877
  }
5754
5878
  //#endregion
5879
+ //#region src/gen/prepare.ts
5880
+ /**
5881
+ * ts-pptx: the pre-serialization pass
5882
+ *
5883
+ * Everything that must happen to authored slide state *before* any XML is built. Two callers
5884
+ * reach serialization and both need it:
5885
+ *
5886
+ * - `package/assemble.ts` — the normal `write`/`stream`/`writeFile` path, building a
5887
+ * complete `.pptx`.
5888
+ * - `PresentationCore.extractSlides()` — serializes slide bodies only, for splicing into
5889
+ * an already-loaded deck via `Presentation.appendSlides()` (`ts-pptx/read`).
5890
+ *
5891
+ * The second used to reimplement the first inline, kept in step by a comment that said
5892
+ * "exactly as ... does". Nothing failed when they drifted: `extractSlides` would simply emit
5893
+ * slides that did not match what a normal write produces, and no test compares the two.
5894
+ *
5895
+ * Two primitives rather than one combined pass, because the package path has work of its own
5896
+ * that must land *between* them: it de-duplicates identical media across the deck (which
5897
+ * needs the encoded bytes {@link encodeMediaForTargets} produces) and assigns package-unique
5898
+ * chart part filenames, both before any slide content is baked. Exposing the halves lets
5899
+ * `assemble` keep its established order exactly while still sharing the code.
5900
+ *
5901
+ * The order across the two is load-bearing:
5902
+ *
5903
+ * 1. **Media first.** It is the only asynchronous step, and the sync XML pass reads the
5904
+ * `rel.data` it populates.
5905
+ * 2. **Backfill placeholders**, so a slide inherits its layout's placeholder objects before
5906
+ * anything measures or serializes them.
5907
+ * 3. **Bake measured fit last**, because it measures the text step 2 may have just added and
5908
+ * writes the `fontScale` the sync XML pass then reads.
5909
+ *
5910
+ * The two functions take different target sets on purpose: media lives on layouts and the
5911
+ * master as well as on slides, while placeholder backfill and measured fit apply only to
5912
+ * slides. `extractSlides` passes only its slides to both, because it emits no layout or
5913
+ * master parts.
5914
+ */
5915
+ /**
5916
+ * Step 1: load and base64-encode every media rel on the given targets, populating `rel.data`.
5917
+ * @param {ReadonlyArray<PresSlideInternal | SlideLayoutInternal>} targets - media-bearing targets (slides, layouts, master)
5918
+ * @param {RuntimeAdapter} runtime - runtime adapter used to load media
5919
+ * @param {'throw' | 'placeholder'} onMediaError - failure policy for unloadable media
5920
+ */
5921
+ async function encodeMediaForTargets(targets, runtime, onMediaError) {
5922
+ const promises = [];
5923
+ for (const target of targets) promises.push(...encodeSlideMediaRels(target, runtime, onMediaError));
5924
+ await Promise.all(promises);
5925
+ }
5926
+ /**
5927
+ * Steps 2 and 3: backfill inherited layout placeholders, then bake measured text fit.
5928
+ * @param {PresSlideInternal[]} slides - the slides to prepare
5929
+ * @param {FontMetricsRegistry} fontMetrics - registered font metrics; an empty registry skips the fit bake
5930
+ */
5931
+ function bakeSlideContent(slides, fontMetrics) {
5932
+ for (const slide of slides) if (slide._slideLayout) addPlaceholdersToSlideLayouts(slide);
5933
+ applyMeasuredFit(slides, fontMetrics);
5934
+ }
5935
+ //#endregion
5755
5936
  //#region src/gen/slide/shape-ids.ts
5756
5937
  /**
5757
5938
  * Every object a slide renders, paired with the `<p:cNvPr>` id it is rendered with: top-level
@@ -5761,7 +5942,7 @@ function encodeSlideMediaRels(layout, runtime, onMediaError = "throw") {
5761
5942
  * This **mirrors** the allocation in `slideObjectToXml`, which hands out ids as it walks the tree.
5762
5943
  * A reference that must name an id *before* the walk reaches it — a connector's `<a:stCxn>`, an
5763
5944
  * animation's `<p:spTgt spid>` — cannot wait for that, so it resolves through this map instead.
5764
- * The two must stay in step: `test/regression/group-shapes.test.js` parses the emitted `cNvPr` ids
5945
+ * The two must stay in step: `test/regression/shape/group-shapes.test.js` parses the emitted `cNvPr` ids
5765
5946
  * back out and asserts each reference points at the shape it names, so drift fails there.
5766
5947
  * @param slideObjects - the slide's top-level objects
5767
5948
  * @returns each object's `<p:cNvPr>` id, keyed by object identity, in id order
@@ -6482,13 +6663,13 @@ function genXmlParagraphProperties(textObj, isDefault) {
6482
6663
  const isPictureBullet = !!(bulletImage && (bulletImage.path || bulletImage.data));
6483
6664
  if (opts.bullet?.indent) bulletMarL = valToPts(opts.bullet.indent);
6484
6665
  if (opts.bullet.color && !isPictureBullet) strXmlBulletColor = el("a:buClr", null, raw(createColorElement(opts.bullet.color)));
6485
- let bulletSizePct = PERCENT_SCALE;
6666
+ let bulletSizePct;
6486
6667
  if (opts.bullet.size !== void 0) {
6487
6668
  const bulletSize = Number(opts.bullet.size);
6488
6669
  if (isNaN(bulletSize) || bulletSize < 25 || bulletSize > 400) warn("bullet/size-out-of-range", "`bullet.size` must be a percentage between 25 and 400!");
6489
6670
  else bulletSizePct = Math.round(bulletSize * FIXED_PCT_PER_PERCENT);
6490
6671
  }
6491
- const strXmlBulletSize = voidEl("a:buSzPct", { val: bulletSizePct });
6672
+ const strXmlBulletSize = bulletSizePct === void 0 ? "" : voidEl("a:buSzPct", { val: bulletSizePct });
6492
6673
  const strXmlBulletFont = opts.bullet.fontFace ? voidEl("a:buFont", { typeface: opts.bullet.fontFace }) : "";
6493
6674
  const bulletIndentAttrs = () => ` marL="${opts.indentLevel && opts.indentLevel > 0 ? bulletMarL + bulletMarL * opts.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
6494
6675
  if (isPictureBullet) {
@@ -6523,7 +6704,7 @@ function genXmlParagraphProperties(textObj, isDefault) {
6523
6704
  }
6524
6705
  } else if (opts.bullet) {
6525
6706
  paragraphPropXml += ` marL="${opts.indentLevel && opts.indentLevel > 0 ? bulletMarL + bulletMarL * opts.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
6526
- strXmlBullet = voidEl("a:buSzPct", { val: "100000" }) + buChar("&#x2022;");
6707
+ strXmlBullet = buChar("&#x2022;");
6527
6708
  } else if (!opts.bullet) {
6528
6709
  paragraphPropXml += " indent=\"0\" marL=\"0\"";
6529
6710
  strXmlBullet = voidEl("a:buNone");
@@ -6655,7 +6836,7 @@ function groupRunsIntoLines(arrTextObjects, opts) {
6655
6836
  arrLines.push(arrTexts);
6656
6837
  arrTexts = [];
6657
6838
  }
6658
- } else if (arrTexts.length > 0 && textObj.options.bullet && arrTexts.length > 0) {
6839
+ } else if (arrTexts.length > 0 && textObj.options.bullet) {
6659
6840
  arrLines.push(arrTexts);
6660
6841
  arrTexts = [];
6661
6842
  textObj.options.breakLine = false;
@@ -7299,28 +7480,31 @@ function genXmlCustGeom(options, cx, cy, layout) {
7299
7480
  * ts-pptx: DrawingML image sizing & cropping
7300
7481
  *
7301
7482
  * Build the `<a:srcRect>` (+ `<a:stretch>`) blipFill children for the image
7302
- * sizing modes (`cover` / `contain` / `crop`) and for explicit percentage crops.
7483
+ * sizing modes (`cover` / `contain` / `crop` / `stretch`), for the implicit
7484
+ * aspect-correct placement a vector source gets, and for explicit percentage crops.
7303
7485
  */
7304
7486
  /** Every `<a:srcRect>` below is followed by this same fill directive. */
7305
7487
  const STRETCH = el("a:stretch", null, raw(voidEl("a:fillRect")));
7488
+ /** The `<a:srcRect>` fitting `imgSize` into `boxDim`, followed by the fill directive. */
7489
+ function fitSrcRect(type, imgSize, boxDim) {
7490
+ const { l, r, t, b } = fitSrcRectPercents(type, imgSize, boxDim);
7491
+ return voidEl("a:srcRect", {
7492
+ l,
7493
+ r,
7494
+ t,
7495
+ b
7496
+ }) + STRETCH;
7497
+ }
7306
7498
  const ImageSizingXml = {
7307
7499
  cover: function(imgSize, boxDim) {
7308
- const { l, r, t, b } = fitSrcRectPercents("cover", imgSize, boxDim);
7309
- return voidEl("a:srcRect", {
7310
- l,
7311
- r,
7312
- t,
7313
- b
7314
- }) + STRETCH;
7500
+ return fitSrcRect("cover", imgSize, boxDim);
7315
7501
  },
7316
7502
  contain: function(imgSize, boxDim) {
7317
- const { l, r, t, b } = fitSrcRectPercents("contain", imgSize, boxDim);
7318
- return voidEl("a:srcRect", {
7319
- l,
7320
- r,
7321
- t,
7322
- b
7323
- }) + STRETCH;
7503
+ return fitSrcRect("contain", imgSize, boxDim);
7504
+ },
7505
+ /** No source rectangle at all: the image fills the box, aspect ratio be damned. */
7506
+ stretch: function() {
7507
+ return STRETCH;
7324
7508
  },
7325
7509
  crop: function(imgSize, boxDim) {
7326
7510
  const l = boxDim.x;
@@ -7345,6 +7529,23 @@ const ImageSizingXml = {
7345
7529
  }
7346
7530
  };
7347
7531
  /**
7532
+ * The blipFill children for a vector source the caller gave no `sizing` for: letterbox it to its
7533
+ * own aspect ratio inside its frame, the way `sizing: 'contain'` would — an SVG carries an
7534
+ * intrinsic ratio, and stretching a glyph into a box that disagrees with it is never the intent.
7535
+ *
7536
+ * Returns `null` when the two ratios already agree, which is the overwhelmingly common case (a
7537
+ * square icon in a square box). The caller then emits the plain `<a:stretch>` it always did, so
7538
+ * this default costs nothing — neither bytes nor a behaviour change — until it actually differs.
7539
+ * @param imgSize - the vector's intrinsic size (viewBox or width/height)
7540
+ * @param boxDim - the picture's displayed frame
7541
+ * @returns the `<a:srcRect>` + `<a:stretch>` children, or `null` if no letterboxing is needed
7542
+ */
7543
+ function genXmlVectorAspectFit(imgSize, boxDim) {
7544
+ const { l, r, t, b } = fitSrcRectPercents("contain", imgSize, boxDim);
7545
+ if (l === 0 && r === 0 && t === 0 && b === 0) return null;
7546
+ return fitSrcRect("contain", imgSize, boxDim);
7547
+ }
7548
+ /**
7348
7549
  * Build an explicit `<a:srcRect>` crop from percentage edge insets (0–100), emitted verbatim.
7349
7550
  * Each inset is the percent of the *source* image trimmed from that edge; values serialize in
7350
7551
  * 1000ths of a percent (DrawingML `ST_Percentage`, where 100% = 100000). Unlike `sizing: 'crop'`
@@ -7381,10 +7582,12 @@ function genXmlImageCrop(crop, objectName) {
7381
7582
  function renderImageObject(slideItemObj, idx, slide, x, y, cx, cy, imgWidth, imgHeight, placeholderObj, locationAttrs, sizing, rounding) {
7382
7583
  let strSlideXml = "";
7383
7584
  slideItemObj.options = slideItemObj.options || {};
7585
+ const mediaRel = (slide._relsMedia || []).find((rel) => rel.rId === slideItemObj.imageRid);
7586
+ const relData = mediaRel?.data;
7587
+ const naturalSize = () => typeof relData === "string" ? getImageSizeFromBase64(relData) : null;
7384
7588
  if (slideItemObj.options._szAuto) {
7385
7589
  const szAuto = slideItemObj.options._szAuto;
7386
- const relData = (slide._relsMedia || []).find((rel) => rel.rId === slideItemObj.imageRid)?.data;
7387
- const natural = typeof relData === "string" ? getImageSizeFromBase64(relData) : null;
7590
+ const natural = naturalSize();
7388
7591
  if (natural) {
7389
7592
  if (szAuto.w && szAuto.h) {
7390
7593
  cx = pixelsToEmu(natural.w, 96);
@@ -7415,7 +7618,8 @@ function renderImageObject(slideItemObj, idx, slide, x, y, cx, cy, imgWidth, img
7415
7618
  imgOpts.biLevel ? raw(voidEl("a:biLevel", { thresh: Math.round(imgOpts.biLevel.threshold * PERCENT_SCALE) })) : null
7416
7619
  ];
7417
7620
  strSlideXml += "<p:blipFill>";
7418
- if ((slide._relsMedia || []).find((rel) => rel.rId === slideItemObj.imageRid)?.extn === "svg") strSlideXml += el("a:blip", { "r:embed": `rId${(slideItemObj.imageRid ?? 0) - 1}` }, [...blipEffects(" "), raw(el("a:extLst", null, raw(el("a:ext", { uri: "{96DAC541-7B7A-43D3-8B79-37D633B846F1}" }, raw(voidEl("asvg:svgBlip", {
7621
+ const isVector = mediaRel?.extn === "svg";
7622
+ if (isVector) strSlideXml += el("a:blip", { "r:embed": `rId${(slideItemObj.imageRid ?? 0) - 1}` }, [...blipEffects(" "), raw(el("a:extLst", null, raw(el("a:ext", { uri: "{96DAC541-7B7A-43D3-8B79-37D633B846F1}" }, raw(voidEl("asvg:svgBlip", {
7419
7623
  "xmlns:asvg": "http://schemas.microsoft.com/office/drawing/2016/SVG/main",
7420
7624
  "r:embed": `rId${slideItemObj.imageRid}`
7421
7625
  }, { openPrefix: " " })), {
@@ -7439,8 +7643,7 @@ function renderImageObject(slideItemObj, idx, slide, x, y, cx, cy, imgWidth, img
7439
7643
  h: imgHeight
7440
7644
  };
7441
7645
  if (sizing.type === "cover" || sizing.type === "contain") {
7442
- const relData = (slide._relsMedia || []).find((rel) => rel.rId === slideItemObj.imageRid)?.data;
7443
- const natural = typeof relData === "string" ? getImageSizeFromBase64(relData) : null;
7646
+ const natural = naturalSize();
7444
7647
  if (natural) cropSize = natural;
7445
7648
  else warn("image/unmeasurable-natural-size", `sizing '${sizing.type}' could not measure natural dimensions for image "${slideItemObj.options.objectName}"; falling back to displayed aspect ratio (crop may be inexact). Provide a raster image (PNG/JPEG/GIF/BMP/WebP) or an SVG with width/height or a viewBox to enable an aspect-correct crop.`);
7446
7649
  }
@@ -7452,7 +7655,14 @@ function renderImageObject(slideItemObj, idx, slide, x, y, cx, cy, imgWidth, img
7452
7655
  });
7453
7656
  imgWidth = boxW;
7454
7657
  imgHeight = boxH;
7455
- } else strSlideXml += el("a:stretch", null, raw(voidEl("a:fillRect")), { openPrefix: " " });
7658
+ } else {
7659
+ const natural = isVector ? naturalSize() : null;
7660
+ const aspectFit = natural ? genXmlVectorAspectFit(natural, {
7661
+ w: imgWidth,
7662
+ h: imgHeight
7663
+ }) : null;
7664
+ strSlideXml += aspectFit ?? el("a:stretch", null, raw(voidEl("a:fillRect")), { openPrefix: " " });
7665
+ }
7456
7666
  strSlideXml += "</p:blipFill>";
7457
7667
  strSlideXml += "<p:spPr>";
7458
7668
  strSlideXml += el("a:xfrm", locationAttrs, [raw(voidEl("a:off", {
@@ -7526,6 +7736,216 @@ function renderMediaObject(slideItemObj, idx, x, y, cx, cy, locationAttrs) {
7526
7736
  ]);
7527
7737
  }
7528
7738
  //#endregion
7739
+ //#region src/gen/slide/objects/model3d.ts
7740
+ /** The `am3d` namespace, doubling as `a:graphicData@uri` and the `mc:Choice Requires` token's URI. */
7741
+ const AM3D_NS = "http://schemas.microsoft.com/office/drawing/2017/model3d";
7742
+ /** Denominator of every `am3d` rational element (`@n`/`@d`). */
7743
+ const RATIO_DEN = 1e6;
7744
+ /**
7745
+ * `am3d:raster@rName`/`@rVer` — the renderer that produced the cached preview. PowerPoint stamps
7746
+ * its own renderer here; ts-pptx did not rasterize anything, but the attributes are what PowerPoint
7747
+ * writes and the element is where the preview blip lives, so they are reproduced verbatim.
7748
+ */
7749
+ const RASTER = {
7750
+ rName: "Office3DRenderer",
7751
+ rVer: "16.0.8326"
7752
+ };
7753
+ /**
7754
+ * `am3d:objViewport@viewportSz`. Bounding-box derived in PowerPoint's output (a cube and a slab
7755
+ * gave different values); the cube's is emitted, matching the fixed default camera.
7756
+ */
7757
+ const OBJ_VIEWPORT_SZ = 3338805;
7758
+ /** The three point lights PowerPoint writes for every inserted model — key, fill and rim. */
7759
+ const PT_LIGHTS = [
7760
+ {
7761
+ clr: {
7762
+ r: 1e5,
7763
+ g: 75e3,
7764
+ b: 5e4
7765
+ },
7766
+ intensityN: 9765625,
7767
+ pos: {
7768
+ x: 21959998,
7769
+ y: 70920001,
7770
+ z: 16344003
7771
+ }
7772
+ },
7773
+ {
7774
+ clr: {
7775
+ r: 4e4,
7776
+ g: 6e4,
7777
+ b: 95e3
7778
+ },
7779
+ intensityN: 1225e4,
7780
+ pos: {
7781
+ x: -37964106,
7782
+ y: 51130435,
7783
+ z: 57631972
7784
+ }
7785
+ },
7786
+ {
7787
+ clr: {
7788
+ r: 86837,
7789
+ g: 72700,
7790
+ b: 1e5
7791
+ },
7792
+ intensityN: 3125e3,
7793
+ pos: {
7794
+ x: -37739122,
7795
+ y: 58056624,
7796
+ z: -34769649
7797
+ }
7798
+ }
7799
+ ];
7800
+ /** `am3d:ambientLight` — neutral 50% scRGB at half illuminance, constant like the point lights. */
7801
+ const AMBIENT = {
7802
+ clr: {
7803
+ r: 5e4,
7804
+ g: 5e4,
7805
+ b: 5e4
7806
+ },
7807
+ illuminanceN: 5e5
7808
+ };
7809
+ /** `<am3d:clr><a:scrgbClr …/></am3d:clr>` — the colour wrapper both light kinds share. */
7810
+ function lightColor(clr) {
7811
+ return el("am3d:clr", null, raw(voidEl("a:scrgbClr", clr)));
7812
+ }
7813
+ /** The `am3d:camera` block: eye position, up vector, aim point and perspective fov. */
7814
+ function cameraEl(model) {
7815
+ return el("am3d:camera", null, [
7816
+ raw(voidEl("am3d:pos", model.pos)),
7817
+ raw(voidEl("am3d:up", model.up)),
7818
+ raw(voidEl("am3d:lookAt", model.lookAt)),
7819
+ raw(voidEl("am3d:perspective", { fov: model.fov }))
7820
+ ]);
7821
+ }
7822
+ /**
7823
+ * The `am3d:trans` block: model-unit scale plus an identity placement. Only `meterPerModelUnit`
7824
+ * varies — `preTrans`/`scale`/`rot`/`postTrans` are the identity PowerPoint writes for a model
7825
+ * inserted without any subsequent 3D manipulation, and `am3d:rot` is emitted attribute-less.
7826
+ */
7827
+ function transEl(model) {
7828
+ const unit = {
7829
+ n: RATIO_DEN,
7830
+ d: RATIO_DEN
7831
+ };
7832
+ return el("am3d:trans", null, [
7833
+ raw(voidEl("am3d:meterPerModelUnit", {
7834
+ n: model.meterPerModelUnitN,
7835
+ d: RATIO_DEN
7836
+ })),
7837
+ raw(voidEl("am3d:preTrans", {
7838
+ dx: 0,
7839
+ dy: 0,
7840
+ dz: 0
7841
+ })),
7842
+ raw(el("am3d:scale", null, [
7843
+ raw(voidEl("am3d:sx", unit)),
7844
+ raw(voidEl("am3d:sy", unit)),
7845
+ raw(voidEl("am3d:sz", unit))
7846
+ ])),
7847
+ raw(voidEl("am3d:rot")),
7848
+ raw(voidEl("am3d:postTrans", {
7849
+ dx: 0,
7850
+ dy: 0,
7851
+ dz: 0
7852
+ }))
7853
+ ]);
7854
+ }
7855
+ /** The fixed lighting rig: one ambient light and three point lights, in PowerPoint's order. */
7856
+ function lightsXml() {
7857
+ return el("am3d:ambientLight", null, [raw(lightColor(AMBIENT.clr)), raw(voidEl("am3d:illuminance", {
7858
+ n: AMBIENT.illuminanceN,
7859
+ d: RATIO_DEN
7860
+ }))]) + PT_LIGHTS.map((light) => el("am3d:ptLight", { rad: 0 }, [
7861
+ raw(lightColor(light.clr)),
7862
+ raw(voidEl("am3d:intensity", {
7863
+ n: light.intensityN,
7864
+ d: RATIO_DEN
7865
+ })),
7866
+ raw(voidEl("am3d:pos", light.pos))
7867
+ ])).join("");
7868
+ }
7869
+ /** The `mc:Fallback` picture — the cached preview, drawn by everything that is not PowerPoint 2019+. */
7870
+ function fallbackPic(picId, objectName, altText, model, xf) {
7871
+ return el("p:pic", null, [
7872
+ raw(el("p:nvPicPr", null, [
7873
+ raw(cNvPrOpen(picId, objectName, altText) + "/>"),
7874
+ raw(el("p:cNvPicPr", null, raw(voidEl("a:picLocks", {
7875
+ noGrp: "1",
7876
+ noRot: "1",
7877
+ noChangeAspect: "1",
7878
+ noMove: "1",
7879
+ noResize: "1",
7880
+ noEditPoints: "1",
7881
+ noAdjustHandles: "1",
7882
+ noChangeArrowheads: "1",
7883
+ noChangeShapeType: "1",
7884
+ noCrop: "1"
7885
+ })))),
7886
+ raw(voidEl("p:nvPr"))
7887
+ ])),
7888
+ raw(el("p:blipFill", null, [raw(voidEl("a:blip", { "r:embed": `rId${model.previewRid}` })), raw(el("a:stretch", null, raw(voidEl("a:fillRect"))))])),
7889
+ raw(el("p:spPr", null, [raw(el("a:xfrm", null, [raw(voidEl("a:off", {
7890
+ x: xf.x,
7891
+ y: xf.y
7892
+ })), raw(voidEl("a:ext", {
7893
+ cx: xf.cx,
7894
+ cy: xf.cy
7895
+ }))])), raw(el("a:prstGeom", { prst: "rect" }, raw(voidEl("a:avLst"))))]))
7896
+ ]);
7897
+ }
7898
+ /**
7899
+ * Render a `model3d` slide object to its `<mc:AlternateContent>` XML. The `mc:Choice` carries the
7900
+ * real `<p:graphicFrame>` (the `am3d:model3d` graphicData PowerPoint 2019+ renders live); the
7901
+ * `mc:Fallback` carries the cached preview picture. See `gen/define/model3d.ts`.
7902
+ */
7903
+ function renderModel3dObject(slideItemObj, idx, x, y, cx, cy) {
7904
+ const model = slideItemObj.model3d;
7905
+ if (!model) return "";
7906
+ const opts = slideItemObj.options || {};
7907
+ const altText = opts.altText || "";
7908
+ const spPr = el("am3d:spPr", null, [raw(el("a:xfrm", null, [raw(voidEl("a:off", {
7909
+ x: 0,
7910
+ y: 0
7911
+ })), raw(voidEl("a:ext", {
7912
+ cx,
7913
+ cy
7914
+ }))])), raw(el("a:prstGeom", { prst: "rect" }, raw(voidEl("a:avLst"))))]);
7915
+ const model3d = el("am3d:model3d", { "r:embed": `rId${model.modelRid}` }, [
7916
+ raw(spPr),
7917
+ raw(cameraEl(model)),
7918
+ raw(transEl(model)),
7919
+ raw(el("am3d:raster", RASTER, raw(voidEl("am3d:blip", { "r:embed": `rId${model.previewRid}` })))),
7920
+ raw(voidEl("am3d:objViewport", { viewportSz: OBJ_VIEWPORT_SZ })),
7921
+ raw(lightsXml())
7922
+ ]);
7923
+ const graphicFrame = el("p:graphicFrame", null, [
7924
+ raw(el("p:nvGraphicFramePr", null, [
7925
+ raw(cNvPrOpen(idx + 2, opts.objectName, altText) + "/>"),
7926
+ raw(el("p:cNvGraphicFramePr", null, raw(voidEl("a:graphicFrameLocks")))),
7927
+ raw(voidEl("p:nvPr"))
7928
+ ])),
7929
+ raw(el("p:xfrm", null, [raw(voidEl("a:off", {
7930
+ x,
7931
+ y
7932
+ })), raw(voidEl("a:ext", {
7933
+ cx,
7934
+ cy
7935
+ }))])),
7936
+ raw(el("a:graphic", null, raw(el("a:graphicData", { uri: AM3D_NS }, raw(model3d)))))
7937
+ ]);
7938
+ return el("mc:AlternateContent", { "xmlns:mc": MC_NS }, [raw(el("mc:Choice", {
7939
+ "xmlns:am3d": AM3D_NS,
7940
+ Requires: "am3d"
7941
+ }, raw(graphicFrame))), raw(el("mc:Fallback", null, raw(fallbackPic(idx + 2, opts.objectName, altText, model, {
7942
+ x,
7943
+ y,
7944
+ cx,
7945
+ cy
7946
+ }))))]);
7947
+ }
7948
+ //#endregion
7529
7949
  //#region src/gen/slide/objects/ole.ts
7530
7950
  /** VML namespace — declared by an OLE object's `mc:Choice Requires="v"` (no VML content is emitted). */
7531
7951
  const VML_NS = "urn:schemas-microsoft-com:vml";
@@ -7686,95 +8106,12 @@ function genTableCellBorderXml(cellBorder, diagonal) {
7686
8106
  }
7687
8107
  //#endregion
7688
8108
  //#region src/gen/drawingml/table-cell3d.ts
7689
- /** `ST_BevelPresetType`. */
7690
- const BEVEL_PRESETS = [
7691
- "relaxedInset",
7692
- "circle",
7693
- "slope",
7694
- "cross",
7695
- "angle",
7696
- "softRound",
7697
- "convex",
7698
- "coolSlant",
7699
- "divot",
7700
- "riblet",
7701
- "hardEdge",
7702
- "artDeco"
7703
- ];
7704
- /** `ST_PresetMaterialType`. */
7705
- const MATERIALS = [
7706
- "legacyMatte",
7707
- "legacyPlastic",
7708
- "legacyMetal",
7709
- "legacyWireframe",
7710
- "matte",
7711
- "plastic",
7712
- "metal",
7713
- "warmMatte",
7714
- "translucentPowder",
7715
- "powder",
7716
- "dkEdge",
7717
- "softEdge",
7718
- "clear",
7719
- "flat",
7720
- "softmetal"
7721
- ];
7722
- /** `ST_LightRigType`. */
7723
- const LIGHT_RIGS = [
7724
- "legacyFlat1",
7725
- "legacyFlat2",
7726
- "legacyFlat3",
7727
- "legacyFlat4",
7728
- "legacyNormal1",
7729
- "legacyNormal2",
7730
- "legacyNormal3",
7731
- "legacyNormal4",
7732
- "legacyHarsh1",
7733
- "legacyHarsh2",
7734
- "legacyHarsh3",
7735
- "legacyHarsh4",
7736
- "threePt",
7737
- "balanced",
7738
- "soft",
7739
- "harsh",
7740
- "flood",
7741
- "contrasting",
7742
- "morning",
7743
- "sunrise",
7744
- "sunset",
7745
- "chilly",
7746
- "freezing",
7747
- "flat",
7748
- "twoPt",
7749
- "glow",
7750
- "brightRoom"
7751
- ];
7752
- /** `ST_LightRigDirection`. */
7753
- const LIGHT_DIRECTIONS = [
7754
- "tl",
7755
- "t",
7756
- "tr",
7757
- "l",
7758
- "r",
7759
- "bl",
7760
- "b",
7761
- "br"
7762
- ];
7763
8109
  /**
7764
- * Vet one enumerated value before it reaches the XML. Anything outside its `ST_` union would
7765
- * make the slide part schema-invalid, and PowerPoint reports that as a corrupt file rather
7766
- * than as a mis-set option — so an unrecognized value is reported and the attribute dropped,
7767
- * leaving the renderer on the schema default.
8110
+ * Vet one enumerated value before it reaches the XML, reporting and dropping anything outside
8111
+ * its `ST_` union. See `ooxml/check-enum.ts` for why the write path drops rather than throws.
7768
8112
  */
7769
8113
  function resolveEnum(value, valid, field) {
7770
- if (value === void 0 || value === null) return null;
7771
- if (valid.includes(value)) return value;
7772
- warnOnce("table/invalid-cell3d", `table cell: cell3D \`${field}\` value \`${String(value)}\` is not valid and is ignored — use one of ${valid.join(", ")}.`, {
7773
- received: value,
7774
- field,
7775
- valid
7776
- });
7777
- return null;
8114
+ return checkEnumOrWarn(value, valid, "table/invalid-cell3d", `table cell: cell3D \`${field}\``, { field });
7778
8115
  }
7779
8116
  /**
7780
8117
  * A bevel dimension in EMU (`ST_PositiveCoordinate`), from a width in points.
@@ -7805,32 +8142,22 @@ function genTableCell3DXml(cell3D) {
7805
8142
  prst: resolveEnum(cell3D.preset, BEVEL_PRESETS, "preset")
7806
8143
  });
7807
8144
  const rig = resolveEnum(cell3D.lightRig?.rig, LIGHT_RIGS, "lightRig.rig");
7808
- const dir = resolveEnum(cell3D.lightRig?.dir, LIGHT_DIRECTIONS, "lightRig.dir");
8145
+ const dir = resolveEnum(cell3D.lightRig?.dir, LIGHT_RIG_DIRECTIONS, "lightRig.dir");
7809
8146
  const lightRig = rig && dir ? voidEl("a:lightRig", {
7810
8147
  rig,
7811
8148
  dir
7812
8149
  }) : "";
7813
8150
  if (cell3D.lightRig && !lightRig) warnOnce("table/invalid-cell3d", "table cell: cell3D `lightRig` needs both `rig` and `dir` (both are required by CT_LightRig); the light rig is dropped and the bevel takes the renderer default.", { received: cell3D.lightRig });
7814
- return el("a:cell3D", { prstMaterial: resolveEnum(cell3D.material, MATERIALS, "material") }, [raw(bevel), lightRig ? raw(lightRig) : null]);
8151
+ return el("a:cell3D", { prstMaterial: resolveEnum(cell3D.material, PRESET_MATERIALS, "material") }, [raw(bevel), lightRig ? raw(lightRig) : null]);
7815
8152
  }
7816
8153
  //#endregion
7817
8154
  //#region src/gen/slide/objects/table.ts
7818
- /** The two values `ST_TextHorzOverflowType` allows on `a:tcPr/@horzOverflow`. */
7819
- const HORZ_OVERFLOW_VALUES = ["clip", "overflow"];
7820
8155
  /**
7821
- * Validate a cell's `horzOverflow` before it reaches the XML. Anything outside
7822
- * `ST_TextHorzOverflowType` would make the slide part schema-invalid — and PowerPoint
7823
- * reports that as a corrupt file, not as a mis-set option — so an unrecognized value is
7824
- * reported and dropped instead of written. `undefined` (the common case) emits nothing.
8156
+ * Validate a cell's `horzOverflow` before it reaches the XML, reporting and dropping anything
8157
+ * outside `ST_TextHorzOverflowType`. `undefined` (the common case) emits nothing.
7825
8158
  */
7826
8159
  function resolveHorzOverflow(value) {
7827
- if (value === void 0 || value === null) return null;
7828
- if (HORZ_OVERFLOW_VALUES.includes(value)) return value;
7829
- warnOnce("table/invalid-horz-overflow", `table cell: horzOverflow \`${String(value)}\` is not a valid value and is ignored — use ${HORZ_OVERFLOW_VALUES.join(" or ")}.`, {
7830
- received: value,
7831
- valid: HORZ_OVERFLOW_VALUES
7832
- });
7833
- return null;
8160
+ return checkEnumOrWarn(value, TEXT_HORZ_OVERFLOW, "table/invalid-horz-overflow", "table cell: horzOverflow");
7834
8161
  }
7835
8162
  /**
7836
8163
  * Overlay `TableProps.outerBorder` onto one cell's own 4-side border tuple.
@@ -8295,7 +8622,6 @@ function renderZoomObject(slideItemObj, idx, x, y, cx, cy) {
8295
8622
  }
8296
8623
  //#endregion
8297
8624
  //#region src/gen/slide/object.ts
8298
- const PACKAGE_REL_NS$1 = "http://schemas.openxmlformats.org/package/2006/relationships";
8299
8625
  /** The MS-2007 `media` rel that pairs with an ECMA audio/video/online rel on the same Target. */
8300
8626
  const MS_MEDIA_REL = "http://schemas.microsoft.com/office/2007/relationships/media";
8301
8627
  /** Slide→chartEx-part relationship type (MS, not the ECMA `.../relationships/chart`). */
@@ -8506,6 +8832,9 @@ function slideObjectToXml(slide) {
8506
8832
  case "zoom":
8507
8833
  strSlideXml += renderZoomObject(slideItemObj, idx, x, y, cx, cy);
8508
8834
  break;
8835
+ case "model3d":
8836
+ strSlideXml += renderModel3dObject(slideItemObj, idx, x, y, cx, cy);
8837
+ break;
8509
8838
  case "group": {
8510
8839
  const groupChildren = slideItemObj._groupObjects || [];
8511
8840
  let innerXml = "";
@@ -8654,12 +8983,12 @@ function slideObjectRelationsToXml(slide, defaultRels) {
8654
8983
  lastRid = Math.max(lastRid, rel.rId);
8655
8984
  if (isHyperlinkRel(rel)) if (rel.data === "slide") rels.push(voidEl("Relationship", {
8656
8985
  Id: `rId${rel.rId}`,
8657
- Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide",
8986
+ Type: OFFICE_REL + "slide",
8658
8987
  Target: `slide${rel.Target}.xml`
8659
8988
  }));
8660
8989
  else rels.push(voidEl("Relationship", {
8661
8990
  Id: `rId${rel.rId}`,
8662
- Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
8991
+ Type: OFFICE_REL + "hyperlink",
8663
8992
  Target: rel.Target,
8664
8993
  TargetMode: "External"
8665
8994
  }));
@@ -8668,7 +8997,7 @@ function slideObjectRelationsToXml(slide, defaultRels) {
8668
8997
  lastRid = Math.max(lastRid, rel.rId);
8669
8998
  rels.push(voidEl("Relationship", {
8670
8999
  Id: `rId${rel.rId}`,
8671
- Type: rel.isChartEx ? CHARTEX_REL : "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart",
9000
+ Type: rel.isChartEx ? CHARTEX_REL : OFFICE_REL + "chart",
8672
9001
  Target: rel.Target
8673
9002
  }));
8674
9003
  });
@@ -8683,10 +9012,11 @@ function slideObjectRelationsToXml(slide, defaultRels) {
8683
9012
  });
8684
9013
  lastRid = Math.max(lastRid, rel.rId);
8685
9014
  if (rel.oleRelType) rels.push(media(rel.oleRelType));
8686
- else if (relType.includes("image")) rels.push(media("http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"));
8687
- else if (relType.includes("audio")) rels.push(hasTarget(relTarget) ? media(MS_MEDIA_REL) : media("http://schemas.openxmlformats.org/officeDocument/2006/relationships/audio"));
8688
- else if (relType.includes("video")) rels.push(hasTarget(relTarget) ? media(MS_MEDIA_REL) : media("http://schemas.openxmlformats.org/officeDocument/2006/relationships/video"));
8689
- else if (relType.includes("online")) rels.push(hasTarget(relTarget) ? media(MS_MEDIA_REL, "External") : media("http://schemas.openxmlformats.org/officeDocument/2006/relationships/video", "External"));
9015
+ else if (rel.model3dRelType) rels.push(media(rel.model3dRelType));
9016
+ else if (relType.includes("image")) rels.push(media(OFFICE_REL + "image"));
9017
+ else if (relType.includes("audio")) rels.push(hasTarget(relTarget) ? media(MS_MEDIA_REL) : media(OFFICE_REL + "audio"));
9018
+ else if (relType.includes("video")) rels.push(hasTarget(relTarget) ? media(MS_MEDIA_REL) : media(OFFICE_REL + "video"));
9019
+ else if (relType.includes("online")) rels.push(hasTarget(relTarget) ? media(MS_MEDIA_REL, "External") : media(OFFICE_REL + "video", "External"));
8690
9020
  });
8691
9021
  defaultRels.forEach((rel, idx) => {
8692
9022
  rels.push(voidEl("Relationship", {
@@ -8695,7 +9025,7 @@ function slideObjectRelationsToXml(slide, defaultRels) {
8695
9025
  Target: rel.target
8696
9026
  }));
8697
9027
  });
8698
- return XML_DECL + "\r\n" + el("Relationships", { xmlns: PACKAGE_REL_NS$1 }, rels.map(raw));
9028
+ return XML_DECL + "\r\n" + el("Relationships", { xmlns: PACKAGE_REL_NS }, rels.map(raw));
8699
9029
  }
8700
9030
  //#endregion
8701
9031
  //#region src/gen/slide/slide.ts
@@ -8773,7 +9103,6 @@ function getLayoutIdxForSlide(slides, slideLayouts, slideNumber) {
8773
9103
  }
8774
9104
  //#endregion
8775
9105
  //#region src/gen/slide/notes.ts
8776
- const PACKAGE_REL_NS = "http://schemas.openxmlformats.org/package/2006/relationships";
8777
9106
  /**
8778
9107
  * Collect the speaker-notes runs for a slide (flattened across any number of `addNotes()` calls).
8779
9108
  * @param {PresSlideInternal} slide - the slide object
@@ -9257,19 +9586,19 @@ function makeXmlNotesSlide(slide) {
9257
9586
  function makeXmlNotesSlideRel(slide, slideNumber) {
9258
9587
  const hlinkRels = buildNotesSlideRels(slide).map((rel) => voidEl("Relationship", {
9259
9588
  Id: `rId${rel.rId}`,
9260
- Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
9589
+ Type: OFFICE_REL + "hyperlink",
9261
9590
  Target: rel.Target,
9262
9591
  TargetMode: "External"
9263
9592
  })).join("");
9264
9593
  return XML_DECL + el("Relationships", { xmlns: PACKAGE_REL_NS }, [
9265
9594
  raw(voidEl("Relationship", {
9266
9595
  Id: "rId1",
9267
- Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster",
9596
+ Type: OFFICE_REL + "notesMaster",
9268
9597
  Target: "../notesMasters/notesMaster1.xml"
9269
9598
  })),
9270
9599
  raw(voidEl("Relationship", {
9271
9600
  Id: "rId2",
9272
- Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide",
9601
+ Type: OFFICE_REL + "slide",
9273
9602
  Target: `../slides/slide${slideNumber}.xml`
9274
9603
  })),
9275
9604
  raw(hlinkRels)
@@ -9286,7 +9615,7 @@ function makeXmlNotesSlideRel(slide, slideNumber) {
9286
9615
  function makeXmlNotesMasterRel() {
9287
9616
  return XML_DECL + "\r\n" + el("Relationships", { xmlns: PACKAGE_REL_NS }, raw(voidEl("Relationship", {
9288
9617
  Id: "rId1",
9289
- Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme",
9618
+ Type: OFFICE_REL + "theme",
9290
9619
  Target: "../theme/theme2.xml"
9291
9620
  })), {
9292
9621
  childPrefix: "\n ",
@@ -9772,11 +10101,10 @@ function makeXmlCustomProperties(props) {
9772
10101
  * Emit the package root relationships (app/core/presentation, plus custom-props
9773
10102
  * when present).
9774
10103
  */
9775
- const SCHEMA_BASE = "http://schemas.openxmlformats.org/";
9776
10104
  function relationship$1(id, type, target) {
9777
10105
  return voidEl("Relationship", {
9778
10106
  Id: id,
9779
- Type: SCHEMA_BASE + type,
10107
+ Type: type,
9780
10108
  Target: target
9781
10109
  }, { openPrefix: "\n " });
9782
10110
  }
@@ -9785,11 +10113,11 @@ function relationship$1(id, type, target) {
9785
10113
  * @returns XML
9786
10114
  */
9787
10115
  function makeXmlRootRels(hasCustomProps) {
9788
- return XML_DECL + "\r\n" + el("Relationships", { xmlns: "http://schemas.openxmlformats.org/package/2006/relationships" }, [
9789
- raw(relationship$1("rId1", "officeDocument/2006/relationships/extended-properties", "docProps/app.xml")),
9790
- raw(relationship$1("rId2", "package/2006/relationships/metadata/core-properties", "docProps/core.xml")),
9791
- raw(relationship$1("rId3", "officeDocument/2006/relationships/officeDocument", "ppt/presentation.xml")),
9792
- hasCustomProps ? raw(relationship$1("rId4", "officeDocument/2006/relationships/custom-properties", "docProps/custom.xml")) : null
10116
+ return XML_DECL + "\r\n" + el("Relationships", { xmlns: PACKAGE_REL_NS }, [
10117
+ raw(relationship$1("rId1", OFFICE_REL + "extended-properties", "docProps/app.xml")),
10118
+ raw(relationship$1("rId2", PACKAGE_REL_NS + "/metadata/core-properties", "docProps/core.xml")),
10119
+ raw(relationship$1("rId3", OFFICE_REL + "officeDocument", "ppt/presentation.xml")),
10120
+ hasCustomProps ? raw(relationship$1("rId4", OFFICE_REL + "custom-properties", "docProps/custom.xml")) : null
9793
10121
  ], { closePrefix: "\n " });
9794
10122
  }
9795
10123
  //#endregion
@@ -9829,13 +10157,13 @@ function presentationFontRelStart(slides) {
9829
10157
  }
9830
10158
  function makeXmlPresentationRels(slides, embeddedFonts) {
9831
10159
  let intRelNum = 1;
9832
- const rels = [relationship(1, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster", "slideMasters/slideMaster1.xml")];
9833
- for (let idx = 1; idx <= slides.length; idx++) rels.push(relationship(++intRelNum, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide", `slides/slide${idx}.xml`));
10160
+ const rels = [relationship(1, OFFICE_REL + "slideMaster", "slideMasters/slideMaster1.xml")];
10161
+ for (let idx = 1; idx <= slides.length; idx++) rels.push(relationship(++intRelNum, OFFICE_REL + "slide", `slides/slide${idx}.xml`));
9834
10162
  intRelNum++;
9835
- rels.push(relationship(intRelNum + 0, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster", "notesMasters/notesMaster1.xml"), relationship(intRelNum + 1, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps", "presProps.xml"), relationship(intRelNum + 2, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps", "viewProps.xml"), relationship(intRelNum + 3, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme", "theme/theme1.xml"), relationship(intRelNum + 4, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles", "tableStyles.xml"));
9836
- if ((slides || []).some((slide) => (slide._comments || []).length > 0)) rels.push(relationship(intRelNum + 5, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/commentAuthors", "commentAuthors.xml"));
10163
+ rels.push(relationship(intRelNum + 0, OFFICE_REL + "notesMaster", "notesMasters/notesMaster1.xml"), relationship(intRelNum + 1, OFFICE_REL + "presProps", "presProps.xml"), relationship(intRelNum + 2, OFFICE_REL + "viewProps", "viewProps.xml"), relationship(intRelNum + 3, OFFICE_REL + "theme", "theme/theme1.xml"), relationship(intRelNum + 4, OFFICE_REL + "tableStyles", "tableStyles.xml"));
10164
+ if ((slides || []).some((slide) => (slide._comments || []).length > 0)) rels.push(relationship(intRelNum + 5, OFFICE_REL + "commentAuthors", "commentAuthors.xml"));
9837
10165
  for (const face of flattenEmbeddedFaces(embeddedFonts || [], presentationFontRelStart(slides))) rels.push(relationship(face.rId, FONT_REL_TYPE, `fonts/font${face.partIndex}.fntdata`));
9838
- return XML_DECL + "\r\n" + el("Relationships", { xmlns: "http://schemas.openxmlformats.org/package/2006/relationships" }, rels.map(raw));
10166
+ return XML_DECL + "\r\n" + el("Relationships", { xmlns: PACKAGE_REL_NS }, rels.map(raw));
9839
10167
  }
9840
10168
  //#endregion
9841
10169
  //#region src/gen/pres/presentation.ts
@@ -10563,18 +10891,14 @@ function createChartMediaRels(slide, zip, chartPromises) {
10563
10891
  async function buildPackageParts(source, props) {
10564
10892
  const pres = source.presentation;
10565
10893
  const arrChartPromises = [];
10566
- let arrMediaPromises = [];
10567
10894
  const zip = new ZipWriter();
10568
10895
  registerTransitionSounds(pres.slides);
10569
10896
  const onMediaError = props.onMediaError ?? "throw";
10570
- pres.slides.forEach((slide) => {
10571
- arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(slide, source.runtime, onMediaError));
10572
- });
10573
- pres.slideLayouts.forEach((layout) => {
10574
- arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(layout, source.runtime, onMediaError));
10575
- });
10576
- arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(pres.masterSlide, source.runtime, onMediaError));
10577
- return await Promise.all(arrMediaPromises).then(async () => {
10897
+ return await encodeMediaForTargets([
10898
+ ...pres.slides,
10899
+ ...pres.slideLayouts,
10900
+ pres.masterSlide
10901
+ ], source.runtime, onMediaError).then(async () => {
10578
10902
  const canonicalMediaTargets = /* @__PURE__ */ new Map();
10579
10903
  for (const target of [
10580
10904
  ...pres.slides,
@@ -10600,10 +10924,7 @@ async function buildPackageParts(source, props) {
10600
10924
  rel.fileName = `${chartBase}.xml`;
10601
10925
  rel.Target = `/ppt/charts/${chartBase}.xml`;
10602
10926
  }
10603
- pres.slides.forEach((slide) => {
10604
- if (slide._slideLayout) addPlaceholdersToSlideLayouts(slide);
10605
- });
10606
- applyMeasuredFit(pres.slides, source.fontMetrics);
10927
+ bakeSlideContent(pres.slides, source.fontMetrics);
10607
10928
  const hasCustomProps = source.customProperties.length > 0;
10608
10929
  zip.add("[Content_Types].xml", makeXmlContTypes(pres.slides, pres.slideLayouts, pres.masterSlide, hasCustomProps, pres.embeddedFonts));
10609
10930
  zip.add("_rels/.rels", makeXmlRootRels(hasCustomProps));
@@ -10674,60 +10995,20 @@ async function writePackage(source, props) {
10674
10995
  //#endregion
10675
10996
  //#region src/presentation.ts
10676
10997
  /**
10677
- * :: presentation.ts ::
10678
- *
10679
- * TypeScript/ESM library that creates PowerPoint (pptx) presentations
10680
- * https://github.com/gitbrent/PptxGenJS
10681
- *
10682
- * This framework is released under the MIT Public License (MIT)
10998
+ * ts-pptx: the presentation core
10683
10999
  *
10684
- * PptxGenJS (C) 2015-present Brent Ely -- https://github.com/gitbrent
11000
+ * Home of {@link PresentationCore}, the class every entry point subclasses with a runtime
11001
+ * adapter (`index.ts`, `node.ts`, `browser.ts`). See the class doc below for the layout of
11002
+ * its body and where package assembly actually happens.
10685
11003
  *
10686
- * Some code derived from the OfficeGen project:
10687
- * github.com/Ziv-Barber/officegen/ (Copyright 2013 Ziv Barber)
11004
+ * Units: this library takes user coordinates in inches and converts to EMU on output — see
11005
+ * `units.ts` for the conversions and `STANDARD_LAYOUTS` for the built-in slide sizes.
10688
11006
  *
10689
- * Permission is hereby granted, free of charge, to any person obtaining a copy
10690
- * of this software and associated documentation files (the "Software"), to deal
10691
- * in the Software without restriction, including without limitation the rights
10692
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10693
- * copies of the Software, and to permit persons to whom the Software is
10694
- * furnished to do so, subject to the following conditions:
10695
- *
10696
- * The above copyright notice and this permission notice shall be included in all
10697
- * copies or substantial portions of the Software.
10698
- *
10699
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10700
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10701
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10702
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
10703
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
10704
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
10705
- * SOFTWARE.
11007
+ * Derived from PptxGenJS (C) 2015-present Brent Ely (MIT), which in turn took some code from
11008
+ * the OfficeGen project (Copyright 2013 Ziv Barber). This project is likewise MIT; the full
11009
+ * license text is in `LICENSE` at the repository root.
10706
11010
  */
10707
- /**
10708
- * Units of Measure used in PowerPoint documents
10709
- *
10710
- * PowerPoint (DrawingML) positions and sizes shapes in `EMU`; font sizing is in points.
10711
- * This library takes user coordinates in inches and converts to EMU on output.
10712
- * - 914400 EMUs is 1 inch
10713
- * - 12700 EMUs is 1 point
10714
- * - 72 points is 1 inch
10715
- *
10716
- * @see https://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/
10717
- */
10718
- /**
10719
- * Object Layouts
10720
- *
10721
- * - 16x9 (10" x 5.625")
10722
- * - 16x10 (10" x 6.25")
10723
- * - 4x3 (10" x 7.5")
10724
- * - Wide (13.33" x 7.5")
10725
- * - [custom] (any size)
10726
- *
10727
- * @see https://docs.microsoft.com/en-us/office/open-xml/structure-of-a-presentationml-document
10728
- * @see https://docs.microsoft.com/en-us/previous-versions/office/developer/office-2010/hh273476(v=office.14)
10729
- */
10730
- const VERSION = "2.0.0";
11011
+ const VERSION = "3.1.0";
10731
11012
  function standardLayoutToPresLayout(layout) {
10732
11013
  return {
10733
11014
  name: layout.name,
@@ -11046,15 +11327,8 @@ var PresentationCore = class {
11046
11327
  */
11047
11328
  extractSlides = async (opts = {}) => {
11048
11329
  const onMediaError = opts.onMediaError ?? "throw";
11049
- let mediaPromises = [];
11050
- this._slides.forEach((slide) => {
11051
- mediaPromises = mediaPromises.concat(encodeSlideMediaRels(slide, this._runtime, onMediaError));
11052
- });
11053
- await Promise.all(mediaPromises);
11054
- this._slides.forEach((slide) => {
11055
- if (slide._slideLayout) addPlaceholdersToSlideLayouts(slide);
11056
- });
11057
- applyMeasuredFit(this._slides, this._fontMetrics);
11330
+ await encodeMediaForTargets(this._slides, this._runtime, onMediaError);
11331
+ bakeSlideContent(this._slides, this._fontMetrics);
11058
11332
  const slides = this._slides.map((slide) => {
11059
11333
  const relByRid = new Map(slide._relsMedia.map((rel) => [rel.rId, rel]));
11060
11334
  const avPreviewRids = /* @__PURE__ */ new Set();
@@ -11475,6 +11749,138 @@ var PresentationCore = class {
11475
11749
  }
11476
11750
  };
11477
11751
  //#endregion
11752
+ //#region src/clip.ts
11753
+ const HALF_DISC = {
11754
+ deep: {
11755
+ topEdgeX: .31788,
11756
+ botEdgeX: .31788,
11757
+ tipTopX: .30061,
11758
+ tipTopY: .01078,
11759
+ tipBotX: .30061,
11760
+ tipBotY: .98922,
11761
+ apexY: .5,
11762
+ c1b: {
11763
+ x: .11594,
11764
+ y: .86833
11765
+ },
11766
+ c2b: {
11767
+ x: 0,
11768
+ y: .69392
11769
+ },
11770
+ c1t: {
11771
+ x: 0,
11772
+ y: .30609
11773
+ },
11774
+ c2t: {
11775
+ x: .11594,
11776
+ y: .13167
11777
+ }
11778
+ },
11779
+ shallow: {
11780
+ topEdgeX: .12611,
11781
+ botEdgeX: .11128,
11782
+ tipTopX: .09976,
11783
+ tipTopY: .05448,
11784
+ tipBotX: .09976,
11785
+ tipBotY: .97617,
11786
+ apexY: .51533,
11787
+ c1b: {
11788
+ x: .03522,
11789
+ y: .83247
11790
+ },
11791
+ c2b: {
11792
+ x: 0,
11793
+ y: .67725
11794
+ },
11795
+ c1t: {
11796
+ x: 0,
11797
+ y: .3534
11798
+ },
11799
+ c2t: {
11800
+ x: .03522,
11801
+ y: .19818
11802
+ }
11803
+ }
11804
+ };
11805
+ /**
11806
+ * Half-disc clip path for a `w × h` box. Authored flat-right; `flat: 'left'` mirrors `x → 1 - x`.
11807
+ */
11808
+ function halfDisc(w, h, flat, preset) {
11809
+ const s = HALF_DISC[preset];
11810
+ const fx = flat === "left" ? (x) => (1 - x) * w : (x) => x * w;
11811
+ const fy = (y) => y * h;
11812
+ return [
11813
+ {
11814
+ x: fx(s.topEdgeX),
11815
+ y: fy(0),
11816
+ moveTo: true
11817
+ },
11818
+ {
11819
+ x: fx(1),
11820
+ y: fy(0)
11821
+ },
11822
+ {
11823
+ x: fx(1),
11824
+ y: fy(1)
11825
+ },
11826
+ {
11827
+ x: fx(s.botEdgeX),
11828
+ y: fy(1)
11829
+ },
11830
+ {
11831
+ x: fx(s.tipBotX),
11832
+ y: fy(s.tipBotY)
11833
+ },
11834
+ {
11835
+ x: fx(0),
11836
+ y: fy(s.apexY),
11837
+ curve: {
11838
+ type: "cubic",
11839
+ x1: fx(s.c1b.x),
11840
+ y1: fy(s.c1b.y),
11841
+ x2: fx(s.c2b.x),
11842
+ y2: fy(s.c2b.y)
11843
+ }
11844
+ },
11845
+ {
11846
+ x: fx(s.tipTopX),
11847
+ y: fy(s.tipTopY),
11848
+ curve: {
11849
+ type: "cubic",
11850
+ x1: fx(s.c1t.x),
11851
+ y1: fy(s.c1t.y),
11852
+ x2: fx(s.c2t.x),
11853
+ y2: fy(s.c2t.y)
11854
+ }
11855
+ },
11856
+ { close: true }
11857
+ ];
11858
+ }
11859
+ /**
11860
+ * Resolve a {@link ClipShape} to a freeform `points` path filling a `w × h` box, ready to hand
11861
+ * to `addImage({ points })`.
11862
+ *
11863
+ * `w`/`h` are the image box's size **in inches** and must match the `w`/`h` the picture is drawn
11864
+ * at — the path is emitted in the box's own coordinate space, not normalized (see the module
11865
+ * note above).
11866
+ * @param {ClipShape} shape - the named silhouette and its options
11867
+ * @param {number} w - image box width (inches)
11868
+ * @param {number} h - image box height (inches)
11869
+ * @returns {GeometryPoint[]} the freeform path, for `addImage({ points })`
11870
+ * @example
11871
+ * const w = 5.22, h = 7.5
11872
+ * slide.addImage({
11873
+ * path: 'cover.jpg', x: 0, y: 0, w, h,
11874
+ * points: clipPath({ kind: 'half-disc', flat: 'right' }, w, h),
11875
+ * sizing: { type: 'cover' }, // center-crop the photo into the "D"
11876
+ * })
11877
+ */
11878
+ function clipPath(shape, w, h) {
11879
+ switch (shape.kind) {
11880
+ case "half-disc": return halfDisc(w, h, shape.flat, shape.preset ?? "deep");
11881
+ }
11882
+ }
11883
+ //#endregion
11478
11884
  //#region src/types/text.ts
11479
11885
  /** Factory for a single inline text run. Prevents `as never` casts when building mixed-style run arrays. */
11480
11886
  function textRun(text, options) {
@@ -11488,6 +11894,6 @@ function textRuns(runs) {
11488
11894
  return runs;
11489
11895
  }
11490
11896
  //#endregion
11491
- export { textRuns as n, PresentationCore as r, textRun as t };
11897
+ export { PresentationCore as i, textRuns as n, clipPath as r, textRun as t };
11492
11898
 
11493
- //# sourceMappingURL=text-B1ZrPEeK.js.map
11899
+ //# sourceMappingURL=text-B6PbMFih.js.map