@shotstack/shotstack-canvas 1.8.2 → 1.8.4

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.
@@ -2690,335 +2690,6 @@ var isGlyphFill2 = (op) => {
2690
2690
  return op.op === "FillPath" && op.isShadow !== true;
2691
2691
  };
2692
2692
 
2693
- // src/core/shape-generators.ts
2694
- var KAPPA = 0.5522847498307936;
2695
- var DEG_TO_RAD = Math.PI / 180;
2696
- function generateShapePath(params) {
2697
- switch (params.shape) {
2698
- case "rectangle":
2699
- return generateRectanglePath(params);
2700
- case "circle":
2701
- return generateCirclePath(params);
2702
- case "ellipse":
2703
- return generateEllipsePath(params);
2704
- case "line":
2705
- return generateLinePath(params);
2706
- case "triangle":
2707
- return generateTrianglePath(params);
2708
- case "polygon":
2709
- return generatePolygonPath(params);
2710
- case "star":
2711
- return generateStarPath(params);
2712
- case "arrow":
2713
- return generateArrowPath(params);
2714
- case "heart":
2715
- return generateHeartPath(params);
2716
- case "cross":
2717
- return generateCrossPath(params);
2718
- case "ring":
2719
- return generateRingPath(params);
2720
- }
2721
- }
2722
- function generateRectanglePath(params) {
2723
- const { width, height, cornerRadius = 0 } = params;
2724
- if (cornerRadius === 0) {
2725
- return `M 0 0 L ${width} 0 L ${width} ${height} L 0 ${height} Z`;
2726
- }
2727
- const r = Math.min(cornerRadius, width / 2, height / 2);
2728
- return [
2729
- `M ${r} 0`,
2730
- `L ${width - r} 0`,
2731
- `Q ${width} 0 ${width} ${r}`,
2732
- `L ${width} ${height - r}`,
2733
- `Q ${width} ${height} ${width - r} ${height}`,
2734
- `L ${r} ${height}`,
2735
- `Q 0 ${height} 0 ${height - r}`,
2736
- `L 0 ${r}`,
2737
- `Q 0 0 ${r} 0`,
2738
- "Z"
2739
- ].join(" ");
2740
- }
2741
- function generateCirclePath(params) {
2742
- const { radius } = params;
2743
- return generateEllipsePath({ radiusX: radius, radiusY: radius });
2744
- }
2745
- function generateEllipsePath(params) {
2746
- const { radiusX, radiusY } = params;
2747
- const kx = radiusX * KAPPA;
2748
- const ky = radiusY * KAPPA;
2749
- return [
2750
- `M ${radiusX} 0`,
2751
- `C ${radiusX} ${ky} ${kx} ${radiusY} 0 ${radiusY}`,
2752
- `C ${-kx} ${radiusY} ${-radiusX} ${ky} ${-radiusX} 0`,
2753
- `C ${-radiusX} ${-ky} ${-kx} ${-radiusY} 0 ${-radiusY}`,
2754
- `C ${kx} ${-radiusY} ${radiusX} ${-ky} ${radiusX} 0`,
2755
- "Z"
2756
- ].join(" ");
2757
- }
2758
- function generateLinePath(params) {
2759
- const { length, thickness } = params;
2760
- const halfThickness = thickness / 2;
2761
- return [
2762
- `M 0 ${-halfThickness}`,
2763
- `L ${length} ${-halfThickness}`,
2764
- `L ${length} ${halfThickness}`,
2765
- `L 0 ${halfThickness}`,
2766
- "Z"
2767
- ].join(" ");
2768
- }
2769
- function generateTrianglePath(params) {
2770
- const { width, height } = params;
2771
- const halfWidth = width / 2;
2772
- return `M 0 ${height} L ${halfWidth} 0 L ${width} ${height} Z`;
2773
- }
2774
- function generatePolygonPath(params) {
2775
- const { sides, radius } = params;
2776
- const points = generatePolygonPoints(sides, radius);
2777
- return pointsToPath(points, true);
2778
- }
2779
- function generatePolygonPoints(sides, radius) {
2780
- const points = [];
2781
- const angleStep = 2 * Math.PI / sides;
2782
- const startAngle = -Math.PI / 2;
2783
- for (let i = 0; i < sides; i++) {
2784
- const angle = startAngle + i * angleStep;
2785
- points.push({
2786
- x: radius * Math.cos(angle),
2787
- y: radius * Math.sin(angle)
2788
- });
2789
- }
2790
- return points;
2791
- }
2792
- function generateStarPath(params) {
2793
- const { points, outerRadius, innerRadius } = params;
2794
- const starPoints = generateStarPoints(points, outerRadius, innerRadius);
2795
- return pointsToPath(starPoints, true);
2796
- }
2797
- function generateStarPoints(pointCount, outerRadius, innerRadius) {
2798
- const points = [];
2799
- const totalPoints = pointCount * 2;
2800
- const angleStep = Math.PI / pointCount;
2801
- const startAngle = -Math.PI / 2;
2802
- for (let i = 0; i < totalPoints; i++) {
2803
- const radius = i % 2 === 0 ? outerRadius : innerRadius;
2804
- const angle = startAngle + i * angleStep;
2805
- points.push({
2806
- x: radius * Math.cos(angle),
2807
- y: radius * Math.sin(angle)
2808
- });
2809
- }
2810
- return points;
2811
- }
2812
- function generateArrowPath(params) {
2813
- const { length, headWidth, headLength, shaftWidth } = params;
2814
- const halfShaft = shaftWidth / 2;
2815
- const halfHead = headWidth / 2;
2816
- const shaftLength = length - headLength;
2817
- return [
2818
- `M 0 ${-halfShaft}`,
2819
- `L ${shaftLength} ${-halfShaft}`,
2820
- `L ${shaftLength} ${-halfHead}`,
2821
- `L ${length} 0`,
2822
- `L ${shaftLength} ${halfHead}`,
2823
- `L ${shaftLength} ${halfShaft}`,
2824
- `L 0 ${halfShaft}`,
2825
- "Z"
2826
- ].join(" ");
2827
- }
2828
- function generateHeartPath(params) {
2829
- const { size } = params;
2830
- const s = size / 2;
2831
- return [
2832
- `M 0 ${s * 0.3}`,
2833
- `C 0 ${-s * 0.3} ${-s} ${-s * 0.3} ${-s} ${s * 0.3}`,
2834
- `C ${-s} ${s * 0.7} 0 ${s} 0 ${s * 1.2}`,
2835
- `C 0 ${s} ${s} ${s * 0.7} ${s} ${s * 0.3}`,
2836
- `C ${s} ${-s * 0.3} 0 ${-s * 0.3} 0 ${s * 0.3}`,
2837
- "Z"
2838
- ].join(" ");
2839
- }
2840
- function generateCrossPath(params) {
2841
- const { width, height, thickness } = params;
2842
- const halfThickness = thickness / 2;
2843
- const halfWidth = width / 2;
2844
- const halfHeight = height / 2;
2845
- return [
2846
- `M ${-halfThickness} ${-halfHeight}`,
2847
- `L ${halfThickness} ${-halfHeight}`,
2848
- `L ${halfThickness} ${-halfThickness}`,
2849
- `L ${halfWidth} ${-halfThickness}`,
2850
- `L ${halfWidth} ${halfThickness}`,
2851
- `L ${halfThickness} ${halfThickness}`,
2852
- `L ${halfThickness} ${halfHeight}`,
2853
- `L ${-halfThickness} ${halfHeight}`,
2854
- `L ${-halfThickness} ${halfThickness}`,
2855
- `L ${-halfWidth} ${halfThickness}`,
2856
- `L ${-halfWidth} ${-halfThickness}`,
2857
- `L ${-halfThickness} ${-halfThickness}`,
2858
- "Z"
2859
- ].join(" ");
2860
- }
2861
- function generateRingPath(params) {
2862
- const { outerRadius, innerRadius } = params;
2863
- const outerPath = generateCirclePath({ radius: outerRadius });
2864
- const innerPath = generateCirclePath({ radius: innerRadius });
2865
- return `${outerPath} ${reverseWindingOrder(innerPath)}`;
2866
- }
2867
- function pointsToPath(points, closed = true) {
2868
- if (points.length === 0) return "";
2869
- const commands = [`M ${points[0].x} ${points[0].y}`];
2870
- for (let i = 1; i < points.length; i++) {
2871
- commands.push(`L ${points[i].x} ${points[i].y}`);
2872
- }
2873
- if (closed) {
2874
- commands.push("Z");
2875
- }
2876
- return commands.join(" ");
2877
- }
2878
- function reverseWindingOrder(path) {
2879
- const commands = parsePathCommands(path);
2880
- const reversed = [];
2881
- const points = [];
2882
- let currentX = 0;
2883
- let currentY = 0;
2884
- for (const cmd of commands) {
2885
- if (cmd.type === "M" || cmd.type === "L") {
2886
- points.push({ x: cmd.x, y: cmd.y });
2887
- currentX = cmd.x;
2888
- currentY = cmd.y;
2889
- } else if (cmd.type === "C") {
2890
- points.push({ x: cmd.x, y: cmd.y });
2891
- currentX = cmd.x;
2892
- currentY = cmd.y;
2893
- }
2894
- }
2895
- if (points.length === 0) return path;
2896
- points.reverse();
2897
- reversed.push(`M ${points[0].x} ${points[0].y}`);
2898
- for (let i = 1; i < points.length; i++) {
2899
- reversed.push(`L ${points[i].x} ${points[i].y}`);
2900
- }
2901
- reversed.push("Z");
2902
- return reversed.join(" ");
2903
- }
2904
- function parsePathCommands(path) {
2905
- const commands = [];
2906
- const regex = /([MLCQZ])\s*([^MLCQZ]*)/gi;
2907
- let match;
2908
- while ((match = regex.exec(path)) !== null) {
2909
- const type = match[1].toUpperCase();
2910
- const args = match[2].trim().split(/[\s,]+/).filter((s) => s.length > 0).map(parseFloat);
2911
- if (type === "M" || type === "L") {
2912
- commands.push({ type, x: args[0], y: args[1] });
2913
- } else if (type === "C") {
2914
- commands.push({
2915
- type,
2916
- x1: args[0],
2917
- y1: args[1],
2918
- x2: args[2],
2919
- y2: args[3],
2920
- x: args[4],
2921
- y: args[5]
2922
- });
2923
- } else if (type === "Q") {
2924
- commands.push({
2925
- type,
2926
- x1: args[0],
2927
- y1: args[1],
2928
- x: args[2],
2929
- y: args[3]
2930
- });
2931
- } else if (type === "Z") {
2932
- commands.push({ type });
2933
- }
2934
- }
2935
- return commands;
2936
- }
2937
- function computePathBounds3(path) {
2938
- const commands = parsePathCommands(path);
2939
- let minX = Infinity;
2940
- let minY = Infinity;
2941
- let maxX = -Infinity;
2942
- let maxY = -Infinity;
2943
- const updateBounds = (x, y) => {
2944
- minX = Math.min(minX, x);
2945
- minY = Math.min(minY, y);
2946
- maxX = Math.max(maxX, x);
2947
- maxY = Math.max(maxY, y);
2948
- };
2949
- for (const cmd of commands) {
2950
- if (cmd.x !== void 0 && cmd.y !== void 0) {
2951
- updateBounds(cmd.x, cmd.y);
2952
- }
2953
- if (cmd.x1 !== void 0 && cmd.y1 !== void 0) {
2954
- updateBounds(cmd.x1, cmd.y1);
2955
- }
2956
- if (cmd.x2 !== void 0 && cmd.y2 !== void 0) {
2957
- updateBounds(cmd.x2, cmd.y2);
2958
- }
2959
- }
2960
- if (minX === Infinity) {
2961
- return { x: 0, y: 0, w: 0, h: 0 };
2962
- }
2963
- return {
2964
- x: minX,
2965
- y: minY,
2966
- w: maxX - minX,
2967
- h: maxY - minY
2968
- };
2969
- }
2970
- function translatePath(path, dx, dy) {
2971
- return path.replace(
2972
- /(-?\d*\.?\d+(?:e[-+]?\d+)?)\s+(-?\d*\.?\d+(?:e[-+]?\d+)?)/gi,
2973
- (match, x, y) => {
2974
- const newX = parseFloat(x) + dx;
2975
- const newY = parseFloat(y) + dy;
2976
- return `${newX} ${newY}`;
2977
- }
2978
- );
2979
- }
2980
- function scalePath(path, sx, sy = sx) {
2981
- return path.replace(
2982
- /(-?\d*\.?\d+(?:e[-+]?\d+)?)\s+(-?\d*\.?\d+(?:e[-+]?\d+)?)/gi,
2983
- (match, x, y) => {
2984
- const newX = parseFloat(x) * sx;
2985
- const newY = parseFloat(y) * sy;
2986
- return `${newX} ${newY}`;
2987
- }
2988
- );
2989
- }
2990
- function rotatePath(path, angleDegrees) {
2991
- const angleRadians = angleDegrees * DEG_TO_RAD;
2992
- const cos = Math.cos(angleRadians);
2993
- const sin = Math.sin(angleRadians);
2994
- return path.replace(
2995
- /(-?\d*\.?\d+(?:e[-+]?\d+)?)\s+(-?\d*\.?\d+(?:e[-+]?\d+)?)/gi,
2996
- (match, x, y) => {
2997
- const px = parseFloat(x);
2998
- const py = parseFloat(y);
2999
- const newX = px * cos - py * sin;
3000
- const newY = px * sin + py * cos;
3001
- return `${newX} ${newY}`;
3002
- }
3003
- );
3004
- }
3005
- function centerPath(path) {
3006
- const bounds = computePathBounds3(path);
3007
- const cx = bounds.x + bounds.w / 2;
3008
- const cy = bounds.y + bounds.h / 2;
3009
- return translatePath(path, -cx, -cy);
3010
- }
3011
- function normalizePathToSize(path, targetWidth, targetHeight) {
3012
- const bounds = computePathBounds3(path);
3013
- if (bounds.w === 0 || bounds.h === 0) return path;
3014
- const scaleX = targetWidth / bounds.w;
3015
- const scaleY = targetHeight / bounds.h;
3016
- const scale = Math.min(scaleX, scaleY);
3017
- let normalized = translatePath(path, -bounds.x, -bounds.y);
3018
- normalized = scalePath(normalized, scale);
3019
- return normalized;
3020
- }
3021
-
3022
2693
  // src/core/svg-path-utils.ts
3023
2694
  var TAU = Math.PI * 2;
3024
2695
  var PATH_COMMAND_REGEX = /([MmLlHhVvCcSsQqTtAaZz])([^MmLlHhVvCcSsQqTtAaZz]*)/g;
@@ -3415,109 +3086,183 @@ function quadraticToCubic(x0, y0, qx, qy, x, y) {
3415
3086
  };
3416
3087
  }
3417
3088
 
3418
- // src/core/svg-import.ts
3419
- var SVG_ATTRS_REGEX = /<svg([^>]*)>/i;
3420
- var PATH_TAG_REGEX = /<path([^/>]*)\/?>/gi;
3421
- var RECT_TAG_REGEX = /<rect([^/>]*)\/?>/gi;
3422
- var CIRCLE_TAG_REGEX = /<circle([^/>]*)\/?>/gi;
3423
- var ELLIPSE_TAG_REGEX = /<ellipse([^/>]*)\/?>/gi;
3424
- var LINE_TAG_REGEX = /<line([^/>]*)\/?>/gi;
3425
- var POLYLINE_TAG_REGEX = /<polyline([^/>]*)\/?>/gi;
3426
- var POLYGON_TAG_REGEX = /<polygon([^/>]*)\/?>/gi;
3427
- function extractAttribute(attrs, name) {
3428
- const regex = new RegExp(`${name}\\s*=\\s*["']([^"']*)["']`, "i");
3429
- const match = attrs.match(regex);
3430
- return match ? match[1] : void 0;
3089
+ // src/core/resvg-renderer.ts
3090
+ var resvgModule = null;
3091
+ async function getResvg() {
3092
+ if (!resvgModule) {
3093
+ resvgModule = await import("@resvg/resvg-js");
3094
+ }
3095
+ return resvgModule;
3096
+ }
3097
+ async function renderSvgToPng(svgString, options = {}) {
3098
+ const { Resvg } = await getResvg();
3099
+ const resvgOptions = {};
3100
+ if (options.background) {
3101
+ resvgOptions.background = options.background;
3102
+ }
3103
+ if (options.fitTo) {
3104
+ resvgOptions.fitTo = options.fitTo;
3105
+ } else if (options.width) {
3106
+ resvgOptions.fitTo = { mode: "width", value: options.width };
3107
+ } else if (options.height) {
3108
+ resvgOptions.fitTo = { mode: "height", value: options.height };
3109
+ }
3110
+ const resvg = new Resvg(svgString, resvgOptions);
3111
+ const rendered = resvg.render();
3112
+ const png = rendered.asPng();
3113
+ return {
3114
+ png,
3115
+ width: rendered.width,
3116
+ height: rendered.height
3117
+ };
3431
3118
  }
3432
- function parseColor(color) {
3433
- if (!color || color === "none" || color === "transparent") {
3434
- return void 0;
3119
+ function shapeToSvgString(asset, defaultWidth, defaultHeight) {
3120
+ if (!asset.shape) {
3121
+ throw new Error("Shape is required for shape mode");
3122
+ }
3123
+ const width = asset.width ?? defaultWidth;
3124
+ const height = asset.height ?? defaultHeight;
3125
+ const pathData = generateShapePathData(asset.shape);
3126
+ const bounds = computeSimplePathBounds(pathData);
3127
+ const centerX = width / 2;
3128
+ const centerY = height / 2;
3129
+ const pathCenterX = bounds.x + bounds.w / 2;
3130
+ const pathCenterY = bounds.y + bounds.h / 2;
3131
+ let offsetX = centerX - pathCenterX;
3132
+ let offsetY = centerY - pathCenterY;
3133
+ if (asset.transform) {
3134
+ offsetX += asset.transform.x ?? 0;
3135
+ offsetY += asset.transform.y ?? 0;
3136
+ }
3137
+ const elements = [];
3138
+ if (asset.shadow) {
3139
+ const shadowOffsetX = asset.shadow.offsetX ?? 4;
3140
+ const shadowOffsetY = asset.shadow.offsetY ?? 4;
3141
+ const shadowColor = asset.shadow.color ?? "#000000";
3142
+ const shadowOpacity = asset.shadow.opacity ?? 0.5;
3143
+ elements.push(
3144
+ `<path d="${pathData}" transform="translate(${offsetX + shadowOffsetX}, ${offsetY + shadowOffsetY})" fill="${shadowColor}" fill-opacity="${shadowOpacity}"/>`
3145
+ );
3435
3146
  }
3436
- color = color.trim();
3437
- if (color.startsWith("#")) {
3438
- if (color.length === 4) {
3439
- const r = color[1];
3440
- const g = color[2];
3441
- const b = color[3];
3442
- return `#${r}${r}${g}${g}${b}${b}`.toUpperCase();
3147
+ const pathAttrs = [`d="${pathData}"`, `transform="translate(${offsetX}, ${offsetY})"`];
3148
+ if (asset.fill) {
3149
+ const fillId = `fill-${Date.now()}`;
3150
+ const fillDef = generateFillDefinition(asset.fill, fillId, bounds);
3151
+ if (fillDef.defs) {
3152
+ elements.unshift(fillDef.defs);
3443
3153
  }
3444
- return color.toUpperCase();
3445
- }
3446
- const rgbMatch = color.match(/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i);
3447
- if (rgbMatch) {
3448
- const r = parseInt(rgbMatch[1], 10);
3449
- const g = parseInt(rgbMatch[2], 10);
3450
- const b = parseInt(rgbMatch[3], 10);
3451
- return rgbToHex(r, g, b);
3154
+ pathAttrs.push(`fill="${fillDef.fill}"`);
3155
+ if (asset.fill.opacity !== void 0 && asset.fill.opacity !== 1) {
3156
+ pathAttrs.push(`fill-opacity="${asset.fill.opacity}"`);
3157
+ }
3158
+ } else {
3159
+ pathAttrs.push(`fill="none"`);
3452
3160
  }
3453
- const rgbaMatch = color.match(/rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*[\d.]+\s*\)/i);
3454
- if (rgbaMatch) {
3455
- const r = parseInt(rgbaMatch[1], 10);
3456
- const g = parseInt(rgbaMatch[2], 10);
3457
- const b = parseInt(rgbaMatch[3], 10);
3458
- return rgbToHex(r, g, b);
3161
+ if (asset.stroke && asset.stroke.width && asset.stroke.width > 0) {
3162
+ pathAttrs.push(`stroke="${asset.stroke.color ?? "#000000"}"`);
3163
+ pathAttrs.push(`stroke-width="${asset.stroke.width}"`);
3164
+ if (asset.stroke.opacity !== void 0 && asset.stroke.opacity !== 1) {
3165
+ pathAttrs.push(`stroke-opacity="${asset.stroke.opacity}"`);
3166
+ }
3167
+ if (asset.stroke.dashArray && asset.stroke.dashArray.length > 0) {
3168
+ pathAttrs.push(`stroke-dasharray="${asset.stroke.dashArray.join(" ")}"`);
3169
+ }
3170
+ if (asset.stroke.lineCap) {
3171
+ pathAttrs.push(`stroke-linecap="${asset.stroke.lineCap}"`);
3172
+ }
3173
+ if (asset.stroke.lineJoin) {
3174
+ pathAttrs.push(`stroke-linejoin="${asset.stroke.lineJoin}"`);
3175
+ }
3459
3176
  }
3460
- const namedColors = {
3461
- black: "#000000",
3462
- white: "#FFFFFF",
3463
- red: "#FF0000",
3464
- green: "#008000",
3465
- blue: "#0000FF",
3466
- yellow: "#FFFF00",
3467
- cyan: "#00FFFF",
3468
- magenta: "#FF00FF",
3469
- gray: "#808080",
3470
- grey: "#808080",
3471
- orange: "#FFA500",
3472
- purple: "#800080",
3473
- pink: "#FFC0CB",
3474
- brown: "#A52A2A",
3475
- navy: "#000080",
3476
- teal: "#008080",
3477
- olive: "#808000",
3478
- maroon: "#800000",
3479
- aqua: "#00FFFF",
3480
- lime: "#00FF00",
3481
- silver: "#C0C0C0",
3482
- fuchsia: "#FF00FF"
3483
- };
3484
- const lowerColor = color.toLowerCase();
3485
- if (namedColors[lowerColor]) {
3486
- return namedColors[lowerColor];
3177
+ if (asset.opacity !== void 0 && asset.opacity !== 1) {
3178
+ pathAttrs.push(`opacity="${asset.opacity}"`);
3487
3179
  }
3488
- return void 0;
3180
+ elements.push(`<path ${pathAttrs.join(" ")}/>`);
3181
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">${elements.join(
3182
+ ""
3183
+ )}</svg>`;
3489
3184
  }
3490
- function rgbToHex(r, g, b) {
3491
- const toHex = (n) => {
3492
- const hex = Math.max(0, Math.min(255, n)).toString(16);
3493
- return hex.length === 1 ? "0" + hex : hex;
3494
- };
3495
- return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();
3496
- }
3497
- function parseNumber(value) {
3498
- if (!value) return void 0;
3499
- const num = parseFloat(value);
3500
- return isNaN(num) ? void 0 : num;
3185
+ function generateFillDefinition(fill, id, bounds) {
3186
+ if (fill.type === "solid") {
3187
+ return { fill: fill.color };
3188
+ }
3189
+ if (fill.type === "linear") {
3190
+ const angle = fill.angle ?? 0;
3191
+ const rad = angle * Math.PI / 180;
3192
+ const x1 = 50 - Math.cos(rad) * 50;
3193
+ const y1 = 50 - Math.sin(rad) * 50;
3194
+ const x2 = 50 + Math.cos(rad) * 50;
3195
+ const y2 = 50 + Math.sin(rad) * 50;
3196
+ const stops = fill.stops.map((s) => `<stop offset="${s.offset * 100}%" stop-color="${s.color}"/>`).join("");
3197
+ const defs = `<defs><linearGradient id="${id}" x1="${x1}%" y1="${y1}%" x2="${x2}%" y2="${y2}%">${stops}</linearGradient></defs>`;
3198
+ return { fill: `url(#${id})`, defs };
3199
+ }
3200
+ if (fill.type === "radial") {
3201
+ const stops = fill.stops.map((s) => `<stop offset="${s.offset * 100}%" stop-color="${s.color}"/>`).join("");
3202
+ const defs = `<defs><radialGradient id="${id}" cx="50%" cy="50%" r="50%">${stops}</radialGradient></defs>`;
3203
+ return { fill: `url(#${id})`, defs };
3204
+ }
3205
+ return { fill: "#000000" };
3501
3206
  }
3502
- function parseViewBox(viewBox) {
3503
- if (!viewBox) return void 0;
3504
- const parts = viewBox.trim().split(/[\s,]+/).map(parseFloat);
3505
- if (parts.length === 4 && parts.every((p) => !isNaN(p))) {
3506
- return { x: parts[0], y: parts[1], width: parts[2], height: parts[3] };
3207
+ function generateShapePathData(shape) {
3208
+ const shapeType = shape.type;
3209
+ switch (shapeType) {
3210
+ case "rectangle": {
3211
+ const s = shape;
3212
+ return generateRectanglePath(s.width, s.height, s.cornerRadius ?? 0);
3213
+ }
3214
+ case "circle": {
3215
+ const s = shape;
3216
+ return generateCirclePath(s.radius);
3217
+ }
3218
+ case "ellipse": {
3219
+ const s = shape;
3220
+ return generateEllipsePath(s.radiusX, s.radiusY);
3221
+ }
3222
+ case "line": {
3223
+ const s = shape;
3224
+ return generateLinePath(s.length, s.thickness ?? 2);
3225
+ }
3226
+ case "polygon": {
3227
+ const s = shape;
3228
+ return generatePolygonPath(s.sides, s.radius);
3229
+ }
3230
+ case "star": {
3231
+ const s = shape;
3232
+ return generateStarPath(s.points, s.outerRadius, s.innerRadius);
3233
+ }
3234
+ case "arrow": {
3235
+ const s = shape;
3236
+ return generateArrowPath(s.length, s.headWidth ?? 30, s.headLength ?? 25, s.shaftWidth ?? 10);
3237
+ }
3238
+ case "heart": {
3239
+ const s = shape;
3240
+ return generateHeartPath(s.size);
3241
+ }
3242
+ case "cross": {
3243
+ const s = shape;
3244
+ return generateCrossPath(s.width, s.height, s.thickness);
3245
+ }
3246
+ case "ring": {
3247
+ const s = shape;
3248
+ return generateRingPath(s.outerRadius, s.innerRadius);
3249
+ }
3250
+ case "path": {
3251
+ const s = shape;
3252
+ return s.d;
3253
+ }
3254
+ default:
3255
+ throw new Error(`Unknown shape type: ${shapeType}`);
3507
3256
  }
3508
- return void 0;
3509
3257
  }
3510
- function rectToPath(attrs) {
3511
- const x = parseNumber(extractAttribute(attrs, "x")) || 0;
3512
- const y = parseNumber(extractAttribute(attrs, "y")) || 0;
3513
- const width = parseNumber(extractAttribute(attrs, "width")) || 0;
3514
- const height = parseNumber(extractAttribute(attrs, "height")) || 0;
3515
- const rx = parseNumber(extractAttribute(attrs, "rx")) || 0;
3516
- const ry = parseNumber(extractAttribute(attrs, "ry")) || rx;
3517
- if (rx === 0 && ry === 0) {
3258
+ var KAPPA = 0.5522847498307936;
3259
+ function generateRectanglePath(width, height, cornerRadius) {
3260
+ const x = -width / 2;
3261
+ const y = -height / 2;
3262
+ const r = Math.min(cornerRadius, width / 2, height / 2);
3263
+ if (r === 0) {
3518
3264
  return `M ${x} ${y} L ${x + width} ${y} L ${x + width} ${y + height} L ${x} ${y + height} Z`;
3519
3265
  }
3520
- const r = Math.min(rx, ry, width / 2, height / 2);
3521
3266
  return [
3522
3267
  `M ${x + r} ${y}`,
3523
3268
  `L ${x + width - r} ${y}`,
@@ -3531,288 +3276,358 @@ function rectToPath(attrs) {
3531
3276
  "Z"
3532
3277
  ].join(" ");
3533
3278
  }
3534
- function circleToPath(attrs) {
3535
- const cx = parseNumber(extractAttribute(attrs, "cx")) || 0;
3536
- const cy = parseNumber(extractAttribute(attrs, "cy")) || 0;
3537
- const r = parseNumber(extractAttribute(attrs, "r")) || 0;
3538
- if (r === 0) return "";
3539
- const KAPPA2 = 0.5522847498307936;
3540
- const k = r * KAPPA2;
3279
+ function generateCirclePath(radius) {
3280
+ const k = radius * KAPPA;
3541
3281
  return [
3542
- `M ${cx + r} ${cy}`,
3543
- `C ${cx + r} ${cy + k} ${cx + k} ${cy + r} ${cx} ${cy + r}`,
3544
- `C ${cx - k} ${cy + r} ${cx - r} ${cy + k} ${cx - r} ${cy}`,
3545
- `C ${cx - r} ${cy - k} ${cx - k} ${cy - r} ${cx} ${cy - r}`,
3546
- `C ${cx + k} ${cy - r} ${cx + r} ${cy - k} ${cx + r} ${cy}`,
3282
+ `M ${radius} 0`,
3283
+ `C ${radius} ${k} ${k} ${radius} 0 ${radius}`,
3284
+ `C ${-k} ${radius} ${-radius} ${k} ${-radius} 0`,
3285
+ `C ${-radius} ${-k} ${-k} ${-radius} 0 ${-radius}`,
3286
+ `C ${k} ${-radius} ${radius} ${-k} ${radius} 0`,
3547
3287
  "Z"
3548
3288
  ].join(" ");
3549
3289
  }
3550
- function ellipseToPath(attrs) {
3551
- const cx = parseNumber(extractAttribute(attrs, "cx")) || 0;
3552
- const cy = parseNumber(extractAttribute(attrs, "cy")) || 0;
3553
- const rx = parseNumber(extractAttribute(attrs, "rx")) || 0;
3554
- const ry = parseNumber(extractAttribute(attrs, "ry")) || 0;
3555
- if (rx === 0 || ry === 0) return "";
3556
- const KAPPA2 = 0.5522847498307936;
3557
- const kx = rx * KAPPA2;
3558
- const ky = ry * KAPPA2;
3290
+ function generateEllipsePath(radiusX, radiusY) {
3291
+ const kx = radiusX * KAPPA;
3292
+ const ky = radiusY * KAPPA;
3559
3293
  return [
3560
- `M ${cx + rx} ${cy}`,
3561
- `C ${cx + rx} ${cy + ky} ${cx + kx} ${cy + ry} ${cx} ${cy + ry}`,
3562
- `C ${cx - kx} ${cy + ry} ${cx - rx} ${cy + ky} ${cx - rx} ${cy}`,
3563
- `C ${cx - rx} ${cy - ky} ${cx - kx} ${cy - ry} ${cx} ${cy - ry}`,
3564
- `C ${cx + kx} ${cy - ry} ${cx + rx} ${cy - ky} ${cx + rx} ${cy}`,
3294
+ `M ${radiusX} 0`,
3295
+ `C ${radiusX} ${ky} ${kx} ${radiusY} 0 ${radiusY}`,
3296
+ `C ${-kx} ${radiusY} ${-radiusX} ${ky} ${-radiusX} 0`,
3297
+ `C ${-radiusX} ${-ky} ${-kx} ${-radiusY} 0 ${-radiusY}`,
3298
+ `C ${kx} ${-radiusY} ${radiusX} ${-ky} ${radiusX} 0`,
3565
3299
  "Z"
3566
3300
  ].join(" ");
3567
3301
  }
3568
- function lineToPath(attrs) {
3569
- const x1 = parseNumber(extractAttribute(attrs, "x1")) || 0;
3570
- const y1 = parseNumber(extractAttribute(attrs, "y1")) || 0;
3571
- const x2 = parseNumber(extractAttribute(attrs, "x2")) || 0;
3572
- const y2 = parseNumber(extractAttribute(attrs, "y2")) || 0;
3573
- return `M ${x1} ${y1} L ${x2} ${y2}`;
3302
+ function generateLinePath(length, thickness) {
3303
+ const halfLen = length / 2;
3304
+ const halfThick = thickness / 2;
3305
+ return `M ${-halfLen} ${-halfThick} L ${halfLen} ${-halfThick} L ${halfLen} ${halfThick} L ${-halfLen} ${halfThick} Z`;
3574
3306
  }
3575
- function polylineToPath(attrs) {
3576
- const points = extractAttribute(attrs, "points");
3577
- if (!points) return "";
3578
- const coords = points.trim().split(/[\s,]+/).map(parseFloat);
3579
- if (coords.length < 2) return "";
3307
+ function generatePolygonPath(sides, radius) {
3308
+ const angleStep = 2 * Math.PI / sides;
3309
+ const startAngle = -Math.PI / 2;
3310
+ const points = [];
3311
+ for (let i = 0; i < sides; i++) {
3312
+ const angle = startAngle + i * angleStep;
3313
+ const x = Math.cos(angle) * radius;
3314
+ const y = Math.sin(angle) * radius;
3315
+ points.push(i === 0 ? `M ${x} ${y}` : `L ${x} ${y}`);
3316
+ }
3317
+ points.push("Z");
3318
+ return points.join(" ");
3319
+ }
3320
+ function generateStarPath(points, outerRadius, innerRadius) {
3321
+ const angleStep = Math.PI / points;
3322
+ const startAngle = -Math.PI / 2;
3580
3323
  const pathParts = [];
3581
- for (let i = 0; i < coords.length; i += 2) {
3582
- if (i + 1 < coords.length) {
3583
- if (i === 0) {
3584
- pathParts.push(`M ${coords[i]} ${coords[i + 1]}`);
3585
- } else {
3586
- pathParts.push(`L ${coords[i]} ${coords[i + 1]}`);
3587
- }
3588
- }
3324
+ for (let i = 0; i < points * 2; i++) {
3325
+ const angle = startAngle + i * angleStep;
3326
+ const radius = i % 2 === 0 ? outerRadius : innerRadius;
3327
+ const x = Math.cos(angle) * radius;
3328
+ const y = Math.sin(angle) * radius;
3329
+ pathParts.push(i === 0 ? `M ${x} ${y}` : `L ${x} ${y}`);
3589
3330
  }
3331
+ pathParts.push("Z");
3590
3332
  return pathParts.join(" ");
3591
3333
  }
3592
- function polygonToPath(attrs) {
3593
- const polyPath = polylineToPath(attrs);
3594
- if (!polyPath) return "";
3595
- return polyPath + " Z";
3334
+ function generateArrowPath(length, headWidth, headLength, shaftWidth) {
3335
+ const halfShaft = shaftWidth / 2;
3336
+ const halfHead = headWidth / 2;
3337
+ const shaftLength = length - headLength;
3338
+ const startX = -length / 2;
3339
+ return [
3340
+ `M ${startX} ${-halfShaft}`,
3341
+ `L ${startX + shaftLength} ${-halfShaft}`,
3342
+ `L ${startX + shaftLength} ${-halfHead}`,
3343
+ `L ${startX + length} 0`,
3344
+ `L ${startX + shaftLength} ${halfHead}`,
3345
+ `L ${startX + shaftLength} ${halfShaft}`,
3346
+ `L ${startX} ${halfShaft}`,
3347
+ "Z"
3348
+ ].join(" ");
3596
3349
  }
3597
- function extractPathAttributes(attrs) {
3598
- const d = extractAttribute(attrs, "d") || "";
3599
- const fill = extractAttribute(attrs, "fill");
3600
- const fillOpacity = parseNumber(extractAttribute(attrs, "fill-opacity"));
3601
- const stroke = extractAttribute(attrs, "stroke");
3602
- const strokeWidth = parseNumber(extractAttribute(attrs, "stroke-width"));
3603
- const strokeOpacity = parseNumber(extractAttribute(attrs, "stroke-opacity"));
3604
- const opacity = parseNumber(extractAttribute(attrs, "opacity"));
3605
- const transform = extractAttribute(attrs, "transform");
3606
- const style = extractAttribute(attrs, "style");
3607
- let styleFill = fill;
3608
- let styleStroke = stroke;
3609
- let styleStrokeWidth = strokeWidth;
3610
- let styleOpacity = opacity;
3611
- if (style) {
3612
- const fillMatch = style.match(/fill\s*:\s*([^;]+)/i);
3613
- if (fillMatch) styleFill = fillMatch[1].trim();
3614
- const strokeMatch = style.match(/stroke\s*:\s*([^;]+)/i);
3615
- if (strokeMatch) styleStroke = strokeMatch[1].trim();
3616
- const strokeWidthMatch = style.match(/stroke-width\s*:\s*([^;]+)/i);
3617
- if (strokeWidthMatch) styleStrokeWidth = parseNumber(strokeWidthMatch[1].trim());
3618
- const opacityMatch = style.match(/opacity\s*:\s*([^;]+)/i);
3619
- if (opacityMatch) styleOpacity = parseNumber(opacityMatch[1].trim());
3620
- }
3621
- return {
3622
- d,
3623
- fill: styleFill,
3624
- fillOpacity,
3625
- stroke: styleStroke,
3626
- strokeWidth: styleStrokeWidth,
3627
- strokeOpacity,
3628
- opacity: styleOpacity,
3629
- transform
3630
- };
3350
+ function generateHeartPath(size) {
3351
+ const scale = size / 32;
3352
+ const points = [
3353
+ "M 0 6",
3354
+ "C -0.5 -3 -12 -3 -12 6",
3355
+ "C -12 12 0 18 0 24",
3356
+ "C 0 18 12 12 12 6",
3357
+ "C 12 -3 0.5 -3 0 6",
3358
+ "Z"
3359
+ ];
3360
+ return points.map((cmd) => {
3361
+ return cmd.replace(/-?\d+(\.\d+)?/g, (match) => {
3362
+ return String(parseFloat(match) * scale);
3363
+ });
3364
+ }).join(" ");
3631
3365
  }
3632
- function extractShapeAsPath(tagAttrs, convertFn) {
3633
- const d = convertFn(tagAttrs);
3634
- if (!d) return null;
3635
- const pathAttrs = extractPathAttributes(tagAttrs);
3636
- pathAttrs.d = d;
3637
- return pathAttrs;
3366
+ function generateCrossPath(width, height, thickness) {
3367
+ const hw = width / 2;
3368
+ const hh = height / 2;
3369
+ const ht = thickness / 2;
3370
+ return [
3371
+ `M ${-ht} ${-hh}`,
3372
+ `L ${ht} ${-hh}`,
3373
+ `L ${ht} ${-ht}`,
3374
+ `L ${hw} ${-ht}`,
3375
+ `L ${hw} ${ht}`,
3376
+ `L ${ht} ${ht}`,
3377
+ `L ${ht} ${hh}`,
3378
+ `L ${-ht} ${hh}`,
3379
+ `L ${-ht} ${ht}`,
3380
+ `L ${-hw} ${ht}`,
3381
+ `L ${-hw} ${-ht}`,
3382
+ `L ${-ht} ${-ht}`,
3383
+ "Z"
3384
+ ].join(" ");
3638
3385
  }
3639
- function parseSvgMarkup(svgString) {
3640
- const svgMatch = svgString.match(SVG_ATTRS_REGEX);
3641
- const svgAttrs = svgMatch ? svgMatch[1] : "";
3642
- const widthAttr = extractAttribute(svgAttrs, "width");
3643
- const heightAttr = extractAttribute(svgAttrs, "height");
3644
- const viewBoxAttr = extractAttribute(svgAttrs, "viewBox");
3645
- let width = parseNumber(widthAttr?.replace(/px$/, "")) || 0;
3646
- let height = parseNumber(heightAttr?.replace(/px$/, "")) || 0;
3647
- const viewBox = parseViewBox(viewBoxAttr);
3648
- if (viewBox && (!width || !height)) {
3649
- width = width || viewBox.width;
3650
- height = height || viewBox.height;
3651
- }
3652
- const paths = [];
3386
+ function generateRingPath(outerRadius, innerRadius) {
3387
+ const outerPath = generateCirclePath(outerRadius);
3388
+ const innerPath = generateCirclePathReversed(innerRadius);
3389
+ return `${outerPath} ${innerPath}`;
3390
+ }
3391
+ function generateCirclePathReversed(radius) {
3392
+ const k = radius * KAPPA;
3393
+ return [
3394
+ `M ${radius} 0`,
3395
+ `C ${radius} ${-k} ${k} ${-radius} 0 ${-radius}`,
3396
+ `C ${-k} ${-radius} ${-radius} ${-k} ${-radius} 0`,
3397
+ `C ${-radius} ${k} ${-k} ${radius} 0 ${radius}`,
3398
+ `C ${k} ${radius} ${radius} ${k} ${radius} 0`,
3399
+ "Z"
3400
+ ].join(" ");
3401
+ }
3402
+ function computeSimplePathBounds(d) {
3403
+ const numberRegex = /-?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/g;
3404
+ const numbers = [];
3653
3405
  let match;
3654
- PATH_TAG_REGEX.lastIndex = 0;
3655
- while ((match = PATH_TAG_REGEX.exec(svgString)) !== null) {
3656
- const pathAttrs = extractPathAttributes(match[1]);
3657
- if (pathAttrs.d) {
3658
- paths.push(pathAttrs);
3406
+ while ((match = numberRegex.exec(d)) !== null) {
3407
+ const num = parseFloat(match[0]);
3408
+ if (!isNaN(num) && isFinite(num)) {
3409
+ numbers.push(num);
3659
3410
  }
3660
3411
  }
3661
- RECT_TAG_REGEX.lastIndex = 0;
3662
- while ((match = RECT_TAG_REGEX.exec(svgString)) !== null) {
3663
- const path = extractShapeAsPath(match[1], rectToPath);
3664
- if (path) paths.push(path);
3665
- }
3666
- CIRCLE_TAG_REGEX.lastIndex = 0;
3667
- while ((match = CIRCLE_TAG_REGEX.exec(svgString)) !== null) {
3668
- const path = extractShapeAsPath(match[1], circleToPath);
3669
- if (path) paths.push(path);
3670
- }
3671
- ELLIPSE_TAG_REGEX.lastIndex = 0;
3672
- while ((match = ELLIPSE_TAG_REGEX.exec(svgString)) !== null) {
3673
- const path = extractShapeAsPath(match[1], ellipseToPath);
3674
- if (path) paths.push(path);
3675
- }
3676
- LINE_TAG_REGEX.lastIndex = 0;
3677
- while ((match = LINE_TAG_REGEX.exec(svgString)) !== null) {
3678
- const path = extractShapeAsPath(match[1], lineToPath);
3679
- if (path) paths.push(path);
3412
+ if (numbers.length < 2) {
3413
+ return { x: 0, y: 0, w: 0, h: 0 };
3680
3414
  }
3681
- POLYLINE_TAG_REGEX.lastIndex = 0;
3682
- while ((match = POLYLINE_TAG_REGEX.exec(svgString)) !== null) {
3683
- const path = extractShapeAsPath(match[1], polylineToPath);
3684
- if (path) paths.push(path);
3415
+ let minX = Infinity;
3416
+ let minY = Infinity;
3417
+ let maxX = -Infinity;
3418
+ let maxY = -Infinity;
3419
+ let currentX = 0;
3420
+ let currentY = 0;
3421
+ let i = 0;
3422
+ const commands = d.match(/[MmLlHhVvCcSsQqTtAaZz]/g) || [];
3423
+ const commandPositions = [];
3424
+ let searchPos = 0;
3425
+ for (const cmd of commands) {
3426
+ const pos = d.indexOf(cmd, searchPos);
3427
+ commandPositions.push(pos);
3428
+ searchPos = pos + 1;
3429
+ }
3430
+ let cmdIndex = 0;
3431
+ let numIndex = 0;
3432
+ while (cmdIndex < commands.length) {
3433
+ const cmd = commands[cmdIndex];
3434
+ switch (cmd) {
3435
+ case "M":
3436
+ case "L":
3437
+ case "T":
3438
+ if (numIndex + 1 < numbers.length) {
3439
+ currentX = numbers[numIndex++];
3440
+ currentY = numbers[numIndex++];
3441
+ minX = Math.min(minX, currentX);
3442
+ maxX = Math.max(maxX, currentX);
3443
+ minY = Math.min(minY, currentY);
3444
+ maxY = Math.max(maxY, currentY);
3445
+ }
3446
+ break;
3447
+ case "m":
3448
+ case "l":
3449
+ case "t":
3450
+ if (numIndex + 1 < numbers.length) {
3451
+ currentX += numbers[numIndex++];
3452
+ currentY += numbers[numIndex++];
3453
+ minX = Math.min(minX, currentX);
3454
+ maxX = Math.max(maxX, currentX);
3455
+ minY = Math.min(minY, currentY);
3456
+ maxY = Math.max(maxY, currentY);
3457
+ }
3458
+ break;
3459
+ case "H":
3460
+ if (numIndex < numbers.length) {
3461
+ currentX = numbers[numIndex++];
3462
+ minX = Math.min(minX, currentX);
3463
+ maxX = Math.max(maxX, currentX);
3464
+ }
3465
+ break;
3466
+ case "h":
3467
+ if (numIndex < numbers.length) {
3468
+ currentX += numbers[numIndex++];
3469
+ minX = Math.min(minX, currentX);
3470
+ maxX = Math.max(maxX, currentX);
3471
+ }
3472
+ break;
3473
+ case "V":
3474
+ if (numIndex < numbers.length) {
3475
+ currentY = numbers[numIndex++];
3476
+ minY = Math.min(minY, currentY);
3477
+ maxY = Math.max(maxY, currentY);
3478
+ }
3479
+ break;
3480
+ case "v":
3481
+ if (numIndex < numbers.length) {
3482
+ currentY += numbers[numIndex++];
3483
+ minY = Math.min(minY, currentY);
3484
+ maxY = Math.max(maxY, currentY);
3485
+ }
3486
+ break;
3487
+ case "C":
3488
+ if (numIndex + 5 < numbers.length) {
3489
+ for (let j = 0; j < 3; j++) {
3490
+ const x = numbers[numIndex++];
3491
+ const y = numbers[numIndex++];
3492
+ minX = Math.min(minX, x);
3493
+ maxX = Math.max(maxX, x);
3494
+ minY = Math.min(minY, y);
3495
+ maxY = Math.max(maxY, y);
3496
+ if (j === 2) {
3497
+ currentX = x;
3498
+ currentY = y;
3499
+ }
3500
+ }
3501
+ }
3502
+ break;
3503
+ case "c":
3504
+ if (numIndex + 5 < numbers.length) {
3505
+ for (let j = 0; j < 3; j++) {
3506
+ const x = currentX + numbers[numIndex++];
3507
+ const y = currentY + numbers[numIndex++];
3508
+ minX = Math.min(minX, x);
3509
+ maxX = Math.max(maxX, x);
3510
+ minY = Math.min(minY, y);
3511
+ maxY = Math.max(maxY, y);
3512
+ if (j === 2) {
3513
+ currentX = x;
3514
+ currentY = y;
3515
+ }
3516
+ }
3517
+ }
3518
+ break;
3519
+ case "S":
3520
+ case "Q":
3521
+ if (numIndex + 3 < numbers.length) {
3522
+ for (let j = 0; j < 2; j++) {
3523
+ const x = numbers[numIndex++];
3524
+ const y = numbers[numIndex++];
3525
+ minX = Math.min(minX, x);
3526
+ maxX = Math.max(maxX, x);
3527
+ minY = Math.min(minY, y);
3528
+ maxY = Math.max(maxY, y);
3529
+ if (j === 1) {
3530
+ currentX = x;
3531
+ currentY = y;
3532
+ }
3533
+ }
3534
+ }
3535
+ break;
3536
+ case "s":
3537
+ case "q":
3538
+ if (numIndex + 3 < numbers.length) {
3539
+ for (let j = 0; j < 2; j++) {
3540
+ const x = currentX + numbers[numIndex++];
3541
+ const y = currentY + numbers[numIndex++];
3542
+ minX = Math.min(minX, x);
3543
+ maxX = Math.max(maxX, x);
3544
+ minY = Math.min(minY, y);
3545
+ maxY = Math.max(maxY, y);
3546
+ if (j === 1) {
3547
+ currentX = x;
3548
+ currentY = y;
3549
+ }
3550
+ }
3551
+ }
3552
+ break;
3553
+ case "A":
3554
+ if (numIndex + 6 < numbers.length) {
3555
+ numIndex += 5;
3556
+ currentX = numbers[numIndex++];
3557
+ currentY = numbers[numIndex++];
3558
+ minX = Math.min(minX, currentX);
3559
+ maxX = Math.max(maxX, currentX);
3560
+ minY = Math.min(minY, currentY);
3561
+ maxY = Math.max(maxY, currentY);
3562
+ }
3563
+ break;
3564
+ case "a":
3565
+ if (numIndex + 6 < numbers.length) {
3566
+ numIndex += 5;
3567
+ currentX += numbers[numIndex++];
3568
+ currentY += numbers[numIndex++];
3569
+ minX = Math.min(minX, currentX);
3570
+ maxX = Math.max(maxX, currentX);
3571
+ minY = Math.min(minY, currentY);
3572
+ maxY = Math.max(maxY, currentY);
3573
+ }
3574
+ break;
3575
+ case "Z":
3576
+ case "z":
3577
+ break;
3578
+ }
3579
+ cmdIndex++;
3685
3580
  }
3686
- POLYGON_TAG_REGEX.lastIndex = 0;
3687
- while ((match = POLYGON_TAG_REGEX.exec(svgString)) !== null) {
3688
- const path = extractShapeAsPath(match[1], polygonToPath);
3689
- if (path) paths.push(path);
3581
+ if (minX === Infinity) {
3582
+ return { x: 0, y: 0, w: 0, h: 0 };
3690
3583
  }
3691
3584
  return {
3692
- width,
3693
- height,
3694
- viewBox,
3695
- paths
3585
+ x: minX,
3586
+ y: minY,
3587
+ w: maxX - minX,
3588
+ h: maxY - minY
3696
3589
  };
3697
3590
  }
3698
- function svgToAsset(svgString) {
3699
- const parsed = parseSvgMarkup(svgString);
3700
- if (parsed.paths.length === 0) {
3701
- return {
3702
- type: "svg",
3703
- shape: {
3704
- type: "path",
3705
- d: ""
3706
- },
3707
- width: parsed.width || void 0,
3708
- height: parsed.height || void 0
3709
- };
3710
- }
3711
- if (parsed.paths.length === 1) {
3712
- return pathToAsset(parsed.paths[0], parsed.width, parsed.height);
3713
- }
3714
- return parsed.paths.map((path) => pathToAsset(path, parsed.width, parsed.height));
3715
- }
3716
- function pathToAsset(path, width, height) {
3717
- const asset = {
3718
- type: "svg",
3719
- shape: {
3720
- type: "path",
3721
- d: path.d
3722
- }
3723
- };
3724
- const fillColor = parseColor(path.fill);
3725
- if (fillColor) {
3726
- asset.fill = {
3727
- type: "solid",
3728
- color: fillColor
3729
- };
3730
- if (path.fillOpacity !== void 0 && path.fillOpacity !== 1) {
3731
- asset.fill.opacity = path.fillOpacity;
3732
- }
3733
- }
3734
- const strokeColor = parseColor(path.stroke);
3735
- if (strokeColor && path.strokeWidth && path.strokeWidth > 0) {
3736
- asset.stroke = {
3737
- color: strokeColor,
3738
- width: path.strokeWidth
3739
- };
3740
- if (path.strokeOpacity !== void 0 && path.strokeOpacity !== 1) {
3741
- asset.stroke.opacity = path.strokeOpacity;
3742
- }
3743
- }
3744
- if (path.opacity !== void 0 && path.opacity !== 1) {
3745
- asset.opacity = path.opacity;
3746
- }
3747
- if (width > 0) {
3748
- asset.width = width;
3749
- }
3750
- if (height > 0) {
3751
- asset.height = height;
3591
+ async function renderSvgAssetToPng(asset, options = {}) {
3592
+ const defaultWidth = options.defaultWidth ?? 1920;
3593
+ const defaultHeight = options.defaultHeight ?? 1080;
3594
+ let svgString;
3595
+ let targetWidth;
3596
+ let targetHeight;
3597
+ if (asset.src) {
3598
+ svgString = asset.src;
3599
+ const dimensions = extractSvgDimensions(svgString);
3600
+ targetWidth = dimensions.width || defaultWidth;
3601
+ targetHeight = dimensions.height || defaultHeight;
3602
+ } else if (asset.shape) {
3603
+ targetWidth = asset.width ?? defaultWidth;
3604
+ targetHeight = asset.height ?? defaultHeight;
3605
+ svgString = shapeToSvgString(asset, targetWidth, targetHeight);
3606
+ } else {
3607
+ throw new Error("Either 'src' or 'shape' must be provided");
3752
3608
  }
3753
- return asset;
3609
+ return renderSvgToPng(svgString, {
3610
+ width: targetWidth,
3611
+ height: targetHeight,
3612
+ background: options.background
3613
+ });
3754
3614
  }
3755
- function svgToSingleAsset(svgString) {
3756
- const parsed = parseSvgMarkup(svgString);
3757
- if (parsed.paths.length === 0) {
3758
- return {
3759
- type: "svg",
3760
- shape: {
3761
- type: "path",
3762
- d: ""
3763
- },
3764
- width: parsed.width || void 0,
3765
- height: parsed.height || void 0
3766
- };
3767
- }
3768
- const combinedD = parsed.paths.map((p) => p.d).join(" ");
3769
- const firstPathWithFill = parsed.paths.find((p) => p.fill && p.fill !== "none");
3770
- const firstPathWithStroke = parsed.paths.find((p) => p.stroke && p.stroke !== "none");
3771
- const asset = {
3772
- type: "svg",
3773
- shape: {
3774
- type: "path",
3775
- d: combinedD
3776
- }
3777
- };
3778
- if (firstPathWithFill) {
3779
- const fillColor = parseColor(firstPathWithFill.fill);
3780
- if (fillColor) {
3781
- asset.fill = {
3782
- type: "solid",
3783
- color: fillColor
3784
- };
3785
- if (firstPathWithFill.fillOpacity !== void 0 && firstPathWithFill.fillOpacity !== 1) {
3786
- asset.fill.opacity = firstPathWithFill.fillOpacity;
3615
+ function extractSvgDimensions(svgString) {
3616
+ const widthMatch = svgString.match(/width\s*=\s*["']?(\d+(?:\.\d+)?)/i);
3617
+ const heightMatch = svgString.match(/height\s*=\s*["']?(\d+(?:\.\d+)?)/i);
3618
+ let width = widthMatch ? parseFloat(widthMatch[1]) : 0;
3619
+ let height = heightMatch ? parseFloat(heightMatch[1]) : 0;
3620
+ if (!width || !height) {
3621
+ const viewBoxMatch = svgString.match(/viewBox\s*=\s*["']([^"']+)["']/i);
3622
+ if (viewBoxMatch) {
3623
+ const parts = viewBoxMatch[1].trim().split(/[\s,]+/).map(parseFloat);
3624
+ if (parts.length === 4) {
3625
+ width = width || parts[2];
3626
+ height = height || parts[3];
3787
3627
  }
3788
3628
  }
3789
3629
  }
3790
- if (firstPathWithStroke) {
3791
- const strokeColor = parseColor(firstPathWithStroke.stroke);
3792
- if (strokeColor && firstPathWithStroke.strokeWidth && firstPathWithStroke.strokeWidth > 0) {
3793
- asset.stroke = {
3794
- color: strokeColor,
3795
- width: firstPathWithStroke.strokeWidth
3796
- };
3797
- if (firstPathWithStroke.strokeOpacity !== void 0 && firstPathWithStroke.strokeOpacity !== 1) {
3798
- asset.stroke.opacity = firstPathWithStroke.strokeOpacity;
3799
- }
3800
- }
3801
- }
3802
- const firstPathWithOpacity = parsed.paths.find((p) => p.opacity !== void 0 && p.opacity !== 1);
3803
- if (firstPathWithOpacity) {
3804
- asset.opacity = firstPathWithOpacity.opacity;
3805
- }
3806
- if (parsed.width > 0) {
3807
- asset.width = parsed.width;
3808
- }
3809
- if (parsed.height > 0) {
3810
- asset.height = parsed.height;
3811
- }
3812
- return asset;
3813
- }
3814
- function importSvg(svgString) {
3815
- return svgToSingleAsset(svgString);
3630
+ return { width, height };
3816
3631
  }
3817
3632
 
3818
3633
  // src/env/entry.node.ts
@@ -4098,38 +3913,20 @@ export {
4098
3913
  CanvasRichTextAssetSchema,
4099
3914
  CanvasSvgAssetSchema,
4100
3915
  arcToCubicBeziers,
4101
- centerPath,
4102
3916
  commandsToPathString,
4103
- computePathBounds3 as computePathBounds,
3917
+ computeSimplePathBounds,
4104
3918
  createNodePainter,
4105
3919
  createTextEngine,
4106
- generateArrowPath,
4107
- generateCirclePath,
4108
- generateCrossPath,
4109
- generateEllipsePath,
4110
- generateHeartPath,
4111
- generateLinePath,
4112
- generatePolygonPath,
4113
- generatePolygonPoints,
4114
- generateRectanglePath,
4115
- generateRingPath,
4116
- generateShapePath,
4117
- generateStarPath,
4118
- generateStarPoints,
4119
- generateTrianglePath,
4120
- importSvg,
3920
+ generateShapePathData,
4121
3921
  isGlyphFill2 as isGlyphFill,
4122
3922
  isShadowFill2 as isShadowFill,
4123
3923
  normalizePath,
4124
3924
  normalizePathString,
4125
- normalizePathToSize,
4126
- parseSvgMarkup,
4127
3925
  parseSvgPath,
4128
- pointsToPath,
4129
3926
  quadraticToCubic,
4130
- reverseWindingOrder,
4131
- rotatePath,
4132
- scalePath,
3927
+ renderSvgAssetToPng,
3928
+ renderSvgToPng,
3929
+ shapeToSvgString,
4133
3930
  svgAssetSchema,
4134
3931
  svgGradientStopSchema,
4135
3932
  svgLinearGradientFillSchema,
@@ -4138,8 +3935,5 @@ export {
4138
3935
  svgShapeSchema,
4139
3936
  svgSolidFillSchema,
4140
3937
  svgStrokeSchema,
4141
- svgToAsset,
4142
- svgToSingleAsset,
4143
- svgTransformSchema,
4144
- translatePath
3938
+ svgTransformSchema
4145
3939
  };