@shbernal/pptxgenjs 5.2.0 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{browser-DraPrTLD.js → browser-CzGC6NnM.js} +3 -3
- package/dist/{browser-DraPrTLD.js.map → browser-CzGC6NnM.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-BFXQk67T.js} +30 -9
- package/dist/core-interfaces-BFXQk67T.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-B-mAxCRC.js} +655 -211
- package/dist/pptxgen-B-mAxCRC.js.map +1 -0
- package/dist/{pptxgen-DzBNFPxG.d.ts → pptxgen-Clv3zz3l.d.ts} +2 -2
- package/dist/{pptxgen-DzBNFPxG.d.ts.map → pptxgen-Clv3zz3l.d.ts.map} +1 -1
- package/dist/standalone.d.ts +288 -30
- package/dist/standalone.d.ts.map +1 -1
- package/dist/standalone.js +724 -215
- 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-BPRPrYRg.d.ts} +289 -31
- package/dist/units-BPRPrYRg.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
|
@@ -1,32 +1,25 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { A as DEF_TEXT_GLOW, B as ONEPT, D as DEF_SHAPE_SHADOW, G as SCHEME_COLOR_NAMES, H as PIECHART_COLORS, I as LAYOUT_IDX_SERIES_BASE, K as SHAPE_TYPE, L as LETTERS, M as EMU, N as IMG_BROKEN, R as LINEH_MODIFIER, T as DEF_PRES_LAYOUT_NAME, U as PLACEHOLDER_TYPES, V as OutputType, W as REGEX_HEX_COLOR, X as ShapeType, Y as SchemeColor, _ as DEF_CELL_MARGIN_IN, a as AXIS_ID_SERIES_PRIMARY, b as DEF_CHART_GRIDLINE, c as AlignH, f as CHART_TYPE, g as DEF_CELL_BORDER, i as AXIS_ID_CATEGORY_SECONDARY, j as DEF_TEXT_SHADOW, k as DEF_SLIDE_MARGIN_IN, l as AlignV, m as ChartType, o as AXIS_ID_VALUE_PRIMARY, q as SLDNUMFLDID, r as AXIS_ID_CATEGORY_PRIMARY, s as AXIS_ID_VALUE_SECONDARY, u as BARCHART_COLORS, w as DEF_PRES_LAYOUT, x as DEF_FONT_COLOR, y as DEF_CHART_BORDER } from "./core-interfaces-
|
|
1
|
+
import { a as coordToEmu, i as STANDARD_LAYOUTS, l as inchesToEmu, o as emuToInches } from "./units-BMrBTU0-.js";
|
|
2
|
+
import { A as DEF_TEXT_GLOW, B as ONEPT, D as DEF_SHAPE_SHADOW, G as SCHEME_COLOR_NAMES, H as PIECHART_COLORS, I as LAYOUT_IDX_SERIES_BASE, K as SHAPE_TYPE, L as LETTERS, M as EMU, N as IMG_BROKEN, R as LINEH_MODIFIER, T as DEF_PRES_LAYOUT_NAME, U as PLACEHOLDER_TYPES, V as OutputType, W as REGEX_HEX_COLOR, X as ShapeType, Y as SchemeColor, _ as DEF_CELL_MARGIN_IN, a as AXIS_ID_SERIES_PRIMARY, b as DEF_CHART_GRIDLINE, c as AlignH, et as VALID_SHAPE_PRESETS, f as CHART_TYPE, g as DEF_CELL_BORDER, i as AXIS_ID_CATEGORY_SECONDARY, j as DEF_TEXT_SHADOW, k as DEF_SLIDE_MARGIN_IN, l as AlignV, m as ChartType, o as AXIS_ID_VALUE_PRIMARY, q as SLDNUMFLDID, r as AXIS_ID_CATEGORY_PRIMARY, s as AXIS_ID_VALUE_SECONDARY, u as BARCHART_COLORS, w as DEF_PRES_LAYOUT, x as DEF_FONT_COLOR, y as DEF_CHART_BORDER } from "./core-interfaces-BFXQk67T.js";
|
|
3
3
|
import JSZip from "jszip";
|
|
4
4
|
//#region src/gen-utils.ts
|
|
5
5
|
/**
|
|
6
6
|
* PptxGenJS: Utility Methods
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
-
*
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {'X' | 'Y'} xyDir -
|
|
16
|
-
* @param {PresLayout} layout - presentation layout
|
|
17
|
-
* @returns {
|
|
9
|
+
* Resolve a user `Coord` (x/y/w/h) to EMU — the single user-coordinate → EMU boundary.
|
|
10
|
+
* - bare `number` → **inches** (no magnitude guessing); `"<n>%"` → percent of the slide axis;
|
|
11
|
+
* `"<n>in"`/`"<n>pt"`/`"<n>emu"` → explicit units (see {@link Coord} / {@link coordToEmu})
|
|
12
|
+
* - `null`/`undefined` → 0 (callers may omit a coordinate)
|
|
13
|
+
* - throws on a non-finite number rather than silently collapsing the object to zero size
|
|
14
|
+
* @param {Coord|null|undefined} size - user coordinate
|
|
15
|
+
* @param {'X' | 'Y'} xyDir - axis (selects slide width vs height for percentages)
|
|
16
|
+
* @param {PresLayout} layout - presentation layout (EMU dimensions)
|
|
17
|
+
* @returns {Emu} resolved EMU value
|
|
18
18
|
*/
|
|
19
19
|
function getSmartParseNumber(size, xyDir, layout) {
|
|
20
|
-
if (
|
|
20
|
+
if (size === null || size === void 0) return 0;
|
|
21
21
|
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).`);
|
|
22
|
-
|
|
23
|
-
if (typeof size === "number" && size >= 100) return size;
|
|
24
|
-
if (typeof size === "string" && size.includes("%")) {
|
|
25
|
-
if (xyDir && xyDir === "X") return Math.round(parseFloat(size) / 100 * layout.width);
|
|
26
|
-
if (xyDir && xyDir === "Y") return Math.round(parseFloat(size) / 100 * layout.height);
|
|
27
|
-
return Math.round(parseFloat(size) / 100 * layout.width);
|
|
28
|
-
}
|
|
29
|
-
return 0;
|
|
22
|
+
return coordToEmu(size, xyDir === "Y" ? layout.height : layout.width);
|
|
30
23
|
}
|
|
31
24
|
/**
|
|
32
25
|
* Basic UUID Generator Adapted
|
|
@@ -99,14 +92,16 @@ function getDuplicateObjectNames(names) {
|
|
|
99
92
|
return Array.from(dupes);
|
|
100
93
|
}
|
|
101
94
|
/**
|
|
102
|
-
* Convert inches into EMU
|
|
103
|
-
*
|
|
104
|
-
* @
|
|
95
|
+
* Convert inches into EMU.
|
|
96
|
+
* - accepts a number (inches) or a numeric/`"<n>in"` string
|
|
97
|
+
* - no magnitude guessing: values are always treated as inches (use {@link coordToEmu} for
|
|
98
|
+
* user coordinates that may carry other units)
|
|
99
|
+
* @param {number|string} inches - inches as number or string
|
|
100
|
+
* @returns {Emu} EMU value
|
|
105
101
|
*/
|
|
106
102
|
function inch2Emu(inches) {
|
|
107
|
-
if (typeof inches === "number" && inches > 100) return inches;
|
|
108
103
|
if (typeof inches === "string") inches = Number(inches.replace(/in*/gi, ""));
|
|
109
|
-
return
|
|
104
|
+
return inchesToEmu(inches);
|
|
110
105
|
}
|
|
111
106
|
/**
|
|
112
107
|
* Convert `pt` into points (using `ONEPT`)
|
|
@@ -118,6 +113,33 @@ function valToPts(pt) {
|
|
|
118
113
|
return isNaN(points) ? 0 : Math.round(points * ONEPT);
|
|
119
114
|
}
|
|
120
115
|
/**
|
|
116
|
+
* Convert a transparency percentage (0-100) into a schema-valid `<a:alpha>` value
|
|
117
|
+
* (ST_PositiveFixedPercentage, 0-100000). Out-of-range transparency yields an
|
|
118
|
+
* alpha that PowerPoint rejects as needing repair, so clamp into range and warn.
|
|
119
|
+
*/
|
|
120
|
+
function transparencyToAlpha(transparency) {
|
|
121
|
+
const pct = Math.min(100, Math.max(0, transparency));
|
|
122
|
+
if (pct !== transparency) console.warn(`Warning: transparency ${transparency} is outside the valid range 0-100; using ${pct}.`);
|
|
123
|
+
return Math.round((100 - pct) * 1e3);
|
|
124
|
+
}
|
|
125
|
+
/** Convert an opacity (0-1) into a schema-valid `<a:alpha>` value (0-100000); clamps + warns on out-of-range input. */
|
|
126
|
+
function opacityToAlpha(opacity) {
|
|
127
|
+
const o = Math.min(1, Math.max(0, opacity));
|
|
128
|
+
if (o !== opacity) console.warn(`Warning: opacity ${opacity} is outside the valid range 0-1; using ${o}.`);
|
|
129
|
+
return Math.round(o * 1e5);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Convert a line width (points) to EMU clamped into ST_LineWidth (0..20116800 EMU,
|
|
133
|
+
* i.e. 0-1584pt). Out-of-range widths make PowerPoint report the package as needing
|
|
134
|
+
* repair, so clamp into range and warn.
|
|
135
|
+
*/
|
|
136
|
+
function lineWidthToEmu(widthPts) {
|
|
137
|
+
const raw = valToPts(widthPts);
|
|
138
|
+
const clamped = Math.min(20116800, Math.max(0, raw));
|
|
139
|
+
if (clamped !== raw) console.warn(`Warning: line width ${widthPts} is outside the valid range 0-1584pt; using ${clamped / ONEPT}.`);
|
|
140
|
+
return clamped;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
121
143
|
* Convert degrees (0..360) to PowerPoint `rot` value
|
|
122
144
|
* @param {number} d degrees
|
|
123
145
|
* @returns {number} calculated `rot` value
|
|
@@ -165,8 +187,10 @@ function createColorElement(colorStr, innerElements) {
|
|
|
165
187
|
}
|
|
166
188
|
let colorVal = (colorStr || "").replace("#", "");
|
|
167
189
|
if (/^[0-9a-fA-F]{8}$/.test(colorVal)) {
|
|
168
|
-
|
|
169
|
-
|
|
190
|
+
if (!innerElements?.includes("<a:alpha")) {
|
|
191
|
+
const alphaHex = colorVal.slice(6, 8);
|
|
192
|
+
innerElements = `<a:alpha val="${Math.round(parseInt(alphaHex, 16) / 255 * 1e5)}"/>${innerElements || ""}`;
|
|
193
|
+
}
|
|
170
194
|
colorVal = colorVal.slice(0, 6);
|
|
171
195
|
}
|
|
172
196
|
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") {
|
|
@@ -192,12 +216,38 @@ function createGlowElement(options, defaults) {
|
|
|
192
216
|
};
|
|
193
217
|
const size = Math.round(opts.size * ONEPT);
|
|
194
218
|
const color = opts.color || "000000";
|
|
195
|
-
const opacity =
|
|
219
|
+
const opacity = opacityToAlpha(opts.opacity ?? 0);
|
|
196
220
|
strXml += `<a:glow rad="${size}">`;
|
|
197
221
|
strXml += createColorElement(color, `<a:alpha val="${opacity}"/>`);
|
|
198
222
|
strXml += "</a:glow>";
|
|
199
223
|
return strXml;
|
|
200
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Creates an `a:outerShdw`/`a:innerShdw` element for a text run or shape.
|
|
227
|
+
* Returns the shadow element only (no wrapping `a:effectLst`) so callers can
|
|
228
|
+
* combine it with other effects (e.g. glow) inside a single `a:effectLst`.
|
|
229
|
+
* @param {ShadowProps} options shadow properties
|
|
230
|
+
* @param {ShadowProps} defaults defaults for unspecified properties in `options`
|
|
231
|
+
* @see http://officeopenxml.com/drwSp-effects.php
|
|
232
|
+
* @returns {string} XML string, or '' when type is 'none'
|
|
233
|
+
*/
|
|
234
|
+
function createShadowElement$1(options, defaults) {
|
|
235
|
+
const opts = {
|
|
236
|
+
...defaults,
|
|
237
|
+
...options
|
|
238
|
+
};
|
|
239
|
+
if (opts.type === "none") return "";
|
|
240
|
+
const type = opts.type || "outer";
|
|
241
|
+
const blur = valToPts(opts.blur ?? 0);
|
|
242
|
+
const offset = valToPts(opts.offset ?? 0);
|
|
243
|
+
const angle = Math.round((opts.angle ?? 0) * 6e4);
|
|
244
|
+
const opacity = Math.round((opts.opacity ?? .75) * 1e5);
|
|
245
|
+
const color = opts.color || "000000";
|
|
246
|
+
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}">`;
|
|
247
|
+
strXml += createColorElement(color, `<a:alpha val="${opacity}"/>`);
|
|
248
|
+
strXml += `</a:${type}Shdw>`;
|
|
249
|
+
return strXml;
|
|
250
|
+
}
|
|
201
251
|
function boolToXml(value) {
|
|
202
252
|
return value ? "1" : "0";
|
|
203
253
|
}
|
|
@@ -208,8 +258,8 @@ function normalizeGradientAngle(angle) {
|
|
|
208
258
|
}
|
|
209
259
|
function gradientStopColorAdjustments(stop) {
|
|
210
260
|
let internalElements = "";
|
|
211
|
-
if (stop.alpha) internalElements += `<a:alpha val="${
|
|
212
|
-
if (stop.transparency) internalElements += `<a:alpha val="${
|
|
261
|
+
if (stop.alpha) internalElements += `<a:alpha val="${transparencyToAlpha(stop.alpha)}"/>`;
|
|
262
|
+
if (stop.transparency) internalElements += `<a:alpha val="${transparencyToAlpha(stop.transparency)}"/>`;
|
|
213
263
|
return internalElements;
|
|
214
264
|
}
|
|
215
265
|
function normalizeGradientStops(stops) {
|
|
@@ -259,6 +309,17 @@ function genXmlPatternFill(pattern) {
|
|
|
259
309
|
* @param {Color | ShapeFillProps | ShapeLineProps} props fill props
|
|
260
310
|
* @returns XML string
|
|
261
311
|
*/
|
|
312
|
+
/**
|
|
313
|
+
* Map a friendly `LineCap` value to the OOXML `cap` attribute value (`flat`/`sq`/`rnd`).
|
|
314
|
+
* @param {LineCap} [lineCap] - line cap style (defaults to `flat`)
|
|
315
|
+
* @returns {string} value for the `cap` attribute on `<a:ln>`
|
|
316
|
+
*/
|
|
317
|
+
function createLineCap(lineCap) {
|
|
318
|
+
if (!lineCap || lineCap === "flat") return "flat";
|
|
319
|
+
else if (lineCap === "square") return "sq";
|
|
320
|
+
else if (lineCap === "round") return "rnd";
|
|
321
|
+
else throw new Error(`Invalid line cap: ${String(lineCap)}`);
|
|
322
|
+
}
|
|
262
323
|
function genXmlColorSelection(props) {
|
|
263
324
|
let fillType = "solid";
|
|
264
325
|
let colorVal = "";
|
|
@@ -269,8 +330,8 @@ function genXmlColorSelection(props) {
|
|
|
269
330
|
else {
|
|
270
331
|
if (props.type) fillType = props.type;
|
|
271
332
|
if (props.color) colorVal = props.color;
|
|
272
|
-
if (props.alpha) internalElements += `<a:alpha val="${
|
|
273
|
-
if (props.transparency) internalElements += `<a:alpha val="${
|
|
333
|
+
if (props.alpha) internalElements += `<a:alpha val="${transparencyToAlpha(props.alpha)}"/>`;
|
|
334
|
+
if (props.transparency) internalElements += `<a:alpha val="${transparencyToAlpha(props.transparency)}"/>`;
|
|
274
335
|
}
|
|
275
336
|
switch (fillType) {
|
|
276
337
|
case "solid":
|
|
@@ -864,6 +925,28 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
|
|
|
864
925
|
return tableRowSlides;
|
|
865
926
|
}
|
|
866
927
|
/**
|
|
928
|
+
* Convert a computed CSS border (width string + color string) from `getComputedStyle` into a
|
|
929
|
+
* pptx `BorderProps`.
|
|
930
|
+
*
|
|
931
|
+
* Preserves *fractional* widths: a hairline CSS border such as `0.5px` must not be rounded to
|
|
932
|
+
* `0pt` and silently vanish — the table serializer (`valToPts`) emits fractional points just
|
|
933
|
+
* fine, so there is no reason to integer-round here (upstream gitbrent/PptxGenJS#1235). A
|
|
934
|
+
* computed width of `0` (or a non-finite value) yields `{ type: 'none' }` so we never emit a
|
|
935
|
+
* zero-width line.
|
|
936
|
+
* @param {string} widthStr - computed `border-<side>-width`, e.g. `"0.5px"`
|
|
937
|
+
* @param {string} colorStr - computed `border-<side>-color`, e.g. `"rgb(102, 102, 102)"`
|
|
938
|
+
* @returns {BorderProps} border props for the cell side
|
|
939
|
+
*/
|
|
940
|
+
function htmlBorderToProps(widthStr, colorStr) {
|
|
941
|
+
const pt = Number(String(widthStr).replace("px", ""));
|
|
942
|
+
if (!isFinite(pt) || pt <= 0) return { type: "none" };
|
|
943
|
+
const arrRGB = String(colorStr).replace(/\s+/gi, "").replace("rgba(", "").replace("rgb(", "").replace(")", "").split(",");
|
|
944
|
+
return {
|
|
945
|
+
pt,
|
|
946
|
+
color: rgbToHex(Number(arrRGB[0]), Number(arrRGB[1]), Number(arrRGB[2]))
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
867
950
|
* Reproduces an HTML table as a PowerPoint table - including column widths, style, etc. - creates 1 or more slides as needed
|
|
868
951
|
* @param {TableToSlidesHost} pptx - pptxgenjs instance
|
|
869
952
|
* @param {string} tabEleId - HTMLElementID of the table
|
|
@@ -1011,12 +1094,8 @@ function genTableToSlides(pptx, tabEleId, options = {}, masterSlide) {
|
|
|
1011
1094
|
"bottom",
|
|
1012
1095
|
"left"
|
|
1013
1096
|
].forEach((val, idxb) => {
|
|
1014
|
-
const
|
|
1015
|
-
|
|
1016
|
-
cellBorder[idxb] = {
|
|
1017
|
-
pt: intBorderW,
|
|
1018
|
-
color: rgbToHex(Number(arrRGB[0]), Number(arrRGB[1]), Number(arrRGB[2]))
|
|
1019
|
-
};
|
|
1097
|
+
const style = window.getComputedStyle(cell);
|
|
1098
|
+
cellBorder[idxb] = htmlBorderToProps(style.getPropertyValue("border-" + val + "-width"), style.getPropertyValue("border-" + val + "-color"));
|
|
1020
1099
|
});
|
|
1021
1100
|
cellOpts.border = cellBorder;
|
|
1022
1101
|
}
|
|
@@ -1086,6 +1165,8 @@ function genTableToSlides(pptx, tabEleId, options = {}, masterSlide) {
|
|
|
1086
1165
|
*/
|
|
1087
1166
|
/** counter for included charts (used for index in their filenames) */
|
|
1088
1167
|
let _chartCounter = 0;
|
|
1168
|
+
/** DPI PowerPoint assumes when sizing an inserted raster image (natural pixels / 96 == inches) */
|
|
1169
|
+
const IMAGE_NATURAL_DPI = 96;
|
|
1089
1170
|
function normalizeBorderTuple(border) {
|
|
1090
1171
|
return Array.isArray(border) ? border : [
|
|
1091
1172
|
border,
|
|
@@ -1122,6 +1203,26 @@ function createSlideMaster(props, target) {
|
|
|
1122
1203
|
if (props.slideNumber && typeof props.slideNumber === "object") target._slideNumberProps = props.slideNumber;
|
|
1123
1204
|
}
|
|
1124
1205
|
/**
|
|
1206
|
+
* Round and clamp an integer chart percentage/angle option into a schema-valid range.
|
|
1207
|
+
*
|
|
1208
|
+
* Several chart attributes are bounded integer types whose out-of-range values make
|
|
1209
|
+
* PowerPoint report the package as needing repair: `<c:overlap>` (ST_Overlap, -100..100),
|
|
1210
|
+
* `<c:gapWidth>`/`<c:gapDepth>` (ST_GapAmount, 0..500), `<c:holeSize>` (ST_HoleSize, 10..90)
|
|
1211
|
+
* and `<c:firstSliceAng>` (ST_FirstSliceAng, 0..360). Missing/non-numeric input returns
|
|
1212
|
+
* `undefined` so the caller can apply its own default; an out-of-range value is clamped
|
|
1213
|
+
* and a warning is emitted (per the library's warn-rather-than-degrade policy).
|
|
1214
|
+
* @param value - caller-supplied option value
|
|
1215
|
+
* @param min - inclusive lower bound
|
|
1216
|
+
* @param max - inclusive upper bound
|
|
1217
|
+
* @param name - option name, for the warning message
|
|
1218
|
+
*/
|
|
1219
|
+
function clampChartPct(value, min, max, name) {
|
|
1220
|
+
if (typeof value !== "number" || isNaN(value)) return void 0;
|
|
1221
|
+
const clamped = Math.min(max, Math.max(min, Math.round(value)));
|
|
1222
|
+
if (clamped !== value) console.warn(`Warning: ${name} ${value} is outside the valid range ${min}-${max}; using ${clamped}.`);
|
|
1223
|
+
return clamped;
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1125
1226
|
* Generate the chart based on input data.
|
|
1126
1227
|
* OOXML Chart Spec: ISO/IEC 29500-1:2016(E)
|
|
1127
1228
|
*
|
|
@@ -1293,7 +1394,13 @@ function addChartDefinition(target, type, data, opt) {
|
|
|
1293
1394
|
"marker",
|
|
1294
1395
|
"filled"
|
|
1295
1396
|
].includes(options.radarStyle || "")) options.radarStyle = "standard";
|
|
1296
|
-
|
|
1397
|
+
{
|
|
1398
|
+
const rawSymbolSize = options.lineDataSymbolSize;
|
|
1399
|
+
const hasSymbolSize = rawSymbolSize != null && !isNaN(rawSymbolSize);
|
|
1400
|
+
const symbolSize = Math.min(72, Math.max(2, Math.round(hasSymbolSize ? rawSymbolSize : 6)));
|
|
1401
|
+
if (hasSymbolSize && symbolSize !== rawSymbolSize) console.warn(`Warning: lineDataSymbolSize ${rawSymbolSize} is outside the valid marker size range (integer 2-72); using ${symbolSize}.`);
|
|
1402
|
+
options.lineDataSymbolSize = symbolSize;
|
|
1403
|
+
}
|
|
1297
1404
|
options.lineDataSymbolLineSize = options.lineDataSymbolLineSize && !isNaN(options.lineDataSymbolLineSize) ? valToPts(options.lineDataSymbolLineSize) : valToPts(.75);
|
|
1298
1405
|
const chartLayout = options.layout;
|
|
1299
1406
|
if (chartLayout) [
|
|
@@ -1343,8 +1450,11 @@ function addChartDefinition(target, type, data, opt) {
|
|
|
1343
1450
|
options.v3DRotY = typeof options.v3DRotY === "number" && !isNaN(options.v3DRotY) && options.v3DRotY >= 0 && options.v3DRotY <= 360 ? options.v3DRotY : 30;
|
|
1344
1451
|
options.v3DRAngAx = options.v3DRAngAx || !options.v3DRAngAx ? options.v3DRAngAx : true;
|
|
1345
1452
|
options.v3DPerspective = typeof options.v3DPerspective === "number" && !isNaN(options.v3DPerspective) && options.v3DPerspective >= 0 && options.v3DPerspective <= 240 ? options.v3DPerspective : 30;
|
|
1346
|
-
options.barGapWidthPct =
|
|
1347
|
-
options.barGapDepthPct =
|
|
1453
|
+
options.barGapWidthPct = clampChartPct(options.barGapWidthPct, 0, 500, "barGapWidthPct") ?? 150;
|
|
1454
|
+
options.barGapDepthPct = clampChartPct(options.barGapDepthPct, 0, 500, "barGapDepthPct") ?? 150;
|
|
1455
|
+
options.barOverlapPct = clampChartPct(options.barOverlapPct, -100, 100, "barOverlapPct");
|
|
1456
|
+
options.holeSize = clampChartPct(options.holeSize, 10, 90, "holeSize");
|
|
1457
|
+
options.firstSliceAng = clampChartPct(options.firstSliceAng, 0, 360, "firstSliceAng");
|
|
1348
1458
|
options.chartColors = Array.isArray(options.chartColors) ? options.chartColors : options._type === "pie" || options._type === "doughnut" ? PIECHART_COLORS : BARCHART_COLORS;
|
|
1349
1459
|
options.chartColorsOpacity = options.chartColorsOpacity && !isNaN(options.chartColorsOpacity) ? options.chartColorsOpacity : void 0;
|
|
1350
1460
|
options.border = options.border && typeof options.border === "object" ? options.border : void 0;
|
|
@@ -1433,16 +1543,29 @@ function addImageDefinition(target, opt) {
|
|
|
1433
1543
|
else if (strImageData?.toLowerCase().includes("image/svg+xml")) strImgExtn = "svg";
|
|
1434
1544
|
newObject._type = "image";
|
|
1435
1545
|
newObject.image = strImagePath || "preencoded.png";
|
|
1546
|
+
let defWidth = intWidth;
|
|
1547
|
+
let defHeight = intHeight;
|
|
1548
|
+
if ((!intWidth || !intHeight) && strImageData && strImgExtn !== "svg") {
|
|
1549
|
+
const natural = getImageSizeFromBase64(strImageData);
|
|
1550
|
+
if (natural) {
|
|
1551
|
+
if (!intWidth && !intHeight) {
|
|
1552
|
+
defWidth = natural.w / IMAGE_NATURAL_DPI;
|
|
1553
|
+
defHeight = natural.h / IMAGE_NATURAL_DPI;
|
|
1554
|
+
} else if (typeof intWidth === "number" && intWidth && !intHeight) defHeight = intWidth * (natural.h / natural.w);
|
|
1555
|
+
else if (typeof intHeight === "number" && intHeight && !intWidth) defWidth = intHeight * (natural.w / natural.h);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1436
1558
|
const objectOptions = {
|
|
1437
1559
|
x: intPosX || 0,
|
|
1438
1560
|
y: intPosY || 0,
|
|
1439
|
-
w:
|
|
1440
|
-
h:
|
|
1561
|
+
w: defWidth || 1,
|
|
1562
|
+
h: defHeight || 1,
|
|
1441
1563
|
altText: opt.altText || "",
|
|
1442
1564
|
rounding: typeof opt.rounding === "boolean" ? opt.rounding : false,
|
|
1443
1565
|
shape: opt.shape,
|
|
1444
1566
|
points: opt.points,
|
|
1445
1567
|
rectRadius: opt.rectRadius,
|
|
1568
|
+
shapeAdjust: opt.shapeAdjust,
|
|
1446
1569
|
sizing,
|
|
1447
1570
|
placeholder: opt.placeholder,
|
|
1448
1571
|
rotate: opt.rotate || 0,
|
|
@@ -1451,6 +1574,7 @@ function addImageDefinition(target, opt) {
|
|
|
1451
1574
|
transparency: opt.transparency || 0,
|
|
1452
1575
|
duotone: opt.duotone,
|
|
1453
1576
|
objectName,
|
|
1577
|
+
objectLock: opt.objectLock,
|
|
1454
1578
|
shadow: correctShadowOptions(opt.shadow)
|
|
1455
1579
|
};
|
|
1456
1580
|
newObject.options = objectOptions;
|
|
@@ -1480,7 +1604,10 @@ function addImageDefinition(target, opt) {
|
|
|
1480
1604
|
});
|
|
1481
1605
|
newObject.imageRid = imageRelId + 1;
|
|
1482
1606
|
} else {
|
|
1483
|
-
const dupeItem = target._relsMedia.find((item) =>
|
|
1607
|
+
const dupeItem = target._relsMedia.find((item) => {
|
|
1608
|
+
if (item.isDuplicate || !item.Target || item.type !== "image/" + strImgExtn) return false;
|
|
1609
|
+
return strImagePath ? item.path === strImagePath : !!strImageData && item.data === strImageData;
|
|
1610
|
+
});
|
|
1484
1611
|
target._relsMedia.push({
|
|
1485
1612
|
path: strImagePath || "preencoded." + strImgExtn,
|
|
1486
1613
|
type: "image/" + strImgExtn,
|
|
@@ -1538,6 +1665,7 @@ function addMediaDefinition(target, opt) {
|
|
|
1538
1665
|
slideData.options.h = intSizeY;
|
|
1539
1666
|
slideData.options.objectName = objectName;
|
|
1540
1667
|
if (opt.altText) slideData.options.altText = opt.altText;
|
|
1668
|
+
if (opt.objectLock) slideData.options.objectLock = opt.objectLock;
|
|
1541
1669
|
/**
|
|
1542
1670
|
* NOTE:
|
|
1543
1671
|
* - rId starts at 2 (hence the intRels+1 below) as slideLayout.xml is rId=1!
|
|
@@ -1567,7 +1695,10 @@ function addMediaDefinition(target, opt) {
|
|
|
1567
1695
|
Target: `../media/image-${target._slideNum}-${target._relsMedia.length + 1}.png`
|
|
1568
1696
|
});
|
|
1569
1697
|
} else {
|
|
1570
|
-
const dupeItem = target._relsMedia.find((item) =>
|
|
1698
|
+
const dupeItem = target._relsMedia.find((item) => {
|
|
1699
|
+
if (item.isDuplicate || !item.Target || item.type !== strType + "/" + strExtn) return false;
|
|
1700
|
+
return strPath ? item.path === strPath : !!strData && item.data === strData;
|
|
1701
|
+
});
|
|
1571
1702
|
const relId1 = getNewRelId(target);
|
|
1572
1703
|
target._relsMedia.push({
|
|
1573
1704
|
path: strPath || "preencoded" + strExtn,
|
|
@@ -1632,12 +1763,14 @@ function addShapeDefinition(target, shapeName, opts) {
|
|
|
1632
1763
|
const options = typeof opts === "object" ? opts : {};
|
|
1633
1764
|
options.line = options.line || { type: "none" };
|
|
1634
1765
|
options.shadow = correctShadowOptions(options.shadow);
|
|
1766
|
+
const resolvedShapeName = typeof shapeName === "string" && SHAPE_NAME_ALIASES[shapeName] ? SHAPE_NAME_ALIASES[shapeName] : shapeName;
|
|
1635
1767
|
const newObject = {
|
|
1636
1768
|
_type: "text",
|
|
1637
|
-
shape:
|
|
1769
|
+
shape: resolvedShapeName || "rect",
|
|
1638
1770
|
options
|
|
1639
1771
|
};
|
|
1640
1772
|
if (!shapeName) throw new Error("Missing/Invalid shape parameter! Example: `addShape(pptxgen.shapes.LINE, {x:1, y:1, w:1, h:1});`");
|
|
1773
|
+
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.`);
|
|
1641
1774
|
const newLineOpts = {
|
|
1642
1775
|
type: options.line.type || "solid",
|
|
1643
1776
|
color: options.line.color || "333333",
|
|
@@ -1734,9 +1867,8 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
|
|
|
1734
1867
|
}
|
|
1735
1868
|
arrRows.push(newRow);
|
|
1736
1869
|
});
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
if (opt.h) opt.h = getSmartParseNumber(opt.h, "Y", presLayout);
|
|
1870
|
+
if (opt.x === void 0 || opt.x === null) opt.x = .5;
|
|
1871
|
+
if (opt.y === void 0 || opt.y === null) opt.y = .5;
|
|
1740
1872
|
opt.fontSize = opt.fontSize || 12;
|
|
1741
1873
|
opt.margin = opt.margin === 0 || opt.margin ? opt.margin : DEF_CELL_MARGIN_IN;
|
|
1742
1874
|
if (typeof opt.margin === "number") opt.margin = [
|
|
@@ -1805,12 +1937,7 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
|
|
|
1805
1937
|
console.warn("addTable: mismatch: (colW.length != data.length) Therefore, defaulting to evenly distributed col widths.");
|
|
1806
1938
|
opt.colW = void 0;
|
|
1807
1939
|
}
|
|
1808
|
-
} else if (opt.w) opt.w =
|
|
1809
|
-
else opt.w = Math.floor((presLayout._sizeW || presLayout.width) / EMU - arrTableMargin[1] - arrTableMargin[3]);
|
|
1810
|
-
if (opt.x && opt.x < 20) opt.x = inch2Emu(opt.x);
|
|
1811
|
-
if (opt.y && opt.y < 20) opt.y = inch2Emu(opt.y);
|
|
1812
|
-
if (opt.w && typeof opt.w === "number" && opt.w < 20) opt.w = inch2Emu(opt.w);
|
|
1813
|
-
if (opt.h && typeof opt.h === "number" && opt.h < 20) opt.h = inch2Emu(opt.h);
|
|
1940
|
+
} else if (opt.w) {} else opt.w = Math.floor((presLayout._sizeW || presLayout.width) / EMU - arrTableMargin[1] - arrTableMargin[3]);
|
|
1814
1941
|
arrRows.forEach((row) => {
|
|
1815
1942
|
row.forEach((cell, idy) => {
|
|
1816
1943
|
if (typeof cell === "number" || typeof cell === "string") row[idy] = {
|
|
@@ -1838,7 +1965,7 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
|
|
|
1838
1965
|
if (opt.autoPageRepeatHeader) opt._arrObjTabHeadRows = arrRows.filter((_row, idx) => idx < (opt.autoPageHeaderRows || 1));
|
|
1839
1966
|
getSlidesForTableRows(arrRows, opt, presLayout, slideLayout).forEach((slide, idx) => {
|
|
1840
1967
|
if (!getSlide(target._slideNum + idx)) slides.push(addSlide({ masterName: slideLayout?._name || void 0 }));
|
|
1841
|
-
if (idx > 0) opt.y =
|
|
1968
|
+
if (idx > 0) opt.y = opt.autoPageSlideStartY || opt.newSlideStartY || arrTableMargin[0];
|
|
1842
1969
|
{
|
|
1843
1970
|
const newSlide = getSlide(target._slideNum + idx);
|
|
1844
1971
|
opt.autoPage = false;
|
|
@@ -1910,6 +2037,10 @@ function addTextDefinition(target, text, opts, isPlaceholder) {
|
|
|
1910
2037
|
itemOpts._bodyProp.anchor = !itemOpts.placeholder ? "ctr" : void 0;
|
|
1911
2038
|
itemOpts._bodyProp.vert = itemOpts.vert;
|
|
1912
2039
|
itemOpts._bodyProp.wrap = typeof itemOpts.wrap === "boolean" ? itemOpts.wrap : true;
|
|
2040
|
+
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)");
|
|
2041
|
+
else itemOpts._bodyProp.numCol = Math.round(itemOpts.columns);
|
|
2042
|
+
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)");
|
|
2043
|
+
else itemOpts._bodyProp.spcCol = valToPts(itemOpts.columnSpacing);
|
|
1913
2044
|
if (itemOpts.inset && !isNaN(Number(itemOpts.inset)) || itemOpts.inset === 0) {
|
|
1914
2045
|
itemOpts._bodyProp.lIns = inch2Emu(itemOpts.inset);
|
|
1915
2046
|
itemOpts._bodyProp.rIns = inch2Emu(itemOpts.inset);
|
|
@@ -2419,6 +2550,22 @@ async function createExcelWorksheet(chartObject, zip) {
|
|
|
2419
2550
|
});
|
|
2420
2551
|
}
|
|
2421
2552
|
/**
|
|
2553
|
+
* Emit the `<a:latin>/<a:ea>/<a:cs>` font trio for a chart text run.
|
|
2554
|
+
*
|
|
2555
|
+
* In DrawingML run properties a typeface applies only to the script class of
|
|
2556
|
+
* its element: `<a:latin>` covers Latin/ASCII, `<a:ea>` covers East Asian, and
|
|
2557
|
+
* `<a:cs>` covers complex scripts. Emitting `<a:latin>` alone leaves East Asian
|
|
2558
|
+
* (e.g. Chinese) and complex-script glyphs falling back to the theme font, so a
|
|
2559
|
+
* user-specified font never takes effect for that text — most visibly on
|
|
2560
|
+
* PowerPoint for Mac. Stamping the same typeface onto all three classes is what
|
|
2561
|
+
* choosing a font in PowerPoint's UI does (upstream gitbrent/PptxGenJS#1420).
|
|
2562
|
+
* @param {string} typeface - font face name
|
|
2563
|
+
* @return {string} `<a:latin/><a:ea/><a:cs/>` XML
|
|
2564
|
+
*/
|
|
2565
|
+
function createChartTextFonts(typeface) {
|
|
2566
|
+
return `<a:latin typeface="${typeface}"/><a:ea typeface="${typeface}"/><a:cs typeface="${typeface}"/>`;
|
|
2567
|
+
}
|
|
2568
|
+
/**
|
|
2422
2569
|
* Main entry point method for create charts
|
|
2423
2570
|
* @see: http://www.datypic.com/sc/ooxml/s-dml-chart.xsd.html
|
|
2424
2571
|
* @param {ISlideRelChart} rel - chart object
|
|
@@ -2428,6 +2575,10 @@ function makeXmlCharts(rel) {
|
|
|
2428
2575
|
let strXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
|
|
2429
2576
|
let usesSecondaryValAxis = false;
|
|
2430
2577
|
let usesSecondaryCatAxis = false;
|
|
2578
|
+
let primaryCatAxisValType = null;
|
|
2579
|
+
let secondaryCatAxisValType = null;
|
|
2580
|
+
let primaryCatAxisHasCategoryChart = false;
|
|
2581
|
+
let secondaryCatAxisHasCategoryChart = false;
|
|
2431
2582
|
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\">";
|
|
2432
2583
|
strXml += "<c:date1904 val=\"0\"/>";
|
|
2433
2584
|
strXml += `<c:roundedCorners val="${rel.opts.chartArea.roundedCorners ? "1" : "0"}"/>`;
|
|
@@ -2440,6 +2591,8 @@ function makeXmlCharts(rel) {
|
|
|
2440
2591
|
fontSize: rel.opts.titleFontSize || 18,
|
|
2441
2592
|
titleAlign: rel.opts.titleAlign,
|
|
2442
2593
|
titleBold: rel.opts.titleBold,
|
|
2594
|
+
titleItalic: rel.opts.titleItalic,
|
|
2595
|
+
titleUnderline: rel.opts.titleUnderline,
|
|
2443
2596
|
titlePos: rel.opts.titlePos,
|
|
2444
2597
|
titleRotate: rel.opts.titleRotate
|
|
2445
2598
|
}, rel.opts.x, rel.opts.y);
|
|
@@ -2472,18 +2625,37 @@ function makeXmlCharts(rel) {
|
|
|
2472
2625
|
const catAxisId = options.secondaryCatAxis ? AXIS_ID_CATEGORY_SECONDARY : AXIS_ID_CATEGORY_PRIMARY;
|
|
2473
2626
|
usesSecondaryValAxis = usesSecondaryValAxis || options.secondaryValAxis;
|
|
2474
2627
|
usesSecondaryCatAxis = usesSecondaryCatAxis || options.secondaryCatAxis;
|
|
2628
|
+
const usesValueXAxis = type.type === "scatter" || type.type === "bubble" || type.type === "bubble3D";
|
|
2629
|
+
if (options.secondaryCatAxis) if (usesValueXAxis) secondaryCatAxisValType = type.type;
|
|
2630
|
+
else secondaryCatAxisHasCategoryChart = true;
|
|
2631
|
+
else if (usesValueXAxis) primaryCatAxisValType = type.type;
|
|
2632
|
+
else primaryCatAxisHasCategoryChart = true;
|
|
2475
2633
|
strXml += makeChartType(type.type, type.data, options, valAxisId, catAxisId);
|
|
2476
2634
|
});
|
|
2477
2635
|
else strXml += makeChartType(rel.opts._type, rel.data, rel.opts, AXIS_ID_VALUE_PRIMARY, AXIS_ID_CATEGORY_PRIMARY);
|
|
2478
2636
|
if (rel.opts._type !== "pie" && rel.opts._type !== "doughnut") {
|
|
2479
2637
|
if (rel.opts.valAxes && rel.opts.valAxes.length > 1 && !usesSecondaryValAxis) throw new Error("Secondary axis must be used by one of the multiple charts");
|
|
2638
|
+
const comboCatAxisType = (isSecondary) => {
|
|
2639
|
+
const valType = isSecondary ? secondaryCatAxisValType : primaryCatAxisValType;
|
|
2640
|
+
const hasCategoryChart = isSecondary ? secondaryCatAxisHasCategoryChart : primaryCatAxisHasCategoryChart;
|
|
2641
|
+
if (!valType) return {};
|
|
2642
|
+
if (hasCategoryChart) {
|
|
2643
|
+
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.`);
|
|
2644
|
+
return {};
|
|
2645
|
+
}
|
|
2646
|
+
return { _type: valType };
|
|
2647
|
+
};
|
|
2480
2648
|
if (rel.opts.catAxes) {
|
|
2481
2649
|
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.");
|
|
2482
2650
|
strXml += makeCatAxis({
|
|
2483
2651
|
...rel.opts,
|
|
2484
|
-
...rel.opts.catAxes[0]
|
|
2652
|
+
...rel.opts.catAxes[0],
|
|
2653
|
+
...comboCatAxisType(false)
|
|
2485
2654
|
}, AXIS_ID_CATEGORY_PRIMARY, AXIS_ID_VALUE_PRIMARY);
|
|
2486
|
-
} else strXml += makeCatAxis(
|
|
2655
|
+
} else strXml += makeCatAxis({
|
|
2656
|
+
...rel.opts,
|
|
2657
|
+
...comboCatAxisType(false)
|
|
2658
|
+
}, AXIS_ID_CATEGORY_PRIMARY, AXIS_ID_VALUE_PRIMARY);
|
|
2487
2659
|
if (rel.opts.valAxes) {
|
|
2488
2660
|
strXml += makeValAxis({
|
|
2489
2661
|
...rel.opts,
|
|
@@ -2500,9 +2672,13 @@ function makeXmlCharts(rel) {
|
|
|
2500
2672
|
}
|
|
2501
2673
|
if (rel.opts?.catAxes && rel.opts?.catAxes[1]) strXml += makeCatAxis({
|
|
2502
2674
|
...rel.opts,
|
|
2503
|
-
...rel.opts.catAxes[1]
|
|
2675
|
+
...rel.opts.catAxes[1],
|
|
2676
|
+
...comboCatAxisType(true)
|
|
2677
|
+
}, AXIS_ID_CATEGORY_SECONDARY, AXIS_ID_VALUE_SECONDARY);
|
|
2678
|
+
else if (usesSecondaryCatAxis && (!rel.opts.catAxes || !rel.opts.catAxes[1])) strXml += makeCatAxis({
|
|
2679
|
+
...rel.opts,
|
|
2680
|
+
...comboCatAxisType(true)
|
|
2504
2681
|
}, AXIS_ID_CATEGORY_SECONDARY, AXIS_ID_VALUE_SECONDARY);
|
|
2505
|
-
else if (usesSecondaryCatAxis && (!rel.opts.catAxes || !rel.opts.catAxes[1])) strXml += makeCatAxis(rel.opts, AXIS_ID_CATEGORY_SECONDARY, AXIS_ID_VALUE_SECONDARY);
|
|
2506
2682
|
}
|
|
2507
2683
|
if (rel.opts.showDataTable) {
|
|
2508
2684
|
strXml += "<c:dTable>";
|
|
@@ -2557,8 +2733,7 @@ function makeXmlCharts(rel) {
|
|
|
2557
2733
|
strXml += " <a:pPr>";
|
|
2558
2734
|
strXml += rel.opts.legendFontSize ? `<a:defRPr sz="${Math.round(Number(rel.opts.legendFontSize) * 100)}">` : "<a:defRPr>";
|
|
2559
2735
|
if (rel.opts.legendColor) strXml += genXmlColorSelection(rel.opts.legendColor);
|
|
2560
|
-
if (rel.opts.legendFontFace) strXml +=
|
|
2561
|
-
if (rel.opts.legendFontFace) strXml += "<a:cs typeface=\"" + rel.opts.legendFontFace + "\"/>";
|
|
2736
|
+
if (rel.opts.legendFontFace) strXml += createChartTextFonts(rel.opts.legendFontFace);
|
|
2562
2737
|
strXml += " </a:defRPr>";
|
|
2563
2738
|
strXml += " </a:pPr>";
|
|
2564
2739
|
strXml += " <a:endParaRPr lang=\"en-US\"/>";
|
|
@@ -2596,6 +2771,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2596
2771
|
let idxColLtr = 1;
|
|
2597
2772
|
let optsChartData;
|
|
2598
2773
|
let strXml = "";
|
|
2774
|
+
const valFmtCode = encodeXmlEntities(opts.valLabelFormatCode || opts.dataTableFormatCode || opts.dataLabelFormatCode || "General");
|
|
2599
2775
|
switch (chartType) {
|
|
2600
2776
|
case "area":
|
|
2601
2777
|
case "bar":
|
|
@@ -2639,7 +2815,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2639
2815
|
} 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>`;
|
|
2640
2816
|
strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
2641
2817
|
strXml += " </c:spPr>";
|
|
2642
|
-
if (chartType
|
|
2818
|
+
if (chartType === "bar" || chartType === "bar3D") strXml += " <c:invertIfNegative val=\"0\"/>";
|
|
2643
2819
|
if (chartType === "line" || chartType === "radar") {
|
|
2644
2820
|
strXml += "<c:marker>";
|
|
2645
2821
|
strXml += " <c:symbol val=\"" + opts.lineDataSymbol + "\"/>";
|
|
@@ -2654,6 +2830,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2654
2830
|
strXml += " </c:spPr>";
|
|
2655
2831
|
strXml += "</c:marker>";
|
|
2656
2832
|
}
|
|
2833
|
+
{
|
|
2834
|
+
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;
|
|
2835
|
+
strXml += makeSeriesDataPointsXml(chartType, obj, opts, barVaryColors);
|
|
2836
|
+
}
|
|
2657
2837
|
if (chartType !== "radar") {
|
|
2658
2838
|
const lblColor = seriesOverride?.dataLabelColor ?? opts.dataLabelColor ?? "000000";
|
|
2659
2839
|
const lblBold = seriesOverride?.dataLabelFontBold ?? opts.dataLabelFontBold ?? false;
|
|
@@ -2661,12 +2841,15 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2661
2841
|
const lblSize = seriesOverride?.dataLabelFontSize ?? opts.dataLabelFontSize ?? 12;
|
|
2662
2842
|
const lblFace = seriesOverride?.dataLabelFontFace ?? opts.dataLabelFontFace ?? "Arial";
|
|
2663
2843
|
strXml += "<c:dLbls>";
|
|
2844
|
+
if (obj.customLabels?.length) obj.customLabels.forEach((lbl, idx) => {
|
|
2845
|
+
if (lbl) strXml += makeCustomDLblXml(idx, lbl, opts);
|
|
2846
|
+
});
|
|
2664
2847
|
strXml += `<c:numFmt formatCode="${encodeXmlEntities(opts.dataLabelFormatCode) || "General"}" sourceLinked="0"/>`;
|
|
2665
2848
|
if (opts.dataLabelBkgrdColors) strXml += `<c:spPr><a:solidFill>${createColorElement(seriesColor)}</a:solidFill></c:spPr>`;
|
|
2666
2849
|
strXml += "<c:txPr><a:bodyPr/><a:lstStyle/><a:p><a:pPr>";
|
|
2667
2850
|
strXml += `<a:defRPr b="${lblBold ? 1 : 0}" i="${lblItalic ? 1 : 0}" strike="noStrike" sz="${Math.round(lblSize * 100)}" u="none">`;
|
|
2668
2851
|
strXml += `<a:solidFill>${createColorElement(lblColor)}</a:solidFill>`;
|
|
2669
|
-
strXml +=
|
|
2852
|
+
strXml += createChartTextFonts(lblFace);
|
|
2670
2853
|
strXml += "</a:defRPr></a:pPr></a:p></c:txPr>";
|
|
2671
2854
|
if (opts.dataLabelPosition) strXml += `<c:dLblPos val="${opts.dataLabelPosition}"/>`;
|
|
2672
2855
|
strXml += "<c:showLegendKey val=\"0\"/>";
|
|
@@ -2675,29 +2858,6 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2675
2858
|
strXml += `<c:showLeaderLines val="${opts.showLeaderLines ? "1" : "0"}"/>`;
|
|
2676
2859
|
strXml += "</c:dLbls>";
|
|
2677
2860
|
}
|
|
2678
|
-
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) => {
|
|
2679
|
-
const arrColors = value < 0 ? opts.invertedColors || opts.chartColors || BARCHART_COLORS : opts.chartColors || [];
|
|
2680
|
-
strXml += " <c:dPt>";
|
|
2681
|
-
strXml += ` <c:idx val="${index}"/>`;
|
|
2682
|
-
strXml += " <c:invertIfNegative val=\"0\"/>";
|
|
2683
|
-
strXml += " <c:bubble3D val=\"0\"/>";
|
|
2684
|
-
strXml += " <c:spPr>";
|
|
2685
|
-
if (opts.lineSize === 0) strXml += "<a:ln><a:noFill/></a:ln>";
|
|
2686
|
-
else if (chartType === "bar") {
|
|
2687
|
-
strXml += "<a:solidFill>";
|
|
2688
|
-
strXml += " <a:srgbClr val=\"" + arrColors[index % arrColors.length] + "\"/>";
|
|
2689
|
-
strXml += "</a:solidFill>";
|
|
2690
|
-
} else {
|
|
2691
|
-
strXml += "<a:ln>";
|
|
2692
|
-
strXml += " <a:solidFill>";
|
|
2693
|
-
strXml += " <a:srgbClr val=\"" + arrColors[index % arrColors.length] + "\"/>";
|
|
2694
|
-
strXml += " </a:solidFill>";
|
|
2695
|
-
strXml += "</a:ln>";
|
|
2696
|
-
}
|
|
2697
|
-
strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
2698
|
-
strXml += " </c:spPr>";
|
|
2699
|
-
strXml += " </c:dPt>";
|
|
2700
|
-
});
|
|
2701
2861
|
strXml += "<c:cat>";
|
|
2702
2862
|
if (opts.catLabelFormatCode) {
|
|
2703
2863
|
strXml += " <c:numRef>";
|
|
@@ -2734,10 +2894,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2734
2894
|
strXml += " <c:numRef>";
|
|
2735
2895
|
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>`;
|
|
2736
2896
|
strXml += " <c:numCache>";
|
|
2737
|
-
strXml += " <c:formatCode>" +
|
|
2897
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
2738
2898
|
strXml += ` <c:ptCount val="${obj.labels[0].length}"/>`;
|
|
2739
2899
|
obj.values.forEach((value, idx) => {
|
|
2740
|
-
|
|
2900
|
+
strXml += numCachePt(idx, value);
|
|
2741
2901
|
});
|
|
2742
2902
|
strXml += " </c:numCache>";
|
|
2743
2903
|
strXml += " </c:numRef>";
|
|
@@ -2753,7 +2913,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2753
2913
|
strXml += " <a:p><a:pPr>";
|
|
2754
2914
|
strXml += ` <a:defRPr b="${opts.dataLabelFontBold ? 1 : 0}" i="${opts.dataLabelFontItalic ? 1 : 0}" strike="noStrike" sz="${Math.round((opts.dataLabelFontSize || 12) * 100)}" u="none">`;
|
|
2755
2915
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
2756
|
-
strXml += "
|
|
2916
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
2757
2917
|
strXml += " </a:defRPr>";
|
|
2758
2918
|
strXml += " </a:pPr></a:p>";
|
|
2759
2919
|
strXml += " </c:txPr>";
|
|
@@ -2769,6 +2929,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2769
2929
|
if (chartType === "bar") {
|
|
2770
2930
|
strXml += ` <c:gapWidth val="${opts.barGapWidthPct}"/>`;
|
|
2771
2931
|
strXml += ` <c:overlap val="${opts.barOverlapPct != null ? opts.barOverlapPct : (opts.barGrouping || "").includes("tacked") ? 100 : 0}"/>`;
|
|
2932
|
+
strXml += createSerLinesElement(opts.barSeriesLine);
|
|
2772
2933
|
} else if (chartType === "bar3D") {
|
|
2773
2934
|
strXml += ` <c:gapWidth val="${opts.barGapWidthPct}"/>`;
|
|
2774
2935
|
strXml += ` <c:gapDepth val="${opts.barGapDepthPct}"/>`;
|
|
@@ -2820,6 +2981,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2820
2981
|
strXml += "<a:effectLst/>";
|
|
2821
2982
|
strXml += "</c:spPr>";
|
|
2822
2983
|
strXml += "</c:marker>";
|
|
2984
|
+
{
|
|
2985
|
+
const scatterVaryColors = data.length === 1 && opts.chartColors !== BARCHART_COLORS ? opts.chartColors || BARCHART_COLORS : null;
|
|
2986
|
+
strXml += makeSeriesDataPointsXml(chartType, obj, opts, scatterVaryColors);
|
|
2987
|
+
}
|
|
2823
2988
|
if (opts.showLabel) {
|
|
2824
2989
|
const chartUuid = getUuid("-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
|
|
2825
2990
|
if (obj.labels[0] && (opts.dataLabelFormatScatter === "custom" || opts.dataLabelFormatScatter === "customXY")) {
|
|
@@ -2838,13 +3003,13 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2838
3003
|
strXml += " <a:pPr>";
|
|
2839
3004
|
strXml += ` <a:defRPr sz="${Math.round((opts.dataLabelFontSize || 12) * 100)}" b="${opts.dataLabelFontBold ? "1" : "0"}" i="${opts.dataLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike">`;
|
|
2840
3005
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
2841
|
-
strXml +=
|
|
3006
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
2842
3007
|
strXml += " </a:defRPr>";
|
|
2843
3008
|
strXml += " </a:pPr>";
|
|
2844
3009
|
strXml += " <a:r>";
|
|
2845
3010
|
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">`;
|
|
2846
3011
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
2847
|
-
strXml +=
|
|
3012
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
2848
3013
|
strXml += " </a:rPr>";
|
|
2849
3014
|
strXml += " <a:t>" + encodeXmlEntities(label) + "</a:t>";
|
|
2850
3015
|
strXml += " </a:r>";
|
|
@@ -2924,7 +3089,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2924
3089
|
strXml += " <a:pPr>";
|
|
2925
3090
|
strXml += ` <a:defRPr sz="${Math.round((opts.dataLabelFontSize || 12) * 100)}" b="${opts.dataLabelFontBold ? "1" : "0"}" i="${opts.dataLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike">`;
|
|
2926
3091
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
2927
|
-
strXml +=
|
|
3092
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
2928
3093
|
strXml += " </a:defRPr>";
|
|
2929
3094
|
strXml += " </a:pPr>";
|
|
2930
3095
|
strXml += ` <a:endParaRPr lang="${opts.lang || "en-US"}"/>`;
|
|
@@ -2945,31 +3110,14 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2945
3110
|
strXml += "</c:dLbls>";
|
|
2946
3111
|
}
|
|
2947
3112
|
}
|
|
2948
|
-
if (data.length === 1 && opts.chartColors !== BARCHART_COLORS) obj.values.forEach((value, index) => {
|
|
2949
|
-
const arrColors = value < 0 ? opts.invertedColors || opts.chartColors || BARCHART_COLORS : opts.chartColors || [];
|
|
2950
|
-
strXml += " <c:dPt>";
|
|
2951
|
-
strXml += ` <c:idx val="${index}"/>`;
|
|
2952
|
-
strXml += " <c:invertIfNegative val=\"0\"/>";
|
|
2953
|
-
strXml += " <c:bubble3D val=\"0\"/>";
|
|
2954
|
-
strXml += " <c:spPr>";
|
|
2955
|
-
if (opts.lineSize === 0) strXml += "<a:ln><a:noFill/></a:ln>";
|
|
2956
|
-
else {
|
|
2957
|
-
strXml += "<a:solidFill>";
|
|
2958
|
-
strXml += " <a:srgbClr val=\"" + arrColors[index % arrColors.length] + "\"/>";
|
|
2959
|
-
strXml += "</a:solidFill>";
|
|
2960
|
-
}
|
|
2961
|
-
strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
2962
|
-
strXml += " </c:spPr>";
|
|
2963
|
-
strXml += " </c:dPt>";
|
|
2964
|
-
});
|
|
2965
3113
|
strXml += "<c:xVal>";
|
|
2966
3114
|
strXml += " <c:numRef>";
|
|
2967
3115
|
strXml += ` <c:f>Sheet1!$A$2:$A$${data[0].values.length + 1}</c:f>`;
|
|
2968
3116
|
strXml += " <c:numCache>";
|
|
2969
|
-
strXml += " <c:formatCode>
|
|
3117
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
2970
3118
|
strXml += ` <c:ptCount val="${data[0].values.length}"/>`;
|
|
2971
3119
|
data[0].values.forEach((value, idx) => {
|
|
2972
|
-
|
|
3120
|
+
strXml += numCachePt(idx, value);
|
|
2973
3121
|
});
|
|
2974
3122
|
strXml += " </c:numCache>";
|
|
2975
3123
|
strXml += " </c:numRef>";
|
|
@@ -2978,10 +3126,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2978
3126
|
strXml += " <c:numRef>";
|
|
2979
3127
|
strXml += ` <c:f>Sheet1!$${getExcelColName(idx + 2)}$2:$${getExcelColName(idx + 2)}$${data[0].values.length + 1}</c:f>`;
|
|
2980
3128
|
strXml += " <c:numCache>";
|
|
2981
|
-
strXml += " <c:formatCode>
|
|
3129
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
2982
3130
|
strXml += ` <c:ptCount val="${data[0].values.length}"/>`;
|
|
2983
3131
|
data[0].values.forEach((_value, idx) => {
|
|
2984
|
-
|
|
3132
|
+
strXml += numCachePt(idx, obj.values[idx]);
|
|
2985
3133
|
});
|
|
2986
3134
|
strXml += " </c:numCache>";
|
|
2987
3135
|
strXml += " </c:numRef>";
|
|
@@ -2997,7 +3145,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2997
3145
|
strXml += " <a:p><a:pPr>";
|
|
2998
3146
|
strXml += ` <a:defRPr b="${opts.dataLabelFontBold ? "1" : "0"}" i="${opts.dataLabelFontItalic ? "1" : "0"}" strike="noStrike" sz="${Math.round((opts.dataLabelFontSize || 12) * 100)}" u="none">`;
|
|
2999
3147
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
3000
|
-
strXml += "
|
|
3148
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
3001
3149
|
strXml += " </a:defRPr>";
|
|
3002
3150
|
strXml += " </a:pPr></a:p>";
|
|
3003
3151
|
strXml += " </c:txPr>";
|
|
@@ -3047,10 +3195,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
3047
3195
|
strXml += " <c:numRef>";
|
|
3048
3196
|
strXml += ` <c:f>Sheet1!$A$2:$A$${data[0].values.length + 1}</c:f>`;
|
|
3049
3197
|
strXml += " <c:numCache>";
|
|
3050
|
-
strXml += " <c:formatCode>
|
|
3198
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
3051
3199
|
strXml += ` <c:ptCount val="${data[0].values.length}"/>`;
|
|
3052
3200
|
data[0].values.forEach((value, idx) => {
|
|
3053
|
-
strXml +=
|
|
3201
|
+
strXml += numCachePt(idx, value);
|
|
3054
3202
|
});
|
|
3055
3203
|
strXml += " </c:numCache>";
|
|
3056
3204
|
strXml += " </c:numRef>";
|
|
@@ -3060,10 +3208,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
3060
3208
|
strXml += `<c:f>Sheet1!$${getExcelColName(idxColLtr + 1)}$2:$${getExcelColName(idxColLtr + 1)}$${data[0].values.length + 1}</c:f>`;
|
|
3061
3209
|
idxColLtr++;
|
|
3062
3210
|
strXml += " <c:numCache>";
|
|
3063
|
-
strXml += " <c:formatCode>
|
|
3211
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
3064
3212
|
strXml += ` <c:ptCount val="${data[0].values.length}"/>`;
|
|
3065
3213
|
data[0].values.forEach((_value, idx) => {
|
|
3066
|
-
strXml +=
|
|
3214
|
+
strXml += numCachePt(idx, obj.values[idx]);
|
|
3067
3215
|
});
|
|
3068
3216
|
strXml += " </c:numCache>";
|
|
3069
3217
|
strXml += " </c:numRef>";
|
|
@@ -3076,7 +3224,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
3076
3224
|
strXml += " <c:formatCode>General</c:formatCode>";
|
|
3077
3225
|
strXml += ` <c:ptCount val="${obj.sizes.length}"/>`;
|
|
3078
3226
|
obj.sizes.forEach((value, idx) => {
|
|
3079
|
-
strXml +=
|
|
3227
|
+
strXml += numCachePt(idx, value);
|
|
3080
3228
|
});
|
|
3081
3229
|
strXml += " </c:numCache>";
|
|
3082
3230
|
strXml += " </c:numRef>";
|
|
@@ -3089,12 +3237,12 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
3089
3237
|
strXml += "<c:txPr><a:bodyPr/><a:lstStyle/><a:p><a:pPr>";
|
|
3090
3238
|
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">`;
|
|
3091
3239
|
strXml += `<a:solidFill>${createColorElement(opts.dataLabelColor || "000000")}</a:solidFill>`;
|
|
3092
|
-
strXml +=
|
|
3240
|
+
strXml += createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
3093
3241
|
strXml += "</a:defRPr></a:pPr></a:p></c:txPr>";
|
|
3094
3242
|
if (opts.dataLabelPosition) strXml += `<c:dLblPos val="${opts.dataLabelPosition}"/>`;
|
|
3095
3243
|
strXml += "<c:showLegendKey val=\"0\"/>";
|
|
3096
3244
|
strXml += `<c:showVal val="${opts.showValue ? "1" : "0"}"/>`;
|
|
3097
|
-
strXml += `<c:showCatName val="0"/><c:showSerName val="${opts.showSerName ? "1" : "0"}"/><c:showPercent val="0"/><c:showBubbleSize val="0"/>`;
|
|
3245
|
+
strXml += `<c:showCatName val="0"/><c:showSerName val="${opts.showSerName ? "1" : "0"}"/><c:showPercent val="0"/><c:showBubbleSize val="${opts.showBubbleSize ? "1" : "0"}"/>`;
|
|
3098
3246
|
strXml += "<c:extLst>";
|
|
3099
3247
|
strXml += " <c:ext uri=\"{CE6537A1-D6FC-4f65-9D91-7224C49458BB}\" xmlns:c15=\"http://schemas.microsoft.com/office/drawing/2012/chart\">";
|
|
3100
3248
|
strXml += " <c15:showLeaderLines val=\"" + (opts.showLeaderLines ? "1" : "0") + "\"/>";
|
|
@@ -3128,33 +3276,37 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
3128
3276
|
else strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
3129
3277
|
strXml += " </c:spPr>";
|
|
3130
3278
|
optsChartData.labels[0].forEach((_label, idx) => {
|
|
3279
|
+
const ptStyle = optsChartData.pointStyles?.[idx];
|
|
3131
3280
|
strXml += "<c:dPt>";
|
|
3132
3281
|
strXml += ` <c:idx val="${idx}"/>`;
|
|
3133
3282
|
strXml += " <c:bubble3D val=\"0\"/>";
|
|
3134
3283
|
strXml += " <c:spPr>";
|
|
3135
|
-
strXml += `<a:solidFill>${createColorElement(opts.chartColors[idx + 1 > opts.chartColors.length ? Math.floor(Math.random() * opts.chartColors.length) : idx])}</a:solidFill>`;
|
|
3136
|
-
if (
|
|
3284
|
+
strXml += `<a:solidFill>${createColorElement(ptStyle?.fill || opts.chartColors[idx + 1 > opts.chartColors.length ? Math.floor(Math.random() * opts.chartColors.length) : idx])}</a:solidFill>`;
|
|
3285
|
+
if (ptStyle?.border) strXml += createChartBorderLine(ptStyle.border);
|
|
3286
|
+
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>`;
|
|
3137
3287
|
strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
3138
3288
|
strXml += " </c:spPr>";
|
|
3139
3289
|
strXml += "</c:dPt>";
|
|
3140
3290
|
});
|
|
3141
3291
|
strXml += "<c:dLbls>";
|
|
3142
3292
|
optsChartData.labels[0].forEach((_label, idx) => {
|
|
3293
|
+
const customLbl = optsChartData.customLabels?.[idx];
|
|
3143
3294
|
strXml += "<c:dLbl>";
|
|
3144
3295
|
strXml += ` <c:idx val="${idx}"/>`;
|
|
3296
|
+
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>`;
|
|
3145
3297
|
strXml += ` <c:numFmt formatCode="${encodeXmlEntities(opts.dataLabelFormatCode) || "General"}" sourceLinked="0"/>`;
|
|
3146
3298
|
strXml += " <c:spPr/><c:txPr>";
|
|
3147
3299
|
strXml += " <a:bodyPr/><a:lstStyle/>";
|
|
3148
3300
|
strXml += " <a:p><a:pPr>";
|
|
3149
3301
|
strXml += ` <a:defRPr sz="${Math.round((opts.dataLabelFontSize || 12) * 100)}" b="${opts.dataLabelFontBold ? 1 : 0}" i="${opts.dataLabelFontItalic ? 1 : 0}" u="none" strike="noStrike">`;
|
|
3150
3302
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
3151
|
-
strXml +=
|
|
3303
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
3152
3304
|
strXml += " </a:defRPr>";
|
|
3153
3305
|
strXml += " </a:pPr></a:p>";
|
|
3154
3306
|
strXml += " </c:txPr>";
|
|
3155
3307
|
if (chartType === "pie" && opts.dataLabelPosition) strXml += `<c:dLblPos val="${opts.dataLabelPosition}"/>`;
|
|
3156
3308
|
strXml += " <c:showLegendKey val=\"0\"/>";
|
|
3157
|
-
strXml += " <c:showVal val=\"" + (opts.showValue ? "1" : "0") + "\"/>";
|
|
3309
|
+
strXml += " <c:showVal val=\"" + (customLbl ? "0" : opts.showValue ? "1" : "0") + "\"/>";
|
|
3158
3310
|
strXml += " <c:showCatName val=\"" + (opts.showLabel ? "1" : "0") + "\"/>";
|
|
3159
3311
|
strXml += " <c:showSerName val=\"" + (opts.showSerName ? "1" : "0") + "\"/>";
|
|
3160
3312
|
strXml += " <c:showPercent val=\"" + (opts.showPercent ? "1" : "0") + "\"/>";
|
|
@@ -3169,7 +3321,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
3169
3321
|
strXml += " <a:pPr>";
|
|
3170
3322
|
strXml += ` <a:defRPr sz="${Math.round((opts.dataLabelFontSize || 12) * 100)}" b="${opts.dataLabelFontBold ? "1" : "0"}" i="${opts.dataLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike">`;
|
|
3171
3323
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
3172
|
-
strXml +=
|
|
3324
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
3173
3325
|
strXml += " </a:defRPr>";
|
|
3174
3326
|
strXml += " </a:pPr>";
|
|
3175
3327
|
strXml += " </a:p>";
|
|
@@ -3198,6 +3350,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
3198
3350
|
strXml += " <c:numRef>";
|
|
3199
3351
|
strXml += ` <c:f>Sheet1!$B$2:$B$${optsChartData.labels[0].length + 1}</c:f>`;
|
|
3200
3352
|
strXml += " <c:numCache>";
|
|
3353
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
3201
3354
|
strXml += ` <c:ptCount val="${optsChartData.labels[0].length}"/>`;
|
|
3202
3355
|
optsChartData.values.forEach((value, idx) => {
|
|
3203
3356
|
strXml += `<c:pt idx="${idx}"><c:v>${value || value === 0 ? value : ""}</c:v></c:pt>`;
|
|
@@ -3273,7 +3426,7 @@ function makeCatAxis(opts, axisId, valAxisId) {
|
|
|
3273
3426
|
strXml += " <a:pPr>";
|
|
3274
3427
|
strXml += ` <a:defRPr sz="${Math.round((opts.catAxisLabelFontSize || 12) * 100)}" b="${opts.catAxisLabelFontBold ? 1 : 0}" i="${opts.catAxisLabelFontItalic ? 1 : 0}" u="none" strike="noStrike">`;
|
|
3275
3428
|
strXml += " <a:solidFill>" + createColorElement(opts.catAxisLabelColor || "000000") + "</a:solidFill>";
|
|
3276
|
-
strXml += "
|
|
3429
|
+
strXml += " " + createChartTextFonts(opts.catAxisLabelFontFace || "Arial");
|
|
3277
3430
|
strXml += " </a:defRPr>";
|
|
3278
3431
|
strXml += " </a:pPr>";
|
|
3279
3432
|
strXml += " <a:endParaRPr lang=\"" + (opts.lang || "en-US") + "\"/>";
|
|
@@ -3366,7 +3519,7 @@ function makeValAxis(opts, valAxisId) {
|
|
|
3366
3519
|
strXml += " <a:pPr>";
|
|
3367
3520
|
strXml += ` <a:defRPr sz="${Math.round((opts.valAxisLabelFontSize || 12) * 100)}" b="${opts.valAxisLabelFontBold ? 1 : 0}" i="${opts.valAxisLabelFontItalic ? 1 : 0}" u="none" strike="noStrike">`;
|
|
3368
3521
|
strXml += " <a:solidFill>" + createColorElement(opts.valAxisLabelColor || "000000") + "</a:solidFill>";
|
|
3369
|
-
strXml += "
|
|
3522
|
+
strXml += " " + createChartTextFonts(opts.valAxisLabelFontFace || "Arial");
|
|
3370
3523
|
strXml += " </a:defRPr>";
|
|
3371
3524
|
strXml += " </a:pPr>";
|
|
3372
3525
|
strXml += " <a:endParaRPr lang=\"" + (opts.lang || "en-US") + "\"/>";
|
|
@@ -3422,7 +3575,7 @@ function makeSerAxis(opts, axisId, valAxisId) {
|
|
|
3422
3575
|
strXml += " <a:pPr>";
|
|
3423
3576
|
strXml += ` <a:defRPr sz="${Math.round((opts.serAxisLabelFontSize || 12) * 100)}" b="${opts.serAxisLabelFontBold ? "1" : "0"}" i="${opts.serAxisLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike">`;
|
|
3424
3577
|
strXml += ` <a:solidFill>${createColorElement(opts.serAxisLabelColor || "000000")}</a:solidFill>`;
|
|
3425
|
-
strXml +=
|
|
3578
|
+
strXml += " " + createChartTextFonts(opts.serAxisLabelFontFace || "Arial");
|
|
3426
3579
|
strXml += " </a:defRPr>";
|
|
3427
3580
|
strXml += " </a:pPr>";
|
|
3428
3581
|
strXml += " <a:endParaRPr lang=\"" + (opts.lang || "en-US") + "\"/>";
|
|
@@ -3462,17 +3615,31 @@ function genXmlTitle(opts, chartX, chartY) {
|
|
|
3462
3615
|
const rotate = opts.titleRotate ? `<a:bodyPr rot="${convertRotationDegrees(opts.titleRotate)}"/>` : "<a:bodyPr/>";
|
|
3463
3616
|
const sizeAttr = opts.fontSize ? `sz="${Math.round(opts.fontSize * 100)}"` : "";
|
|
3464
3617
|
const titleBold = opts.titleBold ? 1 : 0;
|
|
3618
|
+
const titleItalic = opts.titleItalic ? 1 : 0;
|
|
3619
|
+
const titleUnderline = opts.titleUnderline ? "sng" : "none";
|
|
3465
3620
|
let layout = "<c:layout/>";
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
let
|
|
3470
|
-
|
|
3471
|
-
if (
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3621
|
+
const hasX = opts.titlePos && typeof opts.titlePos.x === "number";
|
|
3622
|
+
const hasY = opts.titlePos && typeof opts.titlePos.y === "number";
|
|
3623
|
+
if (hasX || hasY) {
|
|
3624
|
+
let modes = "";
|
|
3625
|
+
let vals = "";
|
|
3626
|
+
if (hasX) {
|
|
3627
|
+
const totalX = opts.titlePos.x + chartX;
|
|
3628
|
+
let valX = totalX === 0 ? 0 : totalX * (totalX / 5) / 10;
|
|
3629
|
+
if (valX >= 1) valX = valX / 10;
|
|
3630
|
+
if (valX >= .1) valX = valX / 10;
|
|
3631
|
+
modes += "<c:xMode val=\"edge\"/>";
|
|
3632
|
+
vals += `<c:x val="${valX}"/>`;
|
|
3633
|
+
}
|
|
3634
|
+
if (hasY) {
|
|
3635
|
+
const totalY = opts.titlePos.y + chartY;
|
|
3636
|
+
let valY = totalY === 0 ? 0 : totalY * (totalY / 5) / 10;
|
|
3637
|
+
if (valY >= 1) valY = valY / 10;
|
|
3638
|
+
if (valY >= .1) valY = valY / 10;
|
|
3639
|
+
modes += "<c:yMode val=\"edge\"/>";
|
|
3640
|
+
vals += `<c:y val="${valY}"/>`;
|
|
3641
|
+
}
|
|
3642
|
+
layout = `<c:layout><c:manualLayout>${modes}${vals}</c:manualLayout></c:layout>`;
|
|
3476
3643
|
}
|
|
3477
3644
|
return `<c:title>
|
|
3478
3645
|
<c:tx>
|
|
@@ -3481,15 +3648,15 @@ function genXmlTitle(opts, chartX, chartY) {
|
|
|
3481
3648
|
<a:lstStyle/>
|
|
3482
3649
|
<a:p>
|
|
3483
3650
|
${align}
|
|
3484
|
-
<a:defRPr ${sizeAttr} b="${titleBold}" i="
|
|
3651
|
+
<a:defRPr ${sizeAttr} b="${titleBold}" i="${titleItalic}" u="${titleUnderline}" strike="noStrike">
|
|
3485
3652
|
<a:solidFill>${createColorElement(opts.color || "000000")}</a:solidFill>
|
|
3486
|
-
|
|
3653
|
+
${createChartTextFonts(opts.fontFace || "Arial")}
|
|
3487
3654
|
</a:defRPr>
|
|
3488
3655
|
</a:pPr>
|
|
3489
3656
|
<a:r>
|
|
3490
|
-
<a:rPr ${sizeAttr} b="${titleBold}" i="
|
|
3657
|
+
<a:rPr ${sizeAttr} b="${titleBold}" i="${titleItalic}" u="${titleUnderline}" strike="noStrike">
|
|
3491
3658
|
<a:solidFill>${createColorElement(opts.color || "000000")}</a:solidFill>
|
|
3492
|
-
|
|
3659
|
+
${createChartTextFonts(opts.fontFace || "Arial")}
|
|
3493
3660
|
</a:rPr>
|
|
3494
3661
|
<a:t>${encodeXmlEntities(opts.title) || ""}</a:t>
|
|
3495
3662
|
</a:r>
|
|
@@ -3563,11 +3730,105 @@ function createGridLineElement(glOpts) {
|
|
|
3563
3730
|
strXml += "</c:majorGridlines>";
|
|
3564
3731
|
return strXml;
|
|
3565
3732
|
}
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3733
|
+
/**
|
|
3734
|
+
* Build a `<c:pt>` numeric-cache data point, or '' to leave a gap.
|
|
3735
|
+
*
|
|
3736
|
+
* `<c:v>` inside a `<c:numCache>` is an `xsd:double`; emitting `NaN`, `Infinity`
|
|
3737
|
+
* or an empty string yields an invalid value that makes PowerPoint report the
|
|
3738
|
+
* package as needing repair. Null/undefined are intentional gaps and are skipped
|
|
3739
|
+
* silently (a sparse, idx-keyed cache is valid); other non-finite numbers are
|
|
3740
|
+
* skipped with a warning, per the library's "warn rather than emit a degenerate
|
|
3741
|
+
* result" policy.
|
|
3742
|
+
* @param idx - zero-based data-point index (emitted as `idx`)
|
|
3743
|
+
* @param value - numeric value (or null/undefined gap)
|
|
3744
|
+
*/
|
|
3745
|
+
function numCachePt(idx, value) {
|
|
3746
|
+
if (value == null) return "";
|
|
3747
|
+
if (!Number.isFinite(value)) {
|
|
3748
|
+
console.warn(`Warning: chart value "${value}" at index ${idx} is not a finite number; data point omitted.`);
|
|
3749
|
+
return "";
|
|
3750
|
+
}
|
|
3751
|
+
return `<c:pt idx="${idx}"><c:v>${value}</c:v></c:pt>`;
|
|
3752
|
+
}
|
|
3753
|
+
/**
|
|
3754
|
+
* Build a `<c:serLines>` ("Series Lines") element for a bar chart.
|
|
3755
|
+
* @param opt - `true` for PowerPoint automatic styling, an {@link OptsChartGridLine}
|
|
3756
|
+
* to customize the line, or falsy / `{ style: 'none' }` to omit the element.
|
|
3757
|
+
*/
|
|
3758
|
+
function createSerLinesElement(opt) {
|
|
3759
|
+
if (!opt) return "";
|
|
3760
|
+
if (opt === true) return "<c:serLines/>";
|
|
3761
|
+
if (opt.style === "none") return "";
|
|
3762
|
+
let strXml = "<c:serLines><c:spPr>";
|
|
3763
|
+
strXml += `<a:ln w="${valToPts(opt.size || DEF_CHART_GRIDLINE.size)}" cap="${createLineCap(opt.cap || DEF_CHART_GRIDLINE.cap)}">`;
|
|
3764
|
+
strXml += `<a:solidFill><a:srgbClr val="${opt.color || DEF_CHART_GRIDLINE.color}"/></a:solidFill>`;
|
|
3765
|
+
strXml += `<a:prstDash val="${opt.style || DEF_CHART_GRIDLINE.style}"/><a:round/>`;
|
|
3766
|
+
strXml += "</a:ln></c:spPr></c:serLines>";
|
|
3767
|
+
return strXml;
|
|
3768
|
+
}
|
|
3769
|
+
function makeCustomDLblXml(idx, text, opts) {
|
|
3770
|
+
const sz = Math.round((opts.dataLabelFontSize || 12) * 100);
|
|
3771
|
+
const bold = opts.dataLabelFontBold ? "1" : "0";
|
|
3772
|
+
const italic = opts.dataLabelFontItalic ? "1" : "0";
|
|
3773
|
+
const color = createColorElement(opts.dataLabelColor || "000000");
|
|
3774
|
+
const face = opts.dataLabelFontFace || "Arial";
|
|
3775
|
+
const lang = opts.lang || "en-US";
|
|
3776
|
+
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>`;
|
|
3777
|
+
}
|
|
3778
|
+
/**
|
|
3779
|
+
* Build an `<a:ln>` border element from a per-data-point `BorderProps`.
|
|
3780
|
+
* @param border - point border style (`type`, `color`, `pt`)
|
|
3781
|
+
*/
|
|
3782
|
+
function createChartBorderLine(border) {
|
|
3783
|
+
if (border.type === "none") return "<a:ln><a:noFill/></a:ln>";
|
|
3784
|
+
const dash = border.type === "dash" ? "dash" : "solid";
|
|
3785
|
+
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>`;
|
|
3786
|
+
}
|
|
3787
|
+
/**
|
|
3788
|
+
* Build `<c:dPt>` entries for a series in the bar/line/area/scatter loops.
|
|
3789
|
+
*
|
|
3790
|
+
* Merges two sources into a single `c:dPt` per index so we never emit a
|
|
3791
|
+
* duplicate `<c:idx>` (which corrupts the chart):
|
|
3792
|
+
* - legacy single-series color-vary fills (bar/scatter), supplied via `varyColors`
|
|
3793
|
+
* - per-point `pointStyles` border/fill overrides
|
|
3794
|
+
*
|
|
3795
|
+
* Must be emitted in schema position *before* `c:dLbls` (CT_*Ser order).
|
|
3796
|
+
* RADAR is skipped: extra per-point markup historically corrupts the chart.
|
|
3797
|
+
*
|
|
3798
|
+
* @param chartType - series chart type
|
|
3799
|
+
* @param obj - series data (reads `values`, `pointStyles`)
|
|
3800
|
+
* @param opts - chart options (fill/shadow/lineSize context)
|
|
3801
|
+
* @param varyColors - color array when single-series color-vary applies, else `null`
|
|
3802
|
+
*/
|
|
3803
|
+
function makeSeriesDataPointsXml(chartType, obj, opts, varyColors) {
|
|
3804
|
+
if (chartType === "radar") return "";
|
|
3805
|
+
const pointStyles = obj.pointStyles;
|
|
3806
|
+
if (!varyColors && !pointStyles?.length) return "";
|
|
3807
|
+
const isBar = chartType === "bar" || chartType === "bar3D";
|
|
3808
|
+
const isScatter = chartType === "scatter";
|
|
3809
|
+
let xml = "";
|
|
3810
|
+
obj.values.forEach((value, index) => {
|
|
3811
|
+
const ptStyle = pointStyles?.[index];
|
|
3812
|
+
const arrColors = varyColors ? value < 0 ? opts.invertedColors || opts.chartColors || BARCHART_COLORS : varyColors : null;
|
|
3813
|
+
const fillColor = ptStyle?.fill || (arrColors ? arrColors[index % arrColors.length] : null);
|
|
3814
|
+
const border = ptStyle?.border;
|
|
3815
|
+
if (!fillColor && !border) return;
|
|
3816
|
+
xml += "<c:dPt>";
|
|
3817
|
+
xml += `<c:idx val="${index}"/>`;
|
|
3818
|
+
if (isBar) xml += "<c:invertIfNegative val=\"0\"/>";
|
|
3819
|
+
xml += "<c:bubble3D val=\"0\"/>";
|
|
3820
|
+
xml += "<c:spPr>";
|
|
3821
|
+
if ((isBar || isScatter) && opts.lineSize === 0 && !border && !ptStyle?.fill) xml += "<a:ln><a:noFill/></a:ln>";
|
|
3822
|
+
else {
|
|
3823
|
+
if (fillColor) if (chartType === "bar3D") xml += `<a:ln><a:solidFill>${createColorElement(fillColor)}</a:solidFill></a:ln>`;
|
|
3824
|
+
else xml += `<a:solidFill>${createColorElement(fillColor)}</a:solidFill>`;
|
|
3825
|
+
if (border) xml += createChartBorderLine(border);
|
|
3826
|
+
}
|
|
3827
|
+
xml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
3828
|
+
xml += "</c:spPr>";
|
|
3829
|
+
xml += "</c:dPt>";
|
|
3830
|
+
});
|
|
3831
|
+
return xml;
|
|
3571
3832
|
}
|
|
3572
3833
|
//#endregion
|
|
3573
3834
|
//#region src/gen-media.ts
|
|
@@ -3616,6 +3877,37 @@ function encodeSlideMediaRels(layout, runtime) {
|
|
|
3616
3877
|
/**
|
|
3617
3878
|
* PptxGenJS: XML Generation
|
|
3618
3879
|
*/
|
|
3880
|
+
const _warnedTextRangeMsgs = /* @__PURE__ */ new Set();
|
|
3881
|
+
function warnTextRangeOnce(msg) {
|
|
3882
|
+
if (_warnedTextRangeMsgs.has(msg)) return;
|
|
3883
|
+
_warnedTextRangeMsgs.add(msg);
|
|
3884
|
+
console.warn(msg);
|
|
3885
|
+
}
|
|
3886
|
+
/**
|
|
3887
|
+
* Clamp a font size (points) into ST_TextFontSize (1-4000pt) and return it in
|
|
3888
|
+
* hundredths of a point for the `sz` attribute. Out-of-range sizes make
|
|
3889
|
+
* PowerPoint report the package as needing repair (e.g. `sz` > 400000 or < 100).
|
|
3890
|
+
*/
|
|
3891
|
+
function clampFontSizeSz(fontSizePts) {
|
|
3892
|
+
const raw = Math.round(fontSizePts * 100);
|
|
3893
|
+
const clamped = Math.min(4e5, Math.max(100, raw));
|
|
3894
|
+
if (clamped !== raw) warnTextRangeOnce(`Warning: fontSize ${fontSizePts} is outside the valid range 1-4000pt; using ${clamped / 100}.`);
|
|
3895
|
+
return clamped;
|
|
3896
|
+
}
|
|
3897
|
+
/** Clamp character spacing (points) into ST_TextPoint (-4000..4000pt); returns hundredths for the `spc` attribute. */
|
|
3898
|
+
function clampCharSpacingSpc(charSpacingPts) {
|
|
3899
|
+
const raw = Math.round(charSpacingPts * 100);
|
|
3900
|
+
const clamped = Math.min(4e5, Math.max(-4e5, raw));
|
|
3901
|
+
if (clamped !== raw) warnTextRangeOnce(`Warning: charSpacing ${charSpacingPts} is outside the valid range -4000..4000pt; using ${clamped / 100}.`);
|
|
3902
|
+
return clamped;
|
|
3903
|
+
}
|
|
3904
|
+
/** Clamp line spacing (points) into ST_TextSpacingPoint (0..1584pt); returns hundredths for `<a:spcPts val>`. */
|
|
3905
|
+
function clampLineSpacingPts(lineSpacingPts) {
|
|
3906
|
+
const raw = Math.round(lineSpacingPts * 100);
|
|
3907
|
+
const clamped = Math.min(158400, Math.max(0, raw));
|
|
3908
|
+
if (clamped !== raw) warnTextRangeOnce(`Warning: lineSpacing ${lineSpacingPts} is outside the valid range 0-1584pt; using ${clamped / 100}.`);
|
|
3909
|
+
return clamped;
|
|
3910
|
+
}
|
|
3619
3911
|
const ImageSizingXml = {
|
|
3620
3912
|
cover: function(imgSize, boxDim) {
|
|
3621
3913
|
const imgRatio = imgSize.h / imgSize.w;
|
|
@@ -3662,23 +3954,90 @@ const ImageSizingXml = {
|
|
|
3662
3954
|
* @return {string} `<a:prstGeom>` XML
|
|
3663
3955
|
*/
|
|
3664
3956
|
const RECT_RADIUS_ADJ1_SHAPES = new Set(["round2SameRect", "round2DiagRect"]);
|
|
3957
|
+
const SHAPE_LOCK_ATTRS = [
|
|
3958
|
+
"noGrp",
|
|
3959
|
+
"noSelect",
|
|
3960
|
+
"noRot",
|
|
3961
|
+
"noChangeAspect",
|
|
3962
|
+
"noMove",
|
|
3963
|
+
"noResize",
|
|
3964
|
+
"noEditPoints",
|
|
3965
|
+
"noAdjustHandles",
|
|
3966
|
+
"noChangeArrowheads",
|
|
3967
|
+
"noChangeShapeType",
|
|
3968
|
+
"noTextEdit"
|
|
3969
|
+
];
|
|
3970
|
+
const PICTURE_LOCK_ATTRS = [
|
|
3971
|
+
"noGrp",
|
|
3972
|
+
"noSelect",
|
|
3973
|
+
"noRot",
|
|
3974
|
+
"noChangeAspect",
|
|
3975
|
+
"noMove",
|
|
3976
|
+
"noResize",
|
|
3977
|
+
"noEditPoints",
|
|
3978
|
+
"noAdjustHandles",
|
|
3979
|
+
"noChangeArrowheads",
|
|
3980
|
+
"noChangeShapeType",
|
|
3981
|
+
"noCrop"
|
|
3982
|
+
];
|
|
3983
|
+
const GRAPHIC_FRAME_LOCK_ATTRS = [
|
|
3984
|
+
"noGrp",
|
|
3985
|
+
"noDrilldown",
|
|
3986
|
+
"noSelect",
|
|
3987
|
+
"noChangeAspect",
|
|
3988
|
+
"noMove",
|
|
3989
|
+
"noResize"
|
|
3990
|
+
];
|
|
3991
|
+
/**
|
|
3992
|
+
* Serialize an object-lock element (`a:spLocks` / `a:picLocks` / `a:graphicFrameLocks`).
|
|
3993
|
+
* Only flags set to `true` AND valid for this element type are emitted; a flag set on an
|
|
3994
|
+
* unsupported element type is dropped with a warning (silent coercion is a footgun).
|
|
3995
|
+
* @param tag - locking element tag, e.g. `'a:spLocks'`
|
|
3996
|
+
* @param allowed - attribute names this element type supports, in desired emit order
|
|
3997
|
+
* @param locks - merged lock flags (callers fold any hard-coded default in first)
|
|
3998
|
+
* @param objectName - for the warning message
|
|
3999
|
+
* @returns the locking element string, or `''` when no applicable flag is set
|
|
4000
|
+
*/
|
|
4001
|
+
function genXmlObjectLock(tag, allowed, locks, objectName) {
|
|
4002
|
+
if (!locks) return "";
|
|
4003
|
+
const lockMap = locks;
|
|
4004
|
+
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.`);
|
|
4005
|
+
const attrs = allowed.filter((name) => lockMap[name] === true).map((name) => `${name}="1"`);
|
|
4006
|
+
return attrs.length > 0 ? `<${tag} ${attrs.join(" ")}/>` : "";
|
|
4007
|
+
}
|
|
3665
4008
|
function genXmlPresetGeom(shapeName, options, cx, cy) {
|
|
3666
|
-
|
|
4009
|
+
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.`);
|
|
4010
|
+
let avLst = "";
|
|
4011
|
+
const emittedAdjNames = /* @__PURE__ */ new Set();
|
|
4012
|
+
const emitGuide = (name, fmlaVal) => {
|
|
4013
|
+
avLst += `<a:gd name="${name}" fmla="val ${fmlaVal}"/>`;
|
|
4014
|
+
emittedAdjNames.add(name);
|
|
4015
|
+
};
|
|
3667
4016
|
if (options.rectRadius) {
|
|
3668
4017
|
const adjVal = Math.round(options.rectRadius * EMU * 1e5 / Math.min(cx, cy));
|
|
3669
4018
|
if (RECT_RADIUS_ADJ1_SHAPES.has(shapeName)) {
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
} else
|
|
4019
|
+
emitGuide("adj1", adjVal);
|
|
4020
|
+
emitGuide("adj2", 0);
|
|
4021
|
+
} else emitGuide("adj", adjVal);
|
|
3673
4022
|
} else if (options.angleRange) {
|
|
3674
4023
|
for (let i = 0; i < 2; i++) {
|
|
3675
4024
|
const angle = options.angleRange[i];
|
|
3676
|
-
|
|
4025
|
+
emitGuide(`adj${i + 1}`, convertRotationDegrees(angle));
|
|
3677
4026
|
}
|
|
3678
|
-
if (options.arcThicknessRatio)
|
|
4027
|
+
if (options.arcThicknessRatio) emitGuide("adj3", Math.round(options.arcThicknessRatio * 5e4));
|
|
3679
4028
|
}
|
|
3680
|
-
|
|
3681
|
-
|
|
4029
|
+
if (options.shapeAdjust) (Array.isArray(options.shapeAdjust) ? options.shapeAdjust : [options.shapeAdjust]).forEach((adj) => {
|
|
4030
|
+
if (!adj || typeof adj.name !== "string" || adj.name.length === 0 || typeof adj.value !== "number" || !isFinite(adj.value)) {
|
|
4031
|
+
console.warn(`Warning: shapeAdjust entry ${JSON.stringify(adj)} is invalid (needs { name:string, value:number }) and was ignored.`);
|
|
4032
|
+
return;
|
|
4033
|
+
}
|
|
4034
|
+
if (emittedAdjNames.has(adj.name)) {
|
|
4035
|
+
console.warn(`Warning: shapeAdjust "${adj.name}" was ignored because rectRadius/angleRange already set that handle.`);
|
|
4036
|
+
return;
|
|
4037
|
+
}
|
|
4038
|
+
emitGuide(adj.name, Math.round(adj.value * 1e5));
|
|
4039
|
+
});
|
|
4040
|
+
return `<a:prstGeom prst="${shapeName}"><a:avLst>${avLst}</a:avLst></a:prstGeom>`;
|
|
3682
4041
|
}
|
|
3683
4042
|
/**
|
|
3684
4043
|
* Emit an `<a:custGeom>` for a freeform path built from `points`.
|
|
@@ -3731,6 +4090,45 @@ function genXmlCustGeom(points, cx, cy, layout) {
|
|
|
3731
4090
|
}
|
|
3732
4091
|
const PLACEHOLDER_TYPE_MAP = PLACEHOLDER_TYPES;
|
|
3733
4092
|
/**
|
|
4093
|
+
* Emit the `<a:lnL>/<a:lnR>/<a:lnT>/<a:lnB>` border children of an `<a:tcPr>` for a table cell.
|
|
4094
|
+
* Shared by normal cells and the dummy span (`_hmerge`/`_vmerge`) cells so a merged region's
|
|
4095
|
+
* outer edges render with the same border as its origin cell (Issue #680).
|
|
4096
|
+
* @param {BorderProps[]} cellBorder - 4-tuple of border props in [top, right, bottom, left] order
|
|
4097
|
+
* @return {string} concatenated border element XML, in the LRTB document order PowerPoint expects
|
|
4098
|
+
*/
|
|
4099
|
+
function genTableCellBorderXml(cellBorder) {
|
|
4100
|
+
let strXml = "";
|
|
4101
|
+
[
|
|
4102
|
+
{
|
|
4103
|
+
idx: 3,
|
|
4104
|
+
name: "lnL"
|
|
4105
|
+
},
|
|
4106
|
+
{
|
|
4107
|
+
idx: 1,
|
|
4108
|
+
name: "lnR"
|
|
4109
|
+
},
|
|
4110
|
+
{
|
|
4111
|
+
idx: 0,
|
|
4112
|
+
name: "lnT"
|
|
4113
|
+
},
|
|
4114
|
+
{
|
|
4115
|
+
idx: 2,
|
|
4116
|
+
name: "lnB"
|
|
4117
|
+
}
|
|
4118
|
+
].forEach((obj) => {
|
|
4119
|
+
const border = cellBorder[obj.idx];
|
|
4120
|
+
if (!border) return;
|
|
4121
|
+
const cap = createLineCap(border.cap);
|
|
4122
|
+
if (border.type !== "none") {
|
|
4123
|
+
strXml += `<a:${obj.name} w="${valToPts(border.pt)}" cap="${cap}" cmpd="sng" algn="ctr">`;
|
|
4124
|
+
strXml += `<a:solidFill>${createColorElement(border.color)}</a:solidFill>`;
|
|
4125
|
+
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"/>`;
|
|
4126
|
+
strXml += `</a:${obj.name}>`;
|
|
4127
|
+
} else strXml += `<a:${obj.name} w="0" cap="${cap}" cmpd="sng" algn="ctr"><a:noFill/></a:${obj.name}>`;
|
|
4128
|
+
});
|
|
4129
|
+
return strXml;
|
|
4130
|
+
}
|
|
4131
|
+
/**
|
|
3734
4132
|
* Transforms a slide or slideLayout to resulting XML string - Creates `ppt/slide*.xml`
|
|
3735
4133
|
* @param {PresSlideInternal|SlideLayoutInternal} slideObject - slide object created within createSlideObject
|
|
3736
4134
|
* @return {string} XML string with <p:cSld> as the root
|
|
@@ -3789,7 +4187,10 @@ function slideObjectToXml(slide) {
|
|
|
3789
4187
|
intColCnt += cellOpts?.colspan ? Number(cellOpts.colspan) : 1;
|
|
3790
4188
|
});
|
|
3791
4189
|
strXml = `<p:graphicFrame><p:nvGraphicFramePr><p:cNvPr id="${intTableNum * slide._slideNum + 1}" name="${slideItemObj.options.objectName}" descr="${encodeXmlEntities(slideItemObj.options.altText || "")}"/>`;
|
|
3792
|
-
strXml +=
|
|
4190
|
+
strXml += `<p:cNvGraphicFramePr>${genXmlObjectLock("a:graphicFrameLocks", GRAPHIC_FRAME_LOCK_ATTRS, {
|
|
4191
|
+
noGrp: true,
|
|
4192
|
+
...slideItemObj.options.objectLock
|
|
4193
|
+
}, 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>`;
|
|
3793
4194
|
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>`;
|
|
3794
4195
|
{
|
|
3795
4196
|
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\"" : "");
|
|
@@ -3821,7 +4222,8 @@ function slideObjectToXml(slide) {
|
|
|
3821
4222
|
return {
|
|
3822
4223
|
_type: "tablecell",
|
|
3823
4224
|
options: { rowspan },
|
|
3824
|
-
_hmerge: true
|
|
4225
|
+
_hmerge: true,
|
|
4226
|
+
_spanOrigin: cell
|
|
3825
4227
|
};
|
|
3826
4228
|
});
|
|
3827
4229
|
cells.splice(cIdx + 1, 0, ...vMergeCells);
|
|
@@ -3837,12 +4239,14 @@ function slideObjectToXml(slide) {
|
|
|
3837
4239
|
const colspan = cell.options?.colspan;
|
|
3838
4240
|
const _hmerge = cell._hmerge;
|
|
3839
4241
|
if (rowspan && rowspan > 1) {
|
|
4242
|
+
const _spanOrigin = cell._spanOrigin || cell;
|
|
3840
4243
|
const hMergeCell = {
|
|
3841
4244
|
_type: "tablecell",
|
|
3842
4245
|
options: { colspan },
|
|
3843
4246
|
_rowContinue: rowspan - 1,
|
|
3844
4247
|
_vmerge: true,
|
|
3845
|
-
_hmerge
|
|
4248
|
+
_hmerge,
|
|
4249
|
+
_spanOrigin
|
|
3846
4250
|
};
|
|
3847
4251
|
nextRow.splice(cIdx, 0, hMergeCell);
|
|
3848
4252
|
}
|
|
@@ -3852,7 +4256,7 @@ function slideObjectToXml(slide) {
|
|
|
3852
4256
|
let intRowH = 0;
|
|
3853
4257
|
if (Array.isArray(objTabOpts.rowH) && objTabOpts.rowH[rIdx]) intRowH = inch2Emu(Number(objTabOpts.rowH[rIdx]));
|
|
3854
4258
|
else if (objTabOpts.rowH && !isNaN(Number(objTabOpts.rowH))) intRowH = inch2Emu(Number(objTabOpts.rowH));
|
|
3855
|
-
else if (slideItemObj.options.cy || slideItemObj.options.h) intRowH = Math.round((slideItemObj.options.h ?
|
|
4259
|
+
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);
|
|
3856
4260
|
strXml += `<a:tr h="${intRowH}">`;
|
|
3857
4261
|
cells.forEach((cellObj) => {
|
|
3858
4262
|
const cell = cellObj;
|
|
@@ -3865,7 +4269,17 @@ function slideObjectToXml(slide) {
|
|
|
3865
4269
|
let cellSpanAttrStr = Object.entries(cellSpanAttrs).filter(([, v]) => !!v).map(([k, v]) => `${String(k)}="${String(v)}"`).join(" ");
|
|
3866
4270
|
if (cellSpanAttrStr) cellSpanAttrStr = " " + cellSpanAttrStr;
|
|
3867
4271
|
if (cell._hmerge || cell._vmerge) {
|
|
3868
|
-
|
|
4272
|
+
const origin = cell._spanOrigin;
|
|
4273
|
+
let spanPrXml = "";
|
|
4274
|
+
if (origin) {
|
|
4275
|
+
const originOpts = origin.options || {};
|
|
4276
|
+
const originBorder = Array.isArray(originOpts.border) ? originOpts.border : null;
|
|
4277
|
+
if (originBorder) spanPrXml += genTableCellBorderXml(originBorder);
|
|
4278
|
+
let spanFill = origin._optImp?.fill?.color ? origin._optImp.fill.color : origin._optImp?.fill && typeof origin._optImp.fill === "string" ? origin._optImp.fill : "";
|
|
4279
|
+
spanFill = spanFill || originOpts.fill ? originOpts.fill : "";
|
|
4280
|
+
if (spanFill) spanPrXml += genXmlColorSelection(spanFill);
|
|
4281
|
+
}
|
|
4282
|
+
strXml += `<a:tc${cellSpanAttrStr}><a:tcPr>${spanPrXml}</a:tcPr></a:tc>`;
|
|
3869
4283
|
return;
|
|
3870
4284
|
}
|
|
3871
4285
|
const cellOpts = cell.options || {};
|
|
@@ -3909,32 +4323,7 @@ function slideObjectToXml(slide) {
|
|
|
3909
4323
|
else cellMarginXml = ` marL="${inch2Emu(cellMargin[3])}" marR="${inch2Emu(cellMargin[1])}" marT="${inch2Emu(cellMargin[0])}" marB="${inch2Emu(cellMargin[2])}"`;
|
|
3910
4324
|
strXml += `<a:tc${cellSpanAttrStr}>${genXmlTextBody(cell)}<a:tcPr${cellMarginXml}${cellValign}${cellTextDir}>`;
|
|
3911
4325
|
const cellBorder = Array.isArray(cellOpts.border) ? cellOpts.border : null;
|
|
3912
|
-
if (cellBorder)
|
|
3913
|
-
{
|
|
3914
|
-
idx: 3,
|
|
3915
|
-
name: "lnL"
|
|
3916
|
-
},
|
|
3917
|
-
{
|
|
3918
|
-
idx: 1,
|
|
3919
|
-
name: "lnR"
|
|
3920
|
-
},
|
|
3921
|
-
{
|
|
3922
|
-
idx: 0,
|
|
3923
|
-
name: "lnT"
|
|
3924
|
-
},
|
|
3925
|
-
{
|
|
3926
|
-
idx: 2,
|
|
3927
|
-
name: "lnB"
|
|
3928
|
-
}
|
|
3929
|
-
].forEach((obj) => {
|
|
3930
|
-
const border = cellBorder[obj.idx];
|
|
3931
|
-
if (border.type !== "none") {
|
|
3932
|
-
strXml += `<a:${obj.name} w="${valToPts(border.pt)}" cap="flat" cmpd="sng" algn="ctr">`;
|
|
3933
|
-
strXml += `<a:solidFill>${createColorElement(border.color)}</a:solidFill>`;
|
|
3934
|
-
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"/>`;
|
|
3935
|
-
strXml += `</a:${obj.name}>`;
|
|
3936
|
-
} else strXml += `<a:${obj.name} w="0" cap="flat" cmpd="sng" algn="ctr"><a:noFill/></a:${obj.name}>`;
|
|
3937
|
-
});
|
|
4326
|
+
if (cellBorder) strXml += genTableCellBorderXml(cellBorder);
|
|
3938
4327
|
strXml += cellFill;
|
|
3939
4328
|
strXml += " </a:tcPr>";
|
|
3940
4329
|
strXml += " </a:tc>";
|
|
@@ -3953,10 +4342,10 @@ function slideObjectToXml(slide) {
|
|
|
3953
4342
|
if (!slideItemObj.options.line && cy === 0) cy = EMU * .3;
|
|
3954
4343
|
if (!slideItemObj.options._bodyProp) slideItemObj.options._bodyProp = {};
|
|
3955
4344
|
if (slideItemObj.options.margin && Array.isArray(slideItemObj.options.margin)) {
|
|
3956
|
-
slideItemObj.options._bodyProp.
|
|
4345
|
+
slideItemObj.options._bodyProp.tIns = valToPts(slideItemObj.options.margin[0] || 0);
|
|
3957
4346
|
slideItemObj.options._bodyProp.rIns = valToPts(slideItemObj.options.margin[1] || 0);
|
|
3958
4347
|
slideItemObj.options._bodyProp.bIns = valToPts(slideItemObj.options.margin[2] || 0);
|
|
3959
|
-
slideItemObj.options._bodyProp.
|
|
4348
|
+
slideItemObj.options._bodyProp.lIns = valToPts(slideItemObj.options.margin[3] || 0);
|
|
3960
4349
|
} else if (typeof slideItemObj.options.margin === "number") {
|
|
3961
4350
|
slideItemObj.options._bodyProp.lIns = valToPts(slideItemObj.options.margin);
|
|
3962
4351
|
slideItemObj.options._bodyProp.rIns = valToPts(slideItemObj.options.margin);
|
|
@@ -3968,7 +4357,11 @@ function slideObjectToXml(slide) {
|
|
|
3968
4357
|
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) : ""}"/>`;
|
|
3969
4358
|
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"/>`;
|
|
3970
4359
|
strSlideXml += "</p:cNvPr>";
|
|
3971
|
-
|
|
4360
|
+
{
|
|
4361
|
+
const spLockXml = genXmlObjectLock("a:spLocks", SHAPE_LOCK_ATTRS, slideItemObj.options.objectLock, slideItemObj.options.objectName);
|
|
4362
|
+
strSlideXml += "<p:cNvSpPr" + (slideItemObj.options?.isTextBox ? " txBox=\"1\"" : "");
|
|
4363
|
+
strSlideXml += spLockXml ? `>${spLockXml}</p:cNvSpPr>` : "/>";
|
|
4364
|
+
}
|
|
3972
4365
|
strSlideXml += `<p:nvPr>${slideItemObj._type === "placeholder" ? genXmlPlaceholder(slideItemObj) : genXmlPlaceholder(placeholderObj)}</p:nvPr>`;
|
|
3973
4366
|
strSlideXml += "</p:nvSpPr><p:spPr>";
|
|
3974
4367
|
strSlideXml += `<a:xfrm${locationAttr}>`;
|
|
@@ -3978,7 +4371,8 @@ function slideObjectToXml(slide) {
|
|
|
3978
4371
|
else strSlideXml += genXmlPresetGeom(slideItemObj.shape, slideItemObj.options, cx, cy);
|
|
3979
4372
|
strSlideXml += slideItemObj.options.fill ? genXmlColorSelection(slideItemObj.options.fill) : "<a:noFill/>";
|
|
3980
4373
|
if (slideItemObj.options.line) {
|
|
3981
|
-
|
|
4374
|
+
const lnAttrs = (slideItemObj.options.line.width ? ` w="${lineWidthToEmu(slideItemObj.options.line.width)}"` : "") + (slideItemObj.options.line.cap ? ` cap="${createLineCap(slideItemObj.options.line.cap)}"` : "");
|
|
4375
|
+
strSlideXml += `<a:ln${lnAttrs}>`;
|
|
3982
4376
|
if (slideItemObj.options.line.color) strSlideXml += genXmlColorSelection(slideItemObj.options.line);
|
|
3983
4377
|
if (slideItemObj.options.line.dashType) strSlideXml += `<a:prstDash val="${slideItemObj.options.line.dashType}"/>`;
|
|
3984
4378
|
if (slideItemObj.options.line.beginArrowType) strSlideXml += `<a:headEnd type="${slideItemObj.options.line.beginArrowType}"/>`;
|
|
@@ -4011,7 +4405,10 @@ function slideObjectToXml(slide) {
|
|
|
4011
4405
|
if (slideItemObj.hyperlink?.url) strSlideXml += `<a:hlinkClick r:id="rId${slideItemObj.hyperlink._rId}" tooltip="${slideItemObj.hyperlink.tooltip ? encodeXmlEntities(slideItemObj.hyperlink.tooltip) : ""}"/>`;
|
|
4012
4406
|
if (slideItemObj.hyperlink?.slide) strSlideXml += `<a:hlinkClick r:id="rId${slideItemObj.hyperlink._rId}" tooltip="${slideItemObj.hyperlink.tooltip ? encodeXmlEntities(slideItemObj.hyperlink.tooltip) : ""}" action="ppaction://hlinksldjump"/>`;
|
|
4013
4407
|
strSlideXml += " </p:cNvPr>";
|
|
4014
|
-
strSlideXml +=
|
|
4408
|
+
strSlideXml += ` <p:cNvPicPr>${genXmlObjectLock("a:picLocks", PICTURE_LOCK_ATTRS, {
|
|
4409
|
+
noChangeAspect: true,
|
|
4410
|
+
...slideItemObj.options.objectLock
|
|
4411
|
+
}, slideItemObj.options.objectName)}</p:cNvPicPr>`;
|
|
4015
4412
|
strSlideXml += " <p:nvPr>" + genXmlPlaceholder(placeholderObj) + "</p:nvPr>";
|
|
4016
4413
|
strSlideXml += " </p:nvPicPr>";
|
|
4017
4414
|
strSlideXml += "<p:blipFill>";
|
|
@@ -4086,7 +4483,7 @@ function slideObjectToXml(slide) {
|
|
|
4086
4483
|
strSlideXml += "<p:pic>";
|
|
4087
4484
|
strSlideXml += " <p:nvPicPr>";
|
|
4088
4485
|
strSlideXml += `<p:cNvPr id="${slideItemObj.mediaRid + 2}" name="${slideItemObj.options.objectName}" descr="${encodeXmlEntities(slideItemObj.options.altText || "")}"/>`;
|
|
4089
|
-
strSlideXml +=
|
|
4486
|
+
strSlideXml += ` <p:cNvPicPr>${genXmlObjectLock("a:picLocks", PICTURE_LOCK_ATTRS, slideItemObj.options.objectLock, slideItemObj.options.objectName)}</p:cNvPicPr>`;
|
|
4090
4487
|
strSlideXml += " <p:nvPr>";
|
|
4091
4488
|
strSlideXml += ` <a:videoFile r:link="rId${slideItemObj.mediaRid}"/>`;
|
|
4092
4489
|
strSlideXml += " </p:nvPr>";
|
|
@@ -4101,7 +4498,10 @@ function slideObjectToXml(slide) {
|
|
|
4101
4498
|
strSlideXml += "<p:pic>";
|
|
4102
4499
|
strSlideXml += " <p:nvPicPr>";
|
|
4103
4500
|
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>`;
|
|
4104
|
-
strSlideXml +=
|
|
4501
|
+
strSlideXml += ` <p:cNvPicPr>${genXmlObjectLock("a:picLocks", PICTURE_LOCK_ATTRS, {
|
|
4502
|
+
noChangeAspect: true,
|
|
4503
|
+
...slideItemObj.options.objectLock
|
|
4504
|
+
}, slideItemObj.options.objectName)}</p:cNvPicPr>`;
|
|
4105
4505
|
strSlideXml += " <p:nvPr>";
|
|
4106
4506
|
strSlideXml += ` <a:videoFile r:link="rId${slideItemObj.mediaRid}"/>`;
|
|
4107
4507
|
strSlideXml += " <p:extLst>";
|
|
@@ -4165,7 +4565,7 @@ function slideObjectToXml(slide) {
|
|
|
4165
4565
|
strSlideXml += "/>";
|
|
4166
4566
|
strSlideXml += " <a:lstStyle><a:lvl1pPr>";
|
|
4167
4567
|
if (slide._slideNumberProps.fontFace || slide._slideNumberProps.fontSize || slide._slideNumberProps.color) {
|
|
4168
|
-
strSlideXml += `<a:defRPr sz="${
|
|
4568
|
+
strSlideXml += `<a:defRPr sz="${clampFontSizeSz(slide._slideNumberProps.fontSize || 12)}">`;
|
|
4169
4569
|
if (slide._slideNumberProps.color) strSlideXml += genXmlColorSelection(slide._slideNumberProps.color);
|
|
4170
4570
|
if (slide._slideNumberProps.fontFace) strSlideXml += `<a:latin typeface="${slide._slideNumberProps.fontFace}"/><a:ea typeface="${slide._slideNumberProps.fontFace}"/><a:cs typeface="${slide._slideNumberProps.fontFace}"/>`;
|
|
4171
4571
|
strSlideXml += "</a:defRPr>";
|
|
@@ -4254,7 +4654,7 @@ function genXmlParagraphProperties(textObj, isDefault) {
|
|
|
4254
4654
|
paragraphPropXml += "";
|
|
4255
4655
|
break;
|
|
4256
4656
|
}
|
|
4257
|
-
if (textObj.options.lineSpacing) strXmlLnSpc = `<a:lnSpc><a:spcPts val="${
|
|
4657
|
+
if (textObj.options.lineSpacing) strXmlLnSpc = `<a:lnSpc><a:spcPts val="${clampLineSpacingPts(textObj.options.lineSpacing)}"/></a:lnSpc>`;
|
|
4258
4658
|
else if (textObj.options.lineSpacingMultiple) strXmlLnSpc = `<a:lnSpc><a:spcPct val="${Math.round(textObj.options.lineSpacingMultiple * 1e5)}"/></a:lnSpc>`;
|
|
4259
4659
|
if (textObj.options.indentLevel && !isNaN(Number(textObj.options.indentLevel)) && textObj.options.indentLevel > 0) paragraphPropXml += ` lvl="${textObj.options.indentLevel}"`;
|
|
4260
4660
|
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>`;
|
|
@@ -4308,7 +4708,7 @@ function genXmlTextRunProperties(opts, isDefault) {
|
|
|
4308
4708
|
let runProps = "";
|
|
4309
4709
|
const runPropsTag = isDefault ? "a:defRPr" : "a:rPr";
|
|
4310
4710
|
runProps += "<" + runPropsTag + " lang=\"" + (opts.lang ? opts.lang : "en-US") + "\"" + (opts.lang ? " altLang=\"en-US\"" : "");
|
|
4311
|
-
runProps += opts.fontSize ? ` sz="${
|
|
4711
|
+
runProps += opts.fontSize ? ` sz="${clampFontSizeSz(opts.fontSize)}"` : "";
|
|
4312
4712
|
runProps += opts?.bold ? ` b="${opts.bold ? "1" : "0"}"` : "";
|
|
4313
4713
|
runProps += opts?.italic ? ` i="${opts.italic ? "1" : "0"}"` : "";
|
|
4314
4714
|
runProps += opts?.strike ? ` strike="${typeof opts.strike === "string" ? opts.strike : "sngStrike"}"` : "";
|
|
@@ -4319,17 +4719,23 @@ function genXmlTextRunProperties(opts, isDefault) {
|
|
|
4319
4719
|
if (opts.baseline) runProps += ` baseline="${Math.round(opts.baseline * 50)}"`;
|
|
4320
4720
|
else if (opts.subscript) runProps += " baseline=\"-40000\"";
|
|
4321
4721
|
else if (opts.superscript) runProps += " baseline=\"30000\"";
|
|
4322
|
-
runProps += opts.charSpacing ? ` spc="${
|
|
4722
|
+
runProps += opts.charSpacing ? ` spc="${clampCharSpacingSpc(opts.charSpacing)}" kern="0"` : "";
|
|
4323
4723
|
runProps += " dirty=\"0\">";
|
|
4324
|
-
|
|
4325
|
-
|
|
4724
|
+
const hasShadow = !!opts.shadow && opts.shadow.type !== "none";
|
|
4725
|
+
if (opts.color || opts.fontFace || opts.outline || opts.glow || hasShadow || typeof opts.underline === "object" && opts.underline.color) {
|
|
4726
|
+
if (opts.outline && typeof opts.outline === "object") runProps += `<a:ln w="${lineWidthToEmu(opts.outline.size || .75)}">${genXmlColorSelection(opts.outline.color || "FFFFFF")}</a:ln>`;
|
|
4326
4727
|
if (opts.color) runProps += genXmlColorSelection({
|
|
4327
4728
|
color: opts.color,
|
|
4328
4729
|
transparency: opts.transparency
|
|
4329
4730
|
});
|
|
4731
|
+
if (opts.glow || hasShadow) {
|
|
4732
|
+
runProps += "<a:effectLst>";
|
|
4733
|
+
if (opts.glow) runProps += createGlowElement(opts.glow, DEF_TEXT_GLOW);
|
|
4734
|
+
if (hasShadow) runProps += createShadowElement$1(opts.shadow, DEF_TEXT_SHADOW);
|
|
4735
|
+
runProps += "</a:effectLst>";
|
|
4736
|
+
}
|
|
4330
4737
|
if (opts.highlight) runProps += `<a:highlight>${createColorElement(opts.highlight)}</a:highlight>`;
|
|
4331
4738
|
if (typeof opts.underline === "object" && opts.underline.color) runProps += `<a:uFill>${genXmlColorSelection(opts.underline.color)}</a:uFill>`;
|
|
4332
|
-
if (opts.glow) runProps += `<a:effectLst>${createGlowElement(opts.glow, DEF_TEXT_GLOW)}</a:effectLst>`;
|
|
4333
4739
|
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"/>`;
|
|
4334
4740
|
}
|
|
4335
4741
|
if (opts.hyperlink) {
|
|
@@ -4359,6 +4765,28 @@ function genXmlTextRun(textObj) {
|
|
|
4359
4765
|
return `<a:r>${genXmlTextRunProperties(textObj.options, false)}<a:t>${encodeXmlEntities(String(textObj.text))}</a:t></a:r>`;
|
|
4360
4766
|
}
|
|
4361
4767
|
/**
|
|
4768
|
+
* Builds `<a:normAutofit>` with explicit fontScale/lnSpcReduction for "shrink text on overflow"
|
|
4769
|
+
* @param {TextFitShrinkProps} fit - shrink fit options
|
|
4770
|
+
* @return {string} XML string (`<a:normAutofit .../>`)
|
|
4771
|
+
* @see ECMA-376 CT_TextNormAutofit (attributes in 1000ths of a percent)
|
|
4772
|
+
*/
|
|
4773
|
+
function genXmlNormAutofit(fit) {
|
|
4774
|
+
let attrs = "";
|
|
4775
|
+
const pct = (val, name) => {
|
|
4776
|
+
if (val === void 0 || val === null) return null;
|
|
4777
|
+
if (typeof val !== "number" || isNaN(val) || val < 0 || val > 100) {
|
|
4778
|
+
console.warn(`Warning: fit.${name} must be a number between 0 and 100 (percent); received ${String(val)} - attribute ignored.`);
|
|
4779
|
+
return null;
|
|
4780
|
+
}
|
|
4781
|
+
return Math.round(val * 1e3);
|
|
4782
|
+
};
|
|
4783
|
+
const fontScale = pct(fit.fontScale, "fontScale");
|
|
4784
|
+
if (fontScale !== null) attrs += ` fontScale="${fontScale}"`;
|
|
4785
|
+
const lnSpcReduction = pct(fit.lnSpcReduction, "lnSpcReduction");
|
|
4786
|
+
if (lnSpcReduction !== null) attrs += ` lnSpcReduction="${lnSpcReduction}"`;
|
|
4787
|
+
return `<a:normAutofit${attrs}/>`;
|
|
4788
|
+
}
|
|
4789
|
+
/**
|
|
4362
4790
|
* Builds `<a:bodyPr></a:bodyPr>` tag for "genXmlTextBody()"
|
|
4363
4791
|
* @param {ISlideObject | TableCell} slideObject - various options
|
|
4364
4792
|
* @return {string} XML string
|
|
@@ -4371,6 +4799,8 @@ function genXmlBodyProperties(slideObject) {
|
|
|
4371
4799
|
if (slideObject.options._bodyProp.tIns || slideObject.options._bodyProp.tIns === 0) bodyProperties += ` tIns="${slideObject.options._bodyProp.tIns}"`;
|
|
4372
4800
|
if (slideObject.options._bodyProp.rIns || slideObject.options._bodyProp.rIns === 0) bodyProperties += ` rIns="${slideObject.options._bodyProp.rIns}"`;
|
|
4373
4801
|
if (slideObject.options._bodyProp.bIns || slideObject.options._bodyProp.bIns === 0) bodyProperties += ` bIns="${slideObject.options._bodyProp.bIns}"`;
|
|
4802
|
+
if (slideObject.options._bodyProp.numCol) bodyProperties += ` numCol="${slideObject.options._bodyProp.numCol}"`;
|
|
4803
|
+
if (slideObject.options._bodyProp.spcCol) bodyProperties += ` spcCol="${slideObject.options._bodyProp.spcCol}"`;
|
|
4374
4804
|
bodyProperties += " rtlCol=\"0\"";
|
|
4375
4805
|
if (slideObject.options._bodyProp.anchor) bodyProperties += " anchor=\"" + slideObject.options._bodyProp.anchor + "\"";
|
|
4376
4806
|
if (slideObject.options._bodyProp.vert) bodyProperties += " vert=\"" + slideObject.options._bodyProp.vert + "\"";
|
|
@@ -4381,9 +4811,11 @@ function genXmlBodyProperties(slideObject) {
|
|
|
4381
4811
|
* @see: http://www.datypic.com/sc/ooxml/g-a_EG_TextAutofit.html
|
|
4382
4812
|
*/
|
|
4383
4813
|
if (slideObject.options.fit) {
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
else if (
|
|
4814
|
+
const fit = slideObject.options.fit;
|
|
4815
|
+
if (fit === "none") bodyProperties += "";
|
|
4816
|
+
else if (fit === "shrink") bodyProperties += "<a:normAutofit/>";
|
|
4817
|
+
else if (fit === "resize") bodyProperties += "<a:spAutoFit/>";
|
|
4818
|
+
else if (typeof fit === "object" && fit.type === "shrink") bodyProperties += genXmlNormAutofit(fit);
|
|
4387
4819
|
}
|
|
4388
4820
|
if (slideObject.options.shrinkText) bodyProperties += "<a:normAutofit/>";
|
|
4389
4821
|
bodyProperties += slideObject.options._bodyProp.autoFit ? "<a:spAutoFit/>" : "";
|
|
@@ -4519,13 +4951,13 @@ function genXmlTextBody(slideObj) {
|
|
|
4519
4951
|
}
|
|
4520
4952
|
});
|
|
4521
4953
|
if (slideObj._type === "tablecell" && (opts.fontSize || opts.fontFace)) if (opts.fontFace) {
|
|
4522
|
-
strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}"` + (opts.fontSize ? ` sz="${
|
|
4954
|
+
strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}"` + (opts.fontSize ? ` sz="${clampFontSizeSz(opts.fontSize)}"` : "") + " dirty=\"0\">";
|
|
4523
4955
|
strSlideXml += `<a:latin typeface="${opts.fontFace}" charset="0"/>`;
|
|
4524
4956
|
strSlideXml += `<a:ea typeface="${opts.fontFace}" charset="0"/>`;
|
|
4525
4957
|
strSlideXml += `<a:cs typeface="${opts.fontFace}" charset="0"/>`;
|
|
4526
4958
|
strSlideXml += "</a:endParaRPr>";
|
|
4527
|
-
} else strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}"` + (opts.fontSize ? ` sz="${
|
|
4528
|
-
else if (reqsClosingFontSize) strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}"` + (opts.fontSize ? ` sz="${
|
|
4959
|
+
} else strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}"` + (opts.fontSize ? ` sz="${clampFontSizeSz(opts.fontSize)}"` : "") + " dirty=\"0\"/>";
|
|
4960
|
+
else if (reqsClosingFontSize) strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}"` + (opts.fontSize ? ` sz="${clampFontSizeSz(opts.fontSize)}"` : "") + " dirty=\"0\"/>";
|
|
4529
4961
|
else strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}" dirty="0"/>`;
|
|
4530
4962
|
strSlideXml += "</a:p>";
|
|
4531
4963
|
});
|
|
@@ -5001,7 +5433,7 @@ function genXmlTableStyleBorders(border) {
|
|
|
5001
5433
|
xml += `<a:${side}>`;
|
|
5002
5434
|
if (b.type === "none") xml += "<a:ln><a:noFill/></a:ln>";
|
|
5003
5435
|
else {
|
|
5004
|
-
xml += `<a:ln w="${
|
|
5436
|
+
xml += `<a:ln w="${lineWidthToEmu(b.pt ?? 1)}" cap="flat" cmpd="sng" algn="ctr">`;
|
|
5005
5437
|
xml += `<a:solidFill>${createColorElement(b.color ?? "666666")}</a:solidFill>`;
|
|
5006
5438
|
xml += `<a:prstDash val="${b.type === "dash" ? "sysDash" : "solid"}"/>`;
|
|
5007
5439
|
xml += "</a:ln>";
|
|
@@ -5079,7 +5511,7 @@ function makeXmlViewProps() {
|
|
|
5079
5511
|
* @see https://docs.microsoft.com/en-us/office/open-xml/structure-of-a-presentationml-document
|
|
5080
5512
|
* @see https://docs.microsoft.com/en-us/previous-versions/office/developer/office-2010/hh273476(v=office.14)
|
|
5081
5513
|
*/
|
|
5082
|
-
const VERSION = "5.
|
|
5514
|
+
const VERSION = "5.3.0";
|
|
5083
5515
|
function standardLayoutToPresLayout(layout) {
|
|
5084
5516
|
return {
|
|
5085
5517
|
name: layout.name,
|
|
@@ -5411,6 +5843,18 @@ var PptxGenJS = class {
|
|
|
5411
5843
|
});
|
|
5412
5844
|
arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(this._masterSlide, this._runtime));
|
|
5413
5845
|
return await Promise.all(arrMediaPromises).then(async () => {
|
|
5846
|
+
const canonicalMediaTargets = /* @__PURE__ */ new Map();
|
|
5847
|
+
for (const target of [
|
|
5848
|
+
...this._slides,
|
|
5849
|
+
...this._slideLayouts,
|
|
5850
|
+
this._masterSlide
|
|
5851
|
+
]) for (const rel of target._relsMedia || []) {
|
|
5852
|
+
if (rel.type === "online" || rel.type === "hyperlink" || typeof rel.data !== "string" || !rel.data) continue;
|
|
5853
|
+
const key = (rel.extn || "") + "\0" + rel.data;
|
|
5854
|
+
const canonical = canonicalMediaTargets.get(key);
|
|
5855
|
+
if (canonical) rel.Target = canonical;
|
|
5856
|
+
else canonicalMediaTargets.set(key, rel.Target);
|
|
5857
|
+
}
|
|
5414
5858
|
this._slides.forEach((slide) => {
|
|
5415
5859
|
if (slide._slideLayout) addPlaceholdersToSlideLayouts(slide);
|
|
5416
5860
|
});
|
|
@@ -5676,4 +6120,4 @@ var PptxGenJS = class {
|
|
|
5676
6120
|
//#endregion
|
|
5677
6121
|
export { PptxGenJS as t };
|
|
5678
6122
|
|
|
5679
|
-
//# sourceMappingURL=pptxgen
|
|
6123
|
+
//# sourceMappingURL=pptxgen-B-mAxCRC.js.map
|