@shbernal/pptxgenjs 5.1.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-tG59vAUr.js → browser-CzGC6NnM.js} +5 -5
- package/dist/browser-CzGC6NnM.js.map +1 -0
- package/dist/browser.d.ts +3 -3
- package/dist/browser.js +4 -4
- package/dist/{core-enums-BCaI1VAf.js → core-interfaces-BFXQk67T.js} +134 -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 +5 -5
- package/dist/node.js.map +1 -1
- package/dist/{pptxgen-3gNqxx8n.js → pptxgen-B-mAxCRC.js} +1093 -337
- package/dist/pptxgen-B-mAxCRC.js.map +1 -0
- package/dist/{pptxgen-CKBnfeDh.d.ts → pptxgen-Clv3zz3l.d.ts} +32 -2
- package/dist/pptxgen-Clv3zz3l.d.ts.map +1 -0
- package/dist/standalone.d.ts +630 -31
- package/dist/standalone.d.ts.map +1 -1
- package/dist/standalone.js +1268 -343
- 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-Bv8xqGyH.d.ts → units-BPRPrYRg.d.ts} +601 -32
- package/dist/units-BPRPrYRg.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/browser-tG59vAUr.js.map +0 -1
- package/dist/core-enums-BCaI1VAf.js.map +0 -1
- package/dist/pptxgen-3gNqxx8n.js.map +0 -1
- package/dist/pptxgen-CKBnfeDh.d.ts.map +0 -1
- package/dist/units-Bv8xqGyH.d.ts.map +0 -1
- package/dist/units-DmzbVUNp.js +0 -62
- package/dist/units-DmzbVUNp.js.map +0 -1
|
@@ -1,32 +1,25 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { A as
|
|
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) {
|
|
@@ -244,10 +294,32 @@ function genXmlGradientFill(gradient) {
|
|
|
244
294
|
return strXml;
|
|
245
295
|
}
|
|
246
296
|
/**
|
|
297
|
+
* Create a native DrawingML pattern fill.
|
|
298
|
+
* @param {PatternFillProps} pattern pattern fill options
|
|
299
|
+
* @returns XML string
|
|
300
|
+
*/
|
|
301
|
+
function genXmlPatternFill(pattern) {
|
|
302
|
+
if (!pattern) throw new Error("Pattern fill requires a pattern object.");
|
|
303
|
+
const fgColor = pattern.fgColor ?? "000000";
|
|
304
|
+
const bgColor = pattern.bgColor ?? "FFFFFF";
|
|
305
|
+
return `<a:pattFill prst="${pattern.preset}"><a:fgClr>${createColorElement(fgColor)}</a:fgClr><a:bgClr>${createColorElement(bgColor)}</a:bgClr></a:pattFill>`;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
247
308
|
* Create color selection
|
|
248
309
|
* @param {Color | ShapeFillProps | ShapeLineProps} props fill props
|
|
249
310
|
* @returns XML string
|
|
250
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
|
+
}
|
|
251
323
|
function genXmlColorSelection(props) {
|
|
252
324
|
let fillType = "solid";
|
|
253
325
|
let colorVal = "";
|
|
@@ -258,8 +330,8 @@ function genXmlColorSelection(props) {
|
|
|
258
330
|
else {
|
|
259
331
|
if (props.type) fillType = props.type;
|
|
260
332
|
if (props.color) colorVal = props.color;
|
|
261
|
-
if (props.alpha) internalElements += `<a:alpha val="${
|
|
262
|
-
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)}"/>`;
|
|
263
335
|
}
|
|
264
336
|
switch (fillType) {
|
|
265
337
|
case "solid":
|
|
@@ -268,6 +340,9 @@ function genXmlColorSelection(props) {
|
|
|
268
340
|
case "gradient":
|
|
269
341
|
outText += genXmlGradientFill(typeof props === "string" ? void 0 : props.gradient);
|
|
270
342
|
break;
|
|
343
|
+
case "pattern":
|
|
344
|
+
outText += genXmlPatternFill(typeof props === "string" ? void 0 : props.pattern);
|
|
345
|
+
break;
|
|
271
346
|
default:
|
|
272
347
|
outText += "";
|
|
273
348
|
break;
|
|
@@ -333,6 +408,123 @@ function svgMarkupToDataUri(svg) {
|
|
|
333
408
|
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
334
409
|
return `data:image/svg+xml;base64,${btoa(binary)}`;
|
|
335
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* Decode a base64 image payload (raw base64 or a `data:` URI) to bytes.
|
|
413
|
+
* - tolerant of the `data:[mime];base64,` prefix and of whitespace in the payload
|
|
414
|
+
* @param {string} b64 - base64 string or data URI
|
|
415
|
+
* @returns {Uint8Array | null} decoded bytes, or `null` when the payload is empty/undecodable
|
|
416
|
+
*/
|
|
417
|
+
function decodeBase64ToBytes(b64) {
|
|
418
|
+
if (!b64) return null;
|
|
419
|
+
const comma = b64.indexOf("base64,");
|
|
420
|
+
const payload = (comma >= 0 ? b64.slice(comma + 7) : b64).replace(/\s/g, "");
|
|
421
|
+
if (!payload) return null;
|
|
422
|
+
try {
|
|
423
|
+
const binary = atob(payload);
|
|
424
|
+
const bytes = new Uint8Array(binary.length);
|
|
425
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
426
|
+
return bytes;
|
|
427
|
+
} catch {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Read the intrinsic pixel dimensions of a raster image from its header bytes.
|
|
433
|
+
* - synchronous: parses only file-format headers, never decodes pixels
|
|
434
|
+
* - supports PNG, JPEG, GIF, BMP, and WebP (VP8 / VP8L / VP8X)
|
|
435
|
+
* - vector (SVG) and unrecognized formats return `null` (no intrinsic pixel size)
|
|
436
|
+
*
|
|
437
|
+
* Used by image `sizing: 'cover' | 'contain'` to compute an aspect-correct
|
|
438
|
+
* `<a:srcRect>` crop from the *natural* image ratio rather than the displayed box.
|
|
439
|
+
* @param {string} dataB64 - base64 image payload or `data:` URI
|
|
440
|
+
* @returns {{ w: number, h: number } | null} natural pixel size, or `null` when unmeasurable
|
|
441
|
+
*/
|
|
442
|
+
function getImageSizeFromBase64(dataB64) {
|
|
443
|
+
const b = decodeBase64ToBytes(dataB64);
|
|
444
|
+
if (!b || b.length < 24) return null;
|
|
445
|
+
if (b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71) {
|
|
446
|
+
const w = b[16] << 24 | b[17] << 16 | b[18] << 8 | b[19];
|
|
447
|
+
const h = b[20] << 24 | b[21] << 16 | b[22] << 8 | b[23];
|
|
448
|
+
return w > 0 && h > 0 ? {
|
|
449
|
+
w,
|
|
450
|
+
h
|
|
451
|
+
} : null;
|
|
452
|
+
}
|
|
453
|
+
if (b[0] === 71 && b[1] === 73 && b[2] === 70) {
|
|
454
|
+
const w = b[6] | b[7] << 8;
|
|
455
|
+
const h = b[8] | b[9] << 8;
|
|
456
|
+
return w > 0 && h > 0 ? {
|
|
457
|
+
w,
|
|
458
|
+
h
|
|
459
|
+
} : null;
|
|
460
|
+
}
|
|
461
|
+
if (b[0] === 66 && b[1] === 77) {
|
|
462
|
+
const w = b[18] | b[19] << 8 | b[20] << 16 | b[21] << 24;
|
|
463
|
+
const h = b[22] | b[23] << 8 | b[24] << 16 | b[25] << 24;
|
|
464
|
+
const aw = Math.abs(w);
|
|
465
|
+
const ah = Math.abs(h);
|
|
466
|
+
return aw > 0 && ah > 0 ? {
|
|
467
|
+
w: aw,
|
|
468
|
+
h: ah
|
|
469
|
+
} : null;
|
|
470
|
+
}
|
|
471
|
+
if (b[0] === 82 && b[1] === 73 && b[2] === 70 && b[3] === 70 && b[8] === 87 && b[9] === 69 && b[10] === 66 && b[11] === 80) {
|
|
472
|
+
const fourCC = String.fromCharCode(b[12], b[13], b[14], b[15]);
|
|
473
|
+
if (fourCC === "VP8 " && b.length >= 30) {
|
|
474
|
+
const w = (b[26] | b[27] << 8) & 16383;
|
|
475
|
+
const h = (b[28] | b[29] << 8) & 16383;
|
|
476
|
+
return w > 0 && h > 0 ? {
|
|
477
|
+
w,
|
|
478
|
+
h
|
|
479
|
+
} : null;
|
|
480
|
+
}
|
|
481
|
+
if (fourCC === "VP8L" && b.length >= 25) {
|
|
482
|
+
const bits = b[21] | b[22] << 8 | b[23] << 16 | b[24] << 24;
|
|
483
|
+
const w = (bits & 16383) + 1;
|
|
484
|
+
const h = (bits >> 14 & 16383) + 1;
|
|
485
|
+
return w > 0 && h > 0 ? {
|
|
486
|
+
w,
|
|
487
|
+
h
|
|
488
|
+
} : null;
|
|
489
|
+
}
|
|
490
|
+
if (fourCC === "VP8X" && b.length >= 30) {
|
|
491
|
+
const w = (b[24] | b[25] << 8 | b[26] << 16) + 1;
|
|
492
|
+
const h = (b[27] | b[28] << 8 | b[29] << 16) + 1;
|
|
493
|
+
return w > 0 && h > 0 ? {
|
|
494
|
+
w,
|
|
495
|
+
h
|
|
496
|
+
} : null;
|
|
497
|
+
}
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
if (b[0] === 255 && b[1] === 216) {
|
|
501
|
+
let i = 2;
|
|
502
|
+
while (i + 9 < b.length) {
|
|
503
|
+
if (b[i] !== 255) {
|
|
504
|
+
i++;
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
const marker = b[i + 1];
|
|
508
|
+
if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) {
|
|
509
|
+
const h = b[i + 5] << 8 | b[i + 6];
|
|
510
|
+
const w = b[i + 7] << 8 | b[i + 8];
|
|
511
|
+
return w > 0 && h > 0 ? {
|
|
512
|
+
w,
|
|
513
|
+
h
|
|
514
|
+
} : null;
|
|
515
|
+
}
|
|
516
|
+
if (marker >= 208 && marker <= 217 || marker === 1) {
|
|
517
|
+
i += 2;
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
const segLen = b[i + 2] << 8 | b[i + 3];
|
|
521
|
+
if (segLen < 2) break;
|
|
522
|
+
i += 2 + segLen;
|
|
523
|
+
}
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
336
528
|
//#endregion
|
|
337
529
|
//#region src/gen-tables.ts
|
|
338
530
|
/**
|
|
@@ -386,52 +578,62 @@ function parseTextToLines(cell, colWidth, verbose) {
|
|
|
386
578
|
*/
|
|
387
579
|
let newLine = [];
|
|
388
580
|
inputCells.forEach((cell) => {
|
|
389
|
-
if (typeof cell.text
|
|
390
|
-
|
|
391
|
-
|
|
581
|
+
if (typeof cell.text !== "string") return;
|
|
582
|
+
if (cell.text.includes("\n")) {
|
|
583
|
+
const parts = cell.text.split("\n");
|
|
584
|
+
parts.forEach((part, partIdx) => {
|
|
585
|
+
if (partIdx === parts.length - 1) newLine.push({
|
|
392
586
|
_type: "tablecell",
|
|
393
|
-
text:
|
|
394
|
-
options:
|
|
395
|
-
...cell.options,
|
|
396
|
-
breakLine: true
|
|
397
|
-
}
|
|
587
|
+
text: part,
|
|
588
|
+
options: cell.options
|
|
398
589
|
});
|
|
590
|
+
else {
|
|
591
|
+
newLine.push({
|
|
592
|
+
_type: "tablecell",
|
|
593
|
+
text: part,
|
|
594
|
+
options: {
|
|
595
|
+
...cell.options,
|
|
596
|
+
breakLine: true
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
inputLines1.push(newLine);
|
|
600
|
+
newLine = [];
|
|
601
|
+
}
|
|
399
602
|
});
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
inputLines1.push(newLine);
|
|
408
|
-
newLine = [];
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
if (newLine.length > 0) {
|
|
603
|
+
} else newLine.push({
|
|
604
|
+
_type: "tablecell",
|
|
605
|
+
text: cell.text.trim(),
|
|
606
|
+
options: cell.options
|
|
607
|
+
});
|
|
608
|
+
if (cell.options?.breakLine) {
|
|
609
|
+
if (verbose) console.log(`inputCells: new line > ${JSON.stringify(newLine)}`);
|
|
412
610
|
inputLines1.push(newLine);
|
|
413
611
|
newLine = [];
|
|
414
612
|
}
|
|
415
613
|
});
|
|
614
|
+
if (newLine.length > 0) {
|
|
615
|
+
inputLines1.push(newLine);
|
|
616
|
+
newLine = [];
|
|
617
|
+
}
|
|
416
618
|
if (verbose) {
|
|
417
619
|
console.log(`[2/4] inputLines1 (${inputLines1.length})`);
|
|
418
620
|
inputLines1.forEach((line, idx) => console.log(`[2/4] [${idx + 1}] line: ${JSON.stringify(line)}`));
|
|
419
621
|
}
|
|
420
622
|
inputLines1.forEach((line) => {
|
|
623
|
+
const lineTokens = [];
|
|
421
624
|
line.forEach((cell) => {
|
|
422
|
-
const lineCells = [];
|
|
423
625
|
const lineWords = String(cell.text).split(" ");
|
|
424
626
|
lineWords.forEach((word, idx) => {
|
|
425
627
|
const cellProps = { ...cell.options };
|
|
426
628
|
if (cellProps?.breakLine) cellProps.breakLine = idx + 1 === lineWords.length;
|
|
427
|
-
|
|
629
|
+
lineTokens.push({
|
|
428
630
|
_type: "tablecell",
|
|
429
631
|
text: word + (idx + 1 < lineWords.length ? " " : ""),
|
|
430
632
|
options: cellProps
|
|
431
633
|
});
|
|
432
634
|
});
|
|
433
|
-
inputLines2.push(lineCells);
|
|
434
635
|
});
|
|
636
|
+
inputLines2.push(lineTokens);
|
|
435
637
|
});
|
|
436
638
|
if (verbose) {
|
|
437
639
|
console.log(`[3/4] inputLines2 (${inputLines2.length})`);
|
|
@@ -539,6 +741,7 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
|
|
|
539
741
|
numCols += Number(cellOpts?.colspan ? cellOpts.colspan : 1);
|
|
540
742
|
});
|
|
541
743
|
if (tableProps.verbose) console.log(`| numCols ......................................... = ${numCols}`);
|
|
744
|
+
const colSpanDepths = new Array(numCols).fill(0);
|
|
542
745
|
if (!tablePropW && tableProps.colW) {
|
|
543
746
|
tableCalcW = Array.isArray(tableProps.colW) ? tableProps.colW.reduce((p, n) => p + n) * EMU : tableProps.colW * numCols || 0;
|
|
544
747
|
if (tableProps.verbose) console.log(`| tableCalcW ...................................... = ${tableCalcW / EMU}`);
|
|
@@ -559,6 +762,7 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
|
|
|
559
762
|
}
|
|
560
763
|
let newTableRowSlide = { rows: [] };
|
|
561
764
|
tableRows.forEach((row, iRow) => {
|
|
765
|
+
const hasActiveRowSpan = colSpanDepths.some((d) => d > 0);
|
|
562
766
|
const rowCellLines = [];
|
|
563
767
|
let maxCellMarTopEmu = 0;
|
|
564
768
|
let maxCellMarBtmEmu = 0;
|
|
@@ -655,7 +859,7 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
|
|
|
655
859
|
rowCellLines.forEach((cell) => {
|
|
656
860
|
if (cell._lineHeight >= emuLineMaxH) emuLineMaxH = cell._lineHeight;
|
|
657
861
|
});
|
|
658
|
-
if (emuTabCurrH + emuLineMaxH > emuSlideTabH) {
|
|
862
|
+
if (emuTabCurrH + emuLineMaxH > emuSlideTabH && !hasActiveRowSpan) {
|
|
659
863
|
if (tableProps.verbose) {
|
|
660
864
|
console.log("\n|-----------------------------------------------------------------------|");
|
|
661
865
|
console.log(`|-- NEW SLIDE CREATED (currTabH+currLineH > maxH) => ${(emuTabCurrH / EMU).toFixed(2)} + ${(srcCell._lineHeight / EMU).toFixed(2)} > ${emuSlideTabH / EMU}`);
|
|
@@ -699,6 +903,16 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
|
|
|
699
903
|
if (rowCellLines.map((cell) => cell._lines.length).reduce((prev, next) => prev + next) === 0) isDone = true;
|
|
700
904
|
}
|
|
701
905
|
if (currTableRow.length > 0) newTableRowSlide.rows.push(currTableRow);
|
|
906
|
+
const occupiedBefore = [...colSpanDepths];
|
|
907
|
+
let colCursor = 0;
|
|
908
|
+
row.forEach((cell) => {
|
|
909
|
+
while (colCursor < numCols && occupiedBefore[colCursor] > 0) colCursor++;
|
|
910
|
+
const cellColspan = cell.options?.colspan ?? 1;
|
|
911
|
+
const cellRowspan = cell.options?.rowspan ?? 1;
|
|
912
|
+
if (cellRowspan > 1) for (let c = 0; c < cellColspan && colCursor + c < numCols; c++) colSpanDepths[colCursor + c] = cellRowspan;
|
|
913
|
+
colCursor += cellColspan;
|
|
914
|
+
});
|
|
915
|
+
for (let c = 0; c < numCols; c++) if (colSpanDepths[c] > 0) colSpanDepths[c]--;
|
|
702
916
|
if (tableProps.verbose) console.log(`- SLIDE [${tableRowSlides.length}]: ROW [${iRow}]: ...COMPLETE ...... emuTabCurrH = ${(emuTabCurrH / EMU).toFixed(2)} ( emuSlideTabH = ${(emuSlideTabH / EMU).toFixed(2)} )`);
|
|
703
917
|
});
|
|
704
918
|
tableRowSlides.push(newTableRowSlide);
|
|
@@ -711,6 +925,28 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
|
|
|
711
925
|
return tableRowSlides;
|
|
712
926
|
}
|
|
713
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
|
+
/**
|
|
714
950
|
* Reproduces an HTML table as a PowerPoint table - including column widths, style, etc. - creates 1 or more slides as needed
|
|
715
951
|
* @param {TableToSlidesHost} pptx - pptxgenjs instance
|
|
716
952
|
* @param {string} tabEleId - HTMLElementID of the table
|
|
@@ -858,12 +1094,8 @@ function genTableToSlides(pptx, tabEleId, options = {}, masterSlide) {
|
|
|
858
1094
|
"bottom",
|
|
859
1095
|
"left"
|
|
860
1096
|
].forEach((val, idxb) => {
|
|
861
|
-
const
|
|
862
|
-
|
|
863
|
-
cellBorder[idxb] = {
|
|
864
|
-
pt: intBorderW,
|
|
865
|
-
color: rgbToHex(Number(arrRGB[0]), Number(arrRGB[1]), Number(arrRGB[2]))
|
|
866
|
-
};
|
|
1097
|
+
const style = window.getComputedStyle(cell);
|
|
1098
|
+
cellBorder[idxb] = htmlBorderToProps(style.getPropertyValue("border-" + val + "-width"), style.getPropertyValue("border-" + val + "-color"));
|
|
867
1099
|
});
|
|
868
1100
|
cellOpts.border = cellBorder;
|
|
869
1101
|
}
|
|
@@ -933,6 +1165,8 @@ function genTableToSlides(pptx, tabEleId, options = {}, masterSlide) {
|
|
|
933
1165
|
*/
|
|
934
1166
|
/** counter for included charts (used for index in their filenames) */
|
|
935
1167
|
let _chartCounter = 0;
|
|
1168
|
+
/** DPI PowerPoint assumes when sizing an inserted raster image (natural pixels / 96 == inches) */
|
|
1169
|
+
const IMAGE_NATURAL_DPI = 96;
|
|
936
1170
|
function normalizeBorderTuple(border) {
|
|
937
1171
|
return Array.isArray(border) ? border : [
|
|
938
1172
|
border,
|
|
@@ -954,6 +1188,7 @@ function createSlideMaster(props, target) {
|
|
|
954
1188
|
else if ("image" in object) addImageDefinition(tgt, object.image);
|
|
955
1189
|
else if ("line" in object) addShapeDefinition(tgt, "line", object.line);
|
|
956
1190
|
else if ("rect" in object) addShapeDefinition(tgt, "rect", object.rect);
|
|
1191
|
+
else if ("roundRect" in object) addShapeDefinition(tgt, "roundRect", object.roundRect);
|
|
957
1192
|
else if ("text" in object) addTextDefinition(tgt, [{ text: object.text.text }], object.text.options || {}, false);
|
|
958
1193
|
else if ("placeholder" in object) {
|
|
959
1194
|
const placeholder = object.placeholder;
|
|
@@ -968,6 +1203,26 @@ function createSlideMaster(props, target) {
|
|
|
968
1203
|
if (props.slideNumber && typeof props.slideNumber === "object") target._slideNumberProps = props.slideNumber;
|
|
969
1204
|
}
|
|
970
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
|
+
/**
|
|
971
1226
|
* Generate the chart based on input data.
|
|
972
1227
|
* OOXML Chart Spec: ISO/IEC 29500-1:2016(E)
|
|
973
1228
|
*
|
|
@@ -1139,7 +1394,13 @@ function addChartDefinition(target, type, data, opt) {
|
|
|
1139
1394
|
"marker",
|
|
1140
1395
|
"filled"
|
|
1141
1396
|
].includes(options.radarStyle || "")) options.radarStyle = "standard";
|
|
1142
|
-
|
|
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
|
+
}
|
|
1143
1404
|
options.lineDataSymbolLineSize = options.lineDataSymbolLineSize && !isNaN(options.lineDataSymbolLineSize) ? valToPts(options.lineDataSymbolLineSize) : valToPts(.75);
|
|
1144
1405
|
const chartLayout = options.layout;
|
|
1145
1406
|
if (chartLayout) [
|
|
@@ -1189,8 +1450,11 @@ function addChartDefinition(target, type, data, opt) {
|
|
|
1189
1450
|
options.v3DRotY = typeof options.v3DRotY === "number" && !isNaN(options.v3DRotY) && options.v3DRotY >= 0 && options.v3DRotY <= 360 ? options.v3DRotY : 30;
|
|
1190
1451
|
options.v3DRAngAx = options.v3DRAngAx || !options.v3DRAngAx ? options.v3DRAngAx : true;
|
|
1191
1452
|
options.v3DPerspective = typeof options.v3DPerspective === "number" && !isNaN(options.v3DPerspective) && options.v3DPerspective >= 0 && options.v3DPerspective <= 240 ? options.v3DPerspective : 30;
|
|
1192
|
-
options.barGapWidthPct =
|
|
1193
|
-
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");
|
|
1194
1458
|
options.chartColors = Array.isArray(options.chartColors) ? options.chartColors : options._type === "pie" || options._type === "doughnut" ? PIECHART_COLORS : BARCHART_COLORS;
|
|
1195
1459
|
options.chartColorsOpacity = options.chartColorsOpacity && !isNaN(options.chartColorsOpacity) ? options.chartColorsOpacity : void 0;
|
|
1196
1460
|
options.border = options.border && typeof options.border === "object" ? options.border : void 0;
|
|
@@ -1279,23 +1543,38 @@ function addImageDefinition(target, opt) {
|
|
|
1279
1543
|
else if (strImageData?.toLowerCase().includes("image/svg+xml")) strImgExtn = "svg";
|
|
1280
1544
|
newObject._type = "image";
|
|
1281
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
|
+
}
|
|
1282
1558
|
const objectOptions = {
|
|
1283
1559
|
x: intPosX || 0,
|
|
1284
1560
|
y: intPosY || 0,
|
|
1285
|
-
w:
|
|
1286
|
-
h:
|
|
1561
|
+
w: defWidth || 1,
|
|
1562
|
+
h: defHeight || 1,
|
|
1287
1563
|
altText: opt.altText || "",
|
|
1288
1564
|
rounding: typeof opt.rounding === "boolean" ? opt.rounding : false,
|
|
1289
1565
|
shape: opt.shape,
|
|
1290
1566
|
points: opt.points,
|
|
1291
1567
|
rectRadius: opt.rectRadius,
|
|
1568
|
+
shapeAdjust: opt.shapeAdjust,
|
|
1292
1569
|
sizing,
|
|
1293
1570
|
placeholder: opt.placeholder,
|
|
1294
1571
|
rotate: opt.rotate || 0,
|
|
1295
1572
|
flipV: opt.flipV || false,
|
|
1296
1573
|
flipH: opt.flipH || false,
|
|
1297
1574
|
transparency: opt.transparency || 0,
|
|
1575
|
+
duotone: opt.duotone,
|
|
1298
1576
|
objectName,
|
|
1577
|
+
objectLock: opt.objectLock,
|
|
1299
1578
|
shadow: correctShadowOptions(opt.shadow)
|
|
1300
1579
|
};
|
|
1301
1580
|
newObject.options = objectOptions;
|
|
@@ -1325,7 +1604,10 @@ function addImageDefinition(target, opt) {
|
|
|
1325
1604
|
});
|
|
1326
1605
|
newObject.imageRid = imageRelId + 1;
|
|
1327
1606
|
} else {
|
|
1328
|
-
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
|
+
});
|
|
1329
1611
|
target._relsMedia.push({
|
|
1330
1612
|
path: strImagePath || "preencoded." + strImgExtn,
|
|
1331
1613
|
type: "image/" + strImgExtn,
|
|
@@ -1344,7 +1626,7 @@ function addImageDefinition(target, opt) {
|
|
|
1344
1626
|
type: "hyperlink",
|
|
1345
1627
|
data: objHyperlink.slide ? "slide" : "dummy",
|
|
1346
1628
|
rId: imageRelId,
|
|
1347
|
-
Target: objHyperlink.url
|
|
1629
|
+
Target: objHyperlink.url ? encodeXmlEntities(objHyperlink.url) : String(objHyperlink.slide)
|
|
1348
1630
|
});
|
|
1349
1631
|
objHyperlink._rId = imageRelId;
|
|
1350
1632
|
newObject.hyperlink = objHyperlink;
|
|
@@ -1383,6 +1665,7 @@ function addMediaDefinition(target, opt) {
|
|
|
1383
1665
|
slideData.options.h = intSizeY;
|
|
1384
1666
|
slideData.options.objectName = objectName;
|
|
1385
1667
|
if (opt.altText) slideData.options.altText = opt.altText;
|
|
1668
|
+
if (opt.objectLock) slideData.options.objectLock = opt.objectLock;
|
|
1386
1669
|
/**
|
|
1387
1670
|
* NOTE:
|
|
1388
1671
|
* - rId starts at 2 (hence the intRels+1 below) as slideLayout.xml is rId=1!
|
|
@@ -1412,7 +1695,10 @@ function addMediaDefinition(target, opt) {
|
|
|
1412
1695
|
Target: `../media/image-${target._slideNum}-${target._relsMedia.length + 1}.png`
|
|
1413
1696
|
});
|
|
1414
1697
|
} else {
|
|
1415
|
-
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
|
+
});
|
|
1416
1702
|
const relId1 = getNewRelId(target);
|
|
1417
1703
|
target._relsMedia.push({
|
|
1418
1704
|
path: strPath || "preencoded" + strExtn,
|
|
@@ -1477,12 +1763,14 @@ function addShapeDefinition(target, shapeName, opts) {
|
|
|
1477
1763
|
const options = typeof opts === "object" ? opts : {};
|
|
1478
1764
|
options.line = options.line || { type: "none" };
|
|
1479
1765
|
options.shadow = correctShadowOptions(options.shadow);
|
|
1766
|
+
const resolvedShapeName = typeof shapeName === "string" && SHAPE_NAME_ALIASES[shapeName] ? SHAPE_NAME_ALIASES[shapeName] : shapeName;
|
|
1480
1767
|
const newObject = {
|
|
1481
1768
|
_type: "text",
|
|
1482
|
-
shape:
|
|
1769
|
+
shape: resolvedShapeName || "rect",
|
|
1483
1770
|
options
|
|
1484
1771
|
};
|
|
1485
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.`);
|
|
1486
1774
|
const newLineOpts = {
|
|
1487
1775
|
type: options.line.type || "solid",
|
|
1488
1776
|
color: options.line.color || "333333",
|
|
@@ -1579,9 +1867,8 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
|
|
|
1579
1867
|
}
|
|
1580
1868
|
arrRows.push(newRow);
|
|
1581
1869
|
});
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
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;
|
|
1585
1872
|
opt.fontSize = opt.fontSize || 12;
|
|
1586
1873
|
opt.margin = opt.margin === 0 || opt.margin ? opt.margin : DEF_CELL_MARGIN_IN;
|
|
1587
1874
|
if (typeof opt.margin === "number") opt.margin = [
|
|
@@ -1650,12 +1937,7 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
|
|
|
1650
1937
|
console.warn("addTable: mismatch: (colW.length != data.length) Therefore, defaulting to evenly distributed col widths.");
|
|
1651
1938
|
opt.colW = void 0;
|
|
1652
1939
|
}
|
|
1653
|
-
} else if (opt.w) opt.w =
|
|
1654
|
-
else opt.w = Math.floor((presLayout._sizeW || presLayout.width) / EMU - arrTableMargin[1] - arrTableMargin[3]);
|
|
1655
|
-
if (opt.x && opt.x < 20) opt.x = inch2Emu(opt.x);
|
|
1656
|
-
if (opt.y && opt.y < 20) opt.y = inch2Emu(opt.y);
|
|
1657
|
-
if (opt.w && typeof opt.w === "number" && opt.w < 20) opt.w = inch2Emu(opt.w);
|
|
1658
|
-
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]);
|
|
1659
1941
|
arrRows.forEach((row) => {
|
|
1660
1942
|
row.forEach((cell, idy) => {
|
|
1661
1943
|
if (typeof cell === "number" || typeof cell === "string") row[idy] = {
|
|
@@ -1683,7 +1965,7 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
|
|
|
1683
1965
|
if (opt.autoPageRepeatHeader) opt._arrObjTabHeadRows = arrRows.filter((_row, idx) => idx < (opt.autoPageHeaderRows || 1));
|
|
1684
1966
|
getSlidesForTableRows(arrRows, opt, presLayout, slideLayout).forEach((slide, idx) => {
|
|
1685
1967
|
if (!getSlide(target._slideNum + idx)) slides.push(addSlide({ masterName: slideLayout?._name || void 0 }));
|
|
1686
|
-
if (idx > 0) opt.y =
|
|
1968
|
+
if (idx > 0) opt.y = opt.autoPageSlideStartY || opt.newSlideStartY || arrTableMargin[0];
|
|
1687
1969
|
{
|
|
1688
1970
|
const newSlide = getSlide(target._slideNum + idx);
|
|
1689
1971
|
opt.autoPage = false;
|
|
@@ -1755,6 +2037,10 @@ function addTextDefinition(target, text, opts, isPlaceholder) {
|
|
|
1755
2037
|
itemOpts._bodyProp.anchor = !itemOpts.placeholder ? "ctr" : void 0;
|
|
1756
2038
|
itemOpts._bodyProp.vert = itemOpts.vert;
|
|
1757
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);
|
|
1758
2044
|
if (itemOpts.inset && !isNaN(Number(itemOpts.inset)) || itemOpts.inset === 0) {
|
|
1759
2045
|
itemOpts._bodyProp.lIns = inch2Emu(itemOpts.inset);
|
|
1760
2046
|
itemOpts._bodyProp.rIns = inch2Emu(itemOpts.inset);
|
|
@@ -2102,7 +2388,7 @@ async function createExcelWorksheet(chartObject, zip) {
|
|
|
2102
2388
|
if (chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D") strSharedStrings += `<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${intBubbleCols}" uniqueCount="${intBubbleCols}">`;
|
|
2103
2389
|
else if (chartObject.opts._type === "scatter") strSharedStrings += `<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${data.length}" uniqueCount="${data.length}">`;
|
|
2104
2390
|
else if (IS_MULTI_CAT_AXES) {
|
|
2105
|
-
let totCount = data.length;
|
|
2391
|
+
let totCount = data.length + 1;
|
|
2106
2392
|
data[0].labels.forEach((arrLabel) => totCount += arrLabel.filter((label) => label && label !== "").length);
|
|
2107
2393
|
strSharedStrings += `<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${totCount}" uniqueCount="${totCount}">`;
|
|
2108
2394
|
strSharedStrings += "<si><t/></si>";
|
|
@@ -2170,7 +2456,7 @@ async function createExcelWorksheet(chartObject, zip) {
|
|
|
2170
2456
|
strSheetXml += "<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" mc:Ignorable=\"x14ac\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\">";
|
|
2171
2457
|
if (chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D") strSheetXml += `<dimension ref="A1:${getExcelColName(intBubbleCols)}${data[0].values.length + 1}"/>`;
|
|
2172
2458
|
else if (chartObject.opts._type === "scatter") strSheetXml += `<dimension ref="A1:${getExcelColName(data.length)}${data[0].values.length + 1}"/>`;
|
|
2173
|
-
else strSheetXml += `<dimension ref="A1:${getExcelColName(data.length +
|
|
2459
|
+
else strSheetXml += `<dimension ref="A1:${getExcelColName(data.length + data[0].labels.length)}${data[0].values.length + 1}"/>`;
|
|
2174
2460
|
strSheetXml += "<sheetViews><sheetView tabSelected=\"1\" workbookViewId=\"0\"><selection activeCell=\"B1\" sqref=\"B1\"/></sheetView></sheetViews>";
|
|
2175
2461
|
strSheetXml += "<sheetFormatPr baseColWidth=\"10\" defaultRowHeight=\"16\"/>";
|
|
2176
2462
|
if (chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D") {
|
|
@@ -2222,82 +2508,28 @@ async function createExcelWorksheet(chartObject, zip) {
|
|
|
2222
2508
|
strSheetXml += "</row>";
|
|
2223
2509
|
});
|
|
2224
2510
|
} else {
|
|
2225
|
-
strSheetXml += `<row r="1" spans="1:${data.length + data[0].labels.length}">`;
|
|
2226
|
-
for (let idx = 0; idx < data[0].labels.length; idx++) strSheetXml += `<c r="${getExcelColName(idx + 1)}1" t="s"><v>0</v></c>`;
|
|
2227
|
-
for (let idx = data[0].labels.length - 1; idx < data.length + data[0].labels.length - 1; idx++) strSheetXml += `<c r="${getExcelColName(idx + data[0].labels.length)}1" t="s"><v>${idx}</v></c>`;
|
|
2228
|
-
strSheetXml += "</row>";
|
|
2229
|
-
/**
|
|
2230
|
-
* @example INPUT
|
|
2231
|
-
* const LABELS = [
|
|
2232
|
-
* ["Gear", "Berg", "Motr", "Swch", "Plug", "Cord", "Pump", "Leak", "Seal"],
|
|
2233
|
-
* ["Mech", "", "", "Elec", "", "", "Hydr", "", ""],
|
|
2234
|
-
* ];
|
|
2235
|
-
* const arrDataRegions = [
|
|
2236
|
-
* { name: "West", labels: LABELS, values: [11, 8, 3, 0, 11, 3, 0, 0, 0] },
|
|
2237
|
-
* { name: "Ctrl", labels: LABELS, values: [0, 11, 6, 19, 12, 5, 0, 0, 0] },
|
|
2238
|
-
* { name: "East", labels: LABELS, values: [0, 3, 2, 0, 0, 0, 4, 3, 1] },
|
|
2239
|
-
* ];
|
|
2240
|
-
*/
|
|
2241
|
-
/**
|
|
2242
|
-
* @example OUTPUT EXCEL SHEET
|
|
2243
|
-
* |/|---A--|---B--|---C--|---D--|---E--|
|
|
2244
|
-
* |1| | | West | Ctrl | East |
|
|
2245
|
-
* |2| Mech | Gear | ## | ## | ## |
|
|
2246
|
-
* |3| | Brng | ## | ## | ## |
|
|
2247
|
-
* |4| | Motr | ## | ## | ## |
|
|
2248
|
-
* |5| Elec | Swch | ## | ## | ## |
|
|
2249
|
-
* |6| | Plug | ## | ## | ## |
|
|
2250
|
-
* |7| | Cord | ## | ## | ## |
|
|
2251
|
-
* |8| Hydr | Pump | ## | ## | ## |
|
|
2252
|
-
* |9| | Leak | ## | ## | ## |
|
|
2253
|
-
*|10| | Seal | ## | ## | ## |
|
|
2254
|
-
*/
|
|
2255
|
-
/**
|
|
2256
|
-
* @example OUTPUT EXCEL SHEET XML
|
|
2257
|
-
* <row r="1" spans="1:5">
|
|
2258
|
-
* <c r="A1" t="s"><v>0</v></c>
|
|
2259
|
-
* <c r="B1" t="s"><v>0</v></c>
|
|
2260
|
-
* <c r="C1" t="s"><v>1</v></c>
|
|
2261
|
-
* <c r="D1" t="s"><v>2</v></c>
|
|
2262
|
-
* <c r="E1" t="s"><v>3</v></c>
|
|
2263
|
-
* </row>
|
|
2264
|
-
* <row r="2" spans="1:5">
|
|
2265
|
-
* <c r="A2" t="s"><v>4</v></c>
|
|
2266
|
-
* <c r="B2" t="s"><v>7</v></c>
|
|
2267
|
-
* <c r="C2" ><v>###</v></c>
|
|
2268
|
-
* </row>
|
|
2269
|
-
* <row r="3" spans="1:5">
|
|
2270
|
-
* <c r="A3" />
|
|
2271
|
-
* <c r="B3" t="s"><v>8</v></c>
|
|
2272
|
-
* <c r="C3" ><v>###</v></c>
|
|
2273
|
-
* </row>
|
|
2274
|
-
*/
|
|
2275
|
-
/**
|
|
2276
|
-
* @example SHARED-STRINGS
|
|
2277
|
-
* 1=West, 2=Ctrl, 3=East, 4=Mech, 5=Elec, 6=Mydr, 7=Gear, 8=Brng, [...], 15=Seal
|
|
2278
|
-
*/
|
|
2279
|
-
/**
|
|
2280
|
-
* const LABELS = [
|
|
2281
|
-
* ["Gear", "Berg", "Motr", "Swch", "Plug", "Cord", "Pump", "Leak", "Seal"],
|
|
2282
|
-
* ["Mech", "", "", "Elec", "", "", "Hydr", "", ""],
|
|
2283
|
-
* ["2010", "", "", "", "", "", "", "", ""],
|
|
2284
|
-
* ];
|
|
2285
|
-
*/
|
|
2286
2511
|
const TOT_SER = data.length;
|
|
2287
2512
|
const TOT_CAT = data[0].labels[0].length;
|
|
2288
2513
|
const TOT_LVL = data[0].labels.length;
|
|
2514
|
+
const revLabelGroups = data[0].labels.slice().reverse();
|
|
2515
|
+
const ssLabelMap = /* @__PURE__ */ new Map();
|
|
2516
|
+
let ssIdx = TOT_SER + 1;
|
|
2517
|
+
revLabelGroups.forEach((labelsGroup, revLevelIdx) => {
|
|
2518
|
+
labelsGroup.forEach((label, rowIdx) => {
|
|
2519
|
+
if (label && label !== "") ssLabelMap.set(`${revLevelIdx}:${rowIdx}`, ssIdx++);
|
|
2520
|
+
});
|
|
2521
|
+
});
|
|
2522
|
+
strSheetXml += `<row r="1" spans="1:${TOT_SER + TOT_LVL}">`;
|
|
2523
|
+
for (let col = 1; col <= TOT_LVL; col++) strSheetXml += `<c r="${getExcelColName(col)}1" t="s"><v>0</v></c>`;
|
|
2524
|
+
for (let ser = 0; ser < TOT_SER; ser++) strSheetXml += `<c r="${getExcelColName(TOT_LVL + ser + 1)}1" t="s"><v>${ser + 1}</v></c>`;
|
|
2525
|
+
strSheetXml += "</row>";
|
|
2289
2526
|
for (let idx = 0; idx < TOT_CAT; idx++) {
|
|
2290
2527
|
strSheetXml += `<row r="${idx + 2}" spans="1:${TOT_SER + TOT_LVL}">`;
|
|
2291
|
-
let totLabels = TOT_SER;
|
|
2292
|
-
const revLabelGroups = data[0].labels.slice().reverse();
|
|
2293
2528
|
revLabelGroups.forEach((labelsGroup, idy) => {
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
totLabels += totGrpLbls;
|
|
2297
|
-
strSheetXml += `<c r="${getExcelColName(idx + 1 + idy)}${idx + 2}" t="s"><v>${totLabels}</v></c>`;
|
|
2298
|
-
}
|
|
2529
|
+
const colLabel = labelsGroup[idx];
|
|
2530
|
+
if (colLabel && colLabel !== "") strSheetXml += `<c r="${getExcelColName(idy + 1)}${idx + 2}" t="s"><v>${ssLabelMap.get(`${idy}:${idx}`)}</v></c>`;
|
|
2299
2531
|
});
|
|
2300
|
-
for (let idy = 0; idy < TOT_SER; idy++) strSheetXml += `<c r="${getExcelColName(TOT_LVL + idy + 1)}${idx + 2}"><v>${data[idy].values[idx]
|
|
2532
|
+
for (let idy = 0; idy < TOT_SER; idy++) strSheetXml += `<c r="${getExcelColName(TOT_LVL + idy + 1)}${idx + 2}"><v>${data[idy].values[idx] ?? ""}</v></c>`;
|
|
2301
2533
|
strSheetXml += "</row>";
|
|
2302
2534
|
}
|
|
2303
2535
|
}
|
|
@@ -2318,6 +2550,22 @@ async function createExcelWorksheet(chartObject, zip) {
|
|
|
2318
2550
|
});
|
|
2319
2551
|
}
|
|
2320
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
|
+
/**
|
|
2321
2569
|
* Main entry point method for create charts
|
|
2322
2570
|
* @see: http://www.datypic.com/sc/ooxml/s-dml-chart.xsd.html
|
|
2323
2571
|
* @param {ISlideRelChart} rel - chart object
|
|
@@ -2327,6 +2575,10 @@ function makeXmlCharts(rel) {
|
|
|
2327
2575
|
let strXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
|
|
2328
2576
|
let usesSecondaryValAxis = false;
|
|
2329
2577
|
let usesSecondaryCatAxis = false;
|
|
2578
|
+
let primaryCatAxisValType = null;
|
|
2579
|
+
let secondaryCatAxisValType = null;
|
|
2580
|
+
let primaryCatAxisHasCategoryChart = false;
|
|
2581
|
+
let secondaryCatAxisHasCategoryChart = false;
|
|
2330
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\">";
|
|
2331
2583
|
strXml += "<c:date1904 val=\"0\"/>";
|
|
2332
2584
|
strXml += `<c:roundedCorners val="${rel.opts.chartArea.roundedCorners ? "1" : "0"}"/>`;
|
|
@@ -2339,6 +2591,8 @@ function makeXmlCharts(rel) {
|
|
|
2339
2591
|
fontSize: rel.opts.titleFontSize || 18,
|
|
2340
2592
|
titleAlign: rel.opts.titleAlign,
|
|
2341
2593
|
titleBold: rel.opts.titleBold,
|
|
2594
|
+
titleItalic: rel.opts.titleItalic,
|
|
2595
|
+
titleUnderline: rel.opts.titleUnderline,
|
|
2342
2596
|
titlePos: rel.opts.titlePos,
|
|
2343
2597
|
titleRotate: rel.opts.titleRotate
|
|
2344
2598
|
}, rel.opts.x, rel.opts.y);
|
|
@@ -2371,18 +2625,37 @@ function makeXmlCharts(rel) {
|
|
|
2371
2625
|
const catAxisId = options.secondaryCatAxis ? AXIS_ID_CATEGORY_SECONDARY : AXIS_ID_CATEGORY_PRIMARY;
|
|
2372
2626
|
usesSecondaryValAxis = usesSecondaryValAxis || options.secondaryValAxis;
|
|
2373
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;
|
|
2374
2633
|
strXml += makeChartType(type.type, type.data, options, valAxisId, catAxisId);
|
|
2375
2634
|
});
|
|
2376
2635
|
else strXml += makeChartType(rel.opts._type, rel.data, rel.opts, AXIS_ID_VALUE_PRIMARY, AXIS_ID_CATEGORY_PRIMARY);
|
|
2377
2636
|
if (rel.opts._type !== "pie" && rel.opts._type !== "doughnut") {
|
|
2378
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
|
+
};
|
|
2379
2648
|
if (rel.opts.catAxes) {
|
|
2380
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.");
|
|
2381
2650
|
strXml += makeCatAxis({
|
|
2382
2651
|
...rel.opts,
|
|
2383
|
-
...rel.opts.catAxes[0]
|
|
2652
|
+
...rel.opts.catAxes[0],
|
|
2653
|
+
...comboCatAxisType(false)
|
|
2384
2654
|
}, AXIS_ID_CATEGORY_PRIMARY, AXIS_ID_VALUE_PRIMARY);
|
|
2385
|
-
} else strXml += makeCatAxis(
|
|
2655
|
+
} else strXml += makeCatAxis({
|
|
2656
|
+
...rel.opts,
|
|
2657
|
+
...comboCatAxisType(false)
|
|
2658
|
+
}, AXIS_ID_CATEGORY_PRIMARY, AXIS_ID_VALUE_PRIMARY);
|
|
2386
2659
|
if (rel.opts.valAxes) {
|
|
2387
2660
|
strXml += makeValAxis({
|
|
2388
2661
|
...rel.opts,
|
|
@@ -2399,9 +2672,13 @@ function makeXmlCharts(rel) {
|
|
|
2399
2672
|
}
|
|
2400
2673
|
if (rel.opts?.catAxes && rel.opts?.catAxes[1]) strXml += makeCatAxis({
|
|
2401
2674
|
...rel.opts,
|
|
2402
|
-
...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)
|
|
2403
2681
|
}, AXIS_ID_CATEGORY_SECONDARY, AXIS_ID_VALUE_SECONDARY);
|
|
2404
|
-
else if (usesSecondaryCatAxis && (!rel.opts.catAxes || !rel.opts.catAxes[1])) strXml += makeCatAxis(rel.opts, AXIS_ID_CATEGORY_SECONDARY, AXIS_ID_VALUE_SECONDARY);
|
|
2405
2682
|
}
|
|
2406
2683
|
if (rel.opts.showDataTable) {
|
|
2407
2684
|
strXml += "<c:dTable>";
|
|
@@ -2440,6 +2717,13 @@ function makeXmlCharts(rel) {
|
|
|
2440
2717
|
if (rel.opts.showLegend) {
|
|
2441
2718
|
strXml += "<c:legend>";
|
|
2442
2719
|
strXml += "<c:legendPos val=\"" + rel.opts.legendPos + "\"/>";
|
|
2720
|
+
if (Array.isArray(rel.opts._type)) {
|
|
2721
|
+
let seriesIdx = 0;
|
|
2722
|
+
rel.opts._type.forEach((type) => {
|
|
2723
|
+
if (type.options?.showLegend === false) for (let i = 0; i < type.data.length; i++) strXml += `<c:legendEntry><c:idx val="${seriesIdx + i}"/><c:delete val="1"/></c:legendEntry>`;
|
|
2724
|
+
seriesIdx += type.data.length;
|
|
2725
|
+
});
|
|
2726
|
+
}
|
|
2443
2727
|
strXml += "<c:overlay val=\"0\"/>";
|
|
2444
2728
|
if (rel.opts.legendFontFace || rel.opts.legendFontSize || rel.opts.legendColor) {
|
|
2445
2729
|
strXml += "<c:txPr>";
|
|
@@ -2449,8 +2733,7 @@ function makeXmlCharts(rel) {
|
|
|
2449
2733
|
strXml += " <a:pPr>";
|
|
2450
2734
|
strXml += rel.opts.legendFontSize ? `<a:defRPr sz="${Math.round(Number(rel.opts.legendFontSize) * 100)}">` : "<a:defRPr>";
|
|
2451
2735
|
if (rel.opts.legendColor) strXml += genXmlColorSelection(rel.opts.legendColor);
|
|
2452
|
-
if (rel.opts.legendFontFace) strXml +=
|
|
2453
|
-
if (rel.opts.legendFontFace) strXml += "<a:cs typeface=\"" + rel.opts.legendFontFace + "\"/>";
|
|
2736
|
+
if (rel.opts.legendFontFace) strXml += createChartTextFonts(rel.opts.legendFontFace);
|
|
2454
2737
|
strXml += " </a:defRPr>";
|
|
2455
2738
|
strXml += " </a:pPr>";
|
|
2456
2739
|
strXml += " <a:endParaRPr lang=\"en-US\"/>";
|
|
@@ -2488,6 +2771,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2488
2771
|
let idxColLtr = 1;
|
|
2489
2772
|
let optsChartData;
|
|
2490
2773
|
let strXml = "";
|
|
2774
|
+
const valFmtCode = encodeXmlEntities(opts.valLabelFormatCode || opts.dataTableFormatCode || opts.dataLabelFormatCode || "General");
|
|
2491
2775
|
switch (chartType) {
|
|
2492
2776
|
case "area":
|
|
2493
2777
|
case "bar":
|
|
@@ -2515,20 +2799,23 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2515
2799
|
strXml += " <c:strCache><c:ptCount val=\"1\"/><c:pt idx=\"0\"><c:v>" + encodeXmlEntities(obj.name) + "</c:v></c:pt></c:strCache>";
|
|
2516
2800
|
strXml += " </c:strRef>";
|
|
2517
2801
|
strXml += " </c:tx>";
|
|
2518
|
-
const
|
|
2802
|
+
const seriesOverride = opts.seriesOptions?.[obj._dataIndex];
|
|
2803
|
+
const seriesColor = seriesOverride?.color ?? (opts.chartColors ? opts.chartColors[colorIndex % opts.chartColors.length] : null);
|
|
2519
2804
|
strXml += " <c:spPr>";
|
|
2520
2805
|
if (seriesColor === "transparent") strXml += "<a:noFill/>";
|
|
2521
2806
|
else if (opts.chartColorsOpacity) strXml += "<a:solidFill>" + createColorElement(seriesColor, `<a:alpha val="${Math.round(opts.chartColorsOpacity * 1e3)}"/>`) + "</a:solidFill>";
|
|
2522
2807
|
else strXml += "<a:solidFill>" + createColorElement(seriesColor) + "</a:solidFill>";
|
|
2523
|
-
if (chartType === "line" || chartType === "radar")
|
|
2524
|
-
|
|
2525
|
-
strXml +=
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2808
|
+
if (chartType === "line" || chartType === "radar") {
|
|
2809
|
+
const effectiveLineSize = seriesOverride?.lineSize ?? opts.lineSize;
|
|
2810
|
+
if (effectiveLineSize === 0) strXml += "<a:ln><a:noFill/></a:ln>";
|
|
2811
|
+
else {
|
|
2812
|
+
strXml += `<a:ln w="${valToPts(effectiveLineSize)}" cap="${createLineCap(opts.lineCap)}"><a:solidFill>${createColorElement(seriesColor)}</a:solidFill>`;
|
|
2813
|
+
strXml += `<a:prstDash val="${opts.lineDashValues?.[colorIndex] ?? opts.lineDash ?? "solid"}"/><a:round/></a:ln>`;
|
|
2814
|
+
}
|
|
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>`;
|
|
2529
2816
|
strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
2530
2817
|
strXml += " </c:spPr>";
|
|
2531
|
-
if (chartType
|
|
2818
|
+
if (chartType === "bar" || chartType === "bar3D") strXml += " <c:invertIfNegative val=\"0\"/>";
|
|
2532
2819
|
if (chartType === "line" || chartType === "radar") {
|
|
2533
2820
|
strXml += "<c:marker>";
|
|
2534
2821
|
strXml += " <c:symbol val=\"" + opts.lineDataSymbol + "\"/>";
|
|
@@ -2543,14 +2830,26 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2543
2830
|
strXml += " </c:spPr>";
|
|
2544
2831
|
strXml += "</c:marker>";
|
|
2545
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
|
+
}
|
|
2546
2837
|
if (chartType !== "radar") {
|
|
2838
|
+
const lblColor = seriesOverride?.dataLabelColor ?? opts.dataLabelColor ?? "000000";
|
|
2839
|
+
const lblBold = seriesOverride?.dataLabelFontBold ?? opts.dataLabelFontBold ?? false;
|
|
2840
|
+
const lblItalic = seriesOverride?.dataLabelFontItalic ?? opts.dataLabelFontItalic ?? false;
|
|
2841
|
+
const lblSize = seriesOverride?.dataLabelFontSize ?? opts.dataLabelFontSize ?? 12;
|
|
2842
|
+
const lblFace = seriesOverride?.dataLabelFontFace ?? opts.dataLabelFontFace ?? "Arial";
|
|
2547
2843
|
strXml += "<c:dLbls>";
|
|
2844
|
+
if (obj.customLabels?.length) obj.customLabels.forEach((lbl, idx) => {
|
|
2845
|
+
if (lbl) strXml += makeCustomDLblXml(idx, lbl, opts);
|
|
2846
|
+
});
|
|
2548
2847
|
strXml += `<c:numFmt formatCode="${encodeXmlEntities(opts.dataLabelFormatCode) || "General"}" sourceLinked="0"/>`;
|
|
2549
2848
|
if (opts.dataLabelBkgrdColors) strXml += `<c:spPr><a:solidFill>${createColorElement(seriesColor)}</a:solidFill></c:spPr>`;
|
|
2550
2849
|
strXml += "<c:txPr><a:bodyPr/><a:lstStyle/><a:p><a:pPr>";
|
|
2551
|
-
strXml += `<a:defRPr b="${
|
|
2552
|
-
strXml += `<a:solidFill>${createColorElement(
|
|
2553
|
-
strXml +=
|
|
2850
|
+
strXml += `<a:defRPr b="${lblBold ? 1 : 0}" i="${lblItalic ? 1 : 0}" strike="noStrike" sz="${Math.round(lblSize * 100)}" u="none">`;
|
|
2851
|
+
strXml += `<a:solidFill>${createColorElement(lblColor)}</a:solidFill>`;
|
|
2852
|
+
strXml += createChartTextFonts(lblFace);
|
|
2554
2853
|
strXml += "</a:defRPr></a:pPr></a:p></c:txPr>";
|
|
2555
2854
|
if (opts.dataLabelPosition) strXml += `<c:dLblPos val="${opts.dataLabelPosition}"/>`;
|
|
2556
2855
|
strXml += "<c:showLegendKey val=\"0\"/>";
|
|
@@ -2559,29 +2858,6 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2559
2858
|
strXml += `<c:showLeaderLines val="${opts.showLeaderLines ? "1" : "0"}"/>`;
|
|
2560
2859
|
strXml += "</c:dLbls>";
|
|
2561
2860
|
}
|
|
2562
|
-
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) => {
|
|
2563
|
-
const arrColors = value < 0 ? opts.invertedColors || opts.chartColors || BARCHART_COLORS : opts.chartColors || [];
|
|
2564
|
-
strXml += " <c:dPt>";
|
|
2565
|
-
strXml += ` <c:idx val="${index}"/>`;
|
|
2566
|
-
strXml += " <c:invertIfNegative val=\"0\"/>";
|
|
2567
|
-
strXml += " <c:bubble3D val=\"0\"/>";
|
|
2568
|
-
strXml += " <c:spPr>";
|
|
2569
|
-
if (opts.lineSize === 0) strXml += "<a:ln><a:noFill/></a:ln>";
|
|
2570
|
-
else if (chartType === "bar") {
|
|
2571
|
-
strXml += "<a:solidFill>";
|
|
2572
|
-
strXml += " <a:srgbClr val=\"" + arrColors[index % arrColors.length] + "\"/>";
|
|
2573
|
-
strXml += "</a:solidFill>";
|
|
2574
|
-
} else {
|
|
2575
|
-
strXml += "<a:ln>";
|
|
2576
|
-
strXml += " <a:solidFill>";
|
|
2577
|
-
strXml += " <a:srgbClr val=\"" + arrColors[index % arrColors.length] + "\"/>";
|
|
2578
|
-
strXml += " </a:solidFill>";
|
|
2579
|
-
strXml += "</a:ln>";
|
|
2580
|
-
}
|
|
2581
|
-
strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
2582
|
-
strXml += " </c:spPr>";
|
|
2583
|
-
strXml += " </c:dPt>";
|
|
2584
|
-
});
|
|
2585
2861
|
strXml += "<c:cat>";
|
|
2586
2862
|
if (opts.catLabelFormatCode) {
|
|
2587
2863
|
strXml += " <c:numRef>";
|
|
@@ -2618,10 +2894,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2618
2894
|
strXml += " <c:numRef>";
|
|
2619
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>`;
|
|
2620
2896
|
strXml += " <c:numCache>";
|
|
2621
|
-
strXml += " <c:formatCode>" +
|
|
2897
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
2622
2898
|
strXml += ` <c:ptCount val="${obj.labels[0].length}"/>`;
|
|
2623
2899
|
obj.values.forEach((value, idx) => {
|
|
2624
|
-
|
|
2900
|
+
strXml += numCachePt(idx, value);
|
|
2625
2901
|
});
|
|
2626
2902
|
strXml += " </c:numCache>";
|
|
2627
2903
|
strXml += " </c:numRef>";
|
|
@@ -2637,7 +2913,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2637
2913
|
strXml += " <a:p><a:pPr>";
|
|
2638
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">`;
|
|
2639
2915
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
2640
|
-
strXml += "
|
|
2916
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
2641
2917
|
strXml += " </a:defRPr>";
|
|
2642
2918
|
strXml += " </a:pPr></a:p>";
|
|
2643
2919
|
strXml += " </c:txPr>";
|
|
@@ -2653,6 +2929,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2653
2929
|
if (chartType === "bar") {
|
|
2654
2930
|
strXml += ` <c:gapWidth val="${opts.barGapWidthPct}"/>`;
|
|
2655
2931
|
strXml += ` <c:overlap val="${opts.barOverlapPct != null ? opts.barOverlapPct : (opts.barGrouping || "").includes("tacked") ? 100 : 0}"/>`;
|
|
2932
|
+
strXml += createSerLinesElement(opts.barSeriesLine);
|
|
2656
2933
|
} else if (chartType === "bar3D") {
|
|
2657
2934
|
strXml += ` <c:gapWidth val="${opts.barGapWidthPct}"/>`;
|
|
2658
2935
|
strXml += ` <c:gapDepth val="${opts.barGapDepthPct}"/>`;
|
|
@@ -2704,6 +2981,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2704
2981
|
strXml += "<a:effectLst/>";
|
|
2705
2982
|
strXml += "</c:spPr>";
|
|
2706
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
|
+
}
|
|
2707
2988
|
if (opts.showLabel) {
|
|
2708
2989
|
const chartUuid = getUuid("-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
|
|
2709
2990
|
if (obj.labels[0] && (opts.dataLabelFormatScatter === "custom" || opts.dataLabelFormatScatter === "customXY")) {
|
|
@@ -2722,13 +3003,13 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2722
3003
|
strXml += " <a:pPr>";
|
|
2723
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">`;
|
|
2724
3005
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
2725
|
-
strXml +=
|
|
3006
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
2726
3007
|
strXml += " </a:defRPr>";
|
|
2727
3008
|
strXml += " </a:pPr>";
|
|
2728
3009
|
strXml += " <a:r>";
|
|
2729
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">`;
|
|
2730
3011
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
2731
|
-
strXml +=
|
|
3012
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
2732
3013
|
strXml += " </a:rPr>";
|
|
2733
3014
|
strXml += " <a:t>" + encodeXmlEntities(label) + "</a:t>";
|
|
2734
3015
|
strXml += " </a:r>";
|
|
@@ -2808,7 +3089,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2808
3089
|
strXml += " <a:pPr>";
|
|
2809
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">`;
|
|
2810
3091
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
2811
|
-
strXml +=
|
|
3092
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
2812
3093
|
strXml += " </a:defRPr>";
|
|
2813
3094
|
strXml += " </a:pPr>";
|
|
2814
3095
|
strXml += ` <a:endParaRPr lang="${opts.lang || "en-US"}"/>`;
|
|
@@ -2829,31 +3110,14 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2829
3110
|
strXml += "</c:dLbls>";
|
|
2830
3111
|
}
|
|
2831
3112
|
}
|
|
2832
|
-
if (data.length === 1 && opts.chartColors !== BARCHART_COLORS) obj.values.forEach((value, index) => {
|
|
2833
|
-
const arrColors = value < 0 ? opts.invertedColors || opts.chartColors || BARCHART_COLORS : opts.chartColors || [];
|
|
2834
|
-
strXml += " <c:dPt>";
|
|
2835
|
-
strXml += ` <c:idx val="${index}"/>`;
|
|
2836
|
-
strXml += " <c:invertIfNegative val=\"0\"/>";
|
|
2837
|
-
strXml += " <c:bubble3D val=\"0\"/>";
|
|
2838
|
-
strXml += " <c:spPr>";
|
|
2839
|
-
if (opts.lineSize === 0) strXml += "<a:ln><a:noFill/></a:ln>";
|
|
2840
|
-
else {
|
|
2841
|
-
strXml += "<a:solidFill>";
|
|
2842
|
-
strXml += " <a:srgbClr val=\"" + arrColors[index % arrColors.length] + "\"/>";
|
|
2843
|
-
strXml += "</a:solidFill>";
|
|
2844
|
-
}
|
|
2845
|
-
strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
2846
|
-
strXml += " </c:spPr>";
|
|
2847
|
-
strXml += " </c:dPt>";
|
|
2848
|
-
});
|
|
2849
3113
|
strXml += "<c:xVal>";
|
|
2850
3114
|
strXml += " <c:numRef>";
|
|
2851
3115
|
strXml += ` <c:f>Sheet1!$A$2:$A$${data[0].values.length + 1}</c:f>`;
|
|
2852
3116
|
strXml += " <c:numCache>";
|
|
2853
|
-
strXml += " <c:formatCode>
|
|
3117
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
2854
3118
|
strXml += ` <c:ptCount val="${data[0].values.length}"/>`;
|
|
2855
3119
|
data[0].values.forEach((value, idx) => {
|
|
2856
|
-
|
|
3120
|
+
strXml += numCachePt(idx, value);
|
|
2857
3121
|
});
|
|
2858
3122
|
strXml += " </c:numCache>";
|
|
2859
3123
|
strXml += " </c:numRef>";
|
|
@@ -2862,10 +3126,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2862
3126
|
strXml += " <c:numRef>";
|
|
2863
3127
|
strXml += ` <c:f>Sheet1!$${getExcelColName(idx + 2)}$2:$${getExcelColName(idx + 2)}$${data[0].values.length + 1}</c:f>`;
|
|
2864
3128
|
strXml += " <c:numCache>";
|
|
2865
|
-
strXml += " <c:formatCode>
|
|
3129
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
2866
3130
|
strXml += ` <c:ptCount val="${data[0].values.length}"/>`;
|
|
2867
3131
|
data[0].values.forEach((_value, idx) => {
|
|
2868
|
-
|
|
3132
|
+
strXml += numCachePt(idx, obj.values[idx]);
|
|
2869
3133
|
});
|
|
2870
3134
|
strXml += " </c:numCache>";
|
|
2871
3135
|
strXml += " </c:numRef>";
|
|
@@ -2881,7 +3145,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2881
3145
|
strXml += " <a:p><a:pPr>";
|
|
2882
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">`;
|
|
2883
3147
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
2884
|
-
strXml += "
|
|
3148
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
2885
3149
|
strXml += " </a:defRPr>";
|
|
2886
3150
|
strXml += " </a:pPr></a:p>";
|
|
2887
3151
|
strXml += " </c:txPr>";
|
|
@@ -2931,10 +3195,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2931
3195
|
strXml += " <c:numRef>";
|
|
2932
3196
|
strXml += ` <c:f>Sheet1!$A$2:$A$${data[0].values.length + 1}</c:f>`;
|
|
2933
3197
|
strXml += " <c:numCache>";
|
|
2934
|
-
strXml += " <c:formatCode>
|
|
3198
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
2935
3199
|
strXml += ` <c:ptCount val="${data[0].values.length}"/>`;
|
|
2936
3200
|
data[0].values.forEach((value, idx) => {
|
|
2937
|
-
strXml +=
|
|
3201
|
+
strXml += numCachePt(idx, value);
|
|
2938
3202
|
});
|
|
2939
3203
|
strXml += " </c:numCache>";
|
|
2940
3204
|
strXml += " </c:numRef>";
|
|
@@ -2944,10 +3208,10 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2944
3208
|
strXml += `<c:f>Sheet1!$${getExcelColName(idxColLtr + 1)}$2:$${getExcelColName(idxColLtr + 1)}$${data[0].values.length + 1}</c:f>`;
|
|
2945
3209
|
idxColLtr++;
|
|
2946
3210
|
strXml += " <c:numCache>";
|
|
2947
|
-
strXml += " <c:formatCode>
|
|
3211
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
2948
3212
|
strXml += ` <c:ptCount val="${data[0].values.length}"/>`;
|
|
2949
3213
|
data[0].values.forEach((_value, idx) => {
|
|
2950
|
-
strXml +=
|
|
3214
|
+
strXml += numCachePt(idx, obj.values[idx]);
|
|
2951
3215
|
});
|
|
2952
3216
|
strXml += " </c:numCache>";
|
|
2953
3217
|
strXml += " </c:numRef>";
|
|
@@ -2960,7 +3224,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2960
3224
|
strXml += " <c:formatCode>General</c:formatCode>";
|
|
2961
3225
|
strXml += ` <c:ptCount val="${obj.sizes.length}"/>`;
|
|
2962
3226
|
obj.sizes.forEach((value, idx) => {
|
|
2963
|
-
strXml +=
|
|
3227
|
+
strXml += numCachePt(idx, value);
|
|
2964
3228
|
});
|
|
2965
3229
|
strXml += " </c:numCache>";
|
|
2966
3230
|
strXml += " </c:numRef>";
|
|
@@ -2973,12 +3237,12 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
2973
3237
|
strXml += "<c:txPr><a:bodyPr/><a:lstStyle/><a:p><a:pPr>";
|
|
2974
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">`;
|
|
2975
3239
|
strXml += `<a:solidFill>${createColorElement(opts.dataLabelColor || "000000")}</a:solidFill>`;
|
|
2976
|
-
strXml +=
|
|
3240
|
+
strXml += createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
2977
3241
|
strXml += "</a:defRPr></a:pPr></a:p></c:txPr>";
|
|
2978
3242
|
if (opts.dataLabelPosition) strXml += `<c:dLblPos val="${opts.dataLabelPosition}"/>`;
|
|
2979
3243
|
strXml += "<c:showLegendKey val=\"0\"/>";
|
|
2980
3244
|
strXml += `<c:showVal val="${opts.showValue ? "1" : "0"}"/>`;
|
|
2981
|
-
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"}"/>`;
|
|
2982
3246
|
strXml += "<c:extLst>";
|
|
2983
3247
|
strXml += " <c:ext uri=\"{CE6537A1-D6FC-4f65-9D91-7224C49458BB}\" xmlns:c15=\"http://schemas.microsoft.com/office/drawing/2012/chart\">";
|
|
2984
3248
|
strXml += " <c15:showLeaderLines val=\"" + (opts.showLeaderLines ? "1" : "0") + "\"/>";
|
|
@@ -3012,33 +3276,37 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
3012
3276
|
else strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
3013
3277
|
strXml += " </c:spPr>";
|
|
3014
3278
|
optsChartData.labels[0].forEach((_label, idx) => {
|
|
3279
|
+
const ptStyle = optsChartData.pointStyles?.[idx];
|
|
3015
3280
|
strXml += "<c:dPt>";
|
|
3016
3281
|
strXml += ` <c:idx val="${idx}"/>`;
|
|
3017
3282
|
strXml += " <c:bubble3D val=\"0\"/>";
|
|
3018
3283
|
strXml += " <c:spPr>";
|
|
3019
|
-
strXml += `<a:solidFill>${createColorElement(opts.chartColors[idx + 1 > opts.chartColors.length ? Math.floor(Math.random() * opts.chartColors.length) : idx])}</a:solidFill>`;
|
|
3020
|
-
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>`;
|
|
3021
3287
|
strXml += createShadowElement(opts.shadow, DEF_SHAPE_SHADOW);
|
|
3022
3288
|
strXml += " </c:spPr>";
|
|
3023
3289
|
strXml += "</c:dPt>";
|
|
3024
3290
|
});
|
|
3025
3291
|
strXml += "<c:dLbls>";
|
|
3026
3292
|
optsChartData.labels[0].forEach((_label, idx) => {
|
|
3293
|
+
const customLbl = optsChartData.customLabels?.[idx];
|
|
3027
3294
|
strXml += "<c:dLbl>";
|
|
3028
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>`;
|
|
3029
3297
|
strXml += ` <c:numFmt formatCode="${encodeXmlEntities(opts.dataLabelFormatCode) || "General"}" sourceLinked="0"/>`;
|
|
3030
3298
|
strXml += " <c:spPr/><c:txPr>";
|
|
3031
3299
|
strXml += " <a:bodyPr/><a:lstStyle/>";
|
|
3032
3300
|
strXml += " <a:p><a:pPr>";
|
|
3033
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">`;
|
|
3034
3302
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
3035
|
-
strXml +=
|
|
3303
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
3036
3304
|
strXml += " </a:defRPr>";
|
|
3037
3305
|
strXml += " </a:pPr></a:p>";
|
|
3038
3306
|
strXml += " </c:txPr>";
|
|
3039
3307
|
if (chartType === "pie" && opts.dataLabelPosition) strXml += `<c:dLblPos val="${opts.dataLabelPosition}"/>`;
|
|
3040
3308
|
strXml += " <c:showLegendKey val=\"0\"/>";
|
|
3041
|
-
strXml += " <c:showVal val=\"" + (opts.showValue ? "1" : "0") + "\"/>";
|
|
3309
|
+
strXml += " <c:showVal val=\"" + (customLbl ? "0" : opts.showValue ? "1" : "0") + "\"/>";
|
|
3042
3310
|
strXml += " <c:showCatName val=\"" + (opts.showLabel ? "1" : "0") + "\"/>";
|
|
3043
3311
|
strXml += " <c:showSerName val=\"" + (opts.showSerName ? "1" : "0") + "\"/>";
|
|
3044
3312
|
strXml += " <c:showPercent val=\"" + (opts.showPercent ? "1" : "0") + "\"/>";
|
|
@@ -3053,7 +3321,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
3053
3321
|
strXml += " <a:pPr>";
|
|
3054
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">`;
|
|
3055
3323
|
strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
|
|
3056
|
-
strXml +=
|
|
3324
|
+
strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
|
|
3057
3325
|
strXml += " </a:defRPr>";
|
|
3058
3326
|
strXml += " </a:pPr>";
|
|
3059
3327
|
strXml += " </a:p>";
|
|
@@ -3082,6 +3350,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
3082
3350
|
strXml += " <c:numRef>";
|
|
3083
3351
|
strXml += ` <c:f>Sheet1!$B$2:$B$${optsChartData.labels[0].length + 1}</c:f>`;
|
|
3084
3352
|
strXml += " <c:numCache>";
|
|
3353
|
+
strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
|
|
3085
3354
|
strXml += ` <c:ptCount val="${optsChartData.labels[0].length}"/>`;
|
|
3086
3355
|
optsChartData.values.forEach((value, idx) => {
|
|
3087
3356
|
strXml += `<c:pt idx="${idx}"><c:v>${value || value === 0 ? value : ""}</c:v></c:pt>`;
|
|
@@ -3157,7 +3426,7 @@ function makeCatAxis(opts, axisId, valAxisId) {
|
|
|
3157
3426
|
strXml += " <a:pPr>";
|
|
3158
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">`;
|
|
3159
3428
|
strXml += " <a:solidFill>" + createColorElement(opts.catAxisLabelColor || "000000") + "</a:solidFill>";
|
|
3160
|
-
strXml += "
|
|
3429
|
+
strXml += " " + createChartTextFonts(opts.catAxisLabelFontFace || "Arial");
|
|
3161
3430
|
strXml += " </a:defRPr>";
|
|
3162
3431
|
strXml += " </a:pPr>";
|
|
3163
3432
|
strXml += " <a:endParaRPr lang=\"" + (opts.lang || "en-US") + "\"/>";
|
|
@@ -3250,7 +3519,7 @@ function makeValAxis(opts, valAxisId) {
|
|
|
3250
3519
|
strXml += " <a:pPr>";
|
|
3251
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">`;
|
|
3252
3521
|
strXml += " <a:solidFill>" + createColorElement(opts.valAxisLabelColor || "000000") + "</a:solidFill>";
|
|
3253
|
-
strXml += "
|
|
3522
|
+
strXml += " " + createChartTextFonts(opts.valAxisLabelFontFace || "Arial");
|
|
3254
3523
|
strXml += " </a:defRPr>";
|
|
3255
3524
|
strXml += " </a:pPr>";
|
|
3256
3525
|
strXml += " <a:endParaRPr lang=\"" + (opts.lang || "en-US") + "\"/>";
|
|
@@ -3306,7 +3575,7 @@ function makeSerAxis(opts, axisId, valAxisId) {
|
|
|
3306
3575
|
strXml += " <a:pPr>";
|
|
3307
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">`;
|
|
3308
3577
|
strXml += ` <a:solidFill>${createColorElement(opts.serAxisLabelColor || "000000")}</a:solidFill>`;
|
|
3309
|
-
strXml +=
|
|
3578
|
+
strXml += " " + createChartTextFonts(opts.serAxisLabelFontFace || "Arial");
|
|
3310
3579
|
strXml += " </a:defRPr>";
|
|
3311
3580
|
strXml += " </a:pPr>";
|
|
3312
3581
|
strXml += " <a:endParaRPr lang=\"" + (opts.lang || "en-US") + "\"/>";
|
|
@@ -3346,17 +3615,31 @@ function genXmlTitle(opts, chartX, chartY) {
|
|
|
3346
3615
|
const rotate = opts.titleRotate ? `<a:bodyPr rot="${convertRotationDegrees(opts.titleRotate)}"/>` : "<a:bodyPr/>";
|
|
3347
3616
|
const sizeAttr = opts.fontSize ? `sz="${Math.round(opts.fontSize * 100)}"` : "";
|
|
3348
3617
|
const titleBold = opts.titleBold ? 1 : 0;
|
|
3618
|
+
const titleItalic = opts.titleItalic ? 1 : 0;
|
|
3619
|
+
const titleUnderline = opts.titleUnderline ? "sng" : "none";
|
|
3349
3620
|
let layout = "<c:layout/>";
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
let
|
|
3354
|
-
|
|
3355
|
-
if (
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
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>`;
|
|
3360
3643
|
}
|
|
3361
3644
|
return `<c:title>
|
|
3362
3645
|
<c:tx>
|
|
@@ -3365,15 +3648,15 @@ function genXmlTitle(opts, chartX, chartY) {
|
|
|
3365
3648
|
<a:lstStyle/>
|
|
3366
3649
|
<a:p>
|
|
3367
3650
|
${align}
|
|
3368
|
-
<a:defRPr ${sizeAttr} b="${titleBold}" i="
|
|
3651
|
+
<a:defRPr ${sizeAttr} b="${titleBold}" i="${titleItalic}" u="${titleUnderline}" strike="noStrike">
|
|
3369
3652
|
<a:solidFill>${createColorElement(opts.color || "000000")}</a:solidFill>
|
|
3370
|
-
|
|
3653
|
+
${createChartTextFonts(opts.fontFace || "Arial")}
|
|
3371
3654
|
</a:defRPr>
|
|
3372
3655
|
</a:pPr>
|
|
3373
3656
|
<a:r>
|
|
3374
|
-
<a:rPr ${sizeAttr} b="${titleBold}" i="
|
|
3657
|
+
<a:rPr ${sizeAttr} b="${titleBold}" i="${titleItalic}" u="${titleUnderline}" strike="noStrike">
|
|
3375
3658
|
<a:solidFill>${createColorElement(opts.color || "000000")}</a:solidFill>
|
|
3376
|
-
|
|
3659
|
+
${createChartTextFonts(opts.fontFace || "Arial")}
|
|
3377
3660
|
</a:rPr>
|
|
3378
3661
|
<a:t>${encodeXmlEntities(opts.title) || ""}</a:t>
|
|
3379
3662
|
</a:r>
|
|
@@ -3447,11 +3730,105 @@ function createGridLineElement(glOpts) {
|
|
|
3447
3730
|
strXml += "</c:majorGridlines>";
|
|
3448
3731
|
return strXml;
|
|
3449
3732
|
}
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
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;
|
|
3455
3832
|
}
|
|
3456
3833
|
//#endregion
|
|
3457
3834
|
//#region src/gen-media.ts
|
|
@@ -3500,6 +3877,37 @@ function encodeSlideMediaRels(layout, runtime) {
|
|
|
3500
3877
|
/**
|
|
3501
3878
|
* PptxGenJS: XML Generation
|
|
3502
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
|
+
}
|
|
3503
3911
|
const ImageSizingXml = {
|
|
3504
3912
|
cover: function(imgSize, boxDim) {
|
|
3505
3913
|
const imgRatio = imgSize.h / imgSize.w;
|
|
@@ -3524,6 +3932,15 @@ const ImageSizingXml = {
|
|
|
3524
3932
|
const r = imgSize.w - (boxDim.x + boxDim.w);
|
|
3525
3933
|
const t = boxDim.y;
|
|
3526
3934
|
const b = imgSize.h - (boxDim.y + boxDim.h);
|
|
3935
|
+
if (l < 0 || r < 0 || t < 0 || b < 0) {
|
|
3936
|
+
const over = [
|
|
3937
|
+
l < 0 && `x (${l < 0 ? -l : 0} past left edge)`,
|
|
3938
|
+
r < 0 && `x+w (${-r} past right edge)`,
|
|
3939
|
+
t < 0 && `y (${-t} past top edge)`,
|
|
3940
|
+
b < 0 && `y+h (${-b} past bottom edge)`
|
|
3941
|
+
].filter(Boolean).join(", ");
|
|
3942
|
+
throw new Error(`addImage sizing.type 'crop': crop window overflows image bounds — ${over}. Ensure x≥0, y≥0, x+w≤w, y+h≤h.`);
|
|
3943
|
+
}
|
|
3527
3944
|
return `<a:srcRect l="${Math.round(1e5 * (l / imgSize.w))}" r="${Math.round(1e5 * (r / imgSize.w))}" t="${Math.round(1e5 * (t / imgSize.h))}" b="${Math.round(1e5 * (b / imgSize.h))}"/><a:stretch/>`;
|
|
3528
3945
|
}
|
|
3529
3946
|
};
|
|
@@ -3536,18 +3953,91 @@ const ImageSizingXml = {
|
|
|
3536
3953
|
* @param {number} cy - shape height (EMU), used to scale `rectRadius`
|
|
3537
3954
|
* @return {string} `<a:prstGeom>` XML
|
|
3538
3955
|
*/
|
|
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
|
+
}
|
|
3539
4008
|
function genXmlPresetGeom(shapeName, options, cx, cy) {
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
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
|
+
};
|
|
4016
|
+
if (options.rectRadius) {
|
|
4017
|
+
const adjVal = Math.round(options.rectRadius * EMU * 1e5 / Math.min(cx, cy));
|
|
4018
|
+
if (RECT_RADIUS_ADJ1_SHAPES.has(shapeName)) {
|
|
4019
|
+
emitGuide("adj1", adjVal);
|
|
4020
|
+
emitGuide("adj2", 0);
|
|
4021
|
+
} else emitGuide("adj", adjVal);
|
|
4022
|
+
} else if (options.angleRange) {
|
|
3543
4023
|
for (let i = 0; i < 2; i++) {
|
|
3544
4024
|
const angle = options.angleRange[i];
|
|
3545
|
-
|
|
4025
|
+
emitGuide(`adj${i + 1}`, convertRotationDegrees(angle));
|
|
3546
4026
|
}
|
|
3547
|
-
if (options.arcThicknessRatio)
|
|
4027
|
+
if (options.arcThicknessRatio) emitGuide("adj3", Math.round(options.arcThicknessRatio * 5e4));
|
|
3548
4028
|
}
|
|
3549
|
-
|
|
3550
|
-
|
|
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>`;
|
|
3551
4041
|
}
|
|
3552
4042
|
/**
|
|
3553
4043
|
* Emit an `<a:custGeom>` for a freeform path built from `points`.
|
|
@@ -3600,6 +4090,45 @@ function genXmlCustGeom(points, cx, cy, layout) {
|
|
|
3600
4090
|
}
|
|
3601
4091
|
const PLACEHOLDER_TYPE_MAP = PLACEHOLDER_TYPES;
|
|
3602
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
|
+
/**
|
|
3603
4132
|
* Transforms a slide or slideLayout to resulting XML string - Creates `ppt/slide*.xml`
|
|
3604
4133
|
* @param {PresSlideInternal|SlideLayoutInternal} slideObject - slide object created within createSlideObject
|
|
3605
4134
|
* @return {string} XML string with <p:cSld> as the root
|
|
@@ -3658,9 +4187,16 @@ function slideObjectToXml(slide) {
|
|
|
3658
4187
|
intColCnt += cellOpts?.colspan ? Number(cellOpts.colspan) : 1;
|
|
3659
4188
|
});
|
|
3660
4189
|
strXml = `<p:graphicFrame><p:nvGraphicFramePr><p:cNvPr id="${intTableNum * slide._slideNum + 1}" name="${slideItemObj.options.objectName}" descr="${encodeXmlEntities(slideItemObj.options.altText || "")}"/>`;
|
|
3661
|
-
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>`;
|
|
3662
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>`;
|
|
3663
|
-
|
|
4195
|
+
{
|
|
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\"" : "");
|
|
4197
|
+
const tblPr = objTabOpts.tableStyle ? `<a:tblPr${tblPrAttrs}><a:tableStyleId>${objTabOpts.tableStyle}</a:tableStyleId></a:tblPr>` : `<a:tblPr${tblPrAttrs}/>`;
|
|
4198
|
+
strXml += `<a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/table"><a:tbl>${tblPr}`;
|
|
4199
|
+
}
|
|
3664
4200
|
if (Array.isArray(objTabOpts.colW)) {
|
|
3665
4201
|
strXml += "<a:tblGrid>";
|
|
3666
4202
|
for (let col = 0; col < intColCnt; col++) {
|
|
@@ -3686,7 +4222,8 @@ function slideObjectToXml(slide) {
|
|
|
3686
4222
|
return {
|
|
3687
4223
|
_type: "tablecell",
|
|
3688
4224
|
options: { rowspan },
|
|
3689
|
-
_hmerge: true
|
|
4225
|
+
_hmerge: true,
|
|
4226
|
+
_spanOrigin: cell
|
|
3690
4227
|
};
|
|
3691
4228
|
});
|
|
3692
4229
|
cells.splice(cIdx + 1, 0, ...vMergeCells);
|
|
@@ -3702,12 +4239,14 @@ function slideObjectToXml(slide) {
|
|
|
3702
4239
|
const colspan = cell.options?.colspan;
|
|
3703
4240
|
const _hmerge = cell._hmerge;
|
|
3704
4241
|
if (rowspan && rowspan > 1) {
|
|
4242
|
+
const _spanOrigin = cell._spanOrigin || cell;
|
|
3705
4243
|
const hMergeCell = {
|
|
3706
4244
|
_type: "tablecell",
|
|
3707
4245
|
options: { colspan },
|
|
3708
4246
|
_rowContinue: rowspan - 1,
|
|
3709
4247
|
_vmerge: true,
|
|
3710
|
-
_hmerge
|
|
4248
|
+
_hmerge,
|
|
4249
|
+
_spanOrigin
|
|
3711
4250
|
};
|
|
3712
4251
|
nextRow.splice(cIdx, 0, hMergeCell);
|
|
3713
4252
|
}
|
|
@@ -3717,7 +4256,7 @@ function slideObjectToXml(slide) {
|
|
|
3717
4256
|
let intRowH = 0;
|
|
3718
4257
|
if (Array.isArray(objTabOpts.rowH) && objTabOpts.rowH[rIdx]) intRowH = inch2Emu(Number(objTabOpts.rowH[rIdx]));
|
|
3719
4258
|
else if (objTabOpts.rowH && !isNaN(Number(objTabOpts.rowH))) intRowH = inch2Emu(Number(objTabOpts.rowH));
|
|
3720
|
-
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);
|
|
3721
4260
|
strXml += `<a:tr h="${intRowH}">`;
|
|
3722
4261
|
cells.forEach((cellObj) => {
|
|
3723
4262
|
const cell = cellObj;
|
|
@@ -3730,7 +4269,17 @@ function slideObjectToXml(slide) {
|
|
|
3730
4269
|
let cellSpanAttrStr = Object.entries(cellSpanAttrs).filter(([, v]) => !!v).map(([k, v]) => `${String(k)}="${String(v)}"`).join(" ");
|
|
3731
4270
|
if (cellSpanAttrStr) cellSpanAttrStr = " " + cellSpanAttrStr;
|
|
3732
4271
|
if (cell._hmerge || cell._vmerge) {
|
|
3733
|
-
|
|
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>`;
|
|
3734
4283
|
return;
|
|
3735
4284
|
}
|
|
3736
4285
|
const cellOpts = cell.options || {};
|
|
@@ -3774,32 +4323,7 @@ function slideObjectToXml(slide) {
|
|
|
3774
4323
|
else cellMarginXml = ` marL="${inch2Emu(cellMargin[3])}" marR="${inch2Emu(cellMargin[1])}" marT="${inch2Emu(cellMargin[0])}" marB="${inch2Emu(cellMargin[2])}"`;
|
|
3775
4324
|
strXml += `<a:tc${cellSpanAttrStr}>${genXmlTextBody(cell)}<a:tcPr${cellMarginXml}${cellValign}${cellTextDir}>`;
|
|
3776
4325
|
const cellBorder = Array.isArray(cellOpts.border) ? cellOpts.border : null;
|
|
3777
|
-
if (cellBorder)
|
|
3778
|
-
{
|
|
3779
|
-
idx: 3,
|
|
3780
|
-
name: "lnL"
|
|
3781
|
-
},
|
|
3782
|
-
{
|
|
3783
|
-
idx: 1,
|
|
3784
|
-
name: "lnR"
|
|
3785
|
-
},
|
|
3786
|
-
{
|
|
3787
|
-
idx: 0,
|
|
3788
|
-
name: "lnT"
|
|
3789
|
-
},
|
|
3790
|
-
{
|
|
3791
|
-
idx: 2,
|
|
3792
|
-
name: "lnB"
|
|
3793
|
-
}
|
|
3794
|
-
].forEach((obj) => {
|
|
3795
|
-
const border = cellBorder[obj.idx];
|
|
3796
|
-
if (border.type !== "none") {
|
|
3797
|
-
strXml += `<a:${obj.name} w="${valToPts(border.pt)}" cap="flat" cmpd="sng" algn="ctr">`;
|
|
3798
|
-
strXml += `<a:solidFill>${createColorElement(border.color)}</a:solidFill>`;
|
|
3799
|
-
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"/>`;
|
|
3800
|
-
strXml += `</a:${obj.name}>`;
|
|
3801
|
-
} else strXml += `<a:${obj.name} w="0" cap="flat" cmpd="sng" algn="ctr"><a:noFill/></a:${obj.name}>`;
|
|
3802
|
-
});
|
|
4326
|
+
if (cellBorder) strXml += genTableCellBorderXml(cellBorder);
|
|
3803
4327
|
strXml += cellFill;
|
|
3804
4328
|
strXml += " </a:tcPr>";
|
|
3805
4329
|
strXml += " </a:tc>";
|
|
@@ -3818,10 +4342,10 @@ function slideObjectToXml(slide) {
|
|
|
3818
4342
|
if (!slideItemObj.options.line && cy === 0) cy = EMU * .3;
|
|
3819
4343
|
if (!slideItemObj.options._bodyProp) slideItemObj.options._bodyProp = {};
|
|
3820
4344
|
if (slideItemObj.options.margin && Array.isArray(slideItemObj.options.margin)) {
|
|
3821
|
-
slideItemObj.options._bodyProp.
|
|
4345
|
+
slideItemObj.options._bodyProp.tIns = valToPts(slideItemObj.options.margin[0] || 0);
|
|
3822
4346
|
slideItemObj.options._bodyProp.rIns = valToPts(slideItemObj.options.margin[1] || 0);
|
|
3823
4347
|
slideItemObj.options._bodyProp.bIns = valToPts(slideItemObj.options.margin[2] || 0);
|
|
3824
|
-
slideItemObj.options._bodyProp.
|
|
4348
|
+
slideItemObj.options._bodyProp.lIns = valToPts(slideItemObj.options.margin[3] || 0);
|
|
3825
4349
|
} else if (typeof slideItemObj.options.margin === "number") {
|
|
3826
4350
|
slideItemObj.options._bodyProp.lIns = valToPts(slideItemObj.options.margin);
|
|
3827
4351
|
slideItemObj.options._bodyProp.rIns = valToPts(slideItemObj.options.margin);
|
|
@@ -3833,7 +4357,11 @@ function slideObjectToXml(slide) {
|
|
|
3833
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) : ""}"/>`;
|
|
3834
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"/>`;
|
|
3835
4359
|
strSlideXml += "</p:cNvPr>";
|
|
3836
|
-
|
|
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
|
+
}
|
|
3837
4365
|
strSlideXml += `<p:nvPr>${slideItemObj._type === "placeholder" ? genXmlPlaceholder(slideItemObj) : genXmlPlaceholder(placeholderObj)}</p:nvPr>`;
|
|
3838
4366
|
strSlideXml += "</p:nvSpPr><p:spPr>";
|
|
3839
4367
|
strSlideXml += `<a:xfrm${locationAttr}>`;
|
|
@@ -3843,7 +4371,8 @@ function slideObjectToXml(slide) {
|
|
|
3843
4371
|
else strSlideXml += genXmlPresetGeom(slideItemObj.shape, slideItemObj.options, cx, cy);
|
|
3844
4372
|
strSlideXml += slideItemObj.options.fill ? genXmlColorSelection(slideItemObj.options.fill) : "<a:noFill/>";
|
|
3845
4373
|
if (slideItemObj.options.line) {
|
|
3846
|
-
|
|
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}>`;
|
|
3847
4376
|
if (slideItemObj.options.line.color) strSlideXml += genXmlColorSelection(slideItemObj.options.line);
|
|
3848
4377
|
if (slideItemObj.options.line.dashType) strSlideXml += `<a:prstDash val="${slideItemObj.options.line.dashType}"/>`;
|
|
3849
4378
|
if (slideItemObj.options.line.beginArrowType) strSlideXml += `<a:headEnd type="${slideItemObj.options.line.beginArrowType}"/>`;
|
|
@@ -3876,13 +4405,17 @@ function slideObjectToXml(slide) {
|
|
|
3876
4405
|
if (slideItemObj.hyperlink?.url) strSlideXml += `<a:hlinkClick r:id="rId${slideItemObj.hyperlink._rId}" tooltip="${slideItemObj.hyperlink.tooltip ? encodeXmlEntities(slideItemObj.hyperlink.tooltip) : ""}"/>`;
|
|
3877
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"/>`;
|
|
3878
4407
|
strSlideXml += " </p:cNvPr>";
|
|
3879
|
-
strSlideXml +=
|
|
4408
|
+
strSlideXml += ` <p:cNvPicPr>${genXmlObjectLock("a:picLocks", PICTURE_LOCK_ATTRS, {
|
|
4409
|
+
noChangeAspect: true,
|
|
4410
|
+
...slideItemObj.options.objectLock
|
|
4411
|
+
}, slideItemObj.options.objectName)}</p:cNvPicPr>`;
|
|
3880
4412
|
strSlideXml += " <p:nvPr>" + genXmlPlaceholder(placeholderObj) + "</p:nvPr>";
|
|
3881
4413
|
strSlideXml += " </p:nvPicPr>";
|
|
3882
4414
|
strSlideXml += "<p:blipFill>";
|
|
3883
4415
|
if ((slide._relsMedia || []).find((rel) => rel.rId === slideItemObj.imageRid)?.extn === "svg") {
|
|
3884
4416
|
strSlideXml += `<a:blip r:embed="rId${slideItemObj.imageRid - 1}">`;
|
|
3885
4417
|
strSlideXml += slideItemObj.options.transparency ? ` <a:alphaModFix amt="${Math.round((100 - slideItemObj.options.transparency) * 1e3)}"/>` : "";
|
|
4418
|
+
strSlideXml += slideItemObj.options.duotone ? `<a:duotone>${createColorElement(slideItemObj.options.duotone.shadow)}${createColorElement(slideItemObj.options.duotone.highlight)}</a:duotone>` : "";
|
|
3886
4419
|
strSlideXml += " <a:extLst>";
|
|
3887
4420
|
strSlideXml += " <a:ext uri=\"{96DAC541-7B7A-43D3-8B79-37D633B846F1}\">";
|
|
3888
4421
|
strSlideXml += ` <asvg:svgBlip xmlns:asvg="http://schemas.microsoft.com/office/drawing/2016/SVG/main" r:embed="rId${slideItemObj.imageRid}"/>`;
|
|
@@ -3892,6 +4425,7 @@ function slideObjectToXml(slide) {
|
|
|
3892
4425
|
} else {
|
|
3893
4426
|
strSlideXml += `<a:blip r:embed="rId${slideItemObj.imageRid}">`;
|
|
3894
4427
|
strSlideXml += slideItemObj.options.transparency ? `<a:alphaModFix amt="${Math.round((100 - slideItemObj.options.transparency) * 1e3)}"/>` : "";
|
|
4428
|
+
strSlideXml += slideItemObj.options.duotone ? `<a:duotone>${createColorElement(slideItemObj.options.duotone.shadow)}${createColorElement(slideItemObj.options.duotone.highlight)}</a:duotone>` : "";
|
|
3895
4429
|
strSlideXml += "</a:blip>";
|
|
3896
4430
|
}
|
|
3897
4431
|
if (sizing?.type) {
|
|
@@ -3899,10 +4433,17 @@ function slideObjectToXml(slide) {
|
|
|
3899
4433
|
const boxH = sizing.h ? getSmartParseNumber(sizing.h, "Y", slide._presLayout) : cy;
|
|
3900
4434
|
const boxX = getSmartParseNumber(sizing.x || 0, "X", slide._presLayout);
|
|
3901
4435
|
const boxY = getSmartParseNumber(sizing.y || 0, "Y", slide._presLayout);
|
|
3902
|
-
|
|
4436
|
+
let cropSize = {
|
|
3903
4437
|
w: imgWidth,
|
|
3904
4438
|
h: imgHeight
|
|
3905
|
-
}
|
|
4439
|
+
};
|
|
4440
|
+
if (sizing.type === "cover" || sizing.type === "contain") {
|
|
4441
|
+
const relData = (slide._relsMedia || []).find((rel) => rel.rId === slideItemObj.imageRid)?.data;
|
|
4442
|
+
const natural = typeof relData === "string" ? getImageSizeFromBase64(relData) : null;
|
|
4443
|
+
if (natural) cropSize = natural;
|
|
4444
|
+
else console.warn(`Warning: sizing '${sizing.type}' could not measure natural dimensions for image "${slideItemObj.options.objectName}"; falling back to displayed aspect ratio (crop may be inexact). Provide a raster image (PNG/JPEG/GIF/BMP/WebP) to enable an aspect-correct crop.`);
|
|
4445
|
+
}
|
|
4446
|
+
strSlideXml += ImageSizingXml[sizing.type](cropSize, {
|
|
3906
4447
|
w: boxW,
|
|
3907
4448
|
h: boxH,
|
|
3908
4449
|
x: boxX,
|
|
@@ -3942,7 +4483,7 @@ function slideObjectToXml(slide) {
|
|
|
3942
4483
|
strSlideXml += "<p:pic>";
|
|
3943
4484
|
strSlideXml += " <p:nvPicPr>";
|
|
3944
4485
|
strSlideXml += `<p:cNvPr id="${slideItemObj.mediaRid + 2}" name="${slideItemObj.options.objectName}" descr="${encodeXmlEntities(slideItemObj.options.altText || "")}"/>`;
|
|
3945
|
-
strSlideXml +=
|
|
4486
|
+
strSlideXml += ` <p:cNvPicPr>${genXmlObjectLock("a:picLocks", PICTURE_LOCK_ATTRS, slideItemObj.options.objectLock, slideItemObj.options.objectName)}</p:cNvPicPr>`;
|
|
3946
4487
|
strSlideXml += " <p:nvPr>";
|
|
3947
4488
|
strSlideXml += ` <a:videoFile r:link="rId${slideItemObj.mediaRid}"/>`;
|
|
3948
4489
|
strSlideXml += " </p:nvPr>";
|
|
@@ -3957,7 +4498,10 @@ function slideObjectToXml(slide) {
|
|
|
3957
4498
|
strSlideXml += "<p:pic>";
|
|
3958
4499
|
strSlideXml += " <p:nvPicPr>";
|
|
3959
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>`;
|
|
3960
|
-
strSlideXml +=
|
|
4501
|
+
strSlideXml += ` <p:cNvPicPr>${genXmlObjectLock("a:picLocks", PICTURE_LOCK_ATTRS, {
|
|
4502
|
+
noChangeAspect: true,
|
|
4503
|
+
...slideItemObj.options.objectLock
|
|
4504
|
+
}, slideItemObj.options.objectName)}</p:cNvPicPr>`;
|
|
3961
4505
|
strSlideXml += " <p:nvPr>";
|
|
3962
4506
|
strSlideXml += ` <a:videoFile r:link="rId${slideItemObj.mediaRid}"/>`;
|
|
3963
4507
|
strSlideXml += " <p:extLst>";
|
|
@@ -4021,7 +4565,7 @@ function slideObjectToXml(slide) {
|
|
|
4021
4565
|
strSlideXml += "/>";
|
|
4022
4566
|
strSlideXml += " <a:lstStyle><a:lvl1pPr>";
|
|
4023
4567
|
if (slide._slideNumberProps.fontFace || slide._slideNumberProps.fontSize || slide._slideNumberProps.color) {
|
|
4024
|
-
strSlideXml += `<a:defRPr sz="${
|
|
4568
|
+
strSlideXml += `<a:defRPr sz="${clampFontSizeSz(slide._slideNumberProps.fontSize || 12)}">`;
|
|
4025
4569
|
if (slide._slideNumberProps.color) strSlideXml += genXmlColorSelection(slide._slideNumberProps.color);
|
|
4026
4570
|
if (slide._slideNumberProps.fontFace) strSlideXml += `<a:latin typeface="${slide._slideNumberProps.fontFace}"/><a:ea typeface="${slide._slideNumberProps.fontFace}"/><a:cs typeface="${slide._slideNumberProps.fontFace}"/>`;
|
|
4027
4571
|
strSlideXml += "</a:defRPr>";
|
|
@@ -4110,7 +4654,7 @@ function genXmlParagraphProperties(textObj, isDefault) {
|
|
|
4110
4654
|
paragraphPropXml += "";
|
|
4111
4655
|
break;
|
|
4112
4656
|
}
|
|
4113
|
-
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>`;
|
|
4114
4658
|
else if (textObj.options.lineSpacingMultiple) strXmlLnSpc = `<a:lnSpc><a:spcPct val="${Math.round(textObj.options.lineSpacingMultiple * 1e5)}"/></a:lnSpc>`;
|
|
4115
4659
|
if (textObj.options.indentLevel && !isNaN(Number(textObj.options.indentLevel)) && textObj.options.indentLevel > 0) paragraphPropXml += ` lvl="${textObj.options.indentLevel}"`;
|
|
4116
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>`;
|
|
@@ -4164,7 +4708,7 @@ function genXmlTextRunProperties(opts, isDefault) {
|
|
|
4164
4708
|
let runProps = "";
|
|
4165
4709
|
const runPropsTag = isDefault ? "a:defRPr" : "a:rPr";
|
|
4166
4710
|
runProps += "<" + runPropsTag + " lang=\"" + (opts.lang ? opts.lang : "en-US") + "\"" + (opts.lang ? " altLang=\"en-US\"" : "");
|
|
4167
|
-
runProps += opts.fontSize ? ` sz="${
|
|
4711
|
+
runProps += opts.fontSize ? ` sz="${clampFontSizeSz(opts.fontSize)}"` : "";
|
|
4168
4712
|
runProps += opts?.bold ? ` b="${opts.bold ? "1" : "0"}"` : "";
|
|
4169
4713
|
runProps += opts?.italic ? ` i="${opts.italic ? "1" : "0"}"` : "";
|
|
4170
4714
|
runProps += opts?.strike ? ` strike="${typeof opts.strike === "string" ? opts.strike : "sngStrike"}"` : "";
|
|
@@ -4175,17 +4719,23 @@ function genXmlTextRunProperties(opts, isDefault) {
|
|
|
4175
4719
|
if (opts.baseline) runProps += ` baseline="${Math.round(opts.baseline * 50)}"`;
|
|
4176
4720
|
else if (opts.subscript) runProps += " baseline=\"-40000\"";
|
|
4177
4721
|
else if (opts.superscript) runProps += " baseline=\"30000\"";
|
|
4178
|
-
runProps += opts.charSpacing ? ` spc="${
|
|
4722
|
+
runProps += opts.charSpacing ? ` spc="${clampCharSpacingSpc(opts.charSpacing)}" kern="0"` : "";
|
|
4179
4723
|
runProps += " dirty=\"0\">";
|
|
4180
|
-
|
|
4181
|
-
|
|
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>`;
|
|
4182
4727
|
if (opts.color) runProps += genXmlColorSelection({
|
|
4183
4728
|
color: opts.color,
|
|
4184
4729
|
transparency: opts.transparency
|
|
4185
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
|
+
}
|
|
4186
4737
|
if (opts.highlight) runProps += `<a:highlight>${createColorElement(opts.highlight)}</a:highlight>`;
|
|
4187
4738
|
if (typeof opts.underline === "object" && opts.underline.color) runProps += `<a:uFill>${genXmlColorSelection(opts.underline.color)}</a:uFill>`;
|
|
4188
|
-
if (opts.glow) runProps += `<a:effectLst>${createGlowElement(opts.glow, DEF_TEXT_GLOW)}</a:effectLst>`;
|
|
4189
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"/>`;
|
|
4190
4740
|
}
|
|
4191
4741
|
if (opts.hyperlink) {
|
|
@@ -4215,6 +4765,28 @@ function genXmlTextRun(textObj) {
|
|
|
4215
4765
|
return `<a:r>${genXmlTextRunProperties(textObj.options, false)}<a:t>${encodeXmlEntities(String(textObj.text))}</a:t></a:r>`;
|
|
4216
4766
|
}
|
|
4217
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
|
+
/**
|
|
4218
4790
|
* Builds `<a:bodyPr></a:bodyPr>` tag for "genXmlTextBody()"
|
|
4219
4791
|
* @param {ISlideObject | TableCell} slideObject - various options
|
|
4220
4792
|
* @return {string} XML string
|
|
@@ -4227,6 +4799,8 @@ function genXmlBodyProperties(slideObject) {
|
|
|
4227
4799
|
if (slideObject.options._bodyProp.tIns || slideObject.options._bodyProp.tIns === 0) bodyProperties += ` tIns="${slideObject.options._bodyProp.tIns}"`;
|
|
4228
4800
|
if (slideObject.options._bodyProp.rIns || slideObject.options._bodyProp.rIns === 0) bodyProperties += ` rIns="${slideObject.options._bodyProp.rIns}"`;
|
|
4229
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}"`;
|
|
4230
4804
|
bodyProperties += " rtlCol=\"0\"";
|
|
4231
4805
|
if (slideObject.options._bodyProp.anchor) bodyProperties += " anchor=\"" + slideObject.options._bodyProp.anchor + "\"";
|
|
4232
4806
|
if (slideObject.options._bodyProp.vert) bodyProperties += " vert=\"" + slideObject.options._bodyProp.vert + "\"";
|
|
@@ -4237,9 +4811,11 @@ function genXmlBodyProperties(slideObject) {
|
|
|
4237
4811
|
* @see: http://www.datypic.com/sc/ooxml/g-a_EG_TextAutofit.html
|
|
4238
4812
|
*/
|
|
4239
4813
|
if (slideObject.options.fit) {
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
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);
|
|
4243
4819
|
}
|
|
4244
4820
|
if (slideObject.options.shrinkText) bodyProperties += "<a:normAutofit/>";
|
|
4245
4821
|
bodyProperties += slideObject.options._bodyProp.autoFit ? "<a:spAutoFit/>" : "";
|
|
@@ -4298,14 +4874,19 @@ function genXmlTextBody(slideObj) {
|
|
|
4298
4874
|
itext.options = itext.options || opts || {};
|
|
4299
4875
|
if (idx === 0 && itext.options && !itext.options.bullet && opts.bullet) itext.options.bullet = opts.bullet;
|
|
4300
4876
|
if (typeof itext.text === "string" || typeof itext.text === "number") itext.text = itext.text.toString().replace(/\r*\n/g, "\r\n");
|
|
4301
|
-
if (itext.text.includes("\r\n") && itext.text.match(/\n$/g) === null)
|
|
4302
|
-
itext.
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4877
|
+
if (itext.text.includes("\r\n") && itext.text.match(/\n$/g) === null) {
|
|
4878
|
+
const lines = itext.text.split("\r\n");
|
|
4879
|
+
lines.forEach((line, lineIdx) => {
|
|
4880
|
+
const isLast = lineIdx === lines.length - 1;
|
|
4881
|
+
arrTextObjects.push({
|
|
4882
|
+
text: line,
|
|
4883
|
+
options: {
|
|
4884
|
+
...itext.options,
|
|
4885
|
+
breakLine: isLast ? itext.options.breakLine : true
|
|
4886
|
+
}
|
|
4887
|
+
});
|
|
4306
4888
|
});
|
|
4307
|
-
});
|
|
4308
|
-
else arrTextObjects.push(itext);
|
|
4889
|
+
} else arrTextObjects.push(itext);
|
|
4309
4890
|
});
|
|
4310
4891
|
const arrLines = [];
|
|
4311
4892
|
let arrTexts = [];
|
|
@@ -4370,13 +4951,13 @@ function genXmlTextBody(slideObj) {
|
|
|
4370
4951
|
}
|
|
4371
4952
|
});
|
|
4372
4953
|
if (slideObj._type === "tablecell" && (opts.fontSize || opts.fontFace)) if (opts.fontFace) {
|
|
4373
|
-
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\">";
|
|
4374
4955
|
strSlideXml += `<a:latin typeface="${opts.fontFace}" charset="0"/>`;
|
|
4375
4956
|
strSlideXml += `<a:ea typeface="${opts.fontFace}" charset="0"/>`;
|
|
4376
4957
|
strSlideXml += `<a:cs typeface="${opts.fontFace}" charset="0"/>`;
|
|
4377
4958
|
strSlideXml += "</a:endParaRPr>";
|
|
4378
|
-
} else strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}"` + (opts.fontSize ? ` sz="${
|
|
4379
|
-
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\"/>";
|
|
4380
4961
|
else strSlideXml += `<a:endParaRPr lang="${opts.lang || "en-US"}" dirty="0"/>`;
|
|
4381
4962
|
strSlideXml += "</a:p>";
|
|
4382
4963
|
});
|
|
@@ -4407,7 +4988,7 @@ function genXmlPlaceholder(placeholderObj) {
|
|
|
4407
4988
|
* @param {PresSlideInternal} masterSlide - master slide
|
|
4408
4989
|
* @returns XML
|
|
4409
4990
|
*/
|
|
4410
|
-
function makeXmlContTypes(slides, slideLayouts, masterSlide) {
|
|
4991
|
+
function makeXmlContTypes(slides, slideLayouts, masterSlide, hasCustomProps) {
|
|
4411
4992
|
let strXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n";
|
|
4412
4993
|
strXml += "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">";
|
|
4413
4994
|
strXml += "<Default Extension=\"xml\" ContentType=\"application/xml\"/>";
|
|
@@ -4457,6 +5038,7 @@ function makeXmlContTypes(slides, slideLayouts, masterSlide) {
|
|
|
4457
5038
|
});
|
|
4458
5039
|
strXml += " <Override PartName=\"/docProps/core.xml\" ContentType=\"application/vnd.openxmlformats-package.core-properties+xml\"/>";
|
|
4459
5040
|
strXml += " <Override PartName=\"/docProps/app.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.extended-properties+xml\"/>";
|
|
5041
|
+
if (hasCustomProps) strXml += " <Override PartName=\"/docProps/custom.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.custom-properties+xml\"/>";
|
|
4460
5042
|
strXml += "</Types>";
|
|
4461
5043
|
return strXml;
|
|
4462
5044
|
}
|
|
@@ -4464,13 +5046,15 @@ function makeXmlContTypes(slides, slideLayouts, masterSlide) {
|
|
|
4464
5046
|
* Creates `_rels/.rels`
|
|
4465
5047
|
* @returns XML
|
|
4466
5048
|
*/
|
|
4467
|
-
function makeXmlRootRels() {
|
|
4468
|
-
|
|
5049
|
+
function makeXmlRootRels(hasCustomProps) {
|
|
5050
|
+
let xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r
|
|
4469
5051
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
4470
5052
|
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
|
|
4471
5053
|
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
|
|
4472
|
-
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"
|
|
4473
|
-
|
|
5054
|
+
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/>`;
|
|
5055
|
+
if (hasCustomProps) xml += "\n <Relationship Id=\"rId4\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties\" Target=\"docProps/custom.xml\"/>";
|
|
5056
|
+
xml += "\n </Relationships>";
|
|
5057
|
+
return xml;
|
|
4474
5058
|
}
|
|
4475
5059
|
/**
|
|
4476
5060
|
* Creates `docProps/app.xml`
|
|
@@ -4536,6 +5120,23 @@ function makeXmlCore(title, subject, author, revision) {
|
|
|
4536
5120
|
<dcterms:modified xsi:type="dcterms:W3CDTF">${(/* @__PURE__ */ new Date()).toISOString().replace(/\.\d\d\dZ/, "Z")}</dcterms:modified>
|
|
4537
5121
|
</cp:coreProperties>`;
|
|
4538
5122
|
}
|
|
5123
|
+
const CUSTOM_PROPS_FMTID = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}";
|
|
5124
|
+
/**
|
|
5125
|
+
* Creates `docProps/custom.xml`
|
|
5126
|
+
* @param props - custom property name/value pairs
|
|
5127
|
+
* @returns XML
|
|
5128
|
+
*/
|
|
5129
|
+
function makeXmlCustomProperties(props) {
|
|
5130
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r
|
|
5131
|
+
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">${props.map(({ name, value }, idx) => {
|
|
5132
|
+
let valueXml;
|
|
5133
|
+
if (typeof value === "boolean") valueXml = `<vt:bool>${value}</vt:bool>`;
|
|
5134
|
+
else if (value instanceof Date) valueXml = `<vt:filetime>${value.toISOString().replace(/\.\d{3}Z$/, "Z")}</vt:filetime>`;
|
|
5135
|
+
else if (typeof value === "number") valueXml = Number.isInteger(value) ? `<vt:i4>${value}</vt:i4>` : `<vt:r8>${value}</vt:r8>`;
|
|
5136
|
+
else valueXml = `<vt:lpwstr>${encodeXmlEntities(String(value))}</vt:lpwstr>`;
|
|
5137
|
+
return `<property fmtid="${CUSTOM_PROPS_FMTID}" pid="${idx + 2}" name="${encodeXmlEntities(name)}">${valueXml}</property>`;
|
|
5138
|
+
}).join("")}</Properties>`;
|
|
5139
|
+
}
|
|
4539
5140
|
/**
|
|
4540
5141
|
* Creates `ppt/_rels/presentation.xml.rels`
|
|
4541
5142
|
* @param {PresSlideInternal[]} slides - Presenation Slides
|
|
@@ -4712,7 +5313,7 @@ function makeXmlTheme(pres) {
|
|
|
4712
5313
|
*/
|
|
4713
5314
|
function makeXmlPresentation(pres) {
|
|
4714
5315
|
let strXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r
|
|
4715
|
-
<p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" ${pres.rtlMode ? "rtl=\"1\"" : ""} saveSubsetFonts="1" autoCompressPictures="0">`;
|
|
5316
|
+
<p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" ${pres.rtlMode ? "rtl=\"1\"" : ""} saveSubsetFonts="1" autoCompressPictures="0"${pres.firstSlideNum !== 1 ? ` firstSlideNum="${pres.firstSlideNum}"` : ""}>`;
|
|
4716
5317
|
strXml += "<p:sldMasterIdLst><p:sldMasterId id=\"2147483648\" r:id=\"rId1\"/></p:sldMasterIdLst>";
|
|
4717
5318
|
strXml += `<p:notesMasterIdLst><p:notesMasterId r:id="rId${pres.slides.length + 2}"/></p:notesMasterIdLst>`;
|
|
4718
5319
|
strXml += "<p:sldIdLst>";
|
|
@@ -4751,9 +5352,96 @@ function makeXmlPresProps() {
|
|
|
4751
5352
|
* @see: http://openxmldeveloper.org/discussions/formats/f/13/p/2398/8107.aspx
|
|
4752
5353
|
* @return {string} XML
|
|
4753
5354
|
*/
|
|
4754
|
-
function makeXmlTableStyles() {
|
|
4755
|
-
|
|
4756
|
-
<a:tblStyleLst xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" def="{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}"
|
|
5355
|
+
function makeXmlTableStyles(tableStyles = []) {
|
|
5356
|
+
const open = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r
|
|
5357
|
+
<a:tblStyleLst xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" def="{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}"`;
|
|
5358
|
+
if (!tableStyles || tableStyles.length === 0) return `${open}/>`;
|
|
5359
|
+
let strXml = `${open}>`;
|
|
5360
|
+
tableStyles.forEach(({ guid, def }) => {
|
|
5361
|
+
strXml += `<a:tblStyle styleId="${guid}" styleName="${encodeXmlEntities(def.name)}">`;
|
|
5362
|
+
[
|
|
5363
|
+
["wholeTbl", def.wholeTbl],
|
|
5364
|
+
["band1H", def.band1H],
|
|
5365
|
+
["band2H", def.band2H],
|
|
5366
|
+
["band1V", def.band1V],
|
|
5367
|
+
["band2V", def.band2V],
|
|
5368
|
+
["lastCol", def.lastCol],
|
|
5369
|
+
["firstCol", def.firstCol],
|
|
5370
|
+
["lastRow", def.lastRow],
|
|
5371
|
+
["firstRow", def.firstRow]
|
|
5372
|
+
].forEach(([name, region]) => {
|
|
5373
|
+
if (region) strXml += genXmlTableStyleRegion(name, region);
|
|
5374
|
+
});
|
|
5375
|
+
strXml += "</a:tblStyle>";
|
|
5376
|
+
});
|
|
5377
|
+
strXml += "</a:tblStyleLst>";
|
|
5378
|
+
return strXml;
|
|
5379
|
+
}
|
|
5380
|
+
/**
|
|
5381
|
+
* Build one `CT_TablePartStyle` region (e.g. `firstRow`, `band1H`) for a custom table style.
|
|
5382
|
+
* Emits `tcTxStyle` (text) before `tcStyle` (cell fill/borders) per the schema sequence.
|
|
5383
|
+
* @param {string} name - region element name
|
|
5384
|
+
* @param {TableStyleRegionProps} region - region styling
|
|
5385
|
+
* @return {string} XML
|
|
5386
|
+
*/
|
|
5387
|
+
function genXmlTableStyleRegion(name, region) {
|
|
5388
|
+
let xml = `<a:${name}>`;
|
|
5389
|
+
if (region.bold !== void 0 || region.italic !== void 0 || region.color) {
|
|
5390
|
+
const b = region.bold ? " b=\"on\"" : "";
|
|
5391
|
+
const i = region.italic ? " i=\"on\"" : "";
|
|
5392
|
+
xml += `<a:tcTxStyle${b}${i}><a:fontRef idx="minor"/>`;
|
|
5393
|
+
xml += region.color ? createColorElement(region.color) : "";
|
|
5394
|
+
xml += "</a:tcTxStyle>";
|
|
5395
|
+
}
|
|
5396
|
+
if (region.border !== void 0 || region.fill !== void 0) {
|
|
5397
|
+
xml += "<a:tcStyle>";
|
|
5398
|
+
if (region.border !== void 0) xml += genXmlTableStyleBorders(region.border);
|
|
5399
|
+
if (region.fill !== void 0) xml += `<a:fill><a:solidFill>${createColorElement(region.fill)}</a:solidFill></a:fill>`;
|
|
5400
|
+
xml += "</a:tcStyle>";
|
|
5401
|
+
}
|
|
5402
|
+
xml += `</a:${name}>`;
|
|
5403
|
+
return xml;
|
|
5404
|
+
}
|
|
5405
|
+
/**
|
|
5406
|
+
* Build the `tcBdr` border block for a custom table style region.
|
|
5407
|
+
* A single `BorderProps` styles all four sides plus the interior grid lines; a
|
|
5408
|
+
* TRBL array styles only the four outer sides. Sides are emitted in schema order.
|
|
5409
|
+
* @param {BorderProps | BorderProps[]} border - border definition
|
|
5410
|
+
* @return {string} XML
|
|
5411
|
+
*/
|
|
5412
|
+
function genXmlTableStyleBorders(border) {
|
|
5413
|
+
let sides;
|
|
5414
|
+
if (Array.isArray(border)) {
|
|
5415
|
+
const [top, right, bottom, left] = border;
|
|
5416
|
+
sides = [
|
|
5417
|
+
["left", left],
|
|
5418
|
+
["right", right],
|
|
5419
|
+
["top", top],
|
|
5420
|
+
["bottom", bottom]
|
|
5421
|
+
];
|
|
5422
|
+
} else sides = [
|
|
5423
|
+
["left", border],
|
|
5424
|
+
["right", border],
|
|
5425
|
+
["top", border],
|
|
5426
|
+
["bottom", border],
|
|
5427
|
+
["insideH", border],
|
|
5428
|
+
["insideV", border]
|
|
5429
|
+
];
|
|
5430
|
+
let xml = "<a:tcBdr>";
|
|
5431
|
+
sides.forEach(([side, b]) => {
|
|
5432
|
+
if (!b) return;
|
|
5433
|
+
xml += `<a:${side}>`;
|
|
5434
|
+
if (b.type === "none") xml += "<a:ln><a:noFill/></a:ln>";
|
|
5435
|
+
else {
|
|
5436
|
+
xml += `<a:ln w="${lineWidthToEmu(b.pt ?? 1)}" cap="flat" cmpd="sng" algn="ctr">`;
|
|
5437
|
+
xml += `<a:solidFill>${createColorElement(b.color ?? "666666")}</a:solidFill>`;
|
|
5438
|
+
xml += `<a:prstDash val="${b.type === "dash" ? "sysDash" : "solid"}"/>`;
|
|
5439
|
+
xml += "</a:ln>";
|
|
5440
|
+
}
|
|
5441
|
+
xml += `</a:${side}>`;
|
|
5442
|
+
});
|
|
5443
|
+
xml += "</a:tcBdr>";
|
|
5444
|
+
return xml;
|
|
4757
5445
|
}
|
|
4758
5446
|
/**
|
|
4759
5447
|
* Creates `ppt/viewProps.xml`
|
|
@@ -4823,7 +5511,7 @@ function makeXmlViewProps() {
|
|
|
4823
5511
|
* @see https://docs.microsoft.com/en-us/office/open-xml/structure-of-a-presentationml-document
|
|
4824
5512
|
* @see https://docs.microsoft.com/en-us/previous-versions/office/developer/office-2010/hh273476(v=office.14)
|
|
4825
5513
|
*/
|
|
4826
|
-
const VERSION = "5.
|
|
5514
|
+
const VERSION = "5.3.0";
|
|
4827
5515
|
function standardLayoutToPresLayout(layout) {
|
|
4828
5516
|
return {
|
|
4829
5517
|
name: layout.name,
|
|
@@ -4924,6 +5612,14 @@ var PptxGenJS = class {
|
|
|
4924
5612
|
get title() {
|
|
4925
5613
|
return this._title;
|
|
4926
5614
|
}
|
|
5615
|
+
/** Slide number shown on the first slide (maps to firstSlideNum in presentation.xml) */
|
|
5616
|
+
_firstSlideNum;
|
|
5617
|
+
set firstSlideNum(value) {
|
|
5618
|
+
this._firstSlideNum = value;
|
|
5619
|
+
}
|
|
5620
|
+
get firstSlideNum() {
|
|
5621
|
+
return this._firstSlideNum;
|
|
5622
|
+
}
|
|
4927
5623
|
/**
|
|
4928
5624
|
* Whether Right-to-Left (RTL) mode is enabled
|
|
4929
5625
|
* @type {boolean}
|
|
@@ -4950,6 +5646,9 @@ var PptxGenJS = class {
|
|
|
4950
5646
|
get sections() {
|
|
4951
5647
|
return this._sections;
|
|
4952
5648
|
}
|
|
5649
|
+
/** custom document properties stored in docProps/custom.xml */
|
|
5650
|
+
_customProperties;
|
|
5651
|
+
_tableStyles;
|
|
4953
5652
|
/** slide layout definition objects, used for generating slide layout files */
|
|
4954
5653
|
_slideLayouts;
|
|
4955
5654
|
get slideLayouts() {
|
|
@@ -4959,6 +5658,7 @@ var PptxGenJS = class {
|
|
|
4959
5658
|
return {
|
|
4960
5659
|
author: this.author,
|
|
4961
5660
|
company: this.company,
|
|
5661
|
+
firstSlideNum: this.firstSlideNum,
|
|
4962
5662
|
layout: this.layout,
|
|
4963
5663
|
masterSlide: this._masterSlide,
|
|
4964
5664
|
presLayout: this.presLayout,
|
|
@@ -5043,6 +5743,7 @@ var PptxGenJS = class {
|
|
|
5043
5743
|
width: this.LAYOUTS[DEF_PRES_LAYOUT].width,
|
|
5044
5744
|
height: this.LAYOUTS[DEF_PRES_LAYOUT].height
|
|
5045
5745
|
};
|
|
5746
|
+
this._firstSlideNum = 1;
|
|
5046
5747
|
this._rtlMode = false;
|
|
5047
5748
|
this._slideLayouts = [{
|
|
5048
5749
|
_margin: DEF_SLIDE_MARGIN_IN,
|
|
@@ -5058,6 +5759,8 @@ var PptxGenJS = class {
|
|
|
5058
5759
|
}];
|
|
5059
5760
|
this._slides = [];
|
|
5060
5761
|
this._sections = [];
|
|
5762
|
+
this._customProperties = [];
|
|
5763
|
+
this._tableStyles = [];
|
|
5061
5764
|
this._masterSlide = {
|
|
5062
5765
|
addChart: null,
|
|
5063
5766
|
addImage: null,
|
|
@@ -5086,7 +5789,8 @@ var PptxGenJS = class {
|
|
|
5086
5789
|
*/
|
|
5087
5790
|
addNewSlide = (options) => {
|
|
5088
5791
|
const nextOptions = options || {};
|
|
5089
|
-
|
|
5792
|
+
const lastSlide = this._slides[this._slides.length - 1];
|
|
5793
|
+
nextOptions.sectionTitle = this._sections.find((sect) => sect._slides.some((s) => s._slideNum === lastSlide._slideNum))?.title ?? null;
|
|
5090
5794
|
return this.addSlide(nextOptions);
|
|
5091
5795
|
};
|
|
5092
5796
|
/**
|
|
@@ -5139,6 +5843,18 @@ var PptxGenJS = class {
|
|
|
5139
5843
|
});
|
|
5140
5844
|
arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(this._masterSlide, this._runtime));
|
|
5141
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
|
+
}
|
|
5142
5858
|
this._slides.forEach((slide) => {
|
|
5143
5859
|
if (slide._slideLayout) addPlaceholdersToSlideLayouts(slide);
|
|
5144
5860
|
});
|
|
@@ -5156,16 +5872,18 @@ var PptxGenJS = class {
|
|
|
5156
5872
|
zip.folder("ppt/theme");
|
|
5157
5873
|
zip.folder("ppt/notesMasters").folder("_rels");
|
|
5158
5874
|
zip.folder("ppt/notesSlides").folder("_rels");
|
|
5159
|
-
|
|
5160
|
-
zip.file("
|
|
5875
|
+
const hasCustomProps = this._customProperties.length > 0;
|
|
5876
|
+
zip.file("[Content_Types].xml", makeXmlContTypes(this._slides, this._slideLayouts, this._masterSlide, hasCustomProps));
|
|
5877
|
+
zip.file("_rels/.rels", makeXmlRootRels(hasCustomProps));
|
|
5161
5878
|
zip.file("docProps/app.xml", makeXmlApp(this._slides, this.company));
|
|
5162
5879
|
zip.file("docProps/core.xml", makeXmlCore(this.title, this.subject, this.author, this.revision));
|
|
5880
|
+
if (hasCustomProps) zip.file("docProps/custom.xml", makeXmlCustomProperties(this._customProperties));
|
|
5163
5881
|
zip.file("ppt/_rels/presentation.xml.rels", makeXmlPresentationRels(this._slides));
|
|
5164
5882
|
zip.file("ppt/theme/theme1.xml", makeXmlTheme(this.internalPresentation));
|
|
5165
5883
|
zip.file("ppt/theme/theme2.xml", makeXmlTheme(this.internalPresentation));
|
|
5166
5884
|
zip.file("ppt/presentation.xml", makeXmlPresentation(this.internalPresentation));
|
|
5167
5885
|
zip.file("ppt/presProps.xml", makeXmlPresProps());
|
|
5168
|
-
zip.file("ppt/tableStyles.xml", makeXmlTableStyles());
|
|
5886
|
+
zip.file("ppt/tableStyles.xml", makeXmlTableStyles(this._tableStyles));
|
|
5169
5887
|
zip.file("ppt/viewProps.xml", makeXmlViewProps());
|
|
5170
5888
|
this._slideLayouts.forEach((layout, idx) => {
|
|
5171
5889
|
zip.file(`ppt/slideLayouts/slideLayout${idx + 1}.xml`, makeXmlLayout(layout));
|
|
@@ -5245,6 +5963,19 @@ var PptxGenJS = class {
|
|
|
5245
5963
|
return await this._runtime.writeFile(fileName, data);
|
|
5246
5964
|
}
|
|
5247
5965
|
/**
|
|
5966
|
+
* Set a custom document property stored in `docProps/custom.xml`.
|
|
5967
|
+
* Calling with the same name replaces the existing value.
|
|
5968
|
+
* @param name - property name
|
|
5969
|
+
* @param value - string, integer/float number, boolean, or Date
|
|
5970
|
+
*/
|
|
5971
|
+
setCustomProperty(name, value) {
|
|
5972
|
+
this._customProperties = this._customProperties.filter((p) => p.name !== name);
|
|
5973
|
+
this._customProperties.push({
|
|
5974
|
+
name,
|
|
5975
|
+
value
|
|
5976
|
+
});
|
|
5977
|
+
}
|
|
5978
|
+
/**
|
|
5248
5979
|
* Add a new Section to Presentation
|
|
5249
5980
|
* @param {ISectionProps} section - section properties
|
|
5250
5981
|
* @example pptx.addSection({ title:'Charts' });
|
|
@@ -5353,6 +6084,31 @@ var PptxGenJS = class {
|
|
|
5353
6084
|
if (newLayout._slideNumberProps && !this._masterSlide._slideNumberProps) this._masterSlide._slideNumberProps = newLayout._slideNumberProps;
|
|
5354
6085
|
}
|
|
5355
6086
|
/**
|
|
6087
|
+
* Register a reusable custom table style and return its GUID.
|
|
6088
|
+
* The style is written to `ppt/tableStyles.xml` and is editable in PowerPoint's
|
|
6089
|
+
* Table Styles gallery. Pass the returned GUID as `TableProps.tableStyle`, and use
|
|
6090
|
+
* the `has*` flags (`hasHeader`, `hasBandedRows`, …) to activate the matching regions.
|
|
6091
|
+
* @param {TableStyleProps} props - custom table style definition (requires `name`)
|
|
6092
|
+
* @returns {string} braced GUID to use as `tableStyle`
|
|
6093
|
+
* @example
|
|
6094
|
+
* const brand = pptx.defineTableStyle({
|
|
6095
|
+
* name: 'Brand Banded',
|
|
6096
|
+
* firstRow: { fill:'1A2B3C', color:'FFFFFF', bold:true },
|
|
6097
|
+
* band1H: { fill:'EAF1F8' },
|
|
6098
|
+
* })
|
|
6099
|
+
* slide.addTable(rows, { tableStyle: brand, hasHeader:true, hasBandedRows:true })
|
|
6100
|
+
*/
|
|
6101
|
+
defineTableStyle(props) {
|
|
6102
|
+
if (!props || typeof props !== "object") throw new Error("defineTableStyle() requires a `{ name, ... }` object argument");
|
|
6103
|
+
if (!props.name || typeof props.name !== "string") throw new Error("defineTableStyle() requires a non-empty `name`");
|
|
6104
|
+
const guid = `{${getUuid("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx").toUpperCase()}}`;
|
|
6105
|
+
this._tableStyles.push({
|
|
6106
|
+
guid,
|
|
6107
|
+
def: props
|
|
6108
|
+
});
|
|
6109
|
+
return guid;
|
|
6110
|
+
}
|
|
6111
|
+
/**
|
|
5356
6112
|
* Reproduces an HTML table as a PowerPoint table - including column widths, style, etc. - creates 1 or more slides as needed
|
|
5357
6113
|
* @param {string} eleId - table HTML element ID
|
|
5358
6114
|
* @param {TableToSlidesProps} options - generation options
|
|
@@ -5364,4 +6120,4 @@ var PptxGenJS = class {
|
|
|
5364
6120
|
//#endregion
|
|
5365
6121
|
export { PptxGenJS as t };
|
|
5366
6122
|
|
|
5367
|
-
//# sourceMappingURL=pptxgen-
|
|
6123
|
+
//# sourceMappingURL=pptxgen-B-mAxCRC.js.map
|