pptx-kit-preview 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index-5nb1RZV7.d.ts +45 -0
- package/dist/index.d.ts +2 -42
- package/dist/index.js +1 -4570
- package/dist/node.d.ts +25 -23
- package/dist/node.js +131 -4687
- package/dist/node.js.map +1 -1
- package/dist/src-CDTTqUfI.js +4177 -0
- package/dist/src-CDTTqUfI.js.map +1 -0
- package/package.json +8 -5
- package/dist/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4571 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
// src/render-slide.ts
|
|
4
|
-
|
|
5
|
-
// src/text-layout.ts
|
|
6
|
-
var SANS = "Carlito";
|
|
7
|
-
var SERIF = "Caladea";
|
|
8
|
-
var ARIAL = "Liberation Sans";
|
|
9
|
-
var TIMES = "Liberation Serif";
|
|
10
|
-
var MONO = "Liberation Mono";
|
|
11
|
-
var substituteFamily = (family) => {
|
|
12
|
-
if (!family) return SANS;
|
|
13
|
-
const f = family.trim().toLowerCase();
|
|
14
|
-
if (f.startsWith("calibri") || f.startsWith("aptos")) return SANS;
|
|
15
|
-
if (f.startsWith("cambria")) return SERIF;
|
|
16
|
-
if (f === "arial" || f === "helvetica" || f === "helvetica neue" || f.startsWith("arial "))
|
|
17
|
-
return ARIAL;
|
|
18
|
-
if (f === "times new roman" || f === "times" || f.startsWith("times ")) return TIMES;
|
|
19
|
-
if (f === "courier new" || f === "courier" || f === "consolas" || f.startsWith("courier "))
|
|
20
|
-
return MONO;
|
|
21
|
-
return SANS;
|
|
22
|
-
};
|
|
23
|
-
var isCjk = (cp) => cp >= 12352 && cp <= 12447 || cp >= 12448 && cp <= 12543 || cp >= 19968 && cp <= 40959 || cp >= 44032 && cp <= 55215;
|
|
24
|
-
var AVG_GLYPH_W_RATIO = 0.55;
|
|
25
|
-
var defaultMeasurer = (text, spec) => {
|
|
26
|
-
let w = 0;
|
|
27
|
-
for (const ch of text) {
|
|
28
|
-
const ratio = isCjk(ch.codePointAt(0) ?? 0) ? 1 : AVG_GLYPH_W_RATIO;
|
|
29
|
-
w += spec.sizePx * ratio;
|
|
30
|
-
}
|
|
31
|
-
const n = [...text].length;
|
|
32
|
-
if (n > 1) w += spec.letterSpacingPx * (n - 1);
|
|
33
|
-
return { widthPx: w };
|
|
34
|
-
};
|
|
35
|
-
var FALLBACK_ASCENT = 0.9;
|
|
36
|
-
var FALLBACK_DESCENT = 0.22;
|
|
37
|
-
var FALLBACK_LINEGAP = 0.08;
|
|
38
|
-
var CENTER_ANCHOR_DROP = 0.036;
|
|
39
|
-
var GRID_NUDGE_X = -0.75;
|
|
40
|
-
var specOf = (piece) => ({
|
|
41
|
-
family: piece.family,
|
|
42
|
-
sizePx: piece.sizePx,
|
|
43
|
-
bold: piece.bold,
|
|
44
|
-
italic: piece.italic,
|
|
45
|
-
letterSpacingPx: piece.letterSpacingPx
|
|
46
|
-
});
|
|
47
|
-
var bulletSpec = (b) => ({
|
|
48
|
-
family: b.family,
|
|
49
|
-
sizePx: b.sizePx,
|
|
50
|
-
bold: false,
|
|
51
|
-
italic: false,
|
|
52
|
-
letterSpacingPx: 0
|
|
53
|
-
});
|
|
54
|
-
var escapeXml = (s) => s.replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c] ?? c);
|
|
55
|
-
var fmt = (n) => {
|
|
56
|
-
const r = Math.round(n * 100) / 100;
|
|
57
|
-
return Object.is(r, -0) ? "0" : String(r);
|
|
58
|
-
};
|
|
59
|
-
var layoutTextSvg = (input, measure) => {
|
|
60
|
-
const widthCache = /* @__PURE__ */ new Map();
|
|
61
|
-
const metricCache = /* @__PURE__ */ new Map();
|
|
62
|
-
const key = (text, s) => `${s.family}|${s.sizePx}|${s.bold}|${s.italic}|${s.letterSpacingPx}|${text}`;
|
|
63
|
-
const mWidth = (text, s) => {
|
|
64
|
-
const k = key(text, s);
|
|
65
|
-
let w = widthCache.get(k);
|
|
66
|
-
if (w === void 0) {
|
|
67
|
-
w = measure(text, s).widthPx;
|
|
68
|
-
widthCache.set(k, w);
|
|
69
|
-
}
|
|
70
|
-
return w;
|
|
71
|
-
};
|
|
72
|
-
const mMetrics = (piece) => {
|
|
73
|
-
const s = specOf(piece);
|
|
74
|
-
const k = `${s.family}|${s.sizePx}|${s.bold}|${s.italic}`;
|
|
75
|
-
let m = metricCache.get(k);
|
|
76
|
-
if (!m) {
|
|
77
|
-
const r = measure("Mg", s);
|
|
78
|
-
m = r.ascentPx !== void 0 && r.descentPx !== void 0 ? { a: r.ascentPx, d: r.descentPx, g: r.lineGapPx ?? 0 } : {
|
|
79
|
-
a: piece.sizePx * FALLBACK_ASCENT,
|
|
80
|
-
d: piece.sizePx * FALLBACK_DESCENT,
|
|
81
|
-
g: piece.sizePx * FALLBACK_LINEGAP
|
|
82
|
-
};
|
|
83
|
-
metricCache.set(k, m);
|
|
84
|
-
}
|
|
85
|
-
return m;
|
|
86
|
-
};
|
|
87
|
-
const buildLines = (contentLeft, contentRight) => {
|
|
88
|
-
const lines = [];
|
|
89
|
-
let cursorY = 0;
|
|
90
|
-
for (const para of input.paragraphs) {
|
|
91
|
-
cursorY += para.spcBefPx;
|
|
92
|
-
const wrapLeft = contentLeft + para.marLpx;
|
|
93
|
-
const wrapRight = contentRight - para.marRpx;
|
|
94
|
-
const firstLeft = wrapLeft + para.firstIndentPx;
|
|
95
|
-
const hasText = para.pieces.some((p) => !p.isBreak && p.text !== "");
|
|
96
|
-
const bullet = para.bullet && hasText ? para.bullet : null;
|
|
97
|
-
const bulletLead = bullet ? bullet.imageHref ? bullet.sizePx + mWidth(" ", bulletSpec(bullet)) : mWidth(`${bullet.text} `, bulletSpec(bullet)) : 0;
|
|
98
|
-
const avail = Math.max(1, wrapRight - wrapLeft);
|
|
99
|
-
const tokens = [];
|
|
100
|
-
for (const piece of para.pieces) {
|
|
101
|
-
if (piece.isBreak) {
|
|
102
|
-
tokens.push({ text: "", piece, isSpace: false, isBreak: true, width: 0 });
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
for (const seg of piece.text.match(/\s+|\S+/g) ?? []) {
|
|
106
|
-
const isSpace = /^\s+$/.test(seg);
|
|
107
|
-
const w = mWidth(seg, specOf(piece));
|
|
108
|
-
if (input.wrap && !isSpace && w > avail - bulletLead && [...seg].length > 1) {
|
|
109
|
-
for (const ch of seg) {
|
|
110
|
-
tokens.push({
|
|
111
|
-
text: ch,
|
|
112
|
-
piece,
|
|
113
|
-
isSpace: false,
|
|
114
|
-
isBreak: false,
|
|
115
|
-
width: mWidth(ch, specOf(piece))
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
tokens.push({ text: seg, piece, isSpace, isBreak: false, width: w });
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
const wrapped = wrapTokens(tokens, input.wrap, wrapRight - firstLeft - bulletLead, avail);
|
|
124
|
-
const paraLines = wrapped.length > 0 ? wrapped : [[]];
|
|
125
|
-
for (let li = 0; li < paraLines.length; li++) {
|
|
126
|
-
const toks = paraLines[li];
|
|
127
|
-
let ascent = 0;
|
|
128
|
-
let descent = 0;
|
|
129
|
-
let lineGap = 0;
|
|
130
|
-
for (const t of toks) {
|
|
131
|
-
if (t.isSpace || t.isBreak) continue;
|
|
132
|
-
const m = mMetrics(t.piece);
|
|
133
|
-
if (m.a > ascent) ascent = m.a;
|
|
134
|
-
if (m.d > descent) descent = m.d;
|
|
135
|
-
if (m.g > lineGap) lineGap = m.g;
|
|
136
|
-
}
|
|
137
|
-
if (ascent === 0) {
|
|
138
|
-
ascent = para.fallbackSizePx * FALLBACK_ASCENT;
|
|
139
|
-
descent = para.fallbackSizePx * FALLBACK_DESCENT;
|
|
140
|
-
lineGap = para.fallbackSizePx * FALLBACK_LINEGAP;
|
|
141
|
-
}
|
|
142
|
-
const isFirst = li === 0;
|
|
143
|
-
const lineLeft = bullet ? Math.max(wrapLeft, firstLeft + bulletLead) : (isFirst ? firstLeft : wrapLeft) + bulletLead;
|
|
144
|
-
const line = {
|
|
145
|
-
tokens: toks,
|
|
146
|
-
ascent,
|
|
147
|
-
descent,
|
|
148
|
-
lineGap,
|
|
149
|
-
topY: cursorY,
|
|
150
|
-
advance: 0,
|
|
151
|
-
anchorX: lineLeft,
|
|
152
|
-
textAnchor: "start",
|
|
153
|
-
bullet: null
|
|
154
|
-
};
|
|
155
|
-
if (para.align === "center") {
|
|
156
|
-
line.textAnchor = "middle";
|
|
157
|
-
line.anchorX = (lineLeft + wrapRight) / 2;
|
|
158
|
-
} else if (para.align === "right") {
|
|
159
|
-
line.textAnchor = "end";
|
|
160
|
-
line.anchorX = wrapRight;
|
|
161
|
-
}
|
|
162
|
-
if (isFirst && bullet) {
|
|
163
|
-
line.bullet = { x: firstLeft, baselineDy: 0, b: bullet };
|
|
164
|
-
}
|
|
165
|
-
line.advance = lineAdvance(line, para);
|
|
166
|
-
cursorY += line.advance;
|
|
167
|
-
lines.push(line);
|
|
168
|
-
}
|
|
169
|
-
cursorY += para.spcAftPx;
|
|
170
|
-
}
|
|
171
|
-
return { lines, blockH: cursorY };
|
|
172
|
-
};
|
|
173
|
-
const vert = input.vert ?? "none";
|
|
174
|
-
const cx = input.boxXpx + input.boxWpx / 2;
|
|
175
|
-
const cy = input.boxYpx + input.boxHpx / 2;
|
|
176
|
-
const frame = vert === "none" ? { x: input.boxXpx, y: input.boxYpx, w: input.boxWpx, h: input.boxHpx } : { x: cx - input.boxHpx / 2, y: cy - input.boxWpx / 2, w: input.boxHpx, h: input.boxWpx };
|
|
177
|
-
const columns = vert === "none" ? input.columns ?? null : null;
|
|
178
|
-
const placements = columns && columns.count >= 2 ? placeColumns(frame, columns, input.anchor, buildLines) : placeSingle(frame, input.anchor, buildLines);
|
|
179
|
-
const body = emitPlacements(placements);
|
|
180
|
-
if (vert === "none") return body;
|
|
181
|
-
const deg = vert === "cw90" ? 90 : 270;
|
|
182
|
-
return `<g transform="rotate(${deg} ${fmt(cx)} ${fmt(cy)})">${body}</g>`;
|
|
183
|
-
};
|
|
184
|
-
var anchorOffsetY = (frameY, frameH, blockH, anchor, firstLine) => {
|
|
185
|
-
let offsetY = frameY;
|
|
186
|
-
if (anchor === "center") offsetY = frameY + (frameH - blockH) / 2;
|
|
187
|
-
else if (anchor === "bottom") offsetY = frameY + (frameH - blockH);
|
|
188
|
-
if ((anchor === "center" || anchor === "bottom") && firstLine) {
|
|
189
|
-
offsetY += CENTER_ANCHOR_DROP * (firstLine.ascent + firstLine.descent);
|
|
190
|
-
}
|
|
191
|
-
return offsetY;
|
|
192
|
-
};
|
|
193
|
-
var placeSingle = (frame, anchor, buildLines) => {
|
|
194
|
-
const { lines, blockH } = buildLines(frame.x, frame.x + frame.w);
|
|
195
|
-
const offsetY = anchorOffsetY(frame.y, frame.h, blockH, anchor, lines[0]);
|
|
196
|
-
return lines.map((line) => ({
|
|
197
|
-
line,
|
|
198
|
-
baselineY: offsetY + line.topY + topPad(line) + line.ascent,
|
|
199
|
-
dx: 0
|
|
200
|
-
}));
|
|
201
|
-
};
|
|
202
|
-
var placeColumns = (frame, columns, anchor, buildLines) => {
|
|
203
|
-
const gap = columns.gapPx;
|
|
204
|
-
const colW = Math.max(1, (frame.w - (columns.count - 1) * gap) / columns.count);
|
|
205
|
-
const { lines } = buildLines(frame.x, frame.x + colW);
|
|
206
|
-
let col = 0;
|
|
207
|
-
let colStartTopY = 0;
|
|
208
|
-
const colHasLine = [];
|
|
209
|
-
let tallest = 0;
|
|
210
|
-
const placed = [];
|
|
211
|
-
for (const line of lines) {
|
|
212
|
-
const localBottom = line.topY - colStartTopY + line.advance;
|
|
213
|
-
if (col < columns.count - 1 && localBottom > frame.h && colHasLine[col]) {
|
|
214
|
-
col += 1;
|
|
215
|
-
colStartTopY = line.topY;
|
|
216
|
-
}
|
|
217
|
-
const localTopY = line.topY - colStartTopY;
|
|
218
|
-
placed.push({ line, localTopY, col });
|
|
219
|
-
colHasLine[col] = true;
|
|
220
|
-
if (localTopY + line.advance > tallest) tallest = localTopY + line.advance;
|
|
221
|
-
}
|
|
222
|
-
const offsetY = anchorOffsetY(frame.y, frame.h, tallest, anchor, lines[0]);
|
|
223
|
-
return placed.map(({ line, localTopY, col: c }) => ({
|
|
224
|
-
line,
|
|
225
|
-
baselineY: offsetY + localTopY + topPad(line) + line.ascent,
|
|
226
|
-
dx: c * (colW + gap)
|
|
227
|
-
}));
|
|
228
|
-
};
|
|
229
|
-
var emitPlacements = (placements) => {
|
|
230
|
-
const parts = [];
|
|
231
|
-
for (const { line, baselineY, dx } of placements) {
|
|
232
|
-
if (line.bullet) {
|
|
233
|
-
const b = line.bullet.b;
|
|
234
|
-
if (b.imageHref) {
|
|
235
|
-
parts.push(
|
|
236
|
-
`<image x="${fmt(line.bullet.x + dx + GRID_NUDGE_X)}" y="${fmt(baselineY - b.sizePx)}" width="${fmt(b.sizePx)}" height="${fmt(b.sizePx)}" href="${b.imageHref}" xlink:href="${b.imageHref}" preserveAspectRatio="xMidYMid meet"/>`
|
|
237
|
-
);
|
|
238
|
-
} else {
|
|
239
|
-
parts.push(
|
|
240
|
-
`<text x="${fmt(line.bullet.x + dx + GRID_NUDGE_X)}" y="${fmt(baselineY)}" font-family="${escapeXml(b.family)}" font-size="${fmt(b.sizePx)}" fill="${b.fillHex}" xml:space="preserve">${escapeXml(b.text)}</text>`
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
parts.push(emitLine(line, baselineY, dx));
|
|
245
|
-
}
|
|
246
|
-
return parts.join("");
|
|
247
|
-
};
|
|
248
|
-
var topPad = (line) => {
|
|
249
|
-
const lead = line.advance - (line.ascent + line.descent);
|
|
250
|
-
return lead > 0 ? lead / 2 : 0;
|
|
251
|
-
};
|
|
252
|
-
var lineAdvance = (line, para) => {
|
|
253
|
-
const natural = line.ascent + line.descent + line.lineGap;
|
|
254
|
-
let adv;
|
|
255
|
-
if (para.lineSpacing?.kind === "pct") adv = para.lineSpacing.value * natural;
|
|
256
|
-
else if (para.lineSpacing?.kind === "pts") adv = para.lineSpacing.px;
|
|
257
|
-
else adv = natural;
|
|
258
|
-
return adv * para.lineAdvanceScale;
|
|
259
|
-
};
|
|
260
|
-
var emitLine = (line, baselineY, dx) => {
|
|
261
|
-
const toks = [...line.tokens];
|
|
262
|
-
while (toks.length > 0 && (toks[toks.length - 1].isSpace || toks[toks.length - 1].isBreak)) {
|
|
263
|
-
toks.pop();
|
|
264
|
-
}
|
|
265
|
-
const content = toks.filter((t) => !t.isBreak);
|
|
266
|
-
if (content.length === 0) return "";
|
|
267
|
-
const tspans = groupTokens(content).map((g) => tspan(g)).join("");
|
|
268
|
-
if (tspans === "") return "";
|
|
269
|
-
return `<text x="${fmt(line.anchorX + dx + GRID_NUDGE_X)}" y="${fmt(baselineY)}" text-anchor="${line.textAnchor}" xml:space="preserve">${tspans}</text>`;
|
|
270
|
-
};
|
|
271
|
-
var groupTokens = (toks) => {
|
|
272
|
-
const groups = [];
|
|
273
|
-
for (const t of toks) {
|
|
274
|
-
if (t.isBreak) continue;
|
|
275
|
-
const last = groups[groups.length - 1];
|
|
276
|
-
if (last && samePiece(last.piece, t.piece)) last.text += t.text;
|
|
277
|
-
else groups.push({ text: t.text, piece: t.piece });
|
|
278
|
-
}
|
|
279
|
-
return groups;
|
|
280
|
-
};
|
|
281
|
-
var samePiece = (a, b) => a.family === b.family && a.sizePx === b.sizePx && a.bold === b.bold && a.italic === b.italic && a.letterSpacingPx === b.letterSpacingPx && a.fillHex === b.fillHex && a.underline === b.underline && a.strike === b.strike && a.superSub === b.superSub && a.href === b.href;
|
|
282
|
-
var tspan = (g) => {
|
|
283
|
-
const p = g.piece;
|
|
284
|
-
const sizePx = p.superSub !== 0 ? p.sizePx * 0.65 : p.sizePx;
|
|
285
|
-
const attrs = [
|
|
286
|
-
`font-family="${escapeXml(p.family)}"`,
|
|
287
|
-
`font-size="${fmt(sizePx)}"`,
|
|
288
|
-
`fill="${p.fillHex}"`
|
|
289
|
-
];
|
|
290
|
-
if (p.bold) attrs.push('font-weight="700"');
|
|
291
|
-
if (p.italic) attrs.push('font-style="italic"');
|
|
292
|
-
const deco = [];
|
|
293
|
-
if (p.underline) deco.push("underline");
|
|
294
|
-
if (p.strike) deco.push("line-through");
|
|
295
|
-
if (deco.length) attrs.push(`text-decoration="${deco.join(" ")}"`);
|
|
296
|
-
if (p.letterSpacingPx !== 0) attrs.push(`letter-spacing="${fmt(p.letterSpacingPx)}"`);
|
|
297
|
-
if (p.superSub === 1) attrs.push(`baseline-shift="${fmt(p.sizePx * 0.33)}"`);
|
|
298
|
-
else if (p.superSub === -1) attrs.push(`baseline-shift="${fmt(-p.sizePx * 0.16)}"`);
|
|
299
|
-
return `<tspan ${attrs.join(" ")}>${escapeXml(g.text)}</tspan>`;
|
|
300
|
-
};
|
|
301
|
-
var wrapTokens = (tokens, wrap, firstAvail, avail) => {
|
|
302
|
-
const lines = [];
|
|
303
|
-
let cur = [];
|
|
304
|
-
let lineW = 0;
|
|
305
|
-
let trailingSpaceW = 0;
|
|
306
|
-
let first = true;
|
|
307
|
-
const trimTrailing = () => {
|
|
308
|
-
while (cur.length > 0 && (cur[cur.length - 1].isSpace || cur[cur.length - 1].isBreak)) {
|
|
309
|
-
cur.pop();
|
|
310
|
-
}
|
|
311
|
-
};
|
|
312
|
-
const close = () => {
|
|
313
|
-
trimTrailing();
|
|
314
|
-
lines.push(cur);
|
|
315
|
-
cur = [];
|
|
316
|
-
lineW = 0;
|
|
317
|
-
trailingSpaceW = 0;
|
|
318
|
-
first = false;
|
|
319
|
-
};
|
|
320
|
-
for (const tok of tokens) {
|
|
321
|
-
if (tok.isBreak) {
|
|
322
|
-
cur.push(tok);
|
|
323
|
-
close();
|
|
324
|
-
continue;
|
|
325
|
-
}
|
|
326
|
-
if (tok.isSpace) {
|
|
327
|
-
cur.push(tok);
|
|
328
|
-
lineW += tok.width;
|
|
329
|
-
trailingSpaceW += tok.width;
|
|
330
|
-
continue;
|
|
331
|
-
}
|
|
332
|
-
const limit = first ? firstAvail : avail;
|
|
333
|
-
const contentW = lineW - trailingSpaceW;
|
|
334
|
-
const hasContent = contentW > 0;
|
|
335
|
-
if (wrap && hasContent && contentW + tok.width > limit + 0.5) {
|
|
336
|
-
close();
|
|
337
|
-
cur.push(tok);
|
|
338
|
-
lineW = tok.width;
|
|
339
|
-
} else {
|
|
340
|
-
cur.push(tok);
|
|
341
|
-
lineW += tok.width;
|
|
342
|
-
}
|
|
343
|
-
trailingSpaceW = 0;
|
|
344
|
-
}
|
|
345
|
-
trimTrailing();
|
|
346
|
-
if (cur.length > 0) lines.push(cur);
|
|
347
|
-
return lines;
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
// src/render-slide.ts
|
|
351
|
-
var DEFAULT_SIZE = { width: 12192e3, height: 6858e3 };
|
|
352
|
-
var EMU_PER_PX = 9525;
|
|
353
|
-
var PX_PER_PT = 96 / 72;
|
|
354
|
-
var DEFAULT_BODY_PT = 18;
|
|
355
|
-
var DEFAULT_TITLE_PT = 44;
|
|
356
|
-
var DEFAULT_FONT = "Calibri, 'Helvetica Neue', Arial, sans-serif";
|
|
357
|
-
var DEFAULT_INSET_X = 91440;
|
|
358
|
-
var DEFAULT_INSET_Y = 45720;
|
|
359
|
-
var u8ToBase64 = (data) => {
|
|
360
|
-
let s = "";
|
|
361
|
-
const chunk = 32768;
|
|
362
|
-
for (let i = 0; i < data.length; i += chunk) {
|
|
363
|
-
s += String.fromCharCode(...data.subarray(i, i + chunk));
|
|
364
|
-
}
|
|
365
|
-
return btoa(s);
|
|
366
|
-
};
|
|
367
|
-
var imageMime = {
|
|
368
|
-
png: "image/png",
|
|
369
|
-
jpeg: "image/jpeg",
|
|
370
|
-
gif: "image/gif",
|
|
371
|
-
bmp: "image/bmp",
|
|
372
|
-
tiff: "image/tiff",
|
|
373
|
-
webp: "image/webp",
|
|
374
|
-
svg: "image/svg+xml"
|
|
375
|
-
};
|
|
376
|
-
var bytesToDataUrl = (bytes) => {
|
|
377
|
-
const fmt2 = detectImageFormatLocal(bytes);
|
|
378
|
-
const mime = fmt2 ? imageMime[fmt2] ?? "image/png" : "image/png";
|
|
379
|
-
return `data:${mime};base64,${u8ToBase64(bytes)}`;
|
|
380
|
-
};
|
|
381
|
-
var EXT_TO_MIME = {
|
|
382
|
-
png: "image/png",
|
|
383
|
-
jpg: "image/jpeg",
|
|
384
|
-
jpeg: "image/jpeg",
|
|
385
|
-
gif: "image/gif",
|
|
386
|
-
bmp: "image/bmp",
|
|
387
|
-
tif: "image/tiff",
|
|
388
|
-
tiff: "image/tiff",
|
|
389
|
-
webp: "image/webp",
|
|
390
|
-
svg: "image/svg+xml",
|
|
391
|
-
avif: "image/avif",
|
|
392
|
-
heic: "image/heic",
|
|
393
|
-
heif: "image/heif"
|
|
394
|
-
};
|
|
395
|
-
var mimeFromPartName = (name) => {
|
|
396
|
-
if (!name) return null;
|
|
397
|
-
const dot = name.lastIndexOf(".");
|
|
398
|
-
if (dot < 0) return null;
|
|
399
|
-
const ext = name.slice(dot + 1).toLowerCase();
|
|
400
|
-
return EXT_TO_MIME[ext] ?? null;
|
|
401
|
-
};
|
|
402
|
-
var renderPicture = (shape, pres, x, y, w, h, transform, textOverlay, bytes, format) => {
|
|
403
|
-
let mime = null;
|
|
404
|
-
if (bytes && format) {
|
|
405
|
-
mime = imageMime[format] ?? null;
|
|
406
|
-
}
|
|
407
|
-
if (bytes && !mime) {
|
|
408
|
-
mime = mimeFromPartName(getShapeImagePartName(shape));
|
|
409
|
-
}
|
|
410
|
-
if (bytes && mime) {
|
|
411
|
-
const dataUrl = `data:${mime};base64,${u8ToBase64(bytes)}`;
|
|
412
|
-
const crop = getShapeImageCrop(shape);
|
|
413
|
-
let imgX = x, imgY = y, imgW = w, imgH = h;
|
|
414
|
-
let clipDef = "";
|
|
415
|
-
let clipAttr = "";
|
|
416
|
-
const cropL = crop?.left ?? 0;
|
|
417
|
-
const cropT = crop?.top ?? 0;
|
|
418
|
-
const cropR = crop?.right ?? 0;
|
|
419
|
-
const cropB = crop?.bottom ?? 0;
|
|
420
|
-
if (cropL > 0 || cropT > 0 || cropR > 0 || cropB > 0) {
|
|
421
|
-
const scaleX = 1 / Math.max(1e-3, 1 - cropL - cropR);
|
|
422
|
-
const scaleY = 1 / Math.max(1e-3, 1 - cropT - cropB);
|
|
423
|
-
imgW = w * scaleX;
|
|
424
|
-
imgH = h * scaleY;
|
|
425
|
-
imgX = x - imgW * cropL;
|
|
426
|
-
imgY = y - imgH * cropT;
|
|
427
|
-
const clipId = mintId();
|
|
428
|
-
clipDef = `<defs><clipPath id="${clipId}"><rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}"/></clipPath></defs>`;
|
|
429
|
-
clipAttr = ` clip-path="url(#${clipId})"`;
|
|
430
|
-
}
|
|
431
|
-
const brightness = getShapeImageBrightness(shape) ?? 0;
|
|
432
|
-
const contrast = getShapeImageContrast(shape) ?? 1;
|
|
433
|
-
const opacity = getShapeImageOpacity(shape) ?? 1;
|
|
434
|
-
const grayscale = isShapeImageGrayscale(shape);
|
|
435
|
-
const biLevel = getShapeImageBiLevelThreshold(shape);
|
|
436
|
-
const duotone = getShapeImageDuotone(pres, shape);
|
|
437
|
-
let filterAttr = "";
|
|
438
|
-
if (brightness !== 0 || contrast !== 1 || grayscale || biLevel !== null || duotone && (duotone.firstColor || duotone.secondColor)) {
|
|
439
|
-
const fid = mintId();
|
|
440
|
-
const prims = [];
|
|
441
|
-
if (brightness !== 0 || contrast !== 1) {
|
|
442
|
-
prims.push(
|
|
443
|
-
`<feComponentTransfer><feFuncR type="linear" slope="${contrast}" intercept="${brightness}"/><feFuncG type="linear" slope="${contrast}" intercept="${brightness}"/><feFuncB type="linear" slope="${contrast}" intercept="${brightness}"/></feComponentTransfer>`
|
|
444
|
-
);
|
|
445
|
-
}
|
|
446
|
-
if (grayscale) {
|
|
447
|
-
prims.push(
|
|
448
|
-
`<feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/>`
|
|
449
|
-
);
|
|
450
|
-
}
|
|
451
|
-
if (duotone && duotone.firstColor && duotone.secondColor) {
|
|
452
|
-
const [r1, g1, b1] = hexChannels(duotone.firstColor);
|
|
453
|
-
const [r2, g2, b2] = hexChannels(duotone.secondColor);
|
|
454
|
-
const steps = 16;
|
|
455
|
-
const tR = [];
|
|
456
|
-
const tG = [];
|
|
457
|
-
const tB = [];
|
|
458
|
-
for (let i = 0; i < steps; i++) {
|
|
459
|
-
const t = i / (steps - 1);
|
|
460
|
-
tR.push(((r1 + (r2 - r1) * t) / 255).toFixed(4));
|
|
461
|
-
tG.push(((g1 + (g2 - g1) * t) / 255).toFixed(4));
|
|
462
|
-
tB.push(((b1 + (b2 - b1) * t) / 255).toFixed(4));
|
|
463
|
-
}
|
|
464
|
-
prims.push(
|
|
465
|
-
`<feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/>`,
|
|
466
|
-
`<feComponentTransfer><feFuncR type="table" tableValues="${tR.join(" ")}"/><feFuncG type="table" tableValues="${tG.join(" ")}"/><feFuncB type="table" tableValues="${tB.join(" ")}"/></feComponentTransfer>`
|
|
467
|
-
);
|
|
468
|
-
}
|
|
469
|
-
if (biLevel !== null) {
|
|
470
|
-
const t = biLevel / 100;
|
|
471
|
-
const steps = 32;
|
|
472
|
-
const vals = [];
|
|
473
|
-
for (let i = 0; i < steps; i++) {
|
|
474
|
-
vals.push(i / (steps - 1) >= t ? "1" : "0");
|
|
475
|
-
}
|
|
476
|
-
const tableStr = vals.join(" ");
|
|
477
|
-
prims.push(
|
|
478
|
-
`<feComponentTransfer><feFuncR type="discrete" tableValues="${tableStr}"/><feFuncG type="discrete" tableValues="${tableStr}"/><feFuncB type="discrete" tableValues="${tableStr}"/></feComponentTransfer>`
|
|
479
|
-
);
|
|
480
|
-
}
|
|
481
|
-
clipDef += `<defs><filter id="${fid}">${prims.join("")}</filter></defs>`;
|
|
482
|
-
filterAttr = ` filter="url(#${fid})"`;
|
|
483
|
-
}
|
|
484
|
-
const opacityAttr = opacity !== 1 ? ` opacity="${opacity.toFixed(3)}"` : "";
|
|
485
|
-
return `${clipDef}<g${transform}${clipAttr}><image x="${E(imgX)}" y="${E(imgY)}" width="${E(imgW)}" height="${E(imgH)}" href="${dataUrl}" xlink:href="${dataUrl}" preserveAspectRatio="none"${filterAttr}${opacityAttr}/></g><g${transform}>${textOverlay}</g>`;
|
|
486
|
-
}
|
|
487
|
-
const linkUrl = getShapeImageLinkUrl(shape);
|
|
488
|
-
const label = !bytes ? linkUrl ? `picture (link: ${linkUrl.length > 48 ? linkUrl.slice(0, 45) + "\u2026" : linkUrl})` : "picture (no bytes)" : `picture (${format ?? "unknown"}${bytes ? `, ${bytes.byteLength} B` : ""})`;
|
|
489
|
-
return `<g data-pptx-fallback="image"${transform}><rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}" fill="#F3F4F6" stroke="#9CA3AF" stroke-width="${E(9525)}" stroke-dasharray="${E(5e4)},${E(3e4)}"/>${renderPicturePlaceholderLabel(x, y, w, h, label)}${textOverlay}</g>`;
|
|
490
|
-
};
|
|
491
|
-
var renderPicturePlaceholderLabel = (x, y, w, h, text) => {
|
|
492
|
-
const cx = x + w / 2;
|
|
493
|
-
const cy = y + h / 2;
|
|
494
|
-
const title = `<title>${escapeXml2(text)}</title>`;
|
|
495
|
-
const label = `<text x="${E(cx)}" y="${E(cy)}" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-weight="600" font-size="${(13 * PX_PER_PT).toFixed(2)}" fill="#374151">${escapeXml2(text)}</text>`;
|
|
496
|
-
return `${title}${label}`;
|
|
497
|
-
};
|
|
498
|
-
var escapeXml2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
499
|
-
var SCHEME_TO_THEME = {
|
|
500
|
-
tx1: "dark1",
|
|
501
|
-
bg1: "light1",
|
|
502
|
-
tx2: "dark2",
|
|
503
|
-
bg2: "light2",
|
|
504
|
-
dk1: "dark1",
|
|
505
|
-
lt1: "light1",
|
|
506
|
-
dk2: "dark2",
|
|
507
|
-
lt2: "light2",
|
|
508
|
-
accent1: "accent1",
|
|
509
|
-
accent2: "accent2",
|
|
510
|
-
accent3: "accent3",
|
|
511
|
-
accent4: "accent4",
|
|
512
|
-
accent5: "accent5",
|
|
513
|
-
accent6: "accent6",
|
|
514
|
-
hlink: "hyperlink",
|
|
515
|
-
folHlink: "followedHyperlink"
|
|
516
|
-
};
|
|
517
|
-
var hexChannels = (hex) => {
|
|
518
|
-
const h = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
519
|
-
return [
|
|
520
|
-
Number.parseInt(h.slice(0, 2), 16) || 0,
|
|
521
|
-
Number.parseInt(h.slice(2, 4), 16) || 0,
|
|
522
|
-
Number.parseInt(h.slice(4, 6), 16) || 0
|
|
523
|
-
];
|
|
524
|
-
};
|
|
525
|
-
var mixHex = (aHex, bHex, t) => {
|
|
526
|
-
const aa = aHex.startsWith("#") ? aHex.slice(1) : aHex;
|
|
527
|
-
const bb = bHex.startsWith("#") ? bHex.slice(1) : bHex;
|
|
528
|
-
const part = (h2, off) => Number.parseInt(h2.slice(off, off + 2), 16);
|
|
529
|
-
const r = Math.round(part(aa, 0) * t + part(bb, 0) * (1 - t));
|
|
530
|
-
const g = Math.round(part(aa, 2) * t + part(bb, 2) * (1 - t));
|
|
531
|
-
const b = Math.round(part(aa, 4) * t + part(bb, 4) * (1 - t));
|
|
532
|
-
const h = (n) => Math.max(0, Math.min(255, n)).toString(16).padStart(2, "0").toUpperCase();
|
|
533
|
-
return `#${h(r)}${h(g)}${h(b)}`;
|
|
534
|
-
};
|
|
535
|
-
var normalizeHex = (s) => {
|
|
536
|
-
if (s.startsWith("#")) return s;
|
|
537
|
-
if (/^[0-9A-Fa-f]{6}$/.test(s)) return `#${s}`;
|
|
538
|
-
if (/^[0-9A-Fa-f]{8}$/.test(s)) return `#${s.slice(2)}`;
|
|
539
|
-
return s;
|
|
540
|
-
};
|
|
541
|
-
var resolveColor = (c, theme, fallback = "#1F2937") => {
|
|
542
|
-
if (!c) return fallback;
|
|
543
|
-
let token = null;
|
|
544
|
-
if (c.startsWith("scheme:")) token = c.slice("scheme:".length);
|
|
545
|
-
else if (SCHEME_TO_THEME[c]) token = c;
|
|
546
|
-
if (token !== null) {
|
|
547
|
-
if (theme) {
|
|
548
|
-
const mapped = activeColorMap?.[token] ?? token;
|
|
549
|
-
const key = SCHEME_TO_THEME[mapped] ?? SCHEME_TO_THEME[token];
|
|
550
|
-
if (key) return normalizeHex(theme[key]);
|
|
551
|
-
}
|
|
552
|
-
if (token === "tx1" || token === "dk1") return "#000000";
|
|
553
|
-
if (token === "bg1" || token === "lt1") return "#FFFFFF";
|
|
554
|
-
if (token === "tx2" || token === "dk2") return "#1F2937";
|
|
555
|
-
if (token === "bg2" || token === "lt2") return "#E5E7EB";
|
|
556
|
-
return fallback;
|
|
557
|
-
}
|
|
558
|
-
return normalizeHex(c);
|
|
559
|
-
};
|
|
560
|
-
var nextDefId = 0;
|
|
561
|
-
var mintId = () => `pkdef-${(nextDefId++).toString(36)}`;
|
|
562
|
-
var activeColorMap = null;
|
|
563
|
-
var activeDeckTextColor = "#000000";
|
|
564
|
-
var gradientDef = (grad, theme) => {
|
|
565
|
-
const id = mintId();
|
|
566
|
-
const stops = grad.stops.map(
|
|
567
|
-
(s) => `<stop offset="${s.offset.toFixed(4)}" stop-color="${resolveColor(s.color, theme, "#E5E7EB")}"/>`
|
|
568
|
-
).join("");
|
|
569
|
-
if (grad.path === "circle" || grad.path === "rect" || grad.path === "shape") {
|
|
570
|
-
const focus = grad.focus ?? { left: 0.5, top: 0.5, right: 0.5, bottom: 0.5 };
|
|
571
|
-
const cx = (focus.left + focus.right) / 2;
|
|
572
|
-
const cy = (focus.top + focus.bottom) / 2;
|
|
573
|
-
const reversed = grad.stops.slice().reverse().map(
|
|
574
|
-
(s) => `<stop offset="${(1 - s.offset).toFixed(4)}" stop-color="${resolveColor(s.color, theme, "#E5E7EB")}"/>`
|
|
575
|
-
).join("");
|
|
576
|
-
const defs2 = `<defs><radialGradient id="${id}" gradientUnits="objectBoundingBox" cx="${cx.toFixed(4)}" cy="${cy.toFixed(4)}" r="${Math.max(0.5, Math.max(cx, cy, 1 - cx, 1 - cy)).toFixed(4)}">${reversed}</radialGradient></defs>`;
|
|
577
|
-
return { defs: defs2, fillAttr: `url(#${id})` };
|
|
578
|
-
}
|
|
579
|
-
const angleRad = (grad.angleDeg ?? 0) * Math.PI / 180;
|
|
580
|
-
const dx = Math.cos(angleRad) / 2;
|
|
581
|
-
const dy = Math.sin(angleRad) / 2;
|
|
582
|
-
const x1 = 0.5 - dx;
|
|
583
|
-
const y1 = 0.5 - dy;
|
|
584
|
-
const x2 = 0.5 + dx;
|
|
585
|
-
const y2 = 0.5 + dy;
|
|
586
|
-
const defs = `<defs><linearGradient id="${id}" gradientUnits="objectBoundingBox" x1="${x1.toFixed(4)}" y1="${y1.toFixed(4)}" x2="${x2.toFixed(4)}" y2="${y2.toFixed(4)}">${stops}</linearGradient></defs>`;
|
|
587
|
-
return { defs, fillAttr: `url(#${id})` };
|
|
588
|
-
};
|
|
589
|
-
var patternDef = (pat) => {
|
|
590
|
-
const id = mintId();
|
|
591
|
-
const fg = pat.foreground;
|
|
592
|
-
const bg = pat.background;
|
|
593
|
-
const preset = pat.preset;
|
|
594
|
-
let body = "";
|
|
595
|
-
const W = 8;
|
|
596
|
-
const H = 8;
|
|
597
|
-
const stripe = (orientation, width = 1) => {
|
|
598
|
-
if (orientation === "h")
|
|
599
|
-
return `<path d="M0 ${H / 2}H${W}" stroke="${fg}" stroke-width="${width}"/>`;
|
|
600
|
-
if (orientation === "v")
|
|
601
|
-
return `<path d="M${W / 2} 0V${H}" stroke="${fg}" stroke-width="${width}"/>`;
|
|
602
|
-
if (orientation === "d")
|
|
603
|
-
return `<path d="M0 0L${W} ${H}" stroke="${fg}" stroke-width="${width}"/>`;
|
|
604
|
-
return `<path d="M${W} 0L0 ${H}" stroke="${fg}" stroke-width="${width}"/>`;
|
|
605
|
-
};
|
|
606
|
-
const dots = (density) => {
|
|
607
|
-
const count = Math.max(1, Math.round(density * 4));
|
|
608
|
-
const out = [];
|
|
609
|
-
const grid = count <= 1 ? [[4, 4]] : count === 2 ? [
|
|
610
|
-
[2, 2],
|
|
611
|
-
[6, 6]
|
|
612
|
-
] : [
|
|
613
|
-
[2, 2],
|
|
614
|
-
[6, 2],
|
|
615
|
-
[2, 6],
|
|
616
|
-
[6, 6]
|
|
617
|
-
];
|
|
618
|
-
for (const [x, y] of grid.slice(0, count)) {
|
|
619
|
-
out.push(`<circle cx="${x}" cy="${y}" r="0.7" fill="${fg}"/>`);
|
|
620
|
-
}
|
|
621
|
-
return out.join("");
|
|
622
|
-
};
|
|
623
|
-
const pctMatch = /^pct(\d+)$/.exec(preset);
|
|
624
|
-
if (pctMatch) {
|
|
625
|
-
const pct = Math.min(100, Math.max(5, Number.parseInt(pctMatch[1], 10)));
|
|
626
|
-
body = dots(pct / 100);
|
|
627
|
-
} else if (preset === "horzBrick" || preset === "ltHorizontal" || preset === "narHorz") {
|
|
628
|
-
body = stripe("h", 0.8);
|
|
629
|
-
} else if (preset === "dkHorizontal") {
|
|
630
|
-
body = stripe("h", 2);
|
|
631
|
-
} else if (preset === "ltVertical" || preset === "narVert") {
|
|
632
|
-
body = stripe("v", 0.8);
|
|
633
|
-
} else if (preset === "dkVertical") {
|
|
634
|
-
body = stripe("v", 2);
|
|
635
|
-
} else if (preset === "ltUpDiag" || preset === "wdUpDiag") {
|
|
636
|
-
body = stripe("d", 0.8);
|
|
637
|
-
} else if (preset === "dkUpDiag") {
|
|
638
|
-
body = stripe("d", 2);
|
|
639
|
-
} else if (preset === "ltDnDiag" || preset === "wdDnDiag") {
|
|
640
|
-
body = stripe("a", 0.8);
|
|
641
|
-
} else if (preset === "dkDnDiag") {
|
|
642
|
-
body = stripe("a", 2);
|
|
643
|
-
} else if (preset === "ltHorzCross" || preset === "smGrid" || preset === "cross") {
|
|
644
|
-
body = stripe("h", 0.8) + stripe("v", 0.8);
|
|
645
|
-
} else if (preset === "dkHorzCross" || preset === "lgGrid" || preset === "plaid") {
|
|
646
|
-
body = stripe("h", 2) + stripe("v", 2);
|
|
647
|
-
} else if (preset === "diagCross" || preset === "trellis" || preset === "shingle" || preset === "dashUpDiag" || preset === "dashDnDiag") {
|
|
648
|
-
body = stripe("d", 0.8) + stripe("a", 0.8);
|
|
649
|
-
} else if (preset === "dkUpDiagStripe" || preset === "dkDnDiagStripe") {
|
|
650
|
-
body = stripe(preset === "dkUpDiagStripe" ? "d" : "a", 2);
|
|
651
|
-
} else if (preset === "wave" || preset === "zigZag") {
|
|
652
|
-
body = `<path d="M0 4Q2 2 4 4T8 4" stroke="${fg}" stroke-width="0.8" fill="none"/>`;
|
|
653
|
-
} else if (preset === "weave" || preset === "divot") {
|
|
654
|
-
body = `<path d="M0 0L4 4 0 8M4 0L8 4 4 8" stroke="${fg}" stroke-width="0.8" fill="none"/>`;
|
|
655
|
-
} else if (preset === "sphere") {
|
|
656
|
-
body = `<circle cx="4" cy="4" r="3" fill="${fg}" fill-opacity="0.7"/>`;
|
|
657
|
-
} else if (preset === "solidDmnd" || preset === "openDmnd") {
|
|
658
|
-
body = `<path d="M4 1L7 4 4 7 1 4Z" fill="${preset === "solidDmnd" ? fg : "none"}" stroke="${fg}" stroke-width="0.6"/>`;
|
|
659
|
-
} else {
|
|
660
|
-
body = dots(0.5);
|
|
661
|
-
}
|
|
662
|
-
const defs = `<defs><pattern id="${id}" patternUnits="userSpaceOnUse" width="${W}" height="${H}"><rect width="${W}" height="${H}" fill="${bg}"/>${body}</pattern></defs>`;
|
|
663
|
-
return { defs, fillAttr: `url(#${id})` };
|
|
664
|
-
};
|
|
665
|
-
var DASH_PATTERNS = {
|
|
666
|
-
solid: "",
|
|
667
|
-
dot: "1 3",
|
|
668
|
-
dash: "4 3",
|
|
669
|
-
lgDash: "8 3",
|
|
670
|
-
dashDot: "4 3 1 3",
|
|
671
|
-
lgDashDot: "8 3 1 3",
|
|
672
|
-
lgDashDotDot: "8 3 1 3 1 3",
|
|
673
|
-
sysDash: "3 1",
|
|
674
|
-
sysDot: "1 1",
|
|
675
|
-
sysDashDot: "3 1 1 1",
|
|
676
|
-
sysDashDotDot: "3 1 1 1 1 1"
|
|
677
|
-
};
|
|
678
|
-
var arrowSize = (size) => size === "sm" ? 3 : size === "lg" ? 7 : 5;
|
|
679
|
-
var buildArrowMarker = (type, width, length, color, orient) => {
|
|
680
|
-
const id = mintId();
|
|
681
|
-
const w = arrowSize(width);
|
|
682
|
-
const h = arrowSize(length);
|
|
683
|
-
let body;
|
|
684
|
-
switch (type) {
|
|
685
|
-
case "triangle":
|
|
686
|
-
case "arrow":
|
|
687
|
-
body = `<path d="M0 0L${w} ${h / 2}L0 ${h}z" fill="${color}"/>`;
|
|
688
|
-
break;
|
|
689
|
-
case "stealth":
|
|
690
|
-
body = `<path d="M0 0L${w} ${h / 2}L0 ${h}L${w * 0.4} ${h / 2}z" fill="${color}"/>`;
|
|
691
|
-
break;
|
|
692
|
-
case "diamond":
|
|
693
|
-
body = `<path d="M0 ${h / 2}L${w / 2} 0L${w} ${h / 2}L${w / 2} ${h}z" fill="${color}"/>`;
|
|
694
|
-
break;
|
|
695
|
-
case "oval":
|
|
696
|
-
body = `<ellipse cx="${w / 2}" cy="${h / 2}" rx="${w / 2}" ry="${h / 2}" fill="${color}"/>`;
|
|
697
|
-
break;
|
|
698
|
-
case "none":
|
|
699
|
-
default:
|
|
700
|
-
body = "";
|
|
701
|
-
}
|
|
702
|
-
const def = `<defs><marker id="${id}" viewBox="0 0 ${w} ${h}" refX="${w}" refY="${h / 2}" markerWidth="${w}" markerHeight="${h}" orient="${orient}">${body}</marker></defs>`;
|
|
703
|
-
return { id, def };
|
|
704
|
-
};
|
|
705
|
-
var paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
|
|
706
|
-
let fillColor;
|
|
707
|
-
let defs = "";
|
|
708
|
-
if (fill.kind === "solid") {
|
|
709
|
-
let resolved = null;
|
|
710
|
-
if (shape && pres) resolved = getShapeFillColorResolved(pres, shape);
|
|
711
|
-
fillColor = resolved ?? resolveColor(fill.color, theme, "#E5E7EB");
|
|
712
|
-
} else if (fill.kind === "none") {
|
|
713
|
-
fillColor = "none";
|
|
714
|
-
} else if (fill.kind === "gradient") {
|
|
715
|
-
const grad = shape ? pres ? getShapeGradientFillEffective(pres, shape) : getShapeGradientFill(shape) : null;
|
|
716
|
-
if (grad) {
|
|
717
|
-
const built = gradientDef(grad, theme);
|
|
718
|
-
defs = built.defs;
|
|
719
|
-
fillColor = built.fillAttr;
|
|
720
|
-
} else {
|
|
721
|
-
fillColor = (shape && pres ? getShapeFillColorResolved(pres, shape) : null) ?? "none";
|
|
722
|
-
}
|
|
723
|
-
} else if (fill.kind === "pattern") {
|
|
724
|
-
const pat = shape && pres ? getShapePatternFill(pres, shape) : null;
|
|
725
|
-
if (pat) {
|
|
726
|
-
const built = patternDef(pat);
|
|
727
|
-
defs = built.defs;
|
|
728
|
-
fillColor = built.fillAttr;
|
|
729
|
-
} else {
|
|
730
|
-
fillColor = "#BFDBFE";
|
|
731
|
-
}
|
|
732
|
-
} else if (fill.kind === "image") {
|
|
733
|
-
fillColor = "#DDD6FE";
|
|
734
|
-
} else {
|
|
735
|
-
fillColor = "none";
|
|
736
|
-
}
|
|
737
|
-
let strokeColor = "none";
|
|
738
|
-
let strokeWidth = 0;
|
|
739
|
-
const strokeAttrParts = [];
|
|
740
|
-
let markerAttrs = "";
|
|
741
|
-
if (stroke.kind === "solid") {
|
|
742
|
-
let resolved = null;
|
|
743
|
-
if (shape && pres) resolved = getShapeStrokeColorResolved(pres, shape);
|
|
744
|
-
strokeColor = resolved ?? resolveColor(stroke.color, theme, "#9CA3AF");
|
|
745
|
-
strokeWidth = stroke.widthEmu ?? 9525;
|
|
746
|
-
if (shape) {
|
|
747
|
-
const dash = getShapeStrokeDash(shape);
|
|
748
|
-
if (dash && dash !== "solid") {
|
|
749
|
-
const pattern = DASH_PATTERNS[dash];
|
|
750
|
-
if (pattern) {
|
|
751
|
-
const swPx = strokeWidth / EMU_PER_PX;
|
|
752
|
-
const arr = pattern.split(" ").map((n) => (Number.parseFloat(n) * swPx).toFixed(2)).join(" ");
|
|
753
|
-
strokeAttrParts.push(`stroke-dasharray="${arr}"`);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
const cap = getShapeStrokeCap(shape);
|
|
757
|
-
if (cap === "rnd") strokeAttrParts.push('stroke-linecap="round"');
|
|
758
|
-
else if (cap === "sq") strokeAttrParts.push('stroke-linecap="square"');
|
|
759
|
-
else if (cap === "flat") strokeAttrParts.push('stroke-linecap="butt"');
|
|
760
|
-
const join = getShapeStrokeJoin(shape);
|
|
761
|
-
if (join === "round") strokeAttrParts.push('stroke-linejoin="round"');
|
|
762
|
-
else if (join === "bevel") strokeAttrParts.push('stroke-linejoin="bevel"');
|
|
763
|
-
else if (join === "miter") strokeAttrParts.push('stroke-linejoin="miter"');
|
|
764
|
-
const cmpd = getShapeStrokeCompound(shape);
|
|
765
|
-
if (cmpd === "dbl") {
|
|
766
|
-
strokeWidth = Math.max(strokeWidth, 19050);
|
|
767
|
-
}
|
|
768
|
-
const head = getShapeStrokeArrow(shape, "head");
|
|
769
|
-
const tail = getShapeStrokeArrow(shape, "tail");
|
|
770
|
-
if (head && head.type !== "none") {
|
|
771
|
-
const m = buildArrowMarker(
|
|
772
|
-
head.type,
|
|
773
|
-
head.width,
|
|
774
|
-
head.length,
|
|
775
|
-
strokeColor,
|
|
776
|
-
"auto-start-reverse"
|
|
777
|
-
);
|
|
778
|
-
defs += m.def;
|
|
779
|
-
markerAttrs += ` marker-start="url(#${m.id})"`;
|
|
780
|
-
}
|
|
781
|
-
if (tail && tail.type !== "none") {
|
|
782
|
-
const m = buildArrowMarker(tail.type, tail.width, tail.length, strokeColor, "auto");
|
|
783
|
-
defs += m.def;
|
|
784
|
-
markerAttrs += ` marker-end="url(#${m.id})"`;
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
return {
|
|
789
|
-
fill: fillColor,
|
|
790
|
-
stroke: strokeColor,
|
|
791
|
-
strokeWidth,
|
|
792
|
-
defs,
|
|
793
|
-
strokeAttrs: strokeAttrParts.join(" "),
|
|
794
|
-
markerAttrs
|
|
795
|
-
};
|
|
796
|
-
};
|
|
797
|
-
var polygon = (n, rotation = -Math.PI / 2) => {
|
|
798
|
-
const out = [];
|
|
799
|
-
for (let i = 0; i < n; i++) {
|
|
800
|
-
const a = rotation + i * 2 * Math.PI / n;
|
|
801
|
-
out.push([0.5 + 0.5 * Math.cos(a), 0.5 + 0.5 * Math.sin(a)]);
|
|
802
|
-
}
|
|
803
|
-
return out;
|
|
804
|
-
};
|
|
805
|
-
var star = (points, innerRatio = 0.42) => {
|
|
806
|
-
const out = [];
|
|
807
|
-
const rotation = -Math.PI / 2;
|
|
808
|
-
for (let i = 0; i < points * 2; i++) {
|
|
809
|
-
const a = rotation + i * Math.PI / points;
|
|
810
|
-
const r = i % 2 === 0 ? 0.5 : 0.5 * innerRatio;
|
|
811
|
-
out.push([0.5 + r * Math.cos(a), 0.5 + r * Math.sin(a)]);
|
|
812
|
-
}
|
|
813
|
-
return out;
|
|
814
|
-
};
|
|
815
|
-
var PRESET_POINTS = {
|
|
816
|
-
triangle: () => [
|
|
817
|
-
[0.5, 0],
|
|
818
|
-
[1, 1],
|
|
819
|
-
[0, 1]
|
|
820
|
-
],
|
|
821
|
-
rtTriangle: () => [
|
|
822
|
-
[0, 0],
|
|
823
|
-
[1, 1],
|
|
824
|
-
[0, 1]
|
|
825
|
-
],
|
|
826
|
-
diamond: () => [
|
|
827
|
-
[0.5, 0],
|
|
828
|
-
[1, 0.5],
|
|
829
|
-
[0.5, 1],
|
|
830
|
-
[0, 0.5]
|
|
831
|
-
],
|
|
832
|
-
parallelogram: () => [
|
|
833
|
-
[0.25, 0],
|
|
834
|
-
[1, 0],
|
|
835
|
-
[0.75, 1],
|
|
836
|
-
[0, 1]
|
|
837
|
-
],
|
|
838
|
-
trapezoid: () => [
|
|
839
|
-
[0.25, 0],
|
|
840
|
-
[0.75, 0],
|
|
841
|
-
[1, 1],
|
|
842
|
-
[0, 1]
|
|
843
|
-
],
|
|
844
|
-
pentagon: () => polygon(5),
|
|
845
|
-
hexagon: () => polygon(6),
|
|
846
|
-
heptagon: () => polygon(7),
|
|
847
|
-
octagon: () => polygon(8),
|
|
848
|
-
decagon: () => polygon(10),
|
|
849
|
-
dodecagon: () => polygon(12),
|
|
850
|
-
star4: () => star(4),
|
|
851
|
-
star5: () => star(5),
|
|
852
|
-
star6: () => star(6),
|
|
853
|
-
star7: () => star(7),
|
|
854
|
-
star8: () => star(8),
|
|
855
|
-
star10: () => star(10),
|
|
856
|
-
star12: () => star(12),
|
|
857
|
-
star16: () => star(16),
|
|
858
|
-
star24: () => star(24),
|
|
859
|
-
star32: () => star(32),
|
|
860
|
-
rightArrow: () => [
|
|
861
|
-
[0, 0.3],
|
|
862
|
-
[0.65, 0.3],
|
|
863
|
-
[0.65, 0],
|
|
864
|
-
[1, 0.5],
|
|
865
|
-
[0.65, 1],
|
|
866
|
-
[0.65, 0.7],
|
|
867
|
-
[0, 0.7]
|
|
868
|
-
],
|
|
869
|
-
leftArrow: () => [
|
|
870
|
-
[1, 0.3],
|
|
871
|
-
[0.35, 0.3],
|
|
872
|
-
[0.35, 0],
|
|
873
|
-
[0, 0.5],
|
|
874
|
-
[0.35, 1],
|
|
875
|
-
[0.35, 0.7],
|
|
876
|
-
[1, 0.7]
|
|
877
|
-
],
|
|
878
|
-
upArrow: () => [
|
|
879
|
-
[0.3, 1],
|
|
880
|
-
[0.3, 0.35],
|
|
881
|
-
[0, 0.35],
|
|
882
|
-
[0.5, 0],
|
|
883
|
-
[1, 0.35],
|
|
884
|
-
[0.7, 0.35],
|
|
885
|
-
[0.7, 1]
|
|
886
|
-
],
|
|
887
|
-
downArrow: () => [
|
|
888
|
-
[0.3, 0],
|
|
889
|
-
[0.3, 0.65],
|
|
890
|
-
[0, 0.65],
|
|
891
|
-
[0.5, 1],
|
|
892
|
-
[1, 0.65],
|
|
893
|
-
[0.7, 0.65],
|
|
894
|
-
[0.7, 0]
|
|
895
|
-
],
|
|
896
|
-
leftRightArrow: () => [
|
|
897
|
-
[0, 0.5],
|
|
898
|
-
[0.18, 0.2],
|
|
899
|
-
[0.18, 0.35],
|
|
900
|
-
[0.82, 0.35],
|
|
901
|
-
[0.82, 0.2],
|
|
902
|
-
[1, 0.5],
|
|
903
|
-
[0.82, 0.8],
|
|
904
|
-
[0.82, 0.65],
|
|
905
|
-
[0.18, 0.65],
|
|
906
|
-
[0.18, 0.8]
|
|
907
|
-
],
|
|
908
|
-
upDownArrow: () => [
|
|
909
|
-
[0.5, 0],
|
|
910
|
-
[0.2, 0.18],
|
|
911
|
-
[0.35, 0.18],
|
|
912
|
-
[0.35, 0.82],
|
|
913
|
-
[0.2, 0.82],
|
|
914
|
-
[0.5, 1],
|
|
915
|
-
[0.8, 0.82],
|
|
916
|
-
[0.65, 0.82],
|
|
917
|
-
[0.65, 0.18],
|
|
918
|
-
[0.8, 0.18]
|
|
919
|
-
],
|
|
920
|
-
chevron: () => [
|
|
921
|
-
[0, 0],
|
|
922
|
-
[0.7, 0],
|
|
923
|
-
[1, 0.5],
|
|
924
|
-
[0.7, 1],
|
|
925
|
-
[0, 1],
|
|
926
|
-
[0.3, 0.5]
|
|
927
|
-
],
|
|
928
|
-
// Additional block arrows beyond the cardinal four.
|
|
929
|
-
bentArrow: () => [
|
|
930
|
-
[0, 0.45],
|
|
931
|
-
[0.55, 0.45],
|
|
932
|
-
[0.55, 0.25],
|
|
933
|
-
[0.55, 0.05],
|
|
934
|
-
[0.95, 0.05],
|
|
935
|
-
[0.95, 0.55],
|
|
936
|
-
[0.8, 0.55],
|
|
937
|
-
[0.8, 0.85],
|
|
938
|
-
[0, 0.85]
|
|
939
|
-
],
|
|
940
|
-
// Right-pointing pentagon (often used for flowcharts).
|
|
941
|
-
homePlate: () => [
|
|
942
|
-
[0, 0],
|
|
943
|
-
[0.75, 0],
|
|
944
|
-
[1, 0.5],
|
|
945
|
-
[0.75, 1],
|
|
946
|
-
[0, 1]
|
|
947
|
-
]
|
|
948
|
-
// Hearts / smileyFace / cloud handled via path renderers below.
|
|
949
|
-
};
|
|
950
|
-
var PRESET_PATHS = {
|
|
951
|
-
// Wedge callouts: rect / round-rect / ellipse body covering ~85% of
|
|
952
|
-
// the bounding box plus a triangular tail pointing down-left.
|
|
953
|
-
// Default adj1/adj2 values aren't read from the XML; the tail
|
|
954
|
-
// position is approximate.
|
|
955
|
-
wedgeRectCallout: (x, y, w, h) => {
|
|
956
|
-
const bodyH = h * 0.78;
|
|
957
|
-
const tailTipX = x + w * 0.12;
|
|
958
|
-
const tailTipY = y + h;
|
|
959
|
-
const tailBaseLeft = x + w * 0.18;
|
|
960
|
-
const tailBaseRight = x + w * 0.32;
|
|
961
|
-
const bodyB = y + bodyH;
|
|
962
|
-
return `M${x},${y} L${x + w},${y} L${x + w},${bodyB} L${tailBaseRight},${bodyB} L${tailTipX},${tailTipY} L${tailBaseLeft},${bodyB} L${x},${bodyB} Z`;
|
|
963
|
-
},
|
|
964
|
-
wedgeRoundRectCallout: (x, y, w, h) => {
|
|
965
|
-
const r = Math.min(w, h) * 0.08;
|
|
966
|
-
const bodyH = h * 0.78;
|
|
967
|
-
const tailTipX = x + w * 0.12;
|
|
968
|
-
const tailTipY = y + h;
|
|
969
|
-
const tailBaseLeft = x + w * 0.18;
|
|
970
|
-
const tailBaseRight = x + w * 0.32;
|
|
971
|
-
const bodyB = y + bodyH;
|
|
972
|
-
return `M${x + r},${y} L${x + w - r},${y} A${r},${r} 0 0 1 ${x + w},${y + r} L${x + w},${bodyB - r} A${r},${r} 0 0 1 ${x + w - r},${bodyB} L${tailBaseRight},${bodyB} L${tailTipX},${tailTipY} L${tailBaseLeft},${bodyB} L${x + r},${bodyB} A${r},${r} 0 0 1 ${x},${bodyB - r} L${x},${y + r} A${r},${r} 0 0 1 ${x + r},${y} Z`;
|
|
973
|
-
},
|
|
974
|
-
wedgeEllipseCallout: (x, y, w, h) => {
|
|
975
|
-
const bodyCy = y + h * 0.39;
|
|
976
|
-
const bodyRy = h * 0.39;
|
|
977
|
-
const cx = x + w / 2;
|
|
978
|
-
const bodyRx = w / 2;
|
|
979
|
-
const tailTipX = x + w * 0.12;
|
|
980
|
-
const tailTipY = y + h;
|
|
981
|
-
const tailBaseAngle = 1.5;
|
|
982
|
-
const tailBase1X = cx + bodyRx * Math.cos(tailBaseAngle - 0.18);
|
|
983
|
-
const tailBase1Y = bodyCy + bodyRy * Math.sin(tailBaseAngle - 0.18);
|
|
984
|
-
const tailBase2X = cx + bodyRx * Math.cos(tailBaseAngle + 0.18);
|
|
985
|
-
const tailBase2Y = bodyCy + bodyRy * Math.sin(tailBaseAngle + 0.18);
|
|
986
|
-
return `M${tailBase1X},${tailBase1Y} A${bodyRx},${bodyRy} 0 1 0 ${tailBase2X},${tailBase2Y} L${tailTipX},${tailTipY} Z`;
|
|
987
|
-
},
|
|
988
|
-
// Cloud callout — body is 8 lobes around an ellipse, plus a small
|
|
989
|
-
// dot trail towards the tail point.
|
|
990
|
-
cloudCallout: (x, y, w, h) => {
|
|
991
|
-
const bodyH = h * 0.78;
|
|
992
|
-
const cx = x + w / 2;
|
|
993
|
-
const cy = y + bodyH / 2;
|
|
994
|
-
const rx = w / 2 * 0.92;
|
|
995
|
-
const ry = bodyH / 2 * 0.85;
|
|
996
|
-
const lobes = 10;
|
|
997
|
-
const path = [];
|
|
998
|
-
for (let i = 0; i < lobes; i++) {
|
|
999
|
-
const a = i / lobes * 2 * Math.PI - Math.PI / 2;
|
|
1000
|
-
const lobeRx = rx * 0.32;
|
|
1001
|
-
const lobeRy = ry * 0.32;
|
|
1002
|
-
const px0 = cx + rx * Math.cos(a);
|
|
1003
|
-
const py0 = cy + ry * Math.sin(a);
|
|
1004
|
-
if (i === 0) path.push(`M${px0 - lobeRx},${py0}`);
|
|
1005
|
-
path.push(`A${lobeRx},${lobeRy} 0 1 1 ${px0 + lobeRx},${py0}`);
|
|
1006
|
-
const nextA = (i + 1) / lobes * 2 * Math.PI - Math.PI / 2;
|
|
1007
|
-
const nextX = cx + rx * Math.cos(nextA) - lobeRx;
|
|
1008
|
-
const nextY = cy + ry * Math.sin(nextA);
|
|
1009
|
-
path.push(`L${nextX},${nextY}`);
|
|
1010
|
-
}
|
|
1011
|
-
path.push("Z");
|
|
1012
|
-
const tailX = x + w * 0.18;
|
|
1013
|
-
const tailY = y + h * 0.95;
|
|
1014
|
-
return `${path.join(" ")} M${tailX - 6},${tailY - 14} a4,3 0 1 0 1,0 Z M${tailX},${tailY} a6,4 0 1 0 1,0 Z`;
|
|
1015
|
-
},
|
|
1016
|
-
// Hearts / sun / lightning / smiley — common decorative shapes.
|
|
1017
|
-
heart: (x, y, w, h) => {
|
|
1018
|
-
const cx = x + w / 2;
|
|
1019
|
-
const top = y + h * 0.27;
|
|
1020
|
-
return `M${cx},${y + h} C${x},${y + h * 0.55} ${x},${top} ${cx},${y + h * 0.4} C${x + w},${top} ${x + w},${y + h * 0.55} ${cx},${y + h} Z`;
|
|
1021
|
-
},
|
|
1022
|
-
sun: (x, y, w, h) => {
|
|
1023
|
-
const cx = x + w / 2;
|
|
1024
|
-
const cy = y + h / 2;
|
|
1025
|
-
const innerR = Math.min(w, h) * 0.25;
|
|
1026
|
-
const outerR = Math.min(w, h) * 0.5;
|
|
1027
|
-
const rays = 12;
|
|
1028
|
-
const path = [];
|
|
1029
|
-
for (let i = 0; i < rays * 2; i++) {
|
|
1030
|
-
const r = i % 2 === 0 ? outerR : innerR;
|
|
1031
|
-
const a = i / (rays * 2) * 2 * Math.PI - Math.PI / 2;
|
|
1032
|
-
const px0 = cx + r * Math.cos(a);
|
|
1033
|
-
const py0 = cy + r * Math.sin(a);
|
|
1034
|
-
path.push(`${i === 0 ? "M" : "L"}${px0},${py0}`);
|
|
1035
|
-
}
|
|
1036
|
-
path.push("Z");
|
|
1037
|
-
return path.join(" ");
|
|
1038
|
-
},
|
|
1039
|
-
smileyFace: (x, y, w, h) => {
|
|
1040
|
-
const cx = x + w / 2;
|
|
1041
|
-
const cy = y + h / 2;
|
|
1042
|
-
const r = Math.min(w, h) / 2 - 1;
|
|
1043
|
-
const eyeR = r * 0.07;
|
|
1044
|
-
const eyeOff = r * 0.32;
|
|
1045
|
-
const mouthW = r * 0.5;
|
|
1046
|
-
const mouthY = cy + r * 0.18;
|
|
1047
|
-
return `M${cx + r},${cy} A${r},${r} 0 1 0 ${cx - r},${cy} A${r},${r} 0 1 0 ${cx + r},${cy} Z M${cx - eyeOff + eyeR},${cy - eyeOff} A${eyeR},${eyeR} 0 1 1 ${cx - eyeOff - eyeR},${cy - eyeOff} A${eyeR},${eyeR} 0 1 1 ${cx - eyeOff + eyeR},${cy - eyeOff} Z M${cx + eyeOff + eyeR},${cy - eyeOff} A${eyeR},${eyeR} 0 1 1 ${cx + eyeOff - eyeR},${cy - eyeOff} A${eyeR},${eyeR} 0 1 1 ${cx + eyeOff + eyeR},${cy - eyeOff} Z M${cx - mouthW},${mouthY} Q${cx},${mouthY + r * 0.32} ${cx + mouthW},${mouthY}`;
|
|
1048
|
-
},
|
|
1049
|
-
lightningBolt: (x, y, w, h) => {
|
|
1050
|
-
return `M${x + w * 0.5},${y} L${x + w * 0.15},${y + h * 0.55} L${x + w * 0.45},${y + h * 0.55} L${x + w * 0.3},${y + h} L${x + w * 0.85},${y + h * 0.4} L${x + w * 0.55},${y + h * 0.4} L${x + w * 0.7},${y} Z`;
|
|
1051
|
-
},
|
|
1052
|
-
// -- Flowchart shapes ---------------------------------------------------
|
|
1053
|
-
// Lightweight approximations of the ~28 ECMA-376 flowchart presets.
|
|
1054
|
-
// They're laid out so the shape's bounding box matches the slide's,
|
|
1055
|
-
// and the geometry is what most viewers expect at a glance.
|
|
1056
|
-
flowChartProcess: (x, y, w, h) => `M${x},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} Z`,
|
|
1057
|
-
flowChartAlternateProcess: (x, y, w, h) => {
|
|
1058
|
-
const r = Math.min(w, h) * 0.18;
|
|
1059
|
-
return `M${x + r},${y} L${x + w - r},${y} A${r},${r} 0 0 1 ${x + w},${y + r} L${x + w},${y + h - r} A${r},${r} 0 0 1 ${x + w - r},${y + h} L${x + r},${y + h} A${r},${r} 0 0 1 ${x},${y + h - r} L${x},${y + r} A${r},${r} 0 0 1 ${x + r},${y} Z`;
|
|
1060
|
-
},
|
|
1061
|
-
flowChartDecision: (x, y, w, h) => {
|
|
1062
|
-
const cx = x + w / 2;
|
|
1063
|
-
const cy = y + h / 2;
|
|
1064
|
-
return `M${cx},${y} L${x + w},${cy} L${cx},${y + h} L${x},${cy} Z`;
|
|
1065
|
-
},
|
|
1066
|
-
flowChartTerminator: (x, y, w, h) => {
|
|
1067
|
-
const r = h / 2;
|
|
1068
|
-
return `M${x + r},${y} L${x + w - r},${y} A${r},${r} 0 0 1 ${x + w - r},${y + h} L${x + r},${y + h} A${r},${r} 0 0 1 ${x + r},${y} Z`;
|
|
1069
|
-
},
|
|
1070
|
-
flowChartConnector: (x, y, w, h) => {
|
|
1071
|
-
const cx = x + w / 2;
|
|
1072
|
-
const cy = y + h / 2;
|
|
1073
|
-
const r = Math.min(w, h) / 2;
|
|
1074
|
-
return `M${cx + r},${cy} A${r},${r} 0 1 1 ${cx - r},${cy} A${r},${r} 0 1 1 ${cx + r},${cy} Z`;
|
|
1075
|
-
},
|
|
1076
|
-
flowChartDocument: (x, y, w, h) => {
|
|
1077
|
-
const wave = h * 0.18;
|
|
1078
|
-
return `M${x},${y} L${x + w},${y} L${x + w},${y + h - wave} C${x + w * 0.75},${y + h + wave * 0.5} ${x + w * 0.25},${y + h - wave * 2} ${x},${y + h - wave * 0.5} Z`;
|
|
1079
|
-
},
|
|
1080
|
-
flowChartMultidocument: (x, y, w, h) => {
|
|
1081
|
-
const inset = w * 0.06;
|
|
1082
|
-
const back = `M${x + inset},${y + inset * 0.6} L${x + w},${y + inset * 0.6} L${x + w},${y + h - inset * 0.6} L${x + w - inset},${y + h - inset * 0.6} L${x + w - inset},${y + inset * 0.6}`;
|
|
1083
|
-
const front = `M${x},${y + inset * 1.2} L${x + w - inset},${y + inset * 1.2} L${x + w - inset},${y + h * 0.85} C${x + (w - inset) * 0.75},${y + h + 6} ${x + (w - inset) * 0.25},${y + h * 0.75} ${x},${y + h * 0.95} Z`;
|
|
1084
|
-
return `${back} Z ${front}`;
|
|
1085
|
-
},
|
|
1086
|
-
flowChartPredefinedProcess: (x, y, w, h) => {
|
|
1087
|
-
const inset = w * 0.1;
|
|
1088
|
-
return `M${x},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} Z M${x + inset},${y} L${x + inset},${y + h} M${x + w - inset},${y} L${x + w - inset},${y + h}`;
|
|
1089
|
-
},
|
|
1090
|
-
flowChartInternalStorage: (x, y, w, h) => {
|
|
1091
|
-
const inset = Math.min(w, h) * 0.1;
|
|
1092
|
-
return `M${x},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} Z M${x + inset},${y} L${x + inset},${y + h} M${x},${y + inset} L${x + w},${y + inset}`;
|
|
1093
|
-
},
|
|
1094
|
-
flowChartManualInput: (x, y, w, h) => {
|
|
1095
|
-
return `M${x},${y + h * 0.35} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} Z`;
|
|
1096
|
-
},
|
|
1097
|
-
flowChartManualOperation: (x, y, w, h) => {
|
|
1098
|
-
return `M${x},${y} L${x + w},${y} L${x + w * 0.8},${y + h} L${x + w * 0.2},${y + h} Z`;
|
|
1099
|
-
},
|
|
1100
|
-
flowChartInputOutput: (x, y, w, h) => {
|
|
1101
|
-
const skew = w * 0.18;
|
|
1102
|
-
return `M${x + skew},${y} L${x + w},${y} L${x + w - skew},${y + h} L${x},${y + h} Z`;
|
|
1103
|
-
},
|
|
1104
|
-
flowChartPunchedTape: (x, y, w, h) => {
|
|
1105
|
-
const wave = h * 0.12;
|
|
1106
|
-
return `M${x},${y + wave} C${x + w * 0.25},${y - wave} ${x + w * 0.75},${y + wave * 2} ${x + w},${y + wave} L${x + w},${y + h - wave} C${x + w * 0.75},${y + h + wave} ${x + w * 0.25},${y + h - wave * 2} ${x},${y + h - wave} Z`;
|
|
1107
|
-
},
|
|
1108
|
-
flowChartCard: (x, y, w, h) => {
|
|
1109
|
-
const cut = h * 0.3;
|
|
1110
|
-
return `M${x + cut},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} L${x},${y + cut} Z`;
|
|
1111
|
-
},
|
|
1112
|
-
flowChartPunchedCard: (x, y, w, h) => {
|
|
1113
|
-
const cut = h * 0.2;
|
|
1114
|
-
return `M${x + cut},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} L${x},${y + cut} Z`;
|
|
1115
|
-
},
|
|
1116
|
-
flowChartOnlineStorage: (x, y, w, h) => {
|
|
1117
|
-
const cap = w * 0.12;
|
|
1118
|
-
return `M${x + cap},${y} L${x + w},${y} L${x + w},${y + h} L${x + cap},${y + h} A${cap},${h / 2} 0 0 1 ${x + cap},${y} Z`;
|
|
1119
|
-
},
|
|
1120
|
-
flowChartMagneticDisk: (x, y, w, h) => {
|
|
1121
|
-
const er = h * 0.12;
|
|
1122
|
-
return `M${x},${y + er} A${w / 2},${er} 0 0 1 ${x + w},${y + er} L${x + w},${y + h - er} A${w / 2},${er} 0 0 1 ${x},${y + h - er} Z M${x},${y + er} A${w / 2},${er} 0 0 0 ${x + w},${y + er}`;
|
|
1123
|
-
},
|
|
1124
|
-
flowChartMagneticDrum: (x, y, w, h) => {
|
|
1125
|
-
const er = w * 0.12;
|
|
1126
|
-
return `M${x + er},${y} L${x + w - er},${y} A${er},${h / 2} 0 0 1 ${x + w - er},${y + h} L${x + er},${y + h} A${er},${h / 2} 0 0 1 ${x + er},${y} Z M${x + w - er},${y} A${er},${h / 2} 0 0 0 ${x + w - er},${y + h}`;
|
|
1127
|
-
},
|
|
1128
|
-
flowChartMagneticTape: (x, y, w, h) => {
|
|
1129
|
-
const cx = x + w / 2;
|
|
1130
|
-
const cy = y + h / 2;
|
|
1131
|
-
const r = Math.min(w, h) / 2;
|
|
1132
|
-
return `M${cx + r},${cy} A${r},${r} 0 1 0 ${cx - r * 0.7},${cy + r * 0.7} L${x + w},${y + h} L${cx + r},${cy} Z`;
|
|
1133
|
-
},
|
|
1134
|
-
flowChartSummingJunction: (x, y, w, h) => {
|
|
1135
|
-
const cx = x + w / 2;
|
|
1136
|
-
const cy = y + h / 2;
|
|
1137
|
-
const r = Math.min(w, h) / 2;
|
|
1138
|
-
const off = r * Math.SQRT1_2;
|
|
1139
|
-
return `M${cx + r},${cy} A${r},${r} 0 1 0 ${cx - r},${cy} A${r},${r} 0 1 0 ${cx + r},${cy} Z M${cx - off},${cy - off} L${cx + off},${cy + off} M${cx - off},${cy + off} L${cx + off},${cy - off}`;
|
|
1140
|
-
},
|
|
1141
|
-
flowChartOr: (x, y, w, h) => {
|
|
1142
|
-
const cx = x + w / 2;
|
|
1143
|
-
const cy = y + h / 2;
|
|
1144
|
-
const r = Math.min(w, h) / 2;
|
|
1145
|
-
return `M${cx + r},${cy} A${r},${r} 0 1 0 ${cx - r},${cy} A${r},${r} 0 1 0 ${cx + r},${cy} Z M${cx - r},${cy} L${cx + r},${cy} M${cx},${cy - r} L${cx},${cy + r}`;
|
|
1146
|
-
},
|
|
1147
|
-
flowChartCollate: (x, y, w, h) => {
|
|
1148
|
-
return `M${x},${y} L${x + w},${y} L${x},${y + h} L${x + w},${y + h} Z`;
|
|
1149
|
-
},
|
|
1150
|
-
flowChartSort: (x, y, w, h) => {
|
|
1151
|
-
const cx = x + w / 2;
|
|
1152
|
-
const cy = y + h / 2;
|
|
1153
|
-
return `M${cx},${y} L${x + w},${cy} L${cx},${y + h} L${x},${cy} Z M${x},${cy} L${x + w},${cy}`;
|
|
1154
|
-
},
|
|
1155
|
-
flowChartExtract: (x, y, w, h) => {
|
|
1156
|
-
return `M${x + w / 2},${y} L${x + w},${y + h} L${x},${y + h} Z`;
|
|
1157
|
-
},
|
|
1158
|
-
flowChartMerge: (x, y, w, h) => {
|
|
1159
|
-
return `M${x},${y} L${x + w},${y} L${x + w / 2},${y + h} Z`;
|
|
1160
|
-
},
|
|
1161
|
-
flowChartOfflineStorage: (x, y, w, h) => {
|
|
1162
|
-
return `M${x + w / 2},${y} L${x + w},${y + h} L${x},${y + h} Z M${x + w * 0.25},${y + h * 0.75} L${x + w * 0.75},${y + h * 0.75}`;
|
|
1163
|
-
},
|
|
1164
|
-
flowChartDelay: (x, y, w, h) => {
|
|
1165
|
-
const r = h / 2;
|
|
1166
|
-
return `M${x},${y} L${x + w - r},${y} A${r},${r} 0 0 1 ${x + w - r},${y + h} L${x},${y + h} Z`;
|
|
1167
|
-
},
|
|
1168
|
-
flowChartDisplay: (x, y, w, h) => {
|
|
1169
|
-
const r = h / 2;
|
|
1170
|
-
return `M${x + r * 0.5},${y} L${x + w - r},${y} A${r},${r} 0 0 1 ${x + w - r},${y + h} L${x + r * 0.5},${y + h} L${x},${y + h / 2} Z`;
|
|
1171
|
-
},
|
|
1172
|
-
flowChartPreparation: (x, y, w, h) => {
|
|
1173
|
-
const cut = w * 0.18;
|
|
1174
|
-
return `M${x + cut},${y} L${x + w - cut},${y} L${x + w},${y + h / 2} L${x + w - cut},${y + h} L${x + cut},${y + h} L${x},${y + h / 2} Z`;
|
|
1175
|
-
},
|
|
1176
|
-
// -- Block arrows -------------------------------------------------------
|
|
1177
|
-
notchedRightArrow: (x, y, w, h) => {
|
|
1178
|
-
return `M${x},${y + h * 0.3} L${x + w * 0.65},${y + h * 0.3} L${x + w * 0.65},${y} L${x + w},${y + h / 2} L${x + w * 0.65},${y + h} L${x + w * 0.65},${y + h * 0.7} L${x},${y + h * 0.7} L${x + w * 0.15},${y + h / 2} Z`;
|
|
1179
|
-
},
|
|
1180
|
-
stripedRightArrow: (x, y, w, h) => {
|
|
1181
|
-
const stripe = w * 0.04;
|
|
1182
|
-
return `M${x},${y + h * 0.3} L${x + stripe},${y + h * 0.3} L${x + stripe},${y + h * 0.7} L${x},${y + h * 0.7} Z M${x + stripe * 2.5},${y + h * 0.3} L${x + stripe * 3.5},${y + h * 0.3} L${x + stripe * 3.5},${y + h * 0.7} L${x + stripe * 2.5},${y + h * 0.7} Z M${x + stripe * 5},${y + h * 0.3} L${x + w * 0.65},${y + h * 0.3} L${x + w * 0.65},${y} L${x + w},${y + h / 2} L${x + w * 0.65},${y + h} L${x + w * 0.65},${y + h * 0.7} L${x + stripe * 5},${y + h * 0.7} Z`;
|
|
1183
|
-
},
|
|
1184
|
-
curvedRightArrow: (x, y, w, h) => {
|
|
1185
|
-
return `M${x},${y + h * 0.6} Q${x + w * 0.5},${y} ${x + w * 0.85},${y + h * 0.25} L${x + w},${y + h * 0.45} L${x + w * 0.85},${y + h * 0.55} L${x + w * 0.7},${y + h * 0.35} Q${x + w * 0.45},${y + h * 0.18} ${x + w * 0.15},${y + h * 0.75} Z`;
|
|
1186
|
-
},
|
|
1187
|
-
uturnArrow: (x, y, w, h) => {
|
|
1188
|
-
return `M${x},${y + h} L${x},${y + h / 2} A${w * 0.4},${h * 0.4} 0 0 1 ${x + w * 0.8},${y + h / 2} L${x + w * 0.8},${y + h * 0.25} L${x + w},${y + h * 0.45} L${x + w * 0.8},${y + h * 0.65} L${x + w * 0.8},${y + h / 2} A${w * 0.2},${h * 0.25} 0 0 0 ${x + w * 0.2},${y + h / 2} L${x + w * 0.2},${y + h} Z`;
|
|
1189
|
-
},
|
|
1190
|
-
// -- Brackets / braces --------------------------------------------------
|
|
1191
|
-
// Drawn as strokes (open paths); fill is none for these by convention.
|
|
1192
|
-
leftBracket: (x, y, w, h) => {
|
|
1193
|
-
return `M${x + w},${y} L${x},${y} L${x},${y + h} L${x + w},${y + h}`;
|
|
1194
|
-
},
|
|
1195
|
-
rightBracket: (x, y, w, h) => {
|
|
1196
|
-
return `M${x},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h}`;
|
|
1197
|
-
},
|
|
1198
|
-
bracketPair: (x, y, w, h) => {
|
|
1199
|
-
return `M${x + w * 0.1},${y} L${x},${y} L${x},${y + h} L${x + w * 0.1},${y + h} M${x + w * 0.9},${y} L${x + w},${y} L${x + w},${y + h} L${x + w * 0.9},${y + h}`;
|
|
1200
|
-
},
|
|
1201
|
-
leftBrace: (x, y, w, h) => {
|
|
1202
|
-
const mid = y + h / 2;
|
|
1203
|
-
return `M${x + w},${y} Q${x},${y} ${x},${mid - 8} Q${x},${mid} ${x - 4},${mid} Q${x},${mid} ${x},${mid + 8} Q${x},${y + h} ${x + w},${y + h}`;
|
|
1204
|
-
},
|
|
1205
|
-
rightBrace: (x, y, w, h) => {
|
|
1206
|
-
const mid = y + h / 2;
|
|
1207
|
-
return `M${x},${y} Q${x + w},${y} ${x + w},${mid - 8} Q${x + w},${mid} ${x + w + 4},${mid} Q${x + w},${mid} ${x + w},${mid + 8} Q${x + w},${y + h} ${x},${y + h}`;
|
|
1208
|
-
},
|
|
1209
|
-
bracePair: (x, y, w, h) => {
|
|
1210
|
-
const mid = y + h / 2;
|
|
1211
|
-
return `M${x + w * 0.12},${y} Q${x},${y} ${x},${mid - 8} Q${x},${mid} ${x - 4},${mid} Q${x},${mid} ${x},${mid + 8} Q${x},${y + h} ${x + w * 0.12},${y + h} M${x + w * 0.88},${y} Q${x + w},${y} ${x + w},${mid - 8} Q${x + w},${mid} ${x + w + 4},${mid} Q${x + w},${mid} ${x + w},${mid + 8} Q${x + w},${y + h} ${x + w * 0.88},${y + h}`;
|
|
1212
|
-
},
|
|
1213
|
-
// -- Snip / round corner rects -----------------------------------------
|
|
1214
|
-
snip1Rect: (x, y, w, h) => {
|
|
1215
|
-
const c = Math.min(w, h) * 0.18;
|
|
1216
|
-
return `M${x},${y} L${x + w - c},${y} L${x + w},${y + c} L${x + w},${y + h} L${x},${y + h} Z`;
|
|
1217
|
-
},
|
|
1218
|
-
snip2SameRect: (x, y, w, h) => {
|
|
1219
|
-
const c = Math.min(w, h) * 0.18;
|
|
1220
|
-
return `M${x + c},${y} L${x + w - c},${y} L${x + w},${y + c} L${x + w},${y + h} L${x},${y + h} L${x},${y + c} Z`;
|
|
1221
|
-
},
|
|
1222
|
-
snip2DiagRect: (x, y, w, h) => {
|
|
1223
|
-
const c = Math.min(w, h) * 0.18;
|
|
1224
|
-
return `M${x},${y} L${x + w - c},${y} L${x + w},${y + c} L${x + w},${y + h} L${x + c},${y + h} L${x},${y + h - c} Z`;
|
|
1225
|
-
},
|
|
1226
|
-
snipRoundRect: (x, y, w, h) => {
|
|
1227
|
-
const c = Math.min(w, h) * 0.18;
|
|
1228
|
-
return `M${x + c},${y} A${c},${c} 0 0 0 ${x},${y + c} L${x},${y + h} L${x + w},${y + h} L${x + w},${y + c} L${x + w - c},${y} Z`;
|
|
1229
|
-
},
|
|
1230
|
-
round1Rect: (x, y, w, h) => {
|
|
1231
|
-
const r = Math.min(w, h) * 0.18;
|
|
1232
|
-
return `M${x},${y} L${x + w - r},${y} A${r},${r} 0 0 1 ${x + w},${y + r} L${x + w},${y + h} L${x},${y + h} Z`;
|
|
1233
|
-
},
|
|
1234
|
-
round2SameRect: (x, y, w, h) => {
|
|
1235
|
-
const r = Math.min(w, h) * 0.18;
|
|
1236
|
-
return `M${x + r},${y} L${x + w - r},${y} A${r},${r} 0 0 1 ${x + w},${y + r} L${x + w},${y + h} L${x},${y + h} L${x},${y + r} A${r},${r} 0 0 1 ${x + r},${y} Z`;
|
|
1237
|
-
},
|
|
1238
|
-
round2DiagRect: (x, y, w, h) => {
|
|
1239
|
-
const r = Math.min(w, h) * 0.18;
|
|
1240
|
-
return `M${x},${y} L${x + w - r},${y} A${r},${r} 0 0 1 ${x + w},${y + r} L${x + w},${y + h} L${x + r},${y + h} A${r},${r} 0 0 1 ${x},${y + h - r} Z`;
|
|
1241
|
-
},
|
|
1242
|
-
// -- Banners & ribbons --------------------------------------------------
|
|
1243
|
-
ribbon: (x, y, w, h) => {
|
|
1244
|
-
const notch = w * 0.06;
|
|
1245
|
-
const bodyTop = y + h * 0.2;
|
|
1246
|
-
const bodyBot = y + h * 0.8;
|
|
1247
|
-
return `M${x},${bodyTop} L${x + notch * 2},${y} L${x + w - notch * 2},${y} L${x + w},${bodyTop} L${x + w * 0.85},${bodyTop + (bodyBot - bodyTop) / 2} L${x + w},${bodyBot} L${x + w - notch * 2},${y + h} L${x + w - notch * 4},${bodyBot} L${x + notch * 4},${bodyBot} L${x + notch * 2},${y + h} L${x},${bodyBot} L${x + w * 0.15},${bodyTop + (bodyBot - bodyTop) / 2} Z`;
|
|
1248
|
-
},
|
|
1249
|
-
ribbon2: (x, y, w, h) => {
|
|
1250
|
-
const notch = w * 0.06;
|
|
1251
|
-
const bodyTop = y + h * 0.2;
|
|
1252
|
-
const bodyBot = y + h * 0.8;
|
|
1253
|
-
return `M${x},${bodyBot} L${x + notch * 2},${y + h} L${x + w - notch * 2},${y + h} L${x + w},${bodyBot} L${x + w * 0.85},${bodyTop + (bodyBot - bodyTop) / 2} L${x + w},${bodyTop} L${x + w - notch * 2},${y} L${x + w - notch * 4},${bodyTop} L${x + notch * 4},${bodyTop} L${x + notch * 2},${y} L${x},${bodyTop} L${x + w * 0.15},${bodyTop + (bodyBot - bodyTop) / 2} Z`;
|
|
1254
|
-
},
|
|
1255
|
-
verticalScroll: (x, y, w, h) => {
|
|
1256
|
-
const r = w * 0.08;
|
|
1257
|
-
return `M${x + r},${y + r} A${r},${r} 0 0 1 ${x + r * 2},${y} L${x + w},${y} L${x + w},${y + h - r} A${r},${r} 0 0 1 ${x + w - r * 2},${y + h} L${x},${y + h} L${x},${y + r} A${r},${r} 0 0 1 ${x + r},${y + r} Z`;
|
|
1258
|
-
},
|
|
1259
|
-
horizontalScroll: (x, y, w, h) => {
|
|
1260
|
-
const r = h * 0.08;
|
|
1261
|
-
return `M${x + r},${y + r} A${r},${r} 0 0 1 ${x},${y + r * 2} L${x},${y + h} L${x + w - r},${y + h} A${r},${r} 0 0 1 ${x + w},${y + h - r * 2} L${x + w},${y} L${x + r},${y} A${r},${r} 0 0 1 ${x + r},${y + r} Z`;
|
|
1262
|
-
},
|
|
1263
|
-
wave: (x, y, w, h) => {
|
|
1264
|
-
return `M${x},${y + h * 0.5} C${x + w * 0.25},${y - h * 0.1} ${x + w * 0.5},${y + h * 0.85} ${x + w * 0.75},${y + h * 0.3} C${x + w * 0.85},${y + h * 0.05} ${x + w * 0.95},${y + h * 0.4} ${x + w},${y + h * 0.5} L${x + w},${y + h} C${x + w * 0.75},${y + h * 0.4} ${x + w * 0.5},${y + h * 1.1} ${x + w * 0.25},${y + h * 0.55} C${x + w * 0.15},${y + h * 0.3} ${x + w * 0.05},${y + h * 0.6} ${x},${y + h * 0.55} Z`;
|
|
1265
|
-
},
|
|
1266
|
-
doubleWave: (x, y, w, h) => {
|
|
1267
|
-
return `M${x},${y + h * 0.4} C${x + w * 0.15},${y - h * 0.05} ${x + w * 0.35},${y + h * 0.65} ${x + w * 0.5},${y + h * 0.3} C${x + w * 0.65},${y - h * 0.05} ${x + w * 0.85},${y + h * 0.65} ${x + w},${y + h * 0.4} L${x + w},${y + h} C${x + w * 0.85},${y + h * 0.4} ${x + w * 0.65},${y + h * 1.05} ${x + w * 0.5},${y + h * 0.7} C${x + w * 0.35},${y + h * 1.05} ${x + w * 0.15},${y + h * 0.4} ${x},${y + h} Z`;
|
|
1268
|
-
},
|
|
1269
|
-
// -- Math operators -----------------------------------------------------
|
|
1270
|
-
mathPlus: (x, y, w, h) => {
|
|
1271
|
-
const t = Math.min(w, h) * 0.2;
|
|
1272
|
-
const cx = x + w / 2;
|
|
1273
|
-
const cy = y + h / 2;
|
|
1274
|
-
return `M${cx - t / 2},${y} L${cx + t / 2},${y} L${cx + t / 2},${cy - t / 2} L${x + w},${cy - t / 2} L${x + w},${cy + t / 2} L${cx + t / 2},${cy + t / 2} L${cx + t / 2},${y + h} L${cx - t / 2},${y + h} L${cx - t / 2},${cy + t / 2} L${x},${cy + t / 2} L${x},${cy - t / 2} L${cx - t / 2},${cy - t / 2} Z`;
|
|
1275
|
-
},
|
|
1276
|
-
mathMinus: (x, y, w, h) => {
|
|
1277
|
-
const t = h * 0.3;
|
|
1278
|
-
const cy = y + h / 2;
|
|
1279
|
-
return `M${x},${cy - t / 2} L${x + w},${cy - t / 2} L${x + w},${cy + t / 2} L${x},${cy + t / 2} Z`;
|
|
1280
|
-
},
|
|
1281
|
-
mathMultiply: (x, y, w, h) => {
|
|
1282
|
-
const t = Math.min(w, h) * 0.16;
|
|
1283
|
-
const cx = x + w / 2;
|
|
1284
|
-
const cy = y + h / 2;
|
|
1285
|
-
return `M${x},${y + t} L${x + t},${y} L${cx},${cy - t} L${x + w - t},${y} L${x + w},${y + t} L${cx + t},${cy} L${x + w},${y + h - t} L${x + w - t},${y + h} L${cx},${cy + t} L${x + t},${y + h} L${x},${y + h - t} L${cx - t},${cy} Z`;
|
|
1286
|
-
},
|
|
1287
|
-
mathDivide: (x, y, w, h) => {
|
|
1288
|
-
const dot = Math.min(w, h) * 0.1;
|
|
1289
|
-
const cx = x + w / 2;
|
|
1290
|
-
const cy = y + h / 2;
|
|
1291
|
-
return `M${x},${cy - dot * 0.4} L${x + w},${cy - dot * 0.4} L${x + w},${cy + dot * 0.4} L${x},${cy + dot * 0.4} Z M${cx - dot},${y + h * 0.18} A${dot},${dot} 0 1 0 ${cx + dot},${y + h * 0.18} A${dot},${dot} 0 1 0 ${cx - dot},${y + h * 0.18} Z M${cx - dot},${y + h * 0.82} A${dot},${dot} 0 1 0 ${cx + dot},${y + h * 0.82} A${dot},${dot} 0 1 0 ${cx - dot},${y + h * 0.82} Z`;
|
|
1292
|
-
},
|
|
1293
|
-
mathEqual: (x, y, w, h) => {
|
|
1294
|
-
const t = h * 0.2;
|
|
1295
|
-
return `M${x},${y + h * 0.3 - t / 2} L${x + w},${y + h * 0.3 - t / 2} L${x + w},${y + h * 0.3 + t / 2} L${x},${y + h * 0.3 + t / 2} Z M${x},${y + h * 0.7 - t / 2} L${x + w},${y + h * 0.7 - t / 2} L${x + w},${y + h * 0.7 + t / 2} L${x},${y + h * 0.7 + t / 2} Z`;
|
|
1296
|
-
},
|
|
1297
|
-
mathNotEqual: (x, y, w, h) => {
|
|
1298
|
-
const t = h * 0.15;
|
|
1299
|
-
const cy = y + h / 2;
|
|
1300
|
-
return `M${x},${cy - h * 0.18 - t / 2} L${x + w},${cy - h * 0.18 - t / 2} L${x + w},${cy - h * 0.18 + t / 2} L${x},${cy - h * 0.18 + t / 2} Z M${x},${cy + h * 0.18 - t / 2} L${x + w},${cy + h * 0.18 - t / 2} L${x + w},${cy + h * 0.18 + t / 2} L${x},${cy + h * 0.18 + t / 2} Z M${x + w * 0.7},${y} L${x + w * 0.85},${y} L${x + w * 0.3},${y + h} L${x + w * 0.15},${y + h} Z`;
|
|
1301
|
-
},
|
|
1302
|
-
// -- Action button glyphs (the chrome is a roundRect; we just add the
|
|
1303
|
-
// glyph). Real action buttons are nested shapes; we approximate.
|
|
1304
|
-
actionButtonBlank: (x, y, w, h) => {
|
|
1305
|
-
const r = Math.min(w, h) * 0.06;
|
|
1306
|
-
return `M${x + r},${y} L${x + w - r},${y} A${r},${r} 0 0 1 ${x + w},${y + r} L${x + w},${y + h - r} A${r},${r} 0 0 1 ${x + w - r},${y + h} L${x + r},${y + h} A${r},${r} 0 0 1 ${x},${y + h - r} L${x},${y + r} A${r},${r} 0 0 1 ${x + r},${y} Z`;
|
|
1307
|
-
},
|
|
1308
|
-
// -- Explosion / starburst callouts (the "jagged" speech marks). ------
|
|
1309
|
-
// `irregularSeal1` and `irregularSeal2` are PowerPoint's two
|
|
1310
|
-
// explosion-style callouts. The geometry is a deterministic pseudo-
|
|
1311
|
-
// random pattern of long + short rays; we mimic that without trying
|
|
1312
|
-
// to match the spec point-for-point.
|
|
1313
|
-
irregularSeal1: (x, y, w, h) => {
|
|
1314
|
-
const cx = x + w / 2;
|
|
1315
|
-
const cy = y + h / 2;
|
|
1316
|
-
const rx = w / 2;
|
|
1317
|
-
const ry = h / 2;
|
|
1318
|
-
const offsets = [
|
|
1319
|
-
1,
|
|
1320
|
-
0.45,
|
|
1321
|
-
0.95,
|
|
1322
|
-
0.5,
|
|
1323
|
-
1,
|
|
1324
|
-
0.4,
|
|
1325
|
-
0.9,
|
|
1326
|
-
0.55,
|
|
1327
|
-
1,
|
|
1328
|
-
0.45,
|
|
1329
|
-
0.95,
|
|
1330
|
-
0.5,
|
|
1331
|
-
1,
|
|
1332
|
-
0.4,
|
|
1333
|
-
0.9,
|
|
1334
|
-
0.55
|
|
1335
|
-
];
|
|
1336
|
-
const points = [];
|
|
1337
|
-
for (let i = 0; i < offsets.length; i++) {
|
|
1338
|
-
const a = i / offsets.length * 2 * Math.PI - Math.PI / 2;
|
|
1339
|
-
const offset = offsets[i] ?? 1;
|
|
1340
|
-
const r = offset;
|
|
1341
|
-
const px0 = cx + rx * r * Math.cos(a);
|
|
1342
|
-
const py0 = cy + ry * r * Math.sin(a);
|
|
1343
|
-
points.push(`${i === 0 ? "M" : "L"}${px0},${py0}`);
|
|
1344
|
-
}
|
|
1345
|
-
points.push("Z");
|
|
1346
|
-
return points.join(" ");
|
|
1347
|
-
},
|
|
1348
|
-
irregularSeal2: (x, y, w, h) => {
|
|
1349
|
-
const cx = x + w / 2;
|
|
1350
|
-
const cy = y + h / 2;
|
|
1351
|
-
const rx = w / 2;
|
|
1352
|
-
const ry = h / 2;
|
|
1353
|
-
const offsets = [
|
|
1354
|
-
1,
|
|
1355
|
-
0.4,
|
|
1356
|
-
0.95,
|
|
1357
|
-
0.5,
|
|
1358
|
-
0.9,
|
|
1359
|
-
0.35,
|
|
1360
|
-
0.85,
|
|
1361
|
-
0.45,
|
|
1362
|
-
1,
|
|
1363
|
-
0.4,
|
|
1364
|
-
0.95,
|
|
1365
|
-
0.5,
|
|
1366
|
-
0.9,
|
|
1367
|
-
0.35,
|
|
1368
|
-
0.85,
|
|
1369
|
-
0.45,
|
|
1370
|
-
1,
|
|
1371
|
-
0.4,
|
|
1372
|
-
0.95,
|
|
1373
|
-
0.5,
|
|
1374
|
-
0.9,
|
|
1375
|
-
0.35,
|
|
1376
|
-
0.85,
|
|
1377
|
-
0.45
|
|
1378
|
-
];
|
|
1379
|
-
const points = [];
|
|
1380
|
-
for (let i = 0; i < offsets.length; i++) {
|
|
1381
|
-
const a = i / offsets.length * 2 * Math.PI - Math.PI / 2;
|
|
1382
|
-
const offset = offsets[i] ?? 1;
|
|
1383
|
-
const r = offset;
|
|
1384
|
-
const px0 = cx + rx * r * Math.cos(a);
|
|
1385
|
-
const py0 = cy + ry * r * Math.sin(a);
|
|
1386
|
-
points.push(`${i === 0 ? "M" : "L"}${px0},${py0}`);
|
|
1387
|
-
}
|
|
1388
|
-
points.push("Z");
|
|
1389
|
-
return points.join(" ");
|
|
1390
|
-
},
|
|
1391
|
-
// `cloudCallout` lives above; "cloud" without callout is just the
|
|
1392
|
-
// body without the tail dots.
|
|
1393
|
-
cloud: (x, y, w, h) => {
|
|
1394
|
-
const cx = x + w / 2;
|
|
1395
|
-
const cy = y + h / 2;
|
|
1396
|
-
const rx = w / 2 * 0.92;
|
|
1397
|
-
const ry = h / 2 * 0.85;
|
|
1398
|
-
const lobes = 10;
|
|
1399
|
-
const path = [];
|
|
1400
|
-
for (let i = 0; i < lobes; i++) {
|
|
1401
|
-
const a = i / lobes * 2 * Math.PI - Math.PI / 2;
|
|
1402
|
-
const lobeRx = rx * 0.32;
|
|
1403
|
-
const lobeRy = ry * 0.32;
|
|
1404
|
-
const px0 = cx + rx * Math.cos(a);
|
|
1405
|
-
const py0 = cy + ry * Math.sin(a);
|
|
1406
|
-
if (i === 0) path.push(`M${px0 - lobeRx},${py0}`);
|
|
1407
|
-
path.push(`A${lobeRx},${lobeRy} 0 1 1 ${px0 + lobeRx},${py0}`);
|
|
1408
|
-
const nextA = (i + 1) / lobes * 2 * Math.PI - Math.PI / 2;
|
|
1409
|
-
const nextX = cx + rx * Math.cos(nextA) - lobeRx;
|
|
1410
|
-
const nextY = cy + ry * Math.sin(nextA);
|
|
1411
|
-
path.push(`L${nextX},${nextY}`);
|
|
1412
|
-
}
|
|
1413
|
-
path.push("Z");
|
|
1414
|
-
return path.join(" ");
|
|
1415
|
-
},
|
|
1416
|
-
// -- Pies / chord / teardrop / arc / blockArc / moon ------------------
|
|
1417
|
-
pie: (x, y, w, h) => {
|
|
1418
|
-
const cx = x + w / 2;
|
|
1419
|
-
const cy = y + h / 2;
|
|
1420
|
-
const r = Math.min(w, h) / 2;
|
|
1421
|
-
return `M${cx},${cy} L${cx + r},${cy} A${r},${r} 0 1 1 ${cx},${cy - r} Z`;
|
|
1422
|
-
},
|
|
1423
|
-
chord: (x, y, w, h) => {
|
|
1424
|
-
const cx = x + w / 2;
|
|
1425
|
-
const cy = y + h / 2;
|
|
1426
|
-
const rx = w / 2;
|
|
1427
|
-
const ry = h / 2;
|
|
1428
|
-
return `M${cx + rx},${cy} A${rx},${ry} 0 1 1 ${cx - rx},${cy} L${cx + rx},${cy} Z`;
|
|
1429
|
-
},
|
|
1430
|
-
teardrop: (x, y, w, h) => {
|
|
1431
|
-
const cx = x + w / 2;
|
|
1432
|
-
const cy = y + h / 2;
|
|
1433
|
-
const rx = w / 2;
|
|
1434
|
-
const ry = h / 2;
|
|
1435
|
-
return `M${cx},${y} L${x + w},${y} L${x + w},${cy} A${rx},${ry} 0 1 1 ${cx - rx},${cy} A${rx},${ry} 0 0 1 ${cx},${y} Z`;
|
|
1436
|
-
},
|
|
1437
|
-
arc: (x, y, w, h) => {
|
|
1438
|
-
const cx = x + w / 2;
|
|
1439
|
-
const cy = y + h / 2;
|
|
1440
|
-
const rx = w / 2;
|
|
1441
|
-
const ry = h / 2;
|
|
1442
|
-
return `M${cx + rx},${cy} A${rx},${ry} 0 1 1 ${cx},${cy + ry}`;
|
|
1443
|
-
},
|
|
1444
|
-
blockArc: (x, y, w, h) => {
|
|
1445
|
-
const cx = x + w / 2;
|
|
1446
|
-
const cy = y + h / 2;
|
|
1447
|
-
const outerR = Math.min(w, h) / 2;
|
|
1448
|
-
const innerR = outerR * 0.6;
|
|
1449
|
-
return `M${cx + outerR},${cy} A${outerR},${outerR} 0 1 1 ${cx},${cy - outerR} L${cx},${cy - innerR} A${innerR},${innerR} 0 1 0 ${cx + innerR},${cy} Z`;
|
|
1450
|
-
},
|
|
1451
|
-
moon: (x, y, w, h) => {
|
|
1452
|
-
const cx = x + w / 2;
|
|
1453
|
-
const cy = y + h / 2;
|
|
1454
|
-
const rx = w / 2;
|
|
1455
|
-
const ry = h / 2;
|
|
1456
|
-
const innerRx = rx * 0.78;
|
|
1457
|
-
const offsetX = rx * 0.32;
|
|
1458
|
-
return `M${cx + rx},${cy} A${rx},${ry} 0 1 1 ${cx - rx},${cy} A${rx},${ry} 0 1 1 ${cx + rx},${cy} M${cx - rx + offsetX + innerRx},${cy} A${innerRx},${ry * 0.95} 0 1 1 ${cx - rx + offsetX - innerRx},${cy} A${innerRx},${ry * 0.95} 0 1 1 ${cx - rx + offsetX + innerRx},${cy}`;
|
|
1459
|
-
},
|
|
1460
|
-
// -- Plates / plaques / frames / corners ------------------------------
|
|
1461
|
-
plus: (x, y, w, h) => {
|
|
1462
|
-
const t = Math.min(w, h) * 0.3;
|
|
1463
|
-
const cx = x + w / 2;
|
|
1464
|
-
const cy = y + h / 2;
|
|
1465
|
-
return `M${cx - t / 2},${y} L${cx + t / 2},${y} L${cx + t / 2},${cy - t / 2} L${x + w},${cy - t / 2} L${x + w},${cy + t / 2} L${cx + t / 2},${cy + t / 2} L${cx + t / 2},${y + h} L${cx - t / 2},${y + h} L${cx - t / 2},${cy + t / 2} L${x},${cy + t / 2} L${x},${cy - t / 2} L${cx - t / 2},${cy - t / 2} Z`;
|
|
1466
|
-
},
|
|
1467
|
-
plaque: (x, y, w, h) => {
|
|
1468
|
-
const r = Math.min(w, h) * 0.18;
|
|
1469
|
-
return `M${x + r},${y} L${x + w - r},${y} A${r},${r} 0 0 0 ${x + w},${y + r} L${x + w},${y + h - r} A${r},${r} 0 0 0 ${x + w - r},${y + h} L${x + r},${y + h} A${r},${r} 0 0 0 ${x},${y + h - r} L${x},${y + r} A${r},${r} 0 0 0 ${x + r},${y} Z`;
|
|
1470
|
-
},
|
|
1471
|
-
can: (x, y, w, h) => {
|
|
1472
|
-
const er = h * 0.12;
|
|
1473
|
-
return `M${x},${y + er} A${w / 2},${er} 0 0 1 ${x + w},${y + er} L${x + w},${y + h - er} A${w / 2},${er} 0 0 1 ${x},${y + h - er} Z M${x},${y + er} A${w / 2},${er} 0 0 0 ${x + w},${y + er}`;
|
|
1474
|
-
},
|
|
1475
|
-
cube: (x, y, w, h) => {
|
|
1476
|
-
const d = Math.min(w, h) * 0.2;
|
|
1477
|
-
return `M${x},${y + d} L${x + d},${y} L${x + w},${y} L${x + w},${y + h - d} L${x + w - d},${y + h} L${x},${y + h} Z M${x},${y + d} L${x + w - d},${y + d} L${x + w},${y} M${x + w - d},${y + d} L${x + w - d},${y + h}`;
|
|
1478
|
-
},
|
|
1479
|
-
bevel: (x, y, w, h) => {
|
|
1480
|
-
const d = Math.min(w, h) * 0.12;
|
|
1481
|
-
return `M${x},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} Z M${x + d},${y + d} L${x + w - d},${y + d} L${x + w - d},${y + h - d} L${x + d},${y + h - d} Z M${x},${y} L${x + d},${y + d} M${x + w},${y} L${x + w - d},${y + d} M${x},${y + h} L${x + d},${y + h - d} M${x + w},${y + h} L${x + w - d},${y + h - d}`;
|
|
1482
|
-
},
|
|
1483
|
-
donut: (x, y, w, h) => {
|
|
1484
|
-
const cx = x + w / 2;
|
|
1485
|
-
const cy = y + h / 2;
|
|
1486
|
-
const r = Math.min(w, h) / 2;
|
|
1487
|
-
const innerR = r * 0.65;
|
|
1488
|
-
return `M${cx + r},${cy} A${r},${r} 0 1 0 ${cx - r},${cy} A${r},${r} 0 1 0 ${cx + r},${cy} Z M${cx + innerR},${cy} A${innerR},${innerR} 0 1 1 ${cx - innerR},${cy} A${innerR},${innerR} 0 1 1 ${cx + innerR},${cy} Z`;
|
|
1489
|
-
},
|
|
1490
|
-
noSmoking: (x, y, w, h) => {
|
|
1491
|
-
const cx = x + w / 2;
|
|
1492
|
-
const cy = y + h / 2;
|
|
1493
|
-
const r = Math.min(w, h) / 2;
|
|
1494
|
-
const innerR = r * 0.78;
|
|
1495
|
-
const t = r * 0.12;
|
|
1496
|
-
return `M${cx + r},${cy} A${r},${r} 0 1 0 ${cx - r},${cy} A${r},${r} 0 1 0 ${cx + r},${cy} Z M${cx + innerR},${cy} A${innerR},${innerR} 0 1 1 ${cx - innerR},${cy} A${innerR},${innerR} 0 1 1 ${cx + innerR},${cy} Z M${cx - r * 0.71 - t},${cy - r * 0.71 + t} L${cx - r * 0.71 + t},${cy - r * 0.71 - t} L${cx + r * 0.71 + t},${cy + r * 0.71 - t} L${cx + r * 0.71 - t},${cy + r * 0.71 + t} Z`;
|
|
1497
|
-
},
|
|
1498
|
-
frame: (x, y, w, h) => {
|
|
1499
|
-
const f = Math.min(w, h) * 0.1;
|
|
1500
|
-
return `M${x},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} Z M${x + f},${y + f} L${x + f},${y + h - f} L${x + w - f},${y + h - f} L${x + w - f},${y + f} Z`;
|
|
1501
|
-
},
|
|
1502
|
-
halfFrame: (x, y, w, h) => {
|
|
1503
|
-
const f = Math.min(w, h) * 0.15;
|
|
1504
|
-
return `M${x},${y} L${x + w},${y} L${x + w - f},${y + f} L${x + f},${y + f} L${x + f},${y + h - f} L${x},${y + h} Z`;
|
|
1505
|
-
},
|
|
1506
|
-
corner: (x, y, w, h) => {
|
|
1507
|
-
const tx = w * 0.4;
|
|
1508
|
-
const ty = h * 0.4;
|
|
1509
|
-
return `M${x},${y} L${x + tx},${y} L${x + tx},${y + h - ty} L${x + w},${y + h - ty} L${x + w},${y + h} L${x},${y + h} Z`;
|
|
1510
|
-
},
|
|
1511
|
-
diagStripe: (x, y, w, h) => {
|
|
1512
|
-
return `M${x},${y} L${x + w * 0.6},${y} L${x},${y + h * 0.6} Z`;
|
|
1513
|
-
},
|
|
1514
|
-
// -- Ellipse ribbons ---------------------------------------------------
|
|
1515
|
-
ellipseRibbon: (x, y, w, h) => {
|
|
1516
|
-
const notch = w * 0.08;
|
|
1517
|
-
const bodyTop = y + h * 0.2;
|
|
1518
|
-
const bodyBot = y + h * 0.85;
|
|
1519
|
-
const arcDip = h * 0.15;
|
|
1520
|
-
return `M${x},${bodyTop} C${x + w * 0.3},${bodyTop - arcDip} ${x + w * 0.7},${bodyTop - arcDip} ${x + w},${bodyTop} L${x + w * 0.85},${bodyTop + (bodyBot - bodyTop) / 2} L${x + w},${bodyBot} L${x + w - notch * 2},${y + h} L${x + w - notch * 4},${bodyBot} C${x + w * 0.7},${bodyBot + arcDip * 0.4} ${x + w * 0.3},${bodyBot + arcDip * 0.4} L${x + notch * 4},${bodyBot} L${x + notch * 2},${y + h} L${x},${bodyBot} L${x + w * 0.15},${bodyTop + (bodyBot - bodyTop) / 2} Z`;
|
|
1521
|
-
},
|
|
1522
|
-
ellipseRibbon2: (x, y, w, h) => {
|
|
1523
|
-
const notch = w * 0.08;
|
|
1524
|
-
const bodyTop = y + h * 0.15;
|
|
1525
|
-
const bodyBot = y + h * 0.8;
|
|
1526
|
-
const arcRise = h * 0.15;
|
|
1527
|
-
return `M${x},${bodyBot} C${x + w * 0.3},${bodyBot + arcRise} ${x + w * 0.7},${bodyBot + arcRise} ${x + w},${bodyBot} L${x + w * 0.85},${bodyTop + (bodyBot - bodyTop) / 2} L${x + w},${bodyTop} L${x + w - notch * 2},${y} L${x + w - notch * 4},${bodyTop} C${x + w * 0.7},${bodyTop - arcRise * 0.4} ${x + w * 0.3},${bodyTop - arcRise * 0.4} L${x + notch * 4},${bodyTop} L${x + notch * 2},${y} L${x},${bodyTop} L${x + w * 0.15},${bodyTop + (bodyBot - bodyTop) / 2} Z`;
|
|
1528
|
-
},
|
|
1529
|
-
// -- Block arrows: the rest of the cardinal & curved family -----------
|
|
1530
|
-
quadArrow: (x, y, w, h) => {
|
|
1531
|
-
const cx = x + w / 2;
|
|
1532
|
-
const cy = y + h / 2;
|
|
1533
|
-
const tip = 0.2;
|
|
1534
|
-
const stem = 0.15;
|
|
1535
|
-
const headW = 0.35;
|
|
1536
|
-
return `M${cx},${y} L${cx + headW * w},${y + tip * h} L${cx + stem * w},${y + tip * h} L${cx + stem * w},${cy - stem * h} L${x + w - tip * w},${cy - stem * h} L${x + w - tip * w},${cy - headW * h} L${x + w},${cy} L${x + w - tip * w},${cy + headW * h} L${x + w - tip * w},${cy + stem * h} L${cx + stem * w},${cy + stem * h} L${cx + stem * w},${y + h - tip * h} L${cx + headW * w},${y + h - tip * h} L${cx},${y + h} L${cx - headW * w},${y + h - tip * h} L${cx - stem * w},${y + h - tip * h} L${cx - stem * w},${cy + stem * h} L${x + tip * w},${cy + stem * h} L${x + tip * w},${cy + headW * h} L${x},${cy} L${x + tip * w},${cy - headW * h} L${x + tip * w},${cy - stem * h} L${cx - stem * w},${cy - stem * h} L${cx - stem * w},${y + tip * h} L${cx - headW * w},${y + tip * h} Z`;
|
|
1537
|
-
},
|
|
1538
|
-
leftRightUpArrow: (x, y, w, h) => {
|
|
1539
|
-
const cx = x + w / 2;
|
|
1540
|
-
const tip = 0.2;
|
|
1541
|
-
const stem = 0.15;
|
|
1542
|
-
const headW = 0.35;
|
|
1543
|
-
return `M${cx},${y} L${cx + headW * w},${y + tip * h} L${cx + stem * w},${y + tip * h} L${cx + stem * w},${y + h - tip * h} L${x + w - tip * w},${y + h - tip * h} L${x + w - tip * w},${y + h - headW * h} L${x + w},${y + h} L${x + w - tip * w},${y + h + headW * h} L${x + w - tip * w},${y + h - tip * h} L${cx - stem * w},${y + h - tip * h} L${cx - stem * w},${y + tip * h} L${cx - headW * w},${y + tip * h} Z`;
|
|
1544
|
-
},
|
|
1545
|
-
bentUpArrow: (x, y, w, h) => {
|
|
1546
|
-
const stem = 0.3;
|
|
1547
|
-
const tip = 0.25;
|
|
1548
|
-
return `M${x},${y + h * 0.55} L${x + w * 0.5},${y + h * 0.55} L${x + w * 0.5},${y + tip * h} L${x + w * (0.5 - stem * 0.5)},${y + tip * h} L${x + w * 0.75},${y} L${x + w},${y + tip * h} L${x + w * (0.5 + stem * 0.5)},${y + tip * h} L${x + w * (0.5 + stem * 0.5)},${y + h * 0.55 + h * 0.4} L${x},${y + h * 0.55 + h * 0.4} Z`;
|
|
1549
|
-
},
|
|
1550
|
-
curvedLeftArrow: (x, y, w, h) => {
|
|
1551
|
-
return `M${x + w},${y + h * 0.6} Q${x + w * 0.5},${y} ${x + w * 0.15},${y + h * 0.25} L${x},${y + h * 0.45} L${x + w * 0.15},${y + h * 0.55} L${x + w * 0.3},${y + h * 0.35} Q${x + w * 0.55},${y + h * 0.18} ${x + w * 0.85},${y + h * 0.75} Z`;
|
|
1552
|
-
},
|
|
1553
|
-
curvedUpArrow: (x, y, w, h) => {
|
|
1554
|
-
return `M${x + w * 0.4},${y + h} Q${x},${y + h * 0.5} ${x + w * 0.25},${y + h * 0.15} L${x + w * 0.45},${y} L${x + w * 0.55},${y + h * 0.15} L${x + w * 0.35},${y + h * 0.3} Q${x + w * 0.18},${y + h * 0.55} ${x + w * 0.75},${y + h * 0.85} Z`;
|
|
1555
|
-
},
|
|
1556
|
-
curvedDownArrow: (x, y, w, h) => {
|
|
1557
|
-
return `M${x + w * 0.4},${y} Q${x},${y + h * 0.5} ${x + w * 0.25},${y + h * 0.85} L${x + w * 0.45},${y + h} L${x + w * 0.55},${y + h * 0.85} L${x + w * 0.35},${y + h * 0.7} Q${x + w * 0.18},${y + h * 0.45} ${x + w * 0.75},${y + h * 0.15} Z`;
|
|
1558
|
-
},
|
|
1559
|
-
swooshArrow: (x, y, w, h) => {
|
|
1560
|
-
return `M${x},${y + h * 0.75} C${x + w * 0.35},${y + h} ${x + w * 0.65},${y + h * 0.3} ${x + w * 0.75},${y + h * 0.2} L${x + w * 0.65},${y + h * 0.05} L${x + w},${y + h * 0.18} L${x + w * 0.78},${y + h * 0.45} L${x + w * 0.7},${y + h * 0.3} C${x + w * 0.55},${y + h * 0.6} ${x + w * 0.35},${y + h * 0.9} ${x},${y + h * 0.85} Z`;
|
|
1561
|
-
},
|
|
1562
|
-
circularArrow: (x, y, w, h) => {
|
|
1563
|
-
const cx = x + w / 2;
|
|
1564
|
-
const cy = y + h / 2;
|
|
1565
|
-
const outerR = Math.min(w, h) / 2;
|
|
1566
|
-
const innerR = outerR * 0.62;
|
|
1567
|
-
const midR = (outerR + innerR) / 2;
|
|
1568
|
-
return `M${cx},${cy - outerR} A${outerR},${outerR} 0 1 1 ${cx - outerR},${cy} L${cx - midR - midR * 0.25},${cy + outerR * 0.15} L${cx - midR + midR * 0.25},${cy + outerR * 0.3} L${cx - innerR},${cy} A${innerR},${innerR} 0 1 0 ${cx},${cy - innerR} Z`;
|
|
1569
|
-
},
|
|
1570
|
-
leftCircularArrow: (x, y, w, h) => {
|
|
1571
|
-
const cx = x + w / 2;
|
|
1572
|
-
const cy = y + h / 2;
|
|
1573
|
-
const outerR = Math.min(w, h) / 2;
|
|
1574
|
-
const innerR = outerR * 0.62;
|
|
1575
|
-
return `M${cx},${cy - outerR} A${outerR},${outerR} 0 1 0 ${cx + outerR},${cy} L${cx + (outerR + innerR) / 2 + (outerR + innerR) / 2 * 0.25},${cy + outerR * 0.15} L${cx + (outerR + innerR) / 2 - (outerR + innerR) / 2 * 0.25},${cy + outerR * 0.3} L${cx + innerR},${cy} A${innerR},${innerR} 0 1 1 ${cx},${cy - innerR} Z`;
|
|
1576
|
-
},
|
|
1577
|
-
leftRightCircularArrow: (x, y, w, h) => {
|
|
1578
|
-
const cx = x + w / 2;
|
|
1579
|
-
const cy = y + h / 2;
|
|
1580
|
-
const outerR = Math.min(w, h) / 2;
|
|
1581
|
-
const innerR = outerR * 0.62;
|
|
1582
|
-
return `M${cx - outerR * 0.15},${cy - outerR * 1.05} L${cx + outerR * 0.15},${cy - outerR * 1.05} L${cx + outerR * 0.1},${cy - outerR * 0.85} A${outerR},${outerR} 0 1 1 ${cx - outerR * 0.1},${cy - outerR * 0.85} Z M${cx},${cy - innerR} A${innerR},${innerR} 0 1 0 ${cx},${cy + innerR}`;
|
|
1583
|
-
},
|
|
1584
|
-
// -- Arrow callouts (block arrow body + flat rect for text) -----------
|
|
1585
|
-
rightArrowCallout: (x, y, w, h) => {
|
|
1586
|
-
const headW = w * 0.25;
|
|
1587
|
-
return `M${x},${y + h * 0.3} L${x + w - headW},${y + h * 0.3} L${x + w - headW},${y} L${x + w},${y + h / 2} L${x + w - headW},${y + h} L${x + w - headW},${y + h * 0.7} L${x},${y + h * 0.7} Z`;
|
|
1588
|
-
},
|
|
1589
|
-
leftArrowCallout: (x, y, w, h) => {
|
|
1590
|
-
const headW = w * 0.25;
|
|
1591
|
-
return `M${x + headW},${y + h * 0.3} L${x + w},${y + h * 0.3} L${x + w},${y + h * 0.7} L${x + headW},${y + h * 0.7} L${x + headW},${y + h} L${x},${y + h / 2} L${x + headW},${y} Z`;
|
|
1592
|
-
},
|
|
1593
|
-
upArrowCallout: (x, y, w, h) => {
|
|
1594
|
-
const headH = h * 0.25;
|
|
1595
|
-
return `M${x + w * 0.3},${y + headH} L${x + w * 0.3},${y + h} L${x + w * 0.7},${y + h} L${x + w * 0.7},${y + headH} L${x + w},${y + headH} L${x + w / 2},${y} L${x},${y + headH} Z`;
|
|
1596
|
-
},
|
|
1597
|
-
downArrowCallout: (x, y, w, h) => {
|
|
1598
|
-
const headH = h * 0.25;
|
|
1599
|
-
return `M${x + w * 0.3},${y} L${x + w * 0.7},${y} L${x + w * 0.7},${y + h - headH} L${x + w},${y + h - headH} L${x + w / 2},${y + h} L${x},${y + h - headH} L${x + w * 0.3},${y + h - headH} Z`;
|
|
1600
|
-
},
|
|
1601
|
-
leftRightArrowCallout: (x, y, w, h) => {
|
|
1602
|
-
const headW = w * 0.2;
|
|
1603
|
-
return `M${x},${y + h / 2} L${x + headW},${y} L${x + headW},${y + h * 0.3} L${x + w - headW},${y + h * 0.3} L${x + w - headW},${y} L${x + w},${y + h / 2} L${x + w - headW},${y + h} L${x + w - headW},${y + h * 0.7} L${x + headW},${y + h * 0.7} L${x + headW},${y + h} Z`;
|
|
1604
|
-
},
|
|
1605
|
-
upDownArrowCallout: (x, y, w, h) => {
|
|
1606
|
-
const headH = h * 0.2;
|
|
1607
|
-
return `M${x + w / 2},${y} L${x},${y + headH} L${x + w * 0.3},${y + headH} L${x + w * 0.3},${y + h - headH} L${x},${y + h - headH} L${x + w / 2},${y + h} L${x + w},${y + h - headH} L${x + w * 0.7},${y + h - headH} L${x + w * 0.7},${y + headH} L${x + w},${y + headH} Z`;
|
|
1608
|
-
},
|
|
1609
|
-
quadArrowCallout: (x, y, w, h) => {
|
|
1610
|
-
const cx = x + w / 2;
|
|
1611
|
-
const cy = y + h / 2;
|
|
1612
|
-
const tip = 0.16;
|
|
1613
|
-
const stem = 0.18;
|
|
1614
|
-
const headW = 0.32;
|
|
1615
|
-
const txt = 0.28;
|
|
1616
|
-
return `M${cx - txt * w},${cy - txt * h} L${cx - txt * w},${cy - stem * h} L${cx - stem * w},${cy - stem * h} L${cx - stem * w},${y + tip * h} L${cx - headW * w},${y + tip * h} L${cx},${y} L${cx + headW * w},${y + tip * h} L${cx + stem * w},${y + tip * h} L${cx + stem * w},${cy - stem * h} L${cx + txt * w},${cy - stem * h} L${cx + txt * w},${cy - txt * h} L${x + w - tip * w},${cy - txt * h} L${x + w - tip * w},${cy - headW * h} L${x + w},${cy} L${x + w - tip * w},${cy + headW * h} L${x + w - tip * w},${cy + txt * h} L${cx + txt * w},${cy + txt * h} L${cx + txt * w},${cy + stem * h} L${cx + stem * w},${cy + stem * h} L${cx + stem * w},${y + h - tip * h} L${cx + headW * w},${y + h - tip * h} L${cx},${y + h} L${cx - headW * w},${y + h - tip * h} L${cx - stem * w},${y + h - tip * h} L${cx - stem * w},${cy + stem * h} L${cx - txt * w},${cy + stem * h} L${cx - txt * w},${cy + txt * h} L${x + tip * w},${cy + txt * h} L${x + tip * w},${cy + headW * h} L${x},${cy} L${x + tip * w},${cy - headW * h} L${x + tip * w},${cy - txt * h} Z`;
|
|
1617
|
-
},
|
|
1618
|
-
// -- Action button chrome (rounded rect) + glyph silhouettes ----------
|
|
1619
|
-
// Each button is rounded-rect chrome + a glyph drawn as a subpath
|
|
1620
|
-
// using the same path's `evenodd` fill rule so the glyph "punches"
|
|
1621
|
-
// out of the chrome.
|
|
1622
|
-
actionButtonHome: (x, y, w, h) => {
|
|
1623
|
-
const cx = x + w / 2;
|
|
1624
|
-
const cy = y + h / 2;
|
|
1625
|
-
const s = Math.min(w, h) * 0.3;
|
|
1626
|
-
return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s},${cy + s * 0.6} L${cx - s},${cy - s * 0.1} L${cx},${cy - s * 0.7} L${cx + s},${cy - s * 0.1} L${cx + s},${cy + s * 0.6} L${cx + s * 0.3},${cy + s * 0.6} L${cx + s * 0.3},${cy + s * 0.1} L${cx - s * 0.3},${cy + s * 0.1} L${cx - s * 0.3},${cy + s * 0.6} Z`;
|
|
1627
|
-
},
|
|
1628
|
-
actionButtonForwardNext: (x, y, w, h) => {
|
|
1629
|
-
const cx = x + w / 2;
|
|
1630
|
-
const cy = y + h / 2;
|
|
1631
|
-
const s = Math.min(w, h) * 0.3;
|
|
1632
|
-
return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s * 0.7},${cy - s} L${cx + s * 0.7},${cy} L${cx - s * 0.7},${cy + s} Z`;
|
|
1633
|
-
},
|
|
1634
|
-
actionButtonBackPrevious: (x, y, w, h) => {
|
|
1635
|
-
const cx = x + w / 2;
|
|
1636
|
-
const cy = y + h / 2;
|
|
1637
|
-
const s = Math.min(w, h) * 0.3;
|
|
1638
|
-
return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx + s * 0.7},${cy - s} L${cx - s * 0.7},${cy} L${cx + s * 0.7},${cy + s} Z`;
|
|
1639
|
-
},
|
|
1640
|
-
actionButtonEnd: (x, y, w, h) => {
|
|
1641
|
-
const cx = x + w / 2;
|
|
1642
|
-
const cy = y + h / 2;
|
|
1643
|
-
const s = Math.min(w, h) * 0.3;
|
|
1644
|
-
return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s},${cy - s} L${cx + s * 0.4},${cy} L${cx - s},${cy + s} Z M${cx + s * 0.5},${cy - s} L${cx + s},${cy - s} L${cx + s},${cy + s} L${cx + s * 0.5},${cy + s} Z`;
|
|
1645
|
-
},
|
|
1646
|
-
actionButtonBeginning: (x, y, w, h) => {
|
|
1647
|
-
const cx = x + w / 2;
|
|
1648
|
-
const cy = y + h / 2;
|
|
1649
|
-
const s = Math.min(w, h) * 0.3;
|
|
1650
|
-
return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx + s},${cy - s} L${cx - s * 0.4},${cy} L${cx + s},${cy + s} Z M${cx - s * 0.5},${cy - s} L${cx - s},${cy - s} L${cx - s},${cy + s} L${cx - s * 0.5},${cy + s} Z`;
|
|
1651
|
-
},
|
|
1652
|
-
actionButtonReturn: (x, y, w, h) => {
|
|
1653
|
-
const cx = x + w / 2;
|
|
1654
|
-
const cy = y + h / 2;
|
|
1655
|
-
const s = Math.min(w, h) * 0.28;
|
|
1656
|
-
return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx + s},${cy - s} L${cx + s * 0.4},${cy - s} L${cx + s * 0.4},${cy + s * 0.2} L${cx - s * 0.2},${cy + s * 0.2} L${cx - s * 0.2},${cy - s * 0.2} L${cx - s},${cy + s * 0.2} L${cx - s * 0.2},${cy + s} L${cx - s * 0.2},${cy + s * 0.5} L${cx + s},${cy + s * 0.5} Z`;
|
|
1657
|
-
},
|
|
1658
|
-
actionButtonHelp: (x, y, w, h) => {
|
|
1659
|
-
const cx = x + w / 2;
|
|
1660
|
-
const cy = y + h / 2;
|
|
1661
|
-
const s = Math.min(w, h) * 0.3;
|
|
1662
|
-
return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s * 0.4},${cy - s * 0.4} Q${cx - s * 0.4},${cy - s} ${cx},${cy - s} Q${cx + s * 0.4},${cy - s} ${cx + s * 0.4},${cy - s * 0.4} Q${cx + s * 0.4},${cy} ${cx},${cy} L${cx},${cy + s * 0.4} M${cx - s * 0.18},${cy + s * 0.8} L${cx + s * 0.18},${cy + s * 0.8} L${cx + s * 0.18},${cy + s} L${cx - s * 0.18},${cy + s} Z`;
|
|
1663
|
-
},
|
|
1664
|
-
actionButtonInformation: (x, y, w, h) => {
|
|
1665
|
-
const cx = x + w / 2;
|
|
1666
|
-
const cy = y + h / 2;
|
|
1667
|
-
const s = Math.min(w, h) * 0.3;
|
|
1668
|
-
return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s * 0.2},${cy - s * 0.7} L${cx + s * 0.2},${cy - s * 0.7} L${cx + s * 0.2},${cy - s * 0.35} L${cx - s * 0.2},${cy - s * 0.35} Z M${cx - s * 0.2},${cy - s * 0.1} L${cx + s * 0.2},${cy - s * 0.1} L${cx + s * 0.2},${cy + s} L${cx - s * 0.2},${cy + s} Z`;
|
|
1669
|
-
},
|
|
1670
|
-
actionButtonDocument: (x, y, w, h) => {
|
|
1671
|
-
const cx = x + w / 2;
|
|
1672
|
-
const cy = y + h / 2;
|
|
1673
|
-
const s = Math.min(w, h) * 0.3;
|
|
1674
|
-
return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s * 0.6},${cy - s} L${cx + s * 0.3},${cy - s} L${cx + s * 0.6},${cy - s * 0.7} L${cx + s * 0.6},${cy + s} L${cx - s * 0.6},${cy + s} Z M${cx + s * 0.3},${cy - s} L${cx + s * 0.3},${cy - s * 0.7} L${cx + s * 0.6},${cy - s * 0.7}`;
|
|
1675
|
-
},
|
|
1676
|
-
actionButtonSound: (x, y, w, h) => {
|
|
1677
|
-
const cx = x + w / 2;
|
|
1678
|
-
const cy = y + h / 2;
|
|
1679
|
-
const s = Math.min(w, h) * 0.3;
|
|
1680
|
-
return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s},${cy - s * 0.4} L${cx - s * 0.3},${cy - s * 0.4} L${cx + s * 0.3},${cy - s} L${cx + s * 0.3},${cy + s} L${cx - s * 0.3},${cy + s * 0.4} L${cx - s},${cy + s * 0.4} Z M${cx + s * 0.55},${cy - s * 0.4} Q${cx + s * 0.95},${cy} ${cx + s * 0.55},${cy + s * 0.4}`;
|
|
1681
|
-
},
|
|
1682
|
-
actionButtonMovie: (x, y, w, h) => {
|
|
1683
|
-
const cx = x + w / 2;
|
|
1684
|
-
const cy = y + h / 2;
|
|
1685
|
-
const s = Math.min(w, h) * 0.3;
|
|
1686
|
-
const out = [PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""];
|
|
1687
|
-
out.push(
|
|
1688
|
-
`M${cx - s},${cy - s * 0.6} L${cx + s},${cy - s * 0.6} L${cx + s},${cy + s * 0.6} L${cx - s},${cy + s * 0.6} Z`
|
|
1689
|
-
);
|
|
1690
|
-
for (let i = 0; i < 4; i++) {
|
|
1691
|
-
const px0 = cx - s + (i + 0.5) * (s * 2 / 4);
|
|
1692
|
-
out.push(
|
|
1693
|
-
`M${px0 - s * 0.08},${cy - s * 0.45} L${px0 + s * 0.08},${cy - s * 0.45} L${px0 + s * 0.08},${cy - s * 0.3} L${px0 - s * 0.08},${cy - s * 0.3} Z`
|
|
1694
|
-
);
|
|
1695
|
-
out.push(
|
|
1696
|
-
`M${px0 - s * 0.08},${cy + s * 0.3} L${px0 + s * 0.08},${cy + s * 0.3} L${px0 + s * 0.08},${cy + s * 0.45} L${px0 - s * 0.08},${cy + s * 0.45} Z`
|
|
1697
|
-
);
|
|
1698
|
-
}
|
|
1699
|
-
return out.join(" ");
|
|
1700
|
-
},
|
|
1701
|
-
// -- Border/accent callouts (simplified — line callouts without
|
|
1702
|
-
// adjustable elbows; rendered as a rect + a single connecting line).
|
|
1703
|
-
borderCallout1: (x, y, w, h) => {
|
|
1704
|
-
return `M${x},${y} L${x + w * 0.6},${y} L${x + w * 0.6},${y + h * 0.5} L${x},${y + h * 0.5} Z M${x + w * 0.6},${y + h * 0.5} L${x + w},${y + h}`;
|
|
1705
|
-
},
|
|
1706
|
-
borderCallout2: (x, y, w, h) => {
|
|
1707
|
-
return `M${x},${y} L${x + w * 0.6},${y} L${x + w * 0.6},${y + h * 0.5} L${x},${y + h * 0.5} Z M${x + w * 0.6},${y + h * 0.5} L${x + w * 0.85},${y + h * 0.75} L${x + w},${y + h}`;
|
|
1708
|
-
},
|
|
1709
|
-
borderCallout3: (x, y, w, h) => {
|
|
1710
|
-
return `M${x},${y} L${x + w * 0.6},${y} L${x + w * 0.6},${y + h * 0.5} L${x},${y + h * 0.5} Z M${x + w * 0.6},${y + h * 0.5} L${x + w * 0.75},${y + h * 0.65} L${x + w * 0.9},${y + h * 0.65} L${x + w},${y + h}`;
|
|
1711
|
-
},
|
|
1712
|
-
accentCallout1: (x, y, w, h) => {
|
|
1713
|
-
return `M${x},${y} L${x + w * 0.6},${y} L${x + w * 0.6},${y + h * 0.5} L${x},${y + h * 0.5} Z M${x + w * 0.58},${y} L${x + w * 0.58},${y + h * 0.5} M${x + w * 0.6},${y + h * 0.5} L${x + w},${y + h}`;
|
|
1714
|
-
},
|
|
1715
|
-
accentBorderCallout1: (x, y, w, h) => {
|
|
1716
|
-
return PRESET_PATHS.accentCallout1?.(x, y, w, h) ?? "";
|
|
1717
|
-
},
|
|
1718
|
-
callout1: (x, y, w, h) => {
|
|
1719
|
-
return `M${x},${y + h * 0.5} L${x + w},${y + h}`;
|
|
1720
|
-
},
|
|
1721
|
-
callout2: (x, y, w, h) => {
|
|
1722
|
-
return `M${x},${y + h * 0.5} L${x + w * 0.6},${y + h * 0.7} L${x + w},${y + h}`;
|
|
1723
|
-
},
|
|
1724
|
-
callout3: (x, y, w, h) => {
|
|
1725
|
-
return `M${x},${y + h * 0.5} L${x + w * 0.4},${y + h * 0.55} L${x + w * 0.7},${y + h * 0.8} L${x + w},${y + h}`;
|
|
1726
|
-
},
|
|
1727
|
-
// -- Connectors / lines (rendered as straight diagonals; PowerPoint
|
|
1728
|
-
// routes these dynamically but the static preview just shows where
|
|
1729
|
-
// the endpoints are).
|
|
1730
|
-
straightConnector1: (x, y, w, h) => `M${x},${y} L${x + w},${y + h}`,
|
|
1731
|
-
bentConnector2: (x, y, w, h) => `M${x},${y} L${x + w},${y} L${x + w},${y + h}`,
|
|
1732
|
-
bentConnector3: (x, y, w, h) => `M${x},${y} L${x + w / 2},${y} L${x + w / 2},${y + h} L${x + w},${y + h}`,
|
|
1733
|
-
bentConnector4: (x, y, w, h) => `M${x},${y} L${x + w * 0.33},${y} L${x + w * 0.33},${y + h * 0.5} L${x + w * 0.66},${y + h * 0.5} L${x + w * 0.66},${y + h} L${x + w},${y + h}`,
|
|
1734
|
-
bentConnector5: (x, y, w, h) => `M${x},${y} L${x + w * 0.25},${y} L${x + w * 0.25},${y + h * 0.5} L${x + w * 0.75},${y + h * 0.5} L${x + w * 0.75},${y + h} L${x + w},${y + h}`,
|
|
1735
|
-
curvedConnector2: (x, y, w, h) => `M${x},${y} Q${x + w},${y} ${x + w},${y + h}`,
|
|
1736
|
-
curvedConnector3: (x, y, w, h) => `M${x},${y} C${x + w * 0.5},${y} ${x + w * 0.5},${y + h} ${x + w},${y + h}`,
|
|
1737
|
-
curvedConnector4: (x, y, w, h) => `M${x},${y} C${x + w * 0.33},${y} ${x + w * 0.33},${y + h * 0.5} ${x + w * 0.5},${y + h * 0.5} C${x + w * 0.66},${y + h * 0.5} ${x + w * 0.66},${y + h} ${x + w},${y + h}`,
|
|
1738
|
-
curvedConnector5: (x, y, w, h) => `M${x},${y} C${x + w * 0.25},${y} ${x + w * 0.25},${y + h * 0.25} ${x + w * 0.5},${y + h * 0.5} C${x + w * 0.75},${y + h * 0.75} ${x + w * 0.75},${y + h} ${x + w},${y + h}`
|
|
1739
|
-
};
|
|
1740
|
-
var ALIGNMENT_TO_CSS = {
|
|
1741
|
-
left: "left",
|
|
1742
|
-
center: "center",
|
|
1743
|
-
right: "right",
|
|
1744
|
-
justify: "justify"
|
|
1745
|
-
};
|
|
1746
|
-
var ANCHOR_TO_CSS = {
|
|
1747
|
-
top: "flex-start",
|
|
1748
|
-
center: "center",
|
|
1749
|
-
bottom: "flex-end"
|
|
1750
|
-
};
|
|
1751
|
-
var placeholderDefaultPt = (phType) => {
|
|
1752
|
-
if (phType === "title" || phType === "ctrTitle") return DEFAULT_TITLE_PT;
|
|
1753
|
-
if (phType === "subTitle") return 32;
|
|
1754
|
-
if (phType === "ftr" || phType === "dt" || phType === "sldNum") return 12;
|
|
1755
|
-
return DEFAULT_BODY_PT;
|
|
1756
|
-
};
|
|
1757
|
-
var bulletChar = (level) => level <= 0 ? "\u2022" : level === 1 ? "\u25E6" : "\u25AA";
|
|
1758
|
-
var bulletAutoNumType = (style) => {
|
|
1759
|
-
if (style === "number") return "arabicPeriod";
|
|
1760
|
-
if (style !== null && typeof style === "object" && "autoNum" in style) {
|
|
1761
|
-
return style.autoNum ?? null;
|
|
1762
|
-
}
|
|
1763
|
-
return null;
|
|
1764
|
-
};
|
|
1765
|
-
var toRoman = (n) => {
|
|
1766
|
-
if (n <= 0) return String(n);
|
|
1767
|
-
const map = [
|
|
1768
|
-
[1e3, "M"],
|
|
1769
|
-
[900, "CM"],
|
|
1770
|
-
[500, "D"],
|
|
1771
|
-
[400, "CD"],
|
|
1772
|
-
[100, "C"],
|
|
1773
|
-
[90, "XC"],
|
|
1774
|
-
[50, "L"],
|
|
1775
|
-
[40, "XL"],
|
|
1776
|
-
[10, "X"],
|
|
1777
|
-
[9, "IX"],
|
|
1778
|
-
[5, "V"],
|
|
1779
|
-
[4, "IV"],
|
|
1780
|
-
[1, "I"]
|
|
1781
|
-
];
|
|
1782
|
-
let out = "";
|
|
1783
|
-
let r = n;
|
|
1784
|
-
for (const [v, s] of map) {
|
|
1785
|
-
while (r >= v) {
|
|
1786
|
-
out += s;
|
|
1787
|
-
r -= v;
|
|
1788
|
-
}
|
|
1789
|
-
}
|
|
1790
|
-
return out;
|
|
1791
|
-
};
|
|
1792
|
-
var toAlpha = (n) => {
|
|
1793
|
-
if (n <= 0) return String(n);
|
|
1794
|
-
let r = n;
|
|
1795
|
-
let out = "";
|
|
1796
|
-
while (r > 0) {
|
|
1797
|
-
r -= 1;
|
|
1798
|
-
out = String.fromCharCode(65 + r % 26) + out;
|
|
1799
|
-
r = Math.floor(r / 26);
|
|
1800
|
-
}
|
|
1801
|
-
return out;
|
|
1802
|
-
};
|
|
1803
|
-
var formatAutoNum = (token, n) => {
|
|
1804
|
-
const arabic = String(n);
|
|
1805
|
-
switch (token) {
|
|
1806
|
-
case "arabicPlain":
|
|
1807
|
-
return arabic;
|
|
1808
|
-
case "arabicPeriod":
|
|
1809
|
-
return `${arabic}.`;
|
|
1810
|
-
case "arabicParenR":
|
|
1811
|
-
return `${arabic})`;
|
|
1812
|
-
case "arabicParenBoth":
|
|
1813
|
-
return `(${arabic})`;
|
|
1814
|
-
case "romanUcPeriod":
|
|
1815
|
-
return `${toRoman(n)}.`;
|
|
1816
|
-
case "romanLcPeriod":
|
|
1817
|
-
return `${toRoman(n).toLowerCase()}.`;
|
|
1818
|
-
case "romanUcParenR":
|
|
1819
|
-
return `${toRoman(n)})`;
|
|
1820
|
-
case "romanLcParenR":
|
|
1821
|
-
return `${toRoman(n).toLowerCase()})`;
|
|
1822
|
-
case "romanUcParenBoth":
|
|
1823
|
-
return `(${toRoman(n)})`;
|
|
1824
|
-
case "romanLcParenBoth":
|
|
1825
|
-
return `(${toRoman(n).toLowerCase()})`;
|
|
1826
|
-
case "alphaUcPeriod":
|
|
1827
|
-
return `${toAlpha(n)}.`;
|
|
1828
|
-
case "alphaLcPeriod":
|
|
1829
|
-
return `${toAlpha(n).toLowerCase()}.`;
|
|
1830
|
-
case "alphaUcParenR":
|
|
1831
|
-
return `${toAlpha(n)})`;
|
|
1832
|
-
case "alphaLcParenR":
|
|
1833
|
-
return `${toAlpha(n).toLowerCase()})`;
|
|
1834
|
-
case "alphaUcParenBoth":
|
|
1835
|
-
return `(${toAlpha(n)})`;
|
|
1836
|
-
case "alphaLcParenBoth":
|
|
1837
|
-
return `(${toAlpha(n).toLowerCase()})`;
|
|
1838
|
-
default:
|
|
1839
|
-
return `${arabic}.`;
|
|
1840
|
-
}
|
|
1841
|
-
};
|
|
1842
|
-
var renderRun = (text, format, theme, effectivePt, _wasDefault = false) => {
|
|
1843
|
-
if (text === "") return "";
|
|
1844
|
-
const styles = [];
|
|
1845
|
-
styles.push(`font-size:${(effectivePt * PX_PER_PT).toFixed(2)}px`);
|
|
1846
|
-
styles.push(`line-height:1.05`);
|
|
1847
|
-
if (format?.font) styles.push(`font-family:${escapeXml2(format.font)}, ${DEFAULT_FONT}`);
|
|
1848
|
-
if (format?.bold) styles.push("font-weight:700");
|
|
1849
|
-
if (format?.italic) styles.push("font-style:italic");
|
|
1850
|
-
const underline = format?.underline;
|
|
1851
|
-
const strike = format?.strike;
|
|
1852
|
-
const hasUnderline = underline !== void 0 && underline !== false && underline !== "none";
|
|
1853
|
-
const hasStrike = strike !== void 0 && strike !== false && strike !== "noStrike";
|
|
1854
|
-
if (hasUnderline && hasStrike) {
|
|
1855
|
-
styles.push("text-decoration:underline line-through");
|
|
1856
|
-
} else if (hasUnderline) {
|
|
1857
|
-
styles.push("text-decoration:underline");
|
|
1858
|
-
} else if (hasStrike) {
|
|
1859
|
-
styles.push("text-decoration:line-through");
|
|
1860
|
-
}
|
|
1861
|
-
if (format?.color !== void 0 && format.color !== null) {
|
|
1862
|
-
styles.push(`color:${resolveColor(format.color, theme, "#000000")}`);
|
|
1863
|
-
}
|
|
1864
|
-
if (format?.spc !== void 0 && format.spc !== 0) {
|
|
1865
|
-
const trackingPx = format.spc / 100 * PX_PER_PT;
|
|
1866
|
-
styles.push(`letter-spacing:${trackingPx.toFixed(3)}px`);
|
|
1867
|
-
}
|
|
1868
|
-
if (format?.baseline !== void 0 && format.baseline !== 0) {
|
|
1869
|
-
const direction = format.baseline > 0 ? "super" : "sub";
|
|
1870
|
-
styles.push(`vertical-align:${direction}`);
|
|
1871
|
-
styles.push("font-size:0.65em");
|
|
1872
|
-
}
|
|
1873
|
-
if (format?.cap === "all") styles.push("text-transform:uppercase");
|
|
1874
|
-
else if (format?.cap === "small") styles.push("font-variant:small-caps");
|
|
1875
|
-
if (format?.highlight !== void 0 && format.highlight !== null) {
|
|
1876
|
-
styles.push(`background-color:${resolveColor(format.highlight, theme, "#FFFF00")}`);
|
|
1877
|
-
}
|
|
1878
|
-
const html = text.split("\n").map((part) => escapeXml2(part)).join("<br/>");
|
|
1879
|
-
return `<span style="${styles.join(";")}">${html}</span>`;
|
|
1880
|
-
};
|
|
1881
|
-
var LINE_HEIGHT = 1.05;
|
|
1882
|
-
var AVG_GLYPH_W_RATIO2 = 0.55;
|
|
1883
|
-
var hasUnderlineFmt = (fmt2) => {
|
|
1884
|
-
const u = fmt2?.underline;
|
|
1885
|
-
return u !== void 0 && u !== false && u !== "none";
|
|
1886
|
-
};
|
|
1887
|
-
var hasStrikeFmt = (fmt2) => {
|
|
1888
|
-
const s = fmt2?.strike;
|
|
1889
|
-
return s !== void 0 && s !== false && s !== "noStrike";
|
|
1890
|
-
};
|
|
1891
|
-
var alignOf = (a) => a === "center" || a === "right" || a === "justify" ? a : "left";
|
|
1892
|
-
var verticalLayoutOf = (vert) => {
|
|
1893
|
-
switch (vert) {
|
|
1894
|
-
case "vert":
|
|
1895
|
-
case "eaVert":
|
|
1896
|
-
case "wordArtVert":
|
|
1897
|
-
case "wordArtVertRtl":
|
|
1898
|
-
return "cw90";
|
|
1899
|
-
case "vert270":
|
|
1900
|
-
case "mongolianVert":
|
|
1901
|
-
return "cw270";
|
|
1902
|
-
case null:
|
|
1903
|
-
return "none";
|
|
1904
|
-
}
|
|
1905
|
-
};
|
|
1906
|
-
var buildAndLayoutSvgText = (a) => {
|
|
1907
|
-
const scale = a.autoFitScale;
|
|
1908
|
-
const paragraphs = a.paraData.map((para, pi) => {
|
|
1909
|
-
const pieces = [];
|
|
1910
|
-
for (const run of para.runs) {
|
|
1911
|
-
if (run.text === "\n" && run.fmt === null) {
|
|
1912
|
-
pieces.push(breakPiece());
|
|
1913
|
-
continue;
|
|
1914
|
-
}
|
|
1915
|
-
let fmt2 = run.fmt;
|
|
1916
|
-
if (run.href) {
|
|
1917
|
-
const hlinkColor = a.theme ? normalizeHex(a.theme.hyperlink) : "#0563C1";
|
|
1918
|
-
fmt2 = {
|
|
1919
|
-
...fmt2,
|
|
1920
|
-
color: fmt2?.color ?? hlinkColor,
|
|
1921
|
-
underline: fmt2?.underline ?? true
|
|
1922
|
-
};
|
|
1923
|
-
}
|
|
1924
|
-
const family = substituteFamily(fmt2?.font ?? a.themeFace);
|
|
1925
|
-
const sizePx = run.sizePt * scale * PX_PER_PT;
|
|
1926
|
-
const fillHex = fmt2?.color !== void 0 && fmt2.color !== null ? resolveColor(fmt2.color, a.theme, "#000000") : a.defaultColor;
|
|
1927
|
-
const superSub = fmt2?.baseline !== void 0 && fmt2.baseline !== 0 ? fmt2.baseline > 0 ? 1 : -1 : 0;
|
|
1928
|
-
const letterSpacingPx = fmt2?.spc !== void 0 && fmt2.spc !== 0 ? fmt2.spc / 100 * PX_PER_PT : 0;
|
|
1929
|
-
const caps = fmt2?.cap === "all" || fmt2?.cap === "small";
|
|
1930
|
-
const base = {
|
|
1931
|
-
family,
|
|
1932
|
-
sizePx,
|
|
1933
|
-
bold: fmt2?.bold ?? false,
|
|
1934
|
-
italic: fmt2?.italic ?? false,
|
|
1935
|
-
letterSpacingPx,
|
|
1936
|
-
fillHex,
|
|
1937
|
-
underline: hasUnderlineFmt(fmt2),
|
|
1938
|
-
strike: hasStrikeFmt(fmt2),
|
|
1939
|
-
superSub,
|
|
1940
|
-
href: run.href ?? null
|
|
1941
|
-
};
|
|
1942
|
-
const segs = run.text.split("\n");
|
|
1943
|
-
segs.forEach((seg, i) => {
|
|
1944
|
-
pieces.push({ ...base, text: caps ? seg.toUpperCase() : seg, isBreak: false });
|
|
1945
|
-
if (i < segs.length - 1) pieces.push(breakPiece());
|
|
1946
|
-
});
|
|
1947
|
-
}
|
|
1948
|
-
const marLpx = para.indent.leftEmu !== null ? para.indent.leftEmu / EMU_PER_PX * scale : para.level > 0 ? para.level * 24 * PX_PER_PT * scale : 0;
|
|
1949
|
-
const marRpx = para.indent.rightEmu !== null ? para.indent.rightEmu / EMU_PER_PX * scale : 0;
|
|
1950
|
-
const firstIndentPx = para.indent.firstLineEmu !== null ? para.indent.firstLineEmu / EMU_PER_PX * scale : 0;
|
|
1951
|
-
const spcBefPx = para.spcBefPts !== null && para.spcBefPts > 0 ? para.spcBefPts * PX_PER_PT * scale : 0;
|
|
1952
|
-
const spcAftPx = para.spcAftPts !== null && para.spcAftPts > 0 ? para.spcAftPts * PX_PER_PT * scale : 0;
|
|
1953
|
-
const lineSpacing = para.lineSpacing?.kind === "pct" ? { kind: "pct", value: para.lineSpacing.value } : para.lineSpacing?.kind === "pts" ? { kind: "pts", px: para.lineSpacing.value * PX_PER_PT * scale } : null;
|
|
1954
|
-
return {
|
|
1955
|
-
align: alignOf(para.align),
|
|
1956
|
-
marLpx,
|
|
1957
|
-
marRpx,
|
|
1958
|
-
firstIndentPx,
|
|
1959
|
-
spcBefPx,
|
|
1960
|
-
spcAftPx,
|
|
1961
|
-
lineSpacing,
|
|
1962
|
-
lineAdvanceScale: a.lineHeightScale,
|
|
1963
|
-
bullet: buildBullet(a, para, pi),
|
|
1964
|
-
pieces,
|
|
1965
|
-
fallbackSizePx: a.defaultPt * scale * PX_PER_PT
|
|
1966
|
-
};
|
|
1967
|
-
});
|
|
1968
|
-
const input = {
|
|
1969
|
-
boxXpx: a.innerX / EMU_PER_PX,
|
|
1970
|
-
boxYpx: a.innerY / EMU_PER_PX,
|
|
1971
|
-
boxWpx: a.innerW / EMU_PER_PX,
|
|
1972
|
-
boxHpx: a.innerH / EMU_PER_PX,
|
|
1973
|
-
anchor: a.anchor,
|
|
1974
|
-
wrap: a.wrap,
|
|
1975
|
-
paragraphs,
|
|
1976
|
-
vert: a.vert,
|
|
1977
|
-
columns: a.columns
|
|
1978
|
-
};
|
|
1979
|
-
return layoutTextSvg(input, a.measure);
|
|
1980
|
-
};
|
|
1981
|
-
var breakPiece = () => ({
|
|
1982
|
-
text: "",
|
|
1983
|
-
family: "",
|
|
1984
|
-
sizePx: 0,
|
|
1985
|
-
bold: false,
|
|
1986
|
-
italic: false,
|
|
1987
|
-
letterSpacingPx: 0,
|
|
1988
|
-
fillHex: "#000000",
|
|
1989
|
-
underline: false,
|
|
1990
|
-
strike: false,
|
|
1991
|
-
superSub: 0,
|
|
1992
|
-
href: null,
|
|
1993
|
-
isBreak: true
|
|
1994
|
-
});
|
|
1995
|
-
var buildBullet = (a, para, pi) => {
|
|
1996
|
-
const explicitChar = para.bulletStyle !== null && typeof para.bulletStyle === "object" && "char" in para.bulletStyle ? para.bulletStyle.char : null;
|
|
1997
|
-
const numberLabel = a.numberLabels[pi] ?? null;
|
|
1998
|
-
const showBullet = para.bulletStyle === "bullet" || explicitChar !== null || numberLabel !== null || para.bulletIsPicture || para.bulletStyle !== "none" && para.level > 0;
|
|
1999
|
-
if (!showBullet) return null;
|
|
2000
|
-
const char = para.bulletIsPicture ? "\u25A0" : numberLabel ?? explicitChar ?? bulletChar(para.level);
|
|
2001
|
-
const baseSizePx = a.defaultPt * PX_PER_PT * a.autoFitScale;
|
|
2002
|
-
const sizePx = para.bulletDetail.sizePct !== null ? baseSizePx * para.bulletDetail.sizePct : para.bulletDetail.sizePts !== null ? para.bulletDetail.sizePts * PX_PER_PT * a.autoFitScale : baseSizePx;
|
|
2003
|
-
const fillHex = para.bulletDetail.color ? resolveColor(para.bulletDetail.color, a.theme, "#000000") : a.defaultColor;
|
|
2004
|
-
return {
|
|
2005
|
-
text: char,
|
|
2006
|
-
family: substituteFamily(para.bulletDetail.font ?? a.themeFace),
|
|
2007
|
-
sizePx,
|
|
2008
|
-
fillHex,
|
|
2009
|
-
...para.bulletImageHref ? { imageHref: para.bulletImageHref } : {}
|
|
2010
|
-
};
|
|
2011
|
-
};
|
|
2012
|
-
var renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
2013
|
-
let paragraphCount;
|
|
2014
|
-
try {
|
|
2015
|
-
paragraphCount = getShapeParagraphCount(shape);
|
|
2016
|
-
} catch {
|
|
2017
|
-
return "";
|
|
2018
|
-
}
|
|
2019
|
-
if (paragraphCount === 0) return "";
|
|
2020
|
-
const defaultPt = placeholderDefaultPt(phType);
|
|
2021
|
-
const themeFonts = getPresentationFonts(pres);
|
|
2022
|
-
const isTitlePlaceholder = phType === "title" || phType === "ctrTitle";
|
|
2023
|
-
const themeFace = isTitlePlaceholder ? themeFonts?.majorLatin ?? null : themeFonts?.minorLatin ?? null;
|
|
2024
|
-
const effectiveDefaultFont = themeFace ? `${escapeXml2(themeFace)}, ${DEFAULT_FONT}` : DEFAULT_FONT;
|
|
2025
|
-
let effectiveBody;
|
|
2026
|
-
try {
|
|
2027
|
-
effectiveBody = getShapeBodyPrEffective(pres, shape);
|
|
2028
|
-
} catch {
|
|
2029
|
-
effectiveBody = {
|
|
2030
|
-
anchor: getShapeTextAnchor(shape),
|
|
2031
|
-
wrap: null,
|
|
2032
|
-
vert: getShapeTextDirection(shape),
|
|
2033
|
-
margins: getShapeTextMargins(shape) ?? { left: null, top: null, right: null, bottom: null }
|
|
2034
|
-
};
|
|
2035
|
-
}
|
|
2036
|
-
const isAutoshape = !isShapePlaceholder(shape) && !isShapeTextBox(shape);
|
|
2037
|
-
const defaultAlign = isAutoshape ? "center" : "left";
|
|
2038
|
-
const defaultAnchor = isAutoshape ? "center" : "top";
|
|
2039
|
-
const anchor = effectiveBody.anchor ?? defaultAnchor;
|
|
2040
|
-
const margins = effectiveBody.margins;
|
|
2041
|
-
const lIns = margins.left ?? DEFAULT_INSET_X;
|
|
2042
|
-
const tIns = margins.top ?? DEFAULT_INSET_Y;
|
|
2043
|
-
const rIns = margins.right ?? DEFAULT_INSET_X;
|
|
2044
|
-
const bIns = margins.bottom ?? DEFAULT_INSET_Y;
|
|
2045
|
-
const innerX = bounds.x + lIns;
|
|
2046
|
-
const innerY = bounds.y + tIns;
|
|
2047
|
-
const innerW = Math.max(0, bounds.w - lIns - rIns);
|
|
2048
|
-
const innerH = Math.max(0, bounds.h - tIns - bIns);
|
|
2049
|
-
if (innerW <= 0 || innerH <= 0) return "";
|
|
2050
|
-
const paraData = [];
|
|
2051
|
-
let hasAnyText = false;
|
|
2052
|
-
for (let p = 0; p < paragraphCount; p++) {
|
|
2053
|
-
let effective;
|
|
2054
|
-
try {
|
|
2055
|
-
effective = getParagraphPropertiesEffective(pres, shape, p);
|
|
2056
|
-
} catch {
|
|
2057
|
-
effective = {
|
|
2058
|
-
align: getParagraphAlignment(shape, p),
|
|
2059
|
-
level: getParagraphLevel(shape, p),
|
|
2060
|
-
marL: null,
|
|
2061
|
-
marR: null,
|
|
2062
|
-
indent: null,
|
|
2063
|
-
lineSpacing: null,
|
|
2064
|
-
spcBefPts: null,
|
|
2065
|
-
spcAftPts: null,
|
|
2066
|
-
rtl: null,
|
|
2067
|
-
bullet: null
|
|
2068
|
-
};
|
|
2069
|
-
}
|
|
2070
|
-
const align = effective.align ?? defaultAlign;
|
|
2071
|
-
const level = effective.level;
|
|
2072
|
-
const bulletStyle = getParagraphBullet(shape, p) ?? effective.bullet;
|
|
2073
|
-
let bulletDetail = {
|
|
2074
|
-
color: null,
|
|
2075
|
-
sizePct: null,
|
|
2076
|
-
sizePts: null,
|
|
2077
|
-
font: null
|
|
2078
|
-
};
|
|
2079
|
-
try {
|
|
2080
|
-
bulletDetail = getParagraphBulletStyle(pres, shape, p);
|
|
2081
|
-
} catch {
|
|
2082
|
-
}
|
|
2083
|
-
let bulletIsPicture = false;
|
|
2084
|
-
try {
|
|
2085
|
-
bulletIsPicture = isParagraphBulletPicture(shape, p);
|
|
2086
|
-
} catch {
|
|
2087
|
-
}
|
|
2088
|
-
let bulletImageHref = null;
|
|
2089
|
-
if (bulletIsPicture) {
|
|
2090
|
-
try {
|
|
2091
|
-
const bytes = getParagraphBulletImageBytes(shape, p);
|
|
2092
|
-
if (bytes) bulletImageHref = bytesToDataUrl(bytes);
|
|
2093
|
-
} catch {
|
|
2094
|
-
}
|
|
2095
|
-
}
|
|
2096
|
-
const lineSpacing = effective.lineSpacing;
|
|
2097
|
-
let spcBefPts = effective.spcBefPts;
|
|
2098
|
-
let spcAftPts = effective.spcAftPts;
|
|
2099
|
-
const indent = {
|
|
2100
|
-
leftEmu: effective.marL,
|
|
2101
|
-
rightEmu: effective.marR,
|
|
2102
|
-
firstLineEmu: effective.indent
|
|
2103
|
-
};
|
|
2104
|
-
try {
|
|
2105
|
-
const spacing = getParagraphSpacing(shape, p);
|
|
2106
|
-
if (spacing.beforePts !== null) spcBefPts = spacing.beforePts;
|
|
2107
|
-
if (spacing.afterPts !== null) spcAftPts = spacing.afterPts;
|
|
2108
|
-
} catch {
|
|
2109
|
-
}
|
|
2110
|
-
try {
|
|
2111
|
-
const lit = getParagraphIndent(shape, p);
|
|
2112
|
-
if (lit.leftEmu !== null) indent.leftEmu = lit.leftEmu;
|
|
2113
|
-
if (lit.rightEmu !== null) indent.rightEmu = lit.rightEmu;
|
|
2114
|
-
if (lit.firstLineEmu !== null) indent.firstLineEmu = lit.firstLineEmu;
|
|
2115
|
-
} catch {
|
|
2116
|
-
}
|
|
2117
|
-
const runs = [];
|
|
2118
|
-
let elements = [];
|
|
2119
|
-
try {
|
|
2120
|
-
elements = getShapeParagraphElements(shape, p);
|
|
2121
|
-
} catch {
|
|
2122
|
-
elements = [];
|
|
2123
|
-
}
|
|
2124
|
-
let rIdx = 0;
|
|
2125
|
-
for (const el of elements) {
|
|
2126
|
-
if (el.kind === "br") {
|
|
2127
|
-
runs.push({ text: "\n", fmt: null, sizePt: defaultPt });
|
|
2128
|
-
continue;
|
|
2129
|
-
}
|
|
2130
|
-
const txt = el.text;
|
|
2131
|
-
let fmt2 = el.format;
|
|
2132
|
-
let href;
|
|
2133
|
-
let hrefTip;
|
|
2134
|
-
if (el.kind === "r") {
|
|
2135
|
-
try {
|
|
2136
|
-
fmt2 = getShapeRunFormatEffective(pres, shape, p, rIdx);
|
|
2137
|
-
} catch {
|
|
2138
|
-
fmt2 = getShapeRunFormat(shape, p, rIdx);
|
|
2139
|
-
}
|
|
2140
|
-
try {
|
|
2141
|
-
href = getShapeRunHyperlink(shape, p, rIdx) ?? void 0;
|
|
2142
|
-
if (!href) {
|
|
2143
|
-
const act = getShapeRunClickAction(shape, p, rIdx);
|
|
2144
|
-
if (act?.kind === "slide") {
|
|
2145
|
-
const idx = getSlideIndex(pres, act.slide);
|
|
2146
|
-
if (idx >= 0) href = `#slide-${idx + 1}`;
|
|
2147
|
-
} else if (act?.kind === "url") {
|
|
2148
|
-
href = act.url;
|
|
2149
|
-
}
|
|
2150
|
-
}
|
|
2151
|
-
if (href) hrefTip = getShapeRunHyperlinkTooltip(shape, p, rIdx) ?? void 0;
|
|
2152
|
-
} catch {
|
|
2153
|
-
href = void 0;
|
|
2154
|
-
}
|
|
2155
|
-
rIdx++;
|
|
2156
|
-
}
|
|
2157
|
-
const sizePt = fmt2?.size ?? defaultPt;
|
|
2158
|
-
if (txt) hasAnyText = true;
|
|
2159
|
-
runs.push({
|
|
2160
|
-
text: txt,
|
|
2161
|
-
fmt: fmt2,
|
|
2162
|
-
sizePt,
|
|
2163
|
-
...href !== void 0 ? { href } : {},
|
|
2164
|
-
...hrefTip !== void 0 ? { hrefTip } : {}
|
|
2165
|
-
});
|
|
2166
|
-
}
|
|
2167
|
-
paraData.push({
|
|
2168
|
-
align,
|
|
2169
|
-
level,
|
|
2170
|
-
bulletStyle,
|
|
2171
|
-
bulletDetail,
|
|
2172
|
-
bulletIsPicture,
|
|
2173
|
-
bulletImageHref,
|
|
2174
|
-
runs,
|
|
2175
|
-
lineSpacing,
|
|
2176
|
-
spcBefPts,
|
|
2177
|
-
spcAftPts,
|
|
2178
|
-
indent
|
|
2179
|
-
});
|
|
2180
|
-
}
|
|
2181
|
-
if (!hasAnyText) return "";
|
|
2182
|
-
const authoredAutofit = getShapeTextAutoFitParams(shape);
|
|
2183
|
-
let autoFitScale = authoredAutofit?.fontScale ?? 1;
|
|
2184
|
-
1 - (authoredAutofit?.lnSpcReduction ?? 0);
|
|
2185
|
-
if (!authoredAutofit) {
|
|
2186
|
-
const innerWPx = innerW / EMU_PER_PX;
|
|
2187
|
-
const innerHPx = innerH / EMU_PER_PX;
|
|
2188
|
-
let totalH = 0;
|
|
2189
|
-
for (const para of paraData) {
|
|
2190
|
-
let maxSize = defaultPt;
|
|
2191
|
-
let totalChars = 0;
|
|
2192
|
-
let cjkChars = 0;
|
|
2193
|
-
for (const run of para.runs) {
|
|
2194
|
-
if (run.sizePt > maxSize) maxSize = run.sizePt;
|
|
2195
|
-
totalChars += run.text.length;
|
|
2196
|
-
for (let i = 0; i < run.text.length; i++) {
|
|
2197
|
-
const c = run.text.charCodeAt(i);
|
|
2198
|
-
if (c >= 12352 && c <= 12447 || c >= 12448 && c <= 12543 || c >= 19968 && c <= 40959 || c >= 44032 && c <= 55215)
|
|
2199
|
-
cjkChars++;
|
|
2200
|
-
}
|
|
2201
|
-
}
|
|
2202
|
-
if (totalChars === 0) totalChars = 1;
|
|
2203
|
-
const cjkRatio = cjkChars / totalChars;
|
|
2204
|
-
const glyphRatio = cjkRatio * 1 + (1 - cjkRatio) * AVG_GLYPH_W_RATIO2;
|
|
2205
|
-
const sizePx = maxSize * PX_PER_PT;
|
|
2206
|
-
const charsPerLine = Math.max(1, Math.floor(innerWPx / Math.max(1, sizePx * glyphRatio)));
|
|
2207
|
-
const lineCount = Math.max(1, Math.ceil(totalChars / charsPerLine));
|
|
2208
|
-
totalH += sizePx * LINE_HEIGHT * lineCount;
|
|
2209
|
-
}
|
|
2210
|
-
if (totalH > innerHPx) {
|
|
2211
|
-
autoFitScale = Math.max(0.4, innerHPx / totalH);
|
|
2212
|
-
}
|
|
2213
|
-
}
|
|
2214
|
-
const numberLabels = Array.from({ length: paraData.length }, () => null);
|
|
2215
|
-
{
|
|
2216
|
-
let counter = 0;
|
|
2217
|
-
let activeLevel = -1;
|
|
2218
|
-
let activeType = null;
|
|
2219
|
-
for (let i = 0; i < paraData.length; i++) {
|
|
2220
|
-
const para = paraData[i];
|
|
2221
|
-
const num = bulletAutoNumType(para.bulletStyle);
|
|
2222
|
-
if (num === null) {
|
|
2223
|
-
counter = 0;
|
|
2224
|
-
activeLevel = -1;
|
|
2225
|
-
activeType = null;
|
|
2226
|
-
continue;
|
|
2227
|
-
}
|
|
2228
|
-
if (para.level !== activeLevel || num !== activeType) {
|
|
2229
|
-
counter = 1;
|
|
2230
|
-
activeLevel = para.level;
|
|
2231
|
-
activeType = num;
|
|
2232
|
-
} else {
|
|
2233
|
-
counter += 1;
|
|
2234
|
-
}
|
|
2235
|
-
numberLabels[i] = formatAutoNum(num, counter);
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
const paragraphs = [];
|
|
2239
|
-
for (let pi = 0; pi < paraData.length; pi++) {
|
|
2240
|
-
const para = paraData[pi];
|
|
2241
|
-
const runHtmls = para.runs.map((run) => {
|
|
2242
|
-
let runFmt = run.fmt;
|
|
2243
|
-
if (run.href) {
|
|
2244
|
-
const hlinkColor = theme ? normalizeHex(theme.hyperlink) : "#0563C1";
|
|
2245
|
-
runFmt = {
|
|
2246
|
-
...runFmt,
|
|
2247
|
-
color: runFmt?.color ?? hlinkColor,
|
|
2248
|
-
underline: runFmt?.underline ?? true
|
|
2249
|
-
};
|
|
2250
|
-
}
|
|
2251
|
-
const span = renderRun(
|
|
2252
|
-
run.text,
|
|
2253
|
-
runFmt,
|
|
2254
|
-
theme,
|
|
2255
|
-
run.sizePt * autoFitScale,
|
|
2256
|
-
run.fmt?.size === void 0
|
|
2257
|
-
);
|
|
2258
|
-
if (!run.href) return span;
|
|
2259
|
-
const isInPage = run.href.startsWith("#");
|
|
2260
|
-
const targetAttrs = isInPage ? "" : ' target="_blank" rel="noopener noreferrer"';
|
|
2261
|
-
const titleAttr = run.hrefTip ? ` title="${escapeXml2(run.hrefTip)}"` : "";
|
|
2262
|
-
return `<a href="${escapeXml2(run.href)}"${targetAttrs}${titleAttr} style="color:inherit;text-decoration:inherit">${span}</a>`;
|
|
2263
|
-
});
|
|
2264
|
-
let lineHeightCss = "";
|
|
2265
|
-
if (para.lineSpacing?.kind === "pct") {
|
|
2266
|
-
lineHeightCss = `line-height:${para.lineSpacing.value.toFixed(3)}`;
|
|
2267
|
-
} else if (para.lineSpacing?.kind === "pts") {
|
|
2268
|
-
lineHeightCss = `line-height:${(para.lineSpacing.value * PX_PER_PT * autoFitScale).toFixed(2)}px`;
|
|
2269
|
-
}
|
|
2270
|
-
const marginTopCss = para.spcBefPts !== null && para.spcBefPts > 0 ? `margin-top:${(para.spcBefPts * PX_PER_PT * autoFitScale).toFixed(2)}px` : "";
|
|
2271
|
-
const marginBottomCss = para.spcAftPts !== null && para.spcAftPts > 0 ? `margin-bottom:${(para.spcAftPts * PX_PER_PT * autoFitScale).toFixed(2)}px` : "";
|
|
2272
|
-
const leftPx = para.indent.leftEmu !== null ? para.indent.leftEmu / EMU_PER_PX * autoFitScale : para.level > 0 ? para.level * 24 * PX_PER_PT * autoFitScale : 0;
|
|
2273
|
-
const rightPx = para.indent.rightEmu !== null ? para.indent.rightEmu / EMU_PER_PX * autoFitScale : 0;
|
|
2274
|
-
const firstLinePx = para.indent.firstLineEmu !== null ? para.indent.firstLineEmu / EMU_PER_PX * autoFitScale : 0;
|
|
2275
|
-
const pStyles = [
|
|
2276
|
-
marginTopCss || (marginBottomCss ? "" : "margin:0"),
|
|
2277
|
-
marginBottomCss,
|
|
2278
|
-
"padding:0",
|
|
2279
|
-
`text-align:${ALIGNMENT_TO_CSS[para.align] ?? "left"}`,
|
|
2280
|
-
lineHeightCss,
|
|
2281
|
-
leftPx > 0 ? `padding-left:${leftPx.toFixed(2)}px` : "",
|
|
2282
|
-
rightPx > 0 ? `padding-right:${rightPx.toFixed(2)}px` : "",
|
|
2283
|
-
firstLinePx !== 0 ? `text-indent:${firstLinePx.toFixed(2)}px` : ""
|
|
2284
|
-
].filter(Boolean);
|
|
2285
|
-
let prefix = "";
|
|
2286
|
-
const explicitChar = para.bulletStyle !== null && typeof para.bulletStyle === "object" && "char" in para.bulletStyle ? para.bulletStyle.char : null;
|
|
2287
|
-
const numberLabel = numberLabels[pi];
|
|
2288
|
-
const showBullet = para.bulletStyle === "bullet" || explicitChar !== null || numberLabel !== null || para.bulletIsPicture || para.bulletStyle !== "none" && para.level > 0;
|
|
2289
|
-
if (showBullet && para.bulletImageHref) {
|
|
2290
|
-
const baseBulletPx = defaultPt * PX_PER_PT * autoFitScale;
|
|
2291
|
-
const bulletPx = para.bulletDetail.sizePct !== null ? baseBulletPx * para.bulletDetail.sizePct : para.bulletDetail.sizePts !== null ? para.bulletDetail.sizePts * PX_PER_PT * autoFitScale : baseBulletPx;
|
|
2292
|
-
const imgStyles = [
|
|
2293
|
-
`width:${bulletPx.toFixed(2)}px`,
|
|
2294
|
-
`height:${bulletPx.toFixed(2)}px`,
|
|
2295
|
-
"display:inline-block",
|
|
2296
|
-
"vertical-align:baseline",
|
|
2297
|
-
`margin-right:${(0.4 * defaultPt * PX_PER_PT * autoFitScale).toFixed(2)}px`
|
|
2298
|
-
];
|
|
2299
|
-
prefix = `<img src="${para.bulletImageHref}" alt="" style="${imgStyles.join(";")}"/>`;
|
|
2300
|
-
} else if (showBullet) {
|
|
2301
|
-
const char = para.bulletIsPicture ? "\u25A0" : numberLabel ?? explicitChar ?? bulletChar(para.level);
|
|
2302
|
-
const bulletStyles = [
|
|
2303
|
-
`margin-right:${(0.4 * defaultPt * PX_PER_PT * autoFitScale).toFixed(2)}px`
|
|
2304
|
-
];
|
|
2305
|
-
if (para.bulletDetail.color) {
|
|
2306
|
-
bulletStyles.push(`color:${resolveColor(para.bulletDetail.color, theme, "#000000")}`);
|
|
2307
|
-
}
|
|
2308
|
-
if (para.bulletDetail.sizePct !== null) {
|
|
2309
|
-
bulletStyles.push(`font-size:${(para.bulletDetail.sizePct * 100).toFixed(1)}%`);
|
|
2310
|
-
} else if (para.bulletDetail.sizePts !== null) {
|
|
2311
|
-
bulletStyles.push(
|
|
2312
|
-
`font-size:${(para.bulletDetail.sizePts * PX_PER_PT * autoFitScale).toFixed(2)}px`
|
|
2313
|
-
);
|
|
2314
|
-
}
|
|
2315
|
-
if (para.bulletDetail.font) {
|
|
2316
|
-
bulletStyles.push(`font-family:${escapeXml2(para.bulletDetail.font)}, ${DEFAULT_FONT}`);
|
|
2317
|
-
}
|
|
2318
|
-
prefix = `<span style="${bulletStyles.join(";")}">${escapeXml2(char)}</span>`;
|
|
2319
|
-
}
|
|
2320
|
-
paragraphs.push(
|
|
2321
|
-
`<p style="${pStyles.join(";")}">${prefix}${runHtmls.join("") || "​"}</p>`
|
|
2322
|
-
);
|
|
2323
|
-
}
|
|
2324
|
-
const justify = ANCHOR_TO_CSS[anchor] ?? "flex-start";
|
|
2325
|
-
const defaultColor = activeDeckTextColor;
|
|
2326
|
-
if (ctx.mode === "svg") {
|
|
2327
|
-
const svgScale = authoredAutofit?.fontScale ?? 1;
|
|
2328
|
-
const svgLineScale = 1 - (authoredAutofit?.lnSpcReduction ?? 0);
|
|
2329
|
-
const svgVert = verticalLayoutOf(effectiveBody.vert ?? getShapeTextDirection(shape));
|
|
2330
|
-
const svgCols = getShapeTextColumns(shape);
|
|
2331
|
-
const svgColumns = svgVert === "none" && svgCols && svgCols.count >= 2 ? {
|
|
2332
|
-
count: svgCols.count,
|
|
2333
|
-
// spcCol is in EMU; default to the foreignObject path's 12px gap.
|
|
2334
|
-
gapPx: svgCols.gapEmu !== void 0 ? svgCols.gapEmu / EMU_PER_PX : 12
|
|
2335
|
-
} : null;
|
|
2336
|
-
const svgInner = buildAndLayoutSvgText({
|
|
2337
|
-
theme,
|
|
2338
|
-
paraData,
|
|
2339
|
-
numberLabels,
|
|
2340
|
-
autoFitScale: svgScale,
|
|
2341
|
-
lineHeightScale: svgLineScale,
|
|
2342
|
-
defaultPt,
|
|
2343
|
-
themeFace,
|
|
2344
|
-
defaultColor,
|
|
2345
|
-
anchor: anchor === "center" || anchor === "bottom" ? anchor : "top",
|
|
2346
|
-
wrap: effectiveBody.wrap !== "none",
|
|
2347
|
-
innerX,
|
|
2348
|
-
innerY,
|
|
2349
|
-
innerW,
|
|
2350
|
-
innerH,
|
|
2351
|
-
measure: ctx.measure,
|
|
2352
|
-
vert: svgVert,
|
|
2353
|
-
columns: svgColumns
|
|
2354
|
-
});
|
|
2355
|
-
const bodyRotDegSvg = getShapeTextBodyRotationDeg(shape);
|
|
2356
|
-
if (bodyRotDegSvg !== null && bodyRotDegSvg !== 0) {
|
|
2357
|
-
const pivotX = innerX + innerW / 2;
|
|
2358
|
-
const pivotY = innerY + innerH / 2;
|
|
2359
|
-
return `<g transform="rotate(${bodyRotDegSvg} ${E(pivotX)} ${E(pivotY)})">${svgInner}</g>`;
|
|
2360
|
-
}
|
|
2361
|
-
return svgInner;
|
|
2362
|
-
}
|
|
2363
|
-
const vert = effectiveBody.vert ?? getShapeTextDirection(shape);
|
|
2364
|
-
let writingMode = "";
|
|
2365
|
-
let extraTransform = "";
|
|
2366
|
-
if (vert === "vert" || vert === "eaVert") {
|
|
2367
|
-
writingMode = "writing-mode:vertical-rl";
|
|
2368
|
-
} else if (vert === "vert270" || vert === "mongolianVert") {
|
|
2369
|
-
writingMode = "writing-mode:vertical-lr";
|
|
2370
|
-
if (vert === "vert270") extraTransform = ";transform:rotate(180deg)";
|
|
2371
|
-
} else if (vert === "wordArtVert") {
|
|
2372
|
-
writingMode = "writing-mode:vertical-rl;text-orientation:upright";
|
|
2373
|
-
} else if (vert === "wordArtVertRtl") {
|
|
2374
|
-
writingMode = "writing-mode:vertical-rl;text-orientation:upright;direction:rtl";
|
|
2375
|
-
}
|
|
2376
|
-
const cols = getShapeTextColumns(shape);
|
|
2377
|
-
let colStyles = "";
|
|
2378
|
-
if (cols && cols.count >= 2) {
|
|
2379
|
-
const gapPx = cols.gapEmu !== void 0 ? (cols.gapEmu / EMU_PER_PX).toFixed(2) : "12";
|
|
2380
|
-
colStyles = `;column-count:${cols.count};column-gap:${gapPx}px`;
|
|
2381
|
-
}
|
|
2382
|
-
const vertStyles = (writingMode ? `;${writingMode}${extraTransform}` : "") + colStyles;
|
|
2383
|
-
const wrapStyle = effectiveBody.wrap === "none" ? "white-space:nowrap" : "word-break:break-word";
|
|
2384
|
-
const body = `<div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;flex-direction:column;justify-content:${justify};width:100%;height:100%;box-sizing:border-box;overflow:visible;font-family:${effectiveDefaultFont};color:${defaultColor};${wrapStyle}${vertStyles}">${paragraphs.join("")}</div>`;
|
|
2385
|
-
const foreign = `<foreignObject x="${E(innerX)}" y="${E(innerY)}" width="${E(innerW)}" height="${E(innerH)}" overflow="visible">${body}</foreignObject>`;
|
|
2386
|
-
const bodyRotDeg = getShapeTextBodyRotationDeg(shape);
|
|
2387
|
-
if (bodyRotDeg !== null && bodyRotDeg !== 0) {
|
|
2388
|
-
const pivotX = innerX + innerW / 2;
|
|
2389
|
-
const pivotY = innerY + innerH / 2;
|
|
2390
|
-
return `<g transform="rotate(${bodyRotDeg} ${E(pivotX)} ${E(pivotY)})">${foreign}</g>`;
|
|
2391
|
-
}
|
|
2392
|
-
return foreign;
|
|
2393
|
-
};
|
|
2394
|
-
var E = (n) => (n / EMU_PER_PX).toFixed(2);
|
|
2395
|
-
var px = (n) => n.toFixed(2);
|
|
2396
|
-
var accentSequence = (theme) => {
|
|
2397
|
-
const fallbacks = ["#5B9BD5", "#ED7D31", "#A5A5A5", "#FFC000", "#4472C4", "#70AD47"];
|
|
2398
|
-
if (!theme) return fallbacks;
|
|
2399
|
-
const hexes = [
|
|
2400
|
-
theme.accent1,
|
|
2401
|
-
theme.accent2,
|
|
2402
|
-
theme.accent3,
|
|
2403
|
-
theme.accent4,
|
|
2404
|
-
theme.accent5,
|
|
2405
|
-
theme.accent6
|
|
2406
|
-
].map((c) => normalizeHex(c)).filter((c) => /^#[0-9A-Fa-f]{6}$/.test(c));
|
|
2407
|
-
return hexes.length > 0 ? hexes : fallbacks;
|
|
2408
|
-
};
|
|
2409
|
-
var layoutChart = (xEmu, yEmu, wEmu, hEmu, hasTitle, hasAxes, titleOverlay = false, legendOverlay = false, hasLegend = true) => {
|
|
2410
|
-
const x = xEmu / EMU_PER_PX;
|
|
2411
|
-
const y = yEmu / EMU_PER_PX;
|
|
2412
|
-
const w = wEmu / EMU_PER_PX;
|
|
2413
|
-
const h = hEmu / EMU_PER_PX;
|
|
2414
|
-
const titleStrip = hasTitle && !titleOverlay ? 18 : 0;
|
|
2415
|
-
const legendStrip = hasLegend && !legendOverlay ? 18 : 0;
|
|
2416
|
-
const padding = 8;
|
|
2417
|
-
const yAxisGutter = hasAxes ? 40 : 0;
|
|
2418
|
-
const xAxisGutter = hasAxes ? 18 : 0;
|
|
2419
|
-
return {
|
|
2420
|
-
x,
|
|
2421
|
-
y,
|
|
2422
|
-
w,
|
|
2423
|
-
h,
|
|
2424
|
-
plotX: x + padding + yAxisGutter,
|
|
2425
|
-
plotY: y + titleStrip + padding,
|
|
2426
|
-
plotW: Math.max(0, w - 2 * padding - yAxisGutter),
|
|
2427
|
-
plotH: Math.max(0, h - titleStrip - legendStrip - xAxisGutter - 2 * padding),
|
|
2428
|
-
titleY: y + (titleOverlay ? 14 : titleStrip - 2),
|
|
2429
|
-
legendY: y + h - (legendOverlay ? 18 : legendStrip / 2)
|
|
2430
|
-
};
|
|
2431
|
-
};
|
|
2432
|
-
var niceStep = (range, target = 5) => {
|
|
2433
|
-
if (range <= 0) return 1;
|
|
2434
|
-
const rawStep = range / target;
|
|
2435
|
-
const exp = Math.floor(Math.log10(rawStep));
|
|
2436
|
-
const base = Math.pow(10, exp);
|
|
2437
|
-
const m = rawStep / base;
|
|
2438
|
-
const stepMultiplier = m < 1.5 ? 1 : m < 3 ? 2 : m < 7 ? 5 : 10;
|
|
2439
|
-
return stepMultiplier * base;
|
|
2440
|
-
};
|
|
2441
|
-
var niceTicks = (min, max, target = 5) => {
|
|
2442
|
-
const range = max - min;
|
|
2443
|
-
if (range <= 0) return [min];
|
|
2444
|
-
const step = niceStep(range, target);
|
|
2445
|
-
const start = Math.ceil(min / step) * step;
|
|
2446
|
-
const ticks = [];
|
|
2447
|
-
for (let v = start; v <= max + step / 2; v += step) ticks.push(v);
|
|
2448
|
-
return ticks;
|
|
2449
|
-
};
|
|
2450
|
-
var formatTick = (v) => {
|
|
2451
|
-
if (v === 0) return "0";
|
|
2452
|
-
const abs = Math.abs(v);
|
|
2453
|
-
if (abs >= 1e9) return `${(v / 1e9).toFixed(1)}B`;
|
|
2454
|
-
if (abs >= 1e6) return `${(v / 1e6).toFixed(1)}M`;
|
|
2455
|
-
if (abs >= 1e4) return `${(v / 1e3).toFixed(0)}K`;
|
|
2456
|
-
if (abs >= 1e3) return `${(v / 1e3).toFixed(1)}K`;
|
|
2457
|
-
if (Number.isInteger(v)) return v.toString();
|
|
2458
|
-
return v.toFixed(abs < 1 ? 2 : 1);
|
|
2459
|
-
};
|
|
2460
|
-
var formatAxisLabel = (v, formatCode) => {
|
|
2461
|
-
if (!formatCode) return formatTick(v);
|
|
2462
|
-
let prefix = "";
|
|
2463
|
-
let suffix = "";
|
|
2464
|
-
let body = formatCode;
|
|
2465
|
-
const leadMatch = /^"((?:\\.|[^"\\])*)"/.exec(body);
|
|
2466
|
-
if (leadMatch) {
|
|
2467
|
-
prefix = leadMatch[1].replace(/\\(.)/g, "$1");
|
|
2468
|
-
body = body.slice(leadMatch[0].length);
|
|
2469
|
-
} else if (body.startsWith("$") || body.startsWith("\xA5") || /^[£€]/.test(body)) {
|
|
2470
|
-
prefix = body[0];
|
|
2471
|
-
body = body.slice(1);
|
|
2472
|
-
}
|
|
2473
|
-
const tailMatch = /"((?:\\.|[^"\\])*)"$/.exec(body);
|
|
2474
|
-
if (tailMatch) {
|
|
2475
|
-
suffix = tailMatch[1].replace(/\\(.)/g, "$1");
|
|
2476
|
-
body = body.slice(0, body.length - tailMatch[0].length);
|
|
2477
|
-
}
|
|
2478
|
-
if (body.includes("%")) {
|
|
2479
|
-
const decMatch = /0\.(0+)%/.exec(body);
|
|
2480
|
-
const dec = decMatch ? decMatch[1].length : 0;
|
|
2481
|
-
return prefix + `${(v * 100).toFixed(dec)}%` + suffix;
|
|
2482
|
-
}
|
|
2483
|
-
if (body.includes("#,##") || /\.(0+)/.test(body) || /^0+$/.test(body)) {
|
|
2484
|
-
return prefix + formatWithGrouping(v, body) + suffix;
|
|
2485
|
-
}
|
|
2486
|
-
return prefix + formatTick(v) + suffix;
|
|
2487
|
-
};
|
|
2488
|
-
var formatWithGrouping = (v, fmt2) => {
|
|
2489
|
-
const decMatch = /\.(0+)/.exec(fmt2);
|
|
2490
|
-
const dec = decMatch ? decMatch[1].length : 0;
|
|
2491
|
-
const fixed = Math.abs(v).toFixed(dec);
|
|
2492
|
-
const [intPart, fracPart] = fixed.split(".");
|
|
2493
|
-
const grouped = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
2494
|
-
const sign = v < 0 ? "-" : "";
|
|
2495
|
-
return sign + grouped + (fracPart ? `.${fracPart}` : "");
|
|
2496
|
-
};
|
|
2497
|
-
var DISPLAY_UNIT_DIVISOR = {
|
|
2498
|
-
hundreds: 100,
|
|
2499
|
-
thousands: 1e3,
|
|
2500
|
-
tenThousands: 1e4,
|
|
2501
|
-
hundredThousands: 1e5,
|
|
2502
|
-
millions: 1e6,
|
|
2503
|
-
tenMillions: 1e7,
|
|
2504
|
-
hundredMillions: 1e8,
|
|
2505
|
-
billions: 1e9,
|
|
2506
|
-
trillions: 1e12
|
|
2507
|
-
};
|
|
2508
|
-
var DISPLAY_UNIT_LABEL = {
|
|
2509
|
-
hundreds: "Hundreds",
|
|
2510
|
-
thousands: "Thousands",
|
|
2511
|
-
tenThousands: "Ten Thousands",
|
|
2512
|
-
hundredThousands: "Hundred Thousands",
|
|
2513
|
-
millions: "Millions",
|
|
2514
|
-
tenMillions: "Ten Millions",
|
|
2515
|
-
hundredMillions: "Hundred Millions",
|
|
2516
|
-
billions: "Billions",
|
|
2517
|
-
trillions: "Trillions"
|
|
2518
|
-
};
|
|
2519
|
-
var axisTickAttrs = (style) => {
|
|
2520
|
-
const sz = style?.sizePt ?? 10;
|
|
2521
|
-
const fill = style?.color ?? "#6B7280";
|
|
2522
|
-
const weight = style?.bold ? ' font-weight="600"' : "";
|
|
2523
|
-
const italic = style?.italic ? ' font-style="italic"' : "";
|
|
2524
|
-
return `font-family="sans-serif" font-size="${sz.toFixed(1)}" fill="${fill}"${weight}${italic}`;
|
|
2525
|
-
};
|
|
2526
|
-
var renderValueAxis = (f, axis) => {
|
|
2527
|
-
const ticks = axis.majorUnit ? (() => {
|
|
2528
|
-
const out2 = [];
|
|
2529
|
-
const start = Math.ceil(axis.min / axis.majorUnit) * axis.majorUnit;
|
|
2530
|
-
for (let t = start; t <= axis.max + 1e-9; t += axis.majorUnit) out2.push(t);
|
|
2531
|
-
return out2.length > 0 ? out2 : niceTicks(axis.min, axis.max);
|
|
2532
|
-
})() : niceTicks(axis.min, axis.max);
|
|
2533
|
-
const out = [];
|
|
2534
|
-
const range = axis.max - axis.min || 1;
|
|
2535
|
-
const showGrid = axis.majorGridlines ?? true;
|
|
2536
|
-
const gridStroke = axis.majorGridlineColor ?? "#E5E7EB";
|
|
2537
|
-
const tickMark = axis.majorTickMark ?? "out";
|
|
2538
|
-
const tickLen = 3;
|
|
2539
|
-
for (const t of ticks) {
|
|
2540
|
-
if (axis.orientation === "vertical") {
|
|
2541
|
-
const yp = f.plotY + f.plotH - (t - axis.min) / range * f.plotH;
|
|
2542
|
-
if (showGrid) {
|
|
2543
|
-
out.push(
|
|
2544
|
-
`<line x1="${px(f.plotX)}" y1="${px(yp)}" x2="${px(f.plotX + f.plotW)}" y2="${px(yp)}" stroke="${gridStroke}" stroke-width="0.5"/>`
|
|
2545
|
-
);
|
|
2546
|
-
}
|
|
2547
|
-
if (tickMark !== "none") {
|
|
2548
|
-
const tx1 = tickMark === "in" ? f.plotX : f.plotX - tickLen;
|
|
2549
|
-
const tx2 = tickMark === "out" ? f.plotX : tickMark === "cross" ? f.plotX + tickLen : f.plotX + tickLen;
|
|
2550
|
-
out.push(
|
|
2551
|
-
`<line x1="${px(tx1)}" y1="${px(yp)}" x2="${px(tx2)}" y2="${px(yp)}" stroke="#9CA3AF" stroke-width="0.5"/>`
|
|
2552
|
-
);
|
|
2553
|
-
}
|
|
2554
|
-
const labelX = f.plotX - 4;
|
|
2555
|
-
const rot = axis.labelRotationDeg ?? 0;
|
|
2556
|
-
const transform = rot ? ` transform="rotate(${rot} ${px(labelX)} ${px(yp)})"` : "";
|
|
2557
|
-
out.push(
|
|
2558
|
-
`<text x="${px(labelX)}" y="${px(yp)}" text-anchor="end" dominant-baseline="middle" ${axisTickAttrs(axis.labelStyle)}${transform}>${escapeXml2(
|
|
2559
|
-
formatAxisLabel(
|
|
2560
|
-
axis.displayUnits ? t / DISPLAY_UNIT_DIVISOR[axis.displayUnits] : t,
|
|
2561
|
-
axis.numberFormat
|
|
2562
|
-
)
|
|
2563
|
-
)}</text>`
|
|
2564
|
-
);
|
|
2565
|
-
} else {
|
|
2566
|
-
const xp = f.plotX + (t - axis.min) / range * f.plotW;
|
|
2567
|
-
if (showGrid) {
|
|
2568
|
-
out.push(
|
|
2569
|
-
`<line x1="${px(xp)}" y1="${px(f.plotY)}" x2="${px(xp)}" y2="${px(f.plotY + f.plotH)}" stroke="${gridStroke}" stroke-width="0.5"/>`
|
|
2570
|
-
);
|
|
2571
|
-
}
|
|
2572
|
-
if (tickMark !== "none") {
|
|
2573
|
-
const baseY = f.plotY + f.plotH;
|
|
2574
|
-
const ty1 = tickMark === "in" ? baseY : baseY + tickLen;
|
|
2575
|
-
const ty2 = tickMark === "out" ? baseY : tickMark === "cross" ? baseY - tickLen : baseY - tickLen;
|
|
2576
|
-
out.push(
|
|
2577
|
-
`<line x1="${px(xp)}" y1="${px(ty1)}" x2="${px(xp)}" y2="${px(ty2)}" stroke="#9CA3AF" stroke-width="0.5"/>`
|
|
2578
|
-
);
|
|
2579
|
-
}
|
|
2580
|
-
const horizLabelY = f.plotY + f.plotH + 12;
|
|
2581
|
-
const rotH = axis.labelRotationDeg ?? 0;
|
|
2582
|
-
const transformH = rotH ? ` transform="rotate(${rotH} ${px(xp)} ${px(horizLabelY)})"` : "";
|
|
2583
|
-
out.push(
|
|
2584
|
-
`<text x="${px(xp)}" y="${px(horizLabelY)}" text-anchor="middle" dominant-baseline="middle" ${axisTickAttrs(axis.labelStyle)}${transformH}>${escapeXml2(
|
|
2585
|
-
formatAxisLabel(
|
|
2586
|
-
axis.displayUnits ? t / DISPLAY_UNIT_DIVISOR[axis.displayUnits] : t,
|
|
2587
|
-
axis.numberFormat
|
|
2588
|
-
)
|
|
2589
|
-
)}</text>`
|
|
2590
|
-
);
|
|
2591
|
-
}
|
|
2592
|
-
}
|
|
2593
|
-
if (axis.displayUnits) {
|
|
2594
|
-
const lbl = DISPLAY_UNIT_LABEL[axis.displayUnits];
|
|
2595
|
-
if (axis.orientation === "vertical") {
|
|
2596
|
-
const lblX = f.plotX - 26;
|
|
2597
|
-
const lblY = f.plotY + f.plotH / 2;
|
|
2598
|
-
out.push(
|
|
2599
|
-
`<text x="${px(lblX)}" y="${px(lblY)}" text-anchor="middle" font-family="sans-serif" font-size="9" fill="#6B7280" font-style="italic" transform="rotate(-90 ${px(lblX)} ${px(lblY)})">${escapeXml2(lbl)}</text>`
|
|
2600
|
-
);
|
|
2601
|
-
} else {
|
|
2602
|
-
out.push(
|
|
2603
|
-
`<text x="${px(f.plotX + f.plotW)}" y="${px(f.plotY + f.plotH + 22)}" text-anchor="end" font-family="sans-serif" font-size="9" fill="#6B7280" font-style="italic">${escapeXml2(lbl)}</text>`
|
|
2604
|
-
);
|
|
2605
|
-
}
|
|
2606
|
-
}
|
|
2607
|
-
if (axis.lineColor !== void 0) {
|
|
2608
|
-
if (axis.orientation === "vertical") {
|
|
2609
|
-
out.push(
|
|
2610
|
-
`<line x1="${px(f.plotX)}" y1="${px(f.plotY)}" x2="${px(f.plotX)}" y2="${px(f.plotY + f.plotH)}" stroke="${axis.lineColor}" stroke-width="0.75"/>`
|
|
2611
|
-
);
|
|
2612
|
-
} else {
|
|
2613
|
-
out.push(
|
|
2614
|
-
`<line x1="${px(f.plotX)}" y1="${px(f.plotY + f.plotH)}" x2="${px(f.plotX + f.plotW)}" y2="${px(f.plotY + f.plotH)}" stroke="${axis.lineColor}" stroke-width="0.75"/>`
|
|
2615
|
-
);
|
|
2616
|
-
}
|
|
2617
|
-
}
|
|
2618
|
-
return out.join("");
|
|
2619
|
-
};
|
|
2620
|
-
var renderCategoryAxis = (f, orientation, cats, pointCount2, skip = 1, labelStyle, labelRotationDeg, labelAlign, lineColor) => {
|
|
2621
|
-
const labels = [];
|
|
2622
|
-
for (let i = 0; i < pointCount2; i++) {
|
|
2623
|
-
labels.push(cats[i] ?? (i + 1).toString());
|
|
2624
|
-
}
|
|
2625
|
-
const out = [];
|
|
2626
|
-
if (orientation === "horizontal") {
|
|
2627
|
-
const step = pointCount2 > 1 ? f.plotW / pointCount2 : 0;
|
|
2628
|
-
const truncLen = labelRotationDeg && Math.abs(labelRotationDeg) >= 30 ? 28 : 14;
|
|
2629
|
-
for (let i = 0; i < pointCount2; i++) {
|
|
2630
|
-
if (skip > 1 && i % skip !== 0) continue;
|
|
2631
|
-
const cx = f.plotX + (i + 0.5) * step;
|
|
2632
|
-
const cy = f.plotY + f.plotH + 12;
|
|
2633
|
-
const truncated = labels[i] !== void 0 && labels[i].length > truncLen ? `${labels[i].slice(0, truncLen - 2)}\u2026` : labels[i] ?? "";
|
|
2634
|
-
const transform = labelRotationDeg && labelRotationDeg !== 0 ? ` transform="rotate(${labelRotationDeg} ${px(cx)} ${px(cy)})"` : "";
|
|
2635
|
-
const anchor = labelAlign === "l" ? "start" : labelAlign === "r" ? "end" : labelAlign === "ctr" ? "middle" : labelRotationDeg && labelRotationDeg > 0 ? "end" : labelRotationDeg && labelRotationDeg < 0 ? "start" : "middle";
|
|
2636
|
-
out.push(
|
|
2637
|
-
`<text x="${px(cx)}" y="${px(cy)}" text-anchor="${anchor}" dominant-baseline="middle" ${axisTickAttrs(labelStyle)}${transform}>${escapeXml2(truncated)}</text>`
|
|
2638
|
-
);
|
|
2639
|
-
}
|
|
2640
|
-
} else {
|
|
2641
|
-
const step = pointCount2 > 0 ? f.plotH / pointCount2 : 0;
|
|
2642
|
-
const truncLen = labelRotationDeg && Math.abs(labelRotationDeg) >= 30 ? 28 : 14;
|
|
2643
|
-
for (let i = 0; i < pointCount2; i++) {
|
|
2644
|
-
if (skip > 1 && i % skip !== 0) continue;
|
|
2645
|
-
const cy = f.plotY + (i + 0.5) * step;
|
|
2646
|
-
const lx = f.plotX - 4;
|
|
2647
|
-
const truncated = labels[i] !== void 0 && labels[i].length > truncLen ? `${labels[i].slice(0, truncLen - 2)}\u2026` : labels[i] ?? "";
|
|
2648
|
-
const transform = labelRotationDeg && labelRotationDeg !== 0 ? ` transform="rotate(${labelRotationDeg} ${px(lx)} ${px(cy)})"` : "";
|
|
2649
|
-
out.push(
|
|
2650
|
-
`<text x="${px(lx)}" y="${px(cy)}" text-anchor="end" dominant-baseline="middle" ${axisTickAttrs(labelStyle)}${transform}>${escapeXml2(truncated)}</text>`
|
|
2651
|
-
);
|
|
2652
|
-
}
|
|
2653
|
-
}
|
|
2654
|
-
if (lineColor !== void 0) {
|
|
2655
|
-
if (orientation === "horizontal") {
|
|
2656
|
-
out.push(
|
|
2657
|
-
`<line x1="${px(f.plotX)}" y1="${px(f.plotY + f.plotH)}" x2="${px(f.plotX + f.plotW)}" y2="${px(f.plotY + f.plotH)}" stroke="${lineColor}" stroke-width="0.75"/>`
|
|
2658
|
-
);
|
|
2659
|
-
} else {
|
|
2660
|
-
out.push(
|
|
2661
|
-
`<line x1="${px(f.plotX)}" y1="${px(f.plotY)}" x2="${px(f.plotX)}" y2="${px(f.plotY + f.plotH)}" stroke="${lineColor}" stroke-width="0.75"/>`
|
|
2662
|
-
);
|
|
2663
|
-
}
|
|
2664
|
-
}
|
|
2665
|
-
return out.join("");
|
|
2666
|
-
};
|
|
2667
|
-
var seriesMinMax = (spec) => {
|
|
2668
|
-
let min = Infinity;
|
|
2669
|
-
let max = -Infinity;
|
|
2670
|
-
for (const s of spec.series) {
|
|
2671
|
-
for (const v of s.values) {
|
|
2672
|
-
if (v !== null && Number.isFinite(v)) {
|
|
2673
|
-
if (v < min) min = v;
|
|
2674
|
-
if (v > max) max = v;
|
|
2675
|
-
}
|
|
2676
|
-
}
|
|
2677
|
-
}
|
|
2678
|
-
if (!Number.isFinite(min)) min = 0;
|
|
2679
|
-
if (!Number.isFinite(max)) max = 1;
|
|
2680
|
-
if (max === min) max = min + 1;
|
|
2681
|
-
if (min > 0) min = 0;
|
|
2682
|
-
const step = niceStep(max - min);
|
|
2683
|
-
max = (Math.floor(max / step) + 1) * step;
|
|
2684
|
-
if (spec.valueAxis?.min !== void 0) min = spec.valueAxis.min;
|
|
2685
|
-
if (spec.valueAxis?.max !== void 0) max = spec.valueAxis.max;
|
|
2686
|
-
if (max === min) max = min + 1;
|
|
2687
|
-
return { min, max, step };
|
|
2688
|
-
};
|
|
2689
|
-
var renderChartTitle = (f, title, style) => {
|
|
2690
|
-
if (!title) return "";
|
|
2691
|
-
const sz = style?.sizePt ?? 13;
|
|
2692
|
-
const fill = style?.color ?? "#1F2937";
|
|
2693
|
-
const weight = style?.bold === false ? "400" : "600";
|
|
2694
|
-
const fontStyleAttr = style?.italic ? ' font-style="italic"' : "";
|
|
2695
|
-
return `<text x="${px(f.x + f.w / 2)}" y="${px(f.titleY)}" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="${sz.toFixed(1)}" fill="${fill}" font-weight="${weight}"${fontStyleAttr}>${escapeXml2(title)}</text>`;
|
|
2696
|
-
};
|
|
2697
|
-
var renderChartLegend = (f, names, colors, position = "b", textStyle, markerSymbols) => {
|
|
2698
|
-
if (names.length === 0) return "";
|
|
2699
|
-
const sz = textStyle?.sizePt ?? 11;
|
|
2700
|
-
const fill = textStyle?.color ?? "#374151";
|
|
2701
|
-
const weight = textStyle?.bold ? ' font-weight="600"' : "";
|
|
2702
|
-
const italic = textStyle?.italic ? ' font-style="italic"' : "";
|
|
2703
|
-
const textAttrs = `font-family="sans-serif" font-size="${sz.toFixed(1)}" fill="${fill}"${weight}${italic}`;
|
|
2704
|
-
const swatch = (i, swatchX, swatchY) => {
|
|
2705
|
-
const color = colors[i % colors.length];
|
|
2706
|
-
const sym = markerSymbols?.[i];
|
|
2707
|
-
if (sym && sym !== "none" && sym !== "auto") {
|
|
2708
|
-
const r = 4.5;
|
|
2709
|
-
return seriesMarker(sym, swatchX + r, swatchY + r, r, color);
|
|
2710
|
-
}
|
|
2711
|
-
return `<rect x="${px(swatchX)}" y="${px(swatchY)}" width="9" height="9" fill="${color}"/>`;
|
|
2712
|
-
};
|
|
2713
|
-
const out = [];
|
|
2714
|
-
if (position === "b") {
|
|
2715
|
-
const itemPx = Math.min(140, f.w / names.length);
|
|
2716
|
-
const totalW = itemPx * names.length;
|
|
2717
|
-
const startX = f.x + (f.w - totalW) / 2;
|
|
2718
|
-
for (let i = 0; i < names.length; i++) {
|
|
2719
|
-
const cx = startX + i * itemPx;
|
|
2720
|
-
const swatchX = cx + 4;
|
|
2721
|
-
const swatchY = f.legendY - 4;
|
|
2722
|
-
const labelX = swatchX + 14;
|
|
2723
|
-
out.push(
|
|
2724
|
-
swatch(i, swatchX, swatchY),
|
|
2725
|
-
`<text x="${px(labelX)}" y="${px(f.legendY)}" dominant-baseline="middle" ${textAttrs}>${escapeXml2(names[i] ?? `Series ${i + 1}`)}</text>`
|
|
2726
|
-
);
|
|
2727
|
-
}
|
|
2728
|
-
return out.join("");
|
|
2729
|
-
}
|
|
2730
|
-
if (position === "t") {
|
|
2731
|
-
const itemPx = Math.min(140, f.w / names.length);
|
|
2732
|
-
const totalW = itemPx * names.length;
|
|
2733
|
-
const startX = f.x + (f.w - totalW) / 2;
|
|
2734
|
-
const yTop = f.y + 4;
|
|
2735
|
-
for (let i = 0; i < names.length; i++) {
|
|
2736
|
-
const cx = startX + i * itemPx;
|
|
2737
|
-
out.push(
|
|
2738
|
-
swatch(i, cx + 4, yTop),
|
|
2739
|
-
`<text x="${px(cx + 18)}" y="${px(yTop + 8)}" dominant-baseline="middle" ${textAttrs}>${escapeXml2(names[i] ?? `Series ${i + 1}`)}</text>`
|
|
2740
|
-
);
|
|
2741
|
-
}
|
|
2742
|
-
return out.join("");
|
|
2743
|
-
}
|
|
2744
|
-
const lineH = 14;
|
|
2745
|
-
const totalH = names.length * lineH;
|
|
2746
|
-
const yStart = position === "tr" ? f.y + 12 : Math.max(f.y + 12, f.y + (f.h - totalH) / 2);
|
|
2747
|
-
const xCol = position === "l" ? f.x + 6 : position === "tr" ? f.x + f.w - 100 : (
|
|
2748
|
-
/* 'r' */
|
|
2749
|
-
f.x + f.w - 100
|
|
2750
|
-
);
|
|
2751
|
-
for (let i = 0; i < names.length; i++) {
|
|
2752
|
-
const yp = yStart + i * lineH;
|
|
2753
|
-
out.push(
|
|
2754
|
-
swatch(i, xCol, yp - 4),
|
|
2755
|
-
`<text x="${px(xCol + 14)}" y="${px(yp + 4)}" dominant-baseline="middle" ${textAttrs}>${escapeXml2(names[i] ?? `Series ${i + 1}`)}</text>`
|
|
2756
|
-
);
|
|
2757
|
-
}
|
|
2758
|
-
return out.join("");
|
|
2759
|
-
};
|
|
2760
|
-
var pointCount = (spec) => {
|
|
2761
|
-
if (spec.categories.length > 0) return spec.categories.length;
|
|
2762
|
-
let n = 0;
|
|
2763
|
-
for (const s of spec.series) if (s.values.length > n) n = s.values.length;
|
|
2764
|
-
return n;
|
|
2765
|
-
};
|
|
2766
|
-
var renderColumnChart = (f, spec, colors) => {
|
|
2767
|
-
const N = pointCount(spec);
|
|
2768
|
-
if (N === 0 || spec.series.length === 0) return "";
|
|
2769
|
-
const grouping = spec.grouping ?? "clustered";
|
|
2770
|
-
const isStacked = grouping === "stacked" || grouping === "percentStacked";
|
|
2771
|
-
const isPercent = grouping === "percentStacked";
|
|
2772
|
-
let { min, max } = seriesMinMax(spec);
|
|
2773
|
-
if (isStacked) {
|
|
2774
|
-
let sumMin = Infinity;
|
|
2775
|
-
let sumMax = -Infinity;
|
|
2776
|
-
for (let c = 0; c < N; c++) {
|
|
2777
|
-
let pos = 0;
|
|
2778
|
-
let neg = 0;
|
|
2779
|
-
for (const s of spec.series) {
|
|
2780
|
-
const v = s.values[c] ?? 0;
|
|
2781
|
-
if (v >= 0) pos += v;
|
|
2782
|
-
else neg += v;
|
|
2783
|
-
}
|
|
2784
|
-
if (neg < sumMin) sumMin = neg;
|
|
2785
|
-
if (pos > sumMax) sumMax = pos;
|
|
2786
|
-
}
|
|
2787
|
-
min = isPercent ? 0 : Math.min(0, sumMin);
|
|
2788
|
-
max = isPercent ? 1 : Math.max(1, sumMax);
|
|
2789
|
-
}
|
|
2790
|
-
const range = max - min || 1;
|
|
2791
|
-
const groupW = f.plotW / N;
|
|
2792
|
-
const gapPctC = (spec.gapWidthPct ?? 150) / 100;
|
|
2793
|
-
const overlapPctC = (spec.overlapPct ?? (isStacked ? 100 : 0)) / 100;
|
|
2794
|
-
const Sc = spec.series.length;
|
|
2795
|
-
const clusterUnitsC = isStacked ? 1 : 1 + (Sc - 1) * (1 - overlapPctC);
|
|
2796
|
-
const barW = groupW / Math.max(0.5, clusterUnitsC + gapPctC);
|
|
2797
|
-
const baseY = f.plotY + f.plotH - (0 - min) / range * f.plotH;
|
|
2798
|
-
const showLabelFor = (s) => spec.series[s]?.dataLabels?.showValue ?? spec.dataLabels?.showValue ?? false;
|
|
2799
|
-
const out = [];
|
|
2800
|
-
for (let c = 0; c < N; c++) {
|
|
2801
|
-
if (isStacked) {
|
|
2802
|
-
let total = 0;
|
|
2803
|
-
if (isPercent) {
|
|
2804
|
-
for (const s of spec.series) total += Math.max(0, s.values[c] ?? 0);
|
|
2805
|
-
}
|
|
2806
|
-
let posAcc = 0;
|
|
2807
|
-
let negAcc = 0;
|
|
2808
|
-
for (let s = 0; s < spec.series.length; s++) {
|
|
2809
|
-
let v = spec.series[s]?.values[c] ?? 0;
|
|
2810
|
-
if (isPercent) {
|
|
2811
|
-
if (total === 0) continue;
|
|
2812
|
-
v = Math.max(0, v) / total;
|
|
2813
|
-
}
|
|
2814
|
-
const base = v >= 0 ? posAcc : negAcc;
|
|
2815
|
-
const stackedTop = base + v;
|
|
2816
|
-
const stackedBase = base;
|
|
2817
|
-
const x0 = f.plotX + c * groupW + (groupW - barW) / 2;
|
|
2818
|
-
const y0 = f.plotY + f.plotH - (Math.max(stackedTop, stackedBase) - min) / range * f.plotH;
|
|
2819
|
-
const y1 = f.plotY + f.plotH - (Math.min(stackedTop, stackedBase) - min) / range * f.plotH;
|
|
2820
|
-
const h = Math.abs(y1 - y0);
|
|
2821
|
-
out.push(
|
|
2822
|
-
`<rect x="${px(x0)}" y="${px(y0)}" width="${px(barW)}" height="${px(h)}" fill="${colors[s % colors.length]}"/>`
|
|
2823
|
-
);
|
|
2824
|
-
if (showLabelFor(s) && Math.abs(v) > 0) {
|
|
2825
|
-
const labelY = (y0 + y1) / 2 + 3;
|
|
2826
|
-
const labelText = isPercent ? `${Math.round(v * 100)}%` : formatDataLabelValue(spec, s, v);
|
|
2827
|
-
out.push(
|
|
2828
|
-
`<text x="${px(x0 + barW / 2)}" y="${px(labelY)}" text-anchor="middle" font-family="sans-serif" font-size="9" fill="#FFFFFF" font-weight="600">${labelText}</text>`
|
|
2829
|
-
);
|
|
2830
|
-
}
|
|
2831
|
-
if (v >= 0) posAcc = stackedTop;
|
|
2832
|
-
else negAcc = stackedTop;
|
|
2833
|
-
}
|
|
2834
|
-
} else {
|
|
2835
|
-
const clusterW = barW * clusterUnitsC;
|
|
2836
|
-
const clusterStartX = f.plotX + c * groupW + (groupW - clusterW) / 2;
|
|
2837
|
-
const stride = barW * (1 - overlapPctC);
|
|
2838
|
-
for (let s = 0; s < spec.series.length; s++) {
|
|
2839
|
-
const v = spec.series[s]?.values[c] ?? 0;
|
|
2840
|
-
const x0 = clusterStartX + s * stride;
|
|
2841
|
-
const top = f.plotY + f.plotH - (v - min) / range * f.plotH;
|
|
2842
|
-
const y0 = Math.min(top, baseY);
|
|
2843
|
-
const h = Math.abs(top - baseY);
|
|
2844
|
-
const baseColor = spec.varyColors && spec.series.length === 1 ? colors[c % colors.length] : spec.series[s]?.color ?? colors[s % colors.length];
|
|
2845
|
-
const fillColor = v < 0 && spec.series[s]?.invertIfNegative ? mixHex(baseColor, "#000000", 0.55) : baseColor;
|
|
2846
|
-
out.push(
|
|
2847
|
-
`<rect x="${px(x0)}" y="${px(y0)}" width="${px(barW)}" height="${px(h)}" fill="${fillColor}"/>`
|
|
2848
|
-
);
|
|
2849
|
-
if (showLabelFor(s)) {
|
|
2850
|
-
const pos = spec.series[s]?.dataLabels?.position ?? spec.dataLabels?.position;
|
|
2851
|
-
let labelY;
|
|
2852
|
-
let fill = "#374151";
|
|
2853
|
-
if (pos === "ctr") {
|
|
2854
|
-
labelY = y0 + h / 2 + 3;
|
|
2855
|
-
fill = "#FFFFFF";
|
|
2856
|
-
} else if (pos === "inEnd") {
|
|
2857
|
-
labelY = v >= 0 ? y0 + 9 : y0 + h - 3;
|
|
2858
|
-
fill = "#FFFFFF";
|
|
2859
|
-
} else if (pos === "inBase") {
|
|
2860
|
-
labelY = v >= 0 ? y0 + h - 3 : y0 + 9;
|
|
2861
|
-
fill = "#FFFFFF";
|
|
2862
|
-
} else {
|
|
2863
|
-
labelY = v >= 0 ? y0 - 2 : y0 + h + 9;
|
|
2864
|
-
}
|
|
2865
|
-
out.push(
|
|
2866
|
-
`<text x="${px(x0 + barW / 2)}" y="${px(labelY)}" text-anchor="middle" ${dataLabelTextAttrs(spec, s, fill)}>${formatDataLabelValue(spec, s, v)}</text>`
|
|
2867
|
-
);
|
|
2868
|
-
}
|
|
2869
|
-
}
|
|
2870
|
-
}
|
|
2871
|
-
}
|
|
2872
|
-
out.push(
|
|
2873
|
-
`<line x1="${px(f.plotX)}" y1="${px(baseY)}" x2="${px(f.plotX + f.plotW)}" y2="${px(baseY)}" stroke="#9CA3AF" stroke-width="0.5"/>`
|
|
2874
|
-
);
|
|
2875
|
-
for (let s = 0; s < spec.series.length; s++) {
|
|
2876
|
-
const series = spec.series[s];
|
|
2877
|
-
if (!series?.trendline) continue;
|
|
2878
|
-
const tlColor = series.trendline.color ?? series.color ?? colors[s % colors.length];
|
|
2879
|
-
const xs = [];
|
|
2880
|
-
const ys = [];
|
|
2881
|
-
const seriesValues = series.values.slice(0, N);
|
|
2882
|
-
for (let c = 0; c < N; c++) {
|
|
2883
|
-
const v = seriesValues[c];
|
|
2884
|
-
if (v === null || v === void 0 || !Number.isFinite(v)) continue;
|
|
2885
|
-
const cx = f.plotX + (c + 0.5) * groupW;
|
|
2886
|
-
const cy = f.plotY + f.plotH - (v - min) / range * f.plotH;
|
|
2887
|
-
xs.push(cx);
|
|
2888
|
-
ys.push(cy);
|
|
2889
|
-
}
|
|
2890
|
-
if (xs.length < 2) continue;
|
|
2891
|
-
out.push(trendlinePath(xs, ys, series.trendline, tlColor));
|
|
2892
|
-
}
|
|
2893
|
-
return out.join("");
|
|
2894
|
-
};
|
|
2895
|
-
var trendlinePath = (xs, ys, tl, color) => {
|
|
2896
|
-
const n = xs.length;
|
|
2897
|
-
const stepX = n >= 2 ? (xs[n - 1] - xs[0]) / (n - 1) : 0;
|
|
2898
|
-
const extendBefore = (tl.backward ?? 0) * stepX;
|
|
2899
|
-
const extendAfter = (tl.forward ?? 0) * stepX;
|
|
2900
|
-
let pts = [];
|
|
2901
|
-
switch (tl.type) {
|
|
2902
|
-
case "movingAvg": {
|
|
2903
|
-
const period = Math.max(2, Math.min(n, tl.period ?? 3));
|
|
2904
|
-
for (let i = period - 1; i < n; i++) {
|
|
2905
|
-
let sum = 0;
|
|
2906
|
-
for (let j = 0; j < period; j++) sum += ys[i - j];
|
|
2907
|
-
pts.push([xs[i], sum / period]);
|
|
2908
|
-
}
|
|
2909
|
-
break;
|
|
2910
|
-
}
|
|
2911
|
-
case "log": {
|
|
2912
|
-
const ix = xs.map((_, i) => i + 1);
|
|
2913
|
-
const sumLn = ix.reduce((a2, x) => a2 + Math.log(x), 0);
|
|
2914
|
-
const sumY = ys.reduce((a2, y) => a2 + y, 0);
|
|
2915
|
-
const sumLnY = ix.reduce((a2, x, i) => a2 + Math.log(x) * ys[i], 0);
|
|
2916
|
-
const sumLn2 = ix.reduce((a2, x) => a2 + Math.log(x) ** 2, 0);
|
|
2917
|
-
const b = (n * sumLnY - sumLn * sumY) / (n * sumLn2 - sumLn ** 2 || 1);
|
|
2918
|
-
const a = (sumY - b * sumLn) / n;
|
|
2919
|
-
pts = xs.map((x, i) => [x, a + b * Math.log(i + 1)]);
|
|
2920
|
-
break;
|
|
2921
|
-
}
|
|
2922
|
-
case "exp": {
|
|
2923
|
-
if (ys.every((y) => y > 0)) {
|
|
2924
|
-
const ix = xs.map((_, i) => i);
|
|
2925
|
-
const lnY = ys.map((y) => Math.log(y));
|
|
2926
|
-
const meanX = ix.reduce((a2, b2) => a2 + b2, 0) / n;
|
|
2927
|
-
const meanLnY = lnY.reduce((a2, b2) => a2 + b2, 0) / n;
|
|
2928
|
-
let num = 0;
|
|
2929
|
-
let den = 0;
|
|
2930
|
-
for (let i = 0; i < n; i++) {
|
|
2931
|
-
num += (ix[i] - meanX) * (lnY[i] - meanLnY);
|
|
2932
|
-
den += (ix[i] - meanX) ** 2;
|
|
2933
|
-
}
|
|
2934
|
-
const b = den === 0 ? 0 : num / den;
|
|
2935
|
-
const a = Math.exp(meanLnY - b * meanX);
|
|
2936
|
-
pts = xs.map((x, i) => [x, a * Math.exp(b * i)]);
|
|
2937
|
-
}
|
|
2938
|
-
if (pts.length === 0) pts = linearFit(xs, ys, extendBefore, extendAfter);
|
|
2939
|
-
break;
|
|
2940
|
-
}
|
|
2941
|
-
case "power":
|
|
2942
|
-
case "poly":
|
|
2943
|
-
case "linear":
|
|
2944
|
-
default:
|
|
2945
|
-
pts = linearFit(xs, ys, extendBefore, extendAfter);
|
|
2946
|
-
}
|
|
2947
|
-
if (pts.length < 2) return "";
|
|
2948
|
-
const d = pts.map(([x, y], i) => `${i === 0 ? "M" : "L"}${px(x)},${px(y)}`).join(" ");
|
|
2949
|
-
const path = `<path d="${d}" fill="none" stroke="${color}" stroke-width="1.5" stroke-dasharray="6 3" stroke-linecap="round"/>`;
|
|
2950
|
-
if (tl.name !== void 0 && tl.name.length > 0) {
|
|
2951
|
-
const [lx, ly] = pts[pts.length - 1];
|
|
2952
|
-
const label = `<text x="${px(lx + 4)}" y="${px(ly)}" dominant-baseline="middle" font-family="sans-serif" font-size="9" fill="${color}">${escapeXml2(tl.name)}</text>`;
|
|
2953
|
-
return path + label;
|
|
2954
|
-
}
|
|
2955
|
-
return path;
|
|
2956
|
-
};
|
|
2957
|
-
var linearFit = (xs, ys, extendBefore = 0, extendAfter = 0) => {
|
|
2958
|
-
const n = xs.length;
|
|
2959
|
-
const meanX = xs.reduce((a, b) => a + b, 0) / n;
|
|
2960
|
-
const meanY = ys.reduce((a, b) => a + b, 0) / n;
|
|
2961
|
-
let num = 0;
|
|
2962
|
-
let den = 0;
|
|
2963
|
-
for (let i = 0; i < n; i++) {
|
|
2964
|
-
num += (xs[i] - meanX) * (ys[i] - meanY);
|
|
2965
|
-
den += (xs[i] - meanX) ** 2;
|
|
2966
|
-
}
|
|
2967
|
-
const slope = den === 0 ? 0 : num / den;
|
|
2968
|
-
const intercept = meanY - slope * meanX;
|
|
2969
|
-
const xStart = xs[0] - extendBefore;
|
|
2970
|
-
const xEnd = xs[n - 1] + extendAfter;
|
|
2971
|
-
return [
|
|
2972
|
-
[xStart, intercept + slope * xStart],
|
|
2973
|
-
[xEnd, intercept + slope * xEnd]
|
|
2974
|
-
];
|
|
2975
|
-
};
|
|
2976
|
-
var smoothPath = (pts) => {
|
|
2977
|
-
if (pts.length < 2) return "";
|
|
2978
|
-
const tension = 0.5;
|
|
2979
|
-
const parts = [];
|
|
2980
|
-
const [x0, y0] = pts[0];
|
|
2981
|
-
parts.push(`M${px(x0)},${px(y0)}`);
|
|
2982
|
-
for (let i = 0; i < pts.length - 1; i++) {
|
|
2983
|
-
pts[i];
|
|
2984
|
-
const p0 = pts[i - 1] ?? pts[i];
|
|
2985
|
-
const p1 = pts[i];
|
|
2986
|
-
const p2 = pts[i + 1];
|
|
2987
|
-
const p3 = pts[i + 2] ?? p2;
|
|
2988
|
-
const cp1x = p1[0] + (p2[0] - p0[0]) * tension / 3;
|
|
2989
|
-
const cp1y = p1[1] + (p2[1] - p0[1]) * tension / 3;
|
|
2990
|
-
const cp2x = p2[0] - (p3[0] - p1[0]) * tension / 3;
|
|
2991
|
-
const cp2y = p2[1] - (p3[1] - p1[1]) * tension / 3;
|
|
2992
|
-
parts.push(`C${px(cp1x)},${px(cp1y)} ${px(cp2x)},${px(cp2y)} ${px(p2[0])},${px(p2[1])}`);
|
|
2993
|
-
}
|
|
2994
|
-
return parts.join(" ");
|
|
2995
|
-
};
|
|
2996
|
-
var seriesMarker = (symbol, cx, cy, r, color) => {
|
|
2997
|
-
switch (symbol) {
|
|
2998
|
-
case "square":
|
|
2999
|
-
return `<rect x="${px(cx - r)}" y="${px(cy - r)}" width="${px(r * 2)}" height="${px(r * 2)}" fill="${color}"/>`;
|
|
3000
|
-
case "diamond":
|
|
3001
|
-
return `<polygon points="${px(cx)},${px(cy - r)} ${px(cx + r)},${px(cy)} ${px(cx)},${px(cy + r)} ${px(cx - r)},${px(cy)}" fill="${color}"/>`;
|
|
3002
|
-
case "triangle":
|
|
3003
|
-
return `<polygon points="${px(cx)},${px(cy - r)} ${px(cx + r)},${px(cy + r)} ${px(cx - r)},${px(cy + r)}" fill="${color}"/>`;
|
|
3004
|
-
case "star":
|
|
3005
|
-
return `<polygon points="${(() => {
|
|
3006
|
-
const pts = [];
|
|
3007
|
-
for (let i = 0; i < 10; i++) {
|
|
3008
|
-
const ang = -Math.PI / 2 + i * Math.PI / 5;
|
|
3009
|
-
const rr = i % 2 === 0 ? r : r * 0.45;
|
|
3010
|
-
pts.push(`${px(cx + rr * Math.cos(ang))},${px(cy + rr * Math.sin(ang))}`);
|
|
3011
|
-
}
|
|
3012
|
-
return pts.join(" ");
|
|
3013
|
-
})()}" fill="${color}"/>`;
|
|
3014
|
-
case "x":
|
|
3015
|
-
return `<g stroke="${color}" stroke-width="1.2" stroke-linecap="round"><line x1="${px(cx - r)}" y1="${px(cy - r)}" x2="${px(cx + r)}" y2="${px(cy + r)}"/><line x1="${px(cx - r)}" y1="${px(cy + r)}" x2="${px(cx + r)}" y2="${px(cy - r)}"/></g>`;
|
|
3016
|
-
case "plus":
|
|
3017
|
-
return `<g stroke="${color}" stroke-width="1.2" stroke-linecap="round"><line x1="${px(cx - r)}" y1="${px(cy)}" x2="${px(cx + r)}" y2="${px(cy)}"/><line x1="${px(cx)}" y1="${px(cy - r)}" x2="${px(cx)}" y2="${px(cy + r)}"/></g>`;
|
|
3018
|
-
case "dash":
|
|
3019
|
-
return `<line x1="${px(cx - r)}" y1="${px(cy)}" x2="${px(cx + r)}" y2="${px(cy)}" stroke="${color}" stroke-width="${Math.max(1.5, r * 0.6).toFixed(2)}" stroke-linecap="round"/>`;
|
|
3020
|
-
case "dot":
|
|
3021
|
-
return `<circle cx="${px(cx)}" cy="${px(cy)}" r="${(r * 0.6).toFixed(2)}" fill="${color}"/>`;
|
|
3022
|
-
case "picture":
|
|
3023
|
-
case "auto":
|
|
3024
|
-
case "circle":
|
|
3025
|
-
case "none":
|
|
3026
|
-
default:
|
|
3027
|
-
return `<circle cx="${px(cx)}" cy="${px(cy)}" r="${px(r)}" fill="${color}"/>`;
|
|
3028
|
-
}
|
|
3029
|
-
};
|
|
3030
|
-
var formatChartValue = (v) => {
|
|
3031
|
-
if (!Number.isFinite(v)) return "";
|
|
3032
|
-
if (Number.isInteger(v)) return String(v);
|
|
3033
|
-
const abs = Math.abs(v);
|
|
3034
|
-
if (abs >= 1e3) return Math.round(v).toString();
|
|
3035
|
-
if (abs >= 10) return v.toFixed(1);
|
|
3036
|
-
return v.toFixed(2).replace(/\.?0+$/, "");
|
|
3037
|
-
};
|
|
3038
|
-
var formatDataLabelValue = (spec, seriesIdx, v) => {
|
|
3039
|
-
const nf = spec.series[seriesIdx]?.dataLabels?.numberFormat ?? spec.dataLabels?.numberFormat;
|
|
3040
|
-
return nf ? formatAxisLabel(v, nf) : formatChartValue(v);
|
|
3041
|
-
};
|
|
3042
|
-
var dataLabelTextAttrs = (spec, seriesIdx, fallbackFill, fallbackSizePt = 9, fallbackBold = false) => {
|
|
3043
|
-
const style = spec.series[seriesIdx]?.dataLabels?.textStyle ?? spec.dataLabels?.textStyle;
|
|
3044
|
-
const sz = style?.sizePt ?? fallbackSizePt;
|
|
3045
|
-
const fill = style?.color ?? fallbackFill;
|
|
3046
|
-
const isBold = style?.bold ?? fallbackBold;
|
|
3047
|
-
const weight = isBold ? ' font-weight="600"' : "";
|
|
3048
|
-
const italic = style?.italic ? ' font-style="italic"' : "";
|
|
3049
|
-
return `font-family="sans-serif" font-size="${sz.toFixed(1)}" fill="${fill}"${weight}${italic}`;
|
|
3050
|
-
};
|
|
3051
|
-
var renderBarChart = (f, spec, colors) => {
|
|
3052
|
-
const N = pointCount(spec);
|
|
3053
|
-
if (N === 0 || spec.series.length === 0) return "";
|
|
3054
|
-
const grouping = spec.grouping ?? "clustered";
|
|
3055
|
-
const isStacked = grouping === "stacked" || grouping === "percentStacked";
|
|
3056
|
-
const isPercent = grouping === "percentStacked";
|
|
3057
|
-
let { min, max } = seriesMinMax(spec);
|
|
3058
|
-
if (isStacked) {
|
|
3059
|
-
let sumMin = Infinity;
|
|
3060
|
-
let sumMax = -Infinity;
|
|
3061
|
-
for (let c = 0; c < N; c++) {
|
|
3062
|
-
let pos = 0;
|
|
3063
|
-
let neg = 0;
|
|
3064
|
-
for (const s of spec.series) {
|
|
3065
|
-
const v = s.values[c] ?? 0;
|
|
3066
|
-
if (v >= 0) pos += v;
|
|
3067
|
-
else neg += v;
|
|
3068
|
-
}
|
|
3069
|
-
if (neg < sumMin) sumMin = neg;
|
|
3070
|
-
if (pos > sumMax) sumMax = pos;
|
|
3071
|
-
}
|
|
3072
|
-
min = isPercent ? 0 : Math.min(0, sumMin);
|
|
3073
|
-
max = isPercent ? 1 : Math.max(1, sumMax);
|
|
3074
|
-
}
|
|
3075
|
-
const range = max - min || 1;
|
|
3076
|
-
const groupH = f.plotH / N;
|
|
3077
|
-
const gapPctB = (spec.gapWidthPct ?? 150) / 100;
|
|
3078
|
-
const overlapPctB = (spec.overlapPct ?? (isStacked ? 100 : 0)) / 100;
|
|
3079
|
-
const Sb = spec.series.length;
|
|
3080
|
-
const clusterUnitsB = isStacked ? 1 : 1 + (Sb - 1) * (1 - overlapPctB);
|
|
3081
|
-
const barH = groupH / Math.max(0.5, clusterUnitsB + gapPctB);
|
|
3082
|
-
const baseX = f.plotX + (0 - min) / range * f.plotW;
|
|
3083
|
-
const showLabelForBar = (s) => spec.series[s]?.dataLabels?.showValue ?? spec.dataLabels?.showValue ?? false;
|
|
3084
|
-
const out = [];
|
|
3085
|
-
for (let c = 0; c < N; c++) {
|
|
3086
|
-
if (isStacked) {
|
|
3087
|
-
let total = 0;
|
|
3088
|
-
if (isPercent) for (const s of spec.series) total += Math.max(0, s.values[c] ?? 0);
|
|
3089
|
-
let posAcc = 0;
|
|
3090
|
-
let negAcc = 0;
|
|
3091
|
-
for (let s = 0; s < spec.series.length; s++) {
|
|
3092
|
-
let v = spec.series[s]?.values[c] ?? 0;
|
|
3093
|
-
if (isPercent) {
|
|
3094
|
-
if (total === 0) continue;
|
|
3095
|
-
v = Math.max(0, v) / total;
|
|
3096
|
-
}
|
|
3097
|
-
const base = v >= 0 ? posAcc : negAcc;
|
|
3098
|
-
const stackedTop = base + v;
|
|
3099
|
-
const y0 = f.plotY + c * groupH + (groupH - barH) / 2;
|
|
3100
|
-
const x0 = f.plotX + (Math.min(base, stackedTop) - min) / range * f.plotW;
|
|
3101
|
-
const x1 = f.plotX + (Math.max(base, stackedTop) - min) / range * f.plotW;
|
|
3102
|
-
const w = Math.abs(x1 - x0);
|
|
3103
|
-
out.push(
|
|
3104
|
-
`<rect x="${px(x0)}" y="${px(y0)}" width="${px(w)}" height="${px(barH)}" fill="${colors[s % colors.length]}"/>`
|
|
3105
|
-
);
|
|
3106
|
-
if (showLabelForBar(s) && Math.abs(v) > 0) {
|
|
3107
|
-
const labelX = (x0 + x1) / 2;
|
|
3108
|
-
const labelText = isPercent ? `${Math.round(v * 100)}%` : formatDataLabelValue(spec, s, v);
|
|
3109
|
-
out.push(
|
|
3110
|
-
`<text x="${px(labelX)}" y="${px(y0 + barH / 2 + 3)}" text-anchor="middle" font-family="sans-serif" font-size="9" fill="#FFFFFF" font-weight="600">${labelText}</text>`
|
|
3111
|
-
);
|
|
3112
|
-
}
|
|
3113
|
-
if (v >= 0) posAcc = stackedTop;
|
|
3114
|
-
else negAcc = stackedTop;
|
|
3115
|
-
}
|
|
3116
|
-
} else {
|
|
3117
|
-
const clusterH = barH * clusterUnitsB;
|
|
3118
|
-
const clusterStartY = f.plotY + c * groupH + (groupH - clusterH) / 2;
|
|
3119
|
-
const strideB = barH * (1 - overlapPctB);
|
|
3120
|
-
for (let s = 0; s < spec.series.length; s++) {
|
|
3121
|
-
const v = spec.series[s]?.values[c] ?? 0;
|
|
3122
|
-
const y0 = clusterStartY + s * strideB;
|
|
3123
|
-
const tip = f.plotX + (v - min) / range * f.plotW;
|
|
3124
|
-
const x0 = Math.min(tip, baseX);
|
|
3125
|
-
const w = Math.abs(tip - baseX);
|
|
3126
|
-
const baseColor = spec.varyColors && spec.series.length === 1 ? colors[c % colors.length] : spec.series[s]?.color ?? colors[s % colors.length];
|
|
3127
|
-
const fillColor = v < 0 && spec.series[s]?.invertIfNegative ? mixHex(baseColor, "#000000", 0.55) : baseColor;
|
|
3128
|
-
out.push(
|
|
3129
|
-
`<rect x="${px(x0)}" y="${px(y0)}" width="${px(w)}" height="${px(barH)}" fill="${fillColor}"/>`
|
|
3130
|
-
);
|
|
3131
|
-
if (showLabelForBar(s)) {
|
|
3132
|
-
const pos = spec.series[s]?.dataLabels?.position ?? spec.dataLabels?.position;
|
|
3133
|
-
let labelX;
|
|
3134
|
-
let anchor;
|
|
3135
|
-
let fill = "#374151";
|
|
3136
|
-
if (pos === "ctr") {
|
|
3137
|
-
labelX = x0 + w / 2;
|
|
3138
|
-
anchor = "middle";
|
|
3139
|
-
fill = "#FFFFFF";
|
|
3140
|
-
} else if (pos === "inEnd") {
|
|
3141
|
-
labelX = v >= 0 ? x0 + w - 4 : x0 + 4;
|
|
3142
|
-
anchor = v >= 0 ? "end" : "start";
|
|
3143
|
-
fill = "#FFFFFF";
|
|
3144
|
-
} else if (pos === "inBase") {
|
|
3145
|
-
labelX = v >= 0 ? x0 + 4 : x0 + w - 4;
|
|
3146
|
-
anchor = v >= 0 ? "start" : "end";
|
|
3147
|
-
fill = "#FFFFFF";
|
|
3148
|
-
} else {
|
|
3149
|
-
labelX = v >= 0 ? x0 + w + 2 : x0 - 2;
|
|
3150
|
-
anchor = v >= 0 ? "start" : "end";
|
|
3151
|
-
}
|
|
3152
|
-
out.push(
|
|
3153
|
-
`<text x="${px(labelX)}" y="${px(y0 + barH / 2 + 3)}" text-anchor="${anchor}" ${dataLabelTextAttrs(spec, s, fill)}>${formatDataLabelValue(spec, s, v)}</text>`
|
|
3154
|
-
);
|
|
3155
|
-
}
|
|
3156
|
-
}
|
|
3157
|
-
}
|
|
3158
|
-
}
|
|
3159
|
-
out.push(
|
|
3160
|
-
`<line x1="${px(baseX)}" y1="${px(f.plotY)}" x2="${px(baseX)}" y2="${px(f.plotY + f.plotH)}" stroke="#9CA3AF" stroke-width="0.5"/>`
|
|
3161
|
-
);
|
|
3162
|
-
return out.join("");
|
|
3163
|
-
};
|
|
3164
|
-
var renderLineChart = (f, spec, colors, fill) => {
|
|
3165
|
-
const N = pointCount(spec);
|
|
3166
|
-
if (N === 0 || spec.series.length === 0) return "";
|
|
3167
|
-
const grouping = spec.grouping ?? "clustered";
|
|
3168
|
-
const isStacked = grouping === "stacked" || grouping === "percentStacked";
|
|
3169
|
-
const isPercent = grouping === "percentStacked";
|
|
3170
|
-
let { min, max } = seriesMinMax(spec);
|
|
3171
|
-
if (isStacked) {
|
|
3172
|
-
let sumMin = Infinity;
|
|
3173
|
-
let sumMax = -Infinity;
|
|
3174
|
-
for (let c = 0; c < N; c++) {
|
|
3175
|
-
let pos = 0;
|
|
3176
|
-
let neg = 0;
|
|
3177
|
-
for (const s of spec.series) {
|
|
3178
|
-
const v = s.values[c] ?? 0;
|
|
3179
|
-
if (v >= 0) pos += v;
|
|
3180
|
-
else neg += v;
|
|
3181
|
-
}
|
|
3182
|
-
if (neg < sumMin) sumMin = neg;
|
|
3183
|
-
if (pos > sumMax) sumMax = pos;
|
|
3184
|
-
}
|
|
3185
|
-
min = isPercent ? 0 : Math.min(0, sumMin);
|
|
3186
|
-
max = isPercent ? 1 : Math.max(1, sumMax);
|
|
3187
|
-
}
|
|
3188
|
-
const range = max - min || 1;
|
|
3189
|
-
const step = N > 1 ? f.plotW / (N - 1) : 0;
|
|
3190
|
-
const baseY = f.plotY + f.plotH - (0 - min) / range * f.plotH;
|
|
3191
|
-
const out = [];
|
|
3192
|
-
out.push(
|
|
3193
|
-
`<line x1="${px(f.plotX)}" y1="${px(baseY)}" x2="${px(f.plotX + f.plotW)}" y2="${px(baseY)}" stroke="#E5E7EB" stroke-width="0.5"/>`
|
|
3194
|
-
);
|
|
3195
|
-
const accumulated = Array.from({ length: N }, () => 0);
|
|
3196
|
-
for (let s = 0; s < spec.series.length; s++) {
|
|
3197
|
-
const series = spec.series[s];
|
|
3198
|
-
if (!series) continue;
|
|
3199
|
-
const color = series.color ?? colors[s % colors.length];
|
|
3200
|
-
const dba = spec.dispBlanksAs ?? "gap";
|
|
3201
|
-
const ptsRaw = [];
|
|
3202
|
-
const basePtsRaw = [];
|
|
3203
|
-
for (let c = 0; c < N; c++) {
|
|
3204
|
-
const xp = f.plotX + c * step;
|
|
3205
|
-
const rawV = series.values[c];
|
|
3206
|
-
const isNullish = rawV === null || rawV === void 0 || !Number.isFinite(rawV);
|
|
3207
|
-
let v;
|
|
3208
|
-
if (isNullish) {
|
|
3209
|
-
if (dba === "zero") v = 0;
|
|
3210
|
-
else {
|
|
3211
|
-
ptsRaw.push(null);
|
|
3212
|
-
basePtsRaw.push(null);
|
|
3213
|
-
continue;
|
|
3214
|
-
}
|
|
3215
|
-
} else {
|
|
3216
|
-
v = rawV;
|
|
3217
|
-
}
|
|
3218
|
-
const baseAt = accumulated[c] ?? 0;
|
|
3219
|
-
if (isPercent) {
|
|
3220
|
-
let total = 0;
|
|
3221
|
-
for (const s2 of spec.series) total += Math.max(0, s2.values[c] ?? 0);
|
|
3222
|
-
v = total === 0 ? 0 : Math.max(0, v) / total;
|
|
3223
|
-
}
|
|
3224
|
-
const top = isStacked ? baseAt + v : v;
|
|
3225
|
-
const yp = f.plotY + f.plotH - (top - min) / range * f.plotH;
|
|
3226
|
-
const yBase = isStacked ? f.plotY + f.plotH - (baseAt - min) / range * f.plotH : baseY;
|
|
3227
|
-
ptsRaw.push([xp, yp]);
|
|
3228
|
-
basePtsRaw.push([xp, yBase]);
|
|
3229
|
-
if (isStacked) accumulated[c] = top;
|
|
3230
|
-
}
|
|
3231
|
-
const pts = dba === "span" ? ptsRaw.filter((p) => p !== null) : ptsRaw.filter((p) => p !== null);
|
|
3232
|
-
const basePts = dba === "span" ? basePtsRaw.filter((p) => p !== null) : basePtsRaw.filter((p) => p !== null);
|
|
3233
|
-
const dPath = series.smooth && pts.length > 2 ? smoothPath(pts) : (() => {
|
|
3234
|
-
let path = "";
|
|
3235
|
-
let starting = true;
|
|
3236
|
-
for (const p of ptsRaw) {
|
|
3237
|
-
if (p === null) {
|
|
3238
|
-
if (dba === "gap") starting = true;
|
|
3239
|
-
continue;
|
|
3240
|
-
}
|
|
3241
|
-
path += `${starting ? "M" : "L"}${px(p[0])},${px(p[1])} `;
|
|
3242
|
-
starting = false;
|
|
3243
|
-
}
|
|
3244
|
-
return path.trim();
|
|
3245
|
-
})();
|
|
3246
|
-
if (fill) {
|
|
3247
|
-
const back = basePts.slice().reverse().map(([xp, yp]) => `L${px(xp)},${px(yp)}`).join(" ");
|
|
3248
|
-
out.push(`<path d="${dPath} ${back} Z" fill="${color}" fill-opacity="0.55" stroke="none"/>`);
|
|
3249
|
-
}
|
|
3250
|
-
const lineWPx = series.lineWidthEmu ? Math.max(0.3, series.lineWidthEmu / EMU_PER_PX) : 1.5;
|
|
3251
|
-
const dashAttr = series.lineDash ? (() => {
|
|
3252
|
-
const sw = lineWPx;
|
|
3253
|
-
const pat = DASH_PATTERNS[series.lineDash];
|
|
3254
|
-
if (!pat) return "";
|
|
3255
|
-
const arr = pat.split(" ").map((n) => (Number.parseFloat(n) * sw).toFixed(2)).join(" ");
|
|
3256
|
-
return ` stroke-dasharray="${arr}"`;
|
|
3257
|
-
})() : "";
|
|
3258
|
-
out.push(
|
|
3259
|
-
`<path d="${dPath}" fill="none" stroke="${color}" stroke-width="${lineWPx.toFixed(2)}" stroke-linejoin="round" stroke-linecap="round"${dashAttr}/>`
|
|
3260
|
-
);
|
|
3261
|
-
if (!isStacked) {
|
|
3262
|
-
const symbol = series.markerSymbol ?? "auto";
|
|
3263
|
-
if (symbol !== "none") {
|
|
3264
|
-
const size = series.markerSizePt ?? 5;
|
|
3265
|
-
const r = Math.max(1, size * 0.5);
|
|
3266
|
-
for (const [xp, yp] of pts) {
|
|
3267
|
-
out.push(seriesMarker(symbol, xp, yp, r, color));
|
|
3268
|
-
}
|
|
3269
|
-
}
|
|
3270
|
-
}
|
|
3271
|
-
const showLineLabel = series.dataLabels?.showValue ?? spec.dataLabels?.showValue ?? false;
|
|
3272
|
-
if (showLineLabel) {
|
|
3273
|
-
const lblPos = series.dataLabels?.position ?? spec.dataLabels?.position;
|
|
3274
|
-
const computeAttrs = (xp, yp) => {
|
|
3275
|
-
switch (lblPos) {
|
|
3276
|
-
case "ctr":
|
|
3277
|
-
return { x: xp, y: yp + 3, anchor: "middle" };
|
|
3278
|
-
case "b":
|
|
3279
|
-
return { x: xp, y: yp + 13, anchor: "middle" };
|
|
3280
|
-
case "l":
|
|
3281
|
-
return { x: xp - 6, y: yp + 3, anchor: "end" };
|
|
3282
|
-
case "r":
|
|
3283
|
-
return { x: xp + 6, y: yp + 3, anchor: "start" };
|
|
3284
|
-
default:
|
|
3285
|
-
return { x: xp, y: yp - 5, anchor: "middle" };
|
|
3286
|
-
}
|
|
3287
|
-
};
|
|
3288
|
-
for (let c = 0; c < N; c++) {
|
|
3289
|
-
const p = ptsRaw[c];
|
|
3290
|
-
if (p == null) continue;
|
|
3291
|
-
const v = series.values[c];
|
|
3292
|
-
if (v === null || v === void 0 || !Number.isFinite(v)) continue;
|
|
3293
|
-
const [xp, yp] = p;
|
|
3294
|
-
const { x: lx, y: ly, anchor } = computeAttrs(xp, yp);
|
|
3295
|
-
out.push(
|
|
3296
|
-
`<text x="${px(lx)}" y="${px(ly)}" text-anchor="${anchor}" ${dataLabelTextAttrs(spec, s, "#374151")}>${formatDataLabelValue(spec, s, v)}</text>`
|
|
3297
|
-
);
|
|
3298
|
-
}
|
|
3299
|
-
}
|
|
3300
|
-
if (!isStacked && series.trendline) {
|
|
3301
|
-
const finiteXs = [];
|
|
3302
|
-
const finiteYs = [];
|
|
3303
|
-
for (const [xp, yp] of pts) {
|
|
3304
|
-
if (Number.isFinite(yp)) {
|
|
3305
|
-
finiteXs.push(xp);
|
|
3306
|
-
finiteYs.push(yp);
|
|
3307
|
-
}
|
|
3308
|
-
}
|
|
3309
|
-
if (finiteXs.length >= 2) {
|
|
3310
|
-
const tlColor = series.trendline.color ?? color;
|
|
3311
|
-
out.push(trendlinePath(finiteXs, finiteYs, series.trendline, tlColor));
|
|
3312
|
-
}
|
|
3313
|
-
}
|
|
3314
|
-
}
|
|
3315
|
-
if (!isStacked && (spec.dropLines || spec.hiLowLines) && spec.series.length > 0) {
|
|
3316
|
-
for (let c = 0; c < N; c++) {
|
|
3317
|
-
const xp = f.plotX + c * step;
|
|
3318
|
-
if (spec.dropLines) {
|
|
3319
|
-
const firstVal = spec.series[0]?.values[c];
|
|
3320
|
-
if (firstVal !== null && firstVal !== void 0 && Number.isFinite(firstVal)) {
|
|
3321
|
-
const yp = f.plotY + f.plotH - (firstVal - min) / range * f.plotH;
|
|
3322
|
-
out.push(
|
|
3323
|
-
`<line x1="${px(xp)}" y1="${px(yp)}" x2="${px(xp)}" y2="${px(baseY)}" stroke="#9CA3AF" stroke-width="0.5" stroke-dasharray="2 2"/>`
|
|
3324
|
-
);
|
|
3325
|
-
}
|
|
3326
|
-
}
|
|
3327
|
-
if (spec.hiLowLines) {
|
|
3328
|
-
let hiV = -Infinity;
|
|
3329
|
-
let loV = Infinity;
|
|
3330
|
-
for (const s of spec.series) {
|
|
3331
|
-
const v = s.values[c];
|
|
3332
|
-
if (v === null || v === void 0 || !Number.isFinite(v)) continue;
|
|
3333
|
-
if (v > hiV) hiV = v;
|
|
3334
|
-
if (v < loV) loV = v;
|
|
3335
|
-
}
|
|
3336
|
-
if (hiV > loV) {
|
|
3337
|
-
const yHi = f.plotY + f.plotH - (hiV - min) / range * f.plotH;
|
|
3338
|
-
const yLo = f.plotY + f.plotH - (loV - min) / range * f.plotH;
|
|
3339
|
-
out.push(
|
|
3340
|
-
`<line x1="${px(xp)}" y1="${px(yHi)}" x2="${px(xp)}" y2="${px(yLo)}" stroke="#4B5563" stroke-width="1"/>`
|
|
3341
|
-
);
|
|
3342
|
-
}
|
|
3343
|
-
}
|
|
3344
|
-
}
|
|
3345
|
-
}
|
|
3346
|
-
return out.join("");
|
|
3347
|
-
};
|
|
3348
|
-
var renderPieChart = (f, spec, colors, doughnut) => {
|
|
3349
|
-
const series = spec.series[0];
|
|
3350
|
-
if (!series) return "";
|
|
3351
|
-
const values = series.values.map((v) => Math.max(0, v ?? 0));
|
|
3352
|
-
const total = values.reduce((a, b) => a + b, 0);
|
|
3353
|
-
if (total === 0) return "";
|
|
3354
|
-
const radius = Math.min(f.plotW, f.plotH) / 2 - 2;
|
|
3355
|
-
const cx = f.plotX + f.plotW / 2;
|
|
3356
|
-
const cy = f.plotY + f.plotH / 2;
|
|
3357
|
-
const innerR = doughnut ? radius * ((spec.holeSizePct ?? 55) / 100) : 0;
|
|
3358
|
-
const startDeg = spec.firstSliceAngleDeg ?? 0;
|
|
3359
|
-
let acc = -Math.PI / 2 + startDeg * Math.PI / 180;
|
|
3360
|
-
const out = [];
|
|
3361
|
-
for (let i = 0; i < values.length; i++) {
|
|
3362
|
-
const v = values[i] ?? 0;
|
|
3363
|
-
const angle = v / total * 2 * Math.PI;
|
|
3364
|
-
const start = acc;
|
|
3365
|
-
const end = acc + angle;
|
|
3366
|
-
acc = end;
|
|
3367
|
-
const largeArc = angle > Math.PI ? 1 : 0;
|
|
3368
|
-
const explPct = series.pointExplosions?.[i] ?? 0;
|
|
3369
|
-
const explOffset = explPct / 100 * radius;
|
|
3370
|
-
const midAngle = (start + end) / 2;
|
|
3371
|
-
const sx = cx + Math.cos(midAngle) * explOffset;
|
|
3372
|
-
const sy = cy + Math.sin(midAngle) * explOffset;
|
|
3373
|
-
const ox1 = sx + radius * Math.cos(start);
|
|
3374
|
-
const oy1 = sy + radius * Math.sin(start);
|
|
3375
|
-
const ox2 = sx + radius * Math.cos(end);
|
|
3376
|
-
const oy2 = sy + radius * Math.sin(end);
|
|
3377
|
-
const dptColor = series.pointColors?.[i];
|
|
3378
|
-
const color = dptColor ?? series.color ?? colors[i % colors.length];
|
|
3379
|
-
if (doughnut) {
|
|
3380
|
-
const ix1 = sx + innerR * Math.cos(start);
|
|
3381
|
-
const iy1 = sy + innerR * Math.sin(start);
|
|
3382
|
-
const ix2 = sx + innerR * Math.cos(end);
|
|
3383
|
-
const iy2 = sy + innerR * Math.sin(end);
|
|
3384
|
-
const d = `M${px(ox1)},${px(oy1)} A${px(radius)},${px(radius)} 0 ${largeArc} 1 ${px(ox2)},${px(oy2)} L${px(ix2)},${px(iy2)} A${px(innerR)},${px(innerR)} 0 ${largeArc} 0 ${px(ix1)},${px(iy1)} Z`;
|
|
3385
|
-
out.push(`<path d="${d}" fill="${color}" stroke="#FFFFFF" stroke-width="0.6"/>`);
|
|
3386
|
-
} else {
|
|
3387
|
-
const d = `M${px(sx)},${px(sy)} L${px(ox1)},${px(oy1)} A${px(radius)},${px(radius)} 0 ${largeArc} 1 ${px(ox2)},${px(oy2)} Z`;
|
|
3388
|
-
out.push(`<path d="${d}" fill="${color}" stroke="#FFFFFF" stroke-width="0.6"/>`);
|
|
3389
|
-
}
|
|
3390
|
-
const labelMid = (start + end) / 2;
|
|
3391
|
-
const pos = spec.series[0]?.dataLabels?.position ?? spec.dataLabels?.position;
|
|
3392
|
-
let labelR;
|
|
3393
|
-
let labelFill = "#FFFFFF";
|
|
3394
|
-
if (pos === "inEnd") {
|
|
3395
|
-
labelR = radius - 12;
|
|
3396
|
-
} else if (pos === "outEnd") {
|
|
3397
|
-
labelR = radius + 12;
|
|
3398
|
-
labelFill = "#374151";
|
|
3399
|
-
} else {
|
|
3400
|
-
labelR = doughnut ? (radius + innerR) / 2 : radius * 0.6;
|
|
3401
|
-
}
|
|
3402
|
-
const labelX = sx + labelR * Math.cos(labelMid);
|
|
3403
|
-
const labelY = sy + labelR * Math.sin(labelMid);
|
|
3404
|
-
const labels = [];
|
|
3405
|
-
if (spec.dataLabels?.showValue) labels.push(formatDataLabelValue(spec, 0, v));
|
|
3406
|
-
if (spec.dataLabels?.showPercent) labels.push(`${(v / total * 100).toFixed(0)}%`);
|
|
3407
|
-
if (spec.dataLabels?.showCategory) {
|
|
3408
|
-
const catLabel = spec.categories[i];
|
|
3409
|
-
if (catLabel) labels.push(catLabel);
|
|
3410
|
-
}
|
|
3411
|
-
if (labels.length > 0) {
|
|
3412
|
-
out.push(
|
|
3413
|
-
`<text x="${px(labelX)}" y="${px(labelY)}" text-anchor="middle" dominant-baseline="middle" ${dataLabelTextAttrs(spec, 0, labelFill, 10, true)}>${escapeXml2(labels.join(spec.series[0]?.dataLabels?.separator ?? spec.dataLabels?.separator ?? " "))}</text>`
|
|
3414
|
-
);
|
|
3415
|
-
}
|
|
3416
|
-
}
|
|
3417
|
-
return out.join("");
|
|
3418
|
-
};
|
|
3419
|
-
var scatterAxisBounds = (vals) => {
|
|
3420
|
-
let min = Infinity;
|
|
3421
|
-
let max = -Infinity;
|
|
3422
|
-
for (const v of vals) {
|
|
3423
|
-
if (Number.isFinite(v)) {
|
|
3424
|
-
if (v < min) min = v;
|
|
3425
|
-
if (v > max) max = v;
|
|
3426
|
-
}
|
|
3427
|
-
}
|
|
3428
|
-
if (!Number.isFinite(min)) return { min: 0, max: 1 };
|
|
3429
|
-
if (min === max) return { min: min - 1, max: max + 1 };
|
|
3430
|
-
const ticks = niceTicks(min, max);
|
|
3431
|
-
const step = ticks.length >= 2 ? ticks[1] - ticks[0] : (max - min) / 4 || 1;
|
|
3432
|
-
return { min: Math.floor(min / step) * step, max: Math.ceil(max / step) * step };
|
|
3433
|
-
};
|
|
3434
|
-
var xyPoints = (series) => {
|
|
3435
|
-
const hasX = series.xValues !== void 0 && series.xValues.length > 0;
|
|
3436
|
-
const n = Math.max(series.xValues?.length ?? 0, series.values.length);
|
|
3437
|
-
const out = [];
|
|
3438
|
-
for (let i = 0; i < n; i++) {
|
|
3439
|
-
let x;
|
|
3440
|
-
if (hasX) {
|
|
3441
|
-
const xv = series.xValues[i];
|
|
3442
|
-
if (xv === null || xv === void 0 || !Number.isFinite(xv)) continue;
|
|
3443
|
-
x = xv;
|
|
3444
|
-
} else {
|
|
3445
|
-
x = i + 1;
|
|
3446
|
-
}
|
|
3447
|
-
const y = series.values[i];
|
|
3448
|
-
if (y === null || y === void 0 || !Number.isFinite(y)) continue;
|
|
3449
|
-
const sz = series.bubbleSizes?.[i];
|
|
3450
|
-
out.push({ x, y, size: sz != null && Number.isFinite(sz) ? sz : 0 });
|
|
3451
|
-
}
|
|
3452
|
-
return out;
|
|
3453
|
-
};
|
|
3454
|
-
var renderScatterAxes = (f, spec, xB, yB) => {
|
|
3455
|
-
const yAxis = {
|
|
3456
|
-
orientation: "vertical",
|
|
3457
|
-
min: yB.min,
|
|
3458
|
-
max: yB.max,
|
|
3459
|
-
...spec.valueAxis?.numberFormat !== void 0 ? { numberFormat: spec.valueAxis.numberFormat } : {}
|
|
3460
|
-
};
|
|
3461
|
-
const xAxis = { orientation: "horizontal", min: xB.min, max: xB.max };
|
|
3462
|
-
return renderValueAxis(f, yAxis) + renderValueAxis(f, xAxis);
|
|
3463
|
-
};
|
|
3464
|
-
var renderScatterChart = (f, spec, colors) => {
|
|
3465
|
-
const perSeries = spec.series.map((s) => xyPoints(s));
|
|
3466
|
-
const allX = [];
|
|
3467
|
-
const allY = [];
|
|
3468
|
-
for (const pts of perSeries) {
|
|
3469
|
-
for (const p of pts) {
|
|
3470
|
-
allX.push(p.x);
|
|
3471
|
-
allY.push(p.y);
|
|
3472
|
-
}
|
|
3473
|
-
}
|
|
3474
|
-
if (allX.length === 0) return "";
|
|
3475
|
-
const xB = scatterAxisBounds(allX);
|
|
3476
|
-
const yB = scatterAxisBounds(allY);
|
|
3477
|
-
if (spec.valueAxis?.min !== void 0) yB.min = spec.valueAxis.min;
|
|
3478
|
-
if (spec.valueAxis?.max !== void 0) yB.max = spec.valueAxis.max;
|
|
3479
|
-
const xRange = xB.max - xB.min || 1;
|
|
3480
|
-
const yRange = yB.max - yB.min || 1;
|
|
3481
|
-
const projX = (x) => f.plotX + (x - xB.min) / xRange * f.plotW;
|
|
3482
|
-
const projY = (y) => f.plotY + f.plotH - (y - yB.min) / yRange * f.plotH;
|
|
3483
|
-
const out = [renderScatterAxes(f, spec, xB, yB)];
|
|
3484
|
-
const style = spec.scatterStyle;
|
|
3485
|
-
const showLine = style === "line" || style === "lineMarker" || style === "smooth" || style === "smoothMarker";
|
|
3486
|
-
const smooth = style === "smooth" || style === "smoothMarker";
|
|
3487
|
-
const showMarker = style === void 0 || style === "marker" || style === "lineMarker" || style === "smoothMarker";
|
|
3488
|
-
for (let s = 0; s < spec.series.length; s++) {
|
|
3489
|
-
const series = spec.series[s];
|
|
3490
|
-
const pts = perSeries[s];
|
|
3491
|
-
if (pts.length === 0) continue;
|
|
3492
|
-
const color = series.color ?? colors[s % colors.length];
|
|
3493
|
-
const proj = pts.map((p) => [projX(p.x), projY(p.y)]);
|
|
3494
|
-
if (showLine && proj.length >= 2) {
|
|
3495
|
-
const d = smooth && proj.length > 2 ? smoothPath(proj) : proj.map(([xp, yp], i) => `${i === 0 ? "M" : "L"}${px(xp)},${px(yp)}`).join(" ");
|
|
3496
|
-
const lineWPx = series.lineWidthEmu ? Math.max(0.3, series.lineWidthEmu / EMU_PER_PX) : 1.5;
|
|
3497
|
-
out.push(
|
|
3498
|
-
`<path d="${d}" fill="none" stroke="${color}" stroke-width="${lineWPx.toFixed(2)}" stroke-linejoin="round" stroke-linecap="round"/>`
|
|
3499
|
-
);
|
|
3500
|
-
}
|
|
3501
|
-
const sym = series.markerSymbol;
|
|
3502
|
-
const drawMarker = sym === "none" ? false : showMarker || sym !== void 0 && sym !== "auto";
|
|
3503
|
-
if (drawMarker) {
|
|
3504
|
-
const r = Math.max(1.5, (series.markerSizePt ?? 5) * 0.5);
|
|
3505
|
-
const glyph = sym && sym !== "auto" && sym !== "none" ? sym : "circle";
|
|
3506
|
-
for (const [xp, yp] of proj) out.push(seriesMarker(glyph, xp, yp, r, color));
|
|
3507
|
-
}
|
|
3508
|
-
}
|
|
3509
|
-
return out.join("");
|
|
3510
|
-
};
|
|
3511
|
-
var renderBubbleChart = (f, spec, colors) => {
|
|
3512
|
-
const perSeries = spec.series.map((s) => xyPoints(s));
|
|
3513
|
-
const allX = [];
|
|
3514
|
-
const allY = [];
|
|
3515
|
-
let maxSize = 0;
|
|
3516
|
-
for (const pts of perSeries) {
|
|
3517
|
-
for (const p of pts) {
|
|
3518
|
-
allX.push(p.x);
|
|
3519
|
-
allY.push(p.y);
|
|
3520
|
-
if (p.size > maxSize) maxSize = p.size;
|
|
3521
|
-
}
|
|
3522
|
-
}
|
|
3523
|
-
if (allX.length === 0) return "";
|
|
3524
|
-
const xB = scatterAxisBounds(allX);
|
|
3525
|
-
const yB = scatterAxisBounds(allY);
|
|
3526
|
-
if (spec.valueAxis?.min !== void 0) yB.min = spec.valueAxis.min;
|
|
3527
|
-
if (spec.valueAxis?.max !== void 0) yB.max = spec.valueAxis.max;
|
|
3528
|
-
const xRange = xB.max - xB.min || 1;
|
|
3529
|
-
const yRange = yB.max - yB.min || 1;
|
|
3530
|
-
const projX = (x) => f.plotX + (x - xB.min) / xRange * f.plotW;
|
|
3531
|
-
const projY = (y) => f.plotY + f.plotH - (y - yB.min) / yRange * f.plotH;
|
|
3532
|
-
const out = [renderScatterAxes(f, spec, xB, yB)];
|
|
3533
|
-
const scaleFraction = spec.bubbleScale !== void 0 ? spec.bubbleScale / 100 : 0.25;
|
|
3534
|
-
const maxRadiusPx = Math.max(2, scaleFraction * Math.min(f.plotW, f.plotH) * 0.5);
|
|
3535
|
-
const areaMode = (spec.bubbleSizeRepresents ?? "area") === "area";
|
|
3536
|
-
const radiusFor = (size) => {
|
|
3537
|
-
if (maxSize <= 0 || size <= 0) return Math.max(1.5, maxRadiusPx * 0.15);
|
|
3538
|
-
const frac = areaMode ? Math.sqrt(size / maxSize) : size / maxSize;
|
|
3539
|
-
return Math.max(1.5, maxRadiusPx * frac);
|
|
3540
|
-
};
|
|
3541
|
-
for (let s = 0; s < spec.series.length; s++) {
|
|
3542
|
-
const series = spec.series[s];
|
|
3543
|
-
const pts = perSeries[s];
|
|
3544
|
-
const color = series.color ?? colors[s % colors.length];
|
|
3545
|
-
for (const p of pts) {
|
|
3546
|
-
const cx = projX(p.x);
|
|
3547
|
-
const cy = projY(p.y);
|
|
3548
|
-
const r = radiusFor(p.size);
|
|
3549
|
-
out.push(
|
|
3550
|
-
`<circle cx="${px(cx)}" cy="${px(cy)}" r="${r.toFixed(2)}" fill="${color}" fill-opacity="0.55" stroke="${color}" stroke-width="0.75"/>`
|
|
3551
|
-
);
|
|
3552
|
-
}
|
|
3553
|
-
}
|
|
3554
|
-
return out.join("");
|
|
3555
|
-
};
|
|
3556
|
-
var renderRadarChart = (f, spec, colors) => {
|
|
3557
|
-
const N = pointCount(spec);
|
|
3558
|
-
if (N === 0 || spec.series.length === 0) return "";
|
|
3559
|
-
let max = -Infinity;
|
|
3560
|
-
let min = 0;
|
|
3561
|
-
for (const sr of spec.series) {
|
|
3562
|
-
for (const v of sr.values) {
|
|
3563
|
-
if (v !== null && Number.isFinite(v) && v > max) max = v;
|
|
3564
|
-
}
|
|
3565
|
-
}
|
|
3566
|
-
if (!Number.isFinite(max)) max = 1;
|
|
3567
|
-
if (spec.valueAxis?.min !== void 0) min = spec.valueAxis.min;
|
|
3568
|
-
if (spec.valueAxis?.max !== void 0) max = spec.valueAxis.max;
|
|
3569
|
-
if (max <= min) max = min + 1;
|
|
3570
|
-
const range = max - min;
|
|
3571
|
-
const cx = f.plotX + f.plotW / 2;
|
|
3572
|
-
const cy = f.plotY + f.plotH / 2;
|
|
3573
|
-
const R = Math.max(4, Math.min(f.plotW, f.plotH) / 2 - 14);
|
|
3574
|
-
const angleAt = (i) => -Math.PI / 2 + i * 2 * Math.PI / N;
|
|
3575
|
-
const out = [];
|
|
3576
|
-
for (const t of niceTicks(min, max)) {
|
|
3577
|
-
if (t < min || t > max + 1e-9) continue;
|
|
3578
|
-
const rr = (t - min) / range * R;
|
|
3579
|
-
if (rr <= 0) continue;
|
|
3580
|
-
const ring = [];
|
|
3581
|
-
for (let i = 0; i < N; i++) {
|
|
3582
|
-
const a = angleAt(i);
|
|
3583
|
-
ring.push(`${px(cx + rr * Math.cos(a))},${px(cy + rr * Math.sin(a))}`);
|
|
3584
|
-
}
|
|
3585
|
-
out.push(
|
|
3586
|
-
`<polygon points="${ring.join(" ")}" fill="none" stroke="#E5E7EB" stroke-width="0.5"/>`
|
|
3587
|
-
);
|
|
3588
|
-
out.push(
|
|
3589
|
-
`<text x="${px(cx + 3)}" y="${px(cy - rr)}" dominant-baseline="middle" font-family="sans-serif" font-size="8" fill="#9CA3AF">${escapeXml2(formatTick(t))}</text>`
|
|
3590
|
-
);
|
|
3591
|
-
}
|
|
3592
|
-
for (let i = 0; i < N; i++) {
|
|
3593
|
-
const a = angleAt(i);
|
|
3594
|
-
out.push(
|
|
3595
|
-
`<line x1="${px(cx)}" y1="${px(cy)}" x2="${px(cx + R * Math.cos(a))}" y2="${px(cy + R * Math.sin(a))}" stroke="#D1D5DB" stroke-width="0.5"/>`
|
|
3596
|
-
);
|
|
3597
|
-
const cat = spec.categories[i] ?? String(i + 1);
|
|
3598
|
-
const lx = cx + (R + 8) * Math.cos(a);
|
|
3599
|
-
const ly = cy + (R + 8) * Math.sin(a);
|
|
3600
|
-
const cosA = Math.cos(a);
|
|
3601
|
-
const anchor = Math.abs(cosA) < 0.3 ? "middle" : cosA > 0 ? "start" : "end";
|
|
3602
|
-
const label = cat.length > 12 ? `${cat.slice(0, 11)}\u2026` : cat;
|
|
3603
|
-
out.push(
|
|
3604
|
-
`<text x="${px(lx)}" y="${px(ly)}" text-anchor="${anchor}" dominant-baseline="middle" ${axisTickAttrs(spec.categoryAxisLabelStyle)}>${escapeXml2(label)}</text>`
|
|
3605
|
-
);
|
|
3606
|
-
}
|
|
3607
|
-
const filled = spec.radarStyle === "filled";
|
|
3608
|
-
const showMarker = spec.radarStyle === "marker";
|
|
3609
|
-
for (let s = 0; s < spec.series.length; s++) {
|
|
3610
|
-
const series = spec.series[s];
|
|
3611
|
-
const color = series.color ?? colors[s % colors.length];
|
|
3612
|
-
const proj = [];
|
|
3613
|
-
for (let i = 0; i < N; i++) {
|
|
3614
|
-
const v = series.values[i];
|
|
3615
|
-
if (v === null || v === void 0 || !Number.isFinite(v)) continue;
|
|
3616
|
-
const rr = (v - min) / range * R;
|
|
3617
|
-
const a = angleAt(i);
|
|
3618
|
-
proj.push([cx + rr * Math.cos(a), cy + rr * Math.sin(a)]);
|
|
3619
|
-
}
|
|
3620
|
-
if (proj.length < 2) continue;
|
|
3621
|
-
const ptsStr = proj.map(([xp, yp]) => `${px(xp)},${px(yp)}`).join(" ");
|
|
3622
|
-
const lineWPx = series.lineWidthEmu ? Math.max(0.3, series.lineWidthEmu / EMU_PER_PX) : 1.8;
|
|
3623
|
-
out.push(
|
|
3624
|
-
filled ? `<polygon points="${ptsStr}" fill="${color}" fill-opacity="0.3" stroke="${color}" stroke-width="${lineWPx.toFixed(2)}" stroke-linejoin="round"/>` : `<polygon points="${ptsStr}" fill="none" stroke="${color}" stroke-width="${lineWPx.toFixed(2)}" stroke-linejoin="round"/>`
|
|
3625
|
-
);
|
|
3626
|
-
if (showMarker) {
|
|
3627
|
-
const r = Math.max(1.5, (series.markerSizePt ?? 5) * 0.5);
|
|
3628
|
-
const glyph = series.markerSymbol && series.markerSymbol !== "auto" && series.markerSymbol !== "none" ? series.markerSymbol : "circle";
|
|
3629
|
-
for (const [xp, yp] of proj) out.push(seriesMarker(glyph, xp, yp, r, color));
|
|
3630
|
-
}
|
|
3631
|
-
}
|
|
3632
|
-
return out.join("");
|
|
3633
|
-
};
|
|
3634
|
-
var renderChart = (shape, x, y, w, h, transform, theme) => {
|
|
3635
|
-
let spec = null;
|
|
3636
|
-
try {
|
|
3637
|
-
spec = getShapeChartSpec(shape);
|
|
3638
|
-
} catch {
|
|
3639
|
-
return null;
|
|
3640
|
-
}
|
|
3641
|
-
if (!spec) return null;
|
|
3642
|
-
const colors = accentSequence(theme);
|
|
3643
|
-
const isCartesian = spec.kind === "column" || spec.kind === "bar" || spec.kind === "line" || spec.kind === "area";
|
|
3644
|
-
const hasAxes = isCartesian || spec.kind === "scatter" || spec.kind === "bubble";
|
|
3645
|
-
const hasLegend = spec.legend !== void 0 && spec.legend.position !== null;
|
|
3646
|
-
const f = layoutChart(
|
|
3647
|
-
x,
|
|
3648
|
-
y,
|
|
3649
|
-
w,
|
|
3650
|
-
h,
|
|
3651
|
-
!!spec.title,
|
|
3652
|
-
hasAxes,
|
|
3653
|
-
spec.titleOverlay ?? false,
|
|
3654
|
-
spec.legend?.overlay ?? false,
|
|
3655
|
-
hasLegend
|
|
3656
|
-
);
|
|
3657
|
-
const allNamesForLegend = spec.kind === "pie" || spec.kind === "doughnut" ? Array.from(spec.categories) : spec.series.map((s) => s.name);
|
|
3658
|
-
const allColorsForLegend = spec.kind === "pie" || spec.kind === "doughnut" ? spec.categories.map(
|
|
3659
|
-
(_, i) => spec.series[0]?.pointColors?.[i] ?? spec.series[0]?.color ?? colors[i % colors.length] ?? "#888"
|
|
3660
|
-
) : spec.series.map((s, i) => s.color ?? colors[i % colors.length] ?? "#888");
|
|
3661
|
-
const hiddenSet = new Set(spec.legend?.hiddenIndices ?? []);
|
|
3662
|
-
const seriesNamesForLegend = allNamesForLegend.filter((_, i) => !hiddenSet.has(i));
|
|
3663
|
-
const seriesColorsForLegend = allColorsForLegend.filter((_, i) => !hiddenSet.has(i));
|
|
3664
|
-
const markerSymbolsForLegend = spec.kind === "line" || spec.kind === "area" ? spec.series.map((s) => s.markerSymbol).filter((_, i) => !hiddenSet.has(i)) : void 0;
|
|
3665
|
-
let finiteCount = 0;
|
|
3666
|
-
for (const s of spec.series) {
|
|
3667
|
-
for (const v of s.values) if (v !== null && Number.isFinite(v)) finiteCount++;
|
|
3668
|
-
}
|
|
3669
|
-
let plot = "";
|
|
3670
|
-
let axes = "";
|
|
3671
|
-
if (isCartesian) {
|
|
3672
|
-
const { min, max, step } = seriesMinMax(spec);
|
|
3673
|
-
const N = pointCount(spec);
|
|
3674
|
-
const majorUnit = spec.valueAxis?.majorUnit ?? step;
|
|
3675
|
-
const numberFormat = spec.valueAxis?.numberFormat;
|
|
3676
|
-
const axisExtras = {
|
|
3677
|
-
...majorUnit !== void 0 ? { majorUnit } : {},
|
|
3678
|
-
...numberFormat !== void 0 ? { numberFormat } : {},
|
|
3679
|
-
...spec.valueAxisMajorGridlines !== void 0 ? { majorGridlines: spec.valueAxisMajorGridlines } : {},
|
|
3680
|
-
...spec.valueAxisLabelStyle !== void 0 ? { labelStyle: spec.valueAxisLabelStyle } : {},
|
|
3681
|
-
...spec.valueAxisMajorGridlineColor !== void 0 ? { majorGridlineColor: spec.valueAxisMajorGridlineColor } : {},
|
|
3682
|
-
...spec.valueAxisMajorTickMark !== void 0 ? { majorTickMark: spec.valueAxisMajorTickMark } : {},
|
|
3683
|
-
...spec.valueAxisLineColor !== void 0 ? { lineColor: spec.valueAxisLineColor } : {},
|
|
3684
|
-
...spec.valueAxisLabelRotationDeg !== void 0 ? { labelRotationDeg: spec.valueAxisLabelRotationDeg } : {},
|
|
3685
|
-
...spec.valueAxis?.displayUnits !== void 0 ? { displayUnits: spec.valueAxis.displayUnits } : {}
|
|
3686
|
-
};
|
|
3687
|
-
const valueAxis = spec.kind === "bar" ? { orientation: "horizontal", min, max, ...axisExtras } : { orientation: "vertical", min, max, ...axisExtras };
|
|
3688
|
-
if (!spec.valueAxisHidden) axes = renderValueAxis(f, valueAxis);
|
|
3689
|
-
const labelsHidden = spec.categoryAxisHidden || spec.categoryAxisTickLabelPos === "none";
|
|
3690
|
-
if (N > 0 && !labelsHidden) {
|
|
3691
|
-
axes += renderCategoryAxis(
|
|
3692
|
-
f,
|
|
3693
|
-
spec.kind === "bar" ? "vertical" : "horizontal",
|
|
3694
|
-
spec.categories,
|
|
3695
|
-
N,
|
|
3696
|
-
spec.categoryAxisTickLabelSkip ?? 1,
|
|
3697
|
-
spec.categoryAxisLabelStyle,
|
|
3698
|
-
spec.categoryAxisLabelRotationDeg,
|
|
3699
|
-
spec.categoryAxisLabelAlign,
|
|
3700
|
-
spec.categoryAxisLineColor
|
|
3701
|
-
);
|
|
3702
|
-
}
|
|
3703
|
-
}
|
|
3704
|
-
switch (spec.kind) {
|
|
3705
|
-
case "column":
|
|
3706
|
-
case "bar":
|
|
3707
|
-
plot = spec.kind === "column" ? renderColumnChart(f, spec, colors) : renderBarChart(f, spec, colors);
|
|
3708
|
-
break;
|
|
3709
|
-
case "line":
|
|
3710
|
-
plot = renderLineChart(f, spec, colors, false);
|
|
3711
|
-
break;
|
|
3712
|
-
case "area":
|
|
3713
|
-
plot = renderLineChart(f, spec, colors, true);
|
|
3714
|
-
break;
|
|
3715
|
-
case "pie":
|
|
3716
|
-
plot = renderPieChart(f, spec, colors, false);
|
|
3717
|
-
break;
|
|
3718
|
-
case "doughnut":
|
|
3719
|
-
plot = renderPieChart(f, spec, colors, true);
|
|
3720
|
-
break;
|
|
3721
|
-
case "scatter":
|
|
3722
|
-
plot = renderScatterChart(f, spec, colors);
|
|
3723
|
-
break;
|
|
3724
|
-
case "radar":
|
|
3725
|
-
plot = renderRadarChart(f, spec, colors);
|
|
3726
|
-
break;
|
|
3727
|
-
case "bubble":
|
|
3728
|
-
plot = renderBubbleChart(f, spec, colors);
|
|
3729
|
-
break;
|
|
3730
|
-
default:
|
|
3731
|
-
return null;
|
|
3732
|
-
}
|
|
3733
|
-
const emptyHint = finiteCount === 0 ? `<text x="${px(f.plotX + f.plotW / 2)}" y="${px(f.plotY + f.plotH / 2)}" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="12" fill="#9CA3AF">${escapeXml2(`chart (${spec.kind}) \u2014 no data`)}</text>` : "";
|
|
3734
|
-
const axisTitleAttrs = (style) => {
|
|
3735
|
-
const sz = style?.sizePt ?? 11;
|
|
3736
|
-
const fill = style?.color ?? "#374151";
|
|
3737
|
-
const weight = style?.bold === false ? "400" : "600";
|
|
3738
|
-
const italicAttr = style?.italic ? ' font-style="italic"' : "";
|
|
3739
|
-
return `font-family="sans-serif" font-size="${sz.toFixed(1)}" fill="${fill}" font-weight="${weight}"${italicAttr}`;
|
|
3740
|
-
};
|
|
3741
|
-
const valueAxisTitleRot = spec.valueAxisTitleRotationDeg ?? -90;
|
|
3742
|
-
const valueAxisTitleSvg = spec.valueAxisTitle ? `<text x="${px(f.plotX - 26)}" y="${px(f.plotY + f.plotH / 2)}" text-anchor="middle" ${axisTitleAttrs(spec.valueAxisTitleStyle)} transform="rotate(${valueAxisTitleRot} ${px(f.plotX - 26)} ${px(f.plotY + f.plotH / 2)})">${escapeXml2(spec.valueAxisTitle)}</text>` : "";
|
|
3743
|
-
const catTitleRot = spec.categoryAxisTitleRotationDeg ?? 0;
|
|
3744
|
-
const catTitleCx = f.plotX + f.plotW / 2;
|
|
3745
|
-
const catTitleCy = f.plotY + f.plotH + 16;
|
|
3746
|
-
const catTitleTransform = catTitleRot !== 0 ? ` transform="rotate(${catTitleRot} ${px(catTitleCx)} ${px(catTitleCy)})"` : "";
|
|
3747
|
-
const categoryAxisTitleSvg = spec.categoryAxisTitle ? `<text x="${px(catTitleCx)}" y="${px(catTitleCy)}" text-anchor="middle" ${axisTitleAttrs(spec.categoryAxisTitleStyle)}${catTitleTransform}>${escapeXml2(spec.categoryAxisTitle)}</text>` : "";
|
|
3748
|
-
return [
|
|
3749
|
-
`<g${transform}>`,
|
|
3750
|
-
// Chart-area backdrop honors <c:chartSpace><c:spPr><a:solidFill> /
|
|
3751
|
-
// <a:ln>. plot-area gets its own tinted rect + border when
|
|
3752
|
-
// <c:plotArea><c:spPr> authors them.
|
|
3753
|
-
`<rect x="${px(f.x)}" y="${px(f.y)}" width="${px(f.w)}" height="${px(f.h)}" fill="${spec.chartAreaFill ?? "#FFFFFF"}" stroke="${spec.chartAreaStrokeColor ?? "#E5E7EB"}" stroke-width="0.6"${spec.roundedCorners ? ' rx="6" ry="6"' : ""}/>`,
|
|
3754
|
-
spec.plotAreaFill || spec.plotAreaStrokeColor ? `<rect x="${px(f.plotX)}" y="${px(f.plotY)}" width="${px(f.plotW)}" height="${px(f.plotH)}" fill="${spec.plotAreaFill ?? "none"}" stroke="${spec.plotAreaStrokeColor ?? "none"}" stroke-width="0.6"/>` : "",
|
|
3755
|
-
renderChartTitle(f, spec.title ?? "", spec.titleStyle),
|
|
3756
|
-
axes,
|
|
3757
|
-
valueAxisTitleSvg,
|
|
3758
|
-
categoryAxisTitleSvg,
|
|
3759
|
-
plot,
|
|
3760
|
-
emptyHint,
|
|
3761
|
-
hasLegend ? renderChartLegend(
|
|
3762
|
-
f,
|
|
3763
|
-
seriesNamesForLegend,
|
|
3764
|
-
seriesColorsForLegend,
|
|
3765
|
-
spec.legend?.position ?? "b",
|
|
3766
|
-
spec.legend?.textStyle,
|
|
3767
|
-
// Marker glyphs only carry visual meaning for line / area
|
|
3768
|
-
// charts; bar / column / pie use the swatch rect.
|
|
3769
|
-
markerSymbolsForLegend
|
|
3770
|
-
) : "",
|
|
3771
|
-
"</g>"
|
|
3772
|
-
].join("");
|
|
3773
|
-
};
|
|
3774
|
-
var cellParaData = (paragraphs) => {
|
|
3775
|
-
let hasText = false;
|
|
3776
|
-
const paraData = paragraphs.map((para) => {
|
|
3777
|
-
const runs = [];
|
|
3778
|
-
for (const el of para.elements) {
|
|
3779
|
-
if (el.kind === "br") {
|
|
3780
|
-
runs.push({ text: "\n", fmt: null, sizePt: DEFAULT_BODY_PT });
|
|
3781
|
-
continue;
|
|
3782
|
-
}
|
|
3783
|
-
if (el.text.trim()) hasText = true;
|
|
3784
|
-
runs.push({ text: el.text, fmt: el.format, sizePt: el.format?.size ?? DEFAULT_BODY_PT });
|
|
3785
|
-
}
|
|
3786
|
-
return {
|
|
3787
|
-
align: para.align ?? "left",
|
|
3788
|
-
level: 0,
|
|
3789
|
-
bulletStyle: null,
|
|
3790
|
-
bulletDetail: { color: null, sizePct: null, sizePts: null, font: null },
|
|
3791
|
-
bulletIsPicture: false,
|
|
3792
|
-
// Table cells never carry picture bullets (no <a:pPr> bullet model
|
|
3793
|
-
// in <a:tc> text bodies).
|
|
3794
|
-
bulletImageHref: null,
|
|
3795
|
-
runs,
|
|
3796
|
-
lineSpacing: null,
|
|
3797
|
-
spcBefPts: null,
|
|
3798
|
-
spcAftPts: null,
|
|
3799
|
-
indent: { leftEmu: null, rightEmu: null, firstLineEmu: null }
|
|
3800
|
-
};
|
|
3801
|
-
});
|
|
3802
|
-
return { paraData, hasText };
|
|
3803
|
-
};
|
|
3804
|
-
var renderTableCellText = (paragraphs, cx, cy, cw, ch, color, pres, shape, theme, themeFace, ctx, vAnchor, margins) => {
|
|
3805
|
-
const { paraData, hasText } = cellParaData(paragraphs);
|
|
3806
|
-
if (!hasText) return "";
|
|
3807
|
-
const defaultPadPx = 4;
|
|
3808
|
-
const padL = margins.left !== null ? margins.left / EMU_PER_PX : defaultPadPx;
|
|
3809
|
-
const padR = margins.right !== null ? margins.right / EMU_PER_PX : defaultPadPx;
|
|
3810
|
-
const padT = margins.top !== null ? margins.top / EMU_PER_PX : defaultPadPx;
|
|
3811
|
-
const padB = margins.bottom !== null ? margins.bottom / EMU_PER_PX : defaultPadPx;
|
|
3812
|
-
const innerX = cx + padL;
|
|
3813
|
-
const innerY = cy + padT;
|
|
3814
|
-
const innerW = Math.max(0, cw - padL - padR);
|
|
3815
|
-
const innerH = Math.max(0, ch - padT - padB);
|
|
3816
|
-
if (innerW <= 0 || innerH <= 0) return "";
|
|
3817
|
-
if (ctx.mode === "svg") {
|
|
3818
|
-
return buildAndLayoutSvgText({
|
|
3819
|
-
theme,
|
|
3820
|
-
paraData,
|
|
3821
|
-
numberLabels: paraData.map(() => null),
|
|
3822
|
-
autoFitScale: 1,
|
|
3823
|
-
lineHeightScale: 1,
|
|
3824
|
-
defaultPt: DEFAULT_BODY_PT,
|
|
3825
|
-
themeFace,
|
|
3826
|
-
defaultColor: color,
|
|
3827
|
-
anchor: vAnchor,
|
|
3828
|
-
wrap: true,
|
|
3829
|
-
innerX: innerX * EMU_PER_PX,
|
|
3830
|
-
innerY: innerY * EMU_PER_PX,
|
|
3831
|
-
innerW: innerW * EMU_PER_PX,
|
|
3832
|
-
innerH: innerH * EMU_PER_PX,
|
|
3833
|
-
measure: ctx.measure,
|
|
3834
|
-
// Cell-level vertical text (<a:tcPr vert>) isn't modeled yet; cells lay
|
|
3835
|
-
// out horizontally, single-column.
|
|
3836
|
-
vert: "none",
|
|
3837
|
-
columns: null
|
|
3838
|
-
});
|
|
3839
|
-
}
|
|
3840
|
-
const justify = vAnchor === "top" ? "flex-start" : vAnchor === "bottom" ? "flex-end" : "center";
|
|
3841
|
-
const familyFont = themeFace ? `${escapeXml2(themeFace)}, ${DEFAULT_FONT}` : DEFAULT_FONT;
|
|
3842
|
-
const body = paraData.map((para) => {
|
|
3843
|
-
const runHtml = para.runs.map((run) => renderRun(run.text, run.fmt, theme, run.sizePt, run.fmt?.size === void 0)).join("");
|
|
3844
|
-
const textAlign = ALIGNMENT_TO_CSS[para.align] ?? "left";
|
|
3845
|
-
return `<p style="margin:0;padding:0;text-align:${textAlign};line-height:1.2">${runHtml || "​"}</p>`;
|
|
3846
|
-
}).join("");
|
|
3847
|
-
return `<foreignObject x="${px(innerX)}" y="${px(innerY)}" width="${px(innerW)}" height="${px(innerH)}"><div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;flex-direction:column;justify-content:${justify};width:100%;height:100%;box-sizing:border-box;overflow:hidden;font-family:${familyFont};color:${color};word-break:break-word">${body}</div></foreignObject>`;
|
|
3848
|
-
};
|
|
3849
|
-
var renderTable = (shape, pres, x, y, w, h, transform, theme, ctx) => {
|
|
3850
|
-
let dims;
|
|
3851
|
-
let widths;
|
|
3852
|
-
let heights;
|
|
3853
|
-
let cells;
|
|
3854
|
-
try {
|
|
3855
|
-
dims = getTableDimensions(shape);
|
|
3856
|
-
widths = getTableColumnWidths(shape);
|
|
3857
|
-
heights = getTableRowHeights(shape);
|
|
3858
|
-
cells = getTableCells(shape);
|
|
3859
|
-
} catch {
|
|
3860
|
-
return null;
|
|
3861
|
-
}
|
|
3862
|
-
if (dims.rows === 0 || dims.cols === 0) return null;
|
|
3863
|
-
const xPx = x / EMU_PER_PX;
|
|
3864
|
-
const yPx = y / EMU_PER_PX;
|
|
3865
|
-
const widthsPx = widths.map((w0) => w0 / EMU_PER_PX);
|
|
3866
|
-
const heightsPx = heights.map((h0) => h0 / EMU_PER_PX);
|
|
3867
|
-
const wSum = widthsPx.reduce((a, b) => a + b, 0);
|
|
3868
|
-
const hSum = heightsPx.reduce((a, b) => a + b, 0);
|
|
3869
|
-
const wScale = wSum > 0 ? w / EMU_PER_PX / wSum : 1;
|
|
3870
|
-
const hScale = hSum > 0 ? h / EMU_PER_PX / hSum : 1;
|
|
3871
|
-
const colXs = [xPx];
|
|
3872
|
-
for (let c = 0; c < widthsPx.length; c++) {
|
|
3873
|
-
colXs.push((colXs[c] ?? xPx) + (widthsPx[c] ?? 0) * wScale);
|
|
3874
|
-
}
|
|
3875
|
-
const rowYs = [yPx];
|
|
3876
|
-
for (let r = 0; r < heightsPx.length; r++) {
|
|
3877
|
-
rowYs.push((rowYs[r] ?? yPx) + (heightsPx[r] ?? 0) * hScale);
|
|
3878
|
-
}
|
|
3879
|
-
const flags = getTableStyleFlags(shape);
|
|
3880
|
-
const accent = theme ? normalizeHex(theme.accent1) : "#4472C4";
|
|
3881
|
-
const headerFill = accent;
|
|
3882
|
-
const bandFill = mixHex(accent, "#FFFFFF", 0.92);
|
|
3883
|
-
const textColor = activeDeckTextColor;
|
|
3884
|
-
const tableThemeFace = getPresentationFonts(pres)?.minorLatin ?? null;
|
|
3885
|
-
const out = [];
|
|
3886
|
-
out.push(`<g${transform}>`);
|
|
3887
|
-
out.push(
|
|
3888
|
-
`<rect x="${px(xPx)}" y="${px(yPx)}" width="${px((colXs[widthsPx.length] ?? xPx) - xPx)}" height="${px((rowYs[heightsPx.length] ?? yPx) - yPx)}" fill="#FFFFFF"/>`
|
|
3889
|
-
);
|
|
3890
|
-
const borderEdges = [];
|
|
3891
|
-
for (let r = 0; r < dims.rows; r++) {
|
|
3892
|
-
for (let c = 0; c < dims.cols; c++) {
|
|
3893
|
-
const cell = cells[r]?.[c];
|
|
3894
|
-
if (!cell) continue;
|
|
3895
|
-
const typedCell = cell;
|
|
3896
|
-
const span = getTableCellSpan(typedCell);
|
|
3897
|
-
if (span.hMerge || span.vMerge) continue;
|
|
3898
|
-
const cx = colXs[c] ?? xPx;
|
|
3899
|
-
const cy = rowYs[r] ?? yPx;
|
|
3900
|
-
const endCol = Math.min(dims.cols, c + span.gridSpan);
|
|
3901
|
-
const endRow = Math.min(dims.rows, r + span.rowSpan);
|
|
3902
|
-
const cw = (colXs[endCol] ?? cx) - cx;
|
|
3903
|
-
const ch = (rowYs[endRow] ?? cy) - cy;
|
|
3904
|
-
const fill = getTableCellFill(cell);
|
|
3905
|
-
let resolvedFill;
|
|
3906
|
-
if (fill) {
|
|
3907
|
-
resolvedFill = resolveColor(fill, theme, "#FFFFFF");
|
|
3908
|
-
} else if (flags.firstRow && r === 0) {
|
|
3909
|
-
resolvedFill = headerFill;
|
|
3910
|
-
} else if (flags.lastRow && r === dims.rows - 1) {
|
|
3911
|
-
resolvedFill = headerFill;
|
|
3912
|
-
} else if (flags.firstCol && c === 0) {
|
|
3913
|
-
resolvedFill = bandFill;
|
|
3914
|
-
} else if (flags.lastCol && c === dims.cols - 1) {
|
|
3915
|
-
resolvedFill = bandFill;
|
|
3916
|
-
} else if (flags.bandRow && r % 2 === (flags.firstRow ? 0 : 1)) {
|
|
3917
|
-
resolvedFill = bandFill;
|
|
3918
|
-
} else if (flags.bandCol && c % 2 === (flags.firstCol ? 0 : 1)) {
|
|
3919
|
-
resolvedFill = bandFill;
|
|
3920
|
-
} else {
|
|
3921
|
-
resolvedFill = "none";
|
|
3922
|
-
}
|
|
3923
|
-
const cellTextColor = resolvedFill === headerFill ? "#FFFFFF" : textColor;
|
|
3924
|
-
out.push(
|
|
3925
|
-
`<rect x="${px(cx)}" y="${px(cy)}" width="${px(cw)}" height="${px(ch)}" fill="${resolvedFill}"/>`
|
|
3926
|
-
);
|
|
3927
|
-
const borders = getTableCellBorders(pres, typedCell);
|
|
3928
|
-
const dashAttr = (dash, widthPx) => {
|
|
3929
|
-
if (!dash || dash === "solid") return "";
|
|
3930
|
-
const pat = DASH_PATTERNS[dash];
|
|
3931
|
-
if (!pat) return "";
|
|
3932
|
-
const arr = pat.split(" ").map((n) => (Number.parseFloat(n) * widthPx).toFixed(2)).join(" ");
|
|
3933
|
-
return ` stroke-dasharray="${arr}"`;
|
|
3934
|
-
};
|
|
3935
|
-
const edge = (side, x1, y1, x2, y2) => {
|
|
3936
|
-
const b = borders[side];
|
|
3937
|
-
if (!b) return;
|
|
3938
|
-
const sw = b.widthEmu ? Math.max(0.4, b.widthEmu / EMU_PER_PX) : 0.5;
|
|
3939
|
-
const col = b.color ?? "#9CA3AF";
|
|
3940
|
-
borderEdges.push(
|
|
3941
|
-
`<line x1="${px(x1)}" y1="${px(y1)}" x2="${px(x2)}" y2="${px(y2)}" stroke="${col}" stroke-width="${px(sw)}"${dashAttr(b.dash, sw)}/>`
|
|
3942
|
-
);
|
|
3943
|
-
};
|
|
3944
|
-
edge("left", cx, cy, cx, cy + ch);
|
|
3945
|
-
edge("right", cx + cw, cy, cx + cw, cy + ch);
|
|
3946
|
-
edge("top", cx, cy, cx + cw, cy);
|
|
3947
|
-
edge("bottom", cx, cy + ch, cx + cw, cy + ch);
|
|
3948
|
-
if (borders.tlToBr) {
|
|
3949
|
-
const sw = borders.tlToBr.widthEmu ? Math.max(0.4, borders.tlToBr.widthEmu / EMU_PER_PX) : 0.5;
|
|
3950
|
-
borderEdges.push(
|
|
3951
|
-
`<line x1="${px(cx)}" y1="${px(cy)}" x2="${px(cx + cw)}" y2="${px(cy + ch)}" stroke="${borders.tlToBr.color ?? "#9CA3AF"}" stroke-width="${px(sw)}"${dashAttr(borders.tlToBr.dash, sw)}/>`
|
|
3952
|
-
);
|
|
3953
|
-
}
|
|
3954
|
-
if (borders.blToTr) {
|
|
3955
|
-
const sw = borders.blToTr.widthEmu ? Math.max(0.4, borders.blToTr.widthEmu / EMU_PER_PX) : 0.5;
|
|
3956
|
-
borderEdges.push(
|
|
3957
|
-
`<line x1="${px(cx)}" y1="${px(cy + ch)}" x2="${px(cx + cw)}" y2="${px(cy)}" stroke="${borders.blToTr.color ?? "#9CA3AF"}" stroke-width="${px(sw)}"${dashAttr(borders.blToTr.dash, sw)}/>`
|
|
3958
|
-
);
|
|
3959
|
-
}
|
|
3960
|
-
const defaultColor = "#9CA3AF";
|
|
3961
|
-
if (!borders.left)
|
|
3962
|
-
borderEdges.push(
|
|
3963
|
-
`<line x1="${px(cx)}" y1="${px(cy)}" x2="${px(cx)}" y2="${px(cy + ch)}" stroke="${defaultColor}" stroke-width="0.4" opacity="0.6"/>`
|
|
3964
|
-
);
|
|
3965
|
-
if (!borders.right)
|
|
3966
|
-
borderEdges.push(
|
|
3967
|
-
`<line x1="${px(cx + cw)}" y1="${px(cy)}" x2="${px(cx + cw)}" y2="${px(cy + ch)}" stroke="${defaultColor}" stroke-width="0.4" opacity="0.6"/>`
|
|
3968
|
-
);
|
|
3969
|
-
if (!borders.top)
|
|
3970
|
-
borderEdges.push(
|
|
3971
|
-
`<line x1="${px(cx)}" y1="${px(cy)}" x2="${px(cx + cw)}" y2="${px(cy)}" stroke="${defaultColor}" stroke-width="0.4" opacity="0.6"/>`
|
|
3972
|
-
);
|
|
3973
|
-
if (!borders.bottom)
|
|
3974
|
-
borderEdges.push(
|
|
3975
|
-
`<line x1="${px(cx)}" y1="${px(cy + ch)}" x2="${px(cx + cw)}" y2="${px(cy + ch)}" stroke="${defaultColor}" stroke-width="0.4" opacity="0.6"/>`
|
|
3976
|
-
);
|
|
3977
|
-
const cellParagraphs = getTableCellParagraphs(
|
|
3978
|
-
cell
|
|
3979
|
-
);
|
|
3980
|
-
const vAnchor = getTableCellAnchor(typedCell) ?? "top";
|
|
3981
|
-
const cellMargins = getTableCellMargins(typedCell);
|
|
3982
|
-
out.push(
|
|
3983
|
-
renderTableCellText(
|
|
3984
|
-
cellParagraphs,
|
|
3985
|
-
cx,
|
|
3986
|
-
cy,
|
|
3987
|
-
cw,
|
|
3988
|
-
ch,
|
|
3989
|
-
cellTextColor,
|
|
3990
|
-
pres,
|
|
3991
|
-
shape,
|
|
3992
|
-
theme,
|
|
3993
|
-
tableThemeFace,
|
|
3994
|
-
ctx,
|
|
3995
|
-
vAnchor,
|
|
3996
|
-
cellMargins
|
|
3997
|
-
)
|
|
3998
|
-
);
|
|
3999
|
-
}
|
|
4000
|
-
}
|
|
4001
|
-
out.push(borderEdges.join(""));
|
|
4002
|
-
out.push("</g>");
|
|
4003
|
-
return out.join("");
|
|
4004
|
-
};
|
|
4005
|
-
var arcToCubicSegments = (startX, startY, wR, hR, stAng, swAng) => {
|
|
4006
|
-
const toRad = (a) => a / 6e4 * (Math.PI / 180);
|
|
4007
|
-
const st = toRad(stAng);
|
|
4008
|
-
const sw = toRad(swAng);
|
|
4009
|
-
const cx = startX - wR * Math.cos(st);
|
|
4010
|
-
const cy = startY - hR * Math.sin(st);
|
|
4011
|
-
const segCount = Math.max(1, Math.ceil(Math.abs(sw) / (Math.PI / 2)));
|
|
4012
|
-
const delta = sw / segCount;
|
|
4013
|
-
const k = 4 / 3 * Math.tan(delta / 4);
|
|
4014
|
-
const segs = [];
|
|
4015
|
-
let a0 = st;
|
|
4016
|
-
for (let i = 0; i < segCount; i++) {
|
|
4017
|
-
const a1 = a0 + delta;
|
|
4018
|
-
const cos0 = Math.cos(a0);
|
|
4019
|
-
const sin0 = Math.sin(a0);
|
|
4020
|
-
const cos1 = Math.cos(a1);
|
|
4021
|
-
const sin1 = Math.sin(a1);
|
|
4022
|
-
const p1x = cx + wR * cos1;
|
|
4023
|
-
const p1y = cy + hR * sin1;
|
|
4024
|
-
segs.push({
|
|
4025
|
-
c1x: cx + wR * cos0 - k * wR * sin0,
|
|
4026
|
-
c1y: cy + hR * sin0 + k * hR * cos0,
|
|
4027
|
-
c2x: p1x + k * wR * sin1,
|
|
4028
|
-
c2y: p1y - k * hR * cos1,
|
|
4029
|
-
ex: p1x,
|
|
4030
|
-
ey: p1y
|
|
4031
|
-
});
|
|
4032
|
-
a0 = a1;
|
|
4033
|
-
}
|
|
4034
|
-
return segs;
|
|
4035
|
-
};
|
|
4036
|
-
var customGeometryToSvg = (geom, x, y, w, h, fill, stroke, strokeWidthEmu, strokeExtra, markerExtra) => {
|
|
4037
|
-
const out = [];
|
|
4038
|
-
for (const path of geom.paths) {
|
|
4039
|
-
const cw = path.w ?? w;
|
|
4040
|
-
const ch = path.h ?? h;
|
|
4041
|
-
if (cw === 0 || ch === 0) continue;
|
|
4042
|
-
const sx = w / cw;
|
|
4043
|
-
const sy = h / ch;
|
|
4044
|
-
const fx = (gx) => ((x + gx * sx) / EMU_PER_PX).toFixed(2);
|
|
4045
|
-
const fy = (gy) => ((y + gy * sy) / EMU_PER_PX).toFixed(2);
|
|
4046
|
-
const pt = (gx, gy) => `${fx(gx)},${fy(gy)}`;
|
|
4047
|
-
let curX = 0;
|
|
4048
|
-
let curY = 0;
|
|
4049
|
-
let startX = 0;
|
|
4050
|
-
let startY = 0;
|
|
4051
|
-
const d = [];
|
|
4052
|
-
for (const cmd of path.commands) {
|
|
4053
|
-
switch (cmd.kind) {
|
|
4054
|
-
case "moveTo":
|
|
4055
|
-
curX = cmd.pt.x;
|
|
4056
|
-
curY = cmd.pt.y;
|
|
4057
|
-
startX = curX;
|
|
4058
|
-
startY = curY;
|
|
4059
|
-
d.push(`M${pt(curX, curY)}`);
|
|
4060
|
-
break;
|
|
4061
|
-
case "lnTo":
|
|
4062
|
-
curX = cmd.pt.x;
|
|
4063
|
-
curY = cmd.pt.y;
|
|
4064
|
-
d.push(`L${pt(curX, curY)}`);
|
|
4065
|
-
break;
|
|
4066
|
-
case "quadBezTo":
|
|
4067
|
-
d.push(`Q${pt(cmd.pts[0].x, cmd.pts[0].y)} ${pt(cmd.pts[1].x, cmd.pts[1].y)}`);
|
|
4068
|
-
curX = cmd.pts[1].x;
|
|
4069
|
-
curY = cmd.pts[1].y;
|
|
4070
|
-
break;
|
|
4071
|
-
case "cubicBezTo":
|
|
4072
|
-
d.push(
|
|
4073
|
-
`C${pt(cmd.pts[0].x, cmd.pts[0].y)} ${pt(cmd.pts[1].x, cmd.pts[1].y)} ${pt(cmd.pts[2].x, cmd.pts[2].y)}`
|
|
4074
|
-
);
|
|
4075
|
-
curX = cmd.pts[2].x;
|
|
4076
|
-
curY = cmd.pts[2].y;
|
|
4077
|
-
break;
|
|
4078
|
-
case "arcTo": {
|
|
4079
|
-
const segs = arcToCubicSegments(curX, curY, cmd.wR, cmd.hR, cmd.stAng, cmd.swAng);
|
|
4080
|
-
for (const s of segs) {
|
|
4081
|
-
d.push(`C${pt(s.c1x, s.c1y)} ${pt(s.c2x, s.c2y)} ${pt(s.ex, s.ey)}`);
|
|
4082
|
-
}
|
|
4083
|
-
const last = segs[segs.length - 1];
|
|
4084
|
-
if (last) {
|
|
4085
|
-
curX = last.ex;
|
|
4086
|
-
curY = last.ey;
|
|
4087
|
-
}
|
|
4088
|
-
break;
|
|
4089
|
-
}
|
|
4090
|
-
case "close":
|
|
4091
|
-
d.push("Z");
|
|
4092
|
-
curX = startX;
|
|
4093
|
-
curY = startY;
|
|
4094
|
-
break;
|
|
4095
|
-
}
|
|
4096
|
-
}
|
|
4097
|
-
if (d.length === 0) continue;
|
|
4098
|
-
const pathFill = path.fill === "none" ? "none" : fill;
|
|
4099
|
-
const strokeAttrs = path.stroke ? ` stroke="${stroke}" stroke-width="${E(strokeWidthEmu)}"${strokeExtra}${markerExtra}` : ' stroke="none"';
|
|
4100
|
-
out.push(`<path d="${d.join(" ")}" fill="${pathFill}"${strokeAttrs} fill-rule="evenodd"/>`);
|
|
4101
|
-
}
|
|
4102
|
-
return out.join("");
|
|
4103
|
-
};
|
|
4104
|
-
var renderShape = (shape, pres, theme, ctx) => {
|
|
4105
|
-
const bounds = getShapeBoundsResolved(pres, shape);
|
|
4106
|
-
if (!bounds) return "";
|
|
4107
|
-
const x = bounds.x;
|
|
4108
|
-
const y = bounds.y;
|
|
4109
|
-
const w = bounds.w;
|
|
4110
|
-
const h = bounds.h;
|
|
4111
|
-
const kind = getShapeKind(shape);
|
|
4112
|
-
if (kind === "connector" ? w <= 0 && h <= 0 : w <= 0 || h <= 0) return "";
|
|
4113
|
-
const fill = getShapeFillEffective(pres, shape);
|
|
4114
|
-
const stroke = getShapeStrokeEffective(pres, shape);
|
|
4115
|
-
const rotation = getShapeRotation(shape);
|
|
4116
|
-
const flip = getShapeFlip(shape) ?? { horizontal: false, vertical: false };
|
|
4117
|
-
const phType = getShapePlaceholderType(shape);
|
|
4118
|
-
const cx = x + w / 2;
|
|
4119
|
-
const cy = y + h / 2;
|
|
4120
|
-
const transforms = [];
|
|
4121
|
-
if (rotation !== 0) transforms.push(`rotate(${rotation} ${E(cx)} ${E(cy)})`);
|
|
4122
|
-
if (flip.horizontal) transforms.push(`translate(${E(2 * cx)} 0) scale(-1 1)`);
|
|
4123
|
-
if (flip.vertical) transforms.push(`translate(0 ${E(2 * cy)}) scale(1 -1)`);
|
|
4124
|
-
const transform = transforms.length > 0 ? ` transform="${transforms.join(" ")}"` : "";
|
|
4125
|
-
const textTransform = rotation !== 0 ? ` transform="rotate(${rotation} ${E(cx)} ${E(cy)})"` : "";
|
|
4126
|
-
const textOverlay = kind === "shape" || kind === "graphicFrame" ? renderTextBody(pres, shape, { x, y, w, h }, theme, phType, ctx) : "";
|
|
4127
|
-
if (kind === "picture") {
|
|
4128
|
-
return renderPicture(
|
|
4129
|
-
shape,
|
|
4130
|
-
pres,
|
|
4131
|
-
x,
|
|
4132
|
-
y,
|
|
4133
|
-
w,
|
|
4134
|
-
h,
|
|
4135
|
-
transform,
|
|
4136
|
-
textOverlay,
|
|
4137
|
-
getShapeImageBytes(shape),
|
|
4138
|
-
getShapeImageFormat(shape)
|
|
4139
|
-
);
|
|
4140
|
-
}
|
|
4141
|
-
if (kind === "shape" && fill.kind === "image") {
|
|
4142
|
-
return renderPicture(
|
|
4143
|
-
shape,
|
|
4144
|
-
pres,
|
|
4145
|
-
x,
|
|
4146
|
-
y,
|
|
4147
|
-
w,
|
|
4148
|
-
h,
|
|
4149
|
-
transform,
|
|
4150
|
-
textOverlay,
|
|
4151
|
-
getShapeImageFillBytes(shape),
|
|
4152
|
-
getShapeImageFormat(shape)
|
|
4153
|
-
);
|
|
4154
|
-
}
|
|
4155
|
-
if (kind === "connector") {
|
|
4156
|
-
const p2 = paint(shape, fill, stroke, theme, false, pres);
|
|
4157
|
-
const sw = p2.strokeWidth || 19050;
|
|
4158
|
-
let x1 = x;
|
|
4159
|
-
let y1 = y;
|
|
4160
|
-
let x2 = x + w;
|
|
4161
|
-
let y2 = y + h;
|
|
4162
|
-
if (flip.horizontal) {
|
|
4163
|
-
x1 = x + w;
|
|
4164
|
-
x2 = x;
|
|
4165
|
-
}
|
|
4166
|
-
if (flip.vertical) {
|
|
4167
|
-
y1 = y + h;
|
|
4168
|
-
y2 = y;
|
|
4169
|
-
}
|
|
4170
|
-
const strokeColor = p2.stroke === "none" ? resolveColor("scheme:tx1", theme, "#1F2937") : p2.stroke;
|
|
4171
|
-
const sa2 = p2.strokeAttrs ? ` ${p2.strokeAttrs}` : "";
|
|
4172
|
-
const ma2 = p2.markerAttrs ?? "";
|
|
4173
|
-
const preset2 = getShapePreset(shape) ?? "line";
|
|
4174
|
-
if (preset2 === "straightConnector1" || preset2 === "line") {
|
|
4175
|
-
return `${p2.defs}<line x1="${E(x1)}" y1="${E(y1)}" x2="${E(x2)}" y2="${E(y2)}" stroke="${strokeColor}" stroke-width="${E(sw)}" stroke-linecap="round"${sa2}${ma2}${transform}/>`;
|
|
4176
|
-
}
|
|
4177
|
-
const px1 = x1 / EMU_PER_PX;
|
|
4178
|
-
const py1 = y1 / EMU_PER_PX;
|
|
4179
|
-
const px2 = x2 / EMU_PER_PX;
|
|
4180
|
-
const py2 = y2 / EMU_PER_PX;
|
|
4181
|
-
let d = `M${px1.toFixed(2)} ${py1.toFixed(2)}`;
|
|
4182
|
-
if (preset2 === "bentConnector2") {
|
|
4183
|
-
d += ` L${px2.toFixed(2)} ${py1.toFixed(2)} L${px2.toFixed(2)} ${py2.toFixed(2)}`;
|
|
4184
|
-
} else if (preset2 === "bentConnector3") {
|
|
4185
|
-
const midX = (px1 + px2) / 2;
|
|
4186
|
-
d += ` L${midX.toFixed(2)} ${py1.toFixed(2)} L${midX.toFixed(2)} ${py2.toFixed(2)} L${px2.toFixed(2)} ${py2.toFixed(2)}`;
|
|
4187
|
-
} else if (preset2 === "bentConnector4") {
|
|
4188
|
-
const mx = (px1 + px2) / 2;
|
|
4189
|
-
const my = (py1 + py2) / 2;
|
|
4190
|
-
d += ` L${mx.toFixed(2)} ${py1.toFixed(2)} L${mx.toFixed(2)} ${my.toFixed(2)} L${px2.toFixed(2)} ${my.toFixed(2)} L${px2.toFixed(2)} ${py2.toFixed(2)}`;
|
|
4191
|
-
} else if (preset2 === "bentConnector5") {
|
|
4192
|
-
const q1x = px1 + (px2 - px1) / 3;
|
|
4193
|
-
const q2x = px1 + 2 * (px2 - px1) / 3;
|
|
4194
|
-
const my = (py1 + py2) / 2;
|
|
4195
|
-
d += ` L${q1x.toFixed(2)} ${py1.toFixed(2)} L${q1x.toFixed(2)} ${my.toFixed(2)} L${q2x.toFixed(2)} ${my.toFixed(2)} L${q2x.toFixed(2)} ${py2.toFixed(2)} L${px2.toFixed(2)} ${py2.toFixed(2)}`;
|
|
4196
|
-
} else if (preset2 === "curvedConnector2") {
|
|
4197
|
-
d += ` Q${px2.toFixed(2)} ${py1.toFixed(2)} ${px2.toFixed(2)} ${py2.toFixed(2)}`;
|
|
4198
|
-
} else if (preset2 === "curvedConnector3" || preset2 === "curvedConnector4" || preset2 === "curvedConnector5") {
|
|
4199
|
-
const c1x = px1 + (px2 - px1) / 3;
|
|
4200
|
-
const c2x = px1 + 2 * (px2 - px1) / 3;
|
|
4201
|
-
d += ` C${c1x.toFixed(2)} ${py1.toFixed(2)} ${c2x.toFixed(2)} ${py2.toFixed(2)} ${px2.toFixed(2)} ${py2.toFixed(2)}`;
|
|
4202
|
-
} else {
|
|
4203
|
-
d += ` L${px2.toFixed(2)} ${py2.toFixed(2)}`;
|
|
4204
|
-
}
|
|
4205
|
-
return `${p2.defs}<path d="${d}" fill="none" stroke="${strokeColor}" stroke-width="${E(sw)}" stroke-linecap="round" stroke-linejoin="round"${sa2}${ma2}${transform}/>`;
|
|
4206
|
-
}
|
|
4207
|
-
if (kind === "group") {
|
|
4208
|
-
const xform = getGroupTransform(shape);
|
|
4209
|
-
const children = getGroupChildren(shape);
|
|
4210
|
-
if (children.length === 0) return "";
|
|
4211
|
-
const tParts = [];
|
|
4212
|
-
if (xform && rotation !== 0) {
|
|
4213
|
-
const cxG = (xform.outer.x + xform.outer.w / 2) / EMU_PER_PX;
|
|
4214
|
-
const cyG = (xform.outer.y + xform.outer.h / 2) / EMU_PER_PX;
|
|
4215
|
-
tParts.push(`rotate(${rotation} ${cxG.toFixed(2)} ${cyG.toFixed(2)})`);
|
|
4216
|
-
}
|
|
4217
|
-
if (xform && flip.horizontal) {
|
|
4218
|
-
const cxG = (xform.outer.x + xform.outer.w / 2) / EMU_PER_PX;
|
|
4219
|
-
tParts.push(`translate(${(2 * cxG).toFixed(2)} 0) scale(-1 1)`);
|
|
4220
|
-
}
|
|
4221
|
-
if (xform && flip.vertical) {
|
|
4222
|
-
const cyG = (xform.outer.y + xform.outer.h / 2) / EMU_PER_PX;
|
|
4223
|
-
tParts.push(`translate(0 ${(2 * cyG).toFixed(2)}) scale(1 -1)`);
|
|
4224
|
-
}
|
|
4225
|
-
if (xform) {
|
|
4226
|
-
const ox = xform.outer.x;
|
|
4227
|
-
const oy = xform.outer.y;
|
|
4228
|
-
const ow = xform.outer.w;
|
|
4229
|
-
const oh = xform.outer.h;
|
|
4230
|
-
const ix = xform.inner.x;
|
|
4231
|
-
const iy = xform.inner.y;
|
|
4232
|
-
const iw = xform.inner.w || 1;
|
|
4233
|
-
const ih = xform.inner.h || 1;
|
|
4234
|
-
const sx = (ow / iw).toFixed(6);
|
|
4235
|
-
const sy = (oh / ih).toFixed(6);
|
|
4236
|
-
const tx = ((ox - ix * (ow / iw)) / EMU_PER_PX).toFixed(2);
|
|
4237
|
-
const ty = ((oy - iy * (oh / ih)) / EMU_PER_PX).toFixed(2);
|
|
4238
|
-
tParts.push(`translate(${tx} ${ty})`, `scale(${sx} ${sy})`);
|
|
4239
|
-
}
|
|
4240
|
-
const groupTransform = tParts.length > 0 ? ` transform="${tParts.join(" ")}"` : "";
|
|
4241
|
-
const childrenSvg = children.map((c) => renderShape(c, pres, theme, ctx)).join("");
|
|
4242
|
-
return `<g${groupTransform}>${childrenSvg}</g>`;
|
|
4243
|
-
}
|
|
4244
|
-
const p = paint(shape, fill, stroke, theme, phType !== null, pres);
|
|
4245
|
-
if (kind === "graphicFrame") {
|
|
4246
|
-
if (isChartShape(shape)) {
|
|
4247
|
-
const chartSvg = renderChart(shape, x, y, w, h, transform, theme);
|
|
4248
|
-
if (chartSvg) return chartSvg;
|
|
4249
|
-
}
|
|
4250
|
-
if (isTableShape(shape)) {
|
|
4251
|
-
const tableSvg = renderTable(shape, pres, x, y, w, h, transform, theme, ctx);
|
|
4252
|
-
if (tableSvg) return tableSvg;
|
|
4253
|
-
}
|
|
4254
|
-
let label = "graphicFrame";
|
|
4255
|
-
try {
|
|
4256
|
-
if (isChartShape(shape)) {
|
|
4257
|
-
label = "chart (unsupported kind)";
|
|
4258
|
-
} else if (isTableShape(shape)) {
|
|
4259
|
-
const t = getTableDimensions(shape);
|
|
4260
|
-
label = `table (${t.rows}\xD7${t.cols})`;
|
|
4261
|
-
}
|
|
4262
|
-
} catch {
|
|
4263
|
-
}
|
|
4264
|
-
const gfFallbackKind = label.startsWith("chart") ? "chart" : "graphicFrame";
|
|
4265
|
-
return `<g data-pptx-fallback="${gfFallbackKind}"${transform}><rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}" fill="${p.fill === "none" ? "#F9FAFB" : p.fill}" stroke="#9CA3AF" stroke-width="${E(9525)}" stroke-dasharray="${E(5e4)},${E(3e4)}"/>${renderPicturePlaceholderLabel(x, y, w, h, label)}${textOverlay}</g>`;
|
|
4266
|
-
}
|
|
4267
|
-
const rawPreset = getShapePreset(shape);
|
|
4268
|
-
const preset = rawPreset ?? "rect";
|
|
4269
|
-
const sa = p.strokeAttrs ? ` ${p.strokeAttrs}` : "";
|
|
4270
|
-
const ma = p.markerAttrs ?? "";
|
|
4271
|
-
const customGeom = rawPreset === null ? getShapeCustomGeometry(shape) : null;
|
|
4272
|
-
let geomSvg = customGeom !== null ? customGeometryToSvg(customGeom, x, y, w, h, p.fill, p.stroke, p.strokeWidth, sa, ma) : "";
|
|
4273
|
-
const isCustGeom = geomSvg === "" && rawPreset === null && getShapeXmlString(shape).includes("custGeom");
|
|
4274
|
-
if (geomSvg !== "") ; else if (preset === "rect") {
|
|
4275
|
-
geomSvg = `<rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}" fill="${p.fill}" stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma}/>`;
|
|
4276
|
-
} else if (preset === "roundRect") {
|
|
4277
|
-
const adjusts = getShapeAdjustValues(shape);
|
|
4278
|
-
const adjVal = adjusts.adj ?? 16667;
|
|
4279
|
-
const ratio = Math.max(0, Math.min(0.5, adjVal / 1e5));
|
|
4280
|
-
const r = E(Math.min(w, h) * ratio);
|
|
4281
|
-
geomSvg = `<rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}" rx="${r}" ry="${r}" fill="${p.fill}" stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma}/>`;
|
|
4282
|
-
} else if (preset === "ellipse" || preset === "oval") {
|
|
4283
|
-
geomSvg = `<ellipse cx="${E(cx)}" cy="${E(cy)}" rx="${E(w / 2)}" ry="${E(h / 2)}" fill="${p.fill}" stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma}/>`;
|
|
4284
|
-
} else {
|
|
4285
|
-
const pathFn = PRESET_PATHS[preset];
|
|
4286
|
-
if (pathFn) {
|
|
4287
|
-
const d = pathFn(x / EMU_PER_PX, y / EMU_PER_PX, w / EMU_PER_PX, h / EMU_PER_PX);
|
|
4288
|
-
geomSvg = `<path d="${d}" fill="${p.fill}" stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}" fill-rule="evenodd"${sa}${ma}/>`;
|
|
4289
|
-
} else {
|
|
4290
|
-
const pointsFn = PRESET_POINTS[preset];
|
|
4291
|
-
if (pointsFn) {
|
|
4292
|
-
const points = pointsFn().map(([nx, ny]) => `${E(x + nx * w)},${E(y + ny * h)}`).join(" ");
|
|
4293
|
-
geomSvg = `<polygon points="${points}" fill="${p.fill}" stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma}/>`;
|
|
4294
|
-
} else {
|
|
4295
|
-
geomSvg = `<rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}" fill="${p.fill}" stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma} data-pptx-preset="${escapeXml2(preset)}"><title>${escapeXml2(`preset: ${preset}`)}</title></rect>`;
|
|
4296
|
-
}
|
|
4297
|
-
}
|
|
4298
|
-
}
|
|
4299
|
-
const fx = buildEffectsFilter(pres, shape);
|
|
4300
|
-
const filterAttr = fx ? ` filter="url(#${fx.id})"` : "";
|
|
4301
|
-
let fxDefs = fx ? fx.defs : "";
|
|
4302
|
-
const reflection = buildReflection(pres, shape, geomSvg, { y, h });
|
|
4303
|
-
geomSvg = `<g${filterAttr}>${geomSvg}</g>`;
|
|
4304
|
-
if (reflection) {
|
|
4305
|
-
geomSvg = reflection.svg + geomSvg;
|
|
4306
|
-
fxDefs += reflection.defs;
|
|
4307
|
-
}
|
|
4308
|
-
const url = getShapeHyperlink(shape);
|
|
4309
|
-
const tooltip = getShapeHyperlinkTooltip(shape);
|
|
4310
|
-
const shapeName = (() => {
|
|
4311
|
-
try {
|
|
4312
|
-
return getShapeName(shape);
|
|
4313
|
-
} catch {
|
|
4314
|
-
return null;
|
|
4315
|
-
}
|
|
4316
|
-
})();
|
|
4317
|
-
const altTitle = (() => {
|
|
4318
|
-
try {
|
|
4319
|
-
return getShapeAltTitle(shape);
|
|
4320
|
-
} catch {
|
|
4321
|
-
return null;
|
|
4322
|
-
}
|
|
4323
|
-
})();
|
|
4324
|
-
const altDesc = (() => {
|
|
4325
|
-
try {
|
|
4326
|
-
return getShapeDescription(shape);
|
|
4327
|
-
} catch {
|
|
4328
|
-
return null;
|
|
4329
|
-
}
|
|
4330
|
-
})();
|
|
4331
|
-
const a11yLabel = altTitle ?? altDesc ?? null;
|
|
4332
|
-
const nameAttr = shapeName ? ` data-pptx-shape-name="${escapeXml2(shapeName)}"` : "";
|
|
4333
|
-
const ariaAttr = a11yLabel ? ` role="img" aria-label="${escapeXml2(a11yLabel)}"` : "";
|
|
4334
|
-
const placedText = textOverlay ? `<g${textTransform}>${textOverlay}</g>` : "";
|
|
4335
|
-
const custGeomAttr = isCustGeom ? ' data-pptx-fallback="custGeom"' : "";
|
|
4336
|
-
const inner = `${p.defs}${fxDefs}<g${nameAttr}${ariaAttr}${custGeomAttr}><g${transform}>${geomSvg}</g>${placedText}</g>`;
|
|
4337
|
-
const titleEl = tooltip ? `<title>${escapeXml2(tooltip)}</title>` : "";
|
|
4338
|
-
if (url) {
|
|
4339
|
-
return `<a href="${escapeXml2(url)}" target="_blank" rel="noopener noreferrer">${titleEl}${inner}</a>`;
|
|
4340
|
-
}
|
|
4341
|
-
const action = getShapeClickAction(shape);
|
|
4342
|
-
if (action) {
|
|
4343
|
-
let href = null;
|
|
4344
|
-
if (action.kind === "slide") {
|
|
4345
|
-
const idx = getSlideIndex(pres, action.slide);
|
|
4346
|
-
if (idx >= 0) href = `#slide-${idx + 1}`;
|
|
4347
|
-
} else if (action.kind === "url") {
|
|
4348
|
-
href = action.url;
|
|
4349
|
-
}
|
|
4350
|
-
if (href !== null) {
|
|
4351
|
-
const isInPage = href.startsWith("#");
|
|
4352
|
-
const targetAttrs = isInPage ? "" : ' target="_blank" rel="noopener noreferrer"';
|
|
4353
|
-
return `<a href="${escapeXml2(href)}"${targetAttrs}>${titleEl}${inner}</a>`;
|
|
4354
|
-
}
|
|
4355
|
-
}
|
|
4356
|
-
return inner;
|
|
4357
|
-
};
|
|
4358
|
-
var buildReflection = (pres, shape, geomRaw, box) => {
|
|
4359
|
-
let effects;
|
|
4360
|
-
try {
|
|
4361
|
-
effects = getShapeEffectsEffective(pres, shape);
|
|
4362
|
-
} catch {
|
|
4363
|
-
return null;
|
|
4364
|
-
}
|
|
4365
|
-
const refl = effects.find((e) => e.kind === "reflection");
|
|
4366
|
-
if (!refl || refl.kind !== "reflection") return null;
|
|
4367
|
-
const f = refl.scaleY ?? -1;
|
|
4368
|
-
const startA = refl.startOpacity ?? 1;
|
|
4369
|
-
const endA = refl.opacity ?? 0;
|
|
4370
|
-
const contactPx = (box.y + box.h) / EMU_PER_PX;
|
|
4371
|
-
const distPx = refl.distEmu / EMU_PER_PX;
|
|
4372
|
-
const transform = `translate(0 ${(distPx + contactPx * (1 - f)).toFixed(2)}) scale(1 ${f})`;
|
|
4373
|
-
const maskId = mintId();
|
|
4374
|
-
const gradId = mintId();
|
|
4375
|
-
const defs = `<defs><linearGradient id="${gradId}" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#fff" stop-opacity="${startA.toFixed(3)}"/><stop offset="1" stop-color="#fff" stop-opacity="${endA.toFixed(3)}"/></linearGradient><mask id="${maskId}" maskContentUnits="objectBoundingBox"><rect width="1" height="1" fill="url(#${gradId})"/></mask></defs>`;
|
|
4376
|
-
const svg = `<g transform="${transform}" mask="url(#${maskId})" data-pptx-reflection="1">${geomRaw}</g>`;
|
|
4377
|
-
return { svg, defs };
|
|
4378
|
-
};
|
|
4379
|
-
var buildEffectsFilter = (pres, shape) => {
|
|
4380
|
-
let effects;
|
|
4381
|
-
try {
|
|
4382
|
-
effects = getShapeEffectsEffective(pres, shape);
|
|
4383
|
-
} catch {
|
|
4384
|
-
return null;
|
|
4385
|
-
}
|
|
4386
|
-
if (effects.length === 0) return null;
|
|
4387
|
-
const id = mintId();
|
|
4388
|
-
const primitives = [];
|
|
4389
|
-
const layers = [];
|
|
4390
|
-
for (const e of effects) {
|
|
4391
|
-
if (e.kind === "outerShdw") {
|
|
4392
|
-
const rad = e.angleDeg * Math.PI / 180;
|
|
4393
|
-
const dx = e.distEmu * Math.cos(rad) / EMU_PER_PX;
|
|
4394
|
-
const dy = e.distEmu * Math.sin(rad) / EMU_PER_PX;
|
|
4395
|
-
const blurPx = e.blurEmu / EMU_PER_PX / 2;
|
|
4396
|
-
const opacity = e.opacity ?? 1;
|
|
4397
|
-
const color = e.color || "#000000";
|
|
4398
|
-
const out = `shdwOut${primitives.length}`;
|
|
4399
|
-
primitives.push(
|
|
4400
|
-
`<feDropShadow dx="${dx.toFixed(2)}" dy="${dy.toFixed(2)}" stdDeviation="${blurPx.toFixed(2)}" flood-color="${color}" flood-opacity="${opacity.toFixed(3)}" result="${out}"/>`
|
|
4401
|
-
);
|
|
4402
|
-
layers.push(out);
|
|
4403
|
-
} else if (e.kind === "innerShdw") {
|
|
4404
|
-
const rad = e.angleDeg * Math.PI / 180;
|
|
4405
|
-
const dx = e.distEmu * Math.cos(rad) / EMU_PER_PX;
|
|
4406
|
-
const dy = e.distEmu * Math.sin(rad) / EMU_PER_PX;
|
|
4407
|
-
const blurPx = e.blurEmu / EMU_PER_PX / 2;
|
|
4408
|
-
const color = e.color || "#000000";
|
|
4409
|
-
const opacity = e.opacity ?? 1;
|
|
4410
|
-
const i = primitives.length;
|
|
4411
|
-
primitives.push(
|
|
4412
|
-
`<feGaussianBlur in="SourceAlpha" stdDeviation="${blurPx.toFixed(2)}" result="innerBlur${i}"/>`,
|
|
4413
|
-
`<feOffset in="innerBlur${i}" dx="${dx.toFixed(2)}" dy="${dy.toFixed(2)}" result="innerOff${i}"/>`,
|
|
4414
|
-
`<feComposite in="innerOff${i}" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="innerMask${i}"/>`,
|
|
4415
|
-
`<feFlood flood-color="${color}" flood-opacity="${opacity.toFixed(3)}" result="innerCol${i}"/>`,
|
|
4416
|
-
`<feComposite in="innerCol${i}" in2="innerMask${i}" operator="in" result="innerOut${i}"/>`
|
|
4417
|
-
);
|
|
4418
|
-
layers.push(`innerOut${i}`);
|
|
4419
|
-
} else if (e.kind === "glow") {
|
|
4420
|
-
const blurPx = e.radiusEmu / EMU_PER_PX / 2;
|
|
4421
|
-
const color = e.color || "#FFFFFF";
|
|
4422
|
-
const opacity = e.opacity ?? 1;
|
|
4423
|
-
const i = primitives.length;
|
|
4424
|
-
primitives.push(
|
|
4425
|
-
`<feMorphology in="SourceAlpha" operator="dilate" radius="${(blurPx / 4).toFixed(2)}" result="glowExp${i}"/>`,
|
|
4426
|
-
`<feGaussianBlur in="glowExp${i}" stdDeviation="${blurPx.toFixed(2)}" result="glowBlur${i}"/>`,
|
|
4427
|
-
`<feFlood flood-color="${color}" flood-opacity="${opacity.toFixed(3)}" result="glowCol${i}"/>`,
|
|
4428
|
-
`<feComposite in="glowCol${i}" in2="glowBlur${i}" operator="in" result="glowOut${i}"/>`
|
|
4429
|
-
);
|
|
4430
|
-
layers.push(`glowOut${i}`);
|
|
4431
|
-
} else if (e.kind === "softEdge") {
|
|
4432
|
-
const blurPx = e.radiusEmu / EMU_PER_PX / 2;
|
|
4433
|
-
const i = primitives.length;
|
|
4434
|
-
primitives.push(
|
|
4435
|
-
`<feGaussianBlur in="SourceGraphic" stdDeviation="${blurPx.toFixed(2)}" result="softOut${i}"/>`
|
|
4436
|
-
);
|
|
4437
|
-
layers.length = 0;
|
|
4438
|
-
layers.push(`softOut${i}`);
|
|
4439
|
-
} else if (e.kind === "blur") {
|
|
4440
|
-
const blurPx = e.radiusEmu / EMU_PER_PX / 2;
|
|
4441
|
-
const i = primitives.length;
|
|
4442
|
-
primitives.push(
|
|
4443
|
-
`<feGaussianBlur in="SourceGraphic" stdDeviation="${blurPx.toFixed(2)}" result="blurOut${i}"/>`
|
|
4444
|
-
);
|
|
4445
|
-
layers.length = 0;
|
|
4446
|
-
layers.push(`blurOut${i}`);
|
|
4447
|
-
}
|
|
4448
|
-
}
|
|
4449
|
-
if (layers.length === 0) return null;
|
|
4450
|
-
const replacedSource = effects.some((e) => e.kind === "softEdge" || e.kind === "blur");
|
|
4451
|
-
const mergeChildren = layers.map((l) => `<feMergeNode in="${l}"/>`).join("");
|
|
4452
|
-
const sourceMerge = replacedSource ? "" : '<feMergeNode in="SourceGraphic"/>';
|
|
4453
|
-
primitives.push(`<feMerge>${mergeChildren}${sourceMerge}</feMerge>`);
|
|
4454
|
-
const defs = `<defs><filter id="${id}" x="-25%" y="-25%" width="150%" height="150%">${primitives.join("")}</filter></defs>`;
|
|
4455
|
-
return { id, defs };
|
|
4456
|
-
};
|
|
4457
|
-
var renderSlideSvg = (pres, slide, opts = {}) => {
|
|
4458
|
-
const size = getSlideSize(pres) ?? DEFAULT_SIZE;
|
|
4459
|
-
const W = size.width;
|
|
4460
|
-
const H = size.height;
|
|
4461
|
-
const theme = getPresentationTheme(pres);
|
|
4462
|
-
activeColorMap = getEffectiveColorMap(slide);
|
|
4463
|
-
activeDeckTextColor = resolveDeckBodyTextColor(slide) ?? "#000000";
|
|
4464
|
-
const ctx = {
|
|
4465
|
-
mode: opts.textLayout ?? "foreignObject",
|
|
4466
|
-
measure: opts.measureText ?? defaultMeasurer
|
|
4467
|
-
};
|
|
4468
|
-
let bg = getSlideBackground(slide);
|
|
4469
|
-
if (bg.kind === "inherit") {
|
|
4470
|
-
const layout = getSlideLayout(slide);
|
|
4471
|
-
if (layout) {
|
|
4472
|
-
const layoutBg = getSlideLayoutBackground(layout);
|
|
4473
|
-
if (layoutBg.kind !== "inherit") {
|
|
4474
|
-
bg = layoutBg;
|
|
4475
|
-
} else {
|
|
4476
|
-
const masterBg = getSlideMasterBackground(pres, layout);
|
|
4477
|
-
if (masterBg.kind !== "inherit") bg = masterBg;
|
|
4478
|
-
}
|
|
4479
|
-
}
|
|
4480
|
-
}
|
|
4481
|
-
let bgColor = "#FFFFFF";
|
|
4482
|
-
let bgGradient = "";
|
|
4483
|
-
let bgGradientDefs = "";
|
|
4484
|
-
if (bg.kind === "solid") {
|
|
4485
|
-
bgColor = resolveColor(bg.color, theme, "#FFFFFF");
|
|
4486
|
-
} else if (bg.kind === "gradient") {
|
|
4487
|
-
let grad = getSlideBackgroundGradientFill(slide);
|
|
4488
|
-
if (!grad) {
|
|
4489
|
-
const layout = getSlideLayout(slide);
|
|
4490
|
-
if (layout) {
|
|
4491
|
-
grad = getSlideLayoutBackgroundGradientFill(layout);
|
|
4492
|
-
if (!grad) grad = getSlideMasterBackgroundGradientFill(pres, layout);
|
|
4493
|
-
}
|
|
4494
|
-
}
|
|
4495
|
-
if (grad) {
|
|
4496
|
-
const built = gradientDef(grad, theme);
|
|
4497
|
-
bgGradientDefs = built.defs;
|
|
4498
|
-
bgGradient = `<rect width="${E(W)}" height="${E(H)}" fill="${built.fillAttr}"/>`;
|
|
4499
|
-
}
|
|
4500
|
-
} else if (bg.kind === "pattern") {
|
|
4501
|
-
let pat = getSlideBackgroundPatternFill(pres, slide);
|
|
4502
|
-
if (!pat) {
|
|
4503
|
-
const layout = getSlideLayout(slide);
|
|
4504
|
-
if (layout) {
|
|
4505
|
-
pat = getSlideLayoutBackgroundPatternFill(pres, layout);
|
|
4506
|
-
if (!pat) pat = getSlideMasterBackgroundPatternFill(pres, layout);
|
|
4507
|
-
}
|
|
4508
|
-
}
|
|
4509
|
-
if (pat) {
|
|
4510
|
-
const built = patternDef(pat);
|
|
4511
|
-
bgGradientDefs += built.defs;
|
|
4512
|
-
bgGradient = `<rect width="${E(W)}" height="${E(H)}" fill="${built.fillAttr}"/>`;
|
|
4513
|
-
}
|
|
4514
|
-
} else if (theme && bg.kind === "inherit") {
|
|
4515
|
-
bgColor = normalizeHex(theme.light1);
|
|
4516
|
-
}
|
|
4517
|
-
let bgImage = "";
|
|
4518
|
-
if (bg.kind === "image") {
|
|
4519
|
-
let bytes = getSlideBackgroundImageBytes(slide);
|
|
4520
|
-
if (!bytes && pres) {
|
|
4521
|
-
const layout = getSlideLayout(slide);
|
|
4522
|
-
if (layout) {
|
|
4523
|
-
bytes = getSlideLayoutBackgroundImageBytes(pres, layout);
|
|
4524
|
-
if (!bytes) bytes = getSlideMasterBackgroundImageBytes(pres, layout);
|
|
4525
|
-
}
|
|
4526
|
-
}
|
|
4527
|
-
if (bytes) {
|
|
4528
|
-
const fmt2 = detectImageFormatLocal(bytes);
|
|
4529
|
-
const mime = fmt2 ? imageMime[fmt2] ?? "image/png" : "image/png";
|
|
4530
|
-
const dataUrl = `data:${mime};base64,${u8ToBase64(bytes)}`;
|
|
4531
|
-
bgImage = `<image x="0" y="0" width="${E(W)}" height="${E(H)}" href="${dataUrl}" xlink:href="${dataUrl}" preserveAspectRatio="xMidYMid slice"/>`;
|
|
4532
|
-
}
|
|
4533
|
-
}
|
|
4534
|
-
let layoutBgShapes = "";
|
|
4535
|
-
const layoutForBg = getSlideLayout(slide);
|
|
4536
|
-
if (layoutForBg) {
|
|
4537
|
-
try {
|
|
4538
|
-
const masterShapes = getSlideMasterShapes(pres, layoutForBg);
|
|
4539
|
-
const layoutShapes = getSlideLayoutShapes(pres, layoutForBg);
|
|
4540
|
-
layoutBgShapes = [...masterShapes, ...layoutShapes].map((s) => renderShape(s, pres, theme, ctx)).join("");
|
|
4541
|
-
} catch {
|
|
4542
|
-
layoutBgShapes = "";
|
|
4543
|
-
}
|
|
4544
|
-
}
|
|
4545
|
-
const shapesSvg = getSlideShapes(slide).map((s) => renderShape(s, pres, theme, ctx)).join("");
|
|
4546
|
-
return [
|
|
4547
|
-
`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 ${E(W)} ${E(H)}" preserveAspectRatio="xMidYMid meet">`,
|
|
4548
|
-
bgGradientDefs,
|
|
4549
|
-
`<rect width="${E(W)}" height="${E(H)}" fill="${bgColor}"/>`,
|
|
4550
|
-
bgGradient,
|
|
4551
|
-
bgImage,
|
|
4552
|
-
layoutBgShapes,
|
|
4553
|
-
shapesSvg,
|
|
4554
|
-
"</svg>"
|
|
4555
|
-
].join("");
|
|
4556
|
-
};
|
|
4557
|
-
var detectImageFormatLocal = (bytes) => {
|
|
4558
|
-
if (bytes.length < 4) return null;
|
|
4559
|
-
if (bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71)
|
|
4560
|
-
return "png";
|
|
4561
|
-
if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "jpeg";
|
|
4562
|
-
if (bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70) return "gif";
|
|
4563
|
-
if (bytes[0] === 66 && bytes[1] === 77) return "bmp";
|
|
4564
|
-
if (bytes.length >= 12 && bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70 && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80)
|
|
4565
|
-
return "webp";
|
|
4566
|
-
return null;
|
|
4567
|
-
};
|
|
4568
|
-
|
|
1
|
+
import { a as SERIF, c as substituteFamily, i as SANS, n as ARIAL, o as TIMES, r as MONO, s as defaultMeasurer, t as renderSlideSvg } from "./src-CDTTqUfI.js";
|
|
4569
2
|
export { ARIAL, MONO, SANS, SERIF, TIMES, defaultMeasurer, renderSlideSvg as renderSlideToSvg, substituteFamily };
|
|
4570
|
-
//# sourceMappingURL=index.js.map
|
|
4571
|
-
//# sourceMappingURL=index.js.map
|