@office-kit/pptx-preview 0.13.0 → 0.15.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.
@@ -1,50 +1,22 @@
1
- import { getEffectiveColorMap, getGroupChildren, getGroupTransform, getParagraphAlignment, getParagraphBullet, getParagraphBulletImageBytes, getParagraphBulletStyle, getParagraphIndent, getParagraphLevel, getParagraphPropertiesEffective, getParagraphSpacing, getPresentationFirstSlideNumber, getPresentationFonts, getPresentationTheme, getShapeAdjustValues, getShapeAltTitle, getShapeBodyPrEffective, getShapeBounds, getShapeBoundsResolved, getShapeChartSpec, getShapeClickAction, getShapeCustomGeometry, getShapeDescription, getShapeEffectsEffective, getShapeFillColorResolved, getShapeFillEffective, getShapeFillOpacity, getShapeFlip, getShapeGradientFill, getShapeGradientFillEffective, getShapeHyperlinkTooltip, getShapeId, getShapeImageBiLevelThreshold, getShapeImageBrightness, getShapeImageBytes, getShapeImageContrast, getShapeImageCrop, getShapeImageDuotone, getShapeImageFillBytes, getShapeImageFillLayout, getShapeImageFormat, getShapeImageIntrinsicSize, getShapeImageLinkUrl, getShapeImageOpacity, getShapeImagePartName, getShapeKind, getShapeName, getShapeParagraphCount, getShapeParagraphElements, getShapePatternFill, getShapePlaceholderType, getShapePreset, getShapeRotation, getShapeRunClickAction, getShapeRunFormat, getShapeRunFormatEffective, getShapeRunHyperlink, getShapeRunHyperlinkTooltip, getShapeStrokeArrow, getShapeStrokeCap, getShapeStrokeColorResolved, getShapeStrokeCompound, getShapeStrokeDash, getShapeStrokeEffective, getShapeStrokeJoin, getShapeStrokeOpacity, getShapeTextAnchor, getShapeTextAutoFit, getShapeTextAutoFitParams, getShapeTextBodyRotationDeg, getShapeTextColumns, getShapeTextDirection, getShapeTextMargins, getShapeXmlString, getSlideBackground, getSlideBackgroundGradientFill, getSlideBackgroundImageBytes, getSlideBackgroundImageCrop, getSlideBackgroundImageFillLayout, getSlideBackgroundImageIntrinsicSize, getSlideBackgroundImageOpacity, getSlideBackgroundPatternFill, getSlideIndex, getSlideLayout, getSlideLayoutBackground, getSlideLayoutBackgroundGradientFill, getSlideLayoutBackgroundImageBytes, getSlideLayoutBackgroundPatternFill, getSlideLayoutShapes, getSlideMasterBackground, getSlideMasterBackgroundGradientFill, getSlideMasterBackgroundImageBytes, getSlideMasterBackgroundPatternFill, getSlideMasterShapes, getSlideShapes, getSlideSize, getSlides, getTableCellAnchor, getTableCellAppearanceEffective, getTableCellMargins, getTableCellParagraphs, getTableCellRunFormatEffective, getTableCellSpan, getTableCellTextDirection, getTableCells, getTableColumnWidths, getTableDimensions, getTableRowHeights, isChartShape, isParagraphBulletPicture, isShapeHidden, isShapeImageGrayscale, isShapePlaceholder, isShapeTextBox, isSlideBackgroundGraphicsHidden, isSlideLayoutBackgroundGraphicsHidden, isTableShape, resolveDeckBodyTextColor } from "@office-kit/pptx";
1
+ import { emu, getEffectiveColorMap, getGroupChildren, getGroupTransform, getParagraphAlignment, getParagraphBullet, getParagraphBulletImageBytes, getParagraphBulletStyle, getParagraphElementLanguages, getParagraphIndent, getParagraphLevel, getParagraphPropertiesEffective, getParagraphSpacing, getPresentationFirstSlideNumber, getPresentationFonts, getPresentationTheme, getPresetGeometry, getShape3D, getShapeAdjustValues, getShapeAltTitle, getShapeBodyPrEffective, getShapeBounds, getShapeBoundsResolved, getShapeChartSpec, getShapeClickAction, getShapeCustomGeometry, getShapeDescription, getShapeEffectsEffective, getShapeFillColorResolved, getShapeFillEffective, getShapeFillOpacity, getShapeFlip, getShapeGradientFill, getShapeGradientFillEffective, getShapeHyperlinkTooltip, getShapeId, getShapeImageBiLevelThreshold, getShapeImageBrightness, getShapeImageBytes, getShapeImageContrast, getShapeImageCrop, getShapeImageDuotone, getShapeImageFillBytes, getShapeImageFillLayout, getShapeImageFormat, getShapeImageIntrinsicSize, getShapeImageLinkUrl, getShapeImageOpacity, getShapeImagePartName, getShapeKind, getShapeName, getShapeParagraphCount, getShapeParagraphElements, getShapePatternFill, getShapePlaceholderType, getShapePreset, getShapeRotation, getShapeRunClickAction, getShapeRunFormat, getShapeRunFormatEffective, getShapeRunHyperlink, getShapeRunHyperlinkTooltip, getShapeStrokeArrow, getShapeStrokeCap, getShapeStrokeColorResolved, getShapeStrokeCompound, getShapeStrokeDash, getShapeStrokeEffective, getShapeStrokeGradient, getShapeStrokeJoin, getShapeStrokeOpacity, getShapeStrokeSketch, getShapeText3D, getShapeTextAnchor, getShapeTextAutoFit, getShapeTextAutoFitParams, getShapeTextBodyRotationDeg, getShapeTextColumns, getShapeTextDirection, getShapeTextMargins, getShapeXmlString, getSlideBackground, getSlideBackgroundGradientFill, getSlideBackgroundImageBytes, getSlideBackgroundImageCrop, getSlideBackgroundImageFillLayout, getSlideBackgroundImageIntrinsicSize, getSlideBackgroundImageOpacity, getSlideBackgroundPatternFill, getSlideIndex, getSlideLayout, getSlideLayoutBackground, getSlideLayoutBackgroundGradientFill, getSlideLayoutBackgroundImageBytes, getSlideLayoutBackgroundPatternFill, getSlideLayoutShapes, getSlideMasterBackground, getSlideMasterBackgroundGradientFill, getSlideMasterBackgroundImageBytes, getSlideMasterBackgroundPatternFill, getSlideMasterShapes, getSlideShapes, getSlideSize, getSlides, getTableBackgroundEffective, getTableCellAnchor, getTableCellAppearanceEffective, getTableCellMargins, getTableCellParagraphs, getTableCellRunFormatEffective, getTableCellSpan, getTableCellText3D, getTableCellTextDirection, getTableCells, getTableColumnWidths, getTableDimensions, getTableRowHeights, isChartShape, isParagraphBulletPicture, isShapeHidden, isShapeImageGrayscale, isShapePlaceholder, isShapeTextBox, isSlideBackgroundGraphicsHidden, isSlideLayoutBackgroundGraphicsHidden, isTableShape, resolveDeckBodyTextColor } from "@office-kit/pptx";
2
2
  //#region src/text-body-rect.ts
3
- const presetTextRect = (preset) => {
4
- switch (preset) {
5
- case "triangle": return {
6
- l: .25,
7
- t: .5,
8
- r: .75,
9
- b: 1
10
- };
11
- case "diamond": return {
12
- l: .25,
13
- t: .25,
14
- r: .75,
15
- b: .75
16
- };
17
- case "pentagon": return {
18
- l: .191,
19
- t: .236,
20
- r: .809,
21
- b: 1
22
- };
23
- case "star5": return {
24
- l: .309,
25
- t: .382,
26
- r: .691,
27
- b: .764
28
- };
29
- case "leftRightArrow": return {
30
- l: .18,
31
- t: .35,
32
- r: .82,
33
- b: .65
34
- };
35
- default: return null;
36
- }
37
- };
38
3
  /**
39
- * The text rectangle a custom-geometry shape states for itself, as fractions
40
- * of its own extent, or `null` for a preset shape and for a custom one that
41
- * states none. `<a:rect>` is written in EMU against the shape's extents, so
42
- * dividing by them here is what lets the result travel with a shape that a
43
- * group has scaled.
4
+ * Where a shape lays out its text, as fractions of its own extent: the
5
+ * `<a:rect>` its geometry states, written in its `<a:custGeom>` or in
6
+ * ECMA-376's definition of its preset (an ellipse's inscribed rectangle, a
7
+ * triangle's lower middle, an arrow's shaft). `null` lays text into the whole
8
+ * box: a shape that inherits its geometry, or whose rectangle has no room.
9
+ *
10
+ * The rectangle is in the shape's own `<a:ext>` space, so dividing by that —
11
+ * not by on-screen bounds, which carry a group's or autofit's scale — is what
12
+ * lets the result travel with the shape.
44
13
  */
45
- function shapeCustomTextRect(custom, extent) {
46
- const rect = custom?.textRect;
47
- if (!rect || extent === null || extent.w <= 0 || extent.h <= 0) return null;
14
+ function shapeTextRect(shape) {
15
+ const extent = getShapeBounds(shape);
16
+ if (extent === null || extent.w <= 0 || extent.h <= 0) return null;
17
+ const preset = getShapePreset(shape);
18
+ const rect = (preset === null ? getShapeCustomGeometry(shape) : getPresetGeometry(preset, extent, getShapeAdjustValues(shape)))?.textRect;
19
+ if (!rect || rect.r <= rect.l || rect.b <= rect.t) return null;
48
20
  return {
49
21
  l: rect.l / extent.w,
50
22
  t: rect.t / extent.h,
@@ -53,18 +25,13 @@ function shapeCustomTextRect(custom, extent) {
53
25
  };
54
26
  }
55
27
  /**
56
- * Text layout rectangle used by the preview, including preset geometry and body
57
- * insets. All coordinates and margins must use the same unit (normally EMU).
58
- * Preset regions follow the preview's current geometry approximations. When
59
- * margins collapse a preset region, retain that region without margins.
60
- *
61
- * A custom-geometry shape states its own rectangle in `<a:custGeom><a:rect>`.
62
- * Pass it as `custom`, as fractions of the shape's extent so that it scales
63
- * with `bounds` the way a preset region does, and it wins: unlike the table
64
- * above it is not an approximation of anything — it is what the file says.
28
+ * Text layout rectangle used by the preview: `region` (from
29
+ * {@link shapeTextRect}, or `null` for the whole box) placed in `bounds`, less
30
+ * the body insets. All coordinates and margins must use the same unit
31
+ * (normally EMU). When margins collapse a region, the region is kept without
32
+ * them, as the reference desktop app does.
65
33
  */
66
- function resolveTextBodyRect(preset, bounds, margins, custom) {
67
- const region = custom ?? presetTextRect(preset);
34
+ function resolveTextBodyRect(bounds, margins, region) {
68
35
  const x = bounds.x + (region?.l ?? 0) * bounds.w;
69
36
  const y = bounds.y + (region?.t ?? 0) * bounds.h;
70
37
  const w = ((region?.r ?? 1) - (region?.l ?? 0)) * bounds.w;
@@ -86,17 +53,17 @@ function resolveTextBodyRect(preset, bounds, margins, custom) {
86
53
  }
87
54
  //#endregion
88
55
  //#region src/text-body-style.ts
89
- /** Mac PowerPoint uses zero spacing when `spcCol` is absent. */
56
+ /** The reference desktop app on Mac uses zero spacing when `spcCol` is absent. */
90
57
  const DEFAULT_COLUMN_GAP_PX = 0;
91
- const EMU_PER_PX$2 = 9525;
58
+ const EMU_PER_PX$3 = 9525;
92
59
  const NONE = {
93
60
  declarations: "",
94
61
  transform: ""
95
62
  };
96
63
  /**
97
64
  * `<a:bodyPr vert="…"/>` as CSS. `writing-mode` is the right primitive for
98
- * the East-Asian and Mongolian cases, and `text-orientation` for the WordArt
99
- * ones, which stack upright glyphs. `vert270` reads bottom-to-top, which no
65
+ * the East-Asian and Mongolian cases, and `text-orientation` for the
66
+ * `wordArtVert` ones, which stack upright glyphs. `vert270` reads bottom-to-top, which no
100
67
  * writing-mode expresses on its own — hence the half turn. Its columns still
101
68
  * follow the OOXML vertical-rl progression before that half turn; using
102
69
  * vertical-lr here reverses the column order relative to the SVG renderer.
@@ -134,7 +101,7 @@ function verticalTextStyle(vert) {
134
101
  */
135
102
  function textColumnsStyle(cols) {
136
103
  if (!cols || cols.count < 2) return "";
137
- const gapPx = cols.gapEmu !== void 0 ? (cols.gapEmu / EMU_PER_PX$2).toFixed(2) : DEFAULT_COLUMN_GAP_PX;
104
+ const gapPx = cols.gapEmu !== void 0 ? (cols.gapEmu / EMU_PER_PX$3).toFixed(2) : DEFAULT_COLUMN_GAP_PX;
138
105
  return `column-count:${cols.count};column-gap:${gapPx}px`;
139
106
  }
140
107
  //#endregion
@@ -250,6 +217,30 @@ const paragraphNumberLabels = (paraData) => {
250
217
  return numberLabels;
251
218
  };
252
219
  //#endregion
220
+ //#region src/decimal-separator.ts
221
+ const separators = /* @__PURE__ */ new Map();
222
+ /**
223
+ * The character a decimal tab stop aligns on for a run's language
224
+ * (`<a:rPr lang>`): `,` for `de-DE`, `.` for `en-US`. Runs without a
225
+ * language, or with one Intl does not know, align on `.`. The reference desktop app on Mac
226
+ * uses the run language too, not the UI or system locale (native capture
227
+ * 2026-10-07: Set Proofing Language to German moved alignment to `,`).
228
+ */
229
+ function decimalSeparatorOf(lang) {
230
+ if (!lang) return ".";
231
+ let separator = separators.get(lang);
232
+ if (separator === void 0) {
233
+ separator = ".";
234
+ try {
235
+ separator = new Intl.NumberFormat(lang, { numberingSystem: "latn" }).formatToParts(1.5).find((part) => part.type === "decimal")?.value ?? ".";
236
+ } catch (error) {
237
+ if (!(error instanceof RangeError)) throw error;
238
+ }
239
+ separators.set(lang, separator);
240
+ }
241
+ return separator;
242
+ }
243
+ //#endregion
253
244
  //#region src/emf.ts
254
245
  const WORD_BYTES = 4;
255
246
  const SHORT_BYTES = 2;
@@ -526,7 +517,7 @@ const FALLBACK_DESCENT = .22;
526
517
  const FALLBACK_LINEGAP = .08;
527
518
  const BASELINE_LEADING_DROP = .036;
528
519
  const GRID_NUDGE_X = -.75;
529
- const EMU_PER_PX$1 = 9525;
520
+ const EMU_PER_PX$2 = 9525;
530
521
  let reflectionNamespace = 0;
531
522
  const EAST_ASIAN_CHAR = /[ᄀ-ᅟ⺀-〾ぁ-㏿㐀-䶿一-鿿ꀀ-꓏가-힣豈-﫿︰-﹏＀-⦆¢-₩]/;
532
523
  const CLOSING_PUNCT = /^[、。,.・)」』】〉》”’!?:;ー〜…,.!?:;)\]}]+$/;
@@ -711,7 +702,7 @@ const layoutCore = (input, measure) => {
711
702
  fieldWidth = decimalWidth = 0;
712
703
  } else {
713
704
  fieldWidth += token.width;
714
- const decimal = token.text.indexOf(".");
705
+ const decimal = token.text.indexOf(token.piece.decimalSeparator ?? ".");
715
706
  decimalWidth = decimal < 0 ? decimalWidth + token.width : mWidth(token.text.slice(0, decimal), {
716
707
  ...specOf(token.piece),
717
708
  sizePx: renderedSizePxOf(token.piece)
@@ -869,7 +860,7 @@ const lineContentExtent = (line, dx) => {
869
860
  };
870
861
  };
871
862
  const resolveFillPaints = (placements) => {
872
- if (!placements.some(({ line }) => line.tokens.some((t) => t.piece.fillPaint))) return {
863
+ if (!placements.some(({ line }) => line.tokens.some((t) => t.piece.fillPaint || t.piece.outlinePaint))) return {
873
864
  defs: "",
874
865
  placements
875
866
  };
@@ -897,12 +888,7 @@ const resolveFillPaints = (placements) => {
897
888
  };
898
889
  const painted = /* @__PURE__ */ new Map();
899
890
  let defs = "";
900
- const pieces = /* @__PURE__ */ new Map();
901
- const repaint = (piece) => {
902
- const paint = piece.fillPaint;
903
- if (paint === void 0) return piece;
904
- let next = pieces.get(piece);
905
- if (next) return next;
891
+ const resolve = (paint) => {
906
892
  let fill = painted.get(paint);
907
893
  if (fill === void 0) {
908
894
  const built = paint(bounds);
@@ -910,9 +896,16 @@ const resolveFillPaints = (placements) => {
910
896
  fill = built.fill;
911
897
  painted.set(paint, fill);
912
898
  }
899
+ return fill;
900
+ };
901
+ const pieces = /* @__PURE__ */ new Map();
902
+ const repaint = (piece) => {
903
+ let next = pieces.get(piece);
904
+ if (next) return next;
913
905
  next = {
914
906
  ...piece,
915
- fillHex: fill
907
+ ...piece.fillPaint ? { fillHex: resolve(piece.fillPaint) } : {},
908
+ ...piece.outlinePaint ? { outlineHex: resolve(piece.outlinePaint) } : {}
916
909
  };
917
910
  pieces.set(piece, next);
918
911
  return next;
@@ -921,7 +914,7 @@ const resolveFillPaints = (placements) => {
921
914
  ...placement,
922
915
  line: {
923
916
  ...placement.line,
924
- tokens: placement.line.tokens.map((token) => token.piece.fillPaint ? {
917
+ tokens: placement.line.tokens.map((token) => token.piece.fillPaint || token.piece.outlinePaint ? {
925
918
  ...token,
926
919
  piece: repaint(token.piece)
927
920
  } : token)
@@ -1069,8 +1062,9 @@ const emitLine = (line, baselineY, dx, descent, nextEffectId, effectsOnly, effec
1069
1062
  const reflections = effectKind === "reflection" || effectKind === "all" ? emitTextReflections(groups, line.textAnchor, x0, baselineY, descent, nextEffectId) : "";
1070
1063
  const outerEffects = effectKind === "outer" || effectKind === "all" ? emitTextOuterEffects(groups, line.textAnchor, x0, baselineY, nextEffectId) : "";
1071
1064
  const innerShadows = effectKind === "innerShadow" || effectKind === "all" ? emitTextInnerShadows(groups, line.textAnchor, x0, baselineY, nextEffectId) : "";
1072
- if (effectsOnly) return outerEffects + reflections + innerShadows;
1073
- return emitHighlights(groups, line.textAnchor, x0, baselineY) + outerEffects + reflections + text + innerShadows + emitTextDecorations(groups, line.textAnchor, x0, baselineY);
1065
+ const bevels = effectKind === "bevel" || effectKind === "all" ? emitTextBevel(groups, line.textAnchor, x0, baselineY, nextEffectId) : "";
1066
+ if (effectsOnly) return outerEffects + reflections + bevels + innerShadows;
1067
+ return emitHighlights(groups, line.textAnchor, x0, baselineY) + outerEffects + reflections + text + bevels + innerShadows + emitTextDecorations(groups, line.textAnchor, x0, baselineY);
1074
1068
  };
1075
1069
  const unpaintedPiece = (piece) => {
1076
1070
  const { outlineHex: _outline, shadow: _shadow, glow: _glow, ...rest } = piece;
@@ -1118,7 +1112,7 @@ const emitTextReflections = (groups, textAnchor, x0, baselineY, descent, nextEff
1118
1112
  continue;
1119
1113
  }
1120
1114
  const scaleY = reflection.scaleY ?? 1;
1121
- const distPx = (reflection.offsetEmu ?? 0) / EMU_PER_PX$1;
1115
+ const distPx = (reflection.offsetEmu ?? 0) / EMU_PER_PX$2;
1122
1116
  const contactY = baselineY + descent;
1123
1117
  const angleRad = (reflection.angleDeg ?? 0) * Math.PI / 180;
1124
1118
  const offsetX = distPx * Math.cos(angleRad);
@@ -1131,7 +1125,7 @@ const emitTextReflections = (groups, textAnchor, x0, baselineY, descent, nextEff
1131
1125
  const gradientId = `${id}-gradient`;
1132
1126
  const maskId = `${id}-mask`;
1133
1127
  const filterId = `${id}-blur`;
1134
- const blurPx = Math.max(0, (reflection.blurEmu ?? 0) / EMU_PER_PX$1 / 2);
1128
+ const blurPx = Math.max(0, (reflection.blurEmu ?? 0) / EMU_PER_PX$2 / 2);
1135
1129
  const fadeDirection = reflection.fadeDirection;
1136
1130
  const defs = `<defs><linearGradient id="${gradientId}" ${fadeDirection === void 0 || fadeDirection === 90 ? "x1=\"0\" y1=\"1\" x2=\"0\" y2=\"0\"" : (() => {
1137
1131
  const radians = fadeDirection * Math.PI / 180;
@@ -1167,8 +1161,8 @@ const emitTextOuterEffects = (groups, textAnchor, x0, baselineY, nextId) => {
1167
1161
  if (shadow) {
1168
1162
  const id = nextId("outer-shadow");
1169
1163
  const angle = (shadow.angleDeg ?? 45) * Math.PI / 180;
1170
- const distance = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$1;
1171
- const blur = Math.max(0, (shadow.blurEmu ?? 50800) / EMU_PER_PX$1 / 2);
1164
+ const distance = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$2;
1165
+ const blur = Math.max(0, (shadow.blurEmu ?? 50800) / EMU_PER_PX$2 / 2);
1172
1166
  const glyphSize = renderedSizePxOf(group.piece);
1173
1167
  const pad = Math.max(1, distance + blur * 3);
1174
1168
  const color = shadow.color ?? "#000000";
@@ -1178,7 +1172,7 @@ const emitTextOuterEffects = (groups, textAnchor, x0, baselineY, nextId) => {
1178
1172
  }
1179
1173
  if (glow) {
1180
1174
  const id = nextId("glow");
1181
- const radius = Math.max(0, (glow.radiusEmu ?? 63500) / EMU_PER_PX$1);
1175
+ const radius = Math.max(0, (glow.radiusEmu ?? 63500) / EMU_PER_PX$2);
1182
1176
  const glyphSize = renderedSizePxOf(group.piece);
1183
1177
  const pad = Math.max(1, radius * 2);
1184
1178
  const color = glow.color ?? "#FFFF00";
@@ -1202,9 +1196,9 @@ const emitTextInnerShadows = (groups, textAnchor, x0, baselineY, nextId) => {
1202
1196
  }
1203
1197
  const id = nextId("inner-shadow");
1204
1198
  const angle = (shadow.angleDeg ?? 45) * Math.PI / 180;
1205
- const dx = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$1 * Math.cos(angle);
1206
- const dy = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$1 * Math.sin(angle);
1207
- const blurPx = Math.max(0, (shadow.blurEmu ?? 50800) / EMU_PER_PX$1 / 2);
1199
+ const dx = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$2 * Math.cos(angle);
1200
+ const dy = (shadow.offsetEmu ?? 38100) / EMU_PER_PX$2 * Math.sin(angle);
1201
+ const blurPx = Math.max(0, (shadow.blurEmu ?? 50800) / EMU_PER_PX$2 / 2);
1208
1202
  const color = shadow.color ?? "#000000";
1209
1203
  const opacity = Math.max(0, Math.min(1, shadow.opacity ?? 1));
1210
1204
  const defs = `<defs><filter id="${id}" x="-25%" y="-25%" width="150%" height="150%"><feGaussianBlur in="SourceAlpha" stdDeviation="${fmt(blurPx)}" result="innerBlur"/><feOffset in="innerBlur" dx="${fmt(dx)}" dy="${fmt(dy)}" result="innerOff"/><feComposite in="innerOff" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="innerMask"/><feFlood flood-color="${escapeXml$1(color)}" flood-opacity="${opacity.toFixed(3)}" result="innerColor"/><feComposite in="innerColor" in2="innerMask" operator="in" result="innerOut"/><feComposite in="innerOut" in2="SourceAlpha" operator="in"/></filter></defs>`;
@@ -1214,6 +1208,25 @@ const emitTextInnerShadows = (groups, textAnchor, x0, baselineY, nextId) => {
1214
1208
  }
1215
1209
  return parts.join("");
1216
1210
  };
1211
+ const emitTextBevel = (groups, textAnchor, x0, baselineY, nextId) => {
1212
+ const bevel = groups.find((g) => g.piece.bevel && !g.isTab && g.width > 0)?.piece.bevel;
1213
+ if (!bevel) return "";
1214
+ const id = nextId("bevel");
1215
+ const widthPx = bevel.widthEmu / EMU_PER_PX$2;
1216
+ const surfaceScale = bevel.heightEmu / EMU_PER_PX$2 * bevel.relief;
1217
+ const table = [...bevel.profile.slice(1).map(() => 0), ...bevel.profile];
1218
+ const flat = Math.sin(bevel.elevationDeg * Math.PI / 180);
1219
+ const lightGain = bevel.contrast / (1 - flat);
1220
+ const shadeGain = bevel.contrast / flat;
1221
+ const light = `<feDistantLight azimuth="${fmt(bevel.azimuthDeg)}" elevation="${fmt(bevel.elevationDeg)}"/>`;
1222
+ const specular = bevel.specular ? `<feSpecularLighting in="bevelHeight" surfaceScale="${fmt(surfaceScale)}" specularConstant="${fmt(bevel.specular.constant)}" specularExponent="${fmt(bevel.specular.exponent)}" lighting-color="#fff" result="bevelSpecular">${light}</feSpecularLighting>` : "";
1223
+ const defs = `<defs><filter id="${id}" x="-25%" y="-25%" width="150%" height="150%" color-interpolation-filters="sRGB" data-pptx-bevel="1"><feGaussianBlur in="SourceAlpha" stdDeviation="${fmt(widthPx / 2)}" result="bevelBlur"/><feComponentTransfer in="bevelBlur" result="bevelHeight"><feFuncA type="table" tableValues="${table.map(fmt).join(" ")}"/></feComponentTransfer><feDiffuseLighting in="bevelHeight" surfaceScale="${fmt(surfaceScale)}" diffuseConstant="1" lighting-color="#fff" result="bevelDiffuse">${light}</feDiffuseLighting><feColorMatrix in="bevelDiffuse" type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 ${fmt(lightGain)} 0 0 0 ${fmt(-lightGain * flat)}" result="bevelLight"/><feColorMatrix in="bevelDiffuse" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ${fmt(-shadeGain)} 0 0 0 ${fmt(shadeGain * flat)}" result="bevelShade"/>` + specular + `<feMerge result="bevelMerged"><feMergeNode in="bevelShade"/><feMergeNode in="bevelLight"/>${specular ? "<feMergeNode in=\"bevelSpecular\"/>" : ""}</feMerge><feComposite in="bevelMerged" in2="SourceAlpha" operator="in"/></filter></defs>`;
1224
+ const beveled = groups.map((g) => g.piece.bevel && !g.isTab ? g : {
1225
+ ...g,
1226
+ piece: unpaintedPiece(g.piece)
1227
+ });
1228
+ return `${defs}<text x="${fmt(x0)}" y="${fmt(baselineY)}" text-anchor="${textAnchor}" xml:space="preserve" filter="url(#${id})" aria-hidden="true" pointer-events="none" data-pptx-bevel="text">${beveled.map(tspan).join("")}</text>`;
1229
+ };
1217
1230
  const baselineShiftPxOf = (p) => p.sizePx * p.baseline;
1218
1231
  const SUPER_SUB_SIZE_RATIO = .65;
1219
1232
  const SMALL_CAPS_LOWERCASE_RATIO = .8;
@@ -1473,6 +1486,206 @@ function browserTextMeasurer() {
1473
1486
  };
1474
1487
  }
1475
1488
  //#endregion
1489
+ //#region src/text-bevel.ts
1490
+ const DEFAULT_BEVEL_EMU$1 = 76200;
1491
+ const DEFAULT_PRESET = "circle";
1492
+ const DEFAULT_RIG = {
1493
+ type: "threePt",
1494
+ direction: "t"
1495
+ };
1496
+ const PROFILE_SAMPLES = 9;
1497
+ const PROFILES = {
1498
+ circle: (t) => Math.sqrt(1 - (1 - t) ** 2),
1499
+ relaxedInset: (t) => Math.sin(t * Math.PI / 2) ** 2,
1500
+ slope: (t) => t,
1501
+ angle: (t) => t,
1502
+ softRound: (t) => t * t * (3 - 2 * t),
1503
+ convex: (t) => Math.sqrt(Math.sqrt(1 - (1 - t) ** 2)),
1504
+ coolSlant: (t) => Math.min(1, t * 1.6),
1505
+ hardEdge: (t) => Math.min(1, t * 3),
1506
+ cross: (t) => t < .5 ? t * 1.6 : .8 + (t - .5) * .4,
1507
+ divot: (t) => t < .35 ? t / .35 : 1 - .55 * Math.sin((t - .35) / .65 * Math.PI),
1508
+ riblet: (t) => t * .7 + .3 * Math.abs(Math.sin(t * 2.5 * Math.PI)),
1509
+ artDeco: (t) => Math.min(1, Math.floor(t * 3 + .5) / 3 + (t * 3 - Math.floor(t * 3)) * .12)
1510
+ };
1511
+ const DIRECTION_AZIMUTH = {
1512
+ r: 0,
1513
+ br: 45,
1514
+ b: 90,
1515
+ bl: 135,
1516
+ l: 180,
1517
+ tl: 225,
1518
+ t: 270,
1519
+ tr: 315
1520
+ };
1521
+ const RIG_LOOK = (type) => {
1522
+ if (type === "flat" || type.startsWith("legacyFlat")) return null;
1523
+ if (type === "harsh" || type.startsWith("legacyHarsh") || type === "contrasting") return {
1524
+ elevationDeg: 30,
1525
+ contrast: .65
1526
+ };
1527
+ if (type === "soft" || type === "flood" || type === "brightRoom" || type === "glow") return {
1528
+ elevationDeg: 45,
1529
+ contrast: .55
1530
+ };
1531
+ return {
1532
+ elevationDeg: 38,
1533
+ contrast: .65
1534
+ };
1535
+ };
1536
+ const MATTE = {
1537
+ contrastScale: 1,
1538
+ specular: null
1539
+ };
1540
+ const MATERIAL_LOOK = {
1541
+ plastic: {
1542
+ contrastScale: 1,
1543
+ specular: {
1544
+ constant: .6,
1545
+ exponent: 20
1546
+ }
1547
+ },
1548
+ legacyPlastic: {
1549
+ contrastScale: 1,
1550
+ specular: {
1551
+ constant: .6,
1552
+ exponent: 20
1553
+ }
1554
+ },
1555
+ metal: {
1556
+ contrastScale: 1.2,
1557
+ specular: {
1558
+ constant: .9,
1559
+ exponent: 12
1560
+ }
1561
+ },
1562
+ legacyMetal: {
1563
+ contrastScale: 1.2,
1564
+ specular: {
1565
+ constant: .9,
1566
+ exponent: 12
1567
+ }
1568
+ },
1569
+ softmetal: {
1570
+ contrastScale: 1.1,
1571
+ specular: {
1572
+ constant: .6,
1573
+ exponent: 8
1574
+ }
1575
+ },
1576
+ clear: {
1577
+ contrastScale: .9,
1578
+ specular: {
1579
+ constant: .8,
1580
+ exponent: 30
1581
+ }
1582
+ },
1583
+ softEdge: {
1584
+ contrastScale: .9,
1585
+ specular: {
1586
+ constant: .25,
1587
+ exponent: 10
1588
+ }
1589
+ },
1590
+ powder: {
1591
+ contrastScale: .8,
1592
+ specular: null
1593
+ },
1594
+ translucentPowder: {
1595
+ contrastScale: .7,
1596
+ specular: null
1597
+ },
1598
+ flat: {
1599
+ contrastScale: 0,
1600
+ specular: null
1601
+ }
1602
+ };
1603
+ const RELIEF = 2.5;
1604
+ /**
1605
+ * The bevel to shade a text body with, or `null` when it has no top bevel or
1606
+ * its rig/material renders it unshaded.
1607
+ */
1608
+ const textBevelOf = (text3d) => {
1609
+ const bevel = text3d?.bevelTop;
1610
+ if (!bevel) return null;
1611
+ const widthEmu = bevel.widthEmu ?? DEFAULT_BEVEL_EMU$1;
1612
+ const heightEmu = bevel.heightEmu ?? DEFAULT_BEVEL_EMU$1;
1613
+ if (widthEmu <= 0 || heightEmu <= 0) return null;
1614
+ const rig = text3d.scene?.lightRig ?? DEFAULT_RIG;
1615
+ const look = RIG_LOOK(rig.type);
1616
+ const material = (text3d.material && MATERIAL_LOOK[text3d.material]) ?? MATTE;
1617
+ if (!look || material.contrastScale === 0) return null;
1618
+ const profile = PROFILES[bevel.preset ?? DEFAULT_PRESET];
1619
+ const azimuthDeg = (DIRECTION_AZIMUTH[rig.direction] + (rig.rotation?.revolutionDeg ?? 0)) % 360;
1620
+ return {
1621
+ widthEmu,
1622
+ heightEmu,
1623
+ profile: Array.from({ length: PROFILE_SAMPLES }, (_, i) => Math.max(0, Math.min(1, profile(i / (PROFILE_SAMPLES - 1))))),
1624
+ azimuthDeg,
1625
+ elevationDeg: look.elevationDeg,
1626
+ contrast: Math.min(1, look.contrast * material.contrastScale),
1627
+ relief: RELIEF,
1628
+ specular: material.specular
1629
+ };
1630
+ };
1631
+ //#endregion
1632
+ //#region src/shape-3d.ts
1633
+ const EMU_PER_PX$1 = 9525;
1634
+ const DEFAULT_BEVEL_EMU = 76200;
1635
+ const LIGHT_AZIMUTH_DEG = 225;
1636
+ const LIGHT_ELEVATION_DEG = 45;
1637
+ const radians = (degrees) => degrees * Math.PI / 180;
1638
+ /**
1639
+ * An SVG `matrix(...)` for the camera's explicit rotation about the point
1640
+ * (`cx`, `cy`) in px, or `null` when the camera keeps the shape face-on.
1641
+ * The camera turns, so the shape appears turned the opposite way: a camera
1642
+ * revolution of +6° (Picture Style "Rotated, White") tilts the picture 6°
1643
+ * counter-clockwise.
1644
+ */
1645
+ const cameraTransform = (shape3d, cx, cy) => {
1646
+ const rotation = shape3d?.scene?.cameraRotation;
1647
+ if (!rotation) return null;
1648
+ const { latitudeDeg, longitudeDeg, revolutionDeg } = rotation;
1649
+ if (latitudeDeg === 0 && longitudeDeg === 0 && revolutionDeg === 0) return null;
1650
+ const lon = radians(-longitudeDeg);
1651
+ const lat = radians(-latitudeDeg);
1652
+ const rev = radians(-revolutionDeg);
1653
+ const ex = [Math.cos(lon), Math.sin(lon) * Math.sin(lat)];
1654
+ const ey = [0, Math.cos(lat)];
1655
+ const cos = Math.cos(rev);
1656
+ const sin = Math.sin(rev);
1657
+ const turn = (v) => [cos * v[0] - sin * v[1], sin * v[0] + cos * v[1]];
1658
+ const [a, b] = turn(ex);
1659
+ const [c, d] = turn(ey);
1660
+ return `matrix(${[
1661
+ a,
1662
+ b,
1663
+ c,
1664
+ d,
1665
+ cx - a * cx - c * cy,
1666
+ cy - b * cx - d * cy
1667
+ ].map((n) => n.toFixed(6)).join(" ")})`;
1668
+ };
1669
+ /**
1670
+ * The `<filter>` body that shades a top bevel onto whatever the shape painted
1671
+ * (picture, fill and border alike): a lit height map built from the blurred
1672
+ * outline, multiplied in so flat areas keep their color and slopes facing
1673
+ * away from the light darken, plus a specular highlight on the lit slopes.
1674
+ * `null` when the shape has no top bevel.
1675
+ */
1676
+ const bevelFilterPrimitives = (shape3d) => {
1677
+ const bevel = shape3d?.bevelTop;
1678
+ if (!bevel) return null;
1679
+ const widthPx = (bevel.widthEmu ?? DEFAULT_BEVEL_EMU) / EMU_PER_PX$1;
1680
+ const heightPx = (bevel.heightEmu ?? DEFAULT_BEVEL_EMU) / EMU_PER_PX$1;
1681
+ if (widthPx <= 0 || heightPx <= 0) return null;
1682
+ const blur = widthPx / 2;
1683
+ const surfaceScale = Math.min(10, Math.max(1, heightPx / widthPx * 6));
1684
+ const diffuse = 1 / Math.sin(radians(LIGHT_ELEVATION_DEG));
1685
+ const light = `<feDistantLight azimuth="${LIGHT_AZIMUTH_DEG}" elevation="${LIGHT_ELEVATION_DEG}"/>`;
1686
+ return `<feGaussianBlur in="SourceAlpha" stdDeviation="${blur.toFixed(2)}" result="bevelHeight"/><feDiffuseLighting in="bevelHeight" surfaceScale="${surfaceScale.toFixed(2)}" diffuseConstant="${diffuse.toFixed(4)}" lighting-color="#fff" result="bevelShade">${light}</feDiffuseLighting><feComposite in="SourceGraphic" in2="bevelShade" operator="arithmetic" k1="1" result="bevelLit"/><feSpecularLighting in="bevelHeight" surfaceScale="${surfaceScale.toFixed(2)}" specularConstant="0.6" specularExponent="20" lighting-color="#fff" result="bevelSpec">${light}</feSpecularLighting><feComposite in="bevelSpec" in2="SourceAlpha" operator="in" result="bevelGloss"/><feComposite in="bevelLit" in2="bevelGloss" operator="arithmetic" k2="1" k3="1"/>`;
1687
+ };
1688
+ //#endregion
1476
1689
  //#region src/render-slide.ts
1477
1690
  const clickActionHref = (pres, action) => {
1478
1691
  if (!action) return void 0;
@@ -1619,20 +1832,64 @@ const renderPicture = (shape, pres, x, y, w, h, transform, textOverlay, bytes, f
1619
1832
  }
1620
1833
  const opacityAttr = opacity !== 1 ? ` opacity="${opacity.toFixed(3)}"` : "";
1621
1834
  const border = outline.stroke !== "none" && outline.strokeWidth > 0 ? `<g${transform} fill="none" stroke="${outline.stroke}" stroke-width="${E(outline.strokeWidth)}"${outline.strokeAttrs ? ` ${outline.strokeAttrs}` : ""}>${pictureClipGeometry(shape, preset, x, y, w, h)}</g>` : "";
1835
+ const under = outline.fill !== "none" ? `${outline.defs}<g${transform} fill="${outline.fill}"${outline.fillAttrs}>${pictureClipGeometry(shape, preset, x, y, w, h)}</g>` : "";
1622
1836
  if (layout?.mode === "tile") {
1623
1837
  const intrinsic = getShapeImageIntrinsicSize(shape);
1624
1838
  if (intrinsic) {
1625
1839
  const pattern = imageTilePattern(dataUrl, layout, intrinsic, x, y, w, h, cropL, cropT, cropR, cropB);
1626
1840
  const geometry = pictureClipGeometry(shape, preset, x, y, w, h);
1627
- return `${clipDef}${pattern.defs}<g${transform} fill="${pattern.fill}"${filterAttr}${opacityAttr}>${geometry}</g>${border}<g${transform}>${textOverlay}</g>`;
1841
+ return `${clipDef}${pattern.defs}${pictureEffects(pres, shape, {
1842
+ x,
1843
+ y,
1844
+ w,
1845
+ h
1846
+ }, `${under}<g${transform} fill="${pattern.fill}"${filterAttr}${opacityAttr}>${geometry}</g>${border}`)}<g${transform}>${textOverlay}</g>`;
1628
1847
  }
1629
1848
  }
1630
- 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>${border}<g${transform}>${textOverlay}</g>`;
1849
+ return `${clipDef}${pictureEffects(pres, shape, {
1850
+ x,
1851
+ y,
1852
+ w,
1853
+ h
1854
+ }, `${under}<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>${border}`)}<g${transform}>${textOverlay}</g>`;
1631
1855
  }
1632
1856
  const linkUrl = getShapeImageLinkUrl(shape);
1633
1857
  const label = !bytes ? linkUrl ? `picture (link: ${linkUrl.length > 48 ? linkUrl.slice(0, 45) + "…" : linkUrl})` : "picture (no bytes)" : `picture (${format ?? "unknown"}${bytes ? `, ${bytes.byteLength} B` : ""})`;
1634
1858
  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>`;
1635
1859
  };
1860
+ /**
1861
+ * Wraps a picture's markup in its 3-D approximation (bevel lighting, then the
1862
+ * camera rotation) and its `<a:effectLst>` (shadows, glow, soft edge and the
1863
+ * reflection), the same effects a shape gets.
1864
+ */
1865
+ const pictureEffects = (pres, shape, box, markup) => {
1866
+ const shape3d = getShape3D(shape);
1867
+ let defs = "";
1868
+ let content = markup;
1869
+ let mirrored = markup;
1870
+ const bevel = bevelFilterPrimitives(shape3d);
1871
+ if (bevel) {
1872
+ const id = mintId();
1873
+ defs += `<defs><filter id="${id}" x="-10%" y="-10%" width="120%" height="120%" color-interpolation-filters="sRGB">${bevel}</filter></defs>`;
1874
+ content = `<g filter="url(#${id})">${content}</g>`;
1875
+ }
1876
+ const camera = cameraTransform(shape3d, (box.x + box.w / 2) / EMU_PER_PX, (box.y + box.h / 2) / EMU_PER_PX);
1877
+ if (camera) {
1878
+ content = `<g transform="${camera}">${content}</g>`;
1879
+ mirrored = `<g transform="${camera}">${mirrored}</g>`;
1880
+ }
1881
+ const reflection = buildReflection(pres, shape, mirrored, box);
1882
+ const fx = buildEffectsFilter(pres, shape);
1883
+ if (fx) {
1884
+ defs += fx.defs;
1885
+ content = `<g filter="url(#${fx.id})">${content}</g>`;
1886
+ }
1887
+ if (reflection) {
1888
+ defs += reflection.defs;
1889
+ content = reflection.svg + content;
1890
+ }
1891
+ return defs + content;
1892
+ };
1636
1893
  const imageTilePattern = (dataUrl, layout, intrinsic, x, y, w, h, cropL = 0, cropT = 0, cropR = 0, cropB = 0) => {
1637
1894
  const sourceW = intrinsic.width * (layout.scaleX ?? 1);
1638
1895
  const sourceH = intrinsic.height * (layout.scaleY ?? 1);
@@ -1677,15 +1934,11 @@ const imageTilePattern = (dataUrl, layout, intrinsic, x, y, w, h, cropL = 0, cro
1677
1934
  };
1678
1935
  };
1679
1936
  const pictureClipGeometry = (shape, preset, x, y, w, h) => {
1680
- if (preset === "ellipse") return `<ellipse cx="${E(x + w / 2)}" cy="${E(y + h / 2)}" rx="${E(w / 2)}" ry="${E(h / 2)}"/>`;
1681
- if (preset === "roundRect") {
1682
- const radius = E(Math.min(w, h) * Math.max(0, Math.min(.5, (getShapeAdjustValues(shape).adj ?? 16667) / 1e5)));
1683
- return `<rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}" rx="${radius}" ry="${radius}"/>`;
1684
- }
1685
- const path = PRESET_PATHS[preset];
1686
- if (path) return `<path d="${path(x / EMU_PER_PX, y / EMU_PER_PX, w / EMU_PER_PX, h / EMU_PER_PX)}" clip-rule="evenodd"/>`;
1687
- const points = PRESET_POINTS[preset];
1688
- if (points) return `<polygon points="${points(w / EMU_PER_PX, h / EMU_PER_PX).map(([nx, ny]) => `${E(x + nx * w)},${E(y + ny * h)}`).join(" ")}"/>`;
1937
+ const d = ((preset === "rect" ? null : getPresetGeometry(preset, {
1938
+ w: emu(w),
1939
+ h: emu(h)
1940
+ }, getShapeAdjustValues(shape)))?.paths ?? []).filter((path) => path.fill !== "none").map((path) => geometryPathData(path, x, y, w, h)).filter((data) => data !== "").join(" ");
1941
+ if (d !== "") return `<path d="${d}" clip-rule="evenodd"/>`;
1689
1942
  return `<rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}"/>`;
1690
1943
  };
1691
1944
  const renderPicturePlaceholderLabel = (x, y, w, h, text) => {
@@ -1938,7 +2191,22 @@ const patternDef = (pat) => {
1938
2191
  fillAttr: `url(#${id})`
1939
2192
  };
1940
2193
  };
1941
- const textFillPaint = (fill, theme) => (box) => {
2194
+ const textFillPaint = (fill, theme, solid) => (box) => {
2195
+ if (fill.kind === "none") return {
2196
+ defs: "",
2197
+ fill: "none"
2198
+ };
2199
+ if (fill.kind === "image") {
2200
+ if (!fill.bytes) return {
2201
+ defs: "",
2202
+ fill: solid
2203
+ };
2204
+ const id = mintId();
2205
+ return {
2206
+ defs: `<defs><pattern id="${id}" patternUnits="userSpaceOnUse" x="${box.x.toFixed(2)}" y="${box.y.toFixed(2)}" width="${box.w.toFixed(2)}" height="${box.h.toFixed(2)}"><image href="${bytesToDataUrl(fill.bytes)}" width="${box.w.toFixed(2)}" height="${box.h.toFixed(2)}" preserveAspectRatio="none"/></pattern></defs>`,
2207
+ fill: `url(#${id})`
2208
+ };
2209
+ }
1942
2210
  if (fill.kind === "pattern") {
1943
2211
  const built = patternDef({
1944
2212
  preset: fill.preset,
@@ -1996,6 +2264,32 @@ const buildArrowMarker = (type, width, length, color, orient) => {
1996
2264
  def: `<defs><marker id="${id}" viewBox="0 0 ${w} ${h}" refX="${w}" refY="${h / 2}" markerWidth="${w}" markerHeight="${h}" orient="${orient}">${body}</marker></defs>`
1997
2265
  };
1998
2266
  };
2267
+ const unrotatedGradientTransform = (grad, shape, pres) => {
2268
+ const bounds = shape && (pres ? getShapeBoundsResolved(pres, shape) : getShapeBounds(shape));
2269
+ if (grad.rotateWithShape !== false || !shape || !bounds || bounds.w <= 0 || bounds.h <= 0) return "";
2270
+ const angle = getShapeRotation(shape) * Math.PI / 180;
2271
+ const cos = Math.cos(angle);
2272
+ const sin = Math.sin(angle);
2273
+ const w = bounds.w;
2274
+ const h = bounds.h;
2275
+ const rotatedW = Math.abs(cos) * w + Math.abs(sin) * h;
2276
+ const rotatedH = Math.abs(sin) * w + Math.abs(cos) * h;
2277
+ const flip = getShapeFlip(shape);
2278
+ const sx = flip?.horizontal ? -1 : 1;
2279
+ const sy = flip?.vertical ? -1 : 1;
2280
+ const a = sx * cos * rotatedW / w;
2281
+ const b = -sy * sin * rotatedW / h;
2282
+ const c = sx * sin * rotatedH / w;
2283
+ const d = sy * cos * rotatedH / h;
2284
+ return ` gradientTransform="matrix(${[
2285
+ a,
2286
+ b,
2287
+ c,
2288
+ d,
2289
+ (1 - a - c) / 2,
2290
+ (1 - b - d) / 2
2291
+ ].map((value) => value.toFixed(6)).join(" ")})"`;
2292
+ };
1999
2293
  const paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
2000
2294
  let fillColor;
2001
2295
  let fillAttrs = "";
@@ -2010,33 +2304,7 @@ const paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
2010
2304
  else if (fill.kind === "gradient") {
2011
2305
  const grad = shape ? pres ? getShapeGradientFillEffective(pres, shape) : getShapeGradientFill(shape) : null;
2012
2306
  if (grad) {
2013
- let transform = "";
2014
- const bounds = shape && (pres ? getShapeBoundsResolved(pres, shape) : getShapeBounds(shape));
2015
- if (grad.rotateWithShape === false && shape && bounds && bounds.w > 0 && bounds.h > 0) {
2016
- const angle = getShapeRotation(shape) * Math.PI / 180;
2017
- const cos = Math.cos(angle);
2018
- const sin = Math.sin(angle);
2019
- const w = bounds.w;
2020
- const h = bounds.h;
2021
- const rotatedW = Math.abs(cos) * w + Math.abs(sin) * h;
2022
- const rotatedH = Math.abs(sin) * w + Math.abs(cos) * h;
2023
- const flip = getShapeFlip(shape);
2024
- const sx = flip?.horizontal ? -1 : 1;
2025
- const sy = flip?.vertical ? -1 : 1;
2026
- const a = sx * cos * rotatedW / w;
2027
- const b = -sy * sin * rotatedW / h;
2028
- const c = sx * sin * rotatedH / w;
2029
- const d = sy * cos * rotatedH / h;
2030
- transform = ` gradientTransform="matrix(${[
2031
- a,
2032
- b,
2033
- c,
2034
- d,
2035
- (1 - a - c) / 2,
2036
- (1 - b - d) / 2
2037
- ].map((value) => value.toFixed(6)).join(" ")})"`;
2038
- }
2039
- const built = gradientDef(grad, theme, transform);
2307
+ const built = gradientDef(grad, theme, unrotatedGradientTransform(grad, shape, pres));
2040
2308
  defs = built.defs;
2041
2309
  fillColor = built.fillAttr;
2042
2310
  } else fillColor = (shape && pres ? getShapeFillColorResolved(pres, shape) : null) ?? "none";
@@ -2053,10 +2321,34 @@ const paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
2053
2321
  let strokeWidth = 0;
2054
2322
  const strokeAttrParts = [];
2055
2323
  let markerAttrs = "";
2324
+ let headColor = strokeColor;
2325
+ let tailColor = strokeColor;
2056
2326
  if (stroke.kind === "solid") {
2057
2327
  let resolved = null;
2058
2328
  if (shape && pres) resolved = getShapeStrokeColorResolved(pres, shape);
2059
2329
  strokeColor = resolved ?? resolveColor(stroke.color, theme, "#9CA3AF");
2330
+ headColor = strokeColor;
2331
+ tailColor = strokeColor;
2332
+ } else if (stroke.kind === "gradient") {
2333
+ const grad = shape ? getShapeStrokeGradient(shape, pres) : null;
2334
+ const bounds = shape && (pres ? getShapeBoundsResolved(pres, shape) : getShapeBounds(shape));
2335
+ if (grad && bounds) {
2336
+ const stopColor = (stop) => stop ? stop.resolvedColor ?? resolveColor(stop.color, theme, "#9CA3AF") : "#9CA3AF";
2337
+ const ordered = [...grad.stops].sort((a, b) => a.offset - b.offset);
2338
+ headColor = stopColor(ordered[0]);
2339
+ tailColor = stopColor(ordered[ordered.length - 1]);
2340
+ const widthEmu = stroke.widthEmu ?? 9525;
2341
+ const built = bounds.w > 0 && bounds.h > 0 ? gradientDef(grad, theme, unrotatedGradientTransform(grad, shape, pres)) : gradientDef(grad, theme, "", {
2342
+ x: (bounds.w > 0 ? bounds.x : bounds.x - widthEmu / 2) / EMU_PER_PX,
2343
+ y: (bounds.h > 0 ? bounds.y : bounds.y - widthEmu / 2) / EMU_PER_PX,
2344
+ w: Math.max(bounds.w, widthEmu) / EMU_PER_PX,
2345
+ h: Math.max(bounds.h, widthEmu) / EMU_PER_PX
2346
+ });
2347
+ defs += built.defs;
2348
+ strokeColor = built.fillAttr;
2349
+ }
2350
+ }
2351
+ if (strokeColor !== "none" && (stroke.kind === "solid" || stroke.kind === "gradient")) {
2060
2352
  strokeWidth = stroke.widthEmu ?? 9525;
2061
2353
  if (shape) {
2062
2354
  const opacity = getShapeStrokeOpacity(shape, pres);
@@ -2082,12 +2374,12 @@ const paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
2082
2374
  const head = getShapeStrokeArrow(shape, "head", pres);
2083
2375
  const tail = getShapeStrokeArrow(shape, "tail", pres);
2084
2376
  if (head && head.type !== "none") {
2085
- const m = buildArrowMarker(head.type, head.width, head.length, strokeColor, "auto-start-reverse");
2377
+ const m = buildArrowMarker(head.type, head.width, head.length, headColor, "auto-start-reverse");
2086
2378
  defs += m.def;
2087
2379
  markerAttrs += ` marker-start="url(#${m.id})"`;
2088
2380
  }
2089
2381
  if (tail && tail.type !== "none") {
2090
- const m = buildArrowMarker(tail.type, tail.width, tail.length, strokeColor, "auto");
2382
+ const m = buildArrowMarker(tail.type, tail.width, tail.length, tailColor, "auto");
2091
2383
  defs += m.def;
2092
2384
  markerAttrs += ` marker-end="url(#${m.id})"`;
2093
2385
  }
@@ -2103,904 +2395,6 @@ const paint = (shape, fill, stroke, theme, isPlaceholder, pres) => {
2103
2395
  markerAttrs
2104
2396
  };
2105
2397
  };
2106
- const polygon = (n, rotation = -Math.PI / 2) => {
2107
- const out = [];
2108
- for (let i = 0; i < n; i++) {
2109
- const a = rotation + i * 2 * Math.PI / n;
2110
- out.push([.5 + .5 * Math.cos(a), .5 + .5 * Math.sin(a)]);
2111
- }
2112
- return out;
2113
- };
2114
- const star = (points, innerRatio = .42) => {
2115
- const out = [];
2116
- const rotation = -Math.PI / 2;
2117
- for (let i = 0; i < points * 2; i++) {
2118
- const a = rotation + i * Math.PI / points;
2119
- const r = i % 2 === 0 ? .5 : .5 * innerRatio;
2120
- out.push([.5 + r * Math.cos(a), .5 + r * Math.sin(a)]);
2121
- }
2122
- return out;
2123
- };
2124
- const PRESET_POINTS = {
2125
- triangle: () => [
2126
- [.5, 0],
2127
- [1, 1],
2128
- [0, 1]
2129
- ],
2130
- rtTriangle: () => [
2131
- [0, 0],
2132
- [1, 1],
2133
- [0, 1]
2134
- ],
2135
- diamond: () => [
2136
- [.5, 0],
2137
- [1, .5],
2138
- [.5, 1],
2139
- [0, .5]
2140
- ],
2141
- parallelogram: () => [
2142
- [.25, 0],
2143
- [1, 0],
2144
- [.75, 1],
2145
- [0, 1]
2146
- ],
2147
- trapezoid: () => [
2148
- [.25, 0],
2149
- [.75, 0],
2150
- [1, 1],
2151
- [0, 1]
2152
- ],
2153
- pentagon: () => polygon(5),
2154
- hexagon: () => polygon(6),
2155
- heptagon: () => polygon(7),
2156
- octagon: () => polygon(8),
2157
- decagon: () => polygon(10),
2158
- dodecagon: () => polygon(12),
2159
- star4: () => star(4),
2160
- star5: () => star(5),
2161
- star6: () => star(6),
2162
- star7: () => star(7),
2163
- star8: () => star(8),
2164
- star10: () => star(10),
2165
- star12: () => star(12),
2166
- star16: () => star(16),
2167
- star24: () => star(24),
2168
- star32: () => star(32),
2169
- rightArrow: (w, h) => {
2170
- const bx = 1 - Math.min(1, .5 * Math.min(w, h) / w);
2171
- return [
2172
- [0, .25],
2173
- [bx, .25],
2174
- [bx, 0],
2175
- [1, .5],
2176
- [bx, 1],
2177
- [bx, .75],
2178
- [0, .75]
2179
- ];
2180
- },
2181
- leftArrow: (w, h) => {
2182
- const bx = Math.min(1, .5 * Math.min(w, h) / w);
2183
- return [
2184
- [1, .25],
2185
- [bx, .25],
2186
- [bx, 0],
2187
- [0, .5],
2188
- [bx, 1],
2189
- [bx, .75],
2190
- [1, .75]
2191
- ];
2192
- },
2193
- upArrow: (w, h) => {
2194
- const by = Math.min(1, .5 * Math.min(w, h) / h);
2195
- return [
2196
- [.25, 1],
2197
- [.25, by],
2198
- [0, by],
2199
- [.5, 0],
2200
- [1, by],
2201
- [.75, by],
2202
- [.75, 1]
2203
- ];
2204
- },
2205
- downArrow: (w, h) => {
2206
- const by = 1 - Math.min(1, .5 * Math.min(w, h) / h);
2207
- return [
2208
- [.25, 0],
2209
- [.25, by],
2210
- [0, by],
2211
- [.5, 1],
2212
- [1, by],
2213
- [.75, by],
2214
- [.75, 0]
2215
- ];
2216
- },
2217
- leftRightArrow: () => [
2218
- [0, .5],
2219
- [.18, .2],
2220
- [.18, .35],
2221
- [.82, .35],
2222
- [.82, .2],
2223
- [1, .5],
2224
- [.82, .8],
2225
- [.82, .65],
2226
- [.18, .65],
2227
- [.18, .8]
2228
- ],
2229
- upDownArrow: () => [
2230
- [.5, 0],
2231
- [.2, .18],
2232
- [.35, .18],
2233
- [.35, .82],
2234
- [.2, .82],
2235
- [.5, 1],
2236
- [.8, .82],
2237
- [.65, .82],
2238
- [.65, .18],
2239
- [.8, .18]
2240
- ],
2241
- chevron: () => [
2242
- [0, 0],
2243
- [.7, 0],
2244
- [1, .5],
2245
- [.7, 1],
2246
- [0, 1],
2247
- [.3, .5]
2248
- ],
2249
- bentArrow: () => [
2250
- [0, .45],
2251
- [.55, .45],
2252
- [.55, .25],
2253
- [.55, .05],
2254
- [.95, .05],
2255
- [.95, .55],
2256
- [.8, .55],
2257
- [.8, .85],
2258
- [0, .85]
2259
- ],
2260
- homePlate: () => [
2261
- [0, 0],
2262
- [.75, 0],
2263
- [1, .5],
2264
- [.75, 1],
2265
- [0, 1]
2266
- ]
2267
- };
2268
- const PRESET_PATHS = {
2269
- wedgeRectCallout: (x, y, w, h) => {
2270
- const bodyH = h * .78;
2271
- const tailTipX = x + w * .12;
2272
- const tailTipY = y + h;
2273
- const tailBaseLeft = x + w * .18;
2274
- const tailBaseRight = x + w * .32;
2275
- const bodyB = y + bodyH;
2276
- 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`;
2277
- },
2278
- wedgeRoundRectCallout: (x, y, w, h) => {
2279
- const r = Math.min(w, h) * .08;
2280
- const bodyH = h * .78;
2281
- const tailTipX = x + w * .12;
2282
- const tailTipY = y + h;
2283
- const tailBaseLeft = x + w * .18;
2284
- const tailBaseRight = x + w * .32;
2285
- const bodyB = y + bodyH;
2286
- 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`;
2287
- },
2288
- wedgeEllipseCallout: (x, y, w, h) => {
2289
- const bodyCy = y + h * .39;
2290
- const bodyRy = h * .39;
2291
- const cx = x + w / 2;
2292
- const bodyRx = w / 2;
2293
- const tailTipX = x + w * .12;
2294
- const tailTipY = y + h;
2295
- const tailBaseAngle = 1.5;
2296
- return `M${cx + bodyRx * Math.cos(tailBaseAngle - .18)},${bodyCy + bodyRy * Math.sin(tailBaseAngle - .18)} A${bodyRx},${bodyRy} 0 1 0 ${cx + bodyRx * Math.cos(1.68)},${bodyCy + bodyRy * Math.sin(1.68)} L${tailTipX},${tailTipY} Z`;
2297
- },
2298
- cloudCallout: (x, y, w, h) => {
2299
- const bodyH = h * .78;
2300
- const cx = x + w / 2;
2301
- const cy = y + bodyH / 2;
2302
- const rx = w / 2 * .92;
2303
- const ry = bodyH / 2 * .85;
2304
- const lobes = 10;
2305
- const path = [];
2306
- for (let i = 0; i < lobes; i++) {
2307
- const a = i / lobes * 2 * Math.PI - Math.PI / 2;
2308
- const lobeRx = rx * .32;
2309
- const lobeRy = ry * .32;
2310
- const px0 = cx + rx * Math.cos(a);
2311
- const py0 = cy + ry * Math.sin(a);
2312
- if (i === 0) path.push(`M${px0 - lobeRx},${py0}`);
2313
- path.push(`A${lobeRx},${lobeRy} 0 1 1 ${px0 + lobeRx},${py0}`);
2314
- const nextA = (i + 1) / lobes * 2 * Math.PI - Math.PI / 2;
2315
- const nextX = cx + rx * Math.cos(nextA) - lobeRx;
2316
- const nextY = cy + ry * Math.sin(nextA);
2317
- path.push(`L${nextX},${nextY}`);
2318
- }
2319
- path.push("Z");
2320
- const tailX = x + w * .18;
2321
- const tailY = y + h * .95;
2322
- 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`;
2323
- },
2324
- heart: (x, y, w, h) => {
2325
- const cx = x + w / 2;
2326
- const top = y + h * .27;
2327
- return `M${cx},${y + h} C${x},${y + h * .55} ${x},${top} ${cx},${y + h * .4} C${x + w},${top} ${x + w},${y + h * .55} ${cx},${y + h} Z`;
2328
- },
2329
- sun: (x, y, w, h) => {
2330
- const cx = x + w / 2;
2331
- const cy = y + h / 2;
2332
- const innerR = Math.min(w, h) * .25;
2333
- const outerR = Math.min(w, h) * .5;
2334
- const rays = 12;
2335
- const path = [];
2336
- for (let i = 0; i < rays * 2; i++) {
2337
- const r = i % 2 === 0 ? outerR : innerR;
2338
- const a = i / (rays * 2) * 2 * Math.PI - Math.PI / 2;
2339
- const px0 = cx + r * Math.cos(a);
2340
- const py0 = cy + r * Math.sin(a);
2341
- path.push(`${i === 0 ? "M" : "L"}${px0},${py0}`);
2342
- }
2343
- path.push("Z");
2344
- return path.join(" ");
2345
- },
2346
- smileyFace: (x, y, w, h) => {
2347
- const cx = x + w / 2;
2348
- const cy = y + h / 2;
2349
- const r = Math.min(w, h) / 2 - 1;
2350
- const eyeR = r * .07;
2351
- const eyeOff = r * .32;
2352
- const mouthW = r * .5;
2353
- const mouthY = cy + r * .18;
2354
- 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 * .32} ${cx + mouthW},${mouthY}`;
2355
- },
2356
- lightningBolt: (x, y, w, h) => {
2357
- return `M${x + w * .5},${y} L${x + w * .15},${y + h * .55} L${x + w * .45},${y + h * .55} L${x + w * .3},${y + h} L${x + w * .85},${y + h * .4} L${x + w * .55},${y + h * .4} L${x + w * .7},${y} Z`;
2358
- },
2359
- flowChartProcess: (x, y, w, h) => `M${x},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} Z`,
2360
- flowChartAlternateProcess: (x, y, w, h) => {
2361
- const r = Math.min(w, h) * .18;
2362
- 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`;
2363
- },
2364
- flowChartDecision: (x, y, w, h) => {
2365
- const cx = x + w / 2;
2366
- const cy = y + h / 2;
2367
- return `M${cx},${y} L${x + w},${cy} L${cx},${y + h} L${x},${cy} Z`;
2368
- },
2369
- flowChartTerminator: (x, y, w, h) => {
2370
- const r = h / 2;
2371
- 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`;
2372
- },
2373
- flowChartConnector: (x, y, w, h) => {
2374
- const cx = x + w / 2;
2375
- const cy = y + h / 2;
2376
- const r = Math.min(w, h) / 2;
2377
- return `M${cx + r},${cy} A${r},${r} 0 1 1 ${cx - r},${cy} A${r},${r} 0 1 1 ${cx + r},${cy} Z`;
2378
- },
2379
- flowChartDocument: (x, y, w, h) => {
2380
- const wave = h * .18;
2381
- return `M${x},${y} L${x + w},${y} L${x + w},${y + h - wave} C${x + w * .75},${y + h + wave * .5} ${x + w * .25},${y + h - wave * 2} ${x},${y + h - wave * .5} Z`;
2382
- },
2383
- flowChartMultidocument: (x, y, w, h) => {
2384
- const inset = w * .06;
2385
- return `${`M${x + inset},${y + inset * .6} L${x + w},${y + inset * .6} L${x + w},${y + h - inset * .6} L${x + w - inset},${y + h - inset * .6} L${x + w - inset},${y + inset * .6}`} Z ${`M${x},${y + inset * 1.2} L${x + w - inset},${y + inset * 1.2} L${x + w - inset},${y + h * .85} C${x + (w - inset) * .75},${y + h + 6} ${x + (w - inset) * .25},${y + h * .75} ${x},${y + h * .95} Z`}`;
2386
- },
2387
- flowChartPredefinedProcess: (x, y, w, h) => {
2388
- const inset = w * .1;
2389
- 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}`;
2390
- },
2391
- flowChartInternalStorage: (x, y, w, h) => {
2392
- const inset = Math.min(w, h) * .1;
2393
- 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}`;
2394
- },
2395
- flowChartManualInput: (x, y, w, h) => {
2396
- return `M${x},${y + h * .35} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} Z`;
2397
- },
2398
- flowChartManualOperation: (x, y, w, h) => {
2399
- return `M${x},${y} L${x + w},${y} L${x + w * .8},${y + h} L${x + w * .2},${y + h} Z`;
2400
- },
2401
- flowChartInputOutput: (x, y, w, h) => {
2402
- const skew = w * .18;
2403
- return `M${x + skew},${y} L${x + w},${y} L${x + w - skew},${y + h} L${x},${y + h} Z`;
2404
- },
2405
- flowChartPunchedTape: (x, y, w, h) => {
2406
- const wave = h * .12;
2407
- return `M${x},${y + wave} C${x + w * .25},${y - wave} ${x + w * .75},${y + wave * 2} ${x + w},${y + wave} L${x + w},${y + h - wave} C${x + w * .75},${y + h + wave} ${x + w * .25},${y + h - wave * 2} ${x},${y + h - wave} Z`;
2408
- },
2409
- flowChartCard: (x, y, w, h) => {
2410
- const cut = h * .3;
2411
- return `M${x + cut},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} L${x},${y + cut} Z`;
2412
- },
2413
- flowChartPunchedCard: (x, y, w, h) => {
2414
- const cut = h * .2;
2415
- return `M${x + cut},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h} L${x},${y + cut} Z`;
2416
- },
2417
- flowChartOnlineStorage: (x, y, w, h) => {
2418
- const cap = w * .12;
2419
- 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`;
2420
- },
2421
- flowChartMagneticDisk: (x, y, w, h) => {
2422
- const er = h * .12;
2423
- 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}`;
2424
- },
2425
- flowChartMagneticDrum: (x, y, w, h) => {
2426
- const er = w * .12;
2427
- 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}`;
2428
- },
2429
- flowChartMagneticTape: (x, y, w, h) => {
2430
- const cx = x + w / 2;
2431
- const cy = y + h / 2;
2432
- const r = Math.min(w, h) / 2;
2433
- return `M${cx + r},${cy} A${r},${r} 0 1 0 ${cx - r * .7},${cy + r * .7} L${x + w},${y + h} L${cx + r},${cy} Z`;
2434
- },
2435
- flowChartSummingJunction: (x, y, w, h) => {
2436
- const cx = x + w / 2;
2437
- const cy = y + h / 2;
2438
- const r = Math.min(w, h) / 2;
2439
- const off = r * Math.SQRT1_2;
2440
- 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}`;
2441
- },
2442
- flowChartOr: (x, y, w, h) => {
2443
- const cx = x + w / 2;
2444
- const cy = y + h / 2;
2445
- const r = Math.min(w, h) / 2;
2446
- 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}`;
2447
- },
2448
- flowChartCollate: (x, y, w, h) => {
2449
- return `M${x},${y} L${x + w},${y} L${x},${y + h} L${x + w},${y + h} Z`;
2450
- },
2451
- flowChartSort: (x, y, w, h) => {
2452
- const cx = x + w / 2;
2453
- const cy = y + h / 2;
2454
- return `M${cx},${y} L${x + w},${cy} L${cx},${y + h} L${x},${cy} Z M${x},${cy} L${x + w},${cy}`;
2455
- },
2456
- flowChartExtract: (x, y, w, h) => {
2457
- return `M${x + w / 2},${y} L${x + w},${y + h} L${x},${y + h} Z`;
2458
- },
2459
- flowChartMerge: (x, y, w, h) => {
2460
- return `M${x},${y} L${x + w},${y} L${x + w / 2},${y + h} Z`;
2461
- },
2462
- flowChartOfflineStorage: (x, y, w, h) => {
2463
- return `M${x + w / 2},${y} L${x + w},${y + h} L${x},${y + h} Z M${x + w * .25},${y + h * .75} L${x + w * .75},${y + h * .75}`;
2464
- },
2465
- flowChartDelay: (x, y, w, h) => {
2466
- const r = h / 2;
2467
- 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`;
2468
- },
2469
- flowChartDisplay: (x, y, w, h) => {
2470
- const r = h / 2;
2471
- return `M${x + r * .5},${y} L${x + w - r},${y} A${r},${r} 0 0 1 ${x + w - r},${y + h} L${x + r * .5},${y + h} L${x},${y + h / 2} Z`;
2472
- },
2473
- flowChartPreparation: (x, y, w, h) => {
2474
- const cut = w * .18;
2475
- 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`;
2476
- },
2477
- notchedRightArrow: (x, y, w, h) => {
2478
- return `M${x},${y + h * .3} L${x + w * .65},${y + h * .3} L${x + w * .65},${y} L${x + w},${y + h / 2} L${x + w * .65},${y + h} L${x + w * .65},${y + h * .7} L${x},${y + h * .7} L${x + w * .15},${y + h / 2} Z`;
2479
- },
2480
- stripedRightArrow: (x, y, w, h) => {
2481
- const stripe = w * .04;
2482
- return `M${x},${y + h * .3} L${x + stripe},${y + h * .3} L${x + stripe},${y + h * .7} L${x},${y + h * .7} Z M${x + stripe * 2.5},${y + h * .3} L${x + stripe * 3.5},${y + h * .3} L${x + stripe * 3.5},${y + h * .7} L${x + stripe * 2.5},${y + h * .7} Z M${x + stripe * 5},${y + h * .3} L${x + w * .65},${y + h * .3} L${x + w * .65},${y} L${x + w},${y + h / 2} L${x + w * .65},${y + h} L${x + w * .65},${y + h * .7} L${x + stripe * 5},${y + h * .7} Z`;
2483
- },
2484
- curvedRightArrow: (x, y, w, h) => {
2485
- return `M${x},${y + h * .6} Q${x + w * .5},${y} ${x + w * .85},${y + h * .25} L${x + w},${y + h * .45} L${x + w * .85},${y + h * .55} L${x + w * .7},${y + h * .35} Q${x + w * .45},${y + h * .18} ${x + w * .15},${y + h * .75} Z`;
2486
- },
2487
- uturnArrow: (x, y, w, h) => {
2488
- return `M${x},${y + h} L${x},${y + h / 2} A${w * .4},${h * .4} 0 0 1 ${x + w * .8},${y + h / 2} L${x + w * .8},${y + h * .25} L${x + w},${y + h * .45} L${x + w * .8},${y + h * .65} L${x + w * .8},${y + h / 2} A${w * .2},${h * .25} 0 0 0 ${x + w * .2},${y + h / 2} L${x + w * .2},${y + h} Z`;
2489
- },
2490
- leftBracket: (x, y, w, h) => {
2491
- return `M${x + w},${y} L${x},${y} L${x},${y + h} L${x + w},${y + h}`;
2492
- },
2493
- rightBracket: (x, y, w, h) => {
2494
- return `M${x},${y} L${x + w},${y} L${x + w},${y + h} L${x},${y + h}`;
2495
- },
2496
- bracketPair: (x, y, w, h) => {
2497
- return `M${x + w * .1},${y} L${x},${y} L${x},${y + h} L${x + w * .1},${y + h} M${x + w * .9},${y} L${x + w},${y} L${x + w},${y + h} L${x + w * .9},${y + h}`;
2498
- },
2499
- leftBrace: (x, y, w, h) => {
2500
- const mid = y + h / 2;
2501
- 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}`;
2502
- },
2503
- rightBrace: (x, y, w, h) => {
2504
- const mid = y + h / 2;
2505
- 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}`;
2506
- },
2507
- bracePair: (x, y, w, h) => {
2508
- const mid = y + h / 2;
2509
- return `M${x + w * .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 * .12},${y + h} M${x + w * .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 * .88},${y + h}`;
2510
- },
2511
- snip1Rect: (x, y, w, h) => {
2512
- const c = Math.min(w, h) * .18;
2513
- return `M${x},${y} L${x + w - c},${y} L${x + w},${y + c} L${x + w},${y + h} L${x},${y + h} Z`;
2514
- },
2515
- snip2SameRect: (x, y, w, h) => {
2516
- const c = Math.min(w, h) * .18;
2517
- 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`;
2518
- },
2519
- snip2DiagRect: (x, y, w, h) => {
2520
- const c = Math.min(w, h) * .18;
2521
- 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`;
2522
- },
2523
- snipRoundRect: (x, y, w, h) => {
2524
- const c = Math.min(w, h) * .18;
2525
- 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`;
2526
- },
2527
- round1Rect: (x, y, w, h) => {
2528
- const r = Math.min(w, h) * .18;
2529
- 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`;
2530
- },
2531
- round2SameRect: (x, y, w, h) => {
2532
- const r = Math.min(w, h) * .18;
2533
- 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`;
2534
- },
2535
- round2DiagRect: (x, y, w, h) => {
2536
- const r = Math.min(w, h) * .18;
2537
- 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`;
2538
- },
2539
- ribbon: (x, y, w, h) => {
2540
- const notch = w * .06;
2541
- const bodyTop = y + h * .2;
2542
- const bodyBot = y + h * .8;
2543
- return `M${x},${bodyTop} L${x + notch * 2},${y} L${x + w - notch * 2},${y} L${x + w},${bodyTop} L${x + w * .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 * .15},${bodyTop + (bodyBot - bodyTop) / 2} Z`;
2544
- },
2545
- ribbon2: (x, y, w, h) => {
2546
- const notch = w * .06;
2547
- const bodyTop = y + h * .2;
2548
- const bodyBot = y + h * .8;
2549
- return `M${x},${bodyBot} L${x + notch * 2},${y + h} L${x + w - notch * 2},${y + h} L${x + w},${bodyBot} L${x + w * .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 * .15},${bodyTop + (bodyBot - bodyTop) / 2} Z`;
2550
- },
2551
- verticalScroll: (x, y, w, h) => {
2552
- const r = w * .08;
2553
- 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`;
2554
- },
2555
- horizontalScroll: (x, y, w, h) => {
2556
- const r = h * .08;
2557
- 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`;
2558
- },
2559
- wave: (x, y, w, h) => {
2560
- return `M${x},${y + h * .5} C${x + w * .25},${y - h * .1} ${x + w * .5},${y + h * .85} ${x + w * .75},${y + h * .3} C${x + w * .85},${y + h * .05} ${x + w * .95},${y + h * .4} ${x + w},${y + h * .5} L${x + w},${y + h} C${x + w * .75},${y + h * .4} ${x + w * .5},${y + h * 1.1} ${x + w * .25},${y + h * .55} C${x + w * .15},${y + h * .3} ${x + w * .05},${y + h * .6} ${x},${y + h * .55} Z`;
2561
- },
2562
- doubleWave: (x, y, w, h) => {
2563
- return `M${x},${y + h * .4} C${x + w * .15},${y - h * .05} ${x + w * .35},${y + h * .65} ${x + w * .5},${y + h * .3} C${x + w * .65},${y - h * .05} ${x + w * .85},${y + h * .65} ${x + w},${y + h * .4} L${x + w},${y + h} C${x + w * .85},${y + h * .4} ${x + w * .65},${y + h * 1.05} ${x + w * .5},${y + h * .7} C${x + w * .35},${y + h * 1.05} ${x + w * .15},${y + h * .4} ${x},${y + h} Z`;
2564
- },
2565
- mathPlus: (x, y, w, h) => {
2566
- const t = Math.min(w, h) * .2;
2567
- const cx = x + w / 2;
2568
- const cy = y + h / 2;
2569
- 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`;
2570
- },
2571
- mathMinus: (x, y, w, h) => {
2572
- const t = h * .3;
2573
- const cy = y + h / 2;
2574
- return `M${x},${cy - t / 2} L${x + w},${cy - t / 2} L${x + w},${cy + t / 2} L${x},${cy + t / 2} Z`;
2575
- },
2576
- mathMultiply: (x, y, w, h) => {
2577
- const t = Math.min(w, h) * .16;
2578
- const cx = x + w / 2;
2579
- const cy = y + h / 2;
2580
- 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`;
2581
- },
2582
- mathDivide: (x, y, w, h) => {
2583
- const dot = Math.min(w, h) * .1;
2584
- const cx = x + w / 2;
2585
- const cy = y + h / 2;
2586
- return `M${x},${cy - dot * .4} L${x + w},${cy - dot * .4} L${x + w},${cy + dot * .4} L${x},${cy + dot * .4} Z M${cx - dot},${y + h * .18} A${dot},${dot} 0 1 0 ${cx + dot},${y + h * .18} A${dot},${dot} 0 1 0 ${cx - dot},${y + h * .18} Z M${cx - dot},${y + h * .82} A${dot},${dot} 0 1 0 ${cx + dot},${y + h * .82} A${dot},${dot} 0 1 0 ${cx - dot},${y + h * .82} Z`;
2587
- },
2588
- mathEqual: (x, y, w, h) => {
2589
- const t = h * .2;
2590
- return `M${x},${y + h * .3 - t / 2} L${x + w},${y + h * .3 - t / 2} L${x + w},${y + h * .3 + t / 2} L${x},${y + h * .3 + t / 2} Z M${x},${y + h * .7 - t / 2} L${x + w},${y + h * .7 - t / 2} L${x + w},${y + h * .7 + t / 2} L${x},${y + h * .7 + t / 2} Z`;
2591
- },
2592
- mathNotEqual: (x, y, w, h) => {
2593
- const t = h * .15;
2594
- const cy = y + h / 2;
2595
- return `M${x},${cy - h * .18 - t / 2} L${x + w},${cy - h * .18 - t / 2} L${x + w},${cy - h * .18 + t / 2} L${x},${cy - h * .18 + t / 2} Z M${x},${cy + h * .18 - t / 2} L${x + w},${cy + h * .18 - t / 2} L${x + w},${cy + h * .18 + t / 2} L${x},${cy + h * .18 + t / 2} Z M${x + w * .7},${y} L${x + w * .85},${y} L${x + w * .3},${y + h} L${x + w * .15},${y + h} Z`;
2596
- },
2597
- actionButtonBlank: (x, y, w, h) => {
2598
- const r = Math.min(w, h) * .06;
2599
- 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`;
2600
- },
2601
- irregularSeal1: (x, y, w, h) => {
2602
- const cx = x + w / 2;
2603
- const cy = y + h / 2;
2604
- const rx = w / 2;
2605
- const ry = h / 2;
2606
- const offsets = [
2607
- 1,
2608
- .45,
2609
- .95,
2610
- .5,
2611
- 1,
2612
- .4,
2613
- .9,
2614
- .55,
2615
- 1,
2616
- .45,
2617
- .95,
2618
- .5,
2619
- 1,
2620
- .4,
2621
- .9,
2622
- .55
2623
- ];
2624
- const points = [];
2625
- for (let i = 0; i < offsets.length; i++) {
2626
- const a = i / offsets.length * 2 * Math.PI - Math.PI / 2;
2627
- const r = offsets[i] ?? 1;
2628
- const px0 = cx + rx * r * Math.cos(a);
2629
- const py0 = cy + ry * r * Math.sin(a);
2630
- points.push(`${i === 0 ? "M" : "L"}${px0},${py0}`);
2631
- }
2632
- points.push("Z");
2633
- return points.join(" ");
2634
- },
2635
- irregularSeal2: (x, y, w, h) => {
2636
- const cx = x + w / 2;
2637
- const cy = y + h / 2;
2638
- const rx = w / 2;
2639
- const ry = h / 2;
2640
- const offsets = [
2641
- 1,
2642
- .4,
2643
- .95,
2644
- .5,
2645
- .9,
2646
- .35,
2647
- .85,
2648
- .45,
2649
- 1,
2650
- .4,
2651
- .95,
2652
- .5,
2653
- .9,
2654
- .35,
2655
- .85,
2656
- .45,
2657
- 1,
2658
- .4,
2659
- .95,
2660
- .5,
2661
- .9,
2662
- .35,
2663
- .85,
2664
- .45
2665
- ];
2666
- const points = [];
2667
- for (let i = 0; i < offsets.length; i++) {
2668
- const a = i / offsets.length * 2 * Math.PI - Math.PI / 2;
2669
- const r = offsets[i] ?? 1;
2670
- const px0 = cx + rx * r * Math.cos(a);
2671
- const py0 = cy + ry * r * Math.sin(a);
2672
- points.push(`${i === 0 ? "M" : "L"}${px0},${py0}`);
2673
- }
2674
- points.push("Z");
2675
- return points.join(" ");
2676
- },
2677
- cloud: (x, y, w, h) => {
2678
- const cx = x + w / 2;
2679
- const cy = y + h / 2;
2680
- const rx = w / 2 * .92;
2681
- const ry = h / 2 * .85;
2682
- const lobes = 10;
2683
- const path = [];
2684
- for (let i = 0; i < lobes; i++) {
2685
- const a = i / lobes * 2 * Math.PI - Math.PI / 2;
2686
- const lobeRx = rx * .32;
2687
- const lobeRy = ry * .32;
2688
- const px0 = cx + rx * Math.cos(a);
2689
- const py0 = cy + ry * Math.sin(a);
2690
- if (i === 0) path.push(`M${px0 - lobeRx},${py0}`);
2691
- path.push(`A${lobeRx},${lobeRy} 0 1 1 ${px0 + lobeRx},${py0}`);
2692
- const nextA = (i + 1) / lobes * 2 * Math.PI - Math.PI / 2;
2693
- const nextX = cx + rx * Math.cos(nextA) - lobeRx;
2694
- const nextY = cy + ry * Math.sin(nextA);
2695
- path.push(`L${nextX},${nextY}`);
2696
- }
2697
- path.push("Z");
2698
- return path.join(" ");
2699
- },
2700
- pie: (x, y, w, h) => {
2701
- const cx = x + w / 2;
2702
- const cy = y + h / 2;
2703
- const r = Math.min(w, h) / 2;
2704
- return `M${cx},${cy} L${cx + r},${cy} A${r},${r} 0 1 1 ${cx},${cy - r} Z`;
2705
- },
2706
- chord: (x, y, w, h) => {
2707
- const cx = x + w / 2;
2708
- const cy = y + h / 2;
2709
- const rx = w / 2;
2710
- const ry = h / 2;
2711
- return `M${cx + rx},${cy} A${rx},${ry} 0 1 1 ${cx - rx},${cy} L${cx + rx},${cy} Z`;
2712
- },
2713
- teardrop: (x, y, w, h) => {
2714
- const cx = x + w / 2;
2715
- const cy = y + h / 2;
2716
- const rx = w / 2;
2717
- const ry = h / 2;
2718
- 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`;
2719
- },
2720
- arc: (x, y, w, h) => {
2721
- const cx = x + w / 2;
2722
- const cy = y + h / 2;
2723
- const rx = w / 2;
2724
- const ry = h / 2;
2725
- return `M${cx + rx},${cy} A${rx},${ry} 0 1 1 ${cx},${cy + ry}`;
2726
- },
2727
- blockArc: (x, y, w, h) => {
2728
- const cx = x + w / 2;
2729
- const cy = y + h / 2;
2730
- const outerR = Math.min(w, h) / 2;
2731
- const innerR = outerR * .6;
2732
- 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`;
2733
- },
2734
- moon: (x, y, w, h) => {
2735
- const cx = x + w / 2;
2736
- const cy = y + h / 2;
2737
- const rx = w / 2;
2738
- const ry = h / 2;
2739
- const innerRx = rx * .78;
2740
- const offsetX = rx * .32;
2741
- 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 * .95} 0 1 1 ${cx - rx + offsetX - innerRx},${cy} A${innerRx},${ry * .95} 0 1 1 ${cx - rx + offsetX + innerRx},${cy}`;
2742
- },
2743
- plus: (x, y, w, h) => {
2744
- const t = Math.min(w, h) * .3;
2745
- const cx = x + w / 2;
2746
- const cy = y + h / 2;
2747
- 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`;
2748
- },
2749
- plaque: (x, y, w, h) => {
2750
- const r = Math.min(w, h) * .18;
2751
- 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`;
2752
- },
2753
- can: (x, y, w, h) => {
2754
- const er = h * .12;
2755
- 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}`;
2756
- },
2757
- cube: (x, y, w, h) => {
2758
- const d = Math.min(w, h) * .2;
2759
- 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}`;
2760
- },
2761
- bevel: (x, y, w, h) => {
2762
- const d = Math.min(w, h) * .12;
2763
- 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}`;
2764
- },
2765
- donut: (x, y, w, h) => {
2766
- const cx = x + w / 2;
2767
- const cy = y + h / 2;
2768
- const r = Math.min(w, h) / 2;
2769
- const innerR = r * .65;
2770
- 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`;
2771
- },
2772
- noSmoking: (x, y, w, h) => {
2773
- const cx = x + w / 2;
2774
- const cy = y + h / 2;
2775
- const r = Math.min(w, h) / 2;
2776
- const innerR = r * .78;
2777
- const t = r * .12;
2778
- 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 * .71 - t},${cy - r * .71 + t} L${cx - r * .71 + t},${cy - r * .71 - t} L${cx + r * .71 + t},${cy + r * .71 - t} L${cx + r * .71 - t},${cy + r * .71 + t} Z`;
2779
- },
2780
- frame: (x, y, w, h) => {
2781
- const f = Math.min(w, h) * .1;
2782
- 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`;
2783
- },
2784
- halfFrame: (x, y, w, h) => {
2785
- const f = Math.min(w, h) * .15;
2786
- 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`;
2787
- },
2788
- corner: (x, y, w, h) => {
2789
- const tx = w * .4;
2790
- const ty = h * .4;
2791
- 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`;
2792
- },
2793
- diagStripe: (x, y, w, h) => {
2794
- return `M${x},${y} L${x + w * .6},${y} L${x},${y + h * .6} Z`;
2795
- },
2796
- ellipseRibbon: (x, y, w, h) => {
2797
- const notch = w * .08;
2798
- const bodyTop = y + h * .2;
2799
- const bodyBot = y + h * .85;
2800
- const arcDip = h * .15;
2801
- return `M${x},${bodyTop} C${x + w * .3},${bodyTop - arcDip} ${x + w * .7},${bodyTop - arcDip} ${x + w},${bodyTop} L${x + w * .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 * .7},${bodyBot + arcDip * .4} ${x + w * .3},${bodyBot + arcDip * .4} L${x + notch * 4},${bodyBot} L${x + notch * 2},${y + h} L${x},${bodyBot} L${x + w * .15},${bodyTop + (bodyBot - bodyTop) / 2} Z`;
2802
- },
2803
- ellipseRibbon2: (x, y, w, h) => {
2804
- const notch = w * .08;
2805
- const bodyTop = y + h * .15;
2806
- const bodyBot = y + h * .8;
2807
- const arcRise = h * .15;
2808
- return `M${x},${bodyBot} C${x + w * .3},${bodyBot + arcRise} ${x + w * .7},${bodyBot + arcRise} ${x + w},${bodyBot} L${x + w * .85},${bodyTop + (bodyBot - bodyTop) / 2} L${x + w},${bodyTop} L${x + w - notch * 2},${y} L${x + w - notch * 4},${bodyTop} C${x + w * .7},${bodyTop - arcRise * .4} ${x + w * .3},${bodyTop - arcRise * .4} L${x + notch * 4},${bodyTop} L${x + notch * 2},${y} L${x},${bodyTop} L${x + w * .15},${bodyTop + (bodyBot - bodyTop) / 2} Z`;
2809
- },
2810
- quadArrow: (x, y, w, h) => {
2811
- const cx = x + w / 2;
2812
- const cy = y + h / 2;
2813
- const tip = .2;
2814
- const stem = .15;
2815
- const headW = .35;
2816
- 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`;
2817
- },
2818
- leftRightUpArrow: (x, y, w, h) => {
2819
- const cx = x + w / 2;
2820
- const tip = .2;
2821
- const stem = .15;
2822
- const headW = .35;
2823
- 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`;
2824
- },
2825
- bentUpArrow: (x, y, w, h) => {
2826
- const stem = .3;
2827
- const tip = .25;
2828
- return `M${x},${y + h * .55} L${x + w * .5},${y + h * .55} L${x + w * .5},${y + tip * h} L${x + w * (.5 - stem * .5)},${y + tip * h} L${x + w * .75},${y} L${x + w},${y + tip * h} L${x + w * .65},${y + tip * h} L${x + w * .65},${y + h * .55 + h * .4} L${x},${y + h * .55 + h * .4} Z`;
2829
- },
2830
- curvedLeftArrow: (x, y, w, h) => {
2831
- return `M${x + w},${y + h * .6} Q${x + w * .5},${y} ${x + w * .15},${y + h * .25} L${x},${y + h * .45} L${x + w * .15},${y + h * .55} L${x + w * .3},${y + h * .35} Q${x + w * .55},${y + h * .18} ${x + w * .85},${y + h * .75} Z`;
2832
- },
2833
- curvedUpArrow: (x, y, w, h) => {
2834
- return `M${x + w * .4},${y + h} Q${x},${y + h * .5} ${x + w * .25},${y + h * .15} L${x + w * .45},${y} L${x + w * .55},${y + h * .15} L${x + w * .35},${y + h * .3} Q${x + w * .18},${y + h * .55} ${x + w * .75},${y + h * .85} Z`;
2835
- },
2836
- curvedDownArrow: (x, y, w, h) => {
2837
- return `M${x + w * .4},${y} Q${x},${y + h * .5} ${x + w * .25},${y + h * .85} L${x + w * .45},${y + h} L${x + w * .55},${y + h * .85} L${x + w * .35},${y + h * .7} Q${x + w * .18},${y + h * .45} ${x + w * .75},${y + h * .15} Z`;
2838
- },
2839
- swooshArrow: (x, y, w, h) => {
2840
- return `M${x},${y + h * .75} C${x + w * .35},${y + h} ${x + w * .65},${y + h * .3} ${x + w * .75},${y + h * .2} L${x + w * .65},${y + h * .05} L${x + w},${y + h * .18} L${x + w * .78},${y + h * .45} L${x + w * .7},${y + h * .3} C${x + w * .55},${y + h * .6} ${x + w * .35},${y + h * .9} ${x},${y + h * .85} Z`;
2841
- },
2842
- circularArrow: (x, y, w, h) => {
2843
- const cx = x + w / 2;
2844
- const cy = y + h / 2;
2845
- const outerR = Math.min(w, h) / 2;
2846
- const innerR = outerR * .62;
2847
- const midR = (outerR + innerR) / 2;
2848
- return `M${cx},${cy - outerR} A${outerR},${outerR} 0 1 1 ${cx - outerR},${cy} L${cx - midR - midR * .25},${cy + outerR * .15} L${cx - midR + midR * .25},${cy + outerR * .3} L${cx - innerR},${cy} A${innerR},${innerR} 0 1 0 ${cx},${cy - innerR} Z`;
2849
- },
2850
- leftCircularArrow: (x, y, w, h) => {
2851
- const cx = x + w / 2;
2852
- const cy = y + h / 2;
2853
- const outerR = Math.min(w, h) / 2;
2854
- const innerR = outerR * .62;
2855
- return `M${cx},${cy - outerR} A${outerR},${outerR} 0 1 0 ${cx + outerR},${cy} L${cx + (outerR + innerR) / 2 + (outerR + innerR) / 2 * .25},${cy + outerR * .15} L${cx + (outerR + innerR) / 2 - (outerR + innerR) / 2 * .25},${cy + outerR * .3} L${cx + innerR},${cy} A${innerR},${innerR} 0 1 1 ${cx},${cy - innerR} Z`;
2856
- },
2857
- leftRightCircularArrow: (x, y, w, h) => {
2858
- const cx = x + w / 2;
2859
- const cy = y + h / 2;
2860
- const outerR = Math.min(w, h) / 2;
2861
- const innerR = outerR * .62;
2862
- return `M${cx - outerR * .15},${cy - outerR * 1.05} L${cx + outerR * .15},${cy - outerR * 1.05} L${cx + outerR * .1},${cy - outerR * .85} A${outerR},${outerR} 0 1 1 ${cx - outerR * .1},${cy - outerR * .85} Z M${cx},${cy - innerR} A${innerR},${innerR} 0 1 0 ${cx},${cy + innerR}`;
2863
- },
2864
- rightArrowCallout: (x, y, w, h) => {
2865
- const headW = w * .25;
2866
- return `M${x},${y + h * .3} L${x + w - headW},${y + h * .3} L${x + w - headW},${y} L${x + w},${y + h / 2} L${x + w - headW},${y + h} L${x + w - headW},${y + h * .7} L${x},${y + h * .7} Z`;
2867
- },
2868
- leftArrowCallout: (x, y, w, h) => {
2869
- const headW = w * .25;
2870
- return `M${x + headW},${y + h * .3} L${x + w},${y + h * .3} L${x + w},${y + h * .7} L${x + headW},${y + h * .7} L${x + headW},${y + h} L${x},${y + h / 2} L${x + headW},${y} Z`;
2871
- },
2872
- upArrowCallout: (x, y, w, h) => {
2873
- const headH = h * .25;
2874
- return `M${x + w * .3},${y + headH} L${x + w * .3},${y + h} L${x + w * .7},${y + h} L${x + w * .7},${y + headH} L${x + w},${y + headH} L${x + w / 2},${y} L${x},${y + headH} Z`;
2875
- },
2876
- downArrowCallout: (x, y, w, h) => {
2877
- const headH = h * .25;
2878
- return `M${x + w * .3},${y} L${x + w * .7},${y} L${x + w * .7},${y + h - headH} L${x + w},${y + h - headH} L${x + w / 2},${y + h} L${x},${y + h - headH} L${x + w * .3},${y + h - headH} Z`;
2879
- },
2880
- leftRightArrowCallout: (x, y, w, h) => {
2881
- const headW = w * .2;
2882
- return `M${x},${y + h / 2} L${x + headW},${y} L${x + headW},${y + h * .3} L${x + w - headW},${y + h * .3} L${x + w - headW},${y} L${x + w},${y + h / 2} L${x + w - headW},${y + h} L${x + w - headW},${y + h * .7} L${x + headW},${y + h * .7} L${x + headW},${y + h} Z`;
2883
- },
2884
- upDownArrowCallout: (x, y, w, h) => {
2885
- const headH = h * .2;
2886
- return `M${x + w / 2},${y} L${x},${y + headH} L${x + w * .3},${y + headH} L${x + w * .3},${y + h - headH} L${x},${y + h - headH} L${x + w / 2},${y + h} L${x + w},${y + h - headH} L${x + w * .7},${y + h - headH} L${x + w * .7},${y + headH} L${x + w},${y + headH} Z`;
2887
- },
2888
- quadArrowCallout: (x, y, w, h) => {
2889
- const cx = x + w / 2;
2890
- const cy = y + h / 2;
2891
- const tip = .16;
2892
- const stem = .18;
2893
- const headW = .32;
2894
- const txt = .28;
2895
- 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`;
2896
- },
2897
- actionButtonHome: (x, y, w, h) => {
2898
- const cx = x + w / 2;
2899
- const cy = y + h / 2;
2900
- const s = Math.min(w, h) * .3;
2901
- return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s},${cy + s * .6} L${cx - s},${cy - s * .1} L${cx},${cy - s * .7} L${cx + s},${cy - s * .1} L${cx + s},${cy + s * .6} L${cx + s * .3},${cy + s * .6} L${cx + s * .3},${cy + s * .1} L${cx - s * .3},${cy + s * .1} L${cx - s * .3},${cy + s * .6} Z`;
2902
- },
2903
- actionButtonForwardNext: (x, y, w, h) => {
2904
- const cx = x + w / 2;
2905
- const cy = y + h / 2;
2906
- const s = Math.min(w, h) * .3;
2907
- return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s * .7},${cy - s} L${cx + s * .7},${cy} L${cx - s * .7},${cy + s} Z`;
2908
- },
2909
- actionButtonBackPrevious: (x, y, w, h) => {
2910
- const cx = x + w / 2;
2911
- const cy = y + h / 2;
2912
- const s = Math.min(w, h) * .3;
2913
- return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx + s * .7},${cy - s} L${cx - s * .7},${cy} L${cx + s * .7},${cy + s} Z`;
2914
- },
2915
- actionButtonEnd: (x, y, w, h) => {
2916
- const cx = x + w / 2;
2917
- const cy = y + h / 2;
2918
- const s = Math.min(w, h) * .3;
2919
- return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s},${cy - s} L${cx + s * .4},${cy} L${cx - s},${cy + s} Z M${cx + s * .5},${cy - s} L${cx + s},${cy - s} L${cx + s},${cy + s} L${cx + s * .5},${cy + s} Z`;
2920
- },
2921
- actionButtonBeginning: (x, y, w, h) => {
2922
- const cx = x + w / 2;
2923
- const cy = y + h / 2;
2924
- const s = Math.min(w, h) * .3;
2925
- return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx + s},${cy - s} L${cx - s * .4},${cy} L${cx + s},${cy + s} Z M${cx - s * .5},${cy - s} L${cx - s},${cy - s} L${cx - s},${cy + s} L${cx - s * .5},${cy + s} Z`;
2926
- },
2927
- actionButtonReturn: (x, y, w, h) => {
2928
- const cx = x + w / 2;
2929
- const cy = y + h / 2;
2930
- const s = Math.min(w, h) * .28;
2931
- return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx + s},${cy - s} L${cx + s * .4},${cy - s} L${cx + s * .4},${cy + s * .2} L${cx - s * .2},${cy + s * .2} L${cx - s * .2},${cy - s * .2} L${cx - s},${cy + s * .2} L${cx - s * .2},${cy + s} L${cx - s * .2},${cy + s * .5} L${cx + s},${cy + s * .5} Z`;
2932
- },
2933
- actionButtonHelp: (x, y, w, h) => {
2934
- const cx = x + w / 2;
2935
- const cy = y + h / 2;
2936
- const s = Math.min(w, h) * .3;
2937
- return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s * .4},${cy - s * .4} Q${cx - s * .4},${cy - s} ${cx},${cy - s} Q${cx + s * .4},${cy - s} ${cx + s * .4},${cy - s * .4} Q${cx + s * .4},${cy} ${cx},${cy} L${cx},${cy + s * .4} M${cx - s * .18},${cy + s * .8} L${cx + s * .18},${cy + s * .8} L${cx + s * .18},${cy + s} L${cx - s * .18},${cy + s} Z`;
2938
- },
2939
- actionButtonInformation: (x, y, w, h) => {
2940
- const cx = x + w / 2;
2941
- const cy = y + h / 2;
2942
- const s = Math.min(w, h) * .3;
2943
- return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s * .2},${cy - s * .7} L${cx + s * .2},${cy - s * .7} L${cx + s * .2},${cy - s * .35} L${cx - s * .2},${cy - s * .35} Z M${cx - s * .2},${cy - s * .1} L${cx + s * .2},${cy - s * .1} L${cx + s * .2},${cy + s} L${cx - s * .2},${cy + s} Z`;
2944
- },
2945
- actionButtonDocument: (x, y, w, h) => {
2946
- const cx = x + w / 2;
2947
- const cy = y + h / 2;
2948
- const s = Math.min(w, h) * .3;
2949
- return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s * .6},${cy - s} L${cx + s * .3},${cy - s} L${cx + s * .6},${cy - s * .7} L${cx + s * .6},${cy + s} L${cx - s * .6},${cy + s} Z M${cx + s * .3},${cy - s} L${cx + s * .3},${cy - s * .7} L${cx + s * .6},${cy - s * .7}`;
2950
- },
2951
- actionButtonSound: (x, y, w, h) => {
2952
- const cx = x + w / 2;
2953
- const cy = y + h / 2;
2954
- const s = Math.min(w, h) * .3;
2955
- return `${PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""} M${cx - s},${cy - s * .4} L${cx - s * .3},${cy - s * .4} L${cx + s * .3},${cy - s} L${cx + s * .3},${cy + s} L${cx - s * .3},${cy + s * .4} L${cx - s},${cy + s * .4} Z M${cx + s * .55},${cy - s * .4} Q${cx + s * .95},${cy} ${cx + s * .55},${cy + s * .4}`;
2956
- },
2957
- actionButtonMovie: (x, y, w, h) => {
2958
- const cx = x + w / 2;
2959
- const cy = y + h / 2;
2960
- const s = Math.min(w, h) * .3;
2961
- const out = [PRESET_PATHS.actionButtonBlank?.(x, y, w, h) ?? ""];
2962
- out.push(`M${cx - s},${cy - s * .6} L${cx + s},${cy - s * .6} L${cx + s},${cy + s * .6} L${cx - s},${cy + s * .6} Z`);
2963
- for (let i = 0; i < 4; i++) {
2964
- const px0 = cx - s + (i + .5) * (s * 2 / 4);
2965
- out.push(`M${px0 - s * .08},${cy - s * .45} L${px0 + s * .08},${cy - s * .45} L${px0 + s * .08},${cy - s * .3} L${px0 - s * .08},${cy - s * .3} Z`);
2966
- out.push(`M${px0 - s * .08},${cy + s * .3} L${px0 + s * .08},${cy + s * .3} L${px0 + s * .08},${cy + s * .45} L${px0 - s * .08},${cy + s * .45} Z`);
2967
- }
2968
- return out.join(" ");
2969
- },
2970
- borderCallout1: (x, y, w, h) => {
2971
- return `M${x},${y} L${x + w * .6},${y} L${x + w * .6},${y + h * .5} L${x},${y + h * .5} Z M${x + w * .6},${y + h * .5} L${x + w},${y + h}`;
2972
- },
2973
- borderCallout2: (x, y, w, h) => {
2974
- return `M${x},${y} L${x + w * .6},${y} L${x + w * .6},${y + h * .5} L${x},${y + h * .5} Z M${x + w * .6},${y + h * .5} L${x + w * .85},${y + h * .75} L${x + w},${y + h}`;
2975
- },
2976
- borderCallout3: (x, y, w, h) => {
2977
- return `M${x},${y} L${x + w * .6},${y} L${x + w * .6},${y + h * .5} L${x},${y + h * .5} Z M${x + w * .6},${y + h * .5} L${x + w * .75},${y + h * .65} L${x + w * .9},${y + h * .65} L${x + w},${y + h}`;
2978
- },
2979
- accentCallout1: (x, y, w, h) => {
2980
- return `M${x},${y} L${x + w * .6},${y} L${x + w * .6},${y + h * .5} L${x},${y + h * .5} Z M${x + w * .58},${y} L${x + w * .58},${y + h * .5} M${x + w * .6},${y + h * .5} L${x + w},${y + h}`;
2981
- },
2982
- accentBorderCallout1: (x, y, w, h) => {
2983
- return PRESET_PATHS.accentCallout1?.(x, y, w, h) ?? "";
2984
- },
2985
- callout1: (x, y, w, h) => {
2986
- return `M${x},${y + h * .5} L${x + w},${y + h}`;
2987
- },
2988
- callout2: (x, y, w, h) => {
2989
- return `M${x},${y + h * .5} L${x + w * .6},${y + h * .7} L${x + w},${y + h}`;
2990
- },
2991
- callout3: (x, y, w, h) => {
2992
- return `M${x},${y + h * .5} L${x + w * .4},${y + h * .55} L${x + w * .7},${y + h * .8} L${x + w},${y + h}`;
2993
- },
2994
- straightConnector1: (x, y, w, h) => `M${x},${y} L${x + w},${y + h}`,
2995
- bentConnector2: (x, y, w, h) => `M${x},${y} L${x + w},${y} L${x + w},${y + h}`,
2996
- bentConnector3: (x, y, w, h) => `M${x},${y} L${x + w / 2},${y} L${x + w / 2},${y + h} L${x + w},${y + h}`,
2997
- bentConnector4: (x, y, w, h) => `M${x},${y} L${x + w * .33},${y} L${x + w * .33},${y + h * .5} L${x + w * .66},${y + h * .5} L${x + w * .66},${y + h} L${x + w},${y + h}`,
2998
- bentConnector5: (x, y, w, h) => `M${x},${y} L${x + w * .25},${y} L${x + w * .25},${y + h * .5} L${x + w * .75},${y + h * .5} L${x + w * .75},${y + h} L${x + w},${y + h}`,
2999
- curvedConnector2: (x, y, w, h) => `M${x},${y} Q${x + w},${y} ${x + w},${y + h}`,
3000
- curvedConnector3: (x, y, w, h) => `M${x},${y} C${x + w * .5},${y} ${x + w * .5},${y + h} ${x + w},${y + h}`,
3001
- curvedConnector4: (x, y, w, h) => `M${x},${y} C${x + w * .33},${y} ${x + w * .33},${y + h * .5} ${x + w * .5},${y + h * .5} C${x + w * .66},${y + h * .5} ${x + w * .66},${y + h} ${x + w},${y + h}`,
3002
- curvedConnector5: (x, y, w, h) => `M${x},${y} C${x + w * .25},${y} ${x + w * .25},${y + h * .25} ${x + w * .5},${y + h * .5} C${x + w * .75},${y + h * .75} ${x + w * .75},${y + h} ${x + w},${y + h}`
3003
- };
3004
2398
  const ALIGNMENT_TO_CSS = {
3005
2399
  left: "left",
3006
2400
  center: "center",
@@ -3032,7 +2426,7 @@ const cssColorWithOpacity = (hex, opacity) => {
3032
2426
  };
3033
2427
  const renderRun = (text, format, theme, effectivePt, defaultColor, _wasDefault = false, suppressLineHeight = false, lineHeightOverride = void 0) => {
3034
2428
  if (text === "") return "";
3035
- if (format?.textFill) {
2429
+ if (format?.textFill || format?.outline?.fill) {
3036
2430
  const { textFill: _fill, outline: _outline, shadow: _shadow, glow: _glow, underline: _underline, strike: _strike, ...layout } = format;
3037
2431
  return renderRun(text, {
3038
2432
  ...layout,
@@ -3092,6 +2486,10 @@ const renderRun = (text, format, theme, effectivePt, defaultColor, _wasDefault =
3092
2486
  const LINE_HEIGHT = 1.05;
3093
2487
  const AUTOFIT_FLOOR = .25;
3094
2488
  const AUTOFIT_STEP = .05;
2489
+ const decimalSeparatorField = (lang) => {
2490
+ const separator = decimalSeparatorOf(lang);
2491
+ return separator === "." ? {} : { decimalSeparator: separator };
2492
+ };
3095
2493
  const underlineStyleOf = (fmt) => {
3096
2494
  const u = fmt?.underline;
3097
2495
  if (u === void 0 || u === false || u === "none") return "none";
@@ -3121,7 +2519,7 @@ const strikeStyleOf = (fmt) => {
3121
2519
  if (s === "dblStrike") return "double";
3122
2520
  return s !== void 0 && s !== false && s !== "noStrike";
3123
2521
  };
3124
- const hasPaintedTextFill = (run) => !!run.fmt?.textFill && !run.href;
2522
+ const hasPaintedTextFill = (run) => !!(run.fmt?.textFill || run.fmt?.outline?.fill) && !run.href;
3125
2523
  const alignOf = (a) => a === "center" || a === "right" || a === "justify" ? a : "left";
3126
2524
  const verticalLayoutOf = (vert) => {
3127
2525
  switch (vert) {
@@ -3199,7 +2597,10 @@ const buildSvgTextInput = (a) => {
3199
2597
  letterSpacingPx,
3200
2598
  kerning,
3201
2599
  fillHex,
3202
- ...fmt?.textFill && !run.href ? { fillPaint: textFillPaint(fmt.textFill, a.theme) } : {},
2600
+ ...hasPaintedTextFill(run) ? { fillPaint: fmt?.textFill ? textFillPaint(fmt.textFill, a.theme, fillHex) : () => ({
2601
+ defs: "",
2602
+ fill: fillHex
2603
+ }) } : {},
3203
2604
  ...fmt?.underlineColor !== void 0 && fmt.underlineColor !== null ? { underlineHex: resolveColor(fmt.underlineColor, a.theme, fillHex) } : {},
3204
2605
  underline: underlineStyleOf(fmt),
3205
2606
  strike: strikeStyleOf(fmt),
@@ -3208,6 +2609,21 @@ const buildSvgTextInput = (a) => {
3208
2609
  outlineHex: resolveColor(fmt.outline.color, a.theme, "#000000"),
3209
2610
  outlineWidthPx: (fmt.outline.widthEmu ?? 9525) / EMU_PER_PX
3210
2611
  } : {},
2612
+ ...fmt?.outline?.fill && !run.href ? (() => {
2613
+ const fill = fmt.outline.fill;
2614
+ const first = fill.stops[0];
2615
+ return {
2616
+ outlineHex: first ? first.resolvedColor ?? resolveColor(first.color, a.theme, "#000000") : "#000000",
2617
+ outlinePaint: (box) => {
2618
+ const built = gradientDef(fill, a.theme, "", box);
2619
+ return {
2620
+ defs: built.defs,
2621
+ fill: built.fillAttr
2622
+ };
2623
+ },
2624
+ outlineWidthPx: (fmt.outline.widthEmu ?? 9525) / EMU_PER_PX
2625
+ };
2626
+ })() : {},
3211
2627
  ...fmt?.shadow ? { shadow: {
3212
2628
  ...fmt.shadow,
3213
2629
  ...fmt.shadow.color ? { color: resolveColor(fmt.shadow.color, a.theme, "#000000") } : {}
@@ -3222,8 +2638,10 @@ const buildSvgTextInput = (a) => {
3222
2638
  ...fmt.innerShadow,
3223
2639
  ...fmt.innerShadow.color ? { color: resolveColor(fmt.innerShadow.color, a.theme, "#000000") } : {}
3224
2640
  } } : {},
2641
+ ...a.bevel ? { bevel: a.bevel } : {},
3225
2642
  href: run.href ?? null,
3226
- ...run.hrefTip !== void 0 ? { hrefTip: run.hrefTip } : {}
2643
+ ...run.hrefTip !== void 0 ? { hrefTip: run.hrefTip } : {},
2644
+ ...run.decimalSeparator !== void 0 ? { decimalSeparator: run.decimalSeparator } : {}
3227
2645
  };
3228
2646
  const segs = run.text.split("\n");
3229
2647
  segs.forEach((seg, i) => {
@@ -3373,12 +2791,12 @@ const resolveTextBodyModel = (pres, shape, bounds, theme, phType, measure, defau
3373
2791
  const tIns = margins.top ?? DEFAULT_INSET_Y;
3374
2792
  const rIns = margins.right ?? DEFAULT_INSET_X;
3375
2793
  const bIns = margins.bottom ?? DEFAULT_INSET_Y;
3376
- const { x: innerX, y: innerY, w: innerW, h: innerH } = resolveTextBodyRect(getShapePreset(shape), bounds, {
2794
+ const { x: innerX, y: innerY, w: innerW, h: innerH } = resolveTextBodyRect(bounds, {
3377
2795
  left: lIns,
3378
2796
  top: tIns,
3379
2797
  right: rIns,
3380
2798
  bottom: bIns
3381
- }, shapeCustomTextRect(getShapeCustomGeometry(shape), getShapeBounds(shape)));
2799
+ }, shapeTextRect(shape));
3382
2800
  if (innerW <= 0 || innerH <= 0) return null;
3383
2801
  const svgTextRect = (v) => useSvgTextRect && v !== "none" && v !== "upright" ? {
3384
2802
  x: bounds.x + tIns,
@@ -3455,10 +2873,11 @@ const resolveTextBodyModel = (pres, shape, bounds, theme, phType, measure, defau
3455
2873
  } catch {
3456
2874
  elements = [];
3457
2875
  }
2876
+ const languages = elements.length ? getParagraphElementLanguages(shape, p) : [];
3458
2877
  let rIdx = 0;
3459
2878
  let fieldIndex = 0;
3460
2879
  let breakIndex = 0;
3461
- for (const el of elements) {
2880
+ for (const [elementIndex, el] of elements.entries()) {
3462
2881
  if (el.kind === "br") {
3463
2882
  const fmt = getShapeRunFormatEffective(pres, shape, p, { breakIndex: breakIndex++ });
3464
2883
  runs.push({
@@ -3493,6 +2912,7 @@ const resolveTextBodyModel = (pres, shape, bounds, theme, phType, measure, defau
3493
2912
  text: txt,
3494
2913
  fmt,
3495
2914
  sizePt,
2915
+ ...decimalSeparatorField(languages[elementIndex]),
3496
2916
  ...href !== void 0 ? { href } : {},
3497
2917
  ...hrefTip !== void 0 ? { hrefTip } : {}
3498
2918
  });
@@ -3632,9 +3052,15 @@ const renderHtmlParagraphs = (paraData, numberLabels, theme, autoFitScale, defau
3632
3052
  let runFmt = run.fmt;
3633
3053
  if (run.href) {
3634
3054
  const hlinkColor = theme ? normalizeHex(theme.hyperlink) : "#0563C1";
3635
- const { textFill: _linkFill, ...linkFmt } = runFmt ?? {};
3055
+ const { textFill: _linkFill, outline: linkOutline, ...linkFmt } = runFmt ?? {};
3056
+ const { fill: outlineFill, ...solidOutline } = linkOutline ?? {};
3057
+ const outlineColor = outlineFill?.stops[0]?.color ?? solidOutline.color;
3636
3058
  runFmt = {
3637
3059
  ...linkFmt,
3060
+ ...linkOutline ? { outline: {
3061
+ ...solidOutline,
3062
+ ...outlineColor !== void 0 ? { color: outlineColor } : {}
3063
+ } } : {},
3638
3064
  color: hlinkColor,
3639
3065
  underline: runFmt?.underline ?? true
3640
3066
  };
@@ -3665,8 +3091,8 @@ const renderHtmlParagraphs = (paraData, numberLabels, theme, autoFitScale, defau
3665
3091
  "padding:0",
3666
3092
  `text-align:${ALIGNMENT_TO_CSS[para.align] ?? "left"}`,
3667
3093
  lineHeightCss,
3668
- leftPx > 0 ? `padding-left:${leftPx.toFixed(2)}px` : "",
3669
- rightPx > 0 ? `padding-right:${rightPx.toFixed(2)}px` : "",
3094
+ leftPx > 0 ? `padding-inline-start:${leftPx.toFixed(2)}px` : "",
3095
+ rightPx > 0 ? `padding-inline-end:${rightPx.toFixed(2)}px` : "",
3670
3096
  firstLinePx !== 0 ? `text-indent:${firstLinePx.toFixed(2)}px` : ""
3671
3097
  ].filter(Boolean);
3672
3098
  let prefix = "";
@@ -3702,6 +3128,7 @@ const renderHtmlParagraphs = (paraData, numberLabels, theme, autoFitScale, defau
3702
3128
  const renderTextBody = (pres, shape, bounds, theme, phType, ctx, output = "body") => {
3703
3129
  const model = resolveTextBodyModel(pres, shape, bounds, theme, phType, ctx.measure, activeDeckTextColor, ctx.mode === "svg" && output === "body");
3704
3130
  if (model === null) return "";
3131
+ const bevel = textBevelOf(getShapeText3D(shape));
3705
3132
  const { paraData, numberLabels, authoredAutofit, autoFitScale, defaultPt, themeFace, effectiveDefaultFont, effectiveBody, anchor, innerX, innerY, innerW, innerH, svgTextRect } = model;
3706
3133
  const defaultColor = activeDeckTextColor;
3707
3134
  const paragraphs = renderHtmlParagraphs(paraData, numberLabels, theme, autoFitScale, defaultPt, defaultColor, effectiveBody.wrap !== "none");
@@ -3737,7 +3164,8 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx, output = "body"
3737
3164
  measure: ctx.mode === "svg" ? ctx.measure : browserTextMeasurer() ?? ctx.measure,
3738
3165
  ...ctx.mode === "foreignObject" ? { resolveFamily: browserFontFamily } : {},
3739
3166
  vert: svgVert,
3740
- columns: svgColumns
3167
+ columns: svgColumns,
3168
+ bevel
3741
3169
  };
3742
3170
  if (output === "effects") {
3743
3171
  const effectArgs = {
@@ -3755,6 +3183,10 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx, output = "body"
3755
3183
  ...effectArgs,
3756
3184
  effectKind: "fill"
3757
3185
  }) : "",
3186
+ bevel ? buildAndLayoutSvgText({
3187
+ ...effectArgs,
3188
+ effectKind: "bevel"
3189
+ }) : "",
3758
3190
  paraData.some((para) => para.runs.some((run) => run.fmt?.innerShadow != null)) ? buildAndLayoutSvgText({
3759
3191
  ...effectArgs,
3760
3192
  effectKind: "innerShadow"
@@ -3791,10 +3223,11 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx, output = "body"
3791
3223
  const content = paragraphs.join("");
3792
3224
  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};${`translate:${model.anchorOffset.x}px ${model.anchorOffset.y}px;`}${wrapStyle}${vertStyles}">${content}</div>`;
3793
3225
  const foreign = `<foreignObject x="${E(innerX)}" y="${E(innerY)}" width="${E(innerW)}" height="${E(innerH)}" overflow="visible">${body}</foreignObject>`;
3794
- const hasCharacterEffect = paraData.some((para) => para.runs.some((run) => run.fmt?.reflection !== void 0 && run.fmt.reflection !== null || run.fmt?.innerShadow !== void 0 && run.fmt.innerShadow !== null || hasPaintedTextFill(run)));
3226
+ const hasCharacterEffect = bevel !== null || paraData.some((para) => para.runs.some((run) => run.fmt?.reflection !== void 0 && run.fmt.reflection !== null || run.fmt?.innerShadow !== void 0 && run.fmt.innerShadow !== null || hasPaintedTextFill(run)));
3795
3227
  let reflectionOverlay = "";
3796
3228
  let fillOverlay = "";
3797
3229
  let innerShadowOverlay = "";
3230
+ let bevelOverlay = "";
3798
3231
  if (hasCharacterEffect) {
3799
3232
  const svgVert = verticalLayoutOf(effectiveBody.vert ?? getShapeTextDirection(shape));
3800
3233
  const { x: rX, y: rY, w: rW, h: rH } = svgTextRect(svgVert);
@@ -3820,7 +3253,8 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx, output = "body"
3820
3253
  vert: svgVert,
3821
3254
  columns: null,
3822
3255
  effectsOnly: true,
3823
- resolveFamily: browserFontFamily
3256
+ resolveFamily: browserFontFamily,
3257
+ bevel
3824
3258
  };
3825
3259
  const hasCharacterReflection = paraData.some((para) => para.runs.some((run) => run.fmt?.reflection !== void 0 && run.fmt.reflection !== null));
3826
3260
  const hasCharacterInnerShadow = paraData.some((para) => para.runs.some((run) => run.fmt?.innerShadow !== void 0 && run.fmt.innerShadow !== null));
@@ -3832,6 +3266,10 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx, output = "body"
3832
3266
  ...effectArgs,
3833
3267
  effectKind: "fill"
3834
3268
  });
3269
+ if (bevel) bevelOverlay = buildAndLayoutSvgText({
3270
+ ...effectArgs,
3271
+ effectKind: "bevel"
3272
+ });
3835
3273
  if (hasCharacterInnerShadow) innerShadowOverlay = buildAndLayoutSvgText({
3836
3274
  ...effectArgs,
3837
3275
  effectKind: "innerShadow"
@@ -3841,10 +3279,10 @@ const renderTextBody = (pres, shape, bounds, theme, phType, ctx, output = "body"
3841
3279
  if (bodyRotDeg !== null && bodyRotDeg !== 0) {
3842
3280
  const pivotX = innerX + innerW / 2;
3843
3281
  const pivotY = innerY + innerH / 2;
3844
- const content = output === "effects" ? `${reflectionOverlay}${fillOverlay}${innerShadowOverlay}` : `${reflectionOverlay}${foreign}${fillOverlay}${innerShadowOverlay}`;
3282
+ const content = output === "effects" ? `${reflectionOverlay}${fillOverlay}${bevelOverlay}${innerShadowOverlay}` : `${reflectionOverlay}${foreign}${fillOverlay}${bevelOverlay}${innerShadowOverlay}`;
3845
3283
  return `<g transform="rotate(${bodyRotDeg} ${E(pivotX)} ${E(pivotY)})">${content}</g>`;
3846
3284
  }
3847
- return output === "effects" ? reflectionOverlay + fillOverlay + innerShadowOverlay : reflectionOverlay + foreign + fillOverlay + innerShadowOverlay;
3285
+ return output === "effects" ? reflectionOverlay + fillOverlay + bevelOverlay + innerShadowOverlay : reflectionOverlay + foreign + fillOverlay + bevelOverlay + innerShadowOverlay;
3848
3286
  };
3849
3287
  /**
3850
3288
  * Paint only character reflection/inner-shadow effects for an editable text
@@ -5313,10 +4751,11 @@ const cellParaData = (paragraphs, cell, pres) => {
5313
4751
  const bulletIsPicture = isParagraphBulletPicture(cell, index);
5314
4752
  const bulletImageBytes = bulletIsPicture ? getParagraphBulletImageBytes(cell, index) : null;
5315
4753
  const runs = [];
4754
+ const languages = getParagraphElementLanguages(cell, index);
5316
4755
  let rIdx = 0;
5317
4756
  let fieldIndex = 0;
5318
4757
  let breakIndex = 0;
5319
- for (const el of para.elements) {
4758
+ for (const [elementIndex, el] of para.elements.entries()) {
5320
4759
  if (el.kind === "br") {
5321
4760
  const fmt = getTableCellRunFormatEffective(pres, cell, index, { breakIndex: breakIndex++ });
5322
4761
  runs.push({
@@ -5333,6 +4772,7 @@ const cellParaData = (paragraphs, cell, pres) => {
5333
4772
  text: el.text,
5334
4773
  fmt,
5335
4774
  sizePt: fmt?.size ?? DEFAULT_BODY_PT,
4775
+ ...decimalSeparatorField(languages[elementIndex]),
5336
4776
  ...href ? {
5337
4777
  href,
5338
4778
  ...el.tooltip !== void 0 ? { hrefTip: el.tooltip } : {}
@@ -5404,13 +4844,19 @@ const renderTableCellText = (cell, paragraphs, cx, cy, cw, ch, color, pres, shap
5404
4844
  ...ctx.mode === "foreignObject" ? { resolveFamily: browserFontFamily } : {},
5405
4845
  vert: verticalLayoutOf(getTableCellTextDirection(cell)),
5406
4846
  columns: null,
5407
- ...effectsOnly ? { effectsOnly: true } : {}
4847
+ ...effectsOnly ? { effectsOnly: true } : {},
4848
+ bevel: textBevelOf(getTableCellText3D(cell))
5408
4849
  };
5409
4850
  const fillOverlay = () => paraData.some((para) => para.runs.some(hasPaintedTextFill)) ? buildAndLayoutSvgText({
5410
4851
  ...svgArgs,
5411
4852
  effectsOnly: true,
5412
4853
  effectKind: "fill"
5413
4854
  }) : "";
4855
+ const bevelOverlay = () => svgArgs.bevel ? buildAndLayoutSvgText({
4856
+ ...svgArgs,
4857
+ effectsOnly: true,
4858
+ effectKind: "bevel"
4859
+ }) : "";
5414
4860
  if (ctx.mode === "svg" || customTabs || effectsOnly) {
5415
4861
  if (effectsOnly) {
5416
4862
  const reflection = paraData.some((para) => para.runs.some((run) => run.fmt?.reflection != null)) ? buildAndLayoutSvgText({
@@ -5421,7 +4867,7 @@ const renderTableCellText = (cell, paragraphs, cx, cy, cw, ch, color, pres, shap
5421
4867
  ...svgArgs,
5422
4868
  effectKind: "innerShadow"
5423
4869
  }) : "";
5424
- return reflection + fillOverlay() + innerShadow;
4870
+ return reflection + fillOverlay() + bevelOverlay() + innerShadow;
5425
4871
  }
5426
4872
  return buildAndLayoutSvgText(svgArgs);
5427
4873
  }
@@ -5430,7 +4876,34 @@ const renderTableCellText = (cell, paragraphs, cx, cy, cw, ch, color, pres, shap
5430
4876
  const justify = vAnchor === "top" ? "flex-start" : vAnchor === "bottom" ? "flex-end" : "center";
5431
4877
  const familyFont = themeFace ? `${escapeXml(themeFace)}, ${DEFAULT_FONT}` : DEFAULT_FONT;
5432
4878
  const body = renderHtmlParagraphs(paraData, numberLabels, theme, 1, DEFAULT_BODY_PT, color).join("");
5433
- 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;line-height:${LINE_HEIGHT};font-family:${familyFont};color:${color};word-break:break-word;${directionStyle}">${body}</div></foreignObject>${fillOverlay()}`;
4879
+ 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;line-height:${LINE_HEIGHT};font-family:${familyFont};color:${color};word-break:break-word;${directionStyle}">${body}</div></foreignObject>${fillOverlay()}${bevelOverlay()}`;
4880
+ };
4881
+ const TWO_STOP_TABLE_GRADIENT_EXPONENT = 1.85;
4882
+ const TWO_STOP_TABLE_GRADIENT_SAMPLES = 16;
4883
+ const twoStopTableGradient = (grad, theme) => {
4884
+ if (grad.path !== void 0 || grad.stops.length !== 2) return grad;
4885
+ const [start, end] = [...grad.stops].sort((a, b) => a.offset - b.offset);
4886
+ if (!start || !end || end.offset <= start.offset) return grad;
4887
+ const span = end.offset - start.offset;
4888
+ const colorOf = (stop) => stop.resolvedColor ?? resolveColor(stop.color, theme, "#E5E7EB");
4889
+ const startColor = colorOf(start);
4890
+ const endColor = colorOf(end);
4891
+ const stops = Array.from({ length: 17 }, (_, i) => {
4892
+ const along = i / TWO_STOP_TABLE_GRADIENT_SAMPLES;
4893
+ const startWeight = (1 - along) ** TWO_STOP_TABLE_GRADIENT_EXPONENT;
4894
+ const color = mixHex(startColor, endColor, startWeight);
4895
+ const opacity = start.opacity === void 0 && end.opacity === void 0 ? void 0 : (start.opacity ?? 1) * startWeight + (end.opacity ?? 1) * (1 - startWeight);
4896
+ return {
4897
+ offset: start.offset + along * span,
4898
+ color,
4899
+ resolvedColor: color,
4900
+ ...opacity === void 0 ? {} : { opacity }
4901
+ };
4902
+ });
4903
+ return {
4904
+ ...grad,
4905
+ stops
4906
+ };
5434
4907
  };
5435
4908
  const renderTable = (shape, pres, x, y, w, h, transform, theme, ctx) => {
5436
4909
  let dims;
@@ -5464,6 +4937,16 @@ const renderTable = (shape, pres, x, y, w, h, transform, theme, ctx) => {
5464
4937
  const tableThemeFace = getPresentationFonts(pres)?.minorLatin ?? null;
5465
4938
  const out = [];
5466
4939
  out.push(`<g${transform}>`);
4940
+ const background = getTableBackgroundEffective(pres, shape);
4941
+ const tableW = (colXs[dims.cols] ?? xPx) - xPx;
4942
+ const tableH = (rowYs[dims.rows] ?? yPx) - yPx;
4943
+ if (background?.fill.kind === "solid") {
4944
+ const opacity = background.fillOpacity !== void 0 && background.fillOpacity < 1 ? ` fill-opacity="${background.fillOpacity.toFixed(3)}"` : "";
4945
+ out.push(`<rect x="${px(xPx)}" y="${px(yPx)}" width="${px(tableW)}" height="${px(tableH)}" fill="${resolveColor(background.fill.color, theme, "#FFFFFF")}"${opacity}/>`);
4946
+ } else if (background?.gradient) {
4947
+ const built = gradientDef(twoStopTableGradient(background.gradient, theme), theme);
4948
+ out.push(`${built.defs}<rect x="${px(xPx)}" y="${px(yPx)}" width="${px(tableW)}" height="${px(tableH)}" fill="${built.fillAttr}"/>`);
4949
+ }
5467
4950
  const borderEdges = [];
5468
4951
  const borderEdgeCandidates = /* @__PURE__ */ new Map();
5469
4952
  for (let r = 0; r < dims.rows; r++) for (let c = 0; c < dims.cols; c++) {
@@ -5486,7 +4969,8 @@ const renderTable = (shape, pres, x, y, w, h, transform, theme, ctx) => {
5486
4969
  else if (fill.kind !== "inherit") resolvedFill = "none";
5487
4970
  else resolvedFill = "none";
5488
4971
  const cellTextColor = textColor;
5489
- out.push(`<g data-pptx-cell="${r},${c}"><rect x="${px(cx)}" y="${px(cy)}" width="${px(cw)}" height="${px(ch)}" fill="${resolvedFill}"/>`);
4972
+ const fillOpacity = fill.kind === "solid" && appearance.fillOpacity !== void 0 && appearance.fillOpacity < 1 ? ` fill-opacity="${appearance.fillOpacity.toFixed(3)}"` : "";
4973
+ out.push(`<g data-pptx-cell="${r},${c}"><rect x="${px(cx)}" y="${px(cy)}" width="${px(cw)}" height="${px(ch)}" fill="${resolvedFill}"${fillOpacity}/>`);
5490
4974
  const borders = appearance.borders;
5491
4975
  const dashAttr = (dash, widthPx) => {
5492
4976
  if (!dash || dash === "solid") return "";
@@ -5547,8 +5031,12 @@ const renderTable = (shape, pres, x, y, w, h, transform, theme, ctx) => {
5547
5031
  */
5548
5032
  const arcToCubicSegments = (startX, startY, wR, hR, stAng, swAng) => {
5549
5033
  const toRad = (a) => a / 6e4 * (Math.PI / 180);
5550
- const st = toRad(stAng);
5551
- const sw = toRad(swAng);
5034
+ const parametric = (visual) => {
5035
+ const offset = Math.atan2(wR * Math.sin(visual), hR * Math.cos(visual)) - visual;
5036
+ return visual + Math.atan2(Math.sin(offset), Math.cos(offset));
5037
+ };
5038
+ const st = parametric(toRad(stAng));
5039
+ const sw = parametric(toRad(stAng + swAng)) - st;
5552
5040
  const cx = startX - wR * Math.cos(st);
5553
5041
  const cy = startY - hR * Math.sin(st);
5554
5042
  const segCount = Math.max(1, Math.ceil(Math.abs(sw) / (Math.PI / 2)));
@@ -5577,75 +5065,237 @@ const arcToCubicSegments = (startX, startY, wR, hR, stAng, swAng) => {
5577
5065
  return segs;
5578
5066
  };
5579
5067
  /**
5580
- * Renders an evaluated {@link CustomGeometry} as one `<path>` per
5581
- * `GeomPath`, scaling each path's coordinate space (its own `w`/`h`, or
5582
- * the shape extents when those are absent) onto the shape's slide box
5583
- * `(x, y, w, h)` in EMU. Returns `''` when nothing drawable remains, so
5584
- * the caller can fall back to the labelled rect.
5585
- *
5586
- * Per-path `fill="none"` paints stroke-only; `stroke="0"` paints
5587
- * fill-only — mirroring `ST_PathFillMode` / the path `stroke` flag.
5068
+ * The SVG path data of one evaluated {@link GeomPath}, scaling its coordinate
5069
+ * space (its own `w`/`h`, or the shape extents when those are absent) onto
5070
+ * the shape's slide box `(x, y, w, h)` in EMU. `''` when nothing is drawable.
5588
5071
  */
5589
- const customGeometryToSvg = (geom, x, y, w, h, fill, fillExtra, stroke, strokeWidthEmu, strokeExtra, markerExtra) => {
5590
- const out = [];
5591
- for (const path of geom.paths) {
5072
+ const geometryPathData = (path, x, y, w, h) => {
5073
+ const cw = path.w ?? w;
5074
+ const ch = path.h ?? h;
5075
+ if (cw === 0 || ch === 0) return "";
5076
+ const sx = w / cw;
5077
+ const sy = h / ch;
5078
+ const fx = (gx) => ((x + gx * sx) / EMU_PER_PX).toFixed(2);
5079
+ const fy = (gy) => ((y + gy * sy) / EMU_PER_PX).toFixed(2);
5080
+ const pt = (gx, gy) => `${fx(gx)},${fy(gy)}`;
5081
+ let curX = 0;
5082
+ let curY = 0;
5083
+ let startX = 0;
5084
+ let startY = 0;
5085
+ const d = [];
5086
+ for (const cmd of path.commands) switch (cmd.kind) {
5087
+ case "moveTo":
5088
+ curX = cmd.pt.x;
5089
+ curY = cmd.pt.y;
5090
+ startX = curX;
5091
+ startY = curY;
5092
+ d.push(`M${pt(curX, curY)}`);
5093
+ break;
5094
+ case "lnTo":
5095
+ curX = cmd.pt.x;
5096
+ curY = cmd.pt.y;
5097
+ d.push(`L${pt(curX, curY)}`);
5098
+ break;
5099
+ case "quadBezTo":
5100
+ d.push(`Q${pt(cmd.pts[0].x, cmd.pts[0].y)} ${pt(cmd.pts[1].x, cmd.pts[1].y)}`);
5101
+ curX = cmd.pts[1].x;
5102
+ curY = cmd.pts[1].y;
5103
+ break;
5104
+ case "cubicBezTo":
5105
+ d.push(`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)}`);
5106
+ curX = cmd.pts[2].x;
5107
+ curY = cmd.pts[2].y;
5108
+ break;
5109
+ case "arcTo": {
5110
+ const segs = arcToCubicSegments(curX, curY, cmd.wR, cmd.hR, cmd.stAng, cmd.swAng);
5111
+ for (const s of segs) d.push(`C${pt(s.c1x, s.c1y)} ${pt(s.c2x, s.c2y)} ${pt(s.ex, s.ey)}`);
5112
+ const last = segs[segs.length - 1];
5113
+ if (last) {
5114
+ curX = last.ex;
5115
+ curY = last.ey;
5116
+ }
5117
+ break;
5118
+ }
5119
+ case "close":
5120
+ d.push("Z");
5121
+ curX = startX;
5122
+ curY = startY;
5123
+ break;
5124
+ }
5125
+ return d.join(" ");
5126
+ };
5127
+ const PATH_SHADE = {
5128
+ darken: "fill=\"#000\" fill-opacity=\"0.4\"",
5129
+ darkenLess: "fill=\"#000\" fill-opacity=\"0.2\"",
5130
+ lighten: "fill=\"#fff\" fill-opacity=\"0.4\"",
5131
+ lightenLess: "fill=\"#fff\" fill-opacity=\"0.2\""
5132
+ };
5133
+ const SKETCH_AMPLITUDE = {
5134
+ curved: .008,
5135
+ freehand: .014,
5136
+ scribble: .022
5137
+ };
5138
+ const SKETCH_STEP = .12;
5139
+ const seededRandom = (seed) => {
5140
+ let state = seed >>> 0;
5141
+ return () => {
5142
+ state = state + 1831565813 >>> 0;
5143
+ let t = state;
5144
+ t = Math.imul(t ^ t >>> 15, t | 1);
5145
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
5146
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
5147
+ };
5148
+ };
5149
+ /**
5150
+ * An approximation of a sketched outline (`ask:lineSketchStyleProps`): every
5151
+ * edge of `geom` is redrawn as a seeded hand-drawn wobble. Only used for a
5152
+ * preset geometry the writer left crisp; sketches the reference desktop app wrote already
5153
+ * carry their hand-drawn path as custom geometry.
5154
+ */
5155
+ const sketchGeometry = (geom, sketch, w, h, seed) => {
5156
+ const random = seededRandom(seed);
5157
+ const jitter = (amp) => (random() * 2 - 1) * amp;
5158
+ const paths = geom.paths.map((path) => {
5592
5159
  const cw = path.w ?? w;
5593
5160
  const ch = path.h ?? h;
5594
- if (cw === 0 || ch === 0) continue;
5595
- const sx = w / cw;
5596
- const sy = h / ch;
5597
- const fx = (gx) => ((x + gx * sx) / EMU_PER_PX).toFixed(2);
5598
- const fy = (gy) => ((y + gy * sy) / EMU_PER_PX).toFixed(2);
5599
- const pt = (gx, gy) => `${fx(gx)},${fy(gy)}`;
5161
+ const size = Math.max(Math.min(cw, ch), Math.max(cw, ch) / 4);
5162
+ const amp = SKETCH_AMPLITUDE[sketch] * size;
5163
+ const step = SKETCH_STEP * Math.max(cw, ch);
5164
+ const nudge = (pt) => ({
5165
+ x: pt.x + jitter(amp),
5166
+ y: pt.y + jitter(amp)
5167
+ });
5168
+ const commands = [];
5600
5169
  let curX = 0;
5601
5170
  let curY = 0;
5602
5171
  let startX = 0;
5603
5172
  let startY = 0;
5604
- const d = [];
5173
+ const wobbleTo = (x, y) => {
5174
+ const dx = x - curX;
5175
+ const dy = y - curY;
5176
+ const len = Math.hypot(dx, dy);
5177
+ const count = step > 0 ? Math.max(1, Math.round(len / step)) : 1;
5178
+ const nx = len > 0 ? -dy / len : 0;
5179
+ const ny = len > 0 ? dx / len : 0;
5180
+ let px = curX;
5181
+ let py = curY;
5182
+ for (let i = 1; i <= count; i++) {
5183
+ const last = i === count;
5184
+ const ex = curX + dx * i / count + (last ? 0 : jitter(amp / 2));
5185
+ const ey = curY + dy * i / count + (last ? 0 : jitter(amp / 2));
5186
+ const bend = jitter(amp);
5187
+ const control = {
5188
+ x: (px + ex) / 2 + nx * bend,
5189
+ y: (py + ey) / 2 + ny * bend
5190
+ };
5191
+ commands.push({
5192
+ kind: "quadBezTo",
5193
+ pts: [control, {
5194
+ x: ex,
5195
+ y: ey
5196
+ }]
5197
+ });
5198
+ px = ex;
5199
+ py = ey;
5200
+ }
5201
+ curX = x;
5202
+ curY = y;
5203
+ };
5605
5204
  for (const cmd of path.commands) switch (cmd.kind) {
5606
5205
  case "moveTo":
5607
5206
  curX = cmd.pt.x;
5608
5207
  curY = cmd.pt.y;
5609
5208
  startX = curX;
5610
5209
  startY = curY;
5611
- d.push(`M${pt(curX, curY)}`);
5210
+ commands.push(cmd);
5612
5211
  break;
5613
5212
  case "lnTo":
5614
- curX = cmd.pt.x;
5615
- curY = cmd.pt.y;
5616
- d.push(`L${pt(curX, curY)}`);
5213
+ wobbleTo(cmd.pt.x, cmd.pt.y);
5617
5214
  break;
5618
5215
  case "quadBezTo":
5619
- d.push(`Q${pt(cmd.pts[0].x, cmd.pts[0].y)} ${pt(cmd.pts[1].x, cmd.pts[1].y)}`);
5216
+ commands.push({
5217
+ kind: "quadBezTo",
5218
+ pts: [nudge(cmd.pts[0]), cmd.pts[1]]
5219
+ });
5620
5220
  curX = cmd.pts[1].x;
5621
5221
  curY = cmd.pts[1].y;
5622
5222
  break;
5623
5223
  case "cubicBezTo":
5624
- d.push(`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)}`);
5224
+ commands.push({
5225
+ kind: "cubicBezTo",
5226
+ pts: [
5227
+ nudge(cmd.pts[0]),
5228
+ nudge(cmd.pts[1]),
5229
+ cmd.pts[2]
5230
+ ]
5231
+ });
5625
5232
  curX = cmd.pts[2].x;
5626
5233
  curY = cmd.pts[2].y;
5627
5234
  break;
5628
- case "arcTo": {
5629
- const segs = arcToCubicSegments(curX, curY, cmd.wR, cmd.hR, cmd.stAng, cmd.swAng);
5630
- for (const s of segs) d.push(`C${pt(s.c1x, s.c1y)} ${pt(s.c2x, s.c2y)} ${pt(s.ex, s.ey)}`);
5631
- const last = segs[segs.length - 1];
5632
- if (last) {
5633
- curX = last.ex;
5634
- curY = last.ey;
5235
+ case "arcTo":
5236
+ for (const s of arcToCubicSegments(curX, curY, cmd.wR, cmd.hR, cmd.stAng, cmd.swAng)) {
5237
+ commands.push({
5238
+ kind: "cubicBezTo",
5239
+ pts: [
5240
+ nudge({
5241
+ x: s.c1x,
5242
+ y: s.c1y
5243
+ }),
5244
+ nudge({
5245
+ x: s.c2x,
5246
+ y: s.c2y
5247
+ }),
5248
+ {
5249
+ x: s.ex,
5250
+ y: s.ey
5251
+ }
5252
+ ]
5253
+ });
5254
+ curX = s.ex;
5255
+ curY = s.ey;
5635
5256
  }
5636
5257
  break;
5637
- }
5638
5258
  case "close":
5639
- d.push("Z");
5259
+ if (curX !== startX || curY !== startY) wobbleTo(startX, startY);
5260
+ commands.push(cmd);
5640
5261
  curX = startX;
5641
5262
  curY = startY;
5642
5263
  break;
5643
5264
  }
5644
- if (d.length === 0) continue;
5645
- const pathFill = path.fill === "none" ? "none" : fill;
5646
- const fillAttrs = path.fill === "none" ? "" : fillExtra;
5265
+ return {
5266
+ ...path,
5267
+ commands
5268
+ };
5269
+ });
5270
+ return {
5271
+ ...geom,
5272
+ paths
5273
+ };
5274
+ };
5275
+ /**
5276
+ * Renders an evaluated {@link CustomGeometry} (a `<a:custGeom>` or a preset
5277
+ * from `getPresetGeometry`) as `<path>`s in the shape's slide box. Returns
5278
+ * `''` when nothing drawable remains, so the caller can fall back to the
5279
+ * labelled rect.
5280
+ *
5281
+ * Per-path `fill="none"` paints stroke-only; `stroke="0"` paints
5282
+ * fill-only — mirroring `ST_PathFillMode` / the path `stroke` flag.
5283
+ */
5284
+ const customGeometryToSvg = (geom, x, y, w, h, fill, fillExtra, stroke, strokeWidthEmu, strokeExtra, markerExtra) => {
5285
+ const out = [];
5286
+ for (const path of geom.paths) {
5287
+ const d = geometryPathData(path, x, y, w, h);
5288
+ if (d === "") continue;
5289
+ const filled = path.fill !== "none" && fill !== "none";
5647
5290
  const strokeAttrs = path.stroke ? ` stroke="${stroke}" stroke-width="${E(strokeWidthEmu)}"${strokeExtra}${markerExtra}` : " stroke=\"none\"";
5648
- out.push(`<path d="${d.join(" ")}" fill="${pathFill}"${fillAttrs}${strokeAttrs} fill-rule="evenodd"/>`);
5291
+ if (filled && path.fill !== "norm") {
5292
+ out.push(`<path d="${d}" fill="${fill}"${fillExtra} stroke="none" fill-rule="evenodd"/>`);
5293
+ out.push(`<path d="${d}" ${PATH_SHADE[path.fill]} stroke="none" fill-rule="evenodd"/>`);
5294
+ if (path.stroke) out.push(`<path d="${d}" fill="none"${strokeAttrs}/>`);
5295
+ continue;
5296
+ }
5297
+ const fillAttrs = filled ? `fill="${fill}"${fillExtra}` : "fill=\"none\"";
5298
+ out.push(`<path d="${d}" ${fillAttrs}${strokeAttrs} fill-rule="evenodd"/>`);
5649
5299
  }
5650
5300
  return out.join("");
5651
5301
  };
@@ -5838,21 +5488,16 @@ const renderShapeContent = (shape, pres, theme, ctx) => {
5838
5488
  const customGeom = rawPreset === null ? getShapeCustomGeometry(shape) : null;
5839
5489
  let geomSvg = customGeom !== null ? customGeometryToSvg(customGeom, x, y, w, h, p.fill, fa, p.stroke, p.strokeWidth, sa, ma) : "";
5840
5490
  const isCustGeom = geomSvg === "" && rawPreset === null && getShapeXmlString(shape).includes("custGeom");
5491
+ const sketch = rawPreset !== null ? getShapeStrokeSketch(shape) : null;
5492
+ if (geomSvg !== "") {} else if (sketch !== null && p.stroke !== "none") {
5493
+ const geometry = getPresetGeometry(preset, bounds, getShapeAdjustValues(shape));
5494
+ if (geometry !== null) geomSvg = customGeometryToSvg(sketchGeometry(geometry, sketch, w, h, getShapeId(shape)), x, y, w, h, p.fill, fa, p.stroke, p.strokeWidth, sa, ma);
5495
+ }
5841
5496
  if (geomSvg !== "") {} else if (preset === "rect") geomSvg = `<rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}" fill="${p.fill}"${fa} stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma}/>`;
5842
- else if (preset === "roundRect") {
5843
- const adjVal = getShapeAdjustValues(shape).adj ?? 16667;
5844
- const ratio = Math.max(0, Math.min(.5, adjVal / 1e5));
5845
- const r = E(Math.min(w, h) * ratio);
5846
- geomSvg = `<rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}" rx="${r}" ry="${r}" fill="${p.fill}"${fa} stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma}/>`;
5847
- } else if (preset === "ellipse" || preset === "oval") geomSvg = `<ellipse cx="${E(cx)}" cy="${E(cy)}" rx="${E(w / 2)}" ry="${E(h / 2)}" fill="${p.fill}"${fa} stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma}/>`;
5848
5497
  else {
5849
- const pathFn = PRESET_PATHS[preset];
5850
- 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}"${fa} stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}" fill-rule="evenodd"${sa}${ma}/>`;
5851
- else {
5852
- const pointsFn = PRESET_POINTS[preset];
5853
- 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}"${fa} stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma}/>`;
5854
- else geomSvg = `<rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}" fill="${p.fill}"${fa} stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma} data-pptx-preset="${escapeXml(preset)}"><title>${escapeXml(`preset: ${preset}`)}</title></rect>`;
5855
- }
5498
+ const geometry = getPresetGeometry(preset, bounds, getShapeAdjustValues(shape));
5499
+ if (geometry !== null) geomSvg = customGeometryToSvg(geometry, x, y, w, h, p.fill, fa, p.stroke, p.strokeWidth, sa, ma);
5500
+ if (geomSvg === "") geomSvg = `<rect x="${E(x)}" y="${E(y)}" width="${E(w)}" height="${E(h)}" fill="${p.fill}"${fa} stroke="${p.stroke}" stroke-width="${E(p.strokeWidth)}"${sa}${ma} data-pptx-preset="${escapeXml(preset)}"><title>${escapeXml(`preset: ${preset}`)}</title></rect>`;
5856
5501
  }
5857
5502
  const fx = buildEffectsFilter(pres, shape);
5858
5503
  const filterAttr = fx ? ` filter="url(#${fx.id})"` : "";
@@ -5940,11 +5585,14 @@ const buildReflection = (pres, shape, geomRaw, box) => {
5940
5585
  const f = refl.scaleY ?? -1;
5941
5586
  const startA = refl.startOpacity ?? 1;
5942
5587
  const endA = refl.opacity ?? 0;
5588
+ const startPos = refl.startPosition ?? 0;
5589
+ const endPos = Math.max(startPos, refl.endPosition ?? 1);
5943
5590
  const contactPx = (box.y + box.h) / EMU_PER_PX;
5944
5591
  const transform = `translate(0 ${(refl.distEmu / EMU_PER_PX + contactPx * (1 - f)).toFixed(2)}) scale(1 ${f})`;
5945
5592
  const maskId = mintId();
5946
5593
  const gradId = mintId();
5947
- 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>`;
5594
+ const [y1, y2] = f < 0 ? [1, 0] : [0, 1];
5595
+ const defs = `<defs><linearGradient id="${gradId}" x1="0" y1="${y1}" x2="0" y2="${y2}"><stop offset="${startPos.toFixed(3)}" stop-color="#fff" stop-opacity="${startA.toFixed(3)}"/><stop offset="${endPos.toFixed(3)}" stop-color="#fff" stop-opacity="${endA.toFixed(3)}"/></linearGradient><mask id="${maskId}" maskContentUnits="objectBoundingBox"><rect width="1" height="1" fill="url(#${gradId})"/></mask></defs>`;
5948
5596
  return {
5949
5597
  svg: `<g transform="${transform}" mask="url(#${maskId})" data-pptx-reflection="1">${geomRaw}</g>`,
5950
5598
  defs
@@ -5992,9 +5640,9 @@ const buildEffectsFilter = (pres, shape) => {
5992
5640
  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}"/>`);
5993
5641
  layers.push(`glowOut${i}`);
5994
5642
  } else if (e.kind === "softEdge") {
5995
- const blurPx = e.radiusEmu / EMU_PER_PX / 2;
5643
+ const radiusPx = e.radiusEmu / EMU_PER_PX;
5996
5644
  const i = primitives.length;
5997
- primitives.push(`<feGaussianBlur in="SourceGraphic" stdDeviation="${blurPx.toFixed(2)}" result="softOut${i}"/>`);
5645
+ primitives.push(`<feMorphology in="SourceAlpha" operator="erode" radius="${(radiusPx / 2).toFixed(2)}" result="softCore${i}"/>`, `<feGaussianBlur in="softCore${i}" stdDeviation="${(radiusPx / 4).toFixed(2)}" result="softMask${i}"/>`, `<feComposite in="SourceGraphic" in2="softMask${i}" operator="in" result="softOut${i}"/>`);
5998
5646
  layers.length = 0;
5999
5647
  layers.push(`softOut${i}`);
6000
5648
  } else if (e.kind === "blur") {
@@ -6036,14 +5684,33 @@ const topLevelShapes = (shapes, opts) => {
6036
5684
  }
6037
5685
  return out;
6038
5686
  };
6039
- const renderSlideSvg = (pres, slide, opts = {}) => {
5687
+ const renderSlideSvg = (pres, slide, opts = {}) => renderSurface(pres, {
5688
+ slide,
5689
+ layout: getSlideLayout(slide),
5690
+ master: false
5691
+ }, opts);
5692
+ /**
5693
+ * Draws a slide layout — or, with `master`, its slide master — the way
5694
+ * the reference desktop app's Slide Master view shows it behind the placeholders: the
5695
+ * background and the decorative (non-placeholder) shapes of the master and,
5696
+ * for a layout, of the layout itself. A layout that hides background graphics
5697
+ * leaves the master's shapes out. Placeholders are not drawn; an editor draws
5698
+ * them with their prompt text over this.
5699
+ */
5700
+ const renderSlideLayoutSvg = (pres, layout, opts = {}) => renderSurface(pres, {
5701
+ slide: null,
5702
+ layout,
5703
+ master: opts.master ?? false
5704
+ }, opts);
5705
+ const renderSurface = (pres, surface, opts) => {
5706
+ const { slide, master } = surface;
6040
5707
  const size = getSlideSize(pres) ?? DEFAULT_SIZE;
6041
5708
  const W = size.width;
6042
5709
  const H = size.height;
6043
5710
  const theme = getPresentationTheme(pres);
6044
- activeColorMap = getEffectiveColorMap(slide);
6045
- activeDeckTextColor = resolveDeckBodyTextColor(slide) ?? "#000000";
6046
- const position = getSlides(pres).indexOf(slide);
5711
+ activeColorMap = getEffectiveColorMap(slide ?? surface.layout);
5712
+ activeDeckTextColor = resolveDeckBodyTextColor(slide ?? surface.layout) ?? "#000000";
5713
+ const position = slide ? getSlides(pres).indexOf(slide) : 0;
6047
5714
  activeSlideNumber = String(getPresentationFirstSlideNumber(pres) + (position < 0 ? 0 : position));
6048
5715
  const ctx = {
6049
5716
  groupScale: {
@@ -6061,11 +5728,11 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
6061
5728
  },
6062
5729
  inverseGroupTransform: ""
6063
5730
  };
6064
- let bg = getSlideBackground(slide);
5731
+ let bg = slide ? getSlideBackground(slide) : { kind: "inherit" };
6065
5732
  if (bg.kind === "inherit") {
6066
- const layout = getSlideLayout(slide);
5733
+ const layout = surface.layout;
6067
5734
  if (layout) {
6068
- const layoutBg = getSlideLayoutBackground(layout);
5735
+ const layoutBg = master ? { kind: "inherit" } : getSlideLayoutBackground(layout);
6069
5736
  if (layoutBg.kind !== "inherit") bg = layoutBg;
6070
5737
  else {
6071
5738
  const masterBg = getSlideMasterBackground(pres, layout);
@@ -6080,11 +5747,11 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
6080
5747
  let bgGradientDefs = "";
6081
5748
  if (bg.kind === "solid") bgColor = resolveColor(bg.color, theme, "#FFFFFF");
6082
5749
  else if (bg.kind === "gradient") {
6083
- let grad = getSlideBackgroundGradientFill(slide);
5750
+ let grad = slide ? getSlideBackgroundGradientFill(slide) : null;
6084
5751
  if (!grad) {
6085
- const layout = getSlideLayout(slide);
5752
+ const layout = surface.layout;
6086
5753
  if (layout) {
6087
- grad = getSlideLayoutBackgroundGradientFill(layout);
5754
+ grad = master ? null : getSlideLayoutBackgroundGradientFill(layout);
6088
5755
  if (!grad) grad = getSlideMasterBackgroundGradientFill(pres, layout);
6089
5756
  }
6090
5757
  }
@@ -6094,11 +5761,11 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
6094
5761
  bgGradient = `<rect width="${E(W)}" height="${E(H)}" fill="${built.fillAttr}"/>`;
6095
5762
  }
6096
5763
  } else if (bg.kind === "pattern") {
6097
- let pat = getSlideBackgroundPatternFill(pres, slide);
5764
+ let pat = slide ? getSlideBackgroundPatternFill(pres, slide) : null;
6098
5765
  if (!pat) {
6099
- const layout = getSlideLayout(slide);
5766
+ const layout = surface.layout;
6100
5767
  if (layout) {
6101
- pat = getSlideLayoutBackgroundPatternFill(pres, layout);
5768
+ pat = master ? null : getSlideLayoutBackgroundPatternFill(pres, layout);
6102
5769
  if (!pat) pat = getSlideMasterBackgroundPatternFill(pres, layout);
6103
5770
  }
6104
5771
  }
@@ -6110,23 +5777,23 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
6110
5777
  } else if (theme && bg.kind === "inherit") bgColor = normalizeHex(theme.light1);
6111
5778
  let bgImage = "";
6112
5779
  if (bg.kind === "image") {
6113
- let bytes = getSlideBackgroundImageBytes(slide);
5780
+ let bytes = slide ? getSlideBackgroundImageBytes(slide) : null;
6114
5781
  if (!bytes && pres) {
6115
- const layout = getSlideLayout(slide);
5782
+ const layout = surface.layout;
6116
5783
  if (layout) {
6117
- bytes = getSlideLayoutBackgroundImageBytes(pres, layout);
5784
+ bytes = master ? null : getSlideLayoutBackgroundImageBytes(pres, layout);
6118
5785
  if (!bytes) bytes = getSlideMasterBackgroundImageBytes(pres, layout);
6119
5786
  }
6120
5787
  }
6121
5788
  if (bytes) {
6122
5789
  const fmt = detectImageFormatLocal(bytes);
6123
5790
  const dataUrl = `data:${fmt ? imageMime[fmt] ?? "image/png" : "image/png"};base64,${u8ToBase64(bytes)}`;
6124
- const layout = getSlideBackgroundImageFillLayout(slide);
6125
- const crop = getSlideBackgroundImageCrop(slide);
5791
+ const layout = slide ? getSlideBackgroundImageFillLayout(slide) : null;
5792
+ const crop = slide ? getSlideBackgroundImageCrop(slide) : null;
6126
5793
  const cropLeft = crop?.left ?? 0, cropTop = crop?.top ?? 0;
6127
5794
  const cropRight = crop?.right ?? 0, cropBottom = crop?.bottom ?? 0;
6128
- const opacity = getSlideBackgroundImageOpacity(slide) ?? 1;
6129
- const intrinsic = layout?.mode === "tile" ? getSlideBackgroundImageIntrinsicSize(slide) : null;
5795
+ const opacity = (slide && getSlideBackgroundImageOpacity(slide)) ?? 1;
5796
+ const intrinsic = layout?.mode === "tile" && slide ? getSlideBackgroundImageIntrinsicSize(slide) : null;
6130
5797
  if (layout?.mode === "tile" && intrinsic) {
6131
5798
  const pattern = imageTilePattern(dataUrl, layout, intrinsic, 0, 0, W, H, cropLeft, cropTop, cropRight, cropBottom);
6132
5799
  bgImage = `${pattern.defs}<rect width="${E(W)}" height="${E(H)}" fill="${pattern.fill}" opacity="${opacity}"/>`;
@@ -6147,10 +5814,11 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
6147
5814
  }
6148
5815
  }
6149
5816
  let layoutBgShapes = "";
6150
- const layoutForBg = getSlideLayout(slide);
6151
- if (layoutForBg && !isSlideBackgroundGraphicsHidden(slide)) try {
6152
- const masterShapes = topLevelShapes(isSlideLayoutBackgroundGraphicsHidden(layoutForBg) ? [] : getSlideMasterShapes(pres, layoutForBg), { dropPlaceholders: true });
6153
- const layoutShapes = topLevelShapes(getSlideLayoutShapes(pres, layoutForBg), { dropPlaceholders: true });
5817
+ const layoutForBg = surface.layout;
5818
+ const paintBackground = opts.background ?? true;
5819
+ if (paintBackground && layoutForBg && !(slide && isSlideBackgroundGraphicsHidden(slide))) try {
5820
+ const masterShapes = topLevelShapes(!master && isSlideLayoutBackgroundGraphicsHidden(layoutForBg) ? [] : getSlideMasterShapes(pres, layoutForBg), { dropPlaceholders: true });
5821
+ const layoutShapes = topLevelShapes(master ? [] : getSlideLayoutShapes(pres, layoutForBg), { dropPlaceholders: true });
6154
5822
  const bgCtx = {
6155
5823
  ...ctx,
6156
5824
  ownShapes: false
@@ -6159,12 +5827,12 @@ const renderSlideSvg = (pres, slide, opts = {}) => {
6159
5827
  } catch {
6160
5828
  layoutBgShapes = "";
6161
5829
  }
6162
- const shapesSvg = topLevelShapes(getSlideShapes(slide), { dropPlaceholders: false }).map((s) => renderShape(s, pres, theme, ctx)).join("");
5830
+ const shapesSvg = topLevelShapes(slide ? getSlideShapes(slide) : [], { dropPlaceholders: false }).map((s) => renderShape(s, pres, theme, ctx)).join("");
6163
5831
  const backgroundSvg = `${bgOpacity === void 0 ? "" : `<rect width="${E(W)}" height="${E(H)}" fill="#FFFFFF"/>`}<rect width="${E(W)}" height="${E(H)}" fill="${bgColor}"${bgOpacityAttr}/>${bgGradient}${bgImage}`;
6164
5832
  return [
6165
5833
  `<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">`,
6166
5834
  bgGradientDefs,
6167
- ctx.background.id ? `<defs><g id="${ctx.background.id}">${backgroundSvg}</g></defs><use href="#${ctx.background.id}" xlink:href="#${ctx.background.id}"/>` : backgroundSvg,
5835
+ ctx.background.id ? `<defs><g id="${ctx.background.id}">${backgroundSvg}</g></defs>${paintBackground ? `<use href="#${ctx.background.id}" xlink:href="#${ctx.background.id}"/>` : ""}` : paintBackground ? backgroundSvg : "",
6168
5836
  layoutBgShapes,
6169
5837
  shapesSvg,
6170
5838
  "</svg>"
@@ -6340,7 +6008,7 @@ const auditTextLayout = (pres, options = {}) => {
6340
6008
  *
6341
6009
  * `1` for a shape with no `<a:normAutofit>`: `<a:noAutofit>` overflows and
6342
6010
  * `<a:spAutoFit>` grows the box instead, so neither shrinks the text. A shape
6343
- * with a baked `fontScale` reports that, unchanged — it is what PowerPoint
6011
+ * with a baked `fontScale` reports that, unchanged — it is what the reference desktop app
6344
6012
  * itself applied.
6345
6013
  */
6346
6014
  function shapeAutoFitScale(pres, shape, options = {}) {
@@ -6373,6 +6041,6 @@ function shapeTextAnchorOffset(pres, shape, options = {}) {
6373
6041
  };
6374
6042
  }
6375
6043
  //#endregion
6376
- export { textColumnsStyle as _, renderTextEffectsSvg as a, shapeCustomTextRect as b, MONO as c, TIMES as d, defaultMeasurer as f, textUnderlineStyle as g, paragraphNumberLabels as h, renderSlideSvg as i, SANS as l, substituteFamily as m, shapeTextAnchorOffset as n, ARIAL as o, isCjk as p, auditTextLayout as r, AVG_GLYPH_W_RATIO as s, shapeAutoFitScale as t, SERIF as u, verticalTextStyle as v, resolveTextBodyRect as y };
6044
+ export { shapeTextRect as S, paragraphNumberLabels as _, renderSlideSvg as a, verticalTextStyle as b, AVG_GLYPH_W_RATIO as c, SERIF as d, TIMES as f, decimalSeparatorOf as g, substituteFamily as h, renderSlideLayoutSvg as i, MONO as l, isCjk as m, shapeTextAnchorOffset as n, renderTextEffectsSvg as o, defaultMeasurer as p, auditTextLayout as r, ARIAL as s, shapeAutoFitScale as t, SANS as u, textUnderlineStyle as v, resolveTextBodyRect as x, textColumnsStyle as y };
6377
6045
 
6378
- //# sourceMappingURL=src-DYfIZIh8.js.map
6046
+ //# sourceMappingURL=src-pb3C61GD.js.map