pptx-kit-preview 0.5.0 → 0.6.1
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.js +1 -1
- package/dist/node.js +1 -1
- package/dist/{src-CDTTqUfI.js → src-Q2z_XgT6.js} +426 -177
- package/dist/src-Q2z_XgT6.js.map +1 -0
- package/package.json +2 -2
- package/dist/src-CDTTqUfI.js.map +0 -1
|
@@ -30,7 +30,7 @@ const defaultMeasurer = (text, spec) => {
|
|
|
30
30
|
const FALLBACK_ASCENT = .9;
|
|
31
31
|
const FALLBACK_DESCENT = .22;
|
|
32
32
|
const FALLBACK_LINEGAP = .08;
|
|
33
|
-
const
|
|
33
|
+
const BASELINE_LEADING_DROP = .036;
|
|
34
34
|
const GRID_NUDGE_X = -.75;
|
|
35
35
|
const specOf = (piece) => ({
|
|
36
36
|
family: piece.family,
|
|
@@ -56,7 +56,7 @@ const fmt = (n) => {
|
|
|
56
56
|
const r = Math.round(n * 100) / 100;
|
|
57
57
|
return Object.is(r, -0) ? "0" : String(r);
|
|
58
58
|
};
|
|
59
|
-
const
|
|
59
|
+
const layoutCore = (input, measure) => {
|
|
60
60
|
const widthCache = /* @__PURE__ */ new Map();
|
|
61
61
|
const metricCache = /* @__PURE__ */ new Map();
|
|
62
62
|
const key = (text, s) => `${s.family}|${s.sizePx}|${s.bold}|${s.italic}|${s.letterSpacingPx}|${text}`;
|
|
@@ -170,11 +170,21 @@ const layoutTextSvg = (input, measure) => {
|
|
|
170
170
|
line.textAnchor = "end";
|
|
171
171
|
line.anchorX = wrapRight;
|
|
172
172
|
}
|
|
173
|
-
if (isFirst && bullet)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
if (isFirst && bullet) {
|
|
174
|
+
let bulletX = firstLeft;
|
|
175
|
+
if (para.align === "center" || para.align === "right") {
|
|
176
|
+
let end = toks.length;
|
|
177
|
+
while (end > 0 && (toks[end - 1].isSpace || toks[end - 1].isBreak)) end--;
|
|
178
|
+
let lineW = 0;
|
|
179
|
+
for (let ti = 0; ti < end; ti++) if (!toks[ti].isBreak) lineW += toks[ti].width;
|
|
180
|
+
bulletX = (para.align === "right" ? wrapRight - lineW : (lineLeft + wrapRight) / 2 - lineW / 2) - (lineLeft - firstLeft);
|
|
181
|
+
}
|
|
182
|
+
line.bullet = {
|
|
183
|
+
x: bulletX,
|
|
184
|
+
baselineDy: 0,
|
|
185
|
+
b: bullet
|
|
186
|
+
};
|
|
187
|
+
}
|
|
178
188
|
line.advance = lineAdvance(line, para);
|
|
179
189
|
cursorY += line.advance;
|
|
180
190
|
lines.push(line);
|
|
@@ -189,7 +199,7 @@ const layoutTextSvg = (input, measure) => {
|
|
|
189
199
|
const vert = input.vert ?? "none";
|
|
190
200
|
const cx = input.boxXpx + input.boxWpx / 2;
|
|
191
201
|
const cy = input.boxYpx + input.boxHpx / 2;
|
|
192
|
-
const frame = vert === "none" ? {
|
|
202
|
+
const frame = vert === "none" || vert === "upright" ? {
|
|
193
203
|
x: input.boxXpx,
|
|
194
204
|
y: input.boxYpx,
|
|
195
205
|
w: input.boxWpx,
|
|
@@ -201,25 +211,43 @@ const layoutTextSvg = (input, measure) => {
|
|
|
201
211
|
h: input.boxWpx
|
|
202
212
|
};
|
|
203
213
|
const columns = vert === "none" ? input.columns ?? null : null;
|
|
204
|
-
const
|
|
205
|
-
|
|
214
|
+
const { placements, requiredH } = columns && columns.count >= 2 ? placeColumns(frame, columns, input.anchor, buildLines) : placeSingle(frame, input.anchor, buildLines);
|
|
215
|
+
return {
|
|
216
|
+
placements,
|
|
217
|
+
requiredH,
|
|
218
|
+
vert,
|
|
219
|
+
cx,
|
|
220
|
+
cy
|
|
221
|
+
};
|
|
222
|
+
};
|
|
223
|
+
const layoutTextSvg = (input, measure) => {
|
|
224
|
+
const { placements, vert, cx, cy } = layoutCore(input, measure);
|
|
225
|
+
const body = emitPlacements(placements);
|
|
226
|
+
if (vert === "none" || vert === "upright") return body;
|
|
206
227
|
return `<g transform="rotate(${vert === "cw90" ? 90 : 270} ${fmt(cx)} ${fmt(cy)})">${body}</g>`;
|
|
207
228
|
};
|
|
229
|
+
/** Content height (px) the body would occupy at the given input's font sizes —
|
|
230
|
+
* for a single column the block height, for multi-column the tallest filled
|
|
231
|
+
* column. The SVG normAutofit path uses this to pick a shrink scale. */
|
|
232
|
+
const measureTextBodyHeight = (input, measure) => layoutCore(input, measure).requiredH;
|
|
208
233
|
const anchorOffsetY = (frameY, frameH, blockH, anchor, firstLine) => {
|
|
209
234
|
let offsetY = frameY;
|
|
210
235
|
if (anchor === "center") offsetY = frameY + (frameH - blockH) / 2;
|
|
211
236
|
else if (anchor === "bottom") offsetY = frameY + (frameH - blockH);
|
|
212
|
-
if (
|
|
237
|
+
if (firstLine) offsetY += BASELINE_LEADING_DROP * (firstLine.ascent + firstLine.descent);
|
|
213
238
|
return offsetY;
|
|
214
239
|
};
|
|
215
240
|
const placeSingle = (frame, anchor, buildLines) => {
|
|
216
241
|
const { lines, blockH } = buildLines(frame.x, frame.x + frame.w);
|
|
217
242
|
const offsetY = anchorOffsetY(frame.y, frame.h, blockH, anchor, lines[0]);
|
|
218
|
-
return
|
|
219
|
-
line
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
243
|
+
return {
|
|
244
|
+
placements: lines.map((line) => ({
|
|
245
|
+
line,
|
|
246
|
+
baselineY: offsetY + line.topY + topPad(line) + line.ascent,
|
|
247
|
+
dx: 0
|
|
248
|
+
})),
|
|
249
|
+
requiredH: blockH
|
|
250
|
+
};
|
|
223
251
|
};
|
|
224
252
|
const placeColumns = (frame, columns, anchor, buildLines) => {
|
|
225
253
|
const gap = columns.gapPx;
|
|
@@ -227,30 +255,38 @@ const placeColumns = (frame, columns, anchor, buildLines) => {
|
|
|
227
255
|
const { lines } = buildLines(frame.x, frame.x + colW);
|
|
228
256
|
let col = 0;
|
|
229
257
|
let colStartTopY = 0;
|
|
230
|
-
|
|
258
|
+
let rowTopY = 0;
|
|
259
|
+
let curColHasLine = false;
|
|
231
260
|
let tallest = 0;
|
|
232
261
|
const placed = [];
|
|
233
262
|
for (const line of lines) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
263
|
+
if (line.topY - colStartTopY + line.ascent + line.descent > frame.h && curColHasLine) {
|
|
264
|
+
if (col + 1 < columns.count) col += 1;
|
|
265
|
+
else {
|
|
266
|
+
col = 0;
|
|
267
|
+
rowTopY += frame.h;
|
|
268
|
+
}
|
|
237
269
|
colStartTopY = line.topY;
|
|
270
|
+
curColHasLine = false;
|
|
238
271
|
}
|
|
239
|
-
const localTopY = line.topY - colStartTopY;
|
|
272
|
+
const localTopY = rowTopY + (line.topY - colStartTopY);
|
|
240
273
|
placed.push({
|
|
241
274
|
line,
|
|
242
275
|
localTopY,
|
|
243
276
|
col
|
|
244
277
|
});
|
|
245
|
-
|
|
278
|
+
curColHasLine = true;
|
|
246
279
|
if (localTopY + line.advance > tallest) tallest = localTopY + line.advance;
|
|
247
280
|
}
|
|
248
281
|
const offsetY = anchorOffsetY(frame.y, frame.h, tallest, anchor, lines[0]);
|
|
249
|
-
return
|
|
250
|
-
line,
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
282
|
+
return {
|
|
283
|
+
placements: placed.map(({ line, localTopY, col: c }) => ({
|
|
284
|
+
line,
|
|
285
|
+
baselineY: offsetY + localTopY + topPad(line) + line.ascent,
|
|
286
|
+
dx: c * (colW + gap)
|
|
287
|
+
})),
|
|
288
|
+
requiredH: tallest
|
|
289
|
+
};
|
|
254
290
|
};
|
|
255
291
|
const emitPlacements = (placements) => {
|
|
256
292
|
const parts = [];
|
|
@@ -281,27 +317,76 @@ const emitLine = (line, baselineY, dx) => {
|
|
|
281
317
|
while (toks.length > 0 && (toks[toks.length - 1].isSpace || toks[toks.length - 1].isBreak)) toks.pop();
|
|
282
318
|
const content = toks.filter((t) => !t.isBreak);
|
|
283
319
|
if (content.length === 0) return "";
|
|
284
|
-
const
|
|
320
|
+
const groups = groupTokens(content);
|
|
321
|
+
const tspans = groups.map((g) => tspan(g)).join("");
|
|
285
322
|
if (tspans === "") return "";
|
|
286
|
-
|
|
323
|
+
const x0 = line.anchorX + dx + GRID_NUDGE_X;
|
|
324
|
+
return `<text x="${fmt(x0)}" y="${fmt(baselineY)}" text-anchor="${line.textAnchor}" xml:space="preserve">${tspans}</text>` + emitWavyUnderlines(groups, line.textAnchor, x0, baselineY);
|
|
287
325
|
};
|
|
288
326
|
const groupTokens = (toks) => {
|
|
289
327
|
const groups = [];
|
|
290
328
|
for (const t of toks) {
|
|
291
329
|
if (t.isBreak) continue;
|
|
292
330
|
const last = groups[groups.length - 1];
|
|
293
|
-
if (last && samePiece(last.piece, t.piece))
|
|
294
|
-
|
|
331
|
+
if (last && samePiece(last.piece, t.piece)) {
|
|
332
|
+
last.text += t.text;
|
|
333
|
+
last.width += t.width;
|
|
334
|
+
} else groups.push({
|
|
295
335
|
text: t.text,
|
|
296
|
-
piece: t.piece
|
|
336
|
+
piece: t.piece,
|
|
337
|
+
width: t.width
|
|
297
338
|
});
|
|
298
339
|
}
|
|
299
340
|
return groups;
|
|
300
341
|
};
|
|
342
|
+
const SUPERSCRIPT_SHIFT_RATIO = .33;
|
|
343
|
+
const SUBSCRIPT_SHIFT_RATIO = .16;
|
|
344
|
+
const baselineShiftPxOf = (p) => p.superSub === 1 ? p.sizePx * SUPERSCRIPT_SHIFT_RATIO : p.superSub === -1 ? -p.sizePx * SUBSCRIPT_SHIFT_RATIO : 0;
|
|
345
|
+
const SUPER_SUB_SIZE_RATIO = .65;
|
|
346
|
+
const renderedSizePxOf = (p) => p.superSub !== 0 ? p.sizePx * SUPER_SUB_SIZE_RATIO : p.sizePx;
|
|
347
|
+
const emitWavyUnderlines = (groups, textAnchor, x0, baselineY) => {
|
|
348
|
+
if (!groups.some((g) => g.piece.underline === "wavy")) return "";
|
|
349
|
+
const totalWidth = groups.reduce((sum, g) => sum + g.width, 0);
|
|
350
|
+
let cursor = textAnchor === "middle" ? x0 - totalWidth / 2 : textAnchor === "end" ? x0 - totalWidth : x0;
|
|
351
|
+
const parts = [];
|
|
352
|
+
for (const g of groups) {
|
|
353
|
+
if (g.piece.underline === "wavy" && g.width > 0) {
|
|
354
|
+
const y = baselineY - baselineShiftPxOf(g.piece);
|
|
355
|
+
parts.push(wavyPath(cursor, cursor + g.width, y, g.piece));
|
|
356
|
+
}
|
|
357
|
+
cursor += g.width;
|
|
358
|
+
}
|
|
359
|
+
return parts.join("");
|
|
360
|
+
};
|
|
361
|
+
const WAVY_AMPLITUDE_RATIO = .045;
|
|
362
|
+
const WAVY_AMPLITUDE_MIN_PX = .6;
|
|
363
|
+
const WAVY_PERIOD_RATIO = .18;
|
|
364
|
+
const WAVY_PERIOD_MIN_PX = 2;
|
|
365
|
+
const WAVY_BASELINE_OFFSET_RATIO = .12;
|
|
366
|
+
const WAVY_STROKE_WIDTH_RATIO = .06;
|
|
367
|
+
const WAVY_STROKE_WIDTH_MIN_PX = .6;
|
|
368
|
+
const wavyPath = (x1, x2, baselineY, piece) => {
|
|
369
|
+
const size = renderedSizePxOf(piece);
|
|
370
|
+
const amp = Math.max(WAVY_AMPLITUDE_MIN_PX, size * WAVY_AMPLITUDE_RATIO);
|
|
371
|
+
const period = Math.max(WAVY_PERIOD_MIN_PX, size * WAVY_PERIOD_RATIO);
|
|
372
|
+
const y = baselineY + size * WAVY_BASELINE_OFFSET_RATIO;
|
|
373
|
+
let d = `M${fmt(x1)} ${fmt(y)}`;
|
|
374
|
+
let cx = x1;
|
|
375
|
+
let up = true;
|
|
376
|
+
while (cx < x2 - .01) {
|
|
377
|
+
const half = Math.min(period / 2, x2 - cx);
|
|
378
|
+
const midX = cx + half;
|
|
379
|
+
d += ` Q${fmt(cx + half / 2)} ${fmt(y + (up ? -amp : amp))} ${fmt(midX)} ${fmt(y)}`;
|
|
380
|
+
cx = midX;
|
|
381
|
+
up = !up;
|
|
382
|
+
}
|
|
383
|
+
const strokeWidth = Math.max(WAVY_STROKE_WIDTH_MIN_PX, size * WAVY_STROKE_WIDTH_RATIO);
|
|
384
|
+
return `<path d="${d}" stroke="${piece.fillHex}" stroke-width="${fmt(strokeWidth)}" fill="none"/>`;
|
|
385
|
+
};
|
|
301
386
|
const 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;
|
|
302
387
|
const tspan = (g) => {
|
|
303
388
|
const p = g.piece;
|
|
304
|
-
const sizePx = p
|
|
389
|
+
const sizePx = renderedSizePxOf(p);
|
|
305
390
|
const attrs = [
|
|
306
391
|
`font-family="${escapeXml$1(p.family)}"`,
|
|
307
392
|
`font-size="${fmt(sizePx)}"`,
|
|
@@ -310,12 +395,11 @@ const tspan = (g) => {
|
|
|
310
395
|
if (p.bold) attrs.push("font-weight=\"700\"");
|
|
311
396
|
if (p.italic) attrs.push("font-style=\"italic\"");
|
|
312
397
|
const deco = [];
|
|
313
|
-
if (p.underline) deco.push("underline");
|
|
398
|
+
if (p.underline === "single") deco.push("underline");
|
|
314
399
|
if (p.strike) deco.push("line-through");
|
|
315
400
|
if (deco.length) attrs.push(`text-decoration="${deco.join(" ")}"`);
|
|
316
401
|
if (p.letterSpacingPx !== 0) attrs.push(`letter-spacing="${fmt(p.letterSpacingPx)}"`);
|
|
317
|
-
if (p.superSub
|
|
318
|
-
else if (p.superSub === -1) attrs.push(`baseline-shift="${fmt(-p.sizePx * .16)}"`);
|
|
402
|
+
if (p.superSub !== 0) attrs.push(`baseline-shift="${fmt(baselineShiftPxOf(p))}"`);
|
|
319
403
|
return `<tspan ${attrs.join(" ")}>${escapeXml$1(g.text)}</tspan>`;
|
|
320
404
|
};
|
|
321
405
|
const wrapTokens = (tokens, wrap, firstAvail, avail) => {
|
|
@@ -348,8 +432,8 @@ const wrapTokens = (tokens, wrap, firstAvail, avail) => {
|
|
|
348
432
|
continue;
|
|
349
433
|
}
|
|
350
434
|
const limit = first ? firstAvail : avail;
|
|
351
|
-
const
|
|
352
|
-
if (wrap &&
|
|
435
|
+
const hasContent = lineW - trailingSpaceW > 0;
|
|
436
|
+
if (wrap && hasContent && lineW + tok.width > limit + .5) {
|
|
353
437
|
close();
|
|
354
438
|
cur.push(tok);
|
|
355
439
|
lineW = tok.width;
|
|
@@ -374,6 +458,7 @@ const PX_PER_PT = 96 / 72;
|
|
|
374
458
|
const DEFAULT_BODY_PT = 18;
|
|
375
459
|
const DEFAULT_TITLE_PT = 44;
|
|
376
460
|
const DEFAULT_FONT = "Calibri, 'Helvetica Neue', Arial, sans-serif";
|
|
461
|
+
const DEFAULT_BULLET_FONT = "Arial";
|
|
377
462
|
const DEFAULT_INSET_X = 91440;
|
|
378
463
|
const DEFAULT_INSET_Y = 45720;
|
|
379
464
|
const u8ToBase64 = (data) => {
|
|
@@ -585,59 +670,65 @@ const gradientDef = (grad, theme) => {
|
|
|
585
670
|
fillAttr: `url(#${id})`
|
|
586
671
|
};
|
|
587
672
|
};
|
|
673
|
+
const ORTHO_TILE = 4;
|
|
674
|
+
const ORTHO_WIDE_TILE = 16;
|
|
675
|
+
const DIAG_TILE = 16;
|
|
676
|
+
const MOTIF_TILE = 8;
|
|
677
|
+
const PATTERN_TILE_SIZE = {
|
|
678
|
+
horz: ORTHO_TILE,
|
|
679
|
+
ltHorz: ORTHO_TILE,
|
|
680
|
+
narHorz: ORTHO_TILE,
|
|
681
|
+
dashHorz: ORTHO_TILE,
|
|
682
|
+
horzBrick: ORTHO_TILE,
|
|
683
|
+
dkHorz: ORTHO_TILE,
|
|
684
|
+
vert: ORTHO_TILE,
|
|
685
|
+
ltVert: ORTHO_TILE,
|
|
686
|
+
narVert: ORTHO_TILE,
|
|
687
|
+
dashVert: ORTHO_TILE,
|
|
688
|
+
dkVert: ORTHO_TILE,
|
|
689
|
+
ltHorzCross: ORTHO_TILE,
|
|
690
|
+
cross: ORTHO_TILE,
|
|
691
|
+
dotGrid: ORTHO_TILE,
|
|
692
|
+
smGrid: ORTHO_TILE,
|
|
693
|
+
dkHorzCross: ORTHO_WIDE_TILE,
|
|
694
|
+
lgGrid: ORTHO_WIDE_TILE,
|
|
695
|
+
plaid: ORTHO_WIDE_TILE,
|
|
696
|
+
wave: MOTIF_TILE,
|
|
697
|
+
zigZag: MOTIF_TILE,
|
|
698
|
+
weave: MOTIF_TILE,
|
|
699
|
+
divot: MOTIF_TILE,
|
|
700
|
+
sphere: MOTIF_TILE,
|
|
701
|
+
solidDmnd: MOTIF_TILE,
|
|
702
|
+
openDmnd: MOTIF_TILE
|
|
703
|
+
};
|
|
588
704
|
const patternDef = (pat) => {
|
|
589
705
|
const id = mintId();
|
|
590
706
|
const fg = pat.foreground;
|
|
591
707
|
const bg = pat.background;
|
|
592
708
|
const preset = pat.preset;
|
|
593
709
|
let body = "";
|
|
594
|
-
const
|
|
595
|
-
const
|
|
710
|
+
const PCT_DENSITY_FLOOR = .05;
|
|
711
|
+
const PCT_TILE_MIN = 6;
|
|
712
|
+
const pctMatch = /^pct(\d+)$/.exec(preset);
|
|
713
|
+
const pctDensity = pctMatch ? Math.min(100, Math.max(0, Number.parseInt(pctMatch[1], 10))) / 100 : null;
|
|
714
|
+
const W = pctDensity !== null ? Math.max(PCT_TILE_MIN, DIAG_TILE * 2 * Math.sqrt(PCT_DENSITY_FLOOR / Math.max(pctDensity, PCT_DENSITY_FLOOR))) : PATTERN_TILE_SIZE[preset] ?? DIAG_TILE;
|
|
715
|
+
const H = W;
|
|
596
716
|
const stripe = (orientation, width = 1) => {
|
|
597
717
|
if (orientation === "h") return `<path d="M0 ${H / 2}H${W}" stroke="${fg}" stroke-width="${width}"/>`;
|
|
598
718
|
if (orientation === "v") return `<path d="M${W / 2} 0V${H}" stroke="${fg}" stroke-width="${width}"/>`;
|
|
599
719
|
if (orientation === "d") return `<path d="M0 0L${W} ${H}" stroke="${fg}" stroke-width="${width}"/>`;
|
|
600
720
|
return `<path d="M${W} 0L0 ${H}" stroke="${fg}" stroke-width="${width}"/>`;
|
|
601
721
|
};
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
12,
|
|
608
|
-
4,
|
|
609
|
-
14,
|
|
610
|
-
6,
|
|
611
|
-
3,
|
|
612
|
-
11,
|
|
613
|
-
1,
|
|
614
|
-
9,
|
|
615
|
-
15,
|
|
616
|
-
7,
|
|
617
|
-
13,
|
|
618
|
-
5
|
|
619
|
-
];
|
|
620
|
-
const screen = (density) => {
|
|
621
|
-
const threshold = density * 16;
|
|
622
|
-
const out = [];
|
|
623
|
-
for (let i = 0; i < 16; i++) if (BAYER4[i] < threshold) {
|
|
624
|
-
const cx = i % 4 * 2;
|
|
625
|
-
const cy = Math.floor(i / 4) * 2;
|
|
626
|
-
out.push(`<rect x="${cx}" y="${cy}" width="2" height="2" fill="${fg}"/>`);
|
|
627
|
-
}
|
|
628
|
-
return out.join("");
|
|
629
|
-
};
|
|
630
|
-
const pctMatch = /^pct(\d+)$/.exec(preset);
|
|
631
|
-
if (pctMatch) body = screen(Math.min(100, Math.max(0, Number.parseInt(pctMatch[1], 10))) / 100);
|
|
632
|
-
else if (preset === "horzBrick" || preset === "ltHorizontal" || preset === "narHorz") body = stripe("h", .8);
|
|
633
|
-
else if (preset === "dkHorizontal") body = stripe("h", 2);
|
|
634
|
-
else if (preset === "ltVertical" || preset === "narVert") body = stripe("v", .8);
|
|
635
|
-
else if (preset === "dkVertical") body = stripe("v", 2);
|
|
722
|
+
if (pctDensity !== null) body = pctDensity >= .3 ? stripe("d", .8) + stripe("a", .8) : stripe("d", .8);
|
|
723
|
+
else if (preset === "horz" || preset === "ltHorz" || preset === "narHorz" || preset === "dashHorz" || preset === "horzBrick") body = stripe("h", .8);
|
|
724
|
+
else if (preset === "dkHorz") body = stripe("h", 2);
|
|
725
|
+
else if (preset === "vert" || preset === "ltVert" || preset === "narVert" || preset === "dashVert") body = stripe("v", .8);
|
|
726
|
+
else if (preset === "dkVert") body = stripe("v", 2);
|
|
636
727
|
else if (preset === "ltUpDiag" || preset === "wdUpDiag") body = stripe("d", .8);
|
|
637
728
|
else if (preset === "dkUpDiag") body = stripe("d", 2);
|
|
638
729
|
else if (preset === "ltDnDiag" || preset === "wdDnDiag") body = stripe("a", .8);
|
|
639
730
|
else if (preset === "dkDnDiag") body = stripe("a", 2);
|
|
640
|
-
else if (preset === "ltHorzCross" || preset === "smGrid" || preset === "cross") body = stripe("h", .8) + stripe("v", .8);
|
|
731
|
+
else if (preset === "ltHorzCross" || preset === "smGrid" || preset === "cross" || preset === "dotGrid") body = stripe("h", .8) + stripe("v", .8);
|
|
641
732
|
else if (preset === "dkHorzCross" || preset === "lgGrid" || preset === "plaid") body = stripe("h", 2) + stripe("v", 2);
|
|
642
733
|
else if (preset === "diagCross" || preset === "trellis" || preset === "shingle" || preset === "dashUpDiag" || preset === "dashDnDiag") body = stripe("d", .8) + stripe("a", .8);
|
|
643
734
|
else if (preset === "dkUpDiagStripe" || preset === "dkDnDiagStripe") body = stripe(preset === "dkUpDiagStripe" ? "d" : "a", 2);
|
|
@@ -645,7 +736,7 @@ const patternDef = (pat) => {
|
|
|
645
736
|
else if (preset === "weave" || preset === "divot") body = `<path d="M0 0L4 4 0 8M4 0L8 4 4 8" stroke="${fg}" stroke-width="0.8" fill="none"/>`;
|
|
646
737
|
else if (preset === "sphere") body = `<circle cx="4" cy="4" r="3" fill="${fg}" fill-opacity="0.7"/>`;
|
|
647
738
|
else if (preset === "solidDmnd" || preset === "openDmnd") body = `<path d="M4 1L7 4 4 7 1 4Z" fill="${preset === "solidDmnd" ? fg : "none"}" stroke="${fg}" stroke-width="0.6"/>`;
|
|
648
|
-
else body =
|
|
739
|
+
else body = stripe("d", .8) + stripe("a", .8);
|
|
649
740
|
return {
|
|
650
741
|
defs: `<defs><pattern id="${id}" patternUnits="userSpaceOnUse" width="${W}" height="${H}"><rect width="${W}" height="${H}" fill="${bg}"/>${body}</pattern></defs>`,
|
|
651
742
|
fillAttr: `url(#${id})`
|
|
@@ -829,42 +920,54 @@ const PRESET_POINTS = {
|
|
|
829
920
|
star16: () => star(16),
|
|
830
921
|
star24: () => star(24),
|
|
831
922
|
star32: () => star(32),
|
|
832
|
-
rightArrow: () =>
|
|
833
|
-
|
|
834
|
-
[
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
[
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
923
|
+
rightArrow: (w, h) => {
|
|
924
|
+
const bx = 1 - Math.min(1, .5 * Math.min(w, h) / w);
|
|
925
|
+
return [
|
|
926
|
+
[0, .25],
|
|
927
|
+
[bx, .25],
|
|
928
|
+
[bx, 0],
|
|
929
|
+
[1, .5],
|
|
930
|
+
[bx, 1],
|
|
931
|
+
[bx, .75],
|
|
932
|
+
[0, .75]
|
|
933
|
+
];
|
|
934
|
+
},
|
|
935
|
+
leftArrow: (w, h) => {
|
|
936
|
+
const bx = Math.min(1, .5 * Math.min(w, h) / w);
|
|
937
|
+
return [
|
|
938
|
+
[1, .25],
|
|
939
|
+
[bx, .25],
|
|
940
|
+
[bx, 0],
|
|
941
|
+
[0, .5],
|
|
942
|
+
[bx, 1],
|
|
943
|
+
[bx, .75],
|
|
944
|
+
[1, .75]
|
|
945
|
+
];
|
|
946
|
+
},
|
|
947
|
+
upArrow: (w, h) => {
|
|
948
|
+
const by = Math.min(1, .5 * Math.min(w, h) / h);
|
|
949
|
+
return [
|
|
950
|
+
[.25, 1],
|
|
951
|
+
[.25, by],
|
|
952
|
+
[0, by],
|
|
953
|
+
[.5, 0],
|
|
954
|
+
[1, by],
|
|
955
|
+
[.75, by],
|
|
956
|
+
[.75, 1]
|
|
957
|
+
];
|
|
958
|
+
},
|
|
959
|
+
downArrow: (w, h) => {
|
|
960
|
+
const by = 1 - Math.min(1, .5 * Math.min(w, h) / h);
|
|
961
|
+
return [
|
|
962
|
+
[.25, 0],
|
|
963
|
+
[.25, by],
|
|
964
|
+
[0, by],
|
|
965
|
+
[.5, 1],
|
|
966
|
+
[1, by],
|
|
967
|
+
[.75, by],
|
|
968
|
+
[.75, 0]
|
|
969
|
+
];
|
|
970
|
+
},
|
|
868
971
|
leftRightArrow: () => [
|
|
869
972
|
[0, .5],
|
|
870
973
|
[.18, .2],
|
|
@@ -1745,9 +1848,14 @@ const renderRun = (text, format, theme, effectivePt, _wasDefault = false) => {
|
|
|
1745
1848
|
const strike = format?.strike;
|
|
1746
1849
|
const hasUnderline = underline !== void 0 && underline !== false && underline !== "none";
|
|
1747
1850
|
const hasStrike = strike !== void 0 && strike !== false && strike !== "noStrike";
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1851
|
+
const isWavyUnderline = typeof underline === "string" && underline.startsWith("wavy");
|
|
1852
|
+
const nestedWavyUnderline = hasUnderline && hasStrike && isWavyUnderline;
|
|
1853
|
+
if (nestedWavyUnderline) styles.push("text-decoration:line-through");
|
|
1854
|
+
else if (hasUnderline && hasStrike) styles.push("text-decoration:underline line-through");
|
|
1855
|
+
else if (hasUnderline) {
|
|
1856
|
+
styles.push("text-decoration:underline");
|
|
1857
|
+
if (isWavyUnderline) styles.push("text-decoration-style:wavy");
|
|
1858
|
+
} else if (hasStrike) styles.push("text-decoration:line-through");
|
|
1751
1859
|
if (format?.color !== void 0 && format.color !== null) styles.push(`color:${resolveColor(format.color, theme, "#000000")}`);
|
|
1752
1860
|
if (format?.spc !== void 0 && format.spc !== 0) {
|
|
1753
1861
|
const trackingPx = format.spc / 100 * PX_PER_PT;
|
|
@@ -1762,13 +1870,18 @@ const renderRun = (text, format, theme, effectivePt, _wasDefault = false) => {
|
|
|
1762
1870
|
else if (format?.cap === "small") styles.push("font-variant:small-caps");
|
|
1763
1871
|
if (format?.highlight !== void 0 && format.highlight !== null) styles.push(`background-color:${resolveColor(format.highlight, theme, "#FFFF00")}`);
|
|
1764
1872
|
const html = text.split("\n").map((part) => escapeXml(part)).join("<br/>");
|
|
1765
|
-
|
|
1873
|
+
const content = nestedWavyUnderline ? `<span style="text-decoration:underline;text-decoration-style:wavy">${html}</span>` : html;
|
|
1874
|
+
return `<span style="${styles.join(";")}">${content}</span>`;
|
|
1766
1875
|
};
|
|
1767
1876
|
const LINE_HEIGHT = 1.05;
|
|
1768
1877
|
const AVG_GLYPH_W_RATIO = .55;
|
|
1769
|
-
const
|
|
1878
|
+
const AUTOFIT_FLOOR = .25;
|
|
1879
|
+
const AUTOFIT_STEP = .05;
|
|
1880
|
+
const underlineStyleOf = (fmt) => {
|
|
1770
1881
|
const u = fmt?.underline;
|
|
1771
|
-
|
|
1882
|
+
if (u === void 0 || u === false || u === "none") return "none";
|
|
1883
|
+
if (typeof u === "string" && u.startsWith("wavy")) return "wavy";
|
|
1884
|
+
return "single";
|
|
1772
1885
|
};
|
|
1773
1886
|
const hasStrikeFmt = (fmt) => {
|
|
1774
1887
|
const s = fmt?.strike;
|
|
@@ -1778,15 +1891,15 @@ const alignOf = (a) => a === "center" || a === "right" || a === "justify" ? a :
|
|
|
1778
1891
|
const verticalLayoutOf = (vert) => {
|
|
1779
1892
|
switch (vert) {
|
|
1780
1893
|
case "vert":
|
|
1781
|
-
case "eaVert":
|
|
1894
|
+
case "eaVert": return "cw90";
|
|
1782
1895
|
case "wordArtVert":
|
|
1783
|
-
case "wordArtVertRtl": return "
|
|
1896
|
+
case "wordArtVertRtl": return "upright";
|
|
1784
1897
|
case "vert270":
|
|
1785
1898
|
case "mongolianVert": return "cw270";
|
|
1786
1899
|
case null: return "none";
|
|
1787
1900
|
}
|
|
1788
1901
|
};
|
|
1789
|
-
const
|
|
1902
|
+
const buildSvgTextInput = (a) => {
|
|
1790
1903
|
const scale = a.autoFitScale;
|
|
1791
1904
|
const paragraphs = a.paraData.map((para, pi) => {
|
|
1792
1905
|
const pieces = [];
|
|
@@ -1800,7 +1913,7 @@ const buildAndLayoutSvgText = (a) => {
|
|
|
1800
1913
|
const hlinkColor = a.theme ? normalizeHex(a.theme.hyperlink) : "#0563C1";
|
|
1801
1914
|
fmt = {
|
|
1802
1915
|
...fmt,
|
|
1803
|
-
color:
|
|
1916
|
+
color: hlinkColor,
|
|
1804
1917
|
underline: fmt?.underline ?? true
|
|
1805
1918
|
};
|
|
1806
1919
|
}
|
|
@@ -1817,16 +1930,26 @@ const buildAndLayoutSvgText = (a) => {
|
|
|
1817
1930
|
italic: fmt?.italic ?? false,
|
|
1818
1931
|
letterSpacingPx,
|
|
1819
1932
|
fillHex,
|
|
1820
|
-
underline:
|
|
1933
|
+
underline: underlineStyleOf(fmt),
|
|
1821
1934
|
strike: hasStrikeFmt(fmt),
|
|
1822
1935
|
superSub,
|
|
1823
1936
|
href: run.href ?? null
|
|
1824
1937
|
};
|
|
1825
1938
|
const segs = run.text.split("\n");
|
|
1826
1939
|
segs.forEach((seg, i) => {
|
|
1827
|
-
|
|
1940
|
+
const segText = caps ? seg.toUpperCase() : seg;
|
|
1941
|
+
if (a.vert === "upright") for (const g of Array.from(segText)) {
|
|
1942
|
+
const last = pieces[pieces.length - 1];
|
|
1943
|
+
if (last !== void 0 && !last.isBreak) pieces.push(breakPiece());
|
|
1944
|
+
pieces.push({
|
|
1945
|
+
...base,
|
|
1946
|
+
text: g,
|
|
1947
|
+
isBreak: false
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1950
|
+
else pieces.push({
|
|
1828
1951
|
...base,
|
|
1829
|
-
text:
|
|
1952
|
+
text: segText,
|
|
1830
1953
|
isBreak: false
|
|
1831
1954
|
});
|
|
1832
1955
|
if (i < segs.length - 1) pieces.push(breakPiece());
|
|
@@ -1858,7 +1981,7 @@ const buildAndLayoutSvgText = (a) => {
|
|
|
1858
1981
|
fallbackSizePx: a.defaultPt * scale * PX_PER_PT
|
|
1859
1982
|
};
|
|
1860
1983
|
});
|
|
1861
|
-
return
|
|
1984
|
+
return {
|
|
1862
1985
|
boxXpx: a.innerX / EMU_PER_PX,
|
|
1863
1986
|
boxYpx: a.innerY / EMU_PER_PX,
|
|
1864
1987
|
boxWpx: a.innerW / EMU_PER_PX,
|
|
@@ -1868,8 +1991,9 @@ const buildAndLayoutSvgText = (a) => {
|
|
|
1868
1991
|
paragraphs,
|
|
1869
1992
|
vert: a.vert,
|
|
1870
1993
|
columns: a.columns
|
|
1871
|
-
}
|
|
1994
|
+
};
|
|
1872
1995
|
};
|
|
1996
|
+
const buildAndLayoutSvgText = (a) => layoutTextSvg(buildSvgTextInput(a), a.measure);
|
|
1873
1997
|
const breakPiece = () => ({
|
|
1874
1998
|
text: "",
|
|
1875
1999
|
family: "",
|
|
@@ -1878,7 +2002,7 @@ const breakPiece = () => ({
|
|
|
1878
2002
|
italic: false,
|
|
1879
2003
|
letterSpacingPx: 0,
|
|
1880
2004
|
fillHex: "#000000",
|
|
1881
|
-
underline:
|
|
2005
|
+
underline: "none",
|
|
1882
2006
|
strike: false,
|
|
1883
2007
|
superSub: 0,
|
|
1884
2008
|
href: null,
|
|
@@ -1889,17 +2013,52 @@ const buildBullet = (a, para, pi) => {
|
|
|
1889
2013
|
const numberLabel = a.numberLabels[pi] ?? null;
|
|
1890
2014
|
if (!(para.bulletStyle === "bullet" || explicitChar !== null || numberLabel !== null || para.bulletIsPicture || para.bulletStyle !== "none" && para.level > 0)) return null;
|
|
1891
2015
|
const char = para.bulletIsPicture ? "■" : numberLabel ?? explicitChar ?? bulletChar(para.level);
|
|
1892
|
-
const baseSizePx = a.defaultPt * PX_PER_PT * a.autoFitScale;
|
|
2016
|
+
const baseSizePx = (para.runs.find((r) => r.text !== "\n" && r.text !== "")?.sizePt ?? a.defaultPt) * PX_PER_PT * a.autoFitScale;
|
|
1893
2017
|
const sizePx = para.bulletDetail.sizePct !== null ? baseSizePx * para.bulletDetail.sizePct : para.bulletDetail.sizePts !== null ? para.bulletDetail.sizePts * PX_PER_PT * a.autoFitScale : baseSizePx;
|
|
1894
2018
|
const fillHex = para.bulletDetail.color ? resolveColor(para.bulletDetail.color, a.theme, "#000000") : a.defaultColor;
|
|
1895
2019
|
return {
|
|
1896
2020
|
text: char,
|
|
1897
|
-
family: substituteFamily(para.bulletDetail.font ??
|
|
2021
|
+
family: substituteFamily(para.bulletDetail.font ?? DEFAULT_BULLET_FONT),
|
|
1898
2022
|
sizePx,
|
|
1899
2023
|
fillHex,
|
|
1900
2024
|
...para.bulletImageHref ? { imageHref: para.bulletImageHref } : {}
|
|
1901
2025
|
};
|
|
1902
2026
|
};
|
|
2027
|
+
const presetTextRect = (preset) => {
|
|
2028
|
+
switch (preset) {
|
|
2029
|
+
case "triangle": return {
|
|
2030
|
+
l: .25,
|
|
2031
|
+
t: .5,
|
|
2032
|
+
r: .75,
|
|
2033
|
+
b: 1
|
|
2034
|
+
};
|
|
2035
|
+
case "diamond": return {
|
|
2036
|
+
l: .25,
|
|
2037
|
+
t: .25,
|
|
2038
|
+
r: .75,
|
|
2039
|
+
b: .75
|
|
2040
|
+
};
|
|
2041
|
+
case "pentagon": return {
|
|
2042
|
+
l: .191,
|
|
2043
|
+
t: .236,
|
|
2044
|
+
r: .809,
|
|
2045
|
+
b: 1
|
|
2046
|
+
};
|
|
2047
|
+
case "star5": return {
|
|
2048
|
+
l: .309,
|
|
2049
|
+
t: .382,
|
|
2050
|
+
r: .691,
|
|
2051
|
+
b: .764
|
|
2052
|
+
};
|
|
2053
|
+
case "leftRightArrow": return {
|
|
2054
|
+
l: .18,
|
|
2055
|
+
t: .35,
|
|
2056
|
+
r: .82,
|
|
2057
|
+
b: .65
|
|
2058
|
+
};
|
|
2059
|
+
default: return null;
|
|
2060
|
+
}
|
|
2061
|
+
};
|
|
1903
2062
|
const renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
1904
2063
|
let paragraphCount;
|
|
1905
2064
|
try {
|
|
@@ -1937,11 +2096,33 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
|
1937
2096
|
const tIns = margins.top ?? DEFAULT_INSET_Y;
|
|
1938
2097
|
const rIns = margins.right ?? DEFAULT_INSET_X;
|
|
1939
2098
|
const bIns = margins.bottom ?? DEFAULT_INSET_Y;
|
|
1940
|
-
const
|
|
1941
|
-
const
|
|
1942
|
-
const
|
|
1943
|
-
const
|
|
2099
|
+
const pRect = presetTextRect(getShapePreset(shape));
|
|
2100
|
+
const rectX = pRect ? bounds.x + pRect.l * bounds.w : bounds.x;
|
|
2101
|
+
const rectY = pRect ? bounds.y + pRect.t * bounds.h : bounds.y;
|
|
2102
|
+
const rectW = pRect ? (pRect.r - pRect.l) * bounds.w : bounds.w;
|
|
2103
|
+
const rectH = pRect ? (pRect.b - pRect.t) * bounds.h : bounds.h;
|
|
2104
|
+
let innerX = rectX + lIns;
|
|
2105
|
+
let innerY = rectY + tIns;
|
|
2106
|
+
let innerW = rectW - lIns - rIns;
|
|
2107
|
+
let innerH = rectH - tIns - bIns;
|
|
2108
|
+
if (pRect && (innerW <= 0 || innerH <= 0)) {
|
|
2109
|
+
innerX = rectX;
|
|
2110
|
+
innerY = rectY;
|
|
2111
|
+
innerW = rectW;
|
|
2112
|
+
innerH = rectH;
|
|
2113
|
+
}
|
|
1944
2114
|
if (innerW <= 0 || innerH <= 0) return "";
|
|
2115
|
+
const svgTextRect = (v) => v === "none" || v === "upright" ? {
|
|
2116
|
+
x: innerX,
|
|
2117
|
+
y: innerY,
|
|
2118
|
+
w: innerW,
|
|
2119
|
+
h: innerH
|
|
2120
|
+
} : {
|
|
2121
|
+
x: bounds.x + tIns,
|
|
2122
|
+
y: bounds.y + lIns,
|
|
2123
|
+
w: Math.max(0, bounds.w - tIns - bIns),
|
|
2124
|
+
h: Math.max(0, bounds.h - lIns - rIns)
|
|
2125
|
+
};
|
|
1945
2126
|
const paraData = [];
|
|
1946
2127
|
let hasAnyText = false;
|
|
1947
2128
|
for (let p = 0; p < paragraphCount; p++) {
|
|
@@ -2121,6 +2302,45 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
|
2121
2302
|
numberLabels[i] = formatAutoNum(num, counter);
|
|
2122
2303
|
}
|
|
2123
2304
|
}
|
|
2305
|
+
if (authoredAutofit && autoFitScale === 1) {
|
|
2306
|
+
const fitVert = verticalLayoutOf(effectiveBody.vert ?? getShapeTextDirection(shape));
|
|
2307
|
+
const fitCols = getShapeTextColumns(shape);
|
|
2308
|
+
const fitColumns = fitVert === "none" && fitCols && fitCols.count >= 2 ? {
|
|
2309
|
+
count: fitCols.count,
|
|
2310
|
+
gapPx: fitCols.gapEmu !== void 0 ? fitCols.gapEmu / EMU_PER_PX : 12
|
|
2311
|
+
} : null;
|
|
2312
|
+
const fitRect = svgTextRect(fitVert);
|
|
2313
|
+
const fitBoxPx = (fitVert === "cw90" || fitVert === "cw270" ? fitRect.w : fitRect.h) / EMU_PER_PX;
|
|
2314
|
+
const fitArgsBase = {
|
|
2315
|
+
pres,
|
|
2316
|
+
shape,
|
|
2317
|
+
theme,
|
|
2318
|
+
paraData,
|
|
2319
|
+
numberLabels,
|
|
2320
|
+
lineHeightScale,
|
|
2321
|
+
defaultPt,
|
|
2322
|
+
themeFace,
|
|
2323
|
+
defaultColor: activeDeckTextColor,
|
|
2324
|
+
anchor: anchor === "center" || anchor === "bottom" ? anchor : "top",
|
|
2325
|
+
wrap: effectiveBody.wrap !== "none",
|
|
2326
|
+
innerX: fitRect.x,
|
|
2327
|
+
innerY: fitRect.y,
|
|
2328
|
+
innerW: fitRect.w,
|
|
2329
|
+
innerH: fitRect.h,
|
|
2330
|
+
measure: ctx.measure,
|
|
2331
|
+
vert: fitVert,
|
|
2332
|
+
columns: fitColumns
|
|
2333
|
+
};
|
|
2334
|
+
let s = 1;
|
|
2335
|
+
while (s > AUTOFIT_FLOOR) {
|
|
2336
|
+
if (measureTextBodyHeight(buildSvgTextInput({
|
|
2337
|
+
...fitArgsBase,
|
|
2338
|
+
autoFitScale: s
|
|
2339
|
+
}), ctx.measure) <= fitBoxPx) break;
|
|
2340
|
+
s -= AUTOFIT_STEP;
|
|
2341
|
+
}
|
|
2342
|
+
autoFitScale = Math.max(AUTOFIT_FLOOR, s);
|
|
2343
|
+
}
|
|
2124
2344
|
const paragraphs = [];
|
|
2125
2345
|
for (let pi = 0; pi < paraData.length; pi++) {
|
|
2126
2346
|
const para = paraData[pi];
|
|
@@ -2130,7 +2350,7 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
|
2130
2350
|
const hlinkColor = theme ? normalizeHex(theme.hyperlink) : "#0563C1";
|
|
2131
2351
|
runFmt = {
|
|
2132
2352
|
...runFmt,
|
|
2133
|
-
color:
|
|
2353
|
+
color: hlinkColor,
|
|
2134
2354
|
underline: runFmt?.underline ?? true
|
|
2135
2355
|
};
|
|
2136
2356
|
}
|
|
@@ -2162,8 +2382,8 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
|
2162
2382
|
const explicitChar = para.bulletStyle !== null && typeof para.bulletStyle === "object" && "char" in para.bulletStyle ? para.bulletStyle.char : null;
|
|
2163
2383
|
const numberLabel = numberLabels[pi];
|
|
2164
2384
|
const showBullet = para.bulletStyle === "bullet" || explicitChar !== null || numberLabel !== null || para.bulletIsPicture || para.bulletStyle !== "none" && para.level > 0;
|
|
2385
|
+
const baseBulletPx = (para.runs.find((r) => r.text !== "\n" && r.text !== "")?.sizePt ?? defaultPt) * PX_PER_PT * autoFitScale;
|
|
2165
2386
|
if (showBullet && para.bulletImageHref) {
|
|
2166
|
-
const baseBulletPx = defaultPt * PX_PER_PT * autoFitScale;
|
|
2167
2387
|
const bulletPx = para.bulletDetail.sizePct !== null ? baseBulletPx * para.bulletDetail.sizePct : para.bulletDetail.sizePts !== null ? para.bulletDetail.sizePts * PX_PER_PT * autoFitScale : baseBulletPx;
|
|
2168
2388
|
const imgStyles = [
|
|
2169
2389
|
`width:${bulletPx.toFixed(2)}px`,
|
|
@@ -2179,7 +2399,8 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
|
2179
2399
|
if (para.bulletDetail.color) bulletStyles.push(`color:${resolveColor(para.bulletDetail.color, theme, "#000000")}`);
|
|
2180
2400
|
if (para.bulletDetail.sizePct !== null) bulletStyles.push(`font-size:${(para.bulletDetail.sizePct * 100).toFixed(1)}%`);
|
|
2181
2401
|
else if (para.bulletDetail.sizePts !== null) bulletStyles.push(`font-size:${(para.bulletDetail.sizePts * PX_PER_PT * autoFitScale).toFixed(2)}px`);
|
|
2182
|
-
|
|
2402
|
+
else bulletStyles.push(`font-size:${baseBulletPx.toFixed(2)}px`);
|
|
2403
|
+
bulletStyles.push(para.bulletDetail.font ? `font-family:${escapeXml(para.bulletDetail.font)}, ${DEFAULT_FONT}` : `font-family:${DEFAULT_BULLET_FONT}, ${DEFAULT_FONT}`);
|
|
2183
2404
|
prefix = `<span style="${bulletStyles.join(";")}">${escapeXml(char)}</span>`;
|
|
2184
2405
|
}
|
|
2185
2406
|
paragraphs.push(`<p style="${pStyles.join(";")}">${prefix}${runHtmls.join("") || "​"}</p>`);
|
|
@@ -2187,7 +2408,6 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
|
2187
2408
|
const justify = ANCHOR_TO_CSS[anchor] ?? "flex-start";
|
|
2188
2409
|
const defaultColor = activeDeckTextColor;
|
|
2189
2410
|
if (ctx.mode === "svg") {
|
|
2190
|
-
const svgScale = authoredAutofit?.fontScale ?? 1;
|
|
2191
2411
|
const svgLineScale = 1 - (authoredAutofit?.lnSpcReduction ?? 0);
|
|
2192
2412
|
const svgVert = verticalLayoutOf(effectiveBody.vert ?? getShapeTextDirection(shape));
|
|
2193
2413
|
const svgCols = getShapeTextColumns(shape);
|
|
@@ -2195,31 +2415,37 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx) => {
|
|
|
2195
2415
|
count: svgCols.count,
|
|
2196
2416
|
gapPx: svgCols.gapEmu !== void 0 ? svgCols.gapEmu / EMU_PER_PX : 12
|
|
2197
2417
|
} : null;
|
|
2198
|
-
const
|
|
2418
|
+
const { x: vInnerX, y: vInnerY, w: vInnerW, h: vInnerH } = svgTextRect(svgVert);
|
|
2419
|
+
if (vInnerW <= 0 || vInnerH <= 0) return "";
|
|
2420
|
+
const svgArgsBase = {
|
|
2199
2421
|
pres,
|
|
2200
2422
|
shape,
|
|
2201
2423
|
theme,
|
|
2202
2424
|
paraData,
|
|
2203
2425
|
numberLabels,
|
|
2204
|
-
autoFitScale: svgScale,
|
|
2205
2426
|
lineHeightScale: svgLineScale,
|
|
2206
2427
|
defaultPt,
|
|
2207
2428
|
themeFace,
|
|
2208
2429
|
defaultColor,
|
|
2209
2430
|
anchor: anchor === "center" || anchor === "bottom" ? anchor : "top",
|
|
2210
2431
|
wrap: effectiveBody.wrap !== "none",
|
|
2211
|
-
innerX,
|
|
2212
|
-
innerY,
|
|
2213
|
-
innerW,
|
|
2214
|
-
innerH,
|
|
2432
|
+
innerX: vInnerX,
|
|
2433
|
+
innerY: vInnerY,
|
|
2434
|
+
innerW: vInnerW,
|
|
2435
|
+
innerH: vInnerH,
|
|
2215
2436
|
measure: ctx.measure,
|
|
2216
2437
|
vert: svgVert,
|
|
2217
2438
|
columns: svgColumns
|
|
2439
|
+
};
|
|
2440
|
+
const svgScale = authoredAutofit ? autoFitScale : 1;
|
|
2441
|
+
const svgInner = buildAndLayoutSvgText({
|
|
2442
|
+
...svgArgsBase,
|
|
2443
|
+
autoFitScale: svgScale
|
|
2218
2444
|
});
|
|
2219
2445
|
const bodyRotDegSvg = getShapeTextBodyRotationDeg(shape);
|
|
2220
2446
|
if (bodyRotDegSvg !== null && bodyRotDegSvg !== 0) {
|
|
2221
|
-
const pivotX =
|
|
2222
|
-
const pivotY =
|
|
2447
|
+
const pivotX = vInnerX + vInnerW / 2;
|
|
2448
|
+
const pivotY = vInnerY + vInnerH / 2;
|
|
2223
2449
|
return `<g transform="rotate(${bodyRotDegSvg} ${E(pivotX)} ${E(pivotY)})">${svgInner}</g>`;
|
|
2224
2450
|
}
|
|
2225
2451
|
return svgInner;
|
|
@@ -2272,15 +2498,16 @@ const accentSequence = (theme) => {
|
|
|
2272
2498
|
].map((c) => normalizeHex(c)).filter((c) => /^#[0-9A-Fa-f]{6}$/.test(c));
|
|
2273
2499
|
return hexes.length > 0 ? hexes : fallbacks;
|
|
2274
2500
|
};
|
|
2275
|
-
const
|
|
2501
|
+
const DEFAULT_CHART_TITLE_PT = 13;
|
|
2502
|
+
const layoutChart = (xEmu, yEmu, wEmu, hEmu, hasTitle, hasAxes, titleOverlay = false, legendOverlay = false, hasLegend = true, titlePx = DEFAULT_CHART_TITLE_PT * PX_PER_PT) => {
|
|
2276
2503
|
const x = xEmu / EMU_PER_PX;
|
|
2277
2504
|
const y = yEmu / EMU_PER_PX;
|
|
2278
2505
|
const w = wEmu / EMU_PER_PX;
|
|
2279
2506
|
const h = hEmu / EMU_PER_PX;
|
|
2280
|
-
const titleStrip = hasTitle && !titleOverlay ?
|
|
2507
|
+
const titleStrip = hasTitle && !titleOverlay ? Math.round(titlePx * 2.4) : 0;
|
|
2281
2508
|
const legendStrip = hasLegend && !legendOverlay ? 18 : 0;
|
|
2282
2509
|
const padding = 8;
|
|
2283
|
-
const yAxisGutter = hasAxes ?
|
|
2510
|
+
const yAxisGutter = hasAxes ? 32 : 0;
|
|
2284
2511
|
const xAxisGutter = hasAxes ? 18 : 0;
|
|
2285
2512
|
return {
|
|
2286
2513
|
x,
|
|
@@ -2291,7 +2518,7 @@ const layoutChart = (xEmu, yEmu, wEmu, hEmu, hasTitle, hasAxes, titleOverlay = f
|
|
|
2291
2518
|
plotY: y + titleStrip + padding,
|
|
2292
2519
|
plotW: Math.max(0, w - 2 * padding - yAxisGutter),
|
|
2293
2520
|
plotH: Math.max(0, h - titleStrip - legendStrip - xAxisGutter - 2 * padding),
|
|
2294
|
-
titleY: y + (titleOverlay ? 14 :
|
|
2521
|
+
titleY: y + (titleOverlay ? 14 : padding + titlePx * .7),
|
|
2295
2522
|
legendY: y + h - (legendOverlay ? 18 : legendStrip / 2)
|
|
2296
2523
|
};
|
|
2297
2524
|
};
|
|
@@ -2379,12 +2606,13 @@ const DISPLAY_UNIT_LABEL = {
|
|
|
2379
2606
|
const DEFAULT_AXIS_COLOR = "#000000";
|
|
2380
2607
|
const DEFAULT_GRID_COLOR = "#D9D9D9";
|
|
2381
2608
|
const AXIS_TICK_LEN = 5;
|
|
2609
|
+
const chartFontPx = (sizePt) => (sizePt * PX_PER_PT).toFixed(1);
|
|
2382
2610
|
const axisTickAttrs = (style) => {
|
|
2383
2611
|
const sz = style?.sizePt ?? 10;
|
|
2384
2612
|
const fill = style?.color ?? "#6B7280";
|
|
2385
2613
|
const weight = style?.bold ? " font-weight=\"600\"" : "";
|
|
2386
2614
|
const italic = style?.italic ? " font-style=\"italic\"" : "";
|
|
2387
|
-
return `font-family="sans-serif" font-size="${sz
|
|
2615
|
+
return `font-family="sans-serif" font-size="${chartFontPx(sz)}" fill="${fill}"${weight}${italic}`;
|
|
2388
2616
|
};
|
|
2389
2617
|
const renderValueAxis = (f, axis) => {
|
|
2390
2618
|
const ticks = axis.majorUnit ? (() => {
|
|
@@ -2537,11 +2765,11 @@ const seriesMinMax = (spec) => {
|
|
|
2537
2765
|
};
|
|
2538
2766
|
const renderChartTitle = (f, title, style) => {
|
|
2539
2767
|
if (!title) return "";
|
|
2540
|
-
const sz = style?.sizePt ??
|
|
2768
|
+
const sz = style?.sizePt ?? DEFAULT_CHART_TITLE_PT;
|
|
2541
2769
|
const fill = style?.color ?? "#1F2937";
|
|
2542
2770
|
const weight = style?.bold === false ? "400" : "600";
|
|
2543
2771
|
const fontStyleAttr = style?.italic ? " font-style=\"italic\"" : "";
|
|
2544
|
-
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
|
|
2772
|
+
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="${chartFontPx(sz)}" fill="${fill}" font-weight="${weight}"${fontStyleAttr}>${escapeXml(title)}</text>`;
|
|
2545
2773
|
};
|
|
2546
2774
|
const renderChartLegend = (f, names, colors, position = "b", textStyle, markerSymbols) => {
|
|
2547
2775
|
if (names.length === 0) return "";
|
|
@@ -2549,7 +2777,7 @@ const renderChartLegend = (f, names, colors, position = "b", textStyle, markerSy
|
|
|
2549
2777
|
const fill = textStyle?.color ?? "#374151";
|
|
2550
2778
|
const weight = textStyle?.bold ? " font-weight=\"600\"" : "";
|
|
2551
2779
|
const italic = textStyle?.italic ? " font-style=\"italic\"" : "";
|
|
2552
|
-
const textAttrs = `font-family="sans-serif" font-size="${sz
|
|
2780
|
+
const textAttrs = `font-family="sans-serif" font-size="${chartFontPx(sz)}" fill="${fill}"${weight}${italic}`;
|
|
2553
2781
|
const swatch = (i, swatchX, swatchY) => {
|
|
2554
2782
|
const color = colors[i % colors.length];
|
|
2555
2783
|
const sym = markerSymbols?.[i];
|
|
@@ -2836,7 +3064,7 @@ const dataLabelTextAttrs = (spec, seriesIdx, fallbackFill, fallbackSizePt = 9, f
|
|
|
2836
3064
|
const fill = style?.color ?? fallbackFill;
|
|
2837
3065
|
const weight = style?.bold ?? fallbackBold ? " font-weight=\"600\"" : "";
|
|
2838
3066
|
const italic = style?.italic ? " font-style=\"italic\"" : "";
|
|
2839
|
-
return `font-family="sans-serif" font-size="${sz
|
|
3067
|
+
return `font-family="sans-serif" font-size="${chartFontPx(sz)}" fill="${fill}"${weight}${italic}`;
|
|
2840
3068
|
};
|
|
2841
3069
|
const renderBarChart = (f, spec, colors) => {
|
|
2842
3070
|
const N = pointCount(spec);
|
|
@@ -2930,11 +3158,13 @@ const renderLineChart = (f, spec, colors, fill) => {
|
|
|
2930
3158
|
const isPercent = grouping === "percentStacked";
|
|
2931
3159
|
const { min, max } = seriesMinMax(spec);
|
|
2932
3160
|
const range = max - min || 1;
|
|
2933
|
-
const
|
|
3161
|
+
const band = N > 1 ? fill ? f.plotW / (N - 1) : f.plotW / N : 0;
|
|
3162
|
+
const xAt = (c) => fill ? f.plotX + c * band : f.plotX + (c + .5) * band;
|
|
2934
3163
|
const baseY = f.plotY + f.plotH - (0 - min) / range * f.plotH;
|
|
2935
3164
|
const out = [];
|
|
2936
3165
|
out.push(`<line x1="${px(f.plotX)}" y1="${px(baseY)}" x2="${px(f.plotX + f.plotW)}" y2="${px(baseY)}" stroke="#E5E7EB" stroke-width="0.5"/>`);
|
|
2937
3166
|
const accumulated = Array.from({ length: N }, () => 0);
|
|
3167
|
+
const perSeries = [];
|
|
2938
3168
|
for (let s = 0; s < spec.series.length; s++) {
|
|
2939
3169
|
const series = spec.series[s];
|
|
2940
3170
|
if (!series) continue;
|
|
@@ -2943,7 +3173,7 @@ const renderLineChart = (f, spec, colors, fill) => {
|
|
|
2943
3173
|
const ptsRaw = [];
|
|
2944
3174
|
const basePtsRaw = [];
|
|
2945
3175
|
for (let c = 0; c < N; c++) {
|
|
2946
|
-
const xp =
|
|
3176
|
+
const xp = xAt(c);
|
|
2947
3177
|
const rawV = series.values[c];
|
|
2948
3178
|
const isNullish = rawV === null || rawV === void 0 || !Number.isFinite(rawV);
|
|
2949
3179
|
let v;
|
|
@@ -2982,10 +3212,22 @@ const renderLineChart = (f, spec, colors, fill) => {
|
|
|
2982
3212
|
}
|
|
2983
3213
|
return path.trim();
|
|
2984
3214
|
})();
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
3215
|
+
perSeries.push({
|
|
3216
|
+
s,
|
|
3217
|
+
series,
|
|
3218
|
+
color,
|
|
3219
|
+
ptsRaw,
|
|
3220
|
+
pts,
|
|
3221
|
+
basePts,
|
|
3222
|
+
dPath
|
|
3223
|
+
});
|
|
3224
|
+
}
|
|
3225
|
+
const paintOrder = fill && !isStacked ? perSeries.slice().reverse() : perSeries;
|
|
3226
|
+
if (fill) for (const { color, basePts, dPath } of paintOrder) {
|
|
3227
|
+
const back = basePts.slice().reverse().map(([xp, yp]) => `L${px(xp)},${px(yp)}`).join(" ");
|
|
3228
|
+
out.push(`<path d="${dPath} ${back} Z" fill="${color}" stroke="none"/>`);
|
|
3229
|
+
}
|
|
3230
|
+
for (const { s, series, color, ptsRaw, pts, dPath } of paintOrder) {
|
|
2989
3231
|
const lineWPx = series.lineWidthEmu ? Math.max(.3, series.lineWidthEmu / EMU_PER_PX) : 1.5;
|
|
2990
3232
|
const dashAttr = series.lineDash ? (() => {
|
|
2991
3233
|
const sw = lineWPx;
|
|
@@ -3058,7 +3300,7 @@ const renderLineChart = (f, spec, colors, fill) => {
|
|
|
3058
3300
|
}
|
|
3059
3301
|
}
|
|
3060
3302
|
if (!isStacked && (spec.dropLines || spec.hiLowLines) && spec.series.length > 0) for (let c = 0; c < N; c++) {
|
|
3061
|
-
const xp =
|
|
3303
|
+
const xp = xAt(c);
|
|
3062
3304
|
if (spec.dropLines) {
|
|
3063
3305
|
const firstVal = spec.series[0]?.values[c];
|
|
3064
3306
|
if (firstVal !== null && firstVal !== void 0 && Number.isFinite(firstVal)) {
|
|
@@ -3136,12 +3378,12 @@ const renderPieChart = (f, spec, colors, doughnut) => {
|
|
|
3136
3378
|
const labelX = sx + labelR * Math.cos(labelMid);
|
|
3137
3379
|
const labelY = sy + labelR * Math.sin(labelMid);
|
|
3138
3380
|
const labels = [];
|
|
3139
|
-
if (spec.dataLabels?.showValue) labels.push(formatDataLabelValue(spec, 0, v));
|
|
3140
|
-
if (spec.dataLabels?.showPercent) labels.push(`${(v / total * 100).toFixed(0)}%`);
|
|
3141
3381
|
if (spec.dataLabels?.showCategory) {
|
|
3142
3382
|
const catLabel = spec.categories[i];
|
|
3143
3383
|
if (catLabel) labels.push(catLabel);
|
|
3144
3384
|
}
|
|
3385
|
+
if (spec.dataLabels?.showValue) labels.push(formatDataLabelValue(spec, 0, v));
|
|
3386
|
+
if (spec.dataLabels?.showPercent) labels.push(`${(v / total * 100).toFixed(0)}%`);
|
|
3145
3387
|
if (labels.length > 0) out.push(`<text x="${px(labelX)}" y="${px(labelY)}" text-anchor="middle" dominant-baseline="middle" ${dataLabelTextAttrs(spec, 0, labelFill, 10, true)}>${escapeXml(labels.join(spec.series[0]?.dataLabels?.separator ?? spec.dataLabels?.separator ?? " "))}</text>`);
|
|
3146
3388
|
}
|
|
3147
3389
|
return out.join("");
|
|
@@ -3363,7 +3605,7 @@ const renderChart = (shape, x, y, w, h, transform, theme) => {
|
|
|
3363
3605
|
const isCartesian = spec.kind === "column" || spec.kind === "bar" || spec.kind === "line" || spec.kind === "area";
|
|
3364
3606
|
const hasAxes = isCartesian || spec.kind === "scatter" || spec.kind === "bubble";
|
|
3365
3607
|
const hasLegend = spec.legend !== void 0 && spec.legend.position !== null;
|
|
3366
|
-
const f = layoutChart(x, y, w, h, !!spec.title, hasAxes, spec.titleOverlay ?? false, spec.legend?.overlay ?? false, hasLegend);
|
|
3608
|
+
const f = layoutChart(x, y, w, h, !!spec.title, hasAxes, spec.titleOverlay ?? false, spec.legend?.overlay ?? false, hasLegend, (spec.titleStyle?.sizePt ?? DEFAULT_CHART_TITLE_PT) * PX_PER_PT);
|
|
3367
3609
|
const allNamesForLegend = spec.kind === "pie" || spec.kind === "doughnut" ? Array.from(spec.categories) : spec.series.map((s) => s.name);
|
|
3368
3610
|
const allColorsForLegend = spec.kind === "pie" || spec.kind === "doughnut" ? spec.categories.map((_, i) => spec.series[0]?.pointColors?.[i] ?? spec.series[0]?.color ?? colors[i % colors.length] ?? "#888") : spec.series.map((s, i) => s.color ?? colors[i % colors.length] ?? "#888");
|
|
3369
3611
|
const hiddenSet = new Set(spec.legend?.hiddenIndices ?? []);
|
|
@@ -3443,13 +3685,13 @@ const renderChart = (shape, x, y, w, h, transform, theme) => {
|
|
|
3443
3685
|
const fill = style?.color ?? "#374151";
|
|
3444
3686
|
const weight = style?.bold === false ? "400" : "600";
|
|
3445
3687
|
const italicAttr = style?.italic ? " font-style=\"italic\"" : "";
|
|
3446
|
-
return `font-family="sans-serif" font-size="${sz
|
|
3688
|
+
return `font-family="sans-serif" font-size="${chartFontPx(sz)}" fill="${fill}" font-weight="${weight}"${italicAttr}`;
|
|
3447
3689
|
};
|
|
3448
3690
|
const valueAxisTitleRot = spec.valueAxisTitleRotationDeg ?? -90;
|
|
3449
3691
|
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)})">${escapeXml(spec.valueAxisTitle)}</text>` : "";
|
|
3450
3692
|
const catTitleRot = spec.categoryAxisTitleRotationDeg ?? 0;
|
|
3451
3693
|
const catTitleCx = f.plotX + f.plotW / 2;
|
|
3452
|
-
const catTitleCy = f.plotY + f.plotH +
|
|
3694
|
+
const catTitleCy = f.plotY + f.plotH + 28;
|
|
3453
3695
|
const catTitleTransform = catTitleRot !== 0 ? ` transform="rotate(${catTitleRot} ${px(catTitleCx)} ${px(catTitleCy)})"` : "";
|
|
3454
3696
|
const categoryAxisTitleSvg = spec.categoryAxisTitle ? `<text x="${px(catTitleCx)}" y="${px(catTitleCy)}" text-anchor="middle" ${axisTitleAttrs(spec.categoryAxisTitleStyle)}${catTitleTransform}>${escapeXml(spec.categoryAxisTitle)}</text>` : "";
|
|
3455
3697
|
return [
|
|
@@ -3584,7 +3826,8 @@ const renderTable = (shape, pres, x, y, w, h, transform, theme, ctx) => {
|
|
|
3584
3826
|
const flags = getTableStyleFlags(shape);
|
|
3585
3827
|
const accent = theme ? normalizeHex(theme.accent1) : "#4472C4";
|
|
3586
3828
|
const headerFill = accent;
|
|
3587
|
-
const bandFill = mixHex(accent, "#FFFFFF", .
|
|
3829
|
+
const bandFill = mixHex(accent, "#FFFFFF", .12);
|
|
3830
|
+
const bandFill2 = mixHex(accent, "#FFFFFF", .27);
|
|
3588
3831
|
const textColor = activeDeckTextColor;
|
|
3589
3832
|
const tableThemeFace = getPresentationFonts(pres)?.minorLatin ?? null;
|
|
3590
3833
|
const out = [];
|
|
@@ -3610,8 +3853,8 @@ const renderTable = (shape, pres, x, y, w, h, transform, theme, ctx) => {
|
|
|
3610
3853
|
else if (flags.lastRow && r === dims.rows - 1) resolvedFill = headerFill;
|
|
3611
3854
|
else if (flags.firstCol && c === 0) resolvedFill = bandFill;
|
|
3612
3855
|
else if (flags.lastCol && c === dims.cols - 1) resolvedFill = bandFill;
|
|
3613
|
-
else if (flags.bandRow
|
|
3614
|
-
else if (flags.bandCol
|
|
3856
|
+
else if (flags.bandRow) resolvedFill = (r - (flags.firstRow ? 1 : 0)) % 2 === 0 ? bandFill : bandFill2;
|
|
3857
|
+
else if (flags.bandCol) resolvedFill = (c - (flags.firstCol ? 1 : 0)) % 2 === 0 ? bandFill : bandFill2;
|
|
3615
3858
|
else resolvedFill = "none";
|
|
3616
3859
|
const cellTextColor = resolvedFill === headerFill ? "#FFFFFF" : textColor;
|
|
3617
3860
|
out.push(`<rect x="${px(cx)}" y="${px(cy)}" width="${px(cw)}" height="${px(ch)}" fill="${resolvedFill}"/>`);
|
|
@@ -3794,7 +4037,8 @@ const renderShape = (shape, pres, theme, ctx) => {
|
|
|
3794
4037
|
if (flip.horizontal) transforms.push(`translate(${E(2 * cx)} 0) scale(-1 1)`);
|
|
3795
4038
|
if (flip.vertical) transforms.push(`translate(0 ${E(2 * cy)}) scale(1 -1)`);
|
|
3796
4039
|
const transform = transforms.length > 0 ? ` transform="${transforms.join(" ")}"` : "";
|
|
3797
|
-
const
|
|
4040
|
+
const textRotation = (flip.vertical ? rotation + 180 : rotation) % 360;
|
|
4041
|
+
const textTransform = textRotation !== 0 ? ` transform="rotate(${textRotation} ${E(cx)} ${E(cy)})"` : "";
|
|
3798
4042
|
const textOverlay = kind === "shape" || kind === "graphicFrame" ? renderTextBody(pres, shape, {
|
|
3799
4043
|
x,
|
|
3800
4044
|
y,
|
|
@@ -3818,11 +4062,14 @@ const renderShape = (shape, pres, theme, ctx) => {
|
|
|
3818
4062
|
y1 = y + h;
|
|
3819
4063
|
y2 = y;
|
|
3820
4064
|
}
|
|
4065
|
+
const connectorTransform = rotation !== 0 ? ` transform="rotate(${rotation} ${E(cx)} ${E(cy)})"` : "";
|
|
3821
4066
|
const strokeColor = p.stroke === "none" ? resolveColor("scheme:tx1", theme, "#1F2937") : p.stroke;
|
|
3822
4067
|
const sa = p.strokeAttrs ? ` ${p.strokeAttrs}` : "";
|
|
3823
4068
|
const ma = p.markerAttrs ?? "";
|
|
4069
|
+
const capDefault = sa.includes("stroke-linecap") ? "" : " stroke-linecap=\"round\"";
|
|
4070
|
+
const joinDefault = sa.includes("stroke-linejoin") ? "" : " stroke-linejoin=\"round\"";
|
|
3824
4071
|
const preset = getShapePreset(shape) ?? "line";
|
|
3825
|
-
if (preset === "straightConnector1" || preset === "line") return `${p.defs}<line x1="${E(x1)}" y1="${E(y1)}" x2="${E(x2)}" y2="${E(y2)}" stroke="${strokeColor}" stroke-width="${E(sw)}"
|
|
4072
|
+
if (preset === "straightConnector1" || preset === "line") return `${p.defs}<line x1="${E(x1)}" y1="${E(y1)}" x2="${E(x2)}" y2="${E(y2)}" stroke="${strokeColor}" stroke-width="${E(sw)}"${capDefault}${sa}${ma}${connectorTransform}/>`;
|
|
3826
4073
|
const px1 = x1 / EMU_PER_PX;
|
|
3827
4074
|
const py1 = y1 / EMU_PER_PX;
|
|
3828
4075
|
const px2 = x2 / EMU_PER_PX;
|
|
@@ -3847,7 +4094,7 @@ const renderShape = (shape, pres, theme, ctx) => {
|
|
|
3847
4094
|
const c2x = px1 + 2 * (px2 - px1) / 3;
|
|
3848
4095
|
d += ` C${c1x.toFixed(2)} ${py1.toFixed(2)} ${c2x.toFixed(2)} ${py2.toFixed(2)} ${px2.toFixed(2)} ${py2.toFixed(2)}`;
|
|
3849
4096
|
} else d += ` L${px2.toFixed(2)} ${py2.toFixed(2)}`;
|
|
3850
|
-
return `${p.defs}<path d="${d}" fill="none" stroke="${strokeColor}" stroke-width="${E(sw)}"
|
|
4097
|
+
return `${p.defs}<path d="${d}" fill="none" stroke="${strokeColor}" stroke-width="${E(sw)}"${capDefault}${joinDefault}${sa}${ma}${connectorTransform}/>`;
|
|
3851
4098
|
}
|
|
3852
4099
|
if (kind === "group") {
|
|
3853
4100
|
const xform = getGroupTransform(shape);
|
|
@@ -3923,7 +4170,7 @@ const renderShape = (shape, pres, theme, ctx) => {
|
|
|
3923
4170
|
if (pathFn) geomSvg = `<path d="${pathFn(x / EMU_PER_PX, y / EMU_PER_PX, w / EMU_PER_PX, h / EMU_PER_PX)}" fill="${p.fill}" stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}" fill-rule="evenodd"${sa}${ma}/>`;
|
|
3924
4171
|
else {
|
|
3925
4172
|
const pointsFn = PRESET_POINTS[preset];
|
|
3926
|
-
if (pointsFn) geomSvg = `<polygon points="${pointsFn().map(([nx, ny]) => `${E(x + nx * w)},${E(y + ny * h)}`).join(" ")}" fill="${p.fill}" stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma}/>`;
|
|
4173
|
+
if (pointsFn) geomSvg = `<polygon points="${pointsFn(w / EMU_PER_PX, h / EMU_PER_PX).map(([nx, ny]) => `${E(x + nx * w)},${E(y + ny * h)}`).join(" ")}" fill="${p.fill}" stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma}/>`;
|
|
3927
4174
|
else 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="${escapeXml(preset)}"><title>${escapeXml(`preset: ${preset}`)}</title></rect>`;
|
|
3928
4175
|
}
|
|
3929
4176
|
}
|
|
@@ -4040,11 +4287,13 @@ const buildEffectsFilter = (pres, shape) => {
|
|
|
4040
4287
|
primitives.push(`<feGaussianBlur in="SourceAlpha" stdDeviation="${blurPx.toFixed(2)}" result="innerBlur${i}"/>`, `<feOffset in="innerBlur${i}" dx="${dx.toFixed(2)}" dy="${dy.toFixed(2)}" result="innerOff${i}"/>`, `<feComposite in="innerOff${i}" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="innerMask${i}"/>`, `<feFlood flood-color="${color}" flood-opacity="${opacity.toFixed(3)}" result="innerCol${i}"/>`, `<feComposite in="innerCol${i}" in2="innerMask${i}" operator="in" result="innerOut${i}"/>`);
|
|
4041
4288
|
layers.push(`innerOut${i}`);
|
|
4042
4289
|
} else if (e.kind === "glow") {
|
|
4043
|
-
const
|
|
4290
|
+
const radiusPx = e.radiusEmu / EMU_PER_PX;
|
|
4291
|
+
const dilatePx = radiusPx * .5;
|
|
4292
|
+
const featherPx = radiusPx * .3;
|
|
4044
4293
|
const color = e.color || "#FFFFFF";
|
|
4045
4294
|
const opacity = e.opacity ?? 1;
|
|
4046
4295
|
const i = primitives.length;
|
|
4047
|
-
primitives.push(`<feMorphology in="SourceAlpha" operator="dilate" radius="${
|
|
4296
|
+
primitives.push(`<feMorphology in="SourceAlpha" operator="dilate" radius="${dilatePx.toFixed(2)}" result="glowExp${i}"/>`, `<feGaussianBlur in="glowExp${i}" stdDeviation="${featherPx.toFixed(2)}" result="glowBlur${i}"/>`, `<feFlood flood-color="${color}" flood-opacity="${opacity.toFixed(3)}" result="glowCol${i}"/>`, `<feComposite in="glowCol${i}" in2="glowBlur${i}" operator="in" result="glowOut${i}"/>`);
|
|
4048
4297
|
layers.push(`glowOut${i}`);
|
|
4049
4298
|
} else if (e.kind === "softEdge") {
|
|
4050
4299
|
const blurPx = e.radiusEmu / EMU_PER_PX / 2;
|
|
@@ -4174,4 +4423,4 @@ const detectImageFormatLocal = (bytes) => {
|
|
|
4174
4423
|
//#endregion
|
|
4175
4424
|
export { SERIF as a, substituteFamily as c, SANS as i, ARIAL as n, TIMES as o, MONO as r, defaultMeasurer as s, renderSlideSvg as t };
|
|
4176
4425
|
|
|
4177
|
-
//# sourceMappingURL=src-
|
|
4426
|
+
//# sourceMappingURL=src-Q2z_XgT6.js.map
|