@shbernal/ts-pptx 2.0.0 → 3.0.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 (70) hide show
  1. package/README.md +7 -2
  2. package/dist/{autopage-tmfjRzL4.js → autopage-MxulehqQ.js} +3 -4
  3. package/dist/{autopage-tmfjRzL4.js.map → autopage-MxulehqQ.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 +12 -4
  7. package/dist/browser.js.map +1 -1
  8. package/dist/{diagnostics-CQ0JWc9m.d.ts → diagnostics-BqBTmA3a.d.ts} +95 -6
  9. package/dist/diagnostics-BqBTmA3a.d.ts.map +1 -0
  10. package/dist/{enums-D3aF4QGb.d.ts → enums-oLLeaSW3.d.ts} +2 -1
  11. package/dist/{enums-D3aF4QGb.d.ts.map → enums-oLLeaSW3.d.ts.map} +1 -1
  12. package/dist/{errors-B22h8Qd0.d.ts → errors-BENA-Xuw.d.ts} +3 -3
  13. package/dist/{errors-B22h8Qd0.d.ts.map → errors-BENA-Xuw.d.ts.map} +1 -1
  14. package/dist/{fit-BSWY0AYt.js → fit-DGSwMMNs.js} +0 -0
  15. package/dist/{fit-BSWY0AYt.js.map → fit-DGSwMMNs.js.map} +1 -1
  16. package/dist/{html-dom-BQLUntho.js → html-dom--J-Xuucf.js} +18 -4
  17. package/dist/html-dom--J-Xuucf.js.map +1 -0
  18. package/dist/html.d.ts +3 -3
  19. package/dist/html.d.ts.map +1 -1
  20. package/dist/html.js +6 -3
  21. package/dist/html.js.map +1 -1
  22. package/dist/index.d.ts +6 -6
  23. package/dist/index.js +4 -4
  24. package/dist/index.js.map +1 -1
  25. package/dist/inspect.d.ts +1 -1
  26. package/dist/inspect.js +2 -2
  27. package/dist/inspect.js.map +1 -1
  28. package/dist/math.d.ts +1 -1
  29. package/dist/math.js.map +1 -1
  30. package/dist/measure.d.ts +2 -2
  31. package/dist/measure.js +1 -1
  32. package/dist/node.d.ts +14 -6
  33. package/dist/node.d.ts.map +1 -1
  34. package/dist/node.js +11 -3
  35. package/dist/node.js.map +1 -1
  36. package/dist/{pres-GU-jjBEx.d.ts → pres-2WsBGimU.d.ts} +3 -3
  37. package/dist/{pres-GU-jjBEx.d.ts.map → pres-2WsBGimU.d.ts.map} +1 -1
  38. package/dist/{presentation-CVaTZw1V.d.ts → presentation-CTVPpiwP.d.ts} +711 -582
  39. package/dist/presentation-CTVPpiwP.d.ts.map +1 -0
  40. package/dist/{presentation-MJe9WDi4.js → presentation-DAcOfzsP.js} +861 -341
  41. package/dist/presentation-DAcOfzsP.js.map +1 -0
  42. package/dist/read.d.ts +4 -4
  43. package/dist/read.d.ts.map +1 -1
  44. package/dist/read.js +2 -2
  45. package/dist/script.d.ts +2 -2
  46. package/dist/script.d.ts.map +1 -1
  47. package/dist/script.js +0 -0
  48. package/dist/script.js.map +1 -1
  49. package/dist/{shapes-CaOgR0i1.js → shapes-BWifcHDV.js} +670 -1034
  50. package/dist/shapes-BWifcHDV.js.map +1 -0
  51. package/dist/{slide-BBlElWr9.d.ts → slide-YuSCGa45.d.ts} +197 -22
  52. package/dist/slide-YuSCGa45.d.ts.map +1 -0
  53. package/dist/{image-size-C8uYE8GJ.js → st-enums-CgadEyPM.js} +233 -2
  54. package/dist/st-enums-CgadEyPM.js.map +1 -0
  55. package/dist/{text-B1ZrPEeK.js → text-120VFSDm.js} +717 -314
  56. package/dist/text-120VFSDm.js.map +1 -0
  57. package/dist/{units-internal-7JcMfYti.js → units-internal-CiFWZWk4.js} +2 -1
  58. package/dist/units-internal-CiFWZWk4.js.map +1 -0
  59. package/dist/zip.d.ts +2 -2
  60. package/dist/zip.js.map +1 -1
  61. package/package.json +30 -17
  62. package/dist/diagnostics-CQ0JWc9m.d.ts.map +0 -1
  63. package/dist/html-dom-BQLUntho.js.map +0 -1
  64. package/dist/image-size-C8uYE8GJ.js.map +0 -1
  65. package/dist/presentation-CVaTZw1V.d.ts.map +0 -1
  66. package/dist/presentation-MJe9WDi4.js.map +0 -1
  67. package/dist/shapes-CaOgR0i1.js.map +0 -1
  68. package/dist/slide-BBlElWr9.d.ts.map +0 -1
  69. package/dist/text-B1ZrPEeK.js.map +0 -1
  70. 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
2
  import { n as InvalidOptionError, o as UnsupportedFeatureError, r as MediaError, t as InternalError } from "./errors-DGE9zh3U.js";
3
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";
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-CiFWZWk4.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-CgadEyPM.js";
6
+ import { t as getSlidesForTableRows } from "./autopage-MxulehqQ.js";
7
+ import { _ as parseFontMetrics, m as FontMetricsRegistry, n as measureText, r as computeTableLayout, t as applyMeasuredFit } from "./fit-DGSwMMNs.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
@@ -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
@@ -3104,23 +3234,6 @@ const BORDER_KEYS = [
3104
3234
  "cap"
3105
3235
  ];
3106
3236
  /**
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
3237
  * Resolve the `a:prstDash/@val` a border should emit.
3125
3238
  *
3126
3239
  * `BorderProps.type` is a three-way switch, so on its own it can only say "dashed" and
@@ -3136,14 +3249,7 @@ const BORDER_DASH_VALUES = [
3136
3249
  */
3137
3250
  function resolveBorderDash(border) {
3138
3251
  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;
3252
+ return checkEnumOrWarn(border.dashType, PRESET_LINE_DASHES, "border/invalid-dash-type", "border: dashType") ?? fromType;
3147
3253
  }
3148
3254
  /**
3149
3255
  * Report an authored key that is not part of `BorderProps`, which the library would
@@ -3585,7 +3691,34 @@ function makeSeriesDataPointsXml(chartType, obj, opts, varyColors) {
3585
3691
  * gridlines, optional axis title -- but PowerPoint requires the child elements in a
3586
3692
  * fixed order that differs per axis, so they stay as three separate builders rather
3587
3693
  * than one parameterized one. Called by {@link ./chart-xml}'s axis region.
3588
- */
3694
+ *
3695
+ * Sub-blocks are shared where they can be shared *exactly*: {@link axisLineSpPr} covers
3696
+ * `<c:spPr>`, which differs between the three only by indent width. `<c:txPr>` is
3697
+ * deliberately left duplicated — its inherited indentation is irregular *within* each copy
3698
+ * as well as between them (the category axis closes `</a:defRPr>` at three spaces, the value
3699
+ * axis at six), so parameterising it would take about as many indent arguments as it has
3700
+ * lines and would hide the quirks rather than share the structure.
3701
+ */
3702
+ /**
3703
+ * The `<c:spPr>` axis-line block, identical in shape on all three axes.
3704
+ *
3705
+ * The only thing that differed between the three copies was how far each was indented — two
3706
+ * spaces on the category and series axes, one on the value axis — and that irregularity is
3707
+ * inherited, not meaningful. It is reproduced here rather than normalised because inter-element
3708
+ * whitespace being inert does not make changing it free: byte-identity is the gate that proves
3709
+ * an emitter refactor changed nothing, and it cannot tell a "harmless" whitespace edit from a
3710
+ * real one. `indent` keeps the quirk visible at the call site.
3711
+ * @param {number} indent - leading spaces on the `<c:spPr>` line; children step out by 2 and 4
3712
+ * @param {number} widthEmu - line width for `a:ln@w`
3713
+ * @param {boolean | undefined} show - false emits `<a:noFill/>` instead of a colour
3714
+ * @param {string | undefined} color - line colour, defaulting to {@link DEF_GRIDLINE_COLOR}
3715
+ * @param {string} dash - `a:prstDash@val`
3716
+ * @return {string} XML `<c:spPr>`
3717
+ */
3718
+ function axisLineSpPr(indent, widthEmu, show, color, dash) {
3719
+ const at = (extra) => " ".repeat(indent + extra);
3720
+ 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>`;
3721
+ }
3589
3722
  function makeCatAxis(opts, axisId, valAxisId) {
3590
3723
  let strXml = "";
3591
3724
  const usesValueAxisForCategories = opts._type === "scatter" || opts._type === "bubble" || opts._type === "bubble3D";
@@ -3594,7 +3727,7 @@ function makeCatAxis(opts, axisId, valAxisId) {
3594
3727
  else strXml += "<c:" + (opts.catLabelFormatCode ? "dateAx" : "catAx") + ">";
3595
3728
  strXml += " <c:axId val=\"" + axisId + "\"/>";
3596
3729
  strXml += " <c:scaling>";
3597
- strXml += "<c:orientation val=\"" + (opts.catAxisOrientation || (opts.barDir === "col" ? "minMax" : "minMax")) + "\"/>";
3730
+ strXml += "<c:orientation val=\"" + (opts.catAxisOrientation || "minMax") + "\"/>";
3598
3731
  if (opts.catAxisMaxVal || opts.catAxisMaxVal === 0) strXml += `<c:max val="${opts.catAxisMaxVal}"/>`;
3599
3732
  if (opts.catAxisMinVal || opts.catAxisMinVal === 0) strXml += `<c:min val="${opts.catAxisMinVal}"/>`;
3600
3733
  strXml += "</c:scaling>";
@@ -3627,13 +3760,7 @@ function makeCatAxis(opts, axisId, valAxisId) {
3627
3760
  strXml += " <c:minorTickMark val=\"" + (opts.catAxisMinorTickMark || "none") + "\"/>";
3628
3761
  strXml += " <c:tickLblPos val=\"" + (opts.catAxisLabelPos || (opts.barDir === "col" ? "low" : "nextTo")) + "\"/>";
3629
3762
  }
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>";
3763
+ strXml += axisLineSpPr(2, opts.catAxisLineSize ? valToPts(opts.catAxisLineSize) : EMU_PER_POINT, opts.catAxisLineShow, opts.catAxisLineColor, opts.catAxisLineStyle || "solid");
3637
3764
  strXml += " <c:txPr>";
3638
3765
  if (opts.catAxisLabelRotate) strXml += `<a:bodyPr rot="${convertRotationDegrees(opts.catAxisLabelRotate)}"/>`;
3639
3766
  else strXml += "<a:bodyPr/>";
@@ -3697,7 +3824,7 @@ function makeValAxis(opts, valAxisId) {
3697
3824
  strXml += " <c:axId val=\"" + valAxisId + "\"/>";
3698
3825
  strXml += " <c:scaling>";
3699
3826
  if (opts.valAxisLogScaleBase) strXml += `<c:logBase val="${opts.valAxisLogScaleBase}"/>`;
3700
- strXml += "<c:orientation val=\"" + (opts.valAxisOrientation || (opts.barDir === "col" ? "minMax" : "minMax")) + "\"/>";
3827
+ strXml += "<c:orientation val=\"" + (opts.valAxisOrientation || "minMax") + "\"/>";
3701
3828
  if (opts.valAxisMaxVal || opts.valAxisMaxVal === 0) strXml += `<c:max val="${opts.valAxisMaxVal}"/>`;
3702
3829
  if (opts.valAxisMinVal || opts.valAxisMinVal === 0) strXml += `<c:min val="${opts.valAxisMinVal}"/>`;
3703
3830
  strXml += " </c:scaling>";
@@ -3724,13 +3851,7 @@ function makeValAxis(opts, valAxisId) {
3724
3851
  strXml += " <c:minorTickMark val=\"" + (opts.valAxisMinorTickMark || "none") + "\"/>";
3725
3852
  strXml += " <c:tickLblPos val=\"" + (opts.valAxisLabelPos || (opts.barDir === "col" ? "nextTo" : "low")) + "\"/>";
3726
3853
  }
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>";
3854
+ strXml += axisLineSpPr(1, opts.valAxisLineSize ? valToPts(opts.valAxisLineSize) : EMU_PER_POINT, opts.valAxisLineShow, opts.valAxisLineColor, opts.valAxisLineStyle || "solid");
3734
3855
  strXml += " <c:txPr>";
3735
3856
  strXml += ` <a:bodyPr${opts.valAxisLabelRotate ? " rot=\"" + convertRotationDegrees(opts.valAxisLabelRotate).toString() + "\"" : ""}/>`;
3736
3857
  strXml += " <a:lstStyle/>";
@@ -3765,7 +3886,7 @@ function makeSerAxis(opts, axisId, valAxisId) {
3765
3886
  let strXml = "";
3766
3887
  strXml += "<c:serAx>";
3767
3888
  strXml += " <c:axId val=\"" + axisId + "\"/>";
3768
- strXml += " <c:scaling><c:orientation val=\"" + (opts.serAxisOrientation || (opts.barDir === "col" ? "minMax" : "minMax")) + "\"/></c:scaling>";
3889
+ strXml += " <c:scaling><c:orientation val=\"" + (opts.serAxisOrientation || "minMax") + "\"/></c:scaling>";
3769
3890
  strXml += " <c:delete val=\"" + (opts.serAxisHidden ? "1" : "0") + "\"/>";
3770
3891
  strXml += " <c:axPos val=\"" + (opts.barDir === "col" ? "b" : "l") + "\"/>";
3771
3892
  strXml += opts.serGridLine && opts.serGridLine.style !== "none" ? createGridLineElement(opts.serGridLine) : "";
@@ -3783,13 +3904,7 @@ function makeSerAxis(opts, axisId, valAxisId) {
3783
3904
  strXml += " <c:majorTickMark val=\"out\"/>";
3784
3905
  strXml += " <c:minorTickMark val=\"none\"/>";
3785
3906
  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>";
3907
+ strXml += axisLineSpPr(2, EMU_PER_POINT, opts.serAxisLineShow, opts.serAxisLineColor, "solid");
3793
3908
  strXml += " <c:txPr>";
3794
3909
  strXml += " <a:bodyPr/>";
3795
3910
  strXml += " <a:lstStyle/>";
@@ -5139,7 +5254,8 @@ function chartExLayoutId(type) {
5139
5254
  /**
5140
5255
  * A deterministic `uniqueId` GUID for a `<cx:series>`, derived from the chart's package-global id.
5141
5256
  * 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`
5257
+ * chart determinism — the package-wide id this derives from is assigned at write time in
5258
+ * `package/assemble.ts`, STEP 2). `seriesIdx`
5143
5259
  * distinguishes multiple series within one chart (pareto emits two), varying the 4th GUID group so
5144
5260
  * every series still gets a distinct id.
5145
5261
  */
@@ -5383,9 +5499,15 @@ function makeXmlChartEx(rel) {
5383
5499
  * template — analogous to the default theme/worksheet boilerplate the library already embeds. They
5384
5500
  * reference theme scheme colors, so they adapt to the deck theme without per-chart customization.
5385
5501
  */
5386
- /** The `cs:colorStyle` payload for a chartEx chart's `colors{N}.xml` part (theme-accent cycle). */
5502
+ /**
5503
+ * The `cs:colorStyle` payload for a chartEx chart's `colors{N}.xml` part (theme-accent cycle).
5504
+ * @raw-xml-asset Captured verbatim from PowerPoint; not built here, so not a migration target.
5505
+ */
5387
5506
  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). */
5507
+ /**
5508
+ * The `cs:chartStyle` payload for a chartEx chart's `style{N}.xml` part (PowerPoint default).
5509
+ * @raw-xml-asset Captured verbatim from PowerPoint; not built here, so not a migration target.
5510
+ */
5389
5511
  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
5512
  /** Build the `ppt/charts/colors{N}.xml` part body. */
5391
5513
  function makeChartExColorsXml() {
@@ -5397,7 +5519,6 @@ function makeChartExStyleXml() {
5397
5519
  }
5398
5520
  //#endregion
5399
5521
  //#region src/gen/chart/embed-xlsx.ts
5400
- const PACKAGE_REL_NS$2 = "http://schemas.openxmlformats.org/package/2006/relationships";
5401
5522
  function relationship$2(id, type, target) {
5402
5523
  return voidEl("Relationship", {
5403
5524
  Id: id,
@@ -5407,7 +5528,7 @@ function relationship$2(id, type, target) {
5407
5528
  }
5408
5529
  /** `<Relationships>` wrapper shared by the workbook's `.rels` parts (all flat, no indent). */
5409
5530
  function relationships(rels) {
5410
- return el("Relationships", { xmlns: PACKAGE_REL_NS$2 }, rels.map(raw));
5531
+ return el("Relationships", { xmlns: PACKAGE_REL_NS }, rels.map(raw));
5411
5532
  }
5412
5533
  /**
5413
5534
  * Build the chart's embedded Excel workbook as a standalone OPC package and
@@ -5426,22 +5547,22 @@ function buildEmbeddedWorksheet(chartObject) {
5426
5547
  const IS_MULTI_CAT_AXES = (data[0]?.labels?.length ?? 0) > 1;
5427
5548
  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
5549
  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")
5550
+ relationship$2("rId1", PACKAGE_REL_NS + "/metadata/core-properties", "docProps/core.xml"),
5551
+ relationship$2("rId2", OFFICE_REL + "extended-properties", "docProps/app.xml"),
5552
+ relationship$2("rId3", OFFICE_REL + "officeDocument", "xl/workbook.xml")
5432
5553
  ]) + "\n");
5433
5554
  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
5555
  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
5556
  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")
5557
+ relationship$2("rId3", OFFICE_REL + "styles", "styles.xml"),
5558
+ relationship$2("rId2", OFFICE_REL + "theme", "theme/theme1.xml"),
5559
+ relationship$2("rId1", OFFICE_REL + "worksheet", "worksheets/sheet1.xml"),
5560
+ relationship$2("rId4", OFFICE_REL + "sharedStrings", "sharedStrings.xml")
5440
5561
  ]));
5441
5562
  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
5563
  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
5564
  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");
5565
+ zipExcel.add("xl/worksheets/_rels/sheet1.xml.rels", XML_DECL + relationships([relationship$2("rId1", OFFICE_REL + "table", "../tables/table1.xml")]) + "\n");
5445
5566
  zipExcel.add("xl/sharedStrings.xml", buildXlsxSharedStrings(chartObject, data, intBubbleCols, IS_MULTI_CAT_AXES));
5446
5567
  zipExcel.add("xl/tables/table1.xml", buildXlsxTable(chartObject, data, intBubbleCols));
5447
5568
  zipExcel.add("xl/worksheets/sheet1.xml", buildXlsxSheet(chartObject, data, intBubbleCols, IS_MULTI_CAT_AXES));
@@ -5622,7 +5743,7 @@ function buildXlsxSheet(chartObject, data, intBubbleCols, IS_MULTI_CAT_AXES) {
5622
5743
  * @return {string} the chart part's `.rels` XML
5623
5744
  */
5624
5745
  function buildChartRelsXml(embeddingTarget) {
5625
- return XML_DECL + relationships([relationship$2("rId1", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package", embeddingTarget)]);
5746
+ return XML_DECL + relationships([relationship$2("rId1", OFFICE_REL + "package", embeddingTarget)]);
5626
5747
  }
5627
5748
  /**
5628
5749
  * Build the `.rels` for a chartEx chart part: the embedded workbook (rId1, via `<cx:externalData>`)
@@ -5634,7 +5755,7 @@ function buildChartRelsXml(embeddingTarget) {
5634
5755
  */
5635
5756
  function buildChartExRelsXml(embeddingTarget, colorsTarget, styleTarget) {
5636
5757
  return XML_DECL + relationships([
5637
- relationship$2("rId1", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package", embeddingTarget),
5758
+ relationship$2("rId1", OFFICE_REL + "package", embeddingTarget),
5638
5759
  relationship$2("rId2", "http://schemas.microsoft.com/office/2011/relationships/chartColorStyle", colorsTarget),
5639
5760
  relationship$2("rId3", "http://schemas.microsoft.com/office/2011/relationships/chartStyle", styleTarget)
5640
5761
  ]);
@@ -5752,6 +5873,63 @@ function encodeSlideMediaRels(layout, runtime, onMediaError = "throw") {
5752
5873
  return imageProms;
5753
5874
  }
5754
5875
  //#endregion
5876
+ //#region src/gen/prepare.ts
5877
+ /**
5878
+ * ts-pptx: the pre-serialization pass
5879
+ *
5880
+ * Everything that must happen to authored slide state *before* any XML is built. Two callers
5881
+ * reach serialization and both need it:
5882
+ *
5883
+ * - `package/assemble.ts` — the normal `write`/`stream`/`writeFile` path, building a
5884
+ * complete `.pptx`.
5885
+ * - `PresentationCore.extractSlides()` — serializes slide bodies only, for splicing into
5886
+ * an already-loaded deck via `Presentation.appendSlides()` (`ts-pptx/read`).
5887
+ *
5888
+ * The second used to reimplement the first inline, kept in step by a comment that said
5889
+ * "exactly as ... does". Nothing failed when they drifted: `extractSlides` would simply emit
5890
+ * slides that did not match what a normal write produces, and no test compares the two.
5891
+ *
5892
+ * Two primitives rather than one combined pass, because the package path has work of its own
5893
+ * that must land *between* them: it de-duplicates identical media across the deck (which
5894
+ * needs the encoded bytes {@link encodeMediaForTargets} produces) and assigns package-unique
5895
+ * chart part filenames, both before any slide content is baked. Exposing the halves lets
5896
+ * `assemble` keep its established order exactly while still sharing the code.
5897
+ *
5898
+ * The order across the two is load-bearing:
5899
+ *
5900
+ * 1. **Media first.** It is the only asynchronous step, and the sync XML pass reads the
5901
+ * `rel.data` it populates.
5902
+ * 2. **Backfill placeholders**, so a slide inherits its layout's placeholder objects before
5903
+ * anything measures or serializes them.
5904
+ * 3. **Bake measured fit last**, because it measures the text step 2 may have just added and
5905
+ * writes the `fontScale` the sync XML pass then reads.
5906
+ *
5907
+ * The two functions take different target sets on purpose: media lives on layouts and the
5908
+ * master as well as on slides, while placeholder backfill and measured fit apply only to
5909
+ * slides. `extractSlides` passes only its slides to both, because it emits no layout or
5910
+ * master parts.
5911
+ */
5912
+ /**
5913
+ * Step 1: load and base64-encode every media rel on the given targets, populating `rel.data`.
5914
+ * @param {ReadonlyArray<PresSlideInternal | SlideLayoutInternal>} targets - media-bearing targets (slides, layouts, master)
5915
+ * @param {RuntimeAdapter} runtime - runtime adapter used to load media
5916
+ * @param {'throw' | 'placeholder'} onMediaError - failure policy for unloadable media
5917
+ */
5918
+ async function encodeMediaForTargets(targets, runtime, onMediaError) {
5919
+ const promises = [];
5920
+ for (const target of targets) promises.push(...encodeSlideMediaRels(target, runtime, onMediaError));
5921
+ await Promise.all(promises);
5922
+ }
5923
+ /**
5924
+ * Steps 2 and 3: backfill inherited layout placeholders, then bake measured text fit.
5925
+ * @param {PresSlideInternal[]} slides - the slides to prepare
5926
+ * @param {FontMetricsRegistry} fontMetrics - registered font metrics; an empty registry skips the fit bake
5927
+ */
5928
+ function bakeSlideContent(slides, fontMetrics) {
5929
+ for (const slide of slides) if (slide._slideLayout) addPlaceholdersToSlideLayouts(slide);
5930
+ applyMeasuredFit(slides, fontMetrics);
5931
+ }
5932
+ //#endregion
5755
5933
  //#region src/gen/slide/shape-ids.ts
5756
5934
  /**
5757
5935
  * Every object a slide renders, paired with the `<p:cNvPr>` id it is rendered with: top-level
@@ -6482,13 +6660,13 @@ function genXmlParagraphProperties(textObj, isDefault) {
6482
6660
  const isPictureBullet = !!(bulletImage && (bulletImage.path || bulletImage.data));
6483
6661
  if (opts.bullet?.indent) bulletMarL = valToPts(opts.bullet.indent);
6484
6662
  if (opts.bullet.color && !isPictureBullet) strXmlBulletColor = el("a:buClr", null, raw(createColorElement(opts.bullet.color)));
6485
- let bulletSizePct = PERCENT_SCALE;
6663
+ let bulletSizePct;
6486
6664
  if (opts.bullet.size !== void 0) {
6487
6665
  const bulletSize = Number(opts.bullet.size);
6488
6666
  if (isNaN(bulletSize) || bulletSize < 25 || bulletSize > 400) warn("bullet/size-out-of-range", "`bullet.size` must be a percentage between 25 and 400!");
6489
6667
  else bulletSizePct = Math.round(bulletSize * FIXED_PCT_PER_PERCENT);
6490
6668
  }
6491
- const strXmlBulletSize = voidEl("a:buSzPct", { val: bulletSizePct });
6669
+ const strXmlBulletSize = bulletSizePct === void 0 ? "" : voidEl("a:buSzPct", { val: bulletSizePct });
6492
6670
  const strXmlBulletFont = opts.bullet.fontFace ? voidEl("a:buFont", { typeface: opts.bullet.fontFace }) : "";
6493
6671
  const bulletIndentAttrs = () => ` marL="${opts.indentLevel && opts.indentLevel > 0 ? bulletMarL + bulletMarL * opts.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
6494
6672
  if (isPictureBullet) {
@@ -6523,7 +6701,7 @@ function genXmlParagraphProperties(textObj, isDefault) {
6523
6701
  }
6524
6702
  } else if (opts.bullet) {
6525
6703
  paragraphPropXml += ` marL="${opts.indentLevel && opts.indentLevel > 0 ? bulletMarL + bulletMarL * opts.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
6526
- strXmlBullet = voidEl("a:buSzPct", { val: "100000" }) + buChar("&#x2022;");
6704
+ strXmlBullet = buChar("&#x2022;");
6527
6705
  } else if (!opts.bullet) {
6528
6706
  paragraphPropXml += " indent=\"0\" marL=\"0\"";
6529
6707
  strXmlBullet = voidEl("a:buNone");
@@ -7299,28 +7477,31 @@ function genXmlCustGeom(options, cx, cy, layout) {
7299
7477
  * ts-pptx: DrawingML image sizing & cropping
7300
7478
  *
7301
7479
  * Build the `<a:srcRect>` (+ `<a:stretch>`) blipFill children for the image
7302
- * sizing modes (`cover` / `contain` / `crop`) and for explicit percentage crops.
7480
+ * sizing modes (`cover` / `contain` / `crop` / `stretch`), for the implicit
7481
+ * aspect-correct placement a vector source gets, and for explicit percentage crops.
7303
7482
  */
7304
7483
  /** Every `<a:srcRect>` below is followed by this same fill directive. */
7305
7484
  const STRETCH = el("a:stretch", null, raw(voidEl("a:fillRect")));
7485
+ /** The `<a:srcRect>` fitting `imgSize` into `boxDim`, followed by the fill directive. */
7486
+ function fitSrcRect(type, imgSize, boxDim) {
7487
+ const { l, r, t, b } = fitSrcRectPercents(type, imgSize, boxDim);
7488
+ return voidEl("a:srcRect", {
7489
+ l,
7490
+ r,
7491
+ t,
7492
+ b
7493
+ }) + STRETCH;
7494
+ }
7306
7495
  const ImageSizingXml = {
7307
7496
  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;
7497
+ return fitSrcRect("cover", imgSize, boxDim);
7315
7498
  },
7316
7499
  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;
7500
+ return fitSrcRect("contain", imgSize, boxDim);
7501
+ },
7502
+ /** No source rectangle at all: the image fills the box, aspect ratio be damned. */
7503
+ stretch: function() {
7504
+ return STRETCH;
7324
7505
  },
7325
7506
  crop: function(imgSize, boxDim) {
7326
7507
  const l = boxDim.x;
@@ -7345,6 +7526,23 @@ const ImageSizingXml = {
7345
7526
  }
7346
7527
  };
7347
7528
  /**
7529
+ * The blipFill children for a vector source the caller gave no `sizing` for: letterbox it to its
7530
+ * own aspect ratio inside its frame, the way `sizing: 'contain'` would — an SVG carries an
7531
+ * intrinsic ratio, and stretching a glyph into a box that disagrees with it is never the intent.
7532
+ *
7533
+ * Returns `null` when the two ratios already agree, which is the overwhelmingly common case (a
7534
+ * square icon in a square box). The caller then emits the plain `<a:stretch>` it always did, so
7535
+ * this default costs nothing — neither bytes nor a behaviour change — until it actually differs.
7536
+ * @param imgSize - the vector's intrinsic size (viewBox or width/height)
7537
+ * @param boxDim - the picture's displayed frame
7538
+ * @returns the `<a:srcRect>` + `<a:stretch>` children, or `null` if no letterboxing is needed
7539
+ */
7540
+ function genXmlVectorAspectFit(imgSize, boxDim) {
7541
+ const { l, r, t, b } = fitSrcRectPercents("contain", imgSize, boxDim);
7542
+ if (l === 0 && r === 0 && t === 0 && b === 0) return null;
7543
+ return fitSrcRect("contain", imgSize, boxDim);
7544
+ }
7545
+ /**
7348
7546
  * Build an explicit `<a:srcRect>` crop from percentage edge insets (0–100), emitted verbatim.
7349
7547
  * Each inset is the percent of the *source* image trimmed from that edge; values serialize in
7350
7548
  * 1000ths of a percent (DrawingML `ST_Percentage`, where 100% = 100000). Unlike `sizing: 'crop'`
@@ -7381,10 +7579,12 @@ function genXmlImageCrop(crop, objectName) {
7381
7579
  function renderImageObject(slideItemObj, idx, slide, x, y, cx, cy, imgWidth, imgHeight, placeholderObj, locationAttrs, sizing, rounding) {
7382
7580
  let strSlideXml = "";
7383
7581
  slideItemObj.options = slideItemObj.options || {};
7582
+ const mediaRel = (slide._relsMedia || []).find((rel) => rel.rId === slideItemObj.imageRid);
7583
+ const relData = mediaRel?.data;
7584
+ const naturalSize = () => typeof relData === "string" ? getImageSizeFromBase64(relData) : null;
7384
7585
  if (slideItemObj.options._szAuto) {
7385
7586
  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;
7587
+ const natural = naturalSize();
7388
7588
  if (natural) {
7389
7589
  if (szAuto.w && szAuto.h) {
7390
7590
  cx = pixelsToEmu(natural.w, 96);
@@ -7415,7 +7615,8 @@ function renderImageObject(slideItemObj, idx, slide, x, y, cx, cy, imgWidth, img
7415
7615
  imgOpts.biLevel ? raw(voidEl("a:biLevel", { thresh: Math.round(imgOpts.biLevel.threshold * PERCENT_SCALE) })) : null
7416
7616
  ];
7417
7617
  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", {
7618
+ const isVector = mediaRel?.extn === "svg";
7619
+ 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
7620
  "xmlns:asvg": "http://schemas.microsoft.com/office/drawing/2016/SVG/main",
7420
7621
  "r:embed": `rId${slideItemObj.imageRid}`
7421
7622
  }, { openPrefix: " " })), {
@@ -7439,8 +7640,7 @@ function renderImageObject(slideItemObj, idx, slide, x, y, cx, cy, imgWidth, img
7439
7640
  h: imgHeight
7440
7641
  };
7441
7642
  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;
7643
+ const natural = naturalSize();
7444
7644
  if (natural) cropSize = natural;
7445
7645
  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
7646
  }
@@ -7452,7 +7652,14 @@ function renderImageObject(slideItemObj, idx, slide, x, y, cx, cy, imgWidth, img
7452
7652
  });
7453
7653
  imgWidth = boxW;
7454
7654
  imgHeight = boxH;
7455
- } else strSlideXml += el("a:stretch", null, raw(voidEl("a:fillRect")), { openPrefix: " " });
7655
+ } else {
7656
+ const natural = isVector ? naturalSize() : null;
7657
+ const aspectFit = natural ? genXmlVectorAspectFit(natural, {
7658
+ w: imgWidth,
7659
+ h: imgHeight
7660
+ }) : null;
7661
+ strSlideXml += aspectFit ?? el("a:stretch", null, raw(voidEl("a:fillRect")), { openPrefix: " " });
7662
+ }
7456
7663
  strSlideXml += "</p:blipFill>";
7457
7664
  strSlideXml += "<p:spPr>";
7458
7665
  strSlideXml += el("a:xfrm", locationAttrs, [raw(voidEl("a:off", {
@@ -7526,6 +7733,216 @@ function renderMediaObject(slideItemObj, idx, x, y, cx, cy, locationAttrs) {
7526
7733
  ]);
7527
7734
  }
7528
7735
  //#endregion
7736
+ //#region src/gen/slide/objects/model3d.ts
7737
+ /** The `am3d` namespace, doubling as `a:graphicData@uri` and the `mc:Choice Requires` token's URI. */
7738
+ const AM3D_NS = "http://schemas.microsoft.com/office/drawing/2017/model3d";
7739
+ /** Denominator of every `am3d` rational element (`@n`/`@d`). */
7740
+ const RATIO_DEN = 1e6;
7741
+ /**
7742
+ * `am3d:raster@rName`/`@rVer` — the renderer that produced the cached preview. PowerPoint stamps
7743
+ * its own renderer here; ts-pptx did not rasterize anything, but the attributes are what PowerPoint
7744
+ * writes and the element is where the preview blip lives, so they are reproduced verbatim.
7745
+ */
7746
+ const RASTER = {
7747
+ rName: "Office3DRenderer",
7748
+ rVer: "16.0.8326"
7749
+ };
7750
+ /**
7751
+ * `am3d:objViewport@viewportSz`. Bounding-box derived in PowerPoint's output (a cube and a slab
7752
+ * gave different values); the cube's is emitted, matching the fixed default camera.
7753
+ */
7754
+ const OBJ_VIEWPORT_SZ = 3338805;
7755
+ /** The three point lights PowerPoint writes for every inserted model — key, fill and rim. */
7756
+ const PT_LIGHTS = [
7757
+ {
7758
+ clr: {
7759
+ r: 1e5,
7760
+ g: 75e3,
7761
+ b: 5e4
7762
+ },
7763
+ intensityN: 9765625,
7764
+ pos: {
7765
+ x: 21959998,
7766
+ y: 70920001,
7767
+ z: 16344003
7768
+ }
7769
+ },
7770
+ {
7771
+ clr: {
7772
+ r: 4e4,
7773
+ g: 6e4,
7774
+ b: 95e3
7775
+ },
7776
+ intensityN: 1225e4,
7777
+ pos: {
7778
+ x: -37964106,
7779
+ y: 51130435,
7780
+ z: 57631972
7781
+ }
7782
+ },
7783
+ {
7784
+ clr: {
7785
+ r: 86837,
7786
+ g: 72700,
7787
+ b: 1e5
7788
+ },
7789
+ intensityN: 3125e3,
7790
+ pos: {
7791
+ x: -37739122,
7792
+ y: 58056624,
7793
+ z: -34769649
7794
+ }
7795
+ }
7796
+ ];
7797
+ /** `am3d:ambientLight` — neutral 50% scRGB at half illuminance, constant like the point lights. */
7798
+ const AMBIENT = {
7799
+ clr: {
7800
+ r: 5e4,
7801
+ g: 5e4,
7802
+ b: 5e4
7803
+ },
7804
+ illuminanceN: 5e5
7805
+ };
7806
+ /** `<am3d:clr><a:scrgbClr …/></am3d:clr>` — the colour wrapper both light kinds share. */
7807
+ function lightColor(clr) {
7808
+ return el("am3d:clr", null, raw(voidEl("a:scrgbClr", clr)));
7809
+ }
7810
+ /** The `am3d:camera` block: eye position, up vector, aim point and perspective fov. */
7811
+ function cameraEl(model) {
7812
+ return el("am3d:camera", null, [
7813
+ raw(voidEl("am3d:pos", model.pos)),
7814
+ raw(voidEl("am3d:up", model.up)),
7815
+ raw(voidEl("am3d:lookAt", model.lookAt)),
7816
+ raw(voidEl("am3d:perspective", { fov: model.fov }))
7817
+ ]);
7818
+ }
7819
+ /**
7820
+ * The `am3d:trans` block: model-unit scale plus an identity placement. Only `meterPerModelUnit`
7821
+ * varies — `preTrans`/`scale`/`rot`/`postTrans` are the identity PowerPoint writes for a model
7822
+ * inserted without any subsequent 3D manipulation, and `am3d:rot` is emitted attribute-less.
7823
+ */
7824
+ function transEl(model) {
7825
+ const unit = {
7826
+ n: RATIO_DEN,
7827
+ d: RATIO_DEN
7828
+ };
7829
+ return el("am3d:trans", null, [
7830
+ raw(voidEl("am3d:meterPerModelUnit", {
7831
+ n: model.meterPerModelUnitN,
7832
+ d: RATIO_DEN
7833
+ })),
7834
+ raw(voidEl("am3d:preTrans", {
7835
+ dx: 0,
7836
+ dy: 0,
7837
+ dz: 0
7838
+ })),
7839
+ raw(el("am3d:scale", null, [
7840
+ raw(voidEl("am3d:sx", unit)),
7841
+ raw(voidEl("am3d:sy", unit)),
7842
+ raw(voidEl("am3d:sz", unit))
7843
+ ])),
7844
+ raw(voidEl("am3d:rot")),
7845
+ raw(voidEl("am3d:postTrans", {
7846
+ dx: 0,
7847
+ dy: 0,
7848
+ dz: 0
7849
+ }))
7850
+ ]);
7851
+ }
7852
+ /** The fixed lighting rig: one ambient light and three point lights, in PowerPoint's order. */
7853
+ function lightsXml() {
7854
+ return el("am3d:ambientLight", null, [raw(lightColor(AMBIENT.clr)), raw(voidEl("am3d:illuminance", {
7855
+ n: AMBIENT.illuminanceN,
7856
+ d: RATIO_DEN
7857
+ }))]) + PT_LIGHTS.map((light) => el("am3d:ptLight", { rad: 0 }, [
7858
+ raw(lightColor(light.clr)),
7859
+ raw(voidEl("am3d:intensity", {
7860
+ n: light.intensityN,
7861
+ d: RATIO_DEN
7862
+ })),
7863
+ raw(voidEl("am3d:pos", light.pos))
7864
+ ])).join("");
7865
+ }
7866
+ /** The `mc:Fallback` picture — the cached preview, drawn by everything that is not PowerPoint 2019+. */
7867
+ function fallbackPic(picId, objectName, altText, model, xf) {
7868
+ return el("p:pic", null, [
7869
+ raw(el("p:nvPicPr", null, [
7870
+ raw(cNvPrOpen(picId, objectName, altText) + "/>"),
7871
+ raw(el("p:cNvPicPr", null, raw(voidEl("a:picLocks", {
7872
+ noGrp: "1",
7873
+ noRot: "1",
7874
+ noChangeAspect: "1",
7875
+ noMove: "1",
7876
+ noResize: "1",
7877
+ noEditPoints: "1",
7878
+ noAdjustHandles: "1",
7879
+ noChangeArrowheads: "1",
7880
+ noChangeShapeType: "1",
7881
+ noCrop: "1"
7882
+ })))),
7883
+ raw(voidEl("p:nvPr"))
7884
+ ])),
7885
+ raw(el("p:blipFill", null, [raw(voidEl("a:blip", { "r:embed": `rId${model.previewRid}` })), raw(el("a:stretch", null, raw(voidEl("a:fillRect"))))])),
7886
+ raw(el("p:spPr", null, [raw(el("a:xfrm", null, [raw(voidEl("a:off", {
7887
+ x: xf.x,
7888
+ y: xf.y
7889
+ })), raw(voidEl("a:ext", {
7890
+ cx: xf.cx,
7891
+ cy: xf.cy
7892
+ }))])), raw(el("a:prstGeom", { prst: "rect" }, raw(voidEl("a:avLst"))))]))
7893
+ ]);
7894
+ }
7895
+ /**
7896
+ * Render a `model3d` slide object to its `<mc:AlternateContent>` XML. The `mc:Choice` carries the
7897
+ * real `<p:graphicFrame>` (the `am3d:model3d` graphicData PowerPoint 2019+ renders live); the
7898
+ * `mc:Fallback` carries the cached preview picture. See `gen/define/model3d.ts`.
7899
+ */
7900
+ function renderModel3dObject(slideItemObj, idx, x, y, cx, cy) {
7901
+ const model = slideItemObj.model3d;
7902
+ if (!model) return "";
7903
+ const opts = slideItemObj.options || {};
7904
+ const altText = opts.altText || "";
7905
+ const spPr = el("am3d:spPr", null, [raw(el("a:xfrm", null, [raw(voidEl("a:off", {
7906
+ x: 0,
7907
+ y: 0
7908
+ })), raw(voidEl("a:ext", {
7909
+ cx,
7910
+ cy
7911
+ }))])), raw(el("a:prstGeom", { prst: "rect" }, raw(voidEl("a:avLst"))))]);
7912
+ const model3d = el("am3d:model3d", { "r:embed": `rId${model.modelRid}` }, [
7913
+ raw(spPr),
7914
+ raw(cameraEl(model)),
7915
+ raw(transEl(model)),
7916
+ raw(el("am3d:raster", RASTER, raw(voidEl("am3d:blip", { "r:embed": `rId${model.previewRid}` })))),
7917
+ raw(voidEl("am3d:objViewport", { viewportSz: OBJ_VIEWPORT_SZ })),
7918
+ raw(lightsXml())
7919
+ ]);
7920
+ const graphicFrame = el("p:graphicFrame", null, [
7921
+ raw(el("p:nvGraphicFramePr", null, [
7922
+ raw(cNvPrOpen(idx + 2, opts.objectName, altText) + "/>"),
7923
+ raw(el("p:cNvGraphicFramePr", null, raw(voidEl("a:graphicFrameLocks")))),
7924
+ raw(voidEl("p:nvPr"))
7925
+ ])),
7926
+ raw(el("p:xfrm", null, [raw(voidEl("a:off", {
7927
+ x,
7928
+ y
7929
+ })), raw(voidEl("a:ext", {
7930
+ cx,
7931
+ cy
7932
+ }))])),
7933
+ raw(el("a:graphic", null, raw(el("a:graphicData", { uri: AM3D_NS }, raw(model3d)))))
7934
+ ]);
7935
+ return el("mc:AlternateContent", { "xmlns:mc": MC_NS }, [raw(el("mc:Choice", {
7936
+ "xmlns:am3d": AM3D_NS,
7937
+ Requires: "am3d"
7938
+ }, raw(graphicFrame))), raw(el("mc:Fallback", null, raw(fallbackPic(idx + 2, opts.objectName, altText, model, {
7939
+ x,
7940
+ y,
7941
+ cx,
7942
+ cy
7943
+ }))))]);
7944
+ }
7945
+ //#endregion
7529
7946
  //#region src/gen/slide/objects/ole.ts
7530
7947
  /** VML namespace — declared by an OLE object's `mc:Choice Requires="v"` (no VML content is emitted). */
7531
7948
  const VML_NS = "urn:schemas-microsoft-com:vml";
@@ -7686,95 +8103,12 @@ function genTableCellBorderXml(cellBorder, diagonal) {
7686
8103
  }
7687
8104
  //#endregion
7688
8105
  //#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
8106
  /**
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.
8107
+ * Vet one enumerated value before it reaches the XML, reporting and dropping anything outside
8108
+ * its `ST_` union. See `ooxml/check-enum.ts` for why the write path drops rather than throws.
7768
8109
  */
7769
8110
  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;
8111
+ return checkEnumOrWarn(value, valid, "table/invalid-cell3d", `table cell: cell3D \`${field}\``, { field });
7778
8112
  }
7779
8113
  /**
7780
8114
  * A bevel dimension in EMU (`ST_PositiveCoordinate`), from a width in points.
@@ -7805,32 +8139,22 @@ function genTableCell3DXml(cell3D) {
7805
8139
  prst: resolveEnum(cell3D.preset, BEVEL_PRESETS, "preset")
7806
8140
  });
7807
8141
  const rig = resolveEnum(cell3D.lightRig?.rig, LIGHT_RIGS, "lightRig.rig");
7808
- const dir = resolveEnum(cell3D.lightRig?.dir, LIGHT_DIRECTIONS, "lightRig.dir");
8142
+ const dir = resolveEnum(cell3D.lightRig?.dir, LIGHT_RIG_DIRECTIONS, "lightRig.dir");
7809
8143
  const lightRig = rig && dir ? voidEl("a:lightRig", {
7810
8144
  rig,
7811
8145
  dir
7812
8146
  }) : "";
7813
8147
  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]);
8148
+ return el("a:cell3D", { prstMaterial: resolveEnum(cell3D.material, PRESET_MATERIALS, "material") }, [raw(bevel), lightRig ? raw(lightRig) : null]);
7815
8149
  }
7816
8150
  //#endregion
7817
8151
  //#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
8152
  /**
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.
8153
+ * Validate a cell's `horzOverflow` before it reaches the XML, reporting and dropping anything
8154
+ * outside `ST_TextHorzOverflowType`. `undefined` (the common case) emits nothing.
7825
8155
  */
7826
8156
  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;
8157
+ return checkEnumOrWarn(value, TEXT_HORZ_OVERFLOW, "table/invalid-horz-overflow", "table cell: horzOverflow");
7834
8158
  }
7835
8159
  /**
7836
8160
  * Overlay `TableProps.outerBorder` onto one cell's own 4-side border tuple.
@@ -8295,7 +8619,6 @@ function renderZoomObject(slideItemObj, idx, x, y, cx, cy) {
8295
8619
  }
8296
8620
  //#endregion
8297
8621
  //#region src/gen/slide/object.ts
8298
- const PACKAGE_REL_NS$1 = "http://schemas.openxmlformats.org/package/2006/relationships";
8299
8622
  /** The MS-2007 `media` rel that pairs with an ECMA audio/video/online rel on the same Target. */
8300
8623
  const MS_MEDIA_REL = "http://schemas.microsoft.com/office/2007/relationships/media";
8301
8624
  /** Slide→chartEx-part relationship type (MS, not the ECMA `.../relationships/chart`). */
@@ -8506,6 +8829,9 @@ function slideObjectToXml(slide) {
8506
8829
  case "zoom":
8507
8830
  strSlideXml += renderZoomObject(slideItemObj, idx, x, y, cx, cy);
8508
8831
  break;
8832
+ case "model3d":
8833
+ strSlideXml += renderModel3dObject(slideItemObj, idx, x, y, cx, cy);
8834
+ break;
8509
8835
  case "group": {
8510
8836
  const groupChildren = slideItemObj._groupObjects || [];
8511
8837
  let innerXml = "";
@@ -8654,12 +8980,12 @@ function slideObjectRelationsToXml(slide, defaultRels) {
8654
8980
  lastRid = Math.max(lastRid, rel.rId);
8655
8981
  if (isHyperlinkRel(rel)) if (rel.data === "slide") rels.push(voidEl("Relationship", {
8656
8982
  Id: `rId${rel.rId}`,
8657
- Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide",
8983
+ Type: OFFICE_REL + "slide",
8658
8984
  Target: `slide${rel.Target}.xml`
8659
8985
  }));
8660
8986
  else rels.push(voidEl("Relationship", {
8661
8987
  Id: `rId${rel.rId}`,
8662
- Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
8988
+ Type: OFFICE_REL + "hyperlink",
8663
8989
  Target: rel.Target,
8664
8990
  TargetMode: "External"
8665
8991
  }));
@@ -8668,7 +8994,7 @@ function slideObjectRelationsToXml(slide, defaultRels) {
8668
8994
  lastRid = Math.max(lastRid, rel.rId);
8669
8995
  rels.push(voidEl("Relationship", {
8670
8996
  Id: `rId${rel.rId}`,
8671
- Type: rel.isChartEx ? CHARTEX_REL : "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart",
8997
+ Type: rel.isChartEx ? CHARTEX_REL : OFFICE_REL + "chart",
8672
8998
  Target: rel.Target
8673
8999
  }));
8674
9000
  });
@@ -8683,10 +9009,11 @@ function slideObjectRelationsToXml(slide, defaultRels) {
8683
9009
  });
8684
9010
  lastRid = Math.max(lastRid, rel.rId);
8685
9011
  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"));
9012
+ else if (rel.model3dRelType) rels.push(media(rel.model3dRelType));
9013
+ else if (relType.includes("image")) rels.push(media(OFFICE_REL + "image"));
9014
+ else if (relType.includes("audio")) rels.push(hasTarget(relTarget) ? media(MS_MEDIA_REL) : media(OFFICE_REL + "audio"));
9015
+ else if (relType.includes("video")) rels.push(hasTarget(relTarget) ? media(MS_MEDIA_REL) : media(OFFICE_REL + "video"));
9016
+ else if (relType.includes("online")) rels.push(hasTarget(relTarget) ? media(MS_MEDIA_REL, "External") : media(OFFICE_REL + "video", "External"));
8690
9017
  });
8691
9018
  defaultRels.forEach((rel, idx) => {
8692
9019
  rels.push(voidEl("Relationship", {
@@ -8695,7 +9022,7 @@ function slideObjectRelationsToXml(slide, defaultRels) {
8695
9022
  Target: rel.target
8696
9023
  }));
8697
9024
  });
8698
- return XML_DECL + "\r\n" + el("Relationships", { xmlns: PACKAGE_REL_NS$1 }, rels.map(raw));
9025
+ return XML_DECL + "\r\n" + el("Relationships", { xmlns: PACKAGE_REL_NS }, rels.map(raw));
8699
9026
  }
8700
9027
  //#endregion
8701
9028
  //#region src/gen/slide/slide.ts
@@ -8773,7 +9100,6 @@ function getLayoutIdxForSlide(slides, slideLayouts, slideNumber) {
8773
9100
  }
8774
9101
  //#endregion
8775
9102
  //#region src/gen/slide/notes.ts
8776
- const PACKAGE_REL_NS = "http://schemas.openxmlformats.org/package/2006/relationships";
8777
9103
  /**
8778
9104
  * Collect the speaker-notes runs for a slide (flattened across any number of `addNotes()` calls).
8779
9105
  * @param {PresSlideInternal} slide - the slide object
@@ -9257,19 +9583,19 @@ function makeXmlNotesSlide(slide) {
9257
9583
  function makeXmlNotesSlideRel(slide, slideNumber) {
9258
9584
  const hlinkRels = buildNotesSlideRels(slide).map((rel) => voidEl("Relationship", {
9259
9585
  Id: `rId${rel.rId}`,
9260
- Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
9586
+ Type: OFFICE_REL + "hyperlink",
9261
9587
  Target: rel.Target,
9262
9588
  TargetMode: "External"
9263
9589
  })).join("");
9264
9590
  return XML_DECL + el("Relationships", { xmlns: PACKAGE_REL_NS }, [
9265
9591
  raw(voidEl("Relationship", {
9266
9592
  Id: "rId1",
9267
- Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster",
9593
+ Type: OFFICE_REL + "notesMaster",
9268
9594
  Target: "../notesMasters/notesMaster1.xml"
9269
9595
  })),
9270
9596
  raw(voidEl("Relationship", {
9271
9597
  Id: "rId2",
9272
- Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide",
9598
+ Type: OFFICE_REL + "slide",
9273
9599
  Target: `../slides/slide${slideNumber}.xml`
9274
9600
  })),
9275
9601
  raw(hlinkRels)
@@ -9286,7 +9612,7 @@ function makeXmlNotesSlideRel(slide, slideNumber) {
9286
9612
  function makeXmlNotesMasterRel() {
9287
9613
  return XML_DECL + "\r\n" + el("Relationships", { xmlns: PACKAGE_REL_NS }, raw(voidEl("Relationship", {
9288
9614
  Id: "rId1",
9289
- Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme",
9615
+ Type: OFFICE_REL + "theme",
9290
9616
  Target: "../theme/theme2.xml"
9291
9617
  })), {
9292
9618
  childPrefix: "\n ",
@@ -9772,11 +10098,10 @@ function makeXmlCustomProperties(props) {
9772
10098
  * Emit the package root relationships (app/core/presentation, plus custom-props
9773
10099
  * when present).
9774
10100
  */
9775
- const SCHEMA_BASE = "http://schemas.openxmlformats.org/";
9776
10101
  function relationship$1(id, type, target) {
9777
10102
  return voidEl("Relationship", {
9778
10103
  Id: id,
9779
- Type: SCHEMA_BASE + type,
10104
+ Type: type,
9780
10105
  Target: target
9781
10106
  }, { openPrefix: "\n " });
9782
10107
  }
@@ -9785,11 +10110,11 @@ function relationship$1(id, type, target) {
9785
10110
  * @returns XML
9786
10111
  */
9787
10112
  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
10113
+ return XML_DECL + "\r\n" + el("Relationships", { xmlns: PACKAGE_REL_NS }, [
10114
+ raw(relationship$1("rId1", OFFICE_REL + "extended-properties", "docProps/app.xml")),
10115
+ raw(relationship$1("rId2", PACKAGE_REL_NS + "/metadata/core-properties", "docProps/core.xml")),
10116
+ raw(relationship$1("rId3", OFFICE_REL + "officeDocument", "ppt/presentation.xml")),
10117
+ hasCustomProps ? raw(relationship$1("rId4", OFFICE_REL + "custom-properties", "docProps/custom.xml")) : null
9793
10118
  ], { closePrefix: "\n " });
9794
10119
  }
9795
10120
  //#endregion
@@ -9829,13 +10154,13 @@ function presentationFontRelStart(slides) {
9829
10154
  }
9830
10155
  function makeXmlPresentationRels(slides, embeddedFonts) {
9831
10156
  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`));
10157
+ const rels = [relationship(1, OFFICE_REL + "slideMaster", "slideMasters/slideMaster1.xml")];
10158
+ for (let idx = 1; idx <= slides.length; idx++) rels.push(relationship(++intRelNum, OFFICE_REL + "slide", `slides/slide${idx}.xml`));
9834
10159
  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"));
10160
+ 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"));
10161
+ if ((slides || []).some((slide) => (slide._comments || []).length > 0)) rels.push(relationship(intRelNum + 5, OFFICE_REL + "commentAuthors", "commentAuthors.xml"));
9837
10162
  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));
10163
+ return XML_DECL + "\r\n" + el("Relationships", { xmlns: PACKAGE_REL_NS }, rels.map(raw));
9839
10164
  }
9840
10165
  //#endregion
9841
10166
  //#region src/gen/pres/presentation.ts
@@ -10563,18 +10888,14 @@ function createChartMediaRels(slide, zip, chartPromises) {
10563
10888
  async function buildPackageParts(source, props) {
10564
10889
  const pres = source.presentation;
10565
10890
  const arrChartPromises = [];
10566
- let arrMediaPromises = [];
10567
10891
  const zip = new ZipWriter();
10568
10892
  registerTransitionSounds(pres.slides);
10569
10893
  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 () => {
10894
+ return await encodeMediaForTargets([
10895
+ ...pres.slides,
10896
+ ...pres.slideLayouts,
10897
+ pres.masterSlide
10898
+ ], source.runtime, onMediaError).then(async () => {
10578
10899
  const canonicalMediaTargets = /* @__PURE__ */ new Map();
10579
10900
  for (const target of [
10580
10901
  ...pres.slides,
@@ -10600,10 +10921,7 @@ async function buildPackageParts(source, props) {
10600
10921
  rel.fileName = `${chartBase}.xml`;
10601
10922
  rel.Target = `/ppt/charts/${chartBase}.xml`;
10602
10923
  }
10603
- pres.slides.forEach((slide) => {
10604
- if (slide._slideLayout) addPlaceholdersToSlideLayouts(slide);
10605
- });
10606
- applyMeasuredFit(pres.slides, source.fontMetrics);
10924
+ bakeSlideContent(pres.slides, source.fontMetrics);
10607
10925
  const hasCustomProps = source.customProperties.length > 0;
10608
10926
  zip.add("[Content_Types].xml", makeXmlContTypes(pres.slides, pres.slideLayouts, pres.masterSlide, hasCustomProps, pres.embeddedFonts));
10609
10927
  zip.add("_rels/.rels", makeXmlRootRels(hasCustomProps));
@@ -10674,60 +10992,20 @@ async function writePackage(source, props) {
10674
10992
  //#endregion
10675
10993
  //#region src/presentation.ts
10676
10994
  /**
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)
10995
+ * ts-pptx: the presentation core
10683
10996
  *
10684
- * PptxGenJS (C) 2015-present Brent Ely -- https://github.com/gitbrent
10997
+ * Home of {@link PresentationCore}, the class every entry point subclasses with a runtime
10998
+ * adapter (`index.ts`, `node.ts`, `browser.ts`). See the class doc below for the layout of
10999
+ * its body and where package assembly actually happens.
10685
11000
  *
10686
- * Some code derived from the OfficeGen project:
10687
- * github.com/Ziv-Barber/officegen/ (Copyright 2013 Ziv Barber)
11001
+ * Units: this library takes user coordinates in inches and converts to EMU on output — see
11002
+ * `units.ts` for the conversions and `STANDARD_LAYOUTS` for the built-in slide sizes.
10688
11003
  *
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.
11004
+ * Derived from PptxGenJS (C) 2015-present Brent Ely (MIT), which in turn took some code from
11005
+ * the OfficeGen project (Copyright 2013 Ziv Barber). This project is likewise MIT; the full
11006
+ * license text is in `LICENSE` at the repository root.
10706
11007
  */
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";
11008
+ const VERSION = "3.0.0";
10731
11009
  function standardLayoutToPresLayout(layout) {
10732
11010
  return {
10733
11011
  name: layout.name,
@@ -11046,15 +11324,8 @@ var PresentationCore = class {
11046
11324
  */
11047
11325
  extractSlides = async (opts = {}) => {
11048
11326
  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);
11327
+ await encodeMediaForTargets(this._slides, this._runtime, onMediaError);
11328
+ bakeSlideContent(this._slides, this._fontMetrics);
11058
11329
  const slides = this._slides.map((slide) => {
11059
11330
  const relByRid = new Map(slide._relsMedia.map((rel) => [rel.rId, rel]));
11060
11331
  const avPreviewRids = /* @__PURE__ */ new Set();
@@ -11475,6 +11746,138 @@ var PresentationCore = class {
11475
11746
  }
11476
11747
  };
11477
11748
  //#endregion
11749
+ //#region src/clip.ts
11750
+ const HALF_DISC = {
11751
+ deep: {
11752
+ topEdgeX: .31788,
11753
+ botEdgeX: .31788,
11754
+ tipTopX: .30061,
11755
+ tipTopY: .01078,
11756
+ tipBotX: .30061,
11757
+ tipBotY: .98922,
11758
+ apexY: .5,
11759
+ c1b: {
11760
+ x: .11594,
11761
+ y: .86833
11762
+ },
11763
+ c2b: {
11764
+ x: 0,
11765
+ y: .69392
11766
+ },
11767
+ c1t: {
11768
+ x: 0,
11769
+ y: .30609
11770
+ },
11771
+ c2t: {
11772
+ x: .11594,
11773
+ y: .13167
11774
+ }
11775
+ },
11776
+ shallow: {
11777
+ topEdgeX: .12611,
11778
+ botEdgeX: .11128,
11779
+ tipTopX: .09976,
11780
+ tipTopY: .05448,
11781
+ tipBotX: .09976,
11782
+ tipBotY: .97617,
11783
+ apexY: .51533,
11784
+ c1b: {
11785
+ x: .03522,
11786
+ y: .83247
11787
+ },
11788
+ c2b: {
11789
+ x: 0,
11790
+ y: .67725
11791
+ },
11792
+ c1t: {
11793
+ x: 0,
11794
+ y: .3534
11795
+ },
11796
+ c2t: {
11797
+ x: .03522,
11798
+ y: .19818
11799
+ }
11800
+ }
11801
+ };
11802
+ /**
11803
+ * Half-disc clip path for a `w × h` box. Authored flat-right; `flat: 'left'` mirrors `x → 1 - x`.
11804
+ */
11805
+ function halfDisc(w, h, flat, preset) {
11806
+ const s = HALF_DISC[preset];
11807
+ const fx = flat === "left" ? (x) => (1 - x) * w : (x) => x * w;
11808
+ const fy = (y) => y * h;
11809
+ return [
11810
+ {
11811
+ x: fx(s.topEdgeX),
11812
+ y: fy(0),
11813
+ moveTo: true
11814
+ },
11815
+ {
11816
+ x: fx(1),
11817
+ y: fy(0)
11818
+ },
11819
+ {
11820
+ x: fx(1),
11821
+ y: fy(1)
11822
+ },
11823
+ {
11824
+ x: fx(s.botEdgeX),
11825
+ y: fy(1)
11826
+ },
11827
+ {
11828
+ x: fx(s.tipBotX),
11829
+ y: fy(s.tipBotY)
11830
+ },
11831
+ {
11832
+ x: fx(0),
11833
+ y: fy(s.apexY),
11834
+ curve: {
11835
+ type: "cubic",
11836
+ x1: fx(s.c1b.x),
11837
+ y1: fy(s.c1b.y),
11838
+ x2: fx(s.c2b.x),
11839
+ y2: fy(s.c2b.y)
11840
+ }
11841
+ },
11842
+ {
11843
+ x: fx(s.tipTopX),
11844
+ y: fy(s.tipTopY),
11845
+ curve: {
11846
+ type: "cubic",
11847
+ x1: fx(s.c1t.x),
11848
+ y1: fy(s.c1t.y),
11849
+ x2: fx(s.c2t.x),
11850
+ y2: fy(s.c2t.y)
11851
+ }
11852
+ },
11853
+ { close: true }
11854
+ ];
11855
+ }
11856
+ /**
11857
+ * Resolve a {@link ClipShape} to a freeform `points` path filling a `w × h` box, ready to hand
11858
+ * to `addImage({ points })`.
11859
+ *
11860
+ * `w`/`h` are the image box's size **in inches** and must match the `w`/`h` the picture is drawn
11861
+ * at — the path is emitted in the box's own coordinate space, not normalized (see the module
11862
+ * note above).
11863
+ * @param {ClipShape} shape - the named silhouette and its options
11864
+ * @param {number} w - image box width (inches)
11865
+ * @param {number} h - image box height (inches)
11866
+ * @returns {GeometryPoint[]} the freeform path, for `addImage({ points })`
11867
+ * @example
11868
+ * const w = 5.22, h = 7.5
11869
+ * slide.addImage({
11870
+ * path: 'cover.jpg', x: 0, y: 0, w, h,
11871
+ * points: clipPath({ kind: 'half-disc', flat: 'right' }, w, h),
11872
+ * sizing: { type: 'cover' }, // center-crop the photo into the "D"
11873
+ * })
11874
+ */
11875
+ function clipPath(shape, w, h) {
11876
+ switch (shape.kind) {
11877
+ case "half-disc": return halfDisc(w, h, shape.flat, shape.preset ?? "deep");
11878
+ }
11879
+ }
11880
+ //#endregion
11478
11881
  //#region src/types/text.ts
11479
11882
  /** Factory for a single inline text run. Prevents `as never` casts when building mixed-style run arrays. */
11480
11883
  function textRun(text, options) {
@@ -11488,6 +11891,6 @@ function textRuns(runs) {
11488
11891
  return runs;
11489
11892
  }
11490
11893
  //#endregion
11491
- export { textRuns as n, PresentationCore as r, textRun as t };
11894
+ export { PresentationCore as i, textRuns as n, clipPath as r, textRun as t };
11492
11895
 
11493
- //# sourceMappingURL=text-B1ZrPEeK.js.map
11896
+ //# sourceMappingURL=text-120VFSDm.js.map