@shbernal/pptxgenjs 5.2.0 → 5.4.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.
- package/dist/{browser-DraPrTLD.js → browser-DGH8T04O.js} +3 -3
- package/dist/{browser-DraPrTLD.js.map → browser-DGH8T04O.js.map} +1 -1
- package/dist/browser.d.ts +3 -3
- package/dist/browser.js +4 -4
- package/dist/{core-interfaces-vUc0ElZs.js → core-interfaces-C091uvh_.js} +41 -9
- package/dist/core-interfaces-C091uvh_.js.map +1 -0
- package/dist/core.d.ts +2 -2
- package/dist/core.js +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/inspect.js +1 -1
- package/dist/node.d.ts +3 -3
- package/dist/node.js +4 -4
- package/dist/{pptxgen--5RWzhb4.js → pptxgen-S8dEuBnC.js} +922 -244
- package/dist/pptxgen-S8dEuBnC.js.map +1 -0
- package/dist/{pptxgen-DzBNFPxG.d.ts → pptxgen-ZuXXUvB-.d.ts} +2 -2
- package/dist/{pptxgen-DzBNFPxG.d.ts.map → pptxgen-ZuXXUvB-.d.ts.map} +1 -1
- package/dist/standalone.d.ts +443 -34
- package/dist/standalone.d.ts.map +1 -1
- package/dist/standalone.js +1002 -248
- package/dist/standalone.js.map +1 -1
- package/dist/units-BMrBTU0-.js +106 -0
- package/dist/units-BMrBTU0-.js.map +1 -0
- package/dist/{units-y594YyBo.d.ts → units-DlsWUw1U.d.ts} +444 -35
- package/dist/units-DlsWUw1U.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/core-interfaces-vUc0ElZs.js.map +0 -1
- package/dist/pptxgen--5RWzhb4.js.map +0 -1
- package/dist/units-DmzbVUNp.js +0 -62
- package/dist/units-DmzbVUNp.js.map +0 -1
- package/dist/units-y594YyBo.d.ts.map +0 -1
package/dist/standalone.js
CHANGED
|
@@ -3137,6 +3137,10 @@ while (n === a[++i] && n === a[++i] && n === a[++i] && n === a[++i] && n === a[+
|
|
|
3137
3137
|
const EMU_PER_INCH = 914400;
|
|
3138
3138
|
const EMU_PER_POINT = 12700;
|
|
3139
3139
|
const POINTS_PER_INCH = 72;
|
|
3140
|
+
/** A bare number larger than this (in inches) is almost certainly a mistake — likely a raw EMU
|
|
3141
|
+
* value passed where inches are expected. We interpret it as inches (the documented contract) but
|
|
3142
|
+
* warn, pointing at the explicit `"<n>emu"` form. ~1000in is far beyond any real slide. */
|
|
3143
|
+
const IMPLAUSIBLE_INCHES = 1e3;
|
|
3140
3144
|
function inchesToEmu(inches) {
|
|
3141
3145
|
assertFiniteNumber(inches, "inches");
|
|
3142
3146
|
return Math.round(inches * EMU_PER_INCH);
|
|
@@ -3150,6 +3154,46 @@ function pixelsToEmu(pixels, dpi) {
|
|
|
3150
3154
|
assertPositiveFiniteNumber(dpi, "dpi");
|
|
3151
3155
|
return inchesToEmu(pixels / dpi);
|
|
3152
3156
|
}
|
|
3157
|
+
/**
|
|
3158
|
+
* Resolve a percentage of an axis length to EMU.
|
|
3159
|
+
* @param percent - percentage value (e.g. `50` for 50%)
|
|
3160
|
+
* @param axisEmu - the axis length in EMU (slide width for x/w, height for y/h)
|
|
3161
|
+
*/
|
|
3162
|
+
function percentToEmu(percent, axisEmu) {
|
|
3163
|
+
assertFiniteNumber(percent, "percent");
|
|
3164
|
+
assertFiniteNumber(axisEmu, "axisEmu");
|
|
3165
|
+
return Math.round(percent / 100 * axisEmu);
|
|
3166
|
+
}
|
|
3167
|
+
/**
|
|
3168
|
+
* The single user-coordinate → EMU boundary. Convert each user-supplied coordinate exactly once.
|
|
3169
|
+
*
|
|
3170
|
+
* Accepts (see {@link Coord}):
|
|
3171
|
+
* - a bare `number` → **always inches** (the documented unit); no magnitude guessing
|
|
3172
|
+
* - `"<n>%"` → percentage of `axisEmu`
|
|
3173
|
+
* - `"<n>in"` / `"<n>pt"` / `"<n>emu"` → explicit units (the escape hatch for non-inch values)
|
|
3174
|
+
*
|
|
3175
|
+
* Throws on non-finite or unparseable input rather than silently emitting a degenerate 0-size.
|
|
3176
|
+
* @param value - user coordinate
|
|
3177
|
+
* @param axisEmu - axis length in EMU, used only to resolve percentages
|
|
3178
|
+
*/
|
|
3179
|
+
function coordToEmu(value, axisEmu) {
|
|
3180
|
+
if (typeof value === "number") {
|
|
3181
|
+
assertFiniteNumber(value, "coordinate");
|
|
3182
|
+
if (Math.abs(value) > IMPLAUSIBLE_INCHES) console.warn(`PptxGenJS: coordinate ${value} interpreted as ${value} inches. A bare number is always inches; if you meant EMU, pass it as a string like "${Math.round(value)}emu".`);
|
|
3183
|
+
return inchesToEmu(value);
|
|
3184
|
+
}
|
|
3185
|
+
const match = /^\s*(-?\d*\.?\d+)\s*(%|in|pt|emu)\s*$/.exec(value);
|
|
3186
|
+
if (!match) throw new Error(`PptxGenJS: invalid coordinate "${value}". Expected a number (inches) or a string like "50%", "5in", "72pt", or "914400emu".`);
|
|
3187
|
+
const n = Number(match[1]);
|
|
3188
|
+
switch (match[2]) {
|
|
3189
|
+
case "%": return percentToEmu(n, axisEmu);
|
|
3190
|
+
case "in": return inchesToEmu(n);
|
|
3191
|
+
case "pt": return pointsToEmu(n);
|
|
3192
|
+
default:
|
|
3193
|
+
assertFiniteNumber(n, "coordinate");
|
|
3194
|
+
return Math.round(n);
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3153
3197
|
function emuToInches(emu) {
|
|
3154
3198
|
assertFiniteNumber(emu, "emu");
|
|
3155
3199
|
return emu / EMU_PER_INCH;
|
|
@@ -3433,7 +3477,7 @@ let ShapeType = /* @__PURE__ */ function(ShapeType) {
|
|
|
3433
3477
|
ShapeType["flowChartSort"] = "flowChartSort";
|
|
3434
3478
|
ShapeType["flowChartSummingJunction"] = "flowChartSummingJunction";
|
|
3435
3479
|
ShapeType["flowChartTerminator"] = "flowChartTerminator";
|
|
3436
|
-
ShapeType["
|
|
3480
|
+
ShapeType["foldedCorner"] = "foldedCorner";
|
|
3437
3481
|
ShapeType["frame"] = "frame";
|
|
3438
3482
|
ShapeType["funnel"] = "funnel";
|
|
3439
3483
|
ShapeType["gear6"] = "gear6";
|
|
@@ -3636,7 +3680,7 @@ let SHAPE_TYPE = /* @__PURE__ */ function(SHAPE_TYPE) {
|
|
|
3636
3680
|
SHAPE_TYPE["FLOWCHART_STORED_DATA"] = "flowChartOnlineStorage";
|
|
3637
3681
|
SHAPE_TYPE["FLOWCHART_SUMMING_JUNCTION"] = "flowChartSummingJunction";
|
|
3638
3682
|
SHAPE_TYPE["FLOWCHART_TERMINATOR"] = "flowChartTerminator";
|
|
3639
|
-
SHAPE_TYPE["FOLDED_CORNER"] = "
|
|
3683
|
+
SHAPE_TYPE["FOLDED_CORNER"] = "foldedCorner";
|
|
3640
3684
|
SHAPE_TYPE["FRAME"] = "frame";
|
|
3641
3685
|
SHAPE_TYPE["FUNNEL"] = "funnel";
|
|
3642
3686
|
SHAPE_TYPE["GEAR_6"] = "gear6";
|
|
@@ -3671,10 +3715,6 @@ let SHAPE_TYPE = /* @__PURE__ */ function(SHAPE_TYPE) {
|
|
|
3671
3715
|
SHAPE_TYPE["LINE_CALLOUT_3_ACCENT_BAR"] = "accentCallout3";
|
|
3672
3716
|
SHAPE_TYPE["LINE_CALLOUT_3_BORDER_AND_ACCENT_BAR"] = "accentBorderCallout3";
|
|
3673
3717
|
SHAPE_TYPE["LINE_CALLOUT_3_NO_BORDER"] = "callout3";
|
|
3674
|
-
SHAPE_TYPE["LINE_CALLOUT_4"] = "borderCallout4";
|
|
3675
|
-
SHAPE_TYPE["LINE_CALLOUT_4_ACCENT_BAR"] = "accentCallout3=4";
|
|
3676
|
-
SHAPE_TYPE["LINE_CALLOUT_4_BORDER_AND_ACCENT_BAR"] = "accentBorderCallout4";
|
|
3677
|
-
SHAPE_TYPE["LINE_CALLOUT_4_NO_BORDER"] = "callout4";
|
|
3678
3718
|
SHAPE_TYPE["LINE"] = "line";
|
|
3679
3719
|
SHAPE_TYPE["LINE_INVERSE"] = "lineInv";
|
|
3680
3720
|
SHAPE_TYPE["MATH_DIVIDE"] = "mathDivide";
|
|
@@ -3742,6 +3782,31 @@ let SHAPE_TYPE = /* @__PURE__ */ function(SHAPE_TYPE) {
|
|
|
3742
3782
|
SHAPE_TYPE["WAVE"] = "wave";
|
|
3743
3783
|
return SHAPE_TYPE;
|
|
3744
3784
|
}({});
|
|
3785
|
+
/**
|
|
3786
|
+
* Valid ECMA-376 `ST_ShapeType` presets that are not surfaced with a friendly
|
|
3787
|
+
* `SHAPE_TYPE` name. They are still legal geometries PowerPoint renders, so the
|
|
3788
|
+
* preset-validation set must accept them.
|
|
3789
|
+
*/
|
|
3790
|
+
const EXTRA_SHAPE_PRESETS = [
|
|
3791
|
+
"straightConnector1",
|
|
3792
|
+
"bentConnector2",
|
|
3793
|
+
"bentConnector3",
|
|
3794
|
+
"bentConnector4",
|
|
3795
|
+
"bentConnector5",
|
|
3796
|
+
"curvedConnector2",
|
|
3797
|
+
"curvedConnector3",
|
|
3798
|
+
"curvedConnector4",
|
|
3799
|
+
"curvedConnector5"
|
|
3800
|
+
];
|
|
3801
|
+
/**
|
|
3802
|
+
* Every shape geometry name PptxGenJS can serialize without corrupting the
|
|
3803
|
+
* package: the OOXML preset geometries (`ST_ShapeType` — `SHAPE_TYPE` values
|
|
3804
|
+
* plus the unexposed connectors above) and `custGeom` (freeform paths, emitted
|
|
3805
|
+
* as `<a:custGeom>` rather than `<a:prstGeom>`). Used to reject bogus presets
|
|
3806
|
+
* before they become an invalid `<a:prstGeom prst="...">` that triggers
|
|
3807
|
+
* PowerPoint's "needs repair" dialog and drops the shape.
|
|
3808
|
+
*/
|
|
3809
|
+
const VALID_SHAPE_PRESETS = new Set([...Object.values(SHAPE_TYPE), ...EXTRA_SHAPE_PRESETS]);
|
|
3745
3810
|
let CHART_TYPE = /* @__PURE__ */ function(CHART_TYPE) {
|
|
3746
3811
|
CHART_TYPE["AREA"] = "area";
|
|
3747
3812
|
CHART_TYPE["BAR"] = "bar";
|
|
@@ -3779,6 +3844,7 @@ let MASTER_OBJECTS = /* @__PURE__ */ function(MASTER_OBJECTS) {
|
|
|
3779
3844
|
}({});
|
|
3780
3845
|
let SLIDE_OBJECT_TYPES = /* @__PURE__ */ function(SLIDE_OBJECT_TYPES) {
|
|
3781
3846
|
SLIDE_OBJECT_TYPES["chart"] = "chart";
|
|
3847
|
+
SLIDE_OBJECT_TYPES["connector"] = "connector";
|
|
3782
3848
|
SLIDE_OBJECT_TYPES["hyperlink"] = "hyperlink";
|
|
3783
3849
|
SLIDE_OBJECT_TYPES["image"] = "image";
|
|
3784
3850
|
SLIDE_OBJECT_TYPES["media"] = "media";
|
|
@@ -3790,6 +3856,16 @@ let SLIDE_OBJECT_TYPES = /* @__PURE__ */ function(SLIDE_OBJECT_TYPES) {
|
|
|
3790
3856
|
SLIDE_OBJECT_TYPES["notes"] = "notes";
|
|
3791
3857
|
return SLIDE_OBJECT_TYPES;
|
|
3792
3858
|
}({});
|
|
3859
|
+
/**
|
|
3860
|
+
* Maps a friendly `ConnectorType` to its OOXML connector preset geometry (`prst`).
|
|
3861
|
+
* These are the canonical 1-segment straight, 3-segment bent (elbow), and 3-segment
|
|
3862
|
+
* curved connector presets PowerPoint uses by default.
|
|
3863
|
+
*/
|
|
3864
|
+
const CONNECTOR_PRESETS = {
|
|
3865
|
+
straight: "straightConnector1",
|
|
3866
|
+
elbow: "bentConnector3",
|
|
3867
|
+
curved: "curvedConnector3"
|
|
3868
|
+
};
|
|
3793
3869
|
let PLACEHOLDER_TYPES = /* @__PURE__ */ function(PLACEHOLDER_TYPES) {
|
|
3794
3870
|
PLACEHOLDER_TYPES["title"] = "title";
|
|
3795
3871
|
PLACEHOLDER_TYPES["body"] = "body";
|
|
@@ -3910,27 +3986,20 @@ const IMG_PLAYBTN = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAB4AAAAVnCAYAA
|
|
|
3910
3986
|
* PptxGenJS: Utility Methods
|
|
3911
3987
|
*/
|
|
3912
3988
|
/**
|
|
3913
|
-
*
|
|
3914
|
-
* -
|
|
3915
|
-
*
|
|
3916
|
-
* -
|
|
3917
|
-
* -
|
|
3918
|
-
* @param {
|
|
3919
|
-
* @param {'X' | 'Y'} xyDir -
|
|
3920
|
-
* @param {PresLayout} layout - presentation layout
|
|
3921
|
-
* @returns {
|
|
3989
|
+
* Resolve a user `Coord` (x/y/w/h) to EMU — the single user-coordinate → EMU boundary.
|
|
3990
|
+
* - bare `number` → **inches** (no magnitude guessing); `"<n>%"` → percent of the slide axis;
|
|
3991
|
+
* `"<n>in"`/`"<n>pt"`/`"<n>emu"` → explicit units (see {@link Coord} / {@link coordToEmu})
|
|
3992
|
+
* - `null`/`undefined` → 0 (callers may omit a coordinate)
|
|
3993
|
+
* - throws on a non-finite number rather than silently collapsing the object to zero size
|
|
3994
|
+
* @param {Coord|null|undefined} size - user coordinate
|
|
3995
|
+
* @param {'X' | 'Y'} xyDir - axis (selects slide width vs height for percentages)
|
|
3996
|
+
* @param {PresLayout} layout - presentation layout (EMU dimensions)
|
|
3997
|
+
* @returns {Emu} resolved EMU value
|
|
3922
3998
|
*/
|
|
3923
3999
|
function getSmartParseNumber(size, xyDir, layout) {
|
|
3924
|
-
if (
|
|
4000
|
+
if (size === null || size === void 0) return 0;
|
|
3925
4001
|
if (typeof size === "number" && !isFinite(size)) throw new Error(`Invalid ${xyDir || "coordinate"} value: expected a finite number but received ${String(size)}. This usually means a layout dimension was read from a missing property (e.g. \`layout.width\` returning \`undefined\`). Use \`slide.width\`/\`slide.height\` or \`STANDARD_LAYOUTS.<NAME>.width\`/\`.height\` (inches).`);
|
|
3926
|
-
|
|
3927
|
-
if (typeof size === "number" && size >= 100) return size;
|
|
3928
|
-
if (typeof size === "string" && size.includes("%")) {
|
|
3929
|
-
if (xyDir && xyDir === "X") return Math.round(parseFloat(size) / 100 * layout.width);
|
|
3930
|
-
if (xyDir && xyDir === "Y") return Math.round(parseFloat(size) / 100 * layout.height);
|
|
3931
|
-
return Math.round(parseFloat(size) / 100 * layout.width);
|
|
3932
|
-
}
|
|
3933
|
-
return 0;
|
|
4002
|
+
return coordToEmu(size, xyDir === "Y" ? layout.height : layout.width);
|
|
3934
4003
|
}
|
|
3935
4004
|
/**
|
|
3936
4005
|
* Basic UUID Generator Adapted
|
|
@@ -4003,14 +4072,16 @@ function getDuplicateObjectNames(names) {
|
|
|
4003
4072
|
return Array.from(dupes);
|
|
4004
4073
|
}
|
|
4005
4074
|
/**
|
|
4006
|
-
* Convert inches into EMU
|
|
4007
|
-
*
|
|
4008
|
-
* @
|
|
4075
|
+
* Convert inches into EMU.
|
|
4076
|
+
* - accepts a number (inches) or a numeric/`"<n>in"` string
|
|
4077
|
+
* - no magnitude guessing: values are always treated as inches (use {@link coordToEmu} for
|
|
4078
|
+
* user coordinates that may carry other units)
|
|
4079
|
+
* @param {number|string} inches - inches as number or string
|
|
4080
|
+
* @returns {Emu} EMU value
|
|
4009
4081
|
*/
|
|
4010
4082
|
function inch2Emu(inches) {
|
|
4011
|
-
if (typeof inches === "number" && inches > 100) return inches;
|
|
4012
4083
|
if (typeof inches === "string") inches = Number(inches.replace(/in*/gi, ""));
|
|
4013
|
-
return
|
|
4084
|
+
return inchesToEmu(inches);
|
|
4014
4085
|
}
|
|
4015
4086
|
/**
|
|
4016
4087
|
* Convert `pt` into points (using `ONEPT`)
|
|
@@ -4022,6 +4093,33 @@ function valToPts(pt) {
|
|
|
4022
4093
|
return isNaN(points) ? 0 : Math.round(points * ONEPT);
|
|
4023
4094
|
}
|
|
4024
4095
|
/**
|
|
4096
|
+
* Convert a transparency percentage (0-100) into a schema-valid `<a:alpha>` value
|
|
4097
|
+
* (ST_PositiveFixedPercentage, 0-100000). Out-of-range transparency yields an
|
|
4098
|
+
* alpha that PowerPoint rejects as needing repair, so clamp into range and warn.
|
|
4099
|
+
*/
|
|
4100
|
+
function transparencyToAlpha(transparency) {
|
|
4101
|
+
const pct = Math.min(100, Math.max(0, transparency));
|
|
4102
|
+
if (pct !== transparency) console.warn(`Warning: transparency ${transparency} is outside the valid range 0-100; using ${pct}.`);
|
|
4103
|
+
return Math.round((100 - pct) * 1e3);
|
|
4104
|
+
}
|
|
4105
|
+
/** Convert an opacity (0-1) into a schema-valid `<a:alpha>` value (0-100000); clamps + warns on out-of-range input. */
|
|
4106
|
+
function opacityToAlpha(opacity) {
|
|
4107
|
+
const o = Math.min(1, Math.max(0, opacity));
|
|
4108
|
+
if (o !== opacity) console.warn(`Warning: opacity ${opacity} is outside the valid range 0-1; using ${o}.`);
|
|
4109
|
+
return Math.round(o * 1e5);
|
|
4110
|
+
}
|
|
4111
|
+
/**
|
|
4112
|
+
* Convert a line width (points) to EMU clamped into ST_LineWidth (0..20116800 EMU,
|
|
4113
|
+
* i.e. 0-1584pt). Out-of-range widths make PowerPoint report the package as needing
|
|
4114
|
+
* repair, so clamp into range and warn.
|
|
4115
|
+
*/
|
|
4116
|
+
function lineWidthToEmu(widthPts) {
|
|
4117
|
+
const raw = valToPts(widthPts);
|
|
4118
|
+
const clamped = Math.min(20116800, Math.max(0, raw));
|
|
4119
|
+
if (clamped !== raw) console.warn(`Warning: line width ${widthPts} is outside the valid range 0-1584pt; using ${clamped / ONEPT}.`);
|
|
4120
|
+
return clamped;
|
|
4121
|
+
}
|
|
4122
|
+
/**
|
|
4025
4123
|
* Convert degrees (0..360) to PowerPoint `rot` value
|
|
4026
4124
|
* @param {number} d degrees
|
|
4027
4125
|
* @returns {number} calculated `rot` value
|
|
@@ -4069,8 +4167,10 @@ function createColorElement(colorStr, innerElements) {
|
|
|
4069
4167
|
}
|
|
4070
4168
|
let colorVal = (colorStr || "").replace("#", "");
|
|
4071
4169
|
if (/^[0-9a-fA-F]{8}$/.test(colorVal)) {
|
|
4072
|
-
|
|
4073
|
-
|
|
4170
|
+
if (!innerElements?.includes("<a:alpha")) {
|
|
4171
|
+
const alphaHex = colorVal.slice(6, 8);
|
|
4172
|
+
innerElements = `<a:alpha val="${Math.round(parseInt(alphaHex, 16) / 255 * 1e5)}"/>${innerElements || ""}`;
|
|
4173
|
+
}
|
|
4074
4174
|
colorVal = colorVal.slice(0, 6);
|
|
4075
4175
|
}
|
|
4076
4176
|
if (!REGEX_HEX_COLOR.test(colorVal) && colorVal !== "bg1" && colorVal !== "bg2" && colorVal !== "tx1" && colorVal !== "tx2" && colorVal !== "accent1" && colorVal !== "accent2" && colorVal !== "accent3" && colorVal !== "accent4" && colorVal !== "accent5" && colorVal !== "accent6") {
|
|
@@ -4096,12 +4196,38 @@ function createGlowElement(options, defaults) {
|
|
|
4096
4196
|
};
|
|
4097
4197
|
const size = Math.round(opts.size * ONEPT);
|
|
4098
4198
|
const color = opts.color || "000000";
|
|
4099
|
-
const opacity =
|
|
4199
|
+
const opacity = opacityToAlpha(opts.opacity ?? 0);
|
|
4100
4200
|
strXml += `<a:glow rad="${size}">`;
|
|
4101
4201
|
strXml += createColorElement(color, `<a:alpha val="${opacity}"/>`);
|
|
4102
4202
|
strXml += "</a:glow>";
|
|
4103
4203
|
return strXml;
|
|
4104
4204
|
}
|
|
4205
|
+
/**
|
|
4206
|
+
* Creates an `a:outerShdw`/`a:innerShdw` element for a text run or shape.
|
|
4207
|
+
* Returns the shadow element only (no wrapping `a:effectLst`) so callers can
|
|
4208
|
+
* combine it with other effects (e.g. glow) inside a single `a:effectLst`.
|
|
4209
|
+
* @param {ShadowProps} options shadow properties
|
|
4210
|
+
* @param {ShadowProps} defaults defaults for unspecified properties in `options`
|
|
4211
|
+
* @see http://officeopenxml.com/drwSp-effects.php
|
|
4212
|
+
* @returns {string} XML string, or '' when type is 'none'
|
|
4213
|
+
*/
|
|
4214
|
+
function createShadowElement$1(options, defaults) {
|
|
4215
|
+
const opts = {
|
|
4216
|
+
...defaults,
|
|
4217
|
+
...options
|
|
4218
|
+
};
|
|
4219
|
+
if (opts.type === "none") return "";
|
|
4220
|
+
const type = opts.type || "outer";
|
|
4221
|
+
const blur = valToPts(opts.blur ?? 0);
|
|
4222
|
+
const offset = valToPts(opts.offset ?? 0);
|
|
4223
|
+
const angle = Math.round((opts.angle ?? 0) * 6e4);
|
|
4224
|
+
const opacity = Math.round((opts.opacity ?? .75) * 1e5);
|
|
4225
|
+
const color = opts.color || "000000";
|
|
4226
|
+
let strXml = `<a:${type}Shdw ${type === "outer" ? "sx=\"100000\" sy=\"100000\" kx=\"0\" ky=\"0\" algn=\"bl\" rotWithShape=\"0\" " : ""}blurRad="${blur}" dist="${offset}" dir="${angle}">`;
|
|
4227
|
+
strXml += createColorElement(color, `<a:alpha val="${opacity}"/>`);
|
|
4228
|
+
strXml += `</a:${type}Shdw>`;
|
|
4229
|
+
return strXml;
|
|
4230
|
+
}
|
|
4105
4231
|
function boolToXml(value) {
|
|
4106
4232
|
return value ? "1" : "0";
|
|
4107
4233
|
}
|
|
@@ -4112,8 +4238,8 @@ function normalizeGradientAngle(angle) {
|
|
|
4112
4238
|
}
|
|
4113
4239
|
function gradientStopColorAdjustments(stop) {
|
|
4114
4240
|
let internalElements = "";
|
|
4115
|
-
if (stop.alpha) internalElements += `<a:alpha val="${
|
|
4116
|
-
if (stop.transparency) internalElements += `<a:alpha val="${
|
|
4241
|
+
if (stop.alpha) internalElements += `<a:alpha val="${transparencyToAlpha(stop.alpha)}"/>`;
|
|
4242
|
+
if (stop.transparency) internalElements += `<a:alpha val="${transparencyToAlpha(stop.transparency)}"/>`;
|
|
4117
4243
|
return internalElements;
|
|
4118
4244
|
}
|
|
4119
4245
|
function normalizeGradientStops(stops) {
|
|
@@ -4163,6 +4289,17 @@ function genXmlPatternFill(pattern) {
|
|
|
4163
4289
|
* @param {Color | ShapeFillProps | ShapeLineProps} props fill props
|
|
4164
4290
|
* @returns XML string
|
|
4165
4291
|
*/
|
|
4292
|
+
/**
|
|
4293
|
+
* Map a friendly `LineCap` value to the OOXML `cap` attribute value (`flat`/`sq`/`rnd`).
|
|
4294
|
+
* @param {LineCap} [lineCap] - line cap style (defaults to `flat`)
|
|
4295
|
+
* @returns {string} value for the `cap` attribute on `<a:ln>`
|
|
4296
|
+
*/
|
|
4297
|
+
function createLineCap(lineCap) {
|
|
4298
|
+
if (!lineCap || lineCap === "flat") return "flat";
|
|
4299
|
+
else if (lineCap === "square") return "sq";
|
|
4300
|
+
else if (lineCap === "round") return "rnd";
|
|
4301
|
+
else throw new Error(`Invalid line cap: ${String(lineCap)}`);
|
|
4302
|
+
}
|
|
4166
4303
|
function genXmlColorSelection(props) {
|
|
4167
4304
|
let fillType = "solid";
|
|
4168
4305
|
let colorVal = "";
|
|
@@ -4173,8 +4310,8 @@ function genXmlColorSelection(props) {
|
|
|
4173
4310
|
else {
|
|
4174
4311
|
if (props.type) fillType = props.type;
|
|
4175
4312
|
if (props.color) colorVal = props.color;
|
|
4176
|
-
if (props.alpha) internalElements += `<a:alpha val="${
|
|
4177
|
-
if (props.transparency) internalElements += `<a:alpha val="${
|
|
4313
|
+
if (props.alpha) internalElements += `<a:alpha val="${transparencyToAlpha(props.alpha)}"/>`;
|
|
4314
|
+
if (props.transparency) internalElements += `<a:alpha val="${transparencyToAlpha(props.transparency)}"/>`;
|
|
4178
4315
|
}
|
|
4179
4316
|
switch (fillType) {
|
|
4180
4317
|
case "solid":
|
|
@@ -4272,15 +4409,16 @@ function decodeBase64ToBytes(b64) {
|
|
|
4272
4409
|
}
|
|
4273
4410
|
}
|
|
4274
4411
|
/**
|
|
4275
|
-
* Read the intrinsic
|
|
4412
|
+
* Read the intrinsic dimensions of an image from its header bytes.
|
|
4276
4413
|
* - synchronous: parses only file-format headers, never decodes pixels
|
|
4277
|
-
* -
|
|
4278
|
-
* - vector
|
|
4414
|
+
* - raster: PNG, JPEG, GIF, BMP, and WebP (VP8 / VP8L / VP8X) — natural pixels
|
|
4415
|
+
* - vector: SVG — intrinsic size from the root `<svg>` width/height or viewBox
|
|
4416
|
+
* - unrecognized formats return `null` (no measurable intrinsic size)
|
|
4279
4417
|
*
|
|
4280
4418
|
* Used by image `sizing: 'cover' | 'contain'` to compute an aspect-correct
|
|
4281
4419
|
* `<a:srcRect>` crop from the *natural* image ratio rather than the displayed box.
|
|
4282
4420
|
* @param {string} dataB64 - base64 image payload or `data:` URI
|
|
4283
|
-
* @returns {{ w: number, h: number } | null} natural
|
|
4421
|
+
* @returns {{ w: number, h: number } | null} natural size, or `null` when unmeasurable
|
|
4284
4422
|
*/
|
|
4285
4423
|
function getImageSizeFromBase64(dataB64) {
|
|
4286
4424
|
const b = decodeBase64ToBytes(dataB64);
|
|
@@ -4366,8 +4504,46 @@ function getImageSizeFromBase64(dataB64) {
|
|
|
4366
4504
|
}
|
|
4367
4505
|
return null;
|
|
4368
4506
|
}
|
|
4507
|
+
const text = utf8Decode(b);
|
|
4508
|
+
if (/<svg[\s>]/i.test(text)) return getSvgSizeFromMarkup(text);
|
|
4369
4509
|
return null;
|
|
4370
4510
|
}
|
|
4511
|
+
/**
|
|
4512
|
+
* Read the intrinsic size of an SVG document from its root `<svg>` element.
|
|
4513
|
+
* Follows the SVG sizing model: an explicit absolute `width`/`height` pair wins;
|
|
4514
|
+
* otherwise the `viewBox` width/height defines the size (and thus aspect ratio).
|
|
4515
|
+
* Percentage or missing `width`/`height` fall through to `viewBox`.
|
|
4516
|
+
* @param {string} svg - SVG markup
|
|
4517
|
+
* @returns {{ w: number, h: number } | null} intrinsic size, or `null` when undeterminable
|
|
4518
|
+
*/
|
|
4519
|
+
function getSvgSizeFromMarkup(svg) {
|
|
4520
|
+
const openTag = /<svg\b[^>]*>/i.exec(svg)?.[0];
|
|
4521
|
+
if (!openTag) return null;
|
|
4522
|
+
const attr = (name) => new RegExp(`\\b${name}\\s*=\\s*["']([^"']*)["']`, "i").exec(openTag)?.[1] ?? null;
|
|
4523
|
+
const absLength = (val) => {
|
|
4524
|
+
if (val == null || /%\s*$/.test(val)) return NaN;
|
|
4525
|
+
const m = /^\s*\+?(\d*\.?\d+)/.exec(val);
|
|
4526
|
+
return m ? parseFloat(m[1]) : NaN;
|
|
4527
|
+
};
|
|
4528
|
+
let w = absLength(attr("width"));
|
|
4529
|
+
let h = absLength(attr("height"));
|
|
4530
|
+
if (!(w > 0 && h > 0)) {
|
|
4531
|
+
const vb = attr("viewBox");
|
|
4532
|
+
const p = vb ? vb.trim().split(/[\s,]+/).map(Number) : [];
|
|
4533
|
+
if (p.length === 4 && p[2] > 0 && p[3] > 0) {
|
|
4534
|
+
w = p[2];
|
|
4535
|
+
h = p[3];
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
return w > 0 && h > 0 ? {
|
|
4539
|
+
w,
|
|
4540
|
+
h
|
|
4541
|
+
} : null;
|
|
4542
|
+
}
|
|
4543
|
+
/** Decode UTF-8 bytes to a string, isomorphic across Node and browsers. */
|
|
4544
|
+
function utf8Decode(bytes) {
|
|
4545
|
+
return new TextDecoder().decode(bytes);
|
|
4546
|
+
}
|
|
4371
4547
|
//#endregion
|
|
4372
4548
|
//#region src/gen-tables.ts
|
|
4373
4549
|
/**
|
|
@@ -4518,6 +4694,7 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
|
|
|
4518
4694
|
let emuSlideTabH = EMU * 1;
|
|
4519
4695
|
let emuTabCurrH = 0;
|
|
4520
4696
|
let numCols = 0;
|
|
4697
|
+
let warnedNoTabH = false;
|
|
4521
4698
|
const tableRowSlides = [];
|
|
4522
4699
|
const tablePropX = getSmartParseNumber(tableProps.x, "X", presLayout);
|
|
4523
4700
|
const tablePropY = getSmartParseNumber(tableProps.y, "Y", presLayout);
|
|
@@ -4537,6 +4714,15 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
|
|
|
4537
4714
|
if (emuSlideTabH < tablePropH) emuSlideTabH = tablePropH;
|
|
4538
4715
|
}
|
|
4539
4716
|
}
|
|
4717
|
+
if (emuSlideTabH <= 0) {
|
|
4718
|
+
const emuStartY = tableRowSlides.length === 0 ? tablePropY || inch2Emu(arrInchMargins[0]) : inch2Emu(tableProps.autoPageSlideStartY || tableProps.newSlideStartY || arrInchMargins[0]);
|
|
4719
|
+
const fallbackH = presLayout.height - emuStartY - inch2Emu(arrInchMargins[2]);
|
|
4720
|
+
if (!warnedNoTabH) {
|
|
4721
|
+
console.warn("addTable/autoPage: the table height (`h`) leaves no room to paginate; ignoring it and using the slide height. Increase `h` or decrease `y`.");
|
|
4722
|
+
warnedNoTabH = true;
|
|
4723
|
+
}
|
|
4724
|
+
emuSlideTabH = fallbackH > 0 ? fallbackH : presLayout.height;
|
|
4725
|
+
}
|
|
4540
4726
|
}
|
|
4541
4727
|
if (tableProps.verbose) {
|
|
4542
4728
|
console.log("[[VERBOSE MODE]]");
|
|
@@ -4709,7 +4895,7 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
|
|
|
4709
4895
|
console.log("|-----------------------------------------------------------------------|\n\n");
|
|
4710
4896
|
}
|
|
4711
4897
|
if (currTableRow.length > 0 && currTableRow.map((cell) => Array.isArray(cell.text) ? cell.text.length : 0).reduce((p, n) => p + n) > 0) newTableRowSlide.rows.push(currTableRow);
|
|
4712
|
-
tableRowSlides.push(newTableRowSlide);
|
|
4898
|
+
if (newTableRowSlide.rows.length > 0) tableRowSlides.push(newTableRowSlide);
|
|
4713
4899
|
newTableRowSlide = { rows: [] };
|
|
4714
4900
|
currTableRow = [];
|
|
4715
4901
|
row.forEach((cell) => currTableRow.push({
|
|
@@ -4758,7 +4944,7 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
|
|
|
4758
4944
|
for (let c = 0; c < numCols; c++) if (colSpanDepths[c] > 0) colSpanDepths[c]--;
|
|
4759
4945
|
if (tableProps.verbose) console.log(`- SLIDE [${tableRowSlides.length}]: ROW [${iRow}]: ...COMPLETE ...... emuTabCurrH = ${(emuTabCurrH / EMU).toFixed(2)} ( emuSlideTabH = ${(emuSlideTabH / EMU).toFixed(2)} )`);
|
|
4760
4946
|
});
|
|
4761
|
-
tableRowSlides.push(newTableRowSlide);
|
|
4947
|
+
if (newTableRowSlide.rows.length > 0 || tableRowSlides.length === 0) tableRowSlides.push(newTableRowSlide);
|
|
4762
4948
|
if (tableProps.verbose) {
|
|
4763
4949
|
console.log("\n|================================================|");
|
|
4764
4950
|
console.log(`| FINAL: tableRowSlides.length = ${tableRowSlides.length}`);
|
|
@@ -4768,6 +4954,47 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
|
|
|
4768
4954
|
return tableRowSlides;
|
|
4769
4955
|
}
|
|
4770
4956
|
/**
|
|
4957
|
+
* Convert a computed CSS border (width string + color string) from `getComputedStyle` into a
|
|
4958
|
+
* pptx `BorderProps`.
|
|
4959
|
+
*
|
|
4960
|
+
* Preserves *fractional* widths: a hairline CSS border such as `0.5px` must not be rounded to
|
|
4961
|
+
* `0pt` and silently vanish — the table serializer (`valToPts`) emits fractional points just
|
|
4962
|
+
* fine, so there is no reason to integer-round here (upstream gitbrent/PptxGenJS#1235). A
|
|
4963
|
+
* computed width of `0` (or a non-finite value) yields `{ type: 'none' }` so we never emit a
|
|
4964
|
+
* zero-width line.
|
|
4965
|
+
* @param {string} widthStr - computed `border-<side>-width`, e.g. `"0.5px"`
|
|
4966
|
+
* @param {string} colorStr - computed `border-<side>-color`, e.g. `"rgb(102, 102, 102)"`
|
|
4967
|
+
* @returns {BorderProps} border props for the cell side
|
|
4968
|
+
*/
|
|
4969
|
+
function htmlBorderToProps(widthStr, colorStr) {
|
|
4970
|
+
const pt = Number(String(widthStr).replace("px", ""));
|
|
4971
|
+
if (!isFinite(pt) || pt <= 0) return { type: "none" };
|
|
4972
|
+
const arrRGB = String(colorStr).replace(/\s+/gi, "").replace("rgba(", "").replace("rgb(", "").replace(")", "").split(",");
|
|
4973
|
+
return {
|
|
4974
|
+
pt,
|
|
4975
|
+
color: rgbToHex(Number(arrRGB[0]), Number(arrRGB[1]), Number(arrRGB[2]))
|
|
4976
|
+
};
|
|
4977
|
+
}
|
|
4978
|
+
/**
|
|
4979
|
+
* Resolve a single HTML-table column width for `tableToSlides`.
|
|
4980
|
+
*
|
|
4981
|
+
* Precedence: an explicit `data-pptx-width` wins outright; otherwise the proportional width
|
|
4982
|
+
* derived from the live table is used, raised to `data-pptx-min-width` when that floor is larger.
|
|
4983
|
+
*
|
|
4984
|
+
* Hidden tables report `offsetWidth` 0 for every cell, which makes `calcWidth` non-finite (a 0/0
|
|
4985
|
+
* proportional calc). Fall back to `0` there so an explicit `data-pptx-width` / `data-pptx-min-width`
|
|
4986
|
+
* override still drives the column instead of emitting a `NaN` width (upstream gitbrent/PptxGenJS#1157).
|
|
4987
|
+
* @param {number} calcWidth - proportional width derived from `offsetWidth` (may be `NaN` for hidden tables)
|
|
4988
|
+
* @param {number} setWidth - `data-pptx-width` override (`0`/`NaN` when absent or invalid)
|
|
4989
|
+
* @param {number} minWidth - `data-pptx-min-width` floor (`0`/`NaN` when absent or invalid)
|
|
4990
|
+
* @returns {number} resolved column width
|
|
4991
|
+
*/
|
|
4992
|
+
function resolveHtmlColWidth(calcWidth, setWidth, minWidth) {
|
|
4993
|
+
const safeCalc = isFinite(calcWidth) ? calcWidth : 0;
|
|
4994
|
+
if (isFinite(setWidth) && setWidth > 0) return setWidth;
|
|
4995
|
+
return isFinite(minWidth) && minWidth > safeCalc ? minWidth : safeCalc;
|
|
4996
|
+
}
|
|
4997
|
+
/**
|
|
4771
4998
|
* Reproduces an HTML table as a PowerPoint table - including column widths, style, etc. - creates 1 or more slides as needed
|
|
4772
4999
|
* @param {TableToSlidesHost} pptx - pptxgenjs instance
|
|
4773
5000
|
* @param {string} tabEleId - HTMLElementID of the table
|
|
@@ -4831,12 +5058,10 @@ function genTableToSlides(pptx, tabEleId, options = {}, masterSlide) {
|
|
|
4831
5058
|
});
|
|
4832
5059
|
arrTabColW.forEach((colW, idxW) => {
|
|
4833
5060
|
const intCalcWidth = Number((Number(emuSlideTabW) * (colW / intTabW * 100) / 100 / EMU).toFixed(2));
|
|
4834
|
-
|
|
4835
|
-
const
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
if (colSelectorSet) intMinWidth = Number(colSelectorSet.getAttribute("data-pptx-width"));
|
|
4839
|
-
arrColW.push(intMinWidth > intCalcWidth ? intMinWidth : intCalcWidth);
|
|
5061
|
+
const headCell = document.querySelector(`#${tabEleId} thead tr:first-child th:nth-child(${idxW + 1})`);
|
|
5062
|
+
const intSetWidth = headCell ? Number(headCell.getAttribute("data-pptx-width")) : 0;
|
|
5063
|
+
const intMinWidth = headCell ? Number(headCell.getAttribute("data-pptx-min-width")) : 0;
|
|
5064
|
+
arrColW.push(resolveHtmlColWidth(intCalcWidth, intSetWidth, intMinWidth));
|
|
4840
5065
|
});
|
|
4841
5066
|
if (opts.verbose) console.log(`| arrColW ......................................... = [${arrColW.join(", ")}]`);
|
|
4842
5067
|
[
|
|
@@ -4915,12 +5140,8 @@ function genTableToSlides(pptx, tabEleId, options = {}, masterSlide) {
|
|
|
4915
5140
|
"bottom",
|
|
4916
5141
|
"left"
|
|
4917
5142
|
].forEach((val, idxb) => {
|
|
4918
|
-
const
|
|
4919
|
-
|
|
4920
|
-
cellBorder[idxb] = {
|
|
4921
|
-
pt: intBorderW,
|
|
4922
|
-
color: rgbToHex(Number(arrRGB[0]), Number(arrRGB[1]), Number(arrRGB[2]))
|
|
4923
|
-
};
|
|
5143
|
+
const style = window.getComputedStyle(cell);
|
|
5144
|
+
cellBorder[idxb] = htmlBorderToProps(style.getPropertyValue("border-" + val + "-width"), style.getPropertyValue("border-" + val + "-color"));
|
|
4924
5145
|
});
|
|
4925
5146
|
cellOpts.border = cellBorder;
|
|
4926
5147
|
}
|
|
@@ -4990,6 +5211,8 @@ function genTableToSlides(pptx, tabEleId, options = {}, masterSlide) {
|
|
|
4990
5211
|
*/
|
|
4991
5212
|
/** counter for included charts (used for index in their filenames) */
|
|
4992
5213
|
let _chartCounter = 0;
|
|
5214
|
+
/** DPI PowerPoint assumes when sizing an inserted raster image (natural pixels / 96 == inches) */
|
|
5215
|
+
const IMAGE_NATURAL_DPI = 96;
|
|
4993
5216
|
function normalizeBorderTuple(border) {
|
|
4994
5217
|
return Array.isArray(border) ? border : [
|
|
4995
5218
|
border,
|
|
@@ -5012,7 +5235,7 @@ function createSlideMaster(props, target) {
|
|
|
5012
5235
|
else if ("line" in object) addShapeDefinition(tgt, "line", object.line);
|
|
5013
5236
|
else if ("rect" in object) addShapeDefinition(tgt, "rect", object.rect);
|
|
5014
5237
|
else if ("roundRect" in object) addShapeDefinition(tgt, "roundRect", object.roundRect);
|
|
5015
|
-
else if ("text" in object) addTextDefinition(tgt, [{ text: object.text.text }], object.text.options || {}, false);
|
|
5238
|
+
else if ("text" in object) addTextDefinition(tgt, Array.isArray(object.text.text) ? object.text.text : [{ text: object.text.text }], object.text.options || {}, false);
|
|
5016
5239
|
else if ("placeholder" in object) {
|
|
5017
5240
|
const placeholder = object.placeholder;
|
|
5018
5241
|
const { name, type, ...rawPlaceholderOptions } = placeholder.options;
|
|
@@ -5026,6 +5249,26 @@ function createSlideMaster(props, target) {
|
|
|
5026
5249
|
if (props.slideNumber && typeof props.slideNumber === "object") target._slideNumberProps = props.slideNumber;
|
|
5027
5250
|
}
|
|
5028
5251
|
/**
|
|
5252
|
+
* Round and clamp an integer chart percentage/angle option into a schema-valid range.
|
|
5253
|
+
*
|
|
5254
|
+
* Several chart attributes are bounded integer types whose out-of-range values make
|
|
5255
|
+
* PowerPoint report the package as needing repair: `<c:overlap>` (ST_Overlap, -100..100),
|
|
5256
|
+
* `<c:gapWidth>`/`<c:gapDepth>` (ST_GapAmount, 0..500), `<c:holeSize>` (ST_HoleSize, 10..90)
|
|
5257
|
+
* and `<c:firstSliceAng>` (ST_FirstSliceAng, 0..360). Missing/non-numeric input returns
|
|
5258
|
+
* `undefined` so the caller can apply its own default; an out-of-range value is clamped
|
|
5259
|
+
* and a warning is emitted (per the library's warn-rather-than-degrade policy).
|
|
5260
|
+
* @param value - caller-supplied option value
|
|
5261
|
+
* @param min - inclusive lower bound
|
|
5262
|
+
* @param max - inclusive upper bound
|
|
5263
|
+
* @param name - option name, for the warning message
|
|
5264
|
+
*/
|
|
5265
|
+
function clampChartPct(value, min, max, name) {
|
|
5266
|
+
if (typeof value !== "number" || isNaN(value)) return void 0;
|
|
5267
|
+
const clamped = Math.min(max, Math.max(min, Math.round(value)));
|
|
5268
|
+
if (clamped !== value) console.warn(`Warning: ${name} ${value} is outside the valid range ${min}-${max}; using ${clamped}.`);
|
|
5269
|
+
return clamped;
|
|
5270
|
+
}
|
|
5271
|
+
/**
|
|
5029
5272
|
* Generate the chart based on input data.
|
|
5030
5273
|
* OOXML Chart Spec: ISO/IEC 29500-1:2016(E)
|
|
5031
5274
|
*
|
|
@@ -5197,7 +5440,13 @@ function addChartDefinition(target, type, data, opt) {
|
|
|
5197
5440
|
"marker",
|
|
5198
5441
|
"filled"
|
|
5199
5442
|
].includes(options.radarStyle || "")) options.radarStyle = "standard";
|
|
5200
|
-
|
|
5443
|
+
{
|
|
5444
|
+
const rawSymbolSize = options.lineDataSymbolSize;
|
|
5445
|
+
const hasSymbolSize = rawSymbolSize != null && !isNaN(rawSymbolSize);
|
|
5446
|
+
const symbolSize = Math.min(72, Math.max(2, Math.round(hasSymbolSize ? rawSymbolSize : 6)));
|
|
5447
|
+
if (hasSymbolSize && symbolSize !== rawSymbolSize) console.warn(`Warning: lineDataSymbolSize ${rawSymbolSize} is outside the valid marker size range (integer 2-72); using ${symbolSize}.`);
|
|
5448
|
+
options.lineDataSymbolSize = symbolSize;
|
|
5449
|
+
}
|
|
5201
5450
|
options.lineDataSymbolLineSize = options.lineDataSymbolLineSize && !isNaN(options.lineDataSymbolLineSize) ? valToPts(options.lineDataSymbolLineSize) : valToPts(.75);
|
|
5202
5451
|
const chartLayout = options.layout;
|
|
5203
5452
|
if (chartLayout) [
|
|
@@ -5247,8 +5496,11 @@ function addChartDefinition(target, type, data, opt) {
|
|
|
5247
5496
|
options.v3DRotY = typeof options.v3DRotY === "number" && !isNaN(options.v3DRotY) && options.v3DRotY >= 0 && options.v3DRotY <= 360 ? options.v3DRotY : 30;
|
|
5248
5497
|
options.v3DRAngAx = options.v3DRAngAx || !options.v3DRAngAx ? options.v3DRAngAx : true;
|
|
5249
5498
|
options.v3DPerspective = typeof options.v3DPerspective === "number" && !isNaN(options.v3DPerspective) && options.v3DPerspective >= 0 && options.v3DPerspective <= 240 ? options.v3DPerspective : 30;
|
|
5250
|
-
options.barGapWidthPct =
|
|
5251
|
-
options.barGapDepthPct =
|
|
5499
|
+
options.barGapWidthPct = clampChartPct(options.barGapWidthPct, 0, 500, "barGapWidthPct") ?? 150;
|
|
5500
|
+
options.barGapDepthPct = clampChartPct(options.barGapDepthPct, 0, 500, "barGapDepthPct") ?? 150;
|
|
5501
|
+
options.barOverlapPct = clampChartPct(options.barOverlapPct, -100, 100, "barOverlapPct");
|
|
5502
|
+
options.holeSize = clampChartPct(options.holeSize, 10, 90, "holeSize");
|
|
5503
|
+
options.firstSliceAng = clampChartPct(options.firstSliceAng, 0, 360, "firstSliceAng");
|
|
5252
5504
|
options.chartColors = Array.isArray(options.chartColors) ? options.chartColors : options._type === "pie" || options._type === "doughnut" ? PIECHART_COLORS : BARCHART_COLORS;
|
|
5253
5505
|
options.chartColorsOpacity = options.chartColorsOpacity && !isNaN(options.chartColorsOpacity) ? options.chartColorsOpacity : void 0;
|
|
5254
5506
|
options.border = options.border && typeof options.border === "object" ? options.border : void 0;
|
|
@@ -5337,16 +5589,29 @@ function addImageDefinition(target, opt) {
|
|
|
5337
5589
|
else if (strImageData?.toLowerCase().includes("image/svg+xml")) strImgExtn = "svg";
|
|
5338
5590
|
newObject._type = "image";
|
|
5339
5591
|
newObject.image = strImagePath || "preencoded.png";
|
|
5592
|
+
let defWidth = intWidth;
|
|
5593
|
+
let defHeight = intHeight;
|
|
5594
|
+
if ((!intWidth || !intHeight) && strImageData && strImgExtn !== "svg") {
|
|
5595
|
+
const natural = getImageSizeFromBase64(strImageData);
|
|
5596
|
+
if (natural) {
|
|
5597
|
+
if (!intWidth && !intHeight) {
|
|
5598
|
+
defWidth = natural.w / IMAGE_NATURAL_DPI;
|
|
5599
|
+
defHeight = natural.h / IMAGE_NATURAL_DPI;
|
|
5600
|
+
} else if (typeof intWidth === "number" && intWidth && !intHeight) defHeight = intWidth * (natural.h / natural.w);
|
|
5601
|
+
else if (typeof intHeight === "number" && intHeight && !intWidth) defWidth = intHeight * (natural.w / natural.h);
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5340
5604
|
const objectOptions = {
|
|
5341
5605
|
x: intPosX || 0,
|
|
5342
5606
|
y: intPosY || 0,
|
|
5343
|
-
w:
|
|
5344
|
-
h:
|
|
5607
|
+
w: defWidth || 1,
|
|
5608
|
+
h: defHeight || 1,
|
|
5345
5609
|
altText: opt.altText || "",
|
|
5346
5610
|
rounding: typeof opt.rounding === "boolean" ? opt.rounding : false,
|
|
5347
5611
|
shape: opt.shape,
|
|
5348
5612
|
points: opt.points,
|
|
5349
5613
|
rectRadius: opt.rectRadius,
|
|
5614
|
+
shapeAdjust: opt.shapeAdjust,
|
|
5350
5615
|
sizing,
|
|
5351
5616
|
placeholder: opt.placeholder,
|
|
5352
5617
|
rotate: opt.rotate || 0,
|
|
@@ -5355,6 +5620,7 @@ function addImageDefinition(target, opt) {
|
|
|
5355
5620
|
transparency: opt.transparency || 0,
|
|
5356
5621
|
duotone: opt.duotone,
|
|
5357
5622
|
objectName,
|
|
5623
|
+
objectLock: opt.objectLock,
|
|
5358
5624
|
shadow: correctShadowOptions(opt.shadow)
|
|
5359
5625
|
};
|
|
5360
5626
|
newObject.options = objectOptions;
|
|
@@ -5384,7 +5650,10 @@ function addImageDefinition(target, opt) {
|
|
|
5384
5650
|
});
|
|
5385
5651
|
newObject.imageRid = imageRelId + 1;
|
|
5386
5652
|
} else {
|
|
5387
|
-
const dupeItem = target._relsMedia.find((item) =>
|
|
5653
|
+
const dupeItem = target._relsMedia.find((item) => {
|
|
5654
|
+
if (item.isDuplicate || !item.Target || item.type !== "image/" + strImgExtn) return false;
|
|
5655
|
+
return strImagePath ? item.path === strImagePath : !!strImageData && item.data === strImageData;
|
|
5656
|
+
});
|
|
5388
5657
|
target._relsMedia.push({
|
|
5389
5658
|
path: strImagePath || "preencoded." + strImgExtn,
|
|
5390
5659
|
type: "image/" + strImgExtn,
|
|
@@ -5442,6 +5711,7 @@ function addMediaDefinition(target, opt) {
|
|
|
5442
5711
|
slideData.options.h = intSizeY;
|
|
5443
5712
|
slideData.options.objectName = objectName;
|
|
5444
5713
|
if (opt.altText) slideData.options.altText = opt.altText;
|
|
5714
|
+
if (opt.objectLock) slideData.options.objectLock = opt.objectLock;
|
|
5445
5715
|
/**
|
|
5446
5716
|
* NOTE:
|
|
5447
5717
|
* - rId starts at 2 (hence the intRels+1 below) as slideLayout.xml is rId=1!
|
|
@@ -5471,7 +5741,10 @@ function addMediaDefinition(target, opt) {
|
|
|
5471
5741
|
Target: `../media/image-${target._slideNum}-${target._relsMedia.length + 1}.png`
|
|
5472
5742
|
});
|
|
5473
5743
|
} else {
|
|
5474
|
-
const dupeItem = target._relsMedia.find((item) =>
|
|
5744
|
+
const dupeItem = target._relsMedia.find((item) => {
|
|
5745
|
+
if (item.isDuplicate || !item.Target || item.type !== strType + "/" + strExtn) return false;
|
|
5746
|
+
return strPath ? item.path === strPath : !!strData && item.data === strData;
|
|
5747
|
+
});
|
|
5475
5748
|
const relId1 = getNewRelId(target);
|
|
5476
5749
|
target._relsMedia.push({
|
|
5477
5750
|
path: strPath || "preencoded" + strExtn,
|
|
@@ -5536,12 +5809,14 @@ function addShapeDefinition(target, shapeName, opts) {
|
|
|
5536
5809
|
const options = typeof opts === "object" ? opts : {};
|
|
5537
5810
|
options.line = options.line || { type: "none" };
|
|
5538
5811
|
options.shadow = correctShadowOptions(options.shadow);
|
|
5812
|
+
const resolvedShapeName = typeof shapeName === "string" && SHAPE_NAME_ALIASES[shapeName] ? SHAPE_NAME_ALIASES[shapeName] : shapeName;
|
|
5539
5813
|
const newObject = {
|
|
5540
5814
|
_type: "text",
|
|
5541
|
-
shape:
|
|
5815
|
+
shape: resolvedShapeName || "rect",
|
|
5542
5816
|
options
|
|
5543
5817
|
};
|
|
5544
5818
|
if (!shapeName) throw new Error("Missing/Invalid shape parameter! Example: `addShape(pptxgen.shapes.LINE, {x:1, y:1, w:1, h:1});`");
|
|
5819
|
+
if (!VALID_SHAPE_PRESETS.has(resolvedShapeName)) throw new Error(`Invalid shape "${String(shapeName)}"! Use a value from \`pptxgen.shapes.*\` (e.g. \`pptxgen.shapes.RECTANGLE\`). PowerPoint can't render unknown preset geometries and will drop the shape during repair.`);
|
|
5545
5820
|
const newLineOpts = {
|
|
5546
5821
|
type: options.line.type || "solid",
|
|
5547
5822
|
color: options.line.color || "333333",
|
|
@@ -5570,6 +5845,51 @@ function addShapeDefinition(target, shapeName, opts) {
|
|
|
5570
5845
|
target._slideObjects.push(newObject);
|
|
5571
5846
|
}
|
|
5572
5847
|
/**
|
|
5848
|
+
* Adds a connector object to a slide definition.
|
|
5849
|
+
* A connector is a line between two points emitted as a PowerPoint connector (`<p:cxnSp>`).
|
|
5850
|
+
* Endpoints are converted to a bounding box (`x/y/w/h`) plus `flipH`/`flipV` so the box can be
|
|
5851
|
+
* oriented from any corner; the connector preset geometry is derived from `type`.
|
|
5852
|
+
* @param {PresSlideInternal} target - slide the connector is added to
|
|
5853
|
+
* @param {ConnectorProps} opts - connector options (endpoints + line styling)
|
|
5854
|
+
*/
|
|
5855
|
+
function addConnectorDefinition(target, opts) {
|
|
5856
|
+
if (!opts || [
|
|
5857
|
+
opts.x1,
|
|
5858
|
+
opts.y1,
|
|
5859
|
+
opts.x2,
|
|
5860
|
+
opts.y2
|
|
5861
|
+
].some((v) => typeof v === "undefined")) throw new Error("addConnector requires { x1, y1, x2, y2 }. Example: `slide.addConnector({ x1:1, y1:1, x2:4, y2:3 })`");
|
|
5862
|
+
const preset = CONNECTOR_PRESETS[opts.type || "straight"];
|
|
5863
|
+
if (!preset) throw new Error(`Invalid connector type "${String(opts.type)}". Use 'straight', 'elbow', or 'curved'.`);
|
|
5864
|
+
const x1 = getSmartParseNumber(opts.x1, "X", target._presLayout) / EMU;
|
|
5865
|
+
const y1 = getSmartParseNumber(opts.y1, "Y", target._presLayout) / EMU;
|
|
5866
|
+
const x2 = getSmartParseNumber(opts.x2, "X", target._presLayout) / EMU;
|
|
5867
|
+
const y2 = getSmartParseNumber(opts.y2, "Y", target._presLayout) / EMU;
|
|
5868
|
+
const newObject = {
|
|
5869
|
+
_type: "connector",
|
|
5870
|
+
shape: preset,
|
|
5871
|
+
options: {
|
|
5872
|
+
x: Math.min(x1, x2),
|
|
5873
|
+
y: Math.min(y1, y2),
|
|
5874
|
+
w: Math.abs(x2 - x1),
|
|
5875
|
+
h: Math.abs(y2 - y1),
|
|
5876
|
+
flipH: x2 < x1,
|
|
5877
|
+
flipV: y2 < y1,
|
|
5878
|
+
line: {
|
|
5879
|
+
type: "solid",
|
|
5880
|
+
color: opts.color || "333333",
|
|
5881
|
+
width: typeof opts.width === "number" ? opts.width : 1,
|
|
5882
|
+
dashType: opts.dashType || "solid",
|
|
5883
|
+
beginArrowType: opts.beginArrowType,
|
|
5884
|
+
endArrowType: opts.endArrowType
|
|
5885
|
+
},
|
|
5886
|
+
altText: opts.altText,
|
|
5887
|
+
objectName: opts.objectName ? encodeXmlEntities(validateObjectName(opts.objectName, "connector")) : `Connector ${target._slideObjects.filter((obj) => obj._type === "connector").length}`
|
|
5888
|
+
}
|
|
5889
|
+
};
|
|
5890
|
+
target._slideObjects.push(newObject);
|
|
5891
|
+
}
|
|
5892
|
+
/**
|
|
5573
5893
|
* Adds a table object to a slide definition.
|
|
5574
5894
|
* @param {PresSlideInternal} target - slide object that the table should be added to
|
|
5575
5895
|
* @param {TableRow[]} tableRows - table data
|
|
@@ -5638,9 +5958,8 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
|
|
|
5638
5958
|
}
|
|
5639
5959
|
arrRows.push(newRow);
|
|
5640
5960
|
});
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
if (opt.h) opt.h = getSmartParseNumber(opt.h, "Y", presLayout);
|
|
5961
|
+
if (opt.x === void 0 || opt.x === null) opt.x = .5;
|
|
5962
|
+
if (opt.y === void 0 || opt.y === null) opt.y = .5;
|
|
5644
5963
|
opt.fontSize = opt.fontSize || 12;
|
|
5645
5964
|
opt.margin = opt.margin === 0 || opt.margin ? opt.margin : DEF_CELL_MARGIN_IN;
|
|
5646
5965
|
if (typeof opt.margin === "number") opt.margin = [
|
|
@@ -5672,6 +5991,7 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
|
|
|
5672
5991
|
});
|
|
5673
5992
|
}
|
|
5674
5993
|
opt.autoPage = typeof opt.autoPage === "boolean" ? opt.autoPage : false;
|
|
5994
|
+
opt.autoPagePlaceholder = typeof opt.autoPagePlaceholder === "boolean" ? opt.autoPagePlaceholder : false;
|
|
5675
5995
|
opt.autoPageRepeatHeader = typeof opt.autoPageRepeatHeader === "boolean" ? opt.autoPageRepeatHeader : false;
|
|
5676
5996
|
opt.autoPageHeaderRows = typeof opt.autoPageHeaderRows !== "undefined" && !isNaN(Number(opt.autoPageHeaderRows)) ? Number(opt.autoPageHeaderRows) : 1;
|
|
5677
5997
|
opt.autoPageLineWeight = typeof opt.autoPageLineWeight !== "undefined" && !isNaN(Number(opt.autoPageLineWeight)) ? Number(opt.autoPageLineWeight) : 0;
|
|
@@ -5709,12 +6029,7 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
|
|
|
5709
6029
|
console.warn("addTable: mismatch: (colW.length != data.length) Therefore, defaulting to evenly distributed col widths.");
|
|
5710
6030
|
opt.colW = void 0;
|
|
5711
6031
|
}
|
|
5712
|
-
} else if (opt.w) opt.w =
|
|
5713
|
-
else opt.w = Math.floor((presLayout._sizeW || presLayout.width) / EMU - arrTableMargin[1] - arrTableMargin[3]);
|
|
5714
|
-
if (opt.x && opt.x < 20) opt.x = inch2Emu(opt.x);
|
|
5715
|
-
if (opt.y && opt.y < 20) opt.y = inch2Emu(opt.y);
|
|
5716
|
-
if (opt.w && typeof opt.w === "number" && opt.w < 20) opt.w = inch2Emu(opt.w);
|
|
5717
|
-
if (opt.h && typeof opt.h === "number" && opt.h < 20) opt.h = inch2Emu(opt.h);
|
|
6032
|
+
} else if (opt.w) {} else opt.w = Math.floor((presLayout._sizeW || presLayout.width) / EMU - arrTableMargin[1] - arrTableMargin[3]);
|
|
5718
6033
|
arrRows.forEach((row) => {
|
|
5719
6034
|
row.forEach((cell, idy) => {
|
|
5720
6035
|
if (typeof cell === "number" || typeof cell === "string") row[idy] = {
|
|
@@ -5740,12 +6055,14 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
|
|
|
5740
6055
|
});
|
|
5741
6056
|
} else {
|
|
5742
6057
|
if (opt.autoPageRepeatHeader) opt._arrObjTabHeadRows = arrRows.filter((_row, idx) => idx < (opt.autoPageHeaderRows || 1));
|
|
6058
|
+
const sourcePlaceholders = opt.autoPagePlaceholder && Array.isArray(target._slideObjects) ? target._slideObjects.filter((obj) => obj._type !== "table" && obj.options?.placeholder) : [];
|
|
5743
6059
|
getSlidesForTableRows(arrRows, opt, presLayout, slideLayout).forEach((slide, idx) => {
|
|
5744
6060
|
if (!getSlide(target._slideNum + idx)) slides.push(addSlide({ masterName: slideLayout?._name || void 0 }));
|
|
5745
|
-
if (idx > 0) opt.y =
|
|
6061
|
+
if (idx > 0) opt.y = opt.autoPageSlideStartY || opt.newSlideStartY || arrTableMargin[0];
|
|
5746
6062
|
{
|
|
5747
6063
|
const newSlide = getSlide(target._slideNum + idx);
|
|
5748
6064
|
opt.autoPage = false;
|
|
6065
|
+
if (idx > 0 && sourcePlaceholders.length > 0) sourcePlaceholders.forEach((ph) => newSlide._slideObjects.push(structuredClone(ph)));
|
|
5749
6066
|
createHyperlinkRels(newSlide, slide.rows);
|
|
5750
6067
|
newSlide.addTable(slide.rows, { ...opt });
|
|
5751
6068
|
if (idx > 0) newAutoPagedSlides.push(newSlide);
|
|
@@ -5814,6 +6131,10 @@ function addTextDefinition(target, text, opts, isPlaceholder) {
|
|
|
5814
6131
|
itemOpts._bodyProp.anchor = !itemOpts.placeholder ? "ctr" : void 0;
|
|
5815
6132
|
itemOpts._bodyProp.vert = itemOpts.vert;
|
|
5816
6133
|
itemOpts._bodyProp.wrap = typeof itemOpts.wrap === "boolean" ? itemOpts.wrap : true;
|
|
6134
|
+
if (itemOpts.columns !== void 0) if (typeof itemOpts.columns !== "number" || isNaN(itemOpts.columns) || itemOpts.columns < 1 || itemOpts.columns > 16) console.warn("Warning: text `columns` must be a number 1-16 (ignoring value)");
|
|
6135
|
+
else itemOpts._bodyProp.numCol = Math.round(itemOpts.columns);
|
|
6136
|
+
if (itemOpts.columnSpacing !== void 0) if (typeof itemOpts.columnSpacing !== "number" || isNaN(itemOpts.columnSpacing) || itemOpts.columnSpacing < 0) console.warn("Warning: text `columnSpacing` must be a number >= 0 (ignoring value)");
|
|
6137
|
+
else itemOpts._bodyProp.spcCol = valToPts(itemOpts.columnSpacing);
|
|
5817
6138
|
if (itemOpts.inset && !isNaN(Number(itemOpts.inset)) || itemOpts.inset === 0) {
|
|
5818
6139
|
itemOpts._bodyProp.lIns = inch2Emu(itemOpts.inset);
|
|
5819
6140
|
itemOpts._bodyProp.rIns = inch2Emu(itemOpts.inset);
|
|
@@ -6094,6 +6415,16 @@ var Slide = class {
|
|
|
6094
6415
|
return this;
|
|
6095
6416
|
}
|
|
6096
6417
|
/**
|
|
6418
|
+
* Add a connector (a line drawn between two points, emitted as a PowerPoint `<p:cxnSp>`).
|
|
6419
|
+
* @param {ConnectorProps} options - connector endpoints (`x1,y1,x2,y2`) and line styling
|
|
6420
|
+
* @return {Slide} this Slide
|
|
6421
|
+
* @example slide.addConnector({ type: 'elbow', x1: 1, y1: 1, x2: 5, y2: 3, endArrowType: 'triangle' })
|
|
6422
|
+
*/
|
|
6423
|
+
addConnector(options) {
|
|
6424
|
+
addConnectorDefinition(this, options);
|
|
6425
|
+
return this;
|
|
6426
|
+
}
|
|
6427
|
+
/**
|
|
6097
6428
|
* Add table to Slide
|
|
6098
6429
|
* @param {TableRow[]} tableRows - table rows
|
|
6099
6430
|
* @param {TableProps} options - table options
|
|
@@ -6323,6 +6654,22 @@ async function createExcelWorksheet(chartObject, zip) {
|
|
|
6323
6654
|
});
|
|
6324
6655
|
}
|
|
6325
6656
|
/**
|
|
6657
|
+
* Emit the `<a:latin>/<a:ea>/<a:cs>` font trio for a chart text run.
|
|
6658
|
+
*
|
|
6659
|
+
* In DrawingML run properties a typeface applies only to the script class of
|
|
6660
|
+
* its element: `<a:latin>` covers Latin/ASCII, `<a:ea>` covers East Asian, and
|
|
6661
|
+
* `<a:cs>` covers complex scripts. Emitting `<a:latin>` alone leaves East Asian
|
|
6662
|
+
* (e.g. Chinese) and complex-script glyphs falling back to the theme font, so a
|
|
6663
|
+
* user-specified font never takes effect for that text — most visibly on
|
|
6664
|
+
* PowerPoint for Mac. Stamping the same typeface onto all three classes is what
|
|
6665
|
+
* choosing a font in PowerPoint's UI does (upstream gitbrent/PptxGenJS#1420).
|
|
6666
|
+
* @param {string} typeface - font face name
|
|
6667
|
+
* @return {string} `<a:latin/><a:ea/><a:cs/>` XML
|
|
6668
|
+
*/
|
|
6669
|
+
function createChartTextFonts(typeface) {
|
|
6670
|
+
return `<a:latin typeface="${typeface}"/><a:ea typeface="${typeface}"/><a:cs typeface="${typeface}"/>`;
|
|
6671
|
+
}
|
|
6672
|
+
/**
|
|
6326
6673
|
* Main entry point method for create charts
|
|
6327
6674
|
* @see: http://www.datypic.com/sc/ooxml/s-dml-chart.xsd.html
|
|
6328
6675
|
* @param {ISlideRelChart} rel - chart object
|
|
@@ -6332,6 +6679,10 @@ function makeXmlCharts(rel) {
|
|
|
6332
6679
|
let strXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
|
|
6333
6680
|
let usesSecondaryValAxis = false;
|
|
6334
6681
|
let usesSecondaryCatAxis = false;
|
|
6682
|
+
let primaryCatAxisValType = null;
|
|
6683
|
+
let secondaryCatAxisValType = null;
|
|
6684
|
+
let primaryCatAxisHasCategoryChart = false;
|
|
6685
|
+
let secondaryCatAxisHasCategoryChart = false;
|
|
6335
6686
|
strXml += "<c:chartSpace xmlns:c=\"http://schemas.openxmlformats.org/drawingml/2006/chart\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">";
|
|
6336
6687
|
strXml += "<c:date1904 val=\"0\"/>";
|
|
6337
6688
|
strXml += `<c:roundedCorners val="${rel.opts.chartArea.roundedCorners ? "1" : "0"}"/>`;
|
|
@@ -6344,6 +6695,8 @@ function makeXmlCharts(rel) {
|
|
|
6344
6695
|
fontSize: rel.opts.titleFontSize || 18,
|
|
6345
6696
|
titleAlign: rel.opts.titleAlign,
|
|
6346
6697
|
titleBold: rel.opts.titleBold,
|
|
6698
|
+
titleItalic: rel.opts.titleItalic,
|
|
6699
|
+
titleUnderline: rel.opts.titleUnderline,
|
|
6347
6700
|
titlePos: rel.opts.titlePos,
|
|
6348
6701
|
titleRotate: rel.opts.titleRotate
|
|
6349
6702
|
}, rel.opts.x, rel.opts.y);
|
|
@@ -6376,18 +6729,37 @@ function makeXmlCharts(rel) {
|
|
|
6376
6729
|
const catAxisId = options.secondaryCatAxis ? AXIS_ID_CATEGORY_SECONDARY : AXIS_ID_CATEGORY_PRIMARY;
|
|
6377
6730
|
usesSecondaryValAxis = usesSecondaryValAxis || options.secondaryValAxis;
|
|
6378
6731
|
usesSecondaryCatAxis = usesSecondaryCatAxis || options.secondaryCatAxis;
|
|
6732
|
+
const usesValueXAxis = type.type === "scatter" || type.type === "bubble" || type.type === "bubble3D";
|
|
6733
|
+
if (options.secondaryCatAxis) if (usesValueXAxis) secondaryCatAxisValType = type.type;
|
|
6734
|
+
else secondaryCatAxisHasCategoryChart = true;
|
|
6735
|
+
else if (usesValueXAxis) primaryCatAxisValType = type.type;
|
|
6736
|
+
else primaryCatAxisHasCategoryChart = true;
|
|
6379
6737
|
strXml += makeChartType(type.type, type.data, options, valAxisId, catAxisId);
|
|
6380
6738
|
});
|
|
6381
6739
|
else strXml += makeChartType(rel.opts._type, rel.data, rel.opts, AXIS_ID_VALUE_PRIMARY, AXIS_ID_CATEGORY_PRIMARY);
|
|
6382
6740
|
if (rel.opts._type !== "pie" && rel.opts._type !== "doughnut") {
|
|
6383
6741
|
if (rel.opts.valAxes && rel.opts.valAxes.length > 1 && !usesSecondaryValAxis) throw new Error("Secondary axis must be used by one of the multiple charts");
|
|
6742
|
+
const comboCatAxisType = (isSecondary) => {
|
|
6743
|
+
const valType = isSecondary ? secondaryCatAxisValType : primaryCatAxisValType;
|
|
6744
|
+
const hasCategoryChart = isSecondary ? secondaryCatAxisHasCategoryChart : primaryCatAxisHasCategoryChart;
|
|
6745
|
+
if (!valType) return {};
|
|
6746
|
+
if (hasCategoryChart) {
|
|
6747
|
+
console.warn(`A category-based chart and a scatter/bubble chart cannot share the same ${isSecondary ? "secondary" : "primary"} category axis; emitting a category axis. Put the scatter/bubble series on a separate axis.`);
|
|
6748
|
+
return {};
|
|
6749
|
+
}
|
|
6750
|
+
return { _type: valType };
|
|
6751
|
+
};
|
|
6384
6752
|
if (rel.opts.catAxes) {
|
|
6385
6753
|
if (!rel.opts.valAxes || rel.opts.valAxes.length !== rel.opts.catAxes.length) throw new Error("There must be the same number of value and category axes.");
|
|
6386
6754
|
strXml += makeCatAxis({
|
|
6387
6755
|
...rel.opts,
|
|
6388
|
-
...rel.opts.catAxes[0]
|
|
6756
|
+
...rel.opts.catAxes[0],
|
|
6757
|
+
...comboCatAxisType(false)
|
|
6389
6758
|
}, AXIS_ID_CATEGORY_PRIMARY, AXIS_ID_VALUE_PRIMARY);
|
|
6390
|
-
} else strXml += makeCatAxis(
|
|
6759
|
+
} else strXml += makeCatAxis({
|
|
6760
|
+
...rel.opts,
|
|
6761
|
+
...comboCatAxisType(false)
|
|
6762
|
+
}, AXIS_ID_CATEGORY_PRIMARY, AXIS_ID_VALUE_PRIMARY);
|
|
6391
6763
|
if (rel.opts.valAxes) {
|
|
6392
6764
|
strXml += makeValAxis({
|
|
6393
6765
|
...rel.opts,
|
|
@@ -6404,9 +6776,13 @@ function makeXmlCharts(rel) {
|
|
|
6404
6776
|
}
|
|
6405
6777
|
if (rel.opts?.catAxes && rel.opts?.catAxes[1]) strXml += makeCatAxis({
|
|
6406
6778
|
...rel.opts,
|
|
6407
|
-
...rel.opts.catAxes[1]
|
|
6779
|
+
...rel.opts.catAxes[1],
|
|
6780
|
+
...comboCatAxisType(true)
|
|
6781
|
+
}, AXIS_ID_CATEGORY_SECONDARY, AXIS_ID_VALUE_SECONDARY);
|
|
6782
|
+
else if (usesSecondaryCatAxis && (!rel.opts.catAxes || !rel.opts.catAxes[1])) strXml += makeCatAxis({
|
|
6783
|
+
...rel.opts,
|
|
6784
|
+
...comboCatAxisType(true)
|
|
6408
6785
|
}, AXIS_ID_CATEGORY_SECONDARY, AXIS_ID_VALUE_SECONDARY);
|
|
6409
|
-
else if (usesSecondaryCatAxis && (!rel.opts.catAxes || !rel.opts.catAxes[1])) strXml += makeCatAxis(rel.opts, AXIS_ID_CATEGORY_SECONDARY, AXIS_ID_VALUE_SECONDARY);
|
|
6410
6786
|
}
|
|
6411
6787
|
if (rel.opts.showDataTable) {
|
|
6412
6788
|
strXml += "<c:dTable>";
|
|
@@ -6461,8 +6837,7 @@ function makeXmlCharts(rel) {
|
|
|
6461
6837
|
strXml += " <a:pPr>";
|
|
6462
6838
|
strXml += rel.opts.legendFontSize ? `<a:defRPr sz="${Math.round(Number(rel.opts.legendFontSize) * 100)}">` : "<a:defRPr>";
|
|
6463
6839
|
if (rel.opts.legendColor) strXml += genXmlColorSelection(rel.opts.legendColor);
|
|
6464
|
-
if (rel.opts.legendFontFace) strXml +=
|
|
6465
|
-
if (rel.opts.legendFontFace) strXml += "<a:cs typeface=\"" + rel.opts.legendFontFace + "\"/>";
|
|
6840
|
+
if (rel.opts.legendFontFace) strXml += createChartTextFonts(rel.opts.legendFontFace);
|
|
6466
6841
|
strXml += " </a:defRPr>";
|
|
6467
6842
|
strXml += " </a:pPr>";
|
|
6468
6843
|
strXml += " <a:endParaRPr lang=\"en-US\"/>";
|
|
@@ -6500,6 +6875,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6500
6875
|
let idxColLtr = 1;
|
|
6501
6876
|
let optsChartData;
|
|
6502
6877
|
let strXml = "";
|
|
6878
|
+
const valFmtCode = encodeXmlEntities(opts.valLabelFormatCode || opts.dataTableFormatCode || opts.dataLabelFormatCode || "General");
|
|
6503
6879
|
switch (chartType) {
|
|
6504
6880
|
case "area":
|
|
6505
6881
|
case "bar":
|
|
@@ -6543,7 +6919,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6543
6919
|
} else if (opts.dataBorder) strXml += `<a:ln w="${valToPts(opts.dataBorder.pt)}" cap="${createLineCap(opts.lineCap)}"><a:solidFill>${createColorElement(opts.dataBorder.color)}</a:solidFill><a:prstDash val="solid"/><a:round/></a:ln>`;
|
|
6544
6920
|
strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
6545
6921
|
strXml += " </c:spPr>";
|
|
6546
|
-
if (chartType
|
|
6922
|
+
if (chartType === "bar" || chartType === "bar3D") strXml += " <c:invertIfNegative val=\"0\"/>";
|
|
6547
6923
|
if (chartType === "line" || chartType === "radar") {
|
|
6548
6924
|
strXml += "<c:marker>";
|
|
6549
6925
|
strXml += " <c:symbol val=\"" + opts.lineDataSymbol + "\"/>";
|
|
@@ -6558,6 +6934,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6558
6934
|
strXml += " </c:spPr>";
|
|
6559
6935
|
strXml += "</c:marker>";
|
|
6560
6936
|
}
|
|
6937
|
+
{
|
|
6938
|
+
const barVaryColors = (chartType === "bar" || chartType === "bar3D") && data.length === 1 && (opts.chartColors && opts.chartColors !== BARCHART_COLORS && opts.chartColors.length > 1 || opts.invertedColors?.length) ? opts.chartColors || BARCHART_COLORS : null;
|
|
6939
|
+
strXml += makeSeriesDataPointsXml(chartType, obj, opts, barVaryColors);
|
|
6940
|
+
}
|
|
6561
6941
|
if (chartType !== "radar") {
|
|
6562
6942
|
const lblColor = seriesOverride?.dataLabelColor ?? opts.dataLabelColor ?? "000000";
|
|
6563
6943
|
const lblBold = seriesOverride?.dataLabelFontBold ?? opts.dataLabelFontBold ?? false;
|
|
@@ -6565,12 +6945,15 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6565
6945
|
const lblSize = seriesOverride?.dataLabelFontSize ?? opts.dataLabelFontSize ?? 12;
|
|
6566
6946
|
const lblFace = seriesOverride?.dataLabelFontFace ?? opts.dataLabelFontFace ?? "Arial";
|
|
6567
6947
|
strXml += "<c:dLbls>";
|
|
6948
|
+
if (obj.customLabels?.length) obj.customLabels.forEach((lbl, idx) => {
|
|
6949
|
+
if (lbl) strXml += makeCustomDLblXml(idx, lbl, opts);
|
|
6950
|
+
});
|
|
6568
6951
|
strXml += `<c:numFmt formatCode="${encodeXmlEntities(opts.dataLabelFormatCode) || "General"}" sourceLinked="0"/>`;
|
|
6569
6952
|
if (opts.dataLabelBkgrdColors) strXml += `<c:spPr><a:solidFill>${createColorElement(seriesColor)}</a:solidFill></c:spPr>`;
|
|
6570
6953
|
strXml += "<c:txPr><a:bodyPr/><a:lstStyle/><a:p><a:pPr>";
|
|
6571
6954
|
strXml += `<a:defRPr b="${lblBold ? 1 : 0}" i="${lblItalic ? 1 : 0}" strike="noStrike" sz="${Math.round(lblSize * 100)}" u="none">`;
|
|
6572
6955
|
strXml += `<a:solidFill>${createColorElement(lblColor)}</a:solidFill>`;
|
|
6573
|
-
strXml +=
|
|
6956
|
+
strXml += createChartTextFonts(lblFace);
|
|
6574
6957
|
strXml += "</a:defRPr></a:pPr></a:p></c:txPr>";
|
|
6575
6958
|
if (opts.dataLabelPosition) strXml += `<c:dLblPos val="${opts.dataLabelPosition}"/>`;
|
|
6576
6959
|
strXml += "<c:showLegendKey val=\"0\"/>";
|
|
@@ -6579,29 +6962,6 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6579
6962
|
strXml += `<c:showLeaderLines val="${opts.showLeaderLines ? "1" : "0"}"/>`;
|
|
6580
6963
|
strXml += "</c:dLbls>";
|
|
6581
6964
|
}
|
|
6582
|
-
if ((chartType === "bar" || chartType === "bar3D") && data.length === 1 && (opts.chartColors && opts.chartColors !== BARCHART_COLORS && opts.chartColors.length > 1 || opts.invertedColors?.length)) obj.values.forEach((value, index) => {
|
|
6583
|
-
const arrColors = value < 0 ? opts.invertedColors || opts.chartColors || BARCHART_COLORS : opts.chartColors || [];
|
|
6584
|
-
strXml += " <c:dPt>";
|
|
6585
|
-
strXml += ` <c:idx val="${index}"/>`;
|
|
6586
|
-
strXml += " <c:invertIfNegative val=\"0\"/>";
|
|
6587
|
-
strXml += " <c:bubble3D val=\"0\"/>";
|
|
6588
|
-
strXml += " <c:spPr>";
|
|
6589
|
-
if (opts.lineSize === 0) strXml += "<a:ln><a:noFill/></a:ln>";
|
|
6590
|
-
else if (chartType === "bar") {
|
|
6591
|
-
strXml += "<a:solidFill>";
|
|
6592
|
-
strXml += " <a:srgbClr val=\"" + arrColors[index % arrColors.length] + "\"/>";
|
|
6593
|
-
strXml += "</a:solidFill>";
|
|
6594
|
-
} else {
|
|
6595
|
-
strXml += "<a:ln>";
|
|
6596
|
-
strXml += " <a:solidFill>";
|
|
6597
|
-
strXml += " <a:srgbClr val=\"" + arrColors[index % arrColors.length] + "\"/>";
|
|
6598
|
-
strXml += " </a:solidFill>";
|
|
6599
|
-
strXml += "</a:ln>";
|
|
6600
|
-
}
|
|
6601
|
-
strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
6602
|
-
strXml += " </c:spPr>";
|
|
6603
|
-
strXml += " </c:dPt>";
|
|
6604
|
-
});
|
|
6605
6965
|
strXml += "<c:cat>";
|
|
6606
6966
|
if (opts.catLabelFormatCode) {
|
|
6607
6967
|
strXml += " <c:numRef>";
|
|
@@ -6638,10 +6998,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6638
6998
|
strXml += " <c:numRef>";
|
|
6639
6999
|
strXml += `<c:f>Sheet1!$${getExcelColName(obj._dataIndex + obj.labels.length + 1)}$2:$${getExcelColName(obj._dataIndex + obj.labels.length + 1)}$${obj.labels[0].length + 1}</c:f>`;
|
|
6640
7000
|
strXml += " <c:numCache>";
|
|
6641
|
-
strXml += " <c:formatCode>" +
|
|
7001
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
6642
7002
|
strXml += ` <c:ptCount val="${obj.labels[0].length}"/>`;
|
|
6643
7003
|
obj.values.forEach((value, idx) => {
|
|
6644
|
-
|
|
7004
|
+
strXml += numCachePt(idx, value);
|
|
6645
7005
|
});
|
|
6646
7006
|
strXml += " </c:numCache>";
|
|
6647
7007
|
strXml += " </c:numRef>";
|
|
@@ -6657,7 +7017,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6657
7017
|
strXml += " <a:p><a:pPr>";
|
|
6658
7018
|
strXml += ` <a:defRPr b="${opts.dataLabelFontBold ? 1 : 0}" i="${opts.dataLabelFontItalic ? 1 : 0}" strike="noStrike" sz="${Math.round((opts.dataLabelFontSize || 12) * 100)}" u="none">`;
|
|
6659
7019
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
6660
|
-
strXml += "
|
|
7020
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
6661
7021
|
strXml += " </a:defRPr>";
|
|
6662
7022
|
strXml += " </a:pPr></a:p>";
|
|
6663
7023
|
strXml += " </c:txPr>";
|
|
@@ -6673,6 +7033,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6673
7033
|
if (chartType === "bar") {
|
|
6674
7034
|
strXml += ` <c:gapWidth val="${opts.barGapWidthPct}"/>`;
|
|
6675
7035
|
strXml += ` <c:overlap val="${opts.barOverlapPct != null ? opts.barOverlapPct : (opts.barGrouping || "").includes("tacked") ? 100 : 0}"/>`;
|
|
7036
|
+
strXml += createSerLinesElement(opts.barSeriesLine);
|
|
6676
7037
|
} else if (chartType === "bar3D") {
|
|
6677
7038
|
strXml += ` <c:gapWidth val="${opts.barGapWidthPct}"/>`;
|
|
6678
7039
|
strXml += ` <c:gapDepth val="${opts.barGapDepthPct}"/>`;
|
|
@@ -6724,6 +7085,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6724
7085
|
strXml += "<a:effectLst/>";
|
|
6725
7086
|
strXml += "</c:spPr>";
|
|
6726
7087
|
strXml += "</c:marker>";
|
|
7088
|
+
{
|
|
7089
|
+
const scatterVaryColors = data.length === 1 && opts.chartColors !== BARCHART_COLORS ? opts.chartColors || BARCHART_COLORS : null;
|
|
7090
|
+
strXml += makeSeriesDataPointsXml(chartType, obj, opts, scatterVaryColors);
|
|
7091
|
+
}
|
|
6727
7092
|
if (opts.showLabel) {
|
|
6728
7093
|
const chartUuid = getUuid("-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
|
|
6729
7094
|
if (obj.labels[0] && (opts.dataLabelFormatScatter === "custom" || opts.dataLabelFormatScatter === "customXY")) {
|
|
@@ -6742,13 +7107,13 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6742
7107
|
strXml += " <a:pPr>";
|
|
6743
7108
|
strXml += ` <a:defRPr sz="${Math.round((opts.dataLabelFontSize || 12) * 100)}" b="${opts.dataLabelFontBold ? "1" : "0"}" i="${opts.dataLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike">`;
|
|
6744
7109
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
6745
|
-
strXml +=
|
|
7110
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
6746
7111
|
strXml += " </a:defRPr>";
|
|
6747
7112
|
strXml += " </a:pPr>";
|
|
6748
7113
|
strXml += " <a:r>";
|
|
6749
7114
|
strXml += ` <a:rPr lang="${opts.lang || "en-US"}" sz="${Math.round((opts.dataLabelFontSize || 12) * 100)}" b="${opts.dataLabelFontBold ? "1" : "0"}" i="${opts.dataLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike" dirty="0">`;
|
|
6750
7115
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
6751
|
-
strXml +=
|
|
7116
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
6752
7117
|
strXml += " </a:rPr>";
|
|
6753
7118
|
strXml += " <a:t>" + encodeXmlEntities(label) + "</a:t>";
|
|
6754
7119
|
strXml += " </a:r>";
|
|
@@ -6828,7 +7193,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6828
7193
|
strXml += " <a:pPr>";
|
|
6829
7194
|
strXml += ` <a:defRPr sz="${Math.round((opts.dataLabelFontSize || 12) * 100)}" b="${opts.dataLabelFontBold ? "1" : "0"}" i="${opts.dataLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike">`;
|
|
6830
7195
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
6831
|
-
strXml +=
|
|
7196
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
6832
7197
|
strXml += " </a:defRPr>";
|
|
6833
7198
|
strXml += " </a:pPr>";
|
|
6834
7199
|
strXml += ` <a:endParaRPr lang="${opts.lang || "en-US"}"/>`;
|
|
@@ -6849,31 +7214,14 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6849
7214
|
strXml += "</c:dLbls>";
|
|
6850
7215
|
}
|
|
6851
7216
|
}
|
|
6852
|
-
if (data.length === 1 && opts.chartColors !== BARCHART_COLORS) obj.values.forEach((value, index) => {
|
|
6853
|
-
const arrColors = value < 0 ? opts.invertedColors || opts.chartColors || BARCHART_COLORS : opts.chartColors || [];
|
|
6854
|
-
strXml += " <c:dPt>";
|
|
6855
|
-
strXml += ` <c:idx val="${index}"/>`;
|
|
6856
|
-
strXml += " <c:invertIfNegative val=\"0\"/>";
|
|
6857
|
-
strXml += " <c:bubble3D val=\"0\"/>";
|
|
6858
|
-
strXml += " <c:spPr>";
|
|
6859
|
-
if (opts.lineSize === 0) strXml += "<a:ln><a:noFill/></a:ln>";
|
|
6860
|
-
else {
|
|
6861
|
-
strXml += "<a:solidFill>";
|
|
6862
|
-
strXml += " <a:srgbClr val=\"" + arrColors[index % arrColors.length] + "\"/>";
|
|
6863
|
-
strXml += "</a:solidFill>";
|
|
6864
|
-
}
|
|
6865
|
-
strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
6866
|
-
strXml += " </c:spPr>";
|
|
6867
|
-
strXml += " </c:dPt>";
|
|
6868
|
-
});
|
|
6869
7217
|
strXml += "<c:xVal>";
|
|
6870
7218
|
strXml += " <c:numRef>";
|
|
6871
7219
|
strXml += ` <c:f>Sheet1!$A$2:$A$${data[0].values.length + 1}</c:f>`;
|
|
6872
7220
|
strXml += " <c:numCache>";
|
|
6873
|
-
strXml += " <c:formatCode>
|
|
7221
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
6874
7222
|
strXml += ` <c:ptCount val="${data[0].values.length}"/>`;
|
|
6875
7223
|
data[0].values.forEach((value, idx) => {
|
|
6876
|
-
|
|
7224
|
+
strXml += numCachePt(idx, value);
|
|
6877
7225
|
});
|
|
6878
7226
|
strXml += " </c:numCache>";
|
|
6879
7227
|
strXml += " </c:numRef>";
|
|
@@ -6882,10 +7230,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6882
7230
|
strXml += " <c:numRef>";
|
|
6883
7231
|
strXml += ` <c:f>Sheet1!$${getExcelColName(idx + 2)}$2:$${getExcelColName(idx + 2)}$${data[0].values.length + 1}</c:f>`;
|
|
6884
7232
|
strXml += " <c:numCache>";
|
|
6885
|
-
strXml += " <c:formatCode>
|
|
7233
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
6886
7234
|
strXml += ` <c:ptCount val="${data[0].values.length}"/>`;
|
|
6887
7235
|
data[0].values.forEach((_value, idx) => {
|
|
6888
|
-
|
|
7236
|
+
strXml += numCachePt(idx, obj.values[idx]);
|
|
6889
7237
|
});
|
|
6890
7238
|
strXml += " </c:numCache>";
|
|
6891
7239
|
strXml += " </c:numRef>";
|
|
@@ -6901,7 +7249,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6901
7249
|
strXml += " <a:p><a:pPr>";
|
|
6902
7250
|
strXml += ` <a:defRPr b="${opts.dataLabelFontBold ? "1" : "0"}" i="${opts.dataLabelFontItalic ? "1" : "0"}" strike="noStrike" sz="${Math.round((opts.dataLabelFontSize || 12) * 100)}" u="none">`;
|
|
6903
7251
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
6904
|
-
strXml += "
|
|
7252
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
6905
7253
|
strXml += " </a:defRPr>";
|
|
6906
7254
|
strXml += " </a:pPr></a:p>";
|
|
6907
7255
|
strXml += " </c:txPr>";
|
|
@@ -6951,10 +7299,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6951
7299
|
strXml += " <c:numRef>";
|
|
6952
7300
|
strXml += ` <c:f>Sheet1!$A$2:$A$${data[0].values.length + 1}</c:f>`;
|
|
6953
7301
|
strXml += " <c:numCache>";
|
|
6954
|
-
strXml += " <c:formatCode>
|
|
7302
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
6955
7303
|
strXml += ` <c:ptCount val="${data[0].values.length}"/>`;
|
|
6956
7304
|
data[0].values.forEach((value, idx) => {
|
|
6957
|
-
strXml +=
|
|
7305
|
+
strXml += numCachePt(idx, value);
|
|
6958
7306
|
});
|
|
6959
7307
|
strXml += " </c:numCache>";
|
|
6960
7308
|
strXml += " </c:numRef>";
|
|
@@ -6964,10 +7312,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6964
7312
|
strXml += `<c:f>Sheet1!$${getExcelColName(idxColLtr + 1)}$2:$${getExcelColName(idxColLtr + 1)}$${data[0].values.length + 1}</c:f>`;
|
|
6965
7313
|
idxColLtr++;
|
|
6966
7314
|
strXml += " <c:numCache>";
|
|
6967
|
-
strXml += " <c:formatCode>
|
|
7315
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
6968
7316
|
strXml += ` <c:ptCount val="${data[0].values.length}"/>`;
|
|
6969
7317
|
data[0].values.forEach((_value, idx) => {
|
|
6970
|
-
strXml +=
|
|
7318
|
+
strXml += numCachePt(idx, obj.values[idx]);
|
|
6971
7319
|
});
|
|
6972
7320
|
strXml += " </c:numCache>";
|
|
6973
7321
|
strXml += " </c:numRef>";
|
|
@@ -6980,7 +7328,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6980
7328
|
strXml += " <c:formatCode>General</c:formatCode>";
|
|
6981
7329
|
strXml += ` <c:ptCount val="${obj.sizes.length}"/>`;
|
|
6982
7330
|
obj.sizes.forEach((value, idx) => {
|
|
6983
|
-
strXml +=
|
|
7331
|
+
strXml += numCachePt(idx, value);
|
|
6984
7332
|
});
|
|
6985
7333
|
strXml += " </c:numCache>";
|
|
6986
7334
|
strXml += " </c:numRef>";
|
|
@@ -6993,12 +7341,12 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
6993
7341
|
strXml += "<c:txPr><a:bodyPr/><a:lstStyle/><a:p><a:pPr>";
|
|
6994
7342
|
strXml += `<a:defRPr b="${opts.dataLabelFontBold ? 1 : 0}" i="${opts.dataLabelFontItalic ? 1 : 0}" strike="noStrike" sz="${Math.round(Math.round(opts.dataLabelFontSize || 12) * 100)}" u="none">`;
|
|
6995
7343
|
strXml += `<a:solidFill>${createColorElement(opts.dataLabelColor || "000000")}</a:solidFill>`;
|
|
6996
|
-
strXml +=
|
|
7344
|
+
strXml += createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
6997
7345
|
strXml += "</a:defRPr></a:pPr></a:p></c:txPr>";
|
|
6998
7346
|
if (opts.dataLabelPosition) strXml += `<c:dLblPos val="${opts.dataLabelPosition}"/>`;
|
|
6999
7347
|
strXml += "<c:showLegendKey val=\"0\"/>";
|
|
7000
7348
|
strXml += `<c:showVal val="${opts.showValue ? "1" : "0"}"/>`;
|
|
7001
|
-
strXml += `<c:showCatName val="0"/><c:showSerName val="${opts.showSerName ? "1" : "0"}"/><c:showPercent val="0"/><c:showBubbleSize val="0"/>`;
|
|
7349
|
+
strXml += `<c:showCatName val="0"/><c:showSerName val="${opts.showSerName ? "1" : "0"}"/><c:showPercent val="0"/><c:showBubbleSize val="${opts.showBubbleSize ? "1" : "0"}"/>`;
|
|
7002
7350
|
strXml += "<c:extLst>";
|
|
7003
7351
|
strXml += " <c:ext uri=\"{CE6537A1-D6FC-4f65-9D91-7224C49458BB}\" xmlns:c15=\"http://schemas.microsoft.com/office/drawing/2012/chart\">";
|
|
7004
7352
|
strXml += " <c15:showLeaderLines val=\"" + (opts.showLeaderLines ? "1" : "0") + "\"/>";
|
|
@@ -7032,33 +7380,37 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
7032
7380
|
else strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
7033
7381
|
strXml += " </c:spPr>";
|
|
7034
7382
|
optsChartData.labels[0].forEach((_label, idx) => {
|
|
7383
|
+
const ptStyle = optsChartData.pointStyles?.[idx];
|
|
7035
7384
|
strXml += "<c:dPt>";
|
|
7036
7385
|
strXml += ` <c:idx val="${idx}"/>`;
|
|
7037
7386
|
strXml += " <c:bubble3D val=\"0\"/>";
|
|
7038
7387
|
strXml += " <c:spPr>";
|
|
7039
|
-
strXml += `<a:solidFill>${createColorElement(opts.chartColors[idx + 1 > opts.chartColors.length ? Math.floor(Math.random() * opts.chartColors.length) : idx])}</a:solidFill>`;
|
|
7040
|
-
if (
|
|
7388
|
+
strXml += `<a:solidFill>${createColorElement(ptStyle?.fill || opts.chartColors[idx + 1 > opts.chartColors.length ? Math.floor(Math.random() * opts.chartColors.length) : idx])}</a:solidFill>`;
|
|
7389
|
+
if (ptStyle?.border) strXml += createChartBorderLine(ptStyle.border);
|
|
7390
|
+
else if (opts.dataBorder) strXml += `<a:ln w="${valToPts(opts.dataBorder.pt)}" cap="flat"><a:solidFill>${createColorElement(opts.dataBorder.color)}</a:solidFill><a:prstDash val="solid"/><a:round/></a:ln>`;
|
|
7041
7391
|
strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
7042
7392
|
strXml += " </c:spPr>";
|
|
7043
7393
|
strXml += "</c:dPt>";
|
|
7044
7394
|
});
|
|
7045
7395
|
strXml += "<c:dLbls>";
|
|
7046
7396
|
optsChartData.labels[0].forEach((_label, idx) => {
|
|
7397
|
+
const customLbl = optsChartData.customLabels?.[idx];
|
|
7047
7398
|
strXml += "<c:dLbl>";
|
|
7048
7399
|
strXml += ` <c:idx val="${idx}"/>`;
|
|
7400
|
+
if (customLbl) strXml += `<c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:r><a:rPr lang="${opts.lang || "en-US"}" dirty="0"/><a:t>${encodeXmlEntities(customLbl)}</a:t></a:r></a:p></c:rich></c:tx>`;
|
|
7049
7401
|
strXml += ` <c:numFmt formatCode="${encodeXmlEntities(opts.dataLabelFormatCode) || "General"}" sourceLinked="0"/>`;
|
|
7050
7402
|
strXml += " <c:spPr/><c:txPr>";
|
|
7051
7403
|
strXml += " <a:bodyPr/><a:lstStyle/>";
|
|
7052
7404
|
strXml += " <a:p><a:pPr>";
|
|
7053
7405
|
strXml += ` <a:defRPr sz="${Math.round((opts.dataLabelFontSize || 12) * 100)}" b="${opts.dataLabelFontBold ? 1 : 0}" i="${opts.dataLabelFontItalic ? 1 : 0}" u="none" strike="noStrike">`;
|
|
7054
7406
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
7055
|
-
strXml +=
|
|
7407
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
7056
7408
|
strXml += " </a:defRPr>";
|
|
7057
7409
|
strXml += " </a:pPr></a:p>";
|
|
7058
7410
|
strXml += " </c:txPr>";
|
|
7059
7411
|
if (chartType === "pie" && opts.dataLabelPosition) strXml += `<c:dLblPos val="${opts.dataLabelPosition}"/>`;
|
|
7060
7412
|
strXml += " <c:showLegendKey val=\"0\"/>";
|
|
7061
|
-
strXml += " <c:showVal val=\"" + (opts.showValue ? "1" : "0") + "\"/>";
|
|
7413
|
+
strXml += " <c:showVal val=\"" + (customLbl ? "0" : opts.showValue ? "1" : "0") + "\"/>";
|
|
7062
7414
|
strXml += " <c:showCatName val=\"" + (opts.showLabel ? "1" : "0") + "\"/>";
|
|
7063
7415
|
strXml += " <c:showSerName val=\"" + (opts.showSerName ? "1" : "0") + "\"/>";
|
|
7064
7416
|
strXml += " <c:showPercent val=\"" + (opts.showPercent ? "1" : "0") + "\"/>";
|
|
@@ -7073,7 +7425,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
7073
7425
|
strXml += " <a:pPr>";
|
|
7074
7426
|
strXml += ` <a:defRPr sz="${Math.round((opts.dataLabelFontSize || 12) * 100)}" b="${opts.dataLabelFontBold ? "1" : "0"}" i="${opts.dataLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike">`;
|
|
7075
7427
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
7076
|
-
strXml +=
|
|
7428
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
7077
7429
|
strXml += " </a:defRPr>";
|
|
7078
7430
|
strXml += " </a:pPr>";
|
|
7079
7431
|
strXml += " </a:p>";
|
|
@@ -7086,6 +7438,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
7086
7438
|
strXml += " <c:showPercent val=\"1\"/>";
|
|
7087
7439
|
strXml += " <c:showBubbleSize val=\"0\"/>";
|
|
7088
7440
|
strXml += ` <c:showLeaderLines val="${opts.showLeaderLines ? "1" : "0"}"/>`;
|
|
7441
|
+
strXml += createLeaderLinesElement(opts);
|
|
7089
7442
|
strXml += "</c:dLbls>";
|
|
7090
7443
|
strXml += "<c:cat>";
|
|
7091
7444
|
strXml += " <c:strRef>";
|
|
@@ -7102,6 +7455,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
7102
7455
|
strXml += " <c:numRef>";
|
|
7103
7456
|
strXml += ` <c:f>Sheet1!$B$2:$B$${optsChartData.labels[0].length + 1}</c:f>`;
|
|
7104
7457
|
strXml += " <c:numCache>";
|
|
7458
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
7105
7459
|
strXml += ` <c:ptCount val="${optsChartData.labels[0].length}"/>`;
|
|
7106
7460
|
optsChartData.values.forEach((value, idx) => {
|
|
7107
7461
|
strXml += `<c:pt idx="${idx}"><c:v>${value || value === 0 ? value : ""}</c:v></c:pt>`;
|
|
@@ -7177,7 +7531,7 @@ function makeCatAxis(opts, axisId, valAxisId) {
|
|
|
7177
7531
|
strXml += " <a:pPr>";
|
|
7178
7532
|
strXml += ` <a:defRPr sz="${Math.round((opts.catAxisLabelFontSize || 12) * 100)}" b="${opts.catAxisLabelFontBold ? 1 : 0}" i="${opts.catAxisLabelFontItalic ? 1 : 0}" u="none" strike="noStrike">`;
|
|
7179
7533
|
strXml += " <a:solidFill>" + createColorElement(opts.catAxisLabelColor || "000000") + "</a:solidFill>";
|
|
7180
|
-
strXml += "
|
|
7534
|
+
strXml += " " + createChartTextFonts(opts.catAxisLabelFontFace || "Arial");
|
|
7181
7535
|
strXml += " </a:defRPr>";
|
|
7182
7536
|
strXml += " </a:pPr>";
|
|
7183
7537
|
strXml += " <a:endParaRPr lang=\"" + (opts.lang || "en-US") + "\"/>";
|
|
@@ -7270,7 +7624,7 @@ function makeValAxis(opts, valAxisId) {
|
|
|
7270
7624
|
strXml += " <a:pPr>";
|
|
7271
7625
|
strXml += ` <a:defRPr sz="${Math.round((opts.valAxisLabelFontSize || 12) * 100)}" b="${opts.valAxisLabelFontBold ? 1 : 0}" i="${opts.valAxisLabelFontItalic ? 1 : 0}" u="none" strike="noStrike">`;
|
|
7272
7626
|
strXml += " <a:solidFill>" + createColorElement(opts.valAxisLabelColor || "000000") + "</a:solidFill>";
|
|
7273
|
-
strXml += "
|
|
7627
|
+
strXml += " " + createChartTextFonts(opts.valAxisLabelFontFace || "Arial");
|
|
7274
7628
|
strXml += " </a:defRPr>";
|
|
7275
7629
|
strXml += " </a:pPr>";
|
|
7276
7630
|
strXml += " <a:endParaRPr lang=\"" + (opts.lang || "en-US") + "\"/>";
|
|
@@ -7326,7 +7680,7 @@ function makeSerAxis(opts, axisId, valAxisId) {
|
|
|
7326
7680
|
strXml += " <a:pPr>";
|
|
7327
7681
|
strXml += ` <a:defRPr sz="${Math.round((opts.serAxisLabelFontSize || 12) * 100)}" b="${opts.serAxisLabelFontBold ? "1" : "0"}" i="${opts.serAxisLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike">`;
|
|
7328
7682
|
strXml += ` <a:solidFill>${createColorElement(opts.serAxisLabelColor || "000000")}</a:solidFill>`;
|
|
7329
|
-
strXml +=
|
|
7683
|
+
strXml += " " + createChartTextFonts(opts.serAxisLabelFontFace || "Arial");
|
|
7330
7684
|
strXml += " </a:defRPr>";
|
|
7331
7685
|
strXml += " </a:pPr>";
|
|
7332
7686
|
strXml += " <a:endParaRPr lang=\"" + (opts.lang || "en-US") + "\"/>";
|
|
@@ -7366,17 +7720,31 @@ function genXmlTitle(opts, chartX, chartY) {
|
|
|
7366
7720
|
const rotate = opts.titleRotate ? `<a:bodyPr rot="${convertRotationDegrees(opts.titleRotate)}"/>` : "<a:bodyPr/>";
|
|
7367
7721
|
const sizeAttr = opts.fontSize ? `sz="${Math.round(opts.fontSize * 100)}"` : "";
|
|
7368
7722
|
const titleBold = opts.titleBold ? 1 : 0;
|
|
7723
|
+
const titleItalic = opts.titleItalic ? 1 : 0;
|
|
7724
|
+
const titleUnderline = opts.titleUnderline ? "sng" : "none";
|
|
7369
7725
|
let layout = "<c:layout/>";
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
let
|
|
7374
|
-
|
|
7375
|
-
if (
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7726
|
+
const hasX = opts.titlePos && typeof opts.titlePos.x === "number";
|
|
7727
|
+
const hasY = opts.titlePos && typeof opts.titlePos.y === "number";
|
|
7728
|
+
if (hasX || hasY) {
|
|
7729
|
+
let modes = "";
|
|
7730
|
+
let vals = "";
|
|
7731
|
+
if (hasX) {
|
|
7732
|
+
const totalX = opts.titlePos.x + chartX;
|
|
7733
|
+
let valX = totalX === 0 ? 0 : totalX * (totalX / 5) / 10;
|
|
7734
|
+
if (valX >= 1) valX = valX / 10;
|
|
7735
|
+
if (valX >= .1) valX = valX / 10;
|
|
7736
|
+
modes += "<c:xMode val=\"edge\"/>";
|
|
7737
|
+
vals += `<c:x val="${valX}"/>`;
|
|
7738
|
+
}
|
|
7739
|
+
if (hasY) {
|
|
7740
|
+
const totalY = opts.titlePos.y + chartY;
|
|
7741
|
+
let valY = totalY === 0 ? 0 : totalY * (totalY / 5) / 10;
|
|
7742
|
+
if (valY >= 1) valY = valY / 10;
|
|
7743
|
+
if (valY >= .1) valY = valY / 10;
|
|
7744
|
+
modes += "<c:yMode val=\"edge\"/>";
|
|
7745
|
+
vals += `<c:y val="${valY}"/>`;
|
|
7746
|
+
}
|
|
7747
|
+
layout = `<c:layout><c:manualLayout>${modes}${vals}</c:manualLayout></c:layout>`;
|
|
7380
7748
|
}
|
|
7381
7749
|
return `<c:title>
|
|
7382
7750
|
<c:tx>
|
|
@@ -7385,15 +7753,15 @@ function genXmlTitle(opts, chartX, chartY) {
|
|
|
7385
7753
|
<a:lstStyle/>
|
|
7386
7754
|
<a:p>
|
|
7387
7755
|
${align}
|
|
7388
|
-
<a:defRPr ${sizeAttr} b="${titleBold}" i="
|
|
7756
|
+
<a:defRPr ${sizeAttr} b="${titleBold}" i="${titleItalic}" u="${titleUnderline}" strike="noStrike">
|
|
7389
7757
|
<a:solidFill>${createColorElement(opts.color || "000000")}</a:solidFill>
|
|
7390
|
-
|
|
7758
|
+
${createChartTextFonts(opts.fontFace || "Arial")}
|
|
7391
7759
|
</a:defRPr>
|
|
7392
7760
|
</a:pPr>
|
|
7393
7761
|
<a:r>
|
|
7394
|
-
<a:rPr ${sizeAttr} b="${titleBold}" i="
|
|
7762
|
+
<a:rPr ${sizeAttr} b="${titleBold}" i="${titleItalic}" u="${titleUnderline}" strike="noStrike">
|
|
7395
7763
|
<a:solidFill>${createColorElement(opts.color || "000000")}</a:solidFill>
|
|
7396
|
-
|
|
7764
|
+
${createChartTextFonts(opts.fontFace || "Arial")}
|
|
7397
7765
|
</a:rPr>
|
|
7398
7766
|
<a:t>${encodeXmlEntities(opts.title) || ""}</a:t>
|
|
7399
7767
|
</a:r>
|
|
@@ -7467,11 +7835,123 @@ function createGridLineElement(glOpts) {
|
|
|
7467
7835
|
strXml += "</c:majorGridlines>";
|
|
7468
7836
|
return strXml;
|
|
7469
7837
|
}
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7838
|
+
/**
|
|
7839
|
+
* Build a `<c:pt>` numeric-cache data point, or '' to leave a gap.
|
|
7840
|
+
*
|
|
7841
|
+
* `<c:v>` inside a `<c:numCache>` is an `xsd:double`; emitting `NaN`, `Infinity`
|
|
7842
|
+
* or an empty string yields an invalid value that makes PowerPoint report the
|
|
7843
|
+
* package as needing repair. Null/undefined are intentional gaps and are skipped
|
|
7844
|
+
* silently (a sparse, idx-keyed cache is valid); other non-finite numbers are
|
|
7845
|
+
* skipped with a warning, per the library's "warn rather than emit a degenerate
|
|
7846
|
+
* result" policy.
|
|
7847
|
+
* @param idx - zero-based data-point index (emitted as `idx`)
|
|
7848
|
+
* @param value - numeric value (or null/undefined gap)
|
|
7849
|
+
*/
|
|
7850
|
+
function numCachePt(idx, value) {
|
|
7851
|
+
if (value == null) return "";
|
|
7852
|
+
if (!Number.isFinite(value)) {
|
|
7853
|
+
console.warn(`Warning: chart value "${value}" at index ${idx} is not a finite number; data point omitted.`);
|
|
7854
|
+
return "";
|
|
7855
|
+
}
|
|
7856
|
+
return `<c:pt idx="${idx}"><c:v>${value}</c:v></c:pt>`;
|
|
7857
|
+
}
|
|
7858
|
+
/**
|
|
7859
|
+
* Build a `<c:serLines>` ("Series Lines") element for a bar chart.
|
|
7860
|
+
* @param opt - `true` for PowerPoint automatic styling, an {@link OptsChartGridLine}
|
|
7861
|
+
* to customize the line, or falsy / `{ style: 'none' }` to omit the element.
|
|
7862
|
+
*/
|
|
7863
|
+
function createSerLinesElement(opt) {
|
|
7864
|
+
if (!opt) return "";
|
|
7865
|
+
if (opt === true) return "<c:serLines/>";
|
|
7866
|
+
if (opt.style === "none") return "";
|
|
7867
|
+
let strXml = "<c:serLines><c:spPr>";
|
|
7868
|
+
strXml += `<a:ln w="${valToPts(opt.size || DEF_CHART_GRIDLINE.size)}" cap="${createLineCap(opt.cap || DEF_CHART_GRIDLINE.cap)}">`;
|
|
7869
|
+
strXml += `<a:solidFill><a:srgbClr val="${opt.color || DEF_CHART_GRIDLINE.color}"/></a:solidFill>`;
|
|
7870
|
+
strXml += `<a:prstDash val="${opt.style || DEF_CHART_GRIDLINE.style}"/><a:round/>`;
|
|
7871
|
+
strXml += "</a:ln></c:spPr></c:serLines>";
|
|
7872
|
+
return strXml;
|
|
7873
|
+
}
|
|
7874
|
+
/**
|
|
7875
|
+
* Build the `<c:leaderLines>` element for pie/doughnut data labels.
|
|
7876
|
+
*
|
|
7877
|
+
* Schema position: inside `<c:dLbls>`, immediately after `<c:showLeaderLines>`
|
|
7878
|
+
* (CT_DLbls / Group_DLbls order: showLeaderLines → leaderLines).
|
|
7879
|
+
*
|
|
7880
|
+
* Returns `''` unless the caller both enabled leader lines (`showLeaderLines`)
|
|
7881
|
+
* and configured their appearance (`leaderLineColor` / `leaderLineSize`). When
|
|
7882
|
+
* appearance is unset we leave the element off so PowerPoint applies its
|
|
7883
|
+
* automatic leader-line color, matching prior behavior.
|
|
7884
|
+
*
|
|
7885
|
+
* @param opts - chart options (reads `showLeaderLines`, `leaderLineColor`, `leaderLineSize`)
|
|
7886
|
+
*/
|
|
7887
|
+
function createLeaderLinesElement(opts) {
|
|
7888
|
+
if (!opts.showLeaderLines) return "";
|
|
7889
|
+
if (!opts.leaderLineColor && opts.leaderLineSize == null) return "";
|
|
7890
|
+
return `<c:leaderLines><c:spPr><a:ln w="${valToPts(opts.leaderLineSize ?? .75)}" cap="flat"><a:solidFill>${createColorElement(opts.leaderLineColor || "808080")}</a:solidFill><a:prstDash val="solid"/><a:round/></a:ln><a:effectLst/></c:spPr></c:leaderLines>`;
|
|
7891
|
+
}
|
|
7892
|
+
function makeCustomDLblXml(idx, text, opts) {
|
|
7893
|
+
const sz = Math.round((opts.dataLabelFontSize || 12) * 100);
|
|
7894
|
+
const bold = opts.dataLabelFontBold ? "1" : "0";
|
|
7895
|
+
const italic = opts.dataLabelFontItalic ? "1" : "0";
|
|
7896
|
+
const color = createColorElement(opts.dataLabelColor || "000000");
|
|
7897
|
+
const face = opts.dataLabelFontFace || "Arial";
|
|
7898
|
+
const lang = opts.lang || "en-US";
|
|
7899
|
+
return `<c:dLbl><c:idx val="${idx}"/><c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:pPr><a:defRPr sz="${sz}" b="${bold}" i="${italic}" u="none" strike="noStrike"><a:solidFill>${color}</a:solidFill>${createChartTextFonts(face)}</a:defRPr></a:pPr><a:r><a:rPr lang="${lang}" sz="${sz}" b="${bold}" i="${italic}" u="none" strike="noStrike" dirty="0"><a:solidFill>${color}</a:solidFill>${createChartTextFonts(face)}</a:rPr><a:t>${encodeXmlEntities(text)}</a:t></a:r></a:p></c:rich></c:tx><c:showLegendKey val="0"/><c:showVal val="0"/><c:showCatName val="0"/><c:showSerName val="0"/><c:showPercent val="0"/><c:showBubbleSize val="0"/></c:dLbl>`;
|
|
7900
|
+
}
|
|
7901
|
+
/**
|
|
7902
|
+
* Build an `<a:ln>` border element from a per-data-point `BorderProps`.
|
|
7903
|
+
* @param border - point border style (`type`, `color`, `pt`)
|
|
7904
|
+
*/
|
|
7905
|
+
function createChartBorderLine(border) {
|
|
7906
|
+
if (border.type === "none") return "<a:ln><a:noFill/></a:ln>";
|
|
7907
|
+
const dash = border.type === "dash" ? "dash" : "solid";
|
|
7908
|
+
return `<a:ln w="${valToPts(border.pt ?? 1)}" cap="flat"><a:solidFill>${createColorElement(border.color || "666666")}</a:solidFill><a:prstDash val="${dash}"/><a:round/></a:ln>`;
|
|
7909
|
+
}
|
|
7910
|
+
/**
|
|
7911
|
+
* Build `<c:dPt>` entries for a series in the bar/line/area/scatter loops.
|
|
7912
|
+
*
|
|
7913
|
+
* Merges two sources into a single `c:dPt` per index so we never emit a
|
|
7914
|
+
* duplicate `<c:idx>` (which corrupts the chart):
|
|
7915
|
+
* - legacy single-series color-vary fills (bar/scatter), supplied via `varyColors`
|
|
7916
|
+
* - per-point `pointStyles` border/fill overrides
|
|
7917
|
+
*
|
|
7918
|
+
* Must be emitted in schema position *before* `c:dLbls` (CT_*Ser order).
|
|
7919
|
+
* RADAR is skipped: extra per-point markup historically corrupts the chart.
|
|
7920
|
+
*
|
|
7921
|
+
* @param chartType - series chart type
|
|
7922
|
+
* @param obj - series data (reads `values`, `pointStyles`)
|
|
7923
|
+
* @param opts - chart options (fill/shadow/lineSize context)
|
|
7924
|
+
* @param varyColors - color array when single-series color-vary applies, else `null`
|
|
7925
|
+
*/
|
|
7926
|
+
function makeSeriesDataPointsXml(chartType, obj, opts, varyColors) {
|
|
7927
|
+
if (chartType === "radar") return "";
|
|
7928
|
+
const pointStyles = obj.pointStyles;
|
|
7929
|
+
if (!varyColors && !pointStyles?.length) return "";
|
|
7930
|
+
const isBar = chartType === "bar" || chartType === "bar3D";
|
|
7931
|
+
const isScatter = chartType === "scatter";
|
|
7932
|
+
let xml = "";
|
|
7933
|
+
obj.values.forEach((value, index) => {
|
|
7934
|
+
const ptStyle = pointStyles?.[index];
|
|
7935
|
+
const arrColors = varyColors ? value < 0 ? opts.invertedColors || opts.chartColors || BARCHART_COLORS : varyColors : null;
|
|
7936
|
+
const fillColor = ptStyle?.fill || (arrColors ? arrColors[index % arrColors.length] : null);
|
|
7937
|
+
const border = ptStyle?.border;
|
|
7938
|
+
if (!fillColor && !border) return;
|
|
7939
|
+
xml += "<c:dPt>";
|
|
7940
|
+
xml += `<c:idx val="${index}"/>`;
|
|
7941
|
+
if (isBar) xml += "<c:invertIfNegative val=\"0\"/>";
|
|
7942
|
+
xml += "<c:bubble3D val=\"0\"/>";
|
|
7943
|
+
xml += "<c:spPr>";
|
|
7944
|
+
if ((isBar || isScatter) && opts.lineSize === 0 && !border && !ptStyle?.fill) xml += "<a:ln><a:noFill/></a:ln>";
|
|
7945
|
+
else {
|
|
7946
|
+
if (fillColor) if (chartType === "bar3D") xml += `<a:ln><a:solidFill>${createColorElement(fillColor)}</a:solidFill></a:ln>`;
|
|
7947
|
+
else xml += `<a:solidFill>${createColorElement(fillColor)}</a:solidFill>`;
|
|
7948
|
+
if (border) xml += createChartBorderLine(border);
|
|
7949
|
+
}
|
|
7950
|
+
xml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
7951
|
+
xml += "</c:spPr>";
|
|
7952
|
+
xml += "</c:dPt>";
|
|
7953
|
+
});
|
|
7954
|
+
return xml;
|
|
7475
7955
|
}
|
|
7476
7956
|
//#endregion
|
|
7477
7957
|
//#region src/gen-media.ts
|
|
@@ -7484,9 +7964,11 @@ function hasEncodingPath(rel) {
|
|
|
7484
7964
|
/**
|
|
7485
7965
|
* Encode Image/Audio/Video into base64
|
|
7486
7966
|
* @param {PresSlideInternal | SlideLayoutInternal} layout - slide layout
|
|
7967
|
+
* @param {RuntimeAdapter} runtime - runtime adapter (Node/browser media loader)
|
|
7968
|
+
* @param {'throw' | 'placeholder'} onMediaError - failure policy: reject the export (default) or substitute a placeholder and warn
|
|
7487
7969
|
* @return {Promise} promise
|
|
7488
7970
|
*/
|
|
7489
|
-
function encodeSlideMediaRels(layout, runtime) {
|
|
7971
|
+
function encodeSlideMediaRels(layout, runtime, onMediaError = "throw") {
|
|
7490
7972
|
const imageProms = [];
|
|
7491
7973
|
const candidateRels = layout._relsMedia.filter((rel) => rel.type !== "online" && !rel.data && hasEncodingPath(rel));
|
|
7492
7974
|
const unqPaths = [];
|
|
@@ -7504,9 +7986,13 @@ function encodeSlideMediaRels(layout, runtime) {
|
|
|
7504
7986
|
if (rel.isSvgPng) await runtime.createSvgPngPreview(rel);
|
|
7505
7987
|
return "done";
|
|
7506
7988
|
} catch (ex) {
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7989
|
+
if (onMediaError === "placeholder") {
|
|
7990
|
+
console.warn(`[WARNING] Failed to load media "${rel.path}"; embedding a broken-image placeholder. (${String(ex)})`);
|
|
7991
|
+
rel.data = IMG_BROKEN;
|
|
7992
|
+
candidateRels.filter((dupe) => dupe.isDuplicate && dupe.path === rel.path).forEach((dupe) => dupe.data = rel.data);
|
|
7993
|
+
return "done";
|
|
7994
|
+
}
|
|
7995
|
+
throw new Error(`Failed to load media "${rel.path}" during export.`, { cause: ex });
|
|
7510
7996
|
}
|
|
7511
7997
|
})());
|
|
7512
7998
|
});
|
|
@@ -7520,6 +8006,37 @@ function encodeSlideMediaRels(layout, runtime) {
|
|
|
7520
8006
|
/**
|
|
7521
8007
|
* PptxGenJS: XML Generation
|
|
7522
8008
|
*/
|
|
8009
|
+
const _warnedTextRangeMsgs = /* @__PURE__ */ new Set();
|
|
8010
|
+
function warnTextRangeOnce(msg) {
|
|
8011
|
+
if (_warnedTextRangeMsgs.has(msg)) return;
|
|
8012
|
+
_warnedTextRangeMsgs.add(msg);
|
|
8013
|
+
console.warn(msg);
|
|
8014
|
+
}
|
|
8015
|
+
/**
|
|
8016
|
+
* Clamp a font size (points) into ST_TextFontSize (1-4000pt) and return it in
|
|
8017
|
+
* hundredths of a point for the `sz` attribute. Out-of-range sizes make
|
|
8018
|
+
* PowerPoint report the package as needing repair (e.g. `sz` > 400000 or < 100).
|
|
8019
|
+
*/
|
|
8020
|
+
function clampFontSizeSz(fontSizePts) {
|
|
8021
|
+
const raw = Math.round(fontSizePts * 100);
|
|
8022
|
+
const clamped = Math.min(4e5, Math.max(100, raw));
|
|
8023
|
+
if (clamped !== raw) warnTextRangeOnce(`Warning: fontSize ${fontSizePts} is outside the valid range 1-4000pt; using ${clamped / 100}.`);
|
|
8024
|
+
return clamped;
|
|
8025
|
+
}
|
|
8026
|
+
/** Clamp character spacing (points) into ST_TextPoint (-4000..4000pt); returns hundredths for the `spc` attribute. */
|
|
8027
|
+
function clampCharSpacingSpc(charSpacingPts) {
|
|
8028
|
+
const raw = Math.round(charSpacingPts * 100);
|
|
8029
|
+
const clamped = Math.min(4e5, Math.max(-4e5, raw));
|
|
8030
|
+
if (clamped !== raw) warnTextRangeOnce(`Warning: charSpacing ${charSpacingPts} is outside the valid range -4000..4000pt; using ${clamped / 100}.`);
|
|
8031
|
+
return clamped;
|
|
8032
|
+
}
|
|
8033
|
+
/** Clamp line spacing (points) into ST_TextSpacingPoint (0..1584pt); returns hundredths for `<a:spcPts val>`. */
|
|
8034
|
+
function clampLineSpacingPts(lineSpacingPts) {
|
|
8035
|
+
const raw = Math.round(lineSpacingPts * 100);
|
|
8036
|
+
const clamped = Math.min(158400, Math.max(0, raw));
|
|
8037
|
+
if (clamped !== raw) warnTextRangeOnce(`Warning: lineSpacing ${lineSpacingPts} is outside the valid range 0-1584pt; using ${clamped / 100}.`);
|
|
8038
|
+
return clamped;
|
|
8039
|
+
}
|
|
7523
8040
|
const ImageSizingXml = {
|
|
7524
8041
|
cover: function(imgSize, boxDim) {
|
|
7525
8042
|
const imgRatio = imgSize.h / imgSize.w;
|
|
@@ -7566,23 +8083,90 @@ const ImageSizingXml = {
|
|
|
7566
8083
|
* @return {string} `<a:prstGeom>` XML
|
|
7567
8084
|
*/
|
|
7568
8085
|
const RECT_RADIUS_ADJ1_SHAPES = new Set(["round2SameRect", "round2DiagRect"]);
|
|
8086
|
+
const SHAPE_LOCK_ATTRS = [
|
|
8087
|
+
"noGrp",
|
|
8088
|
+
"noSelect",
|
|
8089
|
+
"noRot",
|
|
8090
|
+
"noChangeAspect",
|
|
8091
|
+
"noMove",
|
|
8092
|
+
"noResize",
|
|
8093
|
+
"noEditPoints",
|
|
8094
|
+
"noAdjustHandles",
|
|
8095
|
+
"noChangeArrowheads",
|
|
8096
|
+
"noChangeShapeType",
|
|
8097
|
+
"noTextEdit"
|
|
8098
|
+
];
|
|
8099
|
+
const PICTURE_LOCK_ATTRS = [
|
|
8100
|
+
"noGrp",
|
|
8101
|
+
"noSelect",
|
|
8102
|
+
"noRot",
|
|
8103
|
+
"noChangeAspect",
|
|
8104
|
+
"noMove",
|
|
8105
|
+
"noResize",
|
|
8106
|
+
"noEditPoints",
|
|
8107
|
+
"noAdjustHandles",
|
|
8108
|
+
"noChangeArrowheads",
|
|
8109
|
+
"noChangeShapeType",
|
|
8110
|
+
"noCrop"
|
|
8111
|
+
];
|
|
8112
|
+
const GRAPHIC_FRAME_LOCK_ATTRS = [
|
|
8113
|
+
"noGrp",
|
|
8114
|
+
"noDrilldown",
|
|
8115
|
+
"noSelect",
|
|
8116
|
+
"noChangeAspect",
|
|
8117
|
+
"noMove",
|
|
8118
|
+
"noResize"
|
|
8119
|
+
];
|
|
8120
|
+
/**
|
|
8121
|
+
* Serialize an object-lock element (`a:spLocks` / `a:picLocks` / `a:graphicFrameLocks`).
|
|
8122
|
+
* Only flags set to `true` AND valid for this element type are emitted; a flag set on an
|
|
8123
|
+
* unsupported element type is dropped with a warning (silent coercion is a footgun).
|
|
8124
|
+
* @param tag - locking element tag, e.g. `'a:spLocks'`
|
|
8125
|
+
* @param allowed - attribute names this element type supports, in desired emit order
|
|
8126
|
+
* @param locks - merged lock flags (callers fold any hard-coded default in first)
|
|
8127
|
+
* @param objectName - for the warning message
|
|
8128
|
+
* @returns the locking element string, or `''` when no applicable flag is set
|
|
8129
|
+
*/
|
|
8130
|
+
function genXmlObjectLock(tag, allowed, locks, objectName) {
|
|
8131
|
+
if (!locks) return "";
|
|
8132
|
+
const lockMap = locks;
|
|
8133
|
+
for (const key of Object.keys(lockMap)) if (lockMap[key] && !allowed.includes(key)) console.warn(`Warning: objectLock.${key} is not supported on <${tag}> (object "${objectName ?? ""}") and was ignored.`);
|
|
8134
|
+
const attrs = allowed.filter((name) => lockMap[name] === true).map((name) => `${name}="1"`);
|
|
8135
|
+
return attrs.length > 0 ? `<${tag} ${attrs.join(" ")}/>` : "";
|
|
8136
|
+
}
|
|
7569
8137
|
function genXmlPresetGeom(shapeName, options, cx, cy) {
|
|
7570
|
-
|
|
8138
|
+
if (!VALID_SHAPE_PRESETS.has(shapeName)) throw new Error(`Invalid shape "${String(shapeName)}"! Use a value from \`pptxgen.shapes.*\` (e.g. \`pptxgen.shapes.RECTANGLE\`). PowerPoint can't render unknown preset geometries and will drop the shape during repair.`);
|
|
8139
|
+
let avLst = "";
|
|
8140
|
+
const emittedAdjNames = /* @__PURE__ */ new Set();
|
|
8141
|
+
const emitGuide = (name, fmlaVal) => {
|
|
8142
|
+
avLst += `<a:gd name="${name}" fmla="val ${fmlaVal}"/>`;
|
|
8143
|
+
emittedAdjNames.add(name);
|
|
8144
|
+
};
|
|
7571
8145
|
if (options.rectRadius) {
|
|
7572
8146
|
const adjVal = Math.round(options.rectRadius * EMU * 1e5 / Math.min(cx, cy));
|
|
7573
8147
|
if (RECT_RADIUS_ADJ1_SHAPES.has(shapeName)) {
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
} else
|
|
8148
|
+
emitGuide("adj1", adjVal);
|
|
8149
|
+
emitGuide("adj2", 0);
|
|
8150
|
+
} else emitGuide("adj", adjVal);
|
|
7577
8151
|
} else if (options.angleRange) {
|
|
7578
8152
|
for (let i = 0; i < 2; i++) {
|
|
7579
8153
|
const angle = options.angleRange[i];
|
|
7580
|
-
|
|
8154
|
+
emitGuide(`adj${i + 1}`, convertRotationDegrees(angle));
|
|
7581
8155
|
}
|
|
7582
|
-
if (options.arcThicknessRatio)
|
|
8156
|
+
if (options.arcThicknessRatio) emitGuide("adj3", Math.round(options.arcThicknessRatio * 5e4));
|
|
7583
8157
|
}
|
|
7584
|
-
|
|
7585
|
-
|
|
8158
|
+
if (options.shapeAdjust) (Array.isArray(options.shapeAdjust) ? options.shapeAdjust : [options.shapeAdjust]).forEach((adj) => {
|
|
8159
|
+
if (!adj || typeof adj.name !== "string" || adj.name.length === 0 || typeof adj.value !== "number" || !isFinite(adj.value)) {
|
|
8160
|
+
console.warn(`Warning: shapeAdjust entry ${JSON.stringify(adj)} is invalid (needs { name:string, value:number }) and was ignored.`);
|
|
8161
|
+
return;
|
|
8162
|
+
}
|
|
8163
|
+
if (emittedAdjNames.has(adj.name)) {
|
|
8164
|
+
console.warn(`Warning: shapeAdjust "${adj.name}" was ignored because rectRadius/angleRange already set that handle.`);
|
|
8165
|
+
return;
|
|
8166
|
+
}
|
|
8167
|
+
emitGuide(adj.name, Math.round(adj.value * 1e5));
|
|
8168
|
+
});
|
|
8169
|
+
return `<a:prstGeom prst="${shapeName}"><a:avLst>${avLst}</a:avLst></a:prstGeom>`;
|
|
7586
8170
|
}
|
|
7587
8171
|
/**
|
|
7588
8172
|
* Emit an `<a:custGeom>` for a freeform path built from `points`.
|
|
@@ -7635,6 +8219,45 @@ function genXmlCustGeom(points, cx, cy, layout) {
|
|
|
7635
8219
|
}
|
|
7636
8220
|
const PLACEHOLDER_TYPE_MAP = PLACEHOLDER_TYPES;
|
|
7637
8221
|
/**
|
|
8222
|
+
* Emit the `<a:lnL>/<a:lnR>/<a:lnT>/<a:lnB>` border children of an `<a:tcPr>` for a table cell.
|
|
8223
|
+
* Shared by normal cells and the dummy span (`_hmerge`/`_vmerge`) cells so a merged region's
|
|
8224
|
+
* outer edges render with the same border as its origin cell (Issue #680).
|
|
8225
|
+
* @param {BorderProps[]} cellBorder - 4-tuple of border props in [top, right, bottom, left] order
|
|
8226
|
+
* @return {string} concatenated border element XML, in the LRTB document order PowerPoint expects
|
|
8227
|
+
*/
|
|
8228
|
+
function genTableCellBorderXml(cellBorder) {
|
|
8229
|
+
let strXml = "";
|
|
8230
|
+
[
|
|
8231
|
+
{
|
|
8232
|
+
idx: 3,
|
|
8233
|
+
name: "lnL"
|
|
8234
|
+
},
|
|
8235
|
+
{
|
|
8236
|
+
idx: 1,
|
|
8237
|
+
name: "lnR"
|
|
8238
|
+
},
|
|
8239
|
+
{
|
|
8240
|
+
idx: 0,
|
|
8241
|
+
name: "lnT"
|
|
8242
|
+
},
|
|
8243
|
+
{
|
|
8244
|
+
idx: 2,
|
|
8245
|
+
name: "lnB"
|
|
8246
|
+
}
|
|
8247
|
+
].forEach((obj) => {
|
|
8248
|
+
const border = cellBorder[obj.idx];
|
|
8249
|
+
if (!border) return;
|
|
8250
|
+
const cap = createLineCap(border.cap);
|
|
8251
|
+
if (border.type !== "none") {
|
|
8252
|
+
strXml += `<a:${obj.name} w="${valToPts(border.pt)}" cap="${cap}" cmpd="sng" algn="ctr">`;
|
|
8253
|
+
strXml += `<a:solidFill>${createColorElement(border.color)}</a:solidFill>`;
|
|
8254
|
+
strXml += `<a:prstDash val="${border.type === "dash" ? "sysDash" : "solid"}"/><a:round/><a:headEnd type="none" w="med" len="med"/><a:tailEnd type="none" w="med" len="med"/>`;
|
|
8255
|
+
strXml += `</a:${obj.name}>`;
|
|
8256
|
+
} else strXml += `<a:${obj.name} w="0" cap="${cap}" cmpd="sng" algn="ctr"><a:noFill/></a:${obj.name}>`;
|
|
8257
|
+
});
|
|
8258
|
+
return strXml;
|
|
8259
|
+
}
|
|
8260
|
+
/**
|
|
7638
8261
|
* Transforms a slide or slideLayout to resulting XML string - Creates `ppt/slide*.xml`
|
|
7639
8262
|
* @param {PresSlideInternal|SlideLayoutInternal} slideObject - slide object created within createSlideObject
|
|
7640
8263
|
* @return {string} XML string with <p:cSld> as the root
|
|
@@ -7693,7 +8316,10 @@ function slideObjectToXml(slide) {
|
|
|
7693
8316
|
intColCnt += cellOpts?.colspan ? Number(cellOpts.colspan) : 1;
|
|
7694
8317
|
});
|
|
7695
8318
|
strXml = `<p:graphicFrame><p:nvGraphicFramePr><p:cNvPr id="${intTableNum * slide._slideNum + 1}" name="${slideItemObj.options.objectName}" descr="${encodeXmlEntities(slideItemObj.options.altText || "")}"/>`;
|
|
7696
|
-
strXml +=
|
|
8319
|
+
strXml += `<p:cNvGraphicFramePr>${genXmlObjectLock("a:graphicFrameLocks", GRAPHIC_FRAME_LOCK_ATTRS, {
|
|
8320
|
+
noGrp: true,
|
|
8321
|
+
...slideItemObj.options.objectLock
|
|
8322
|
+
}, slideItemObj.options.objectName)}</p:cNvGraphicFramePr> <p:nvPr><p:extLst><p:ext uri="{D42A27DB-BD31-4B8C-83A1-F6EECF244321}"><p14:modId xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main" val="1579011935"/></p:ext></p:extLst></p:nvPr></p:nvGraphicFramePr>`;
|
|
7697
8323
|
strXml += `<p:xfrm><a:off x="${x || (x === 0 ? 0 : EMU)}" y="${y || (y === 0 ? 0 : EMU)}"/><a:ext cx="${cx || (cx === 0 ? 0 : EMU)}" cy="${cy || EMU}"/></p:xfrm>`;
|
|
7698
8324
|
{
|
|
7699
8325
|
const tblPrAttrs = (objTabOpts.hasHeader ? " firstRow=\"1\"" : "") + (objTabOpts.hasFooter ? " lastRow=\"1\"" : "") + (objTabOpts.hasBandedRows ? " bandRow=\"1\"" : "") + (objTabOpts.hasBandedColumns ? " bandCol=\"1\"" : "") + (objTabOpts.hasFirstColumn ? " firstCol=\"1\"" : "") + (objTabOpts.hasLastColumn ? " lastCol=\"1\"" : "");
|
|
@@ -7725,7 +8351,8 @@ function slideObjectToXml(slide) {
|
|
|
7725
8351
|
return {
|
|
7726
8352
|
_type: "tablecell",
|
|
7727
8353
|
options: { rowspan },
|
|
7728
|
-
_hmerge: true
|
|
8354
|
+
_hmerge: true,
|
|
8355
|
+
_spanOrigin: cell
|
|
7729
8356
|
};
|
|
7730
8357
|
});
|
|
7731
8358
|
cells.splice(cIdx + 1, 0, ...vMergeCells);
|
|
@@ -7741,12 +8368,14 @@ function slideObjectToXml(slide) {
|
|
|
7741
8368
|
const colspan = cell.options?.colspan;
|
|
7742
8369
|
const _hmerge = cell._hmerge;
|
|
7743
8370
|
if (rowspan && rowspan > 1) {
|
|
8371
|
+
const _spanOrigin = cell._spanOrigin || cell;
|
|
7744
8372
|
const hMergeCell = {
|
|
7745
8373
|
_type: "tablecell",
|
|
7746
8374
|
options: { colspan },
|
|
7747
8375
|
_rowContinue: rowspan - 1,
|
|
7748
8376
|
_vmerge: true,
|
|
7749
|
-
_hmerge
|
|
8377
|
+
_hmerge,
|
|
8378
|
+
_spanOrigin
|
|
7750
8379
|
};
|
|
7751
8380
|
nextRow.splice(cIdx, 0, hMergeCell);
|
|
7752
8381
|
}
|
|
@@ -7756,7 +8385,7 @@ function slideObjectToXml(slide) {
|
|
|
7756
8385
|
let intRowH = 0;
|
|
7757
8386
|
if (Array.isArray(objTabOpts.rowH) && objTabOpts.rowH[rIdx]) intRowH = inch2Emu(Number(objTabOpts.rowH[rIdx]));
|
|
7758
8387
|
else if (objTabOpts.rowH && !isNaN(Number(objTabOpts.rowH))) intRowH = inch2Emu(Number(objTabOpts.rowH));
|
|
7759
|
-
else if (slideItemObj.options.cy || slideItemObj.options.h) intRowH = Math.round((slideItemObj.options.h ?
|
|
8388
|
+
else if (slideItemObj.options.cy || slideItemObj.options.h) intRowH = Math.round((slideItemObj.options.h ? cy : typeof slideItemObj.options.cy === "number" ? slideItemObj.options.cy : 1) / arrTabRows.length);
|
|
7760
8389
|
strXml += `<a:tr h="${intRowH}">`;
|
|
7761
8390
|
cells.forEach((cellObj) => {
|
|
7762
8391
|
const cell = cellObj;
|
|
@@ -7769,7 +8398,17 @@ function slideObjectToXml(slide) {
|
|
|
7769
8398
|
let cellSpanAttrStr = Object.entries(cellSpanAttrs).filter(([, v]) => !!v).map(([k, v]) => `${String(k)}="${String(v)}"`).join(" ");
|
|
7770
8399
|
if (cellSpanAttrStr) cellSpanAttrStr = " " + cellSpanAttrStr;
|
|
7771
8400
|
if (cell._hmerge || cell._vmerge) {
|
|
7772
|
-
|
|
8401
|
+
const origin = cell._spanOrigin;
|
|
8402
|
+
let spanPrXml = "";
|
|
8403
|
+
if (origin) {
|
|
8404
|
+
const originOpts = origin.options || {};
|
|
8405
|
+
const originBorder = Array.isArray(originOpts.border) ? originOpts.border : null;
|
|
8406
|
+
if (originBorder) spanPrXml += genTableCellBorderXml(originBorder);
|
|
8407
|
+
let spanFill = origin._optImp?.fill?.color ? origin._optImp.fill.color : origin._optImp?.fill && typeof origin._optImp.fill === "string" ? origin._optImp.fill : "";
|
|
8408
|
+
spanFill = spanFill || originOpts.fill ? originOpts.fill : "";
|
|
8409
|
+
if (spanFill) spanPrXml += genXmlColorSelection(spanFill);
|
|
8410
|
+
}
|
|
8411
|
+
strXml += `<a:tc${cellSpanAttrStr}><a:tcPr>${spanPrXml}</a:tcPr></a:tc>`;
|
|
7773
8412
|
return;
|
|
7774
8413
|
}
|
|
7775
8414
|
const cellOpts = cell.options || {};
|
|
@@ -7813,32 +8452,7 @@ function slideObjectToXml(slide) {
|
|
|
7813
8452
|
else cellMarginXml = ` marL="${inch2Emu(cellMargin[3])}" marR="${inch2Emu(cellMargin[1])}" marT="${inch2Emu(cellMargin[0])}" marB="${inch2Emu(cellMargin[2])}"`;
|
|
7814
8453
|
strXml += `<a:tc${cellSpanAttrStr}>${genXmlTextBody(cell)}<a:tcPr${cellMarginXml}${cellValign}${cellTextDir}>`;
|
|
7815
8454
|
const cellBorder = Array.isArray(cellOpts.border) ? cellOpts.border : null;
|
|
7816
|
-
if (cellBorder)
|
|
7817
|
-
{
|
|
7818
|
-
idx: 3,
|
|
7819
|
-
name: "lnL"
|
|
7820
|
-
},
|
|
7821
|
-
{
|
|
7822
|
-
idx: 1,
|
|
7823
|
-
name: "lnR"
|
|
7824
|
-
},
|
|
7825
|
-
{
|
|
7826
|
-
idx: 0,
|
|
7827
|
-
name: "lnT"
|
|
7828
|
-
},
|
|
7829
|
-
{
|
|
7830
|
-
idx: 2,
|
|
7831
|
-
name: "lnB"
|
|
7832
|
-
}
|
|
7833
|
-
].forEach((obj) => {
|
|
7834
|
-
const border = cellBorder[obj.idx];
|
|
7835
|
-
if (border.type !== "none") {
|
|
7836
|
-
strXml += `<a:${obj.name} w="${valToPts(border.pt)}" cap="flat" cmpd="sng" algn="ctr">`;
|
|
7837
|
-
strXml += `<a:solidFill>${createColorElement(border.color)}</a:solidFill>`;
|
|
7838
|
-
strXml += `<a:prstDash val="${border.type === "dash" ? "sysDash" : "solid"}"/><a:round/><a:headEnd type="none" w="med" len="med"/><a:tailEnd type="none" w="med" len="med"/>`;
|
|
7839
|
-
strXml += `</a:${obj.name}>`;
|
|
7840
|
-
} else strXml += `<a:${obj.name} w="0" cap="flat" cmpd="sng" algn="ctr"><a:noFill/></a:${obj.name}>`;
|
|
7841
|
-
});
|
|
8455
|
+
if (cellBorder) strXml += genTableCellBorderXml(cellBorder);
|
|
7842
8456
|
strXml += cellFill;
|
|
7843
8457
|
strXml += " </a:tcPr>";
|
|
7844
8458
|
strXml += " </a:tc>";
|
|
@@ -7857,10 +8471,10 @@ function slideObjectToXml(slide) {
|
|
|
7857
8471
|
if (!slideItemObj.options.line && cy === 0) cy = EMU * .3;
|
|
7858
8472
|
if (!slideItemObj.options._bodyProp) slideItemObj.options._bodyProp = {};
|
|
7859
8473
|
if (slideItemObj.options.margin && Array.isArray(slideItemObj.options.margin)) {
|
|
7860
|
-
slideItemObj.options._bodyProp.
|
|
8474
|
+
slideItemObj.options._bodyProp.tIns = valToPts(slideItemObj.options.margin[0] || 0);
|
|
7861
8475
|
slideItemObj.options._bodyProp.rIns = valToPts(slideItemObj.options.margin[1] || 0);
|
|
7862
8476
|
slideItemObj.options._bodyProp.bIns = valToPts(slideItemObj.options.margin[2] || 0);
|
|
7863
|
-
slideItemObj.options._bodyProp.
|
|
8477
|
+
slideItemObj.options._bodyProp.lIns = valToPts(slideItemObj.options.margin[3] || 0);
|
|
7864
8478
|
} else if (typeof slideItemObj.options.margin === "number") {
|
|
7865
8479
|
slideItemObj.options._bodyProp.lIns = valToPts(slideItemObj.options.margin);
|
|
7866
8480
|
slideItemObj.options._bodyProp.rIns = valToPts(slideItemObj.options.margin);
|
|
@@ -7872,7 +8486,11 @@ function slideObjectToXml(slide) {
|
|
|
7872
8486
|
if (slideItemObj.options.hyperlink?.url) strSlideXml += `<a:hlinkClick r:id="rId${slideItemObj.options.hyperlink._rId}" tooltip="${slideItemObj.options.hyperlink.tooltip ? encodeXmlEntities(slideItemObj.options.hyperlink.tooltip) : ""}"/>`;
|
|
7873
8487
|
if (slideItemObj.options.hyperlink?.slide) strSlideXml += `<a:hlinkClick r:id="rId${slideItemObj.options.hyperlink._rId}" tooltip="${slideItemObj.options.hyperlink.tooltip ? encodeXmlEntities(slideItemObj.options.hyperlink.tooltip) : ""}" action="ppaction://hlinksldjump"/>`;
|
|
7874
8488
|
strSlideXml += "</p:cNvPr>";
|
|
7875
|
-
|
|
8489
|
+
{
|
|
8490
|
+
const spLockXml = genXmlObjectLock("a:spLocks", SHAPE_LOCK_ATTRS, slideItemObj.options.objectLock, slideItemObj.options.objectName);
|
|
8491
|
+
strSlideXml += "<p:cNvSpPr" + (slideItemObj.options?.isTextBox ? " txBox=\"1\"" : "");
|
|
8492
|
+
strSlideXml += spLockXml ? `>${spLockXml}</p:cNvSpPr>` : "/>";
|
|
8493
|
+
}
|
|
7876
8494
|
strSlideXml += `<p:nvPr>${slideItemObj._type === "placeholder" ? genXmlPlaceholder(slideItemObj) : genXmlPlaceholder(placeholderObj)}</p:nvPr>`;
|
|
7877
8495
|
strSlideXml += "</p:nvSpPr><p:spPr>";
|
|
7878
8496
|
strSlideXml += `<a:xfrm${locationAttr}>`;
|
|
@@ -7882,7 +8500,8 @@ function slideObjectToXml(slide) {
|
|
|
7882
8500
|
else strSlideXml += genXmlPresetGeom(slideItemObj.shape, slideItemObj.options, cx, cy);
|
|
7883
8501
|
strSlideXml += slideItemObj.options.fill ? genXmlColorSelection(slideItemObj.options.fill) : "<a:noFill/>";
|
|
7884
8502
|
if (slideItemObj.options.line) {
|
|
7885
|
-
|
|
8503
|
+
const lnAttrs = (slideItemObj.options.line.width ? ` w="${lineWidthToEmu(slideItemObj.options.line.width)}"` : "") + (slideItemObj.options.line.cap ? ` cap="${createLineCap(slideItemObj.options.line.cap)}"` : "");
|
|
8504
|
+
strSlideXml += `<a:ln${lnAttrs}>`;
|
|
7886
8505
|
if (slideItemObj.options.line.color) strSlideXml += genXmlColorSelection(slideItemObj.options.line);
|
|
7887
8506
|
if (slideItemObj.options.line.dashType) strSlideXml += `<a:prstDash val="${slideItemObj.options.line.dashType}"/>`;
|
|
7888
8507
|
if (slideItemObj.options.line.beginArrowType) strSlideXml += `<a:headEnd type="${slideItemObj.options.line.beginArrowType}"/>`;
|
|
@@ -7908,6 +8527,24 @@ function slideObjectToXml(slide) {
|
|
|
7908
8527
|
strSlideXml += genXmlTextBody(slideItemObj);
|
|
7909
8528
|
strSlideXml += "</p:sp>";
|
|
7910
8529
|
break;
|
|
8530
|
+
case "connector":
|
|
8531
|
+
strSlideXml += "<p:cxnSp><p:nvCxnSpPr>";
|
|
8532
|
+
strSlideXml += `<p:cNvPr id="${idx + 2}" name="${slideItemObj.options.objectName}" descr="${encodeXmlEntities(slideItemObj.options.altText || "")}"/>`;
|
|
8533
|
+
strSlideXml += "<p:cNvCxnSpPr/><p:nvPr/></p:nvCxnSpPr><p:spPr>";
|
|
8534
|
+
strSlideXml += `<a:xfrm${locationAttr}><a:off x="${x}" y="${y}"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm>`;
|
|
8535
|
+
strSlideXml += `<a:prstGeom prst="${slideItemObj.shape}"><a:avLst/></a:prstGeom>`;
|
|
8536
|
+
{
|
|
8537
|
+
const ln = slideItemObj.options.line || {};
|
|
8538
|
+
const lnAttrs = (ln.width ? ` w="${lineWidthToEmu(ln.width)}"` : "") + (ln.cap ? ` cap="${createLineCap(ln.cap)}"` : "");
|
|
8539
|
+
strSlideXml += `<a:ln${lnAttrs}>`;
|
|
8540
|
+
if (ln.color) strSlideXml += genXmlColorSelection(ln);
|
|
8541
|
+
if (ln.dashType) strSlideXml += `<a:prstDash val="${ln.dashType}"/>`;
|
|
8542
|
+
if (ln.beginArrowType) strSlideXml += `<a:headEnd type="${ln.beginArrowType}"/>`;
|
|
8543
|
+
if (ln.endArrowType) strSlideXml += `<a:tailEnd type="${ln.endArrowType}"/>`;
|
|
8544
|
+
strSlideXml += "</a:ln>";
|
|
8545
|
+
}
|
|
8546
|
+
strSlideXml += "</p:spPr></p:cxnSp>";
|
|
8547
|
+
break;
|
|
7911
8548
|
case "image":
|
|
7912
8549
|
strSlideXml += "<p:pic>";
|
|
7913
8550
|
strSlideXml += " <p:nvPicPr>";
|
|
@@ -7915,7 +8552,10 @@ function slideObjectToXml(slide) {
|
|
|
7915
8552
|
if (slideItemObj.hyperlink?.url) strSlideXml += `<a:hlinkClick r:id="rId${slideItemObj.hyperlink._rId}" tooltip="${slideItemObj.hyperlink.tooltip ? encodeXmlEntities(slideItemObj.hyperlink.tooltip) : ""}"/>`;
|
|
7916
8553
|
if (slideItemObj.hyperlink?.slide) strSlideXml += `<a:hlinkClick r:id="rId${slideItemObj.hyperlink._rId}" tooltip="${slideItemObj.hyperlink.tooltip ? encodeXmlEntities(slideItemObj.hyperlink.tooltip) : ""}" action="ppaction://hlinksldjump"/>`;
|
|
7917
8554
|
strSlideXml += " </p:cNvPr>";
|
|
7918
|
-
strSlideXml +=
|
|
8555
|
+
strSlideXml += ` <p:cNvPicPr>${genXmlObjectLock("a:picLocks", PICTURE_LOCK_ATTRS, {
|
|
8556
|
+
noChangeAspect: true,
|
|
8557
|
+
...slideItemObj.options.objectLock
|
|
8558
|
+
}, slideItemObj.options.objectName)}</p:cNvPicPr>`;
|
|
7919
8559
|
strSlideXml += " <p:nvPr>" + genXmlPlaceholder(placeholderObj) + "</p:nvPr>";
|
|
7920
8560
|
strSlideXml += " </p:nvPicPr>";
|
|
7921
8561
|
strSlideXml += "<p:blipFill>";
|
|
@@ -7948,7 +8588,7 @@ function slideObjectToXml(slide) {
|
|
|
7948
8588
|
const relData = (slide._relsMedia || []).find((rel) => rel.rId === slideItemObj.imageRid)?.data;
|
|
7949
8589
|
const natural = typeof relData === "string" ? getImageSizeFromBase64(relData) : null;
|
|
7950
8590
|
if (natural) cropSize = natural;
|
|
7951
|
-
else console.warn(`Warning: 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) to enable an aspect-correct crop.`);
|
|
8591
|
+
else console.warn(`Warning: 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.`);
|
|
7952
8592
|
}
|
|
7953
8593
|
strSlideXml += ImageSizingXml[sizing.type](cropSize, {
|
|
7954
8594
|
w: boxW,
|
|
@@ -7967,6 +8607,16 @@ function slideObjectToXml(slide) {
|
|
|
7967
8607
|
strSlideXml += " </a:xfrm>";
|
|
7968
8608
|
if (slideItemObj.options.points) strSlideXml += " " + genXmlCustGeom(slideItemObj.options.points, imgWidth, imgHeight, slide._presLayout);
|
|
7969
8609
|
else strSlideXml += " " + genXmlPresetGeom(slideItemObj.options.shape ?? (rounding ? "ellipse" : "rect"), slideItemObj.options, imgWidth, imgHeight);
|
|
8610
|
+
if (slideItemObj.options.line) {
|
|
8611
|
+
const imgLine = slideItemObj.options.line;
|
|
8612
|
+
const lnAttrs = (imgLine.width ? ` w="${lineWidthToEmu(imgLine.width)}"` : "") + (imgLine.cap ? ` cap="${createLineCap(imgLine.cap)}"` : "");
|
|
8613
|
+
strSlideXml += `<a:ln${lnAttrs}>`;
|
|
8614
|
+
if (imgLine.color) strSlideXml += genXmlColorSelection(imgLine);
|
|
8615
|
+
if (imgLine.dashType) strSlideXml += `<a:prstDash val="${imgLine.dashType}"/>`;
|
|
8616
|
+
if (imgLine.beginArrowType) strSlideXml += `<a:headEnd type="${imgLine.beginArrowType}"/>`;
|
|
8617
|
+
if (imgLine.endArrowType) strSlideXml += `<a:tailEnd type="${imgLine.endArrowType}"/>`;
|
|
8618
|
+
strSlideXml += "</a:ln>";
|
|
8619
|
+
}
|
|
7970
8620
|
if (slideItemObj.options.shadow && slideItemObj.options.shadow.type !== "none") {
|
|
7971
8621
|
const sh = slideItemObj.options.shadow;
|
|
7972
8622
|
const shadowType = sh.type || "outer";
|
|
@@ -7990,7 +8640,7 @@ function slideObjectToXml(slide) {
|
|
|
7990
8640
|
strSlideXml += "<p:pic>";
|
|
7991
8641
|
strSlideXml += " <p:nvPicPr>";
|
|
7992
8642
|
strSlideXml += `<p:cNvPr id="${slideItemObj.mediaRid + 2}" name="${slideItemObj.options.objectName}" descr="${encodeXmlEntities(slideItemObj.options.altText || "")}"/>`;
|
|
7993
|
-
strSlideXml +=
|
|
8643
|
+
strSlideXml += ` <p:cNvPicPr>${genXmlObjectLock("a:picLocks", PICTURE_LOCK_ATTRS, slideItemObj.options.objectLock, slideItemObj.options.objectName)}</p:cNvPicPr>`;
|
|
7994
8644
|
strSlideXml += " <p:nvPr>";
|
|
7995
8645
|
strSlideXml += ` <a:videoFile r:link="rId${slideItemObj.mediaRid}"/>`;
|
|
7996
8646
|
strSlideXml += " </p:nvPr>";
|
|
@@ -8005,7 +8655,10 @@ function slideObjectToXml(slide) {
|
|
|
8005
8655
|
strSlideXml += "<p:pic>";
|
|
8006
8656
|
strSlideXml += " <p:nvPicPr>";
|
|
8007
8657
|
strSlideXml += `<p:cNvPr id="${slideItemObj.mediaRid + 2}" name="${slideItemObj.options.objectName}" descr="${encodeXmlEntities(slideItemObj.options.altText || "")}"><a:hlinkClick r:id="" action="ppaction://media"/></p:cNvPr>`;
|
|
8008
|
-
strSlideXml +=
|
|
8658
|
+
strSlideXml += ` <p:cNvPicPr>${genXmlObjectLock("a:picLocks", PICTURE_LOCK_ATTRS, {
|
|
8659
|
+
noChangeAspect: true,
|
|
8660
|
+
...slideItemObj.options.objectLock
|
|
8661
|
+
}, slideItemObj.options.objectName)}</p:cNvPicPr>`;
|
|
8009
8662
|
strSlideXml += " <p:nvPr>";
|
|
8010
8663
|
strSlideXml += ` <a:videoFile r:link="rId${slideItemObj.mediaRid}"/>`;
|
|
8011
8664
|
strSlideXml += " <p:extLst>";
|
|
@@ -8069,7 +8722,7 @@ function slideObjectToXml(slide) {
|
|
|
8069
8722
|
strSlideXml += "/>";
|
|
8070
8723
|
strSlideXml += " <a:lstStyle><a:lvl1pPr>";
|
|
8071
8724
|
if (slide._slideNumberProps.fontFace || slide._slideNumberProps.fontSize || slide._slideNumberProps.color) {
|
|
8072
|
-
strSlideXml += `<a:defRPr sz="${
|
|
8725
|
+
strSlideXml += `<a:defRPr sz="${clampFontSizeSz(slide._slideNumberProps.fontSize || 12)}">`;
|
|
8073
8726
|
if (slide._slideNumberProps.color) strSlideXml += genXmlColorSelection(slide._slideNumberProps.color);
|
|
8074
8727
|
if (slide._slideNumberProps.fontFace) strSlideXml += `<a:latin typeface="${slide._slideNumberProps.fontFace}"/><a:ea typeface="${slide._slideNumberProps.fontFace}"/><a:cs typeface="${slide._slideNumberProps.fontFace}"/>`;
|
|
8075
8728
|
strSlideXml += "</a:defRPr>";
|
|
@@ -8158,7 +8811,7 @@ function genXmlParagraphProperties(textObj, isDefault) {
|
|
|
8158
8811
|
paragraphPropXml += "";
|
|
8159
8812
|
break;
|
|
8160
8813
|
}
|
|
8161
|
-
if (textObj.options.lineSpacing) strXmlLnSpc = `<a:lnSpc><a:spcPts val="${
|
|
8814
|
+
if (textObj.options.lineSpacing) strXmlLnSpc = `<a:lnSpc><a:spcPts val="${clampLineSpacingPts(textObj.options.lineSpacing)}"/></a:lnSpc>`;
|
|
8162
8815
|
else if (textObj.options.lineSpacingMultiple) strXmlLnSpc = `<a:lnSpc><a:spcPct val="${Math.round(textObj.options.lineSpacingMultiple * 1e5)}"/></a:lnSpc>`;
|
|
8163
8816
|
if (textObj.options.indentLevel && !isNaN(Number(textObj.options.indentLevel)) && textObj.options.indentLevel > 0) paragraphPropXml += ` lvl="${textObj.options.indentLevel}"`;
|
|
8164
8817
|
if (textObj.options.paraSpaceBefore && !isNaN(Number(textObj.options.paraSpaceBefore)) && textObj.options.paraSpaceBefore > 0) strXmlParaSpc += `<a:spcBef><a:spcPts val="${Math.round(textObj.options.paraSpaceBefore * 100)}"/></a:spcBef>`;
|
|
@@ -8166,9 +8819,17 @@ function genXmlParagraphProperties(textObj, isDefault) {
|
|
|
8166
8819
|
if (typeof textObj.options.bullet === "object") {
|
|
8167
8820
|
if (textObj?.options?.bullet?.indent) bulletMarL = valToPts(textObj.options.bullet.indent);
|
|
8168
8821
|
if (textObj.options.bullet.color) strXmlBulletColor = `<a:buClr>${createColorElement(textObj.options.bullet.color)}</a:buClr>`;
|
|
8822
|
+
let bulletSizePct = 1e5;
|
|
8823
|
+
if (textObj.options.bullet.size !== void 0) {
|
|
8824
|
+
const bulletSize = Number(textObj.options.bullet.size);
|
|
8825
|
+
if (isNaN(bulletSize) || bulletSize < 25 || bulletSize > 400) console.warn("Warning: `bullet.size` must be a percentage between 25 and 400!");
|
|
8826
|
+
else bulletSizePct = Math.round(bulletSize * 1e3);
|
|
8827
|
+
}
|
|
8828
|
+
const strXmlBulletSize = `<a:buSzPct val="${bulletSizePct}"/>`;
|
|
8829
|
+
const strXmlBulletFont = textObj.options.bullet.fontFace ? `<a:buFont typeface="${encodeXmlEntities(textObj.options.bullet.fontFace)}"/>` : "";
|
|
8169
8830
|
if (textObj.options.bullet.type && textObj.options.bullet.type.toString().toLowerCase() === "number") {
|
|
8170
8831
|
paragraphPropXml += ` marL="${textObj.options.indentLevel && textObj.options.indentLevel > 0 ? bulletMarL + bulletMarL * textObj.options.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
|
|
8171
|
-
strXmlBullet =
|
|
8832
|
+
strXmlBullet = `${strXmlBulletSize}${strXmlBulletFont || "<a:buFont typeface=\"+mj-lt\"/>"}<a:buAutoNum type="${textObj.options.bullet.style || "arabicPeriod"}" startAt="${textObj.options.bullet.numberStartAt || textObj.options.bullet.startAt || "1"}"/>`;
|
|
8172
8833
|
} else if (textObj.options.bullet.characterCode) {
|
|
8173
8834
|
let bulletCode = `&#x${textObj.options.bullet.characterCode};`;
|
|
8174
8835
|
if (!/^[0-9A-Fa-f]{4}$/.test(textObj.options.bullet.characterCode)) {
|
|
@@ -8176,7 +8837,7 @@ function genXmlParagraphProperties(textObj, isDefault) {
|
|
|
8176
8837
|
bulletCode = "•";
|
|
8177
8838
|
}
|
|
8178
8839
|
paragraphPropXml += ` marL="${textObj.options.indentLevel && textObj.options.indentLevel > 0 ? bulletMarL + bulletMarL * textObj.options.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
|
|
8179
|
-
strXmlBullet = "<a:
|
|
8840
|
+
strXmlBullet = strXmlBulletSize + strXmlBulletFont + "<a:buChar char=\"" + bulletCode + "\"/>";
|
|
8180
8841
|
} else if (textObj.options.bullet.code) {
|
|
8181
8842
|
let bulletCode = `&#x${textObj.options.bullet.code};`;
|
|
8182
8843
|
if (!/^[0-9A-Fa-f]{4}$/.test(textObj.options.bullet.code)) {
|
|
@@ -8184,10 +8845,10 @@ function genXmlParagraphProperties(textObj, isDefault) {
|
|
|
8184
8845
|
bulletCode = "•";
|
|
8185
8846
|
}
|
|
8186
8847
|
paragraphPropXml += ` marL="${textObj.options.indentLevel && textObj.options.indentLevel > 0 ? bulletMarL + bulletMarL * textObj.options.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
|
|
8187
|
-
strXmlBullet = "<a:
|
|
8848
|
+
strXmlBullet = strXmlBulletSize + strXmlBulletFont + "<a:buChar char=\"" + bulletCode + "\"/>";
|
|
8188
8849
|
} else {
|
|
8189
8850
|
paragraphPropXml += ` marL="${textObj.options.indentLevel && textObj.options.indentLevel > 0 ? bulletMarL + bulletMarL * textObj.options.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
|
|
8190
|
-
strXmlBullet =
|
|
8851
|
+
strXmlBullet = `${strXmlBulletSize}${strXmlBulletFont}<a:buChar char="•"/>`;
|
|
8191
8852
|
}
|
|
8192
8853
|
} else if (textObj.options.bullet) {
|
|
8193
8854
|
paragraphPropXml += ` marL="${textObj.options.indentLevel && textObj.options.indentLevel > 0 ? bulletMarL + bulletMarL * textObj.options.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
|
|
@@ -8212,7 +8873,7 @@ function genXmlTextRunProperties(opts, isDefault) {
|
|
|
8212
8873
|
let runProps = "";
|
|
8213
8874
|
const runPropsTag = isDefault ? "a:defRPr" : "a:rPr";
|
|
8214
8875
|
runProps += "<" + runPropsTag + " lang=\"" + (opts.lang ? opts.lang : "en-US") + "\"" + (opts.lang ? " altLang=\"en-US\"" : "");
|
|
8215
|
-
runProps += opts.fontSize ? ` sz="${
|
|
8876
|
+
runProps += opts.fontSize ? ` sz="${clampFontSizeSz(opts.fontSize)}"` : "";
|
|
8216
8877
|
runProps += opts?.bold ? ` b="${opts.bold ? "1" : "0"}"` : "";
|
|
8217
8878
|
runProps += opts?.italic ? ` i="${opts.italic ? "1" : "0"}"` : "";
|
|
8218
8879
|
runProps += opts?.strike ? ` strike="${typeof opts.strike === "string" ? opts.strike : "sngStrike"}"` : "";
|
|
@@ -8223,17 +8884,23 @@ function genXmlTextRunProperties(opts, isDefault) {
|
|
|
8223
8884
|
if (opts.baseline) runProps += ` baseline="${Math.round(opts.baseline * 50)}"`;
|
|
8224
8885
|
else if (opts.subscript) runProps += " baseline=\"-40000\"";
|
|
8225
8886
|
else if (opts.superscript) runProps += " baseline=\"30000\"";
|
|
8226
|
-
runProps += opts.charSpacing ? ` spc="${
|
|
8887
|
+
runProps += opts.charSpacing ? ` spc="${clampCharSpacingSpc(opts.charSpacing)}" kern="0"` : "";
|
|
8227
8888
|
runProps += " dirty=\"0\">";
|
|
8228
|
-
|
|
8229
|
-
|
|
8889
|
+
const hasShadow = !!opts.shadow && opts.shadow.type !== "none";
|
|
8890
|
+
if (opts.color || opts.fontFace || opts.outline || opts.glow || hasShadow || typeof opts.underline === "object" && opts.underline.color) {
|
|
8891
|
+
if (opts.outline && typeof opts.outline === "object") runProps += `<a:ln w="${lineWidthToEmu(opts.outline.size || .75)}">${genXmlColorSelection(opts.outline.color || "FFFFFF")}</a:ln>`;
|
|
8230
8892
|
if (opts.color) runProps += genXmlColorSelection({
|
|
8231
8893
|
color: opts.color,
|
|
8232
8894
|
transparency: opts.transparency
|
|
8233
8895
|
});
|
|
8896
|
+
if (opts.glow || hasShadow) {
|
|
8897
|
+
runProps += "<a:effectLst>";
|
|
8898
|
+
if (opts.glow) runProps += createGlowElement(opts.glow, DEF_TEXT_GLOW);
|
|
8899
|
+
if (hasShadow) runProps += createShadowElement$1(opts.shadow, DEF_TEXT_SHADOW);
|
|
8900
|
+
runProps += "</a:effectLst>";
|
|
8901
|
+
}
|
|
8234
8902
|
if (opts.highlight) runProps += `<a:highlight>${createColorElement(opts.highlight)}</a:highlight>`;
|
|
8235
8903
|
if (typeof opts.underline === "object" && opts.underline.color) runProps += `<a:uFill>${genXmlColorSelection(opts.underline.color)}</a:uFill>`;
|
|
8236
|
-
if (opts.glow) runProps += `<a:effectLst>${createGlowElement(opts.glow, DEF_TEXT_GLOW)}</a:effectLst>`;
|
|
8237
8904
|
if (opts.fontFace) runProps += `<a:latin typeface="${opts.fontFace}" pitchFamily="34" charset="0"/><a:ea typeface="${opts.fontFace}" pitchFamily="34" charset="-122"/><a:cs typeface="${opts.fontFace}" pitchFamily="34" charset="-120"/>`;
|
|
8238
8905
|
}
|
|
8239
8906
|
if (opts.hyperlink) {
|
|
@@ -8263,6 +8930,28 @@ function genXmlTextRun(textObj) {
|
|
|
8263
8930
|
return `<a:r>${genXmlTextRunProperties(textObj.options, false)}<a:t>${encodeXmlEntities(String(textObj.text))}</a:t></a:r>`;
|
|
8264
8931
|
}
|
|
8265
8932
|
/**
|
|
8933
|
+
* Builds `<a:normAutofit>` with explicit fontScale/lnSpcReduction for "shrink text on overflow"
|
|
8934
|
+
* @param {TextFitShrinkProps} fit - shrink fit options
|
|
8935
|
+
* @return {string} XML string (`<a:normAutofit .../>`)
|
|
8936
|
+
* @see ECMA-376 CT_TextNormAutofit (attributes in 1000ths of a percent)
|
|
8937
|
+
*/
|
|
8938
|
+
function genXmlNormAutofit(fit) {
|
|
8939
|
+
let attrs = "";
|
|
8940
|
+
const pct = (val, name) => {
|
|
8941
|
+
if (val === void 0 || val === null) return null;
|
|
8942
|
+
if (typeof val !== "number" || isNaN(val) || val < 0 || val > 100) {
|
|
8943
|
+
console.warn(`Warning: fit.${name} must be a number between 0 and 100 (percent); received ${String(val)} - attribute ignored.`);
|
|
8944
|
+
return null;
|
|
8945
|
+
}
|
|
8946
|
+
return Math.round(val * 1e3);
|
|
8947
|
+
};
|
|
8948
|
+
const fontScale = pct(fit.fontScale, "fontScale");
|
|
8949
|
+
if (fontScale !== null) attrs += ` fontScale="${fontScale}"`;
|
|
8950
|
+
const lnSpcReduction = pct(fit.lnSpcReduction, "lnSpcReduction");
|
|
8951
|
+
if (lnSpcReduction !== null) attrs += ` lnSpcReduction="${lnSpcReduction}"`;
|
|
8952
|
+
return `<a:normAutofit${attrs}/>`;
|
|
8953
|
+
}
|
|
8954
|
+
/**
|
|
8266
8955
|
* Builds `<a:bodyPr></a:bodyPr>` tag for "genXmlTextBody()"
|
|
8267
8956
|
* @param {ISlideObject | TableCell} slideObject - various options
|
|
8268
8957
|
* @return {string} XML string
|
|
@@ -8275,6 +8964,8 @@ function genXmlBodyProperties(slideObject) {
|
|
|
8275
8964
|
if (slideObject.options._bodyProp.tIns || slideObject.options._bodyProp.tIns === 0) bodyProperties += ` tIns="${slideObject.options._bodyProp.tIns}"`;
|
|
8276
8965
|
if (slideObject.options._bodyProp.rIns || slideObject.options._bodyProp.rIns === 0) bodyProperties += ` rIns="${slideObject.options._bodyProp.rIns}"`;
|
|
8277
8966
|
if (slideObject.options._bodyProp.bIns || slideObject.options._bodyProp.bIns === 0) bodyProperties += ` bIns="${slideObject.options._bodyProp.bIns}"`;
|
|
8967
|
+
if (slideObject.options._bodyProp.numCol) bodyProperties += ` numCol="${slideObject.options._bodyProp.numCol}"`;
|
|
8968
|
+
if (slideObject.options._bodyProp.spcCol) bodyProperties += ` spcCol="${slideObject.options._bodyProp.spcCol}"`;
|
|
8278
8969
|
bodyProperties += " rtlCol=\"0\"";
|
|
8279
8970
|
if (slideObject.options._bodyProp.anchor) bodyProperties += " anchor=\"" + slideObject.options._bodyProp.anchor + "\"";
|
|
8280
8971
|
if (slideObject.options._bodyProp.vert) bodyProperties += " vert=\"" + slideObject.options._bodyProp.vert + "\"";
|
|
@@ -8285,9 +8976,11 @@ function genXmlBodyProperties(slideObject) {
|
|
|
8285
8976
|
* @see: http://www.datypic.com/sc/ooxml/g-a_EG_TextAutofit.html
|
|
8286
8977
|
*/
|
|
8287
8978
|
if (slideObject.options.fit) {
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
else if (
|
|
8979
|
+
const fit = slideObject.options.fit;
|
|
8980
|
+
if (fit === "none") bodyProperties += "";
|
|
8981
|
+
else if (fit === "shrink") bodyProperties += "<a:normAutofit/>";
|
|
8982
|
+
else if (fit === "resize") bodyProperties += "<a:spAutoFit/>";
|
|
8983
|
+
else if (typeof fit === "object" && fit.type === "shrink") bodyProperties += genXmlNormAutofit(fit);
|
|
8291
8984
|
}
|
|
8292
8985
|
if (slideObject.options.shrinkText) bodyProperties += "<a:normAutofit/>";
|
|
8293
8986
|
bodyProperties += slideObject.options._bodyProp.autoFit ? "<a:spAutoFit/>" : "";
|
|
@@ -8423,13 +9116,13 @@ function genXmlTextBody(slideObj) {
|
|
|
8423
9116
|
}
|
|
8424
9117
|
});
|
|
8425
9118
|
if (slideObj._type === "tablecell" && (opts.fontSize || opts.fontFace)) if (opts.fontFace) {
|
|
8426
|
-
strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}"` + (opts.fontSize ? ` sz="${
|
|
9119
|
+
strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}"` + (opts.fontSize ? ` sz="${clampFontSizeSz(opts.fontSize)}"` : "") + " dirty=\"0\">";
|
|
8427
9120
|
strSlideXml += `<a:latin typeface="${opts.fontFace}" charset="0"/>`;
|
|
8428
9121
|
strSlideXml += `<a:ea typeface="${opts.fontFace}" charset="0"/>`;
|
|
8429
9122
|
strSlideXml += `<a:cs typeface="${opts.fontFace}" charset="0"/>`;
|
|
8430
9123
|
strSlideXml += "</a:endParaRPr>";
|
|
8431
|
-
} else strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}"` + (opts.fontSize ? ` sz="${
|
|
8432
|
-
else if (reqsClosingFontSize) strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}"` + (opts.fontSize ? ` sz="${
|
|
9124
|
+
} else strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}"` + (opts.fontSize ? ` sz="${clampFontSizeSz(opts.fontSize)}"` : "") + " dirty=\"0\"/>";
|
|
9125
|
+
else if (reqsClosingFontSize) strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}"` + (opts.fontSize ? ` sz="${clampFontSizeSz(opts.fontSize)}"` : "") + " dirty=\"0\"/>";
|
|
8433
9126
|
else strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}" dirty="0"/>`;
|
|
8434
9127
|
strSlideXml += "</a:p>";
|
|
8435
9128
|
});
|
|
@@ -8770,11 +9463,51 @@ function getLayoutIdxForSlide(slides, slideLayouts, slideNumber) {
|
|
|
8770
9463
|
return 1;
|
|
8771
9464
|
}
|
|
8772
9465
|
/**
|
|
9466
|
+
* Theme `<a:clrScheme>` slots in OOXML document order, with their default Office color child.
|
|
9467
|
+
* `dk1`/`lt1` default to `sysClr` (windowText/window); the rest are `srgbClr`. A user override
|
|
9468
|
+
* for any slot is emitted as `<a:srgbClr>` (see `buildThemeClrScheme`).
|
|
9469
|
+
*/
|
|
9470
|
+
const THEME_CLR_SCHEME_DEFAULTS = [
|
|
9471
|
+
["dk1", "<a:sysClr val=\"windowText\" lastClr=\"000000\"/>"],
|
|
9472
|
+
["lt1", "<a:sysClr val=\"window\" lastClr=\"FFFFFF\"/>"],
|
|
9473
|
+
["dk2", "<a:srgbClr val=\"44546A\"/>"],
|
|
9474
|
+
["lt2", "<a:srgbClr val=\"E7E6E6\"/>"],
|
|
9475
|
+
["accent1", "<a:srgbClr val=\"4472C4\"/>"],
|
|
9476
|
+
["accent2", "<a:srgbClr val=\"ED7D31\"/>"],
|
|
9477
|
+
["accent3", "<a:srgbClr val=\"A5A5A5\"/>"],
|
|
9478
|
+
["accent4", "<a:srgbClr val=\"FFC000\"/>"],
|
|
9479
|
+
["accent5", "<a:srgbClr val=\"5B9BD5\"/>"],
|
|
9480
|
+
["accent6", "<a:srgbClr val=\"70AD47\"/>"],
|
|
9481
|
+
["hlink", "<a:srgbClr val=\"0563C1\"/>"],
|
|
9482
|
+
["folHlink", "<a:srgbClr val=\"954F72\"/>"]
|
|
9483
|
+
];
|
|
9484
|
+
/**
|
|
9485
|
+
* Build the theme `<a:clrScheme>` block, applying any caller-supplied color overrides over the
|
|
9486
|
+
* default Office scheme. Invalid (non 6-digit-hex) overrides warn and keep the default rather
|
|
9487
|
+
* than emitting a degenerate color.
|
|
9488
|
+
* @param {ThemeColorScheme} [scheme] - per-slot hex overrides
|
|
9489
|
+
* @return {string} the `<a:clrScheme>...</a:clrScheme>` XML
|
|
9490
|
+
*/
|
|
9491
|
+
function buildThemeClrScheme(scheme) {
|
|
9492
|
+
return `<a:clrScheme name="Office">${THEME_CLR_SCHEME_DEFAULTS.map(([slot, defaultChild]) => {
|
|
9493
|
+
const override = scheme?.[slot];
|
|
9494
|
+
let child = defaultChild;
|
|
9495
|
+
if (typeof override === "string" && override.length > 0) {
|
|
9496
|
+
const hex = override.replace("#", "");
|
|
9497
|
+
if (REGEX_HEX_COLOR.test(hex)) child = `<a:srgbClr val="${hex.toUpperCase()}"/>`;
|
|
9498
|
+
else console.warn(`makeXmlTheme: colorScheme.${slot} "${override}" is not a 6-digit hex color; keeping the Office default.`);
|
|
9499
|
+
}
|
|
9500
|
+
return `<a:${slot}>${child}</a:${slot}>`;
|
|
9501
|
+
}).join("")}</a:clrScheme>`;
|
|
9502
|
+
}
|
|
9503
|
+
/**
|
|
8773
9504
|
* Creates `ppt/theme/theme1.xml`
|
|
8774
9505
|
* @return {string} XML
|
|
8775
9506
|
*/
|
|
8776
9507
|
function makeXmlTheme(pres) {
|
|
8777
|
-
|
|
9508
|
+
const majorFont = pres.theme?.headFontFace ? `<a:latin typeface="${pres.theme?.headFontFace}"/>` : "<a:latin typeface=\"Calibri Light\" panose=\"020F0302020204030204\"/>";
|
|
9509
|
+
const minorFont = pres.theme?.bodyFontFace ? `<a:latin typeface="${pres.theme?.bodyFontFace}"/>` : "<a:latin typeface=\"Calibri\" panose=\"020F0502020204030204\"/>";
|
|
9510
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme"><a:themeElements>${buildThemeClrScheme(pres.theme?.colorScheme)}<a:fontScheme name="Office"><a:majorFont>${majorFont}<a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="游ゴシック Light"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="等线 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="Angsana New"/><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:font script="Armn" typeface="Arial"/><a:font script="Bugi" typeface="Leelawadee UI"/><a:font script="Bopo" typeface="Microsoft JhengHei"/><a:font script="Java" typeface="Javanese Text"/><a:font script="Lisu" typeface="Segoe UI"/><a:font script="Mymr" typeface="Myanmar Text"/><a:font script="Nkoo" typeface="Ebrima"/><a:font script="Olck" typeface="Nirmala UI"/><a:font script="Osma" typeface="Ebrima"/><a:font script="Phag" typeface="Phagspa"/><a:font script="Syrn" typeface="Estrangelo Edessa"/><a:font script="Syrj" typeface="Estrangelo Edessa"/><a:font script="Syre" typeface="Estrangelo Edessa"/><a:font script="Sora" typeface="Nirmala UI"/><a:font script="Tale" typeface="Microsoft Tai Le"/><a:font script="Talu" typeface="Microsoft New Tai Lue"/><a:font script="Tfng" typeface="Ebrima"/></a:majorFont><a:minorFont>${minorFont}<a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="游ゴシック"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="等线"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Arial"/><a:font script="Hebr" typeface="Arial"/><a:font script="Thai" typeface="Cordia New"/><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:font script="Armn" typeface="Arial"/><a:font script="Bugi" typeface="Leelawadee UI"/><a:font script="Bopo" typeface="Microsoft JhengHei"/><a:font script="Java" typeface="Javanese Text"/><a:font script="Lisu" typeface="Segoe UI"/><a:font script="Mymr" typeface="Myanmar Text"/><a:font script="Nkoo" typeface="Ebrima"/><a:font script="Olck" typeface="Nirmala UI"/><a:font script="Osma" typeface="Ebrima"/><a:font script="Phag" typeface="Phagspa"/><a:font script="Syrn" typeface="Estrangelo Edessa"/><a:font script="Syrj" typeface="Estrangelo Edessa"/><a:font script="Syre" typeface="Estrangelo Edessa"/><a:font script="Sora" typeface="Nirmala UI"/><a:font script="Tale" typeface="Microsoft Tai Le"/><a:font script="Talu" typeface="Microsoft New Tai Lue"/><a:font script="Tfng" typeface="Ebrima"/></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>`;
|
|
8778
9511
|
}
|
|
8779
9512
|
/**
|
|
8780
9513
|
* Create presentation file (`ppt/presentation.xml`)
|
|
@@ -8905,7 +9638,7 @@ function genXmlTableStyleBorders(border) {
|
|
|
8905
9638
|
xml += `<a:${side}>`;
|
|
8906
9639
|
if (b.type === "none") xml += "<a:ln><a:noFill/></a:ln>";
|
|
8907
9640
|
else {
|
|
8908
|
-
xml += `<a:ln w="${
|
|
9641
|
+
xml += `<a:ln w="${lineWidthToEmu(b.pt ?? 1)}" cap="flat" cmpd="sng" algn="ctr">`;
|
|
8909
9642
|
xml += `<a:solidFill>${createColorElement(b.color ?? "666666")}</a:solidFill>`;
|
|
8910
9643
|
xml += `<a:prstDash val="${b.type === "dash" ? "sysDash" : "solid"}"/>`;
|
|
8911
9644
|
xml += "</a:ln>";
|
|
@@ -8983,7 +9716,7 @@ function makeXmlViewProps() {
|
|
|
8983
9716
|
* @see https://docs.microsoft.com/en-us/office/open-xml/structure-of-a-presentationml-document
|
|
8984
9717
|
* @see https://docs.microsoft.com/en-us/previous-versions/office/developer/office-2010/hh273476(v=office.14)
|
|
8985
9718
|
*/
|
|
8986
|
-
const VERSION = "5.
|
|
9719
|
+
const VERSION = "5.4.0";
|
|
8987
9720
|
function standardLayoutToPresLayout(layout) {
|
|
8988
9721
|
return {
|
|
8989
9722
|
name: layout.name,
|
|
@@ -9235,6 +9968,7 @@ var PptxGenJS$1 = class {
|
|
|
9235
9968
|
this._tableStyles = [];
|
|
9236
9969
|
this._masterSlide = {
|
|
9237
9970
|
addChart: null,
|
|
9971
|
+
addConnector: null,
|
|
9238
9972
|
addImage: null,
|
|
9239
9973
|
addMedia: null,
|
|
9240
9974
|
addNotes: null,
|
|
@@ -9307,14 +10041,27 @@ var PptxGenJS$1 = class {
|
|
|
9307
10041
|
const arrChartPromises = [];
|
|
9308
10042
|
let arrMediaPromises = [];
|
|
9309
10043
|
const zip = new import_jszip_min.default();
|
|
10044
|
+
const onMediaError = props.onMediaError ?? "throw";
|
|
9310
10045
|
this._slides.forEach((slide) => {
|
|
9311
|
-
arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(slide, this._runtime));
|
|
10046
|
+
arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(slide, this._runtime, onMediaError));
|
|
9312
10047
|
});
|
|
9313
10048
|
this._slideLayouts.forEach((layout) => {
|
|
9314
|
-
arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(layout, this._runtime));
|
|
10049
|
+
arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(layout, this._runtime, onMediaError));
|
|
9315
10050
|
});
|
|
9316
|
-
arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(this._masterSlide, this._runtime));
|
|
10051
|
+
arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(this._masterSlide, this._runtime, onMediaError));
|
|
9317
10052
|
return await Promise.all(arrMediaPromises).then(async () => {
|
|
10053
|
+
const canonicalMediaTargets = /* @__PURE__ */ new Map();
|
|
10054
|
+
for (const target of [
|
|
10055
|
+
...this._slides,
|
|
10056
|
+
...this._slideLayouts,
|
|
10057
|
+
this._masterSlide
|
|
10058
|
+
]) for (const rel of target._relsMedia || []) {
|
|
10059
|
+
if (rel.type === "online" || rel.type === "hyperlink" || typeof rel.data !== "string" || !rel.data) continue;
|
|
10060
|
+
const key = (rel.extn || "") + "\0" + rel.data;
|
|
10061
|
+
const canonical = canonicalMediaTargets.get(key);
|
|
10062
|
+
if (canonical) rel.Target = canonical;
|
|
10063
|
+
else canonicalMediaTargets.set(key, rel.Target);
|
|
10064
|
+
}
|
|
9318
10065
|
this._slides.forEach((slide) => {
|
|
9319
10066
|
if (slide._slideLayout) addPlaceholdersToSlideLayouts(slide);
|
|
9320
10067
|
});
|
|
@@ -9367,14 +10114,18 @@ var PptxGenJS$1 = class {
|
|
|
9367
10114
|
});
|
|
9368
10115
|
this.createChartMediaRels(this._masterSlide, zip, arrChartPromises);
|
|
9369
10116
|
return await Promise.all(arrChartPromises).then(async () => {
|
|
10117
|
+
const compression = props.compression === false ? "STORE" : "DEFLATE";
|
|
9370
10118
|
if (props.outputType === "STREAM") return await zip.generateAsync({
|
|
9371
10119
|
type: "nodebuffer",
|
|
9372
|
-
compression
|
|
10120
|
+
compression
|
|
10121
|
+
});
|
|
10122
|
+
else if (props.outputType) return await zip.generateAsync({
|
|
10123
|
+
type: props.outputType,
|
|
10124
|
+
compression
|
|
9373
10125
|
});
|
|
9374
|
-
else if (props.outputType) return await zip.generateAsync({ type: props.outputType });
|
|
9375
10126
|
else return await zip.generateAsync({
|
|
9376
10127
|
type: "blob",
|
|
9377
|
-
compression
|
|
10128
|
+
compression
|
|
9378
10129
|
});
|
|
9379
10130
|
});
|
|
9380
10131
|
});
|
|
@@ -9397,10 +10148,12 @@ var PptxGenJS$1 = class {
|
|
|
9397
10148
|
*/
|
|
9398
10149
|
async write(props) {
|
|
9399
10150
|
const propsOutpType = typeof props === "object" && props?.outputType ? props.outputType : props ? props : null;
|
|
9400
|
-
const propsCompress = typeof props === "object"
|
|
10151
|
+
const propsCompress = typeof props === "object" ? props?.compression : void 0;
|
|
10152
|
+
const propsMediaError = typeof props === "object" ? props?.onMediaError : void 0;
|
|
9401
10153
|
return await this.exportPresentation({
|
|
9402
10154
|
compression: propsCompress,
|
|
9403
|
-
outputType: propsOutpType
|
|
10155
|
+
outputType: propsOutpType,
|
|
10156
|
+
onMediaError: propsMediaError
|
|
9404
10157
|
});
|
|
9405
10158
|
}
|
|
9406
10159
|
/**
|
|
@@ -9414,11 +10167,12 @@ var PptxGenJS$1 = class {
|
|
|
9414
10167
|
console.warn("[WARNING] writeFile(string) is deprecated - pass { fileName } instead.");
|
|
9415
10168
|
props = { fileName: props };
|
|
9416
10169
|
}
|
|
9417
|
-
const { fileName: rawName = "Presentation.pptx", compression
|
|
10170
|
+
const { fileName: rawName = "Presentation.pptx", compression, onMediaError } = props;
|
|
9418
10171
|
const fileName = rawName.toLowerCase().endsWith(".pptx") ? rawName : `${rawName}.pptx`;
|
|
9419
10172
|
const data = await this.exportPresentation({
|
|
9420
10173
|
compression,
|
|
9421
|
-
outputType: this._runtime.writeFileOutputType
|
|
10174
|
+
outputType: this._runtime.writeFileOutputType,
|
|
10175
|
+
onMediaError
|
|
9422
10176
|
});
|
|
9423
10177
|
return await this._runtime.writeFile(fileName, data);
|
|
9424
10178
|
}
|
|
@@ -9666,6 +10420,6 @@ var PptxGenJS = class extends PptxGenJS$1 {
|
|
|
9666
10420
|
}
|
|
9667
10421
|
};
|
|
9668
10422
|
//#endregion
|
|
9669
|
-
export { AXIS_ID_CATEGORY_PRIMARY, AXIS_ID_CATEGORY_SECONDARY, AXIS_ID_SERIES_PRIMARY, AXIS_ID_VALUE_PRIMARY, AXIS_ID_VALUE_SECONDARY, AlignH, AlignV, BARCHART_COLORS, BULLET_TYPES, CHART_TYPE, CRLF, ChartType, DEF_BULLET_MARGIN, DEF_CELL_BORDER, DEF_CELL_MARGIN_IN, DEF_CELL_MARGIN_PT, DEF_CHART_BORDER, DEF_CHART_GRIDLINE, DEF_FONT_COLOR, DEF_FONT_SIZE, DEF_FONT_TITLE_SIZE, DEF_PRES_LAYOUT, DEF_PRES_LAYOUT_NAME, DEF_SHAPE_LINE_COLOR, DEF_SHAPE_SHADOW, DEF_SLIDE_BKGD, DEF_SLIDE_MARGIN_IN, DEF_TEXT_GLOW, DEF_TEXT_SHADOW, EMU, EMU_PER_INCH, EMU_PER_POINT, IMG_BROKEN, IMG_PLAYBTN, IMG_SVG_PLACEHOLDER, LAYOUT_IDX_SERIES_BASE, LETTERS, LINEH_MODIFIER, MASTER_OBJECTS, ONEPT, OutputType, PIECHART_COLORS, PLACEHOLDER_TYPES, POINTS_PER_INCH, PptxGenJS, PptxGenJS as Presentation, PptxGenJS as default, REGEX_HEX_COLOR, SCHEME_COLOR_NAMES, SHAPE_TYPE, SLDNUMFLDID, SLIDE_OBJECT_TYPES, STANDARD_LAYOUTS, SchemeColor, ShapeType, TABLE_STYLE, TEXT_HALIGN, TEXT_VALIGN, emuToInches, emuToPixels, emuToPoints, inchesToEmu, pixelsToEmu, pointsToEmu, textRun, textRuns };
|
|
10423
|
+
export { AXIS_ID_CATEGORY_PRIMARY, AXIS_ID_CATEGORY_SECONDARY, AXIS_ID_SERIES_PRIMARY, AXIS_ID_VALUE_PRIMARY, AXIS_ID_VALUE_SECONDARY, AlignH, AlignV, BARCHART_COLORS, BULLET_TYPES, CHART_TYPE, CONNECTOR_PRESETS, CRLF, ChartType, DEF_BULLET_MARGIN, DEF_CELL_BORDER, DEF_CELL_MARGIN_IN, DEF_CELL_MARGIN_PT, DEF_CHART_BORDER, DEF_CHART_GRIDLINE, DEF_FONT_COLOR, DEF_FONT_SIZE, DEF_FONT_TITLE_SIZE, DEF_PRES_LAYOUT, DEF_PRES_LAYOUT_NAME, DEF_SHAPE_LINE_COLOR, DEF_SHAPE_SHADOW, DEF_SLIDE_BKGD, DEF_SLIDE_MARGIN_IN, DEF_TEXT_GLOW, DEF_TEXT_SHADOW, EMU, EMU_PER_INCH, EMU_PER_POINT, IMG_BROKEN, IMG_PLAYBTN, IMG_SVG_PLACEHOLDER, LAYOUT_IDX_SERIES_BASE, LETTERS, LINEH_MODIFIER, MASTER_OBJECTS, ONEPT, OutputType, PIECHART_COLORS, PLACEHOLDER_TYPES, POINTS_PER_INCH, PptxGenJS, PptxGenJS as Presentation, PptxGenJS as default, REGEX_HEX_COLOR, SCHEME_COLOR_NAMES, SHAPE_TYPE, SLDNUMFLDID, SLIDE_OBJECT_TYPES, STANDARD_LAYOUTS, SchemeColor, ShapeType, TABLE_STYLE, TEXT_HALIGN, TEXT_VALIGN, VALID_SHAPE_PRESETS, coordToEmu, emuToInches, emuToPixels, emuToPoints, inchesToEmu, percentToEmu, pixelsToEmu, pointsToEmu, textRun, textRuns };
|
|
9670
10424
|
|
|
9671
10425
|
//# sourceMappingURL=standalone.js.map
|