@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.
package/dist/entry.web.js CHANGED
@@ -2349,335 +2349,6 @@ var isGlyphFill2 = (op) => {
2349
2349
  return op.op === "FillPath" && op.isShadow !== true;
2350
2350
  };
2351
2351
 
2352
- // src/core/shape-generators.ts
2353
- var KAPPA = 0.5522847498307936;
2354
- var DEG_TO_RAD = Math.PI / 180;
2355
- function generateShapePath(params) {
2356
- switch (params.shape) {
2357
- case "rectangle":
2358
- return generateRectanglePath(params);
2359
- case "circle":
2360
- return generateCirclePath(params);
2361
- case "ellipse":
2362
- return generateEllipsePath(params);
2363
- case "line":
2364
- return generateLinePath(params);
2365
- case "triangle":
2366
- return generateTrianglePath(params);
2367
- case "polygon":
2368
- return generatePolygonPath(params);
2369
- case "star":
2370
- return generateStarPath(params);
2371
- case "arrow":
2372
- return generateArrowPath(params);
2373
- case "heart":
2374
- return generateHeartPath(params);
2375
- case "cross":
2376
- return generateCrossPath(params);
2377
- case "ring":
2378
- return generateRingPath(params);
2379
- }
2380
- }
2381
- function generateRectanglePath(params) {
2382
- const { width, height, cornerRadius = 0 } = params;
2383
- if (cornerRadius === 0) {
2384
- return `M 0 0 L ${width} 0 L ${width} ${height} L 0 ${height} Z`;
2385
- }
2386
- const r = Math.min(cornerRadius, width / 2, height / 2);
2387
- return [
2388
- `M ${r} 0`,
2389
- `L ${width - r} 0`,
2390
- `Q ${width} 0 ${width} ${r}`,
2391
- `L ${width} ${height - r}`,
2392
- `Q ${width} ${height} ${width - r} ${height}`,
2393
- `L ${r} ${height}`,
2394
- `Q 0 ${height} 0 ${height - r}`,
2395
- `L 0 ${r}`,
2396
- `Q 0 0 ${r} 0`,
2397
- "Z"
2398
- ].join(" ");
2399
- }
2400
- function generateCirclePath(params) {
2401
- const { radius } = params;
2402
- return generateEllipsePath({ radiusX: radius, radiusY: radius });
2403
- }
2404
- function generateEllipsePath(params) {
2405
- const { radiusX, radiusY } = params;
2406
- const kx = radiusX * KAPPA;
2407
- const ky = radiusY * KAPPA;
2408
- return [
2409
- `M ${radiusX} 0`,
2410
- `C ${radiusX} ${ky} ${kx} ${radiusY} 0 ${radiusY}`,
2411
- `C ${-kx} ${radiusY} ${-radiusX} ${ky} ${-radiusX} 0`,
2412
- `C ${-radiusX} ${-ky} ${-kx} ${-radiusY} 0 ${-radiusY}`,
2413
- `C ${kx} ${-radiusY} ${radiusX} ${-ky} ${radiusX} 0`,
2414
- "Z"
2415
- ].join(" ");
2416
- }
2417
- function generateLinePath(params) {
2418
- const { length, thickness } = params;
2419
- const halfThickness = thickness / 2;
2420
- return [
2421
- `M 0 ${-halfThickness}`,
2422
- `L ${length} ${-halfThickness}`,
2423
- `L ${length} ${halfThickness}`,
2424
- `L 0 ${halfThickness}`,
2425
- "Z"
2426
- ].join(" ");
2427
- }
2428
- function generateTrianglePath(params) {
2429
- const { width, height } = params;
2430
- const halfWidth = width / 2;
2431
- return `M 0 ${height} L ${halfWidth} 0 L ${width} ${height} Z`;
2432
- }
2433
- function generatePolygonPath(params) {
2434
- const { sides, radius } = params;
2435
- const points = generatePolygonPoints(sides, radius);
2436
- return pointsToPath(points, true);
2437
- }
2438
- function generatePolygonPoints(sides, radius) {
2439
- const points = [];
2440
- const angleStep = 2 * Math.PI / sides;
2441
- const startAngle = -Math.PI / 2;
2442
- for (let i = 0; i < sides; i++) {
2443
- const angle = startAngle + i * angleStep;
2444
- points.push({
2445
- x: radius * Math.cos(angle),
2446
- y: radius * Math.sin(angle)
2447
- });
2448
- }
2449
- return points;
2450
- }
2451
- function generateStarPath(params) {
2452
- const { points, outerRadius, innerRadius } = params;
2453
- const starPoints = generateStarPoints(points, outerRadius, innerRadius);
2454
- return pointsToPath(starPoints, true);
2455
- }
2456
- function generateStarPoints(pointCount, outerRadius, innerRadius) {
2457
- const points = [];
2458
- const totalPoints = pointCount * 2;
2459
- const angleStep = Math.PI / pointCount;
2460
- const startAngle = -Math.PI / 2;
2461
- for (let i = 0; i < totalPoints; i++) {
2462
- const radius = i % 2 === 0 ? outerRadius : innerRadius;
2463
- const angle = startAngle + i * angleStep;
2464
- points.push({
2465
- x: radius * Math.cos(angle),
2466
- y: radius * Math.sin(angle)
2467
- });
2468
- }
2469
- return points;
2470
- }
2471
- function generateArrowPath(params) {
2472
- const { length, headWidth, headLength, shaftWidth } = params;
2473
- const halfShaft = shaftWidth / 2;
2474
- const halfHead = headWidth / 2;
2475
- const shaftLength = length - headLength;
2476
- return [
2477
- `M 0 ${-halfShaft}`,
2478
- `L ${shaftLength} ${-halfShaft}`,
2479
- `L ${shaftLength} ${-halfHead}`,
2480
- `L ${length} 0`,
2481
- `L ${shaftLength} ${halfHead}`,
2482
- `L ${shaftLength} ${halfShaft}`,
2483
- `L 0 ${halfShaft}`,
2484
- "Z"
2485
- ].join(" ");
2486
- }
2487
- function generateHeartPath(params) {
2488
- const { size } = params;
2489
- const s = size / 2;
2490
- return [
2491
- `M 0 ${s * 0.3}`,
2492
- `C 0 ${-s * 0.3} ${-s} ${-s * 0.3} ${-s} ${s * 0.3}`,
2493
- `C ${-s} ${s * 0.7} 0 ${s} 0 ${s * 1.2}`,
2494
- `C 0 ${s} ${s} ${s * 0.7} ${s} ${s * 0.3}`,
2495
- `C ${s} ${-s * 0.3} 0 ${-s * 0.3} 0 ${s * 0.3}`,
2496
- "Z"
2497
- ].join(" ");
2498
- }
2499
- function generateCrossPath(params) {
2500
- const { width, height, thickness } = params;
2501
- const halfThickness = thickness / 2;
2502
- const halfWidth = width / 2;
2503
- const halfHeight = height / 2;
2504
- return [
2505
- `M ${-halfThickness} ${-halfHeight}`,
2506
- `L ${halfThickness} ${-halfHeight}`,
2507
- `L ${halfThickness} ${-halfThickness}`,
2508
- `L ${halfWidth} ${-halfThickness}`,
2509
- `L ${halfWidth} ${halfThickness}`,
2510
- `L ${halfThickness} ${halfThickness}`,
2511
- `L ${halfThickness} ${halfHeight}`,
2512
- `L ${-halfThickness} ${halfHeight}`,
2513
- `L ${-halfThickness} ${halfThickness}`,
2514
- `L ${-halfWidth} ${halfThickness}`,
2515
- `L ${-halfWidth} ${-halfThickness}`,
2516
- `L ${-halfThickness} ${-halfThickness}`,
2517
- "Z"
2518
- ].join(" ");
2519
- }
2520
- function generateRingPath(params) {
2521
- const { outerRadius, innerRadius } = params;
2522
- const outerPath = generateCirclePath({ radius: outerRadius });
2523
- const innerPath = generateCirclePath({ radius: innerRadius });
2524
- return `${outerPath} ${reverseWindingOrder(innerPath)}`;
2525
- }
2526
- function pointsToPath(points, closed = true) {
2527
- if (points.length === 0) return "";
2528
- const commands = [`M ${points[0].x} ${points[0].y}`];
2529
- for (let i = 1; i < points.length; i++) {
2530
- commands.push(`L ${points[i].x} ${points[i].y}`);
2531
- }
2532
- if (closed) {
2533
- commands.push("Z");
2534
- }
2535
- return commands.join(" ");
2536
- }
2537
- function reverseWindingOrder(path) {
2538
- const commands = parsePathCommands(path);
2539
- const reversed = [];
2540
- const points = [];
2541
- let currentX = 0;
2542
- let currentY = 0;
2543
- for (const cmd of commands) {
2544
- if (cmd.type === "M" || cmd.type === "L") {
2545
- points.push({ x: cmd.x, y: cmd.y });
2546
- currentX = cmd.x;
2547
- currentY = cmd.y;
2548
- } else if (cmd.type === "C") {
2549
- points.push({ x: cmd.x, y: cmd.y });
2550
- currentX = cmd.x;
2551
- currentY = cmd.y;
2552
- }
2553
- }
2554
- if (points.length === 0) return path;
2555
- points.reverse();
2556
- reversed.push(`M ${points[0].x} ${points[0].y}`);
2557
- for (let i = 1; i < points.length; i++) {
2558
- reversed.push(`L ${points[i].x} ${points[i].y}`);
2559
- }
2560
- reversed.push("Z");
2561
- return reversed.join(" ");
2562
- }
2563
- function parsePathCommands(path) {
2564
- const commands = [];
2565
- const regex = /([MLCQZ])\s*([^MLCQZ]*)/gi;
2566
- let match;
2567
- while ((match = regex.exec(path)) !== null) {
2568
- const type = match[1].toUpperCase();
2569
- const args = match[2].trim().split(/[\s,]+/).filter((s) => s.length > 0).map(parseFloat);
2570
- if (type === "M" || type === "L") {
2571
- commands.push({ type, x: args[0], y: args[1] });
2572
- } else if (type === "C") {
2573
- commands.push({
2574
- type,
2575
- x1: args[0],
2576
- y1: args[1],
2577
- x2: args[2],
2578
- y2: args[3],
2579
- x: args[4],
2580
- y: args[5]
2581
- });
2582
- } else if (type === "Q") {
2583
- commands.push({
2584
- type,
2585
- x1: args[0],
2586
- y1: args[1],
2587
- x: args[2],
2588
- y: args[3]
2589
- });
2590
- } else if (type === "Z") {
2591
- commands.push({ type });
2592
- }
2593
- }
2594
- return commands;
2595
- }
2596
- function computePathBounds3(path) {
2597
- const commands = parsePathCommands(path);
2598
- let minX = Infinity;
2599
- let minY = Infinity;
2600
- let maxX = -Infinity;
2601
- let maxY = -Infinity;
2602
- const updateBounds = (x, y) => {
2603
- minX = Math.min(minX, x);
2604
- minY = Math.min(minY, y);
2605
- maxX = Math.max(maxX, x);
2606
- maxY = Math.max(maxY, y);
2607
- };
2608
- for (const cmd of commands) {
2609
- if (cmd.x !== void 0 && cmd.y !== void 0) {
2610
- updateBounds(cmd.x, cmd.y);
2611
- }
2612
- if (cmd.x1 !== void 0 && cmd.y1 !== void 0) {
2613
- updateBounds(cmd.x1, cmd.y1);
2614
- }
2615
- if (cmd.x2 !== void 0 && cmd.y2 !== void 0) {
2616
- updateBounds(cmd.x2, cmd.y2);
2617
- }
2618
- }
2619
- if (minX === Infinity) {
2620
- return { x: 0, y: 0, w: 0, h: 0 };
2621
- }
2622
- return {
2623
- x: minX,
2624
- y: minY,
2625
- w: maxX - minX,
2626
- h: maxY - minY
2627
- };
2628
- }
2629
- function translatePath(path, dx, dy) {
2630
- return path.replace(
2631
- /(-?\d*\.?\d+(?:e[-+]?\d+)?)\s+(-?\d*\.?\d+(?:e[-+]?\d+)?)/gi,
2632
- (match, x, y) => {
2633
- const newX = parseFloat(x) + dx;
2634
- const newY = parseFloat(y) + dy;
2635
- return `${newX} ${newY}`;
2636
- }
2637
- );
2638
- }
2639
- function scalePath(path, sx, sy = sx) {
2640
- return path.replace(
2641
- /(-?\d*\.?\d+(?:e[-+]?\d+)?)\s+(-?\d*\.?\d+(?:e[-+]?\d+)?)/gi,
2642
- (match, x, y) => {
2643
- const newX = parseFloat(x) * sx;
2644
- const newY = parseFloat(y) * sy;
2645
- return `${newX} ${newY}`;
2646
- }
2647
- );
2648
- }
2649
- function rotatePath(path, angleDegrees) {
2650
- const angleRadians = angleDegrees * DEG_TO_RAD;
2651
- const cos = Math.cos(angleRadians);
2652
- const sin = Math.sin(angleRadians);
2653
- return path.replace(
2654
- /(-?\d*\.?\d+(?:e[-+]?\d+)?)\s+(-?\d*\.?\d+(?:e[-+]?\d+)?)/gi,
2655
- (match, x, y) => {
2656
- const px = parseFloat(x);
2657
- const py = parseFloat(y);
2658
- const newX = px * cos - py * sin;
2659
- const newY = px * sin + py * cos;
2660
- return `${newX} ${newY}`;
2661
- }
2662
- );
2663
- }
2664
- function centerPath(path) {
2665
- const bounds = computePathBounds3(path);
2666
- const cx = bounds.x + bounds.w / 2;
2667
- const cy = bounds.y + bounds.h / 2;
2668
- return translatePath(path, -cx, -cy);
2669
- }
2670
- function normalizePathToSize(path, targetWidth, targetHeight) {
2671
- const bounds = computePathBounds3(path);
2672
- if (bounds.w === 0 || bounds.h === 0) return path;
2673
- const scaleX = targetWidth / bounds.w;
2674
- const scaleY = targetHeight / bounds.h;
2675
- const scale = Math.min(scaleX, scaleY);
2676
- let normalized = translatePath(path, -bounds.x, -bounds.y);
2677
- normalized = scalePath(normalized, scale);
2678
- return normalized;
2679
- }
2680
-
2681
2352
  // src/core/svg-path-utils.ts
2682
2353
  var TAU = Math.PI * 2;
2683
2354
  var PATH_COMMAND_REGEX = /([MmLlHhVvCcSsQqTtAaZz])([^MmLlHhVvCcSsQqTtAaZz]*)/g;
@@ -3074,109 +2745,212 @@ function quadraticToCubic(x0, y0, qx, qy, x, y) {
3074
2745
  };
3075
2746
  }
3076
2747
 
3077
- // src/core/svg-import.ts
3078
- var SVG_ATTRS_REGEX = /<svg([^>]*)>/i;
3079
- var PATH_TAG_REGEX = /<path([^/>]*)\/?>/gi;
3080
- var RECT_TAG_REGEX = /<rect([^/>]*)\/?>/gi;
3081
- var CIRCLE_TAG_REGEX = /<circle([^/>]*)\/?>/gi;
3082
- var ELLIPSE_TAG_REGEX = /<ellipse([^/>]*)\/?>/gi;
3083
- var LINE_TAG_REGEX = /<line([^/>]*)\/?>/gi;
3084
- var POLYLINE_TAG_REGEX = /<polyline([^/>]*)\/?>/gi;
3085
- var POLYGON_TAG_REGEX = /<polygon([^/>]*)\/?>/gi;
3086
- function extractAttribute(attrs, name) {
3087
- const regex = new RegExp(`${name}\\s*=\\s*["']([^"']*)["']`, "i");
3088
- const match = attrs.match(regex);
3089
- return match ? match[1] : void 0;
3090
- }
3091
- function parseColor(color) {
3092
- if (!color || color === "none" || color === "transparent") {
3093
- return void 0;
3094
- }
3095
- color = color.trim();
3096
- if (color.startsWith("#")) {
3097
- if (color.length === 4) {
3098
- const r = color[1];
3099
- const g = color[2];
3100
- const b = color[3];
3101
- return `#${r}${r}${g}${g}${b}${b}`.toUpperCase();
3102
- }
3103
- return color.toUpperCase();
3104
- }
3105
- const rgbMatch = color.match(/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i);
3106
- if (rgbMatch) {
3107
- const r = parseInt(rgbMatch[1], 10);
3108
- const g = parseInt(rgbMatch[2], 10);
3109
- const b = parseInt(rgbMatch[3], 10);
3110
- return rgbToHex(r, g, b);
3111
- }
3112
- const rgbaMatch = color.match(/rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*[\d.]+\s*\)/i);
3113
- if (rgbaMatch) {
3114
- const r = parseInt(rgbaMatch[1], 10);
3115
- const g = parseInt(rgbaMatch[2], 10);
3116
- const b = parseInt(rgbaMatch[3], 10);
3117
- return rgbToHex(r, g, b);
3118
- }
3119
- const namedColors = {
3120
- black: "#000000",
3121
- white: "#FFFFFF",
3122
- red: "#FF0000",
3123
- green: "#008000",
3124
- blue: "#0000FF",
3125
- yellow: "#FFFF00",
3126
- cyan: "#00FFFF",
3127
- magenta: "#FF00FF",
3128
- gray: "#808080",
3129
- grey: "#808080",
3130
- orange: "#FFA500",
3131
- purple: "#800080",
3132
- pink: "#FFC0CB",
3133
- brown: "#A52A2A",
3134
- navy: "#000080",
3135
- teal: "#008080",
3136
- olive: "#808000",
3137
- maroon: "#800000",
3138
- aqua: "#00FFFF",
3139
- lime: "#00FF00",
3140
- silver: "#C0C0C0",
3141
- fuchsia: "#FF00FF"
2748
+ // src/core/resvg-renderer-web.ts
2749
+ var resvgWasmModule = null;
2750
+ var wasmInitialized = false;
2751
+ async function initResvgWasm() {
2752
+ if (resvgWasmModule && wasmInitialized) {
2753
+ return resvgWasmModule;
2754
+ }
2755
+ const wasmModule = await import("@resvg/resvg-wasm");
2756
+ resvgWasmModule = wasmModule;
2757
+ if (!wasmInitialized) {
2758
+ const wasmUrl = new URL("@resvg/resvg-wasm/index_bg.wasm", import.meta.url);
2759
+ const wasmResponse = await fetch(wasmUrl);
2760
+ const wasmBytes = await wasmResponse.arrayBuffer();
2761
+ const compiled = await WebAssembly.compile(wasmBytes);
2762
+ await resvgWasmModule.initWasm(compiled);
2763
+ wasmInitialized = true;
2764
+ }
2765
+ return resvgWasmModule;
2766
+ }
2767
+ async function initResvg(wasmBinary) {
2768
+ if (wasmInitialized) {
2769
+ return;
2770
+ }
2771
+ const wasmModule = await import("@resvg/resvg-wasm");
2772
+ resvgWasmModule = wasmModule;
2773
+ if (wasmBinary) {
2774
+ const compiled = await WebAssembly.compile(wasmBinary);
2775
+ await resvgWasmModule.initWasm(compiled);
2776
+ } else {
2777
+ const wasmUrl = new URL("@resvg/resvg-wasm/index_bg.wasm", import.meta.url);
2778
+ const wasmResponse = await fetch(wasmUrl);
2779
+ const wasmBytes = await wasmResponse.arrayBuffer();
2780
+ const compiled = await WebAssembly.compile(wasmBytes);
2781
+ await resvgWasmModule.initWasm(compiled);
2782
+ }
2783
+ wasmInitialized = true;
2784
+ }
2785
+ async function renderSvgToPng(svgString, options = {}) {
2786
+ const { Resvg } = await initResvgWasm();
2787
+ const resvgOptions = {};
2788
+ if (options.background) {
2789
+ resvgOptions.background = options.background;
2790
+ }
2791
+ if (options.fitTo) {
2792
+ resvgOptions.fitTo = options.fitTo;
2793
+ } else if (options.width) {
2794
+ resvgOptions.fitTo = { mode: "width", value: options.width };
2795
+ } else if (options.height) {
2796
+ resvgOptions.fitTo = { mode: "height", value: options.height };
2797
+ }
2798
+ const resvg = new Resvg(svgString, resvgOptions);
2799
+ const rendered = resvg.render();
2800
+ const png = rendered.asPng();
2801
+ return {
2802
+ png,
2803
+ width: rendered.width,
2804
+ height: rendered.height
3142
2805
  };
3143
- const lowerColor = color.toLowerCase();
3144
- if (namedColors[lowerColor]) {
3145
- return namedColors[lowerColor];
2806
+ }
2807
+ function shapeToSvgString(asset, defaultWidth, defaultHeight) {
2808
+ if (!asset.shape) {
2809
+ throw new Error("Shape is required for shape mode");
2810
+ }
2811
+ const width = asset.width ?? defaultWidth;
2812
+ const height = asset.height ?? defaultHeight;
2813
+ const pathData = generateShapePathData(asset.shape);
2814
+ const bounds = computeSimplePathBounds(pathData);
2815
+ const centerX = width / 2;
2816
+ const centerY = height / 2;
2817
+ const pathCenterX = bounds.x + bounds.w / 2;
2818
+ const pathCenterY = bounds.y + bounds.h / 2;
2819
+ let offsetX = centerX - pathCenterX;
2820
+ let offsetY = centerY - pathCenterY;
2821
+ if (asset.transform) {
2822
+ offsetX += asset.transform.x ?? 0;
2823
+ offsetY += asset.transform.y ?? 0;
2824
+ }
2825
+ const elements = [];
2826
+ if (asset.shadow) {
2827
+ const shadowOffsetX = asset.shadow.offsetX ?? 4;
2828
+ const shadowOffsetY = asset.shadow.offsetY ?? 4;
2829
+ const shadowColor = asset.shadow.color ?? "#000000";
2830
+ const shadowOpacity = asset.shadow.opacity ?? 0.5;
2831
+ elements.push(
2832
+ `<path d="${pathData}" transform="translate(${offsetX + shadowOffsetX}, ${offsetY + shadowOffsetY})" fill="${shadowColor}" fill-opacity="${shadowOpacity}"/>`
2833
+ );
2834
+ }
2835
+ const pathAttrs = [`d="${pathData}"`, `transform="translate(${offsetX}, ${offsetY})"`];
2836
+ if (asset.fill) {
2837
+ const fillId = `fill-${Date.now()}`;
2838
+ const fillDef = generateFillDefinition(asset.fill, fillId, bounds);
2839
+ if (fillDef.defs) {
2840
+ elements.unshift(fillDef.defs);
2841
+ }
2842
+ pathAttrs.push(`fill="${fillDef.fill}"`);
2843
+ if (asset.fill.opacity !== void 0 && asset.fill.opacity !== 1) {
2844
+ pathAttrs.push(`fill-opacity="${asset.fill.opacity}"`);
2845
+ }
2846
+ } else {
2847
+ pathAttrs.push(`fill="none"`);
2848
+ }
2849
+ if (asset.stroke && asset.stroke.width && asset.stroke.width > 0) {
2850
+ pathAttrs.push(`stroke="${asset.stroke.color ?? "#000000"}"`);
2851
+ pathAttrs.push(`stroke-width="${asset.stroke.width}"`);
2852
+ if (asset.stroke.opacity !== void 0 && asset.stroke.opacity !== 1) {
2853
+ pathAttrs.push(`stroke-opacity="${asset.stroke.opacity}"`);
2854
+ }
2855
+ if (asset.stroke.dashArray && asset.stroke.dashArray.length > 0) {
2856
+ pathAttrs.push(`stroke-dasharray="${asset.stroke.dashArray.join(" ")}"`);
2857
+ }
2858
+ if (asset.stroke.lineCap) {
2859
+ pathAttrs.push(`stroke-linecap="${asset.stroke.lineCap}"`);
2860
+ }
2861
+ if (asset.stroke.lineJoin) {
2862
+ pathAttrs.push(`stroke-linejoin="${asset.stroke.lineJoin}"`);
2863
+ }
2864
+ }
2865
+ if (asset.opacity !== void 0 && asset.opacity !== 1) {
2866
+ pathAttrs.push(`opacity="${asset.opacity}"`);
3146
2867
  }
3147
- return void 0;
2868
+ elements.push(`<path ${pathAttrs.join(" ")}/>`);
2869
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">${elements.join(
2870
+ ""
2871
+ )}</svg>`;
3148
2872
  }
3149
- function rgbToHex(r, g, b) {
3150
- const toHex = (n) => {
3151
- const hex = Math.max(0, Math.min(255, n)).toString(16);
3152
- return hex.length === 1 ? "0" + hex : hex;
3153
- };
3154
- return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();
3155
- }
3156
- function parseNumber(value) {
3157
- if (!value) return void 0;
3158
- const num = parseFloat(value);
3159
- return isNaN(num) ? void 0 : num;
3160
- }
3161
- function parseViewBox(viewBox) {
3162
- if (!viewBox) return void 0;
3163
- const parts = viewBox.trim().split(/[\s,]+/).map(parseFloat);
3164
- if (parts.length === 4 && parts.every((p) => !isNaN(p))) {
3165
- return { x: parts[0], y: parts[1], width: parts[2], height: parts[3] };
3166
- }
3167
- return void 0;
3168
- }
3169
- function rectToPath(attrs) {
3170
- const x = parseNumber(extractAttribute(attrs, "x")) || 0;
3171
- const y = parseNumber(extractAttribute(attrs, "y")) || 0;
3172
- const width = parseNumber(extractAttribute(attrs, "width")) || 0;
3173
- const height = parseNumber(extractAttribute(attrs, "height")) || 0;
3174
- const rx = parseNumber(extractAttribute(attrs, "rx")) || 0;
3175
- const ry = parseNumber(extractAttribute(attrs, "ry")) || rx;
3176
- if (rx === 0 && ry === 0) {
2873
+ function generateFillDefinition(fill, id, _bounds) {
2874
+ if (fill.type === "solid") {
2875
+ return { fill: fill.color };
2876
+ }
2877
+ if (fill.type === "linear") {
2878
+ const angle = fill.angle ?? 0;
2879
+ const rad = angle * Math.PI / 180;
2880
+ const x1 = 50 - Math.cos(rad) * 50;
2881
+ const y1 = 50 - Math.sin(rad) * 50;
2882
+ const x2 = 50 + Math.cos(rad) * 50;
2883
+ const y2 = 50 + Math.sin(rad) * 50;
2884
+ const stops = fill.stops.map((s) => `<stop offset="${s.offset * 100}%" stop-color="${s.color}"/>`).join("");
2885
+ const defs = `<defs><linearGradient id="${id}" x1="${x1}%" y1="${y1}%" x2="${x2}%" y2="${y2}%">${stops}</linearGradient></defs>`;
2886
+ return { fill: `url(#${id})`, defs };
2887
+ }
2888
+ if (fill.type === "radial") {
2889
+ const stops = fill.stops.map((s) => `<stop offset="${s.offset * 100}%" stop-color="${s.color}"/>`).join("");
2890
+ const defs = `<defs><radialGradient id="${id}" cx="50%" cy="50%" r="50%">${stops}</radialGradient></defs>`;
2891
+ return { fill: `url(#${id})`, defs };
2892
+ }
2893
+ return { fill: "#000000" };
2894
+ }
2895
+ function generateShapePathData(shape) {
2896
+ const shapeType = shape.type;
2897
+ switch (shapeType) {
2898
+ case "rectangle": {
2899
+ const s = shape;
2900
+ return generateRectanglePath(s.width, s.height, s.cornerRadius ?? 0);
2901
+ }
2902
+ case "circle": {
2903
+ const s = shape;
2904
+ return generateCirclePath(s.radius);
2905
+ }
2906
+ case "ellipse": {
2907
+ const s = shape;
2908
+ return generateEllipsePath(s.radiusX, s.radiusY);
2909
+ }
2910
+ case "line": {
2911
+ const s = shape;
2912
+ return generateLinePath(s.length, s.thickness ?? 2);
2913
+ }
2914
+ case "polygon": {
2915
+ const s = shape;
2916
+ return generatePolygonPath(s.sides, s.radius);
2917
+ }
2918
+ case "star": {
2919
+ const s = shape;
2920
+ return generateStarPath(s.points, s.outerRadius, s.innerRadius);
2921
+ }
2922
+ case "arrow": {
2923
+ const s = shape;
2924
+ return generateArrowPath(s.length, s.headWidth ?? 30, s.headLength ?? 25, s.shaftWidth ?? 10);
2925
+ }
2926
+ case "heart": {
2927
+ const s = shape;
2928
+ return generateHeartPath(s.size);
2929
+ }
2930
+ case "cross": {
2931
+ const s = shape;
2932
+ return generateCrossPath(s.width, s.height, s.thickness);
2933
+ }
2934
+ case "ring": {
2935
+ const s = shape;
2936
+ return generateRingPath(s.outerRadius, s.innerRadius);
2937
+ }
2938
+ case "path": {
2939
+ const s = shape;
2940
+ return s.d;
2941
+ }
2942
+ default:
2943
+ throw new Error(`Unknown shape type: ${shapeType}`);
2944
+ }
2945
+ }
2946
+ var KAPPA = 0.5522847498307936;
2947
+ function generateRectanglePath(width, height, cornerRadius) {
2948
+ const x = -width / 2;
2949
+ const y = -height / 2;
2950
+ const r = Math.min(cornerRadius, width / 2, height / 2);
2951
+ if (r === 0) {
3177
2952
  return `M ${x} ${y} L ${x + width} ${y} L ${x + width} ${y + height} L ${x} ${y + height} Z`;
3178
2953
  }
3179
- const r = Math.min(rx, ry, width / 2, height / 2);
3180
2954
  return [
3181
2955
  `M ${x + r} ${y}`,
3182
2956
  `L ${x + width - r} ${y}`,
@@ -3190,288 +2964,350 @@ function rectToPath(attrs) {
3190
2964
  "Z"
3191
2965
  ].join(" ");
3192
2966
  }
3193
- function circleToPath(attrs) {
3194
- const cx = parseNumber(extractAttribute(attrs, "cx")) || 0;
3195
- const cy = parseNumber(extractAttribute(attrs, "cy")) || 0;
3196
- const r = parseNumber(extractAttribute(attrs, "r")) || 0;
3197
- if (r === 0) return "";
3198
- const KAPPA2 = 0.5522847498307936;
3199
- const k = r * KAPPA2;
2967
+ function generateCirclePath(radius) {
2968
+ const k = radius * KAPPA;
3200
2969
  return [
3201
- `M ${cx + r} ${cy}`,
3202
- `C ${cx + r} ${cy + k} ${cx + k} ${cy + r} ${cx} ${cy + r}`,
3203
- `C ${cx - k} ${cy + r} ${cx - r} ${cy + k} ${cx - r} ${cy}`,
3204
- `C ${cx - r} ${cy - k} ${cx - k} ${cy - r} ${cx} ${cy - r}`,
3205
- `C ${cx + k} ${cy - r} ${cx + r} ${cy - k} ${cx + r} ${cy}`,
2970
+ `M ${radius} 0`,
2971
+ `C ${radius} ${k} ${k} ${radius} 0 ${radius}`,
2972
+ `C ${-k} ${radius} ${-radius} ${k} ${-radius} 0`,
2973
+ `C ${-radius} ${-k} ${-k} ${-radius} 0 ${-radius}`,
2974
+ `C ${k} ${-radius} ${radius} ${-k} ${radius} 0`,
3206
2975
  "Z"
3207
2976
  ].join(" ");
3208
2977
  }
3209
- function ellipseToPath(attrs) {
3210
- const cx = parseNumber(extractAttribute(attrs, "cx")) || 0;
3211
- const cy = parseNumber(extractAttribute(attrs, "cy")) || 0;
3212
- const rx = parseNumber(extractAttribute(attrs, "rx")) || 0;
3213
- const ry = parseNumber(extractAttribute(attrs, "ry")) || 0;
3214
- if (rx === 0 || ry === 0) return "";
3215
- const KAPPA2 = 0.5522847498307936;
3216
- const kx = rx * KAPPA2;
3217
- const ky = ry * KAPPA2;
2978
+ function generateCirclePathReversed(radius) {
2979
+ const k = radius * KAPPA;
3218
2980
  return [
3219
- `M ${cx + rx} ${cy}`,
3220
- `C ${cx + rx} ${cy + ky} ${cx + kx} ${cy + ry} ${cx} ${cy + ry}`,
3221
- `C ${cx - kx} ${cy + ry} ${cx - rx} ${cy + ky} ${cx - rx} ${cy}`,
3222
- `C ${cx - rx} ${cy - ky} ${cx - kx} ${cy - ry} ${cx} ${cy - ry}`,
3223
- `C ${cx + kx} ${cy - ry} ${cx + rx} ${cy - ky} ${cx + rx} ${cy}`,
2981
+ `M ${radius} 0`,
2982
+ `C ${radius} ${-k} ${k} ${-radius} 0 ${-radius}`,
2983
+ `C ${-k} ${-radius} ${-radius} ${-k} ${-radius} 0`,
2984
+ `C ${-radius} ${k} ${-k} ${radius} 0 ${radius}`,
2985
+ `C ${k} ${radius} ${radius} ${k} ${radius} 0`,
2986
+ "Z"
2987
+ ].join(" ");
2988
+ }
2989
+ function generateEllipsePath(radiusX, radiusY) {
2990
+ const kx = radiusX * KAPPA;
2991
+ const ky = radiusY * KAPPA;
2992
+ return [
2993
+ `M ${radiusX} 0`,
2994
+ `C ${radiusX} ${ky} ${kx} ${radiusY} 0 ${radiusY}`,
2995
+ `C ${-kx} ${radiusY} ${-radiusX} ${ky} ${-radiusX} 0`,
2996
+ `C ${-radiusX} ${-ky} ${-kx} ${-radiusY} 0 ${-radiusY}`,
2997
+ `C ${kx} ${-radiusY} ${radiusX} ${-ky} ${radiusX} 0`,
3224
2998
  "Z"
3225
2999
  ].join(" ");
3226
3000
  }
3227
- function lineToPath(attrs) {
3228
- const x1 = parseNumber(extractAttribute(attrs, "x1")) || 0;
3229
- const y1 = parseNumber(extractAttribute(attrs, "y1")) || 0;
3230
- const x2 = parseNumber(extractAttribute(attrs, "x2")) || 0;
3231
- const y2 = parseNumber(extractAttribute(attrs, "y2")) || 0;
3232
- return `M ${x1} ${y1} L ${x2} ${y2}`;
3233
- }
3234
- function polylineToPath(attrs) {
3235
- const points = extractAttribute(attrs, "points");
3236
- if (!points) return "";
3237
- const coords = points.trim().split(/[\s,]+/).map(parseFloat);
3238
- if (coords.length < 2) return "";
3001
+ function generateLinePath(length, thickness) {
3002
+ const halfLen = length / 2;
3003
+ const halfThick = thickness / 2;
3004
+ return `M ${-halfLen} ${-halfThick} L ${halfLen} ${-halfThick} L ${halfLen} ${halfThick} L ${-halfLen} ${halfThick} Z`;
3005
+ }
3006
+ function generatePolygonPath(sides, radius) {
3007
+ const angleStep = 2 * Math.PI / sides;
3008
+ const startAngle = -Math.PI / 2;
3009
+ const points = [];
3010
+ for (let i = 0; i < sides; i++) {
3011
+ const angle = startAngle + i * angleStep;
3012
+ const x = Math.cos(angle) * radius;
3013
+ const y = Math.sin(angle) * radius;
3014
+ points.push(i === 0 ? `M ${x} ${y}` : `L ${x} ${y}`);
3015
+ }
3016
+ points.push("Z");
3017
+ return points.join(" ");
3018
+ }
3019
+ function generateStarPath(points, outerRadius, innerRadius) {
3020
+ const angleStep = Math.PI / points;
3021
+ const startAngle = -Math.PI / 2;
3239
3022
  const pathParts = [];
3240
- for (let i = 0; i < coords.length; i += 2) {
3241
- if (i + 1 < coords.length) {
3242
- if (i === 0) {
3243
- pathParts.push(`M ${coords[i]} ${coords[i + 1]}`);
3244
- } else {
3245
- pathParts.push(`L ${coords[i]} ${coords[i + 1]}`);
3246
- }
3247
- }
3023
+ for (let i = 0; i < points * 2; i++) {
3024
+ const angle = startAngle + i * angleStep;
3025
+ const radius = i % 2 === 0 ? outerRadius : innerRadius;
3026
+ const x = Math.cos(angle) * radius;
3027
+ const y = Math.sin(angle) * radius;
3028
+ pathParts.push(i === 0 ? `M ${x} ${y}` : `L ${x} ${y}`);
3248
3029
  }
3030
+ pathParts.push("Z");
3249
3031
  return pathParts.join(" ");
3250
3032
  }
3251
- function polygonToPath(attrs) {
3252
- const polyPath = polylineToPath(attrs);
3253
- if (!polyPath) return "";
3254
- return polyPath + " Z";
3255
- }
3256
- function extractPathAttributes(attrs) {
3257
- const d = extractAttribute(attrs, "d") || "";
3258
- const fill = extractAttribute(attrs, "fill");
3259
- const fillOpacity = parseNumber(extractAttribute(attrs, "fill-opacity"));
3260
- const stroke = extractAttribute(attrs, "stroke");
3261
- const strokeWidth = parseNumber(extractAttribute(attrs, "stroke-width"));
3262
- const strokeOpacity = parseNumber(extractAttribute(attrs, "stroke-opacity"));
3263
- const opacity = parseNumber(extractAttribute(attrs, "opacity"));
3264
- const transform = extractAttribute(attrs, "transform");
3265
- const style = extractAttribute(attrs, "style");
3266
- let styleFill = fill;
3267
- let styleStroke = stroke;
3268
- let styleStrokeWidth = strokeWidth;
3269
- let styleOpacity = opacity;
3270
- if (style) {
3271
- const fillMatch = style.match(/fill\s*:\s*([^;]+)/i);
3272
- if (fillMatch) styleFill = fillMatch[1].trim();
3273
- const strokeMatch = style.match(/stroke\s*:\s*([^;]+)/i);
3274
- if (strokeMatch) styleStroke = strokeMatch[1].trim();
3275
- const strokeWidthMatch = style.match(/stroke-width\s*:\s*([^;]+)/i);
3276
- if (strokeWidthMatch) styleStrokeWidth = parseNumber(strokeWidthMatch[1].trim());
3277
- const opacityMatch = style.match(/opacity\s*:\s*([^;]+)/i);
3278
- if (opacityMatch) styleOpacity = parseNumber(opacityMatch[1].trim());
3279
- }
3280
- return {
3281
- d,
3282
- fill: styleFill,
3283
- fillOpacity,
3284
- stroke: styleStroke,
3285
- strokeWidth: styleStrokeWidth,
3286
- strokeOpacity,
3287
- opacity: styleOpacity,
3288
- transform
3289
- };
3033
+ function generateArrowPath(length, headWidth, headLength, shaftWidth) {
3034
+ const halfShaft = shaftWidth / 2;
3035
+ const halfHead = headWidth / 2;
3036
+ const shaftLength = length - headLength;
3037
+ const startX = -length / 2;
3038
+ return [
3039
+ `M ${startX} ${-halfShaft}`,
3040
+ `L ${startX + shaftLength} ${-halfShaft}`,
3041
+ `L ${startX + shaftLength} ${-halfHead}`,
3042
+ `L ${startX + length} 0`,
3043
+ `L ${startX + shaftLength} ${halfHead}`,
3044
+ `L ${startX + shaftLength} ${halfShaft}`,
3045
+ `L ${startX} ${halfShaft}`,
3046
+ "Z"
3047
+ ].join(" ");
3290
3048
  }
3291
- function extractShapeAsPath(tagAttrs, convertFn) {
3292
- const d = convertFn(tagAttrs);
3293
- if (!d) return null;
3294
- const pathAttrs = extractPathAttributes(tagAttrs);
3295
- pathAttrs.d = d;
3296
- return pathAttrs;
3297
- }
3298
- function parseSvgMarkup(svgString) {
3299
- const svgMatch = svgString.match(SVG_ATTRS_REGEX);
3300
- const svgAttrs = svgMatch ? svgMatch[1] : "";
3301
- const widthAttr = extractAttribute(svgAttrs, "width");
3302
- const heightAttr = extractAttribute(svgAttrs, "height");
3303
- const viewBoxAttr = extractAttribute(svgAttrs, "viewBox");
3304
- let width = parseNumber(widthAttr?.replace(/px$/, "")) || 0;
3305
- let height = parseNumber(heightAttr?.replace(/px$/, "")) || 0;
3306
- const viewBox = parseViewBox(viewBoxAttr);
3307
- if (viewBox && (!width || !height)) {
3308
- width = width || viewBox.width;
3309
- height = height || viewBox.height;
3310
- }
3311
- const paths = [];
3312
- let match;
3313
- PATH_TAG_REGEX.lastIndex = 0;
3314
- while ((match = PATH_TAG_REGEX.exec(svgString)) !== null) {
3315
- const pathAttrs = extractPathAttributes(match[1]);
3316
- if (pathAttrs.d) {
3317
- paths.push(pathAttrs);
3318
- }
3319
- }
3320
- RECT_TAG_REGEX.lastIndex = 0;
3321
- while ((match = RECT_TAG_REGEX.exec(svgString)) !== null) {
3322
- const path = extractShapeAsPath(match[1], rectToPath);
3323
- if (path) paths.push(path);
3324
- }
3325
- CIRCLE_TAG_REGEX.lastIndex = 0;
3326
- while ((match = CIRCLE_TAG_REGEX.exec(svgString)) !== null) {
3327
- const path = extractShapeAsPath(match[1], circleToPath);
3328
- if (path) paths.push(path);
3329
- }
3330
- ELLIPSE_TAG_REGEX.lastIndex = 0;
3331
- while ((match = ELLIPSE_TAG_REGEX.exec(svgString)) !== null) {
3332
- const path = extractShapeAsPath(match[1], ellipseToPath);
3333
- if (path) paths.push(path);
3334
- }
3335
- LINE_TAG_REGEX.lastIndex = 0;
3336
- while ((match = LINE_TAG_REGEX.exec(svgString)) !== null) {
3337
- const path = extractShapeAsPath(match[1], lineToPath);
3338
- if (path) paths.push(path);
3339
- }
3340
- POLYLINE_TAG_REGEX.lastIndex = 0;
3341
- while ((match = POLYLINE_TAG_REGEX.exec(svgString)) !== null) {
3342
- const path = extractShapeAsPath(match[1], polylineToPath);
3343
- if (path) paths.push(path);
3344
- }
3345
- POLYGON_TAG_REGEX.lastIndex = 0;
3346
- while ((match = POLYGON_TAG_REGEX.exec(svgString)) !== null) {
3347
- const path = extractShapeAsPath(match[1], polygonToPath);
3348
- if (path) paths.push(path);
3349
- }
3350
- return {
3351
- width,
3352
- height,
3353
- viewBox,
3354
- paths
3355
- };
3049
+ function generateHeartPath(size) {
3050
+ const scale = size / 32;
3051
+ const points = [
3052
+ "M 0 6",
3053
+ "C -0.5 -3 -12 -3 -12 6",
3054
+ "C -12 12 0 18 0 24",
3055
+ "C 0 18 12 12 12 6",
3056
+ "C 12 -3 0.5 -3 0 6",
3057
+ "Z"
3058
+ ];
3059
+ return points.map((cmd) => {
3060
+ return cmd.replace(/-?\d+(\.\d+)?/g, (match) => {
3061
+ return String(parseFloat(match) * scale);
3062
+ });
3063
+ }).join(" ");
3356
3064
  }
3357
- function svgToAsset(svgString) {
3358
- const parsed = parseSvgMarkup(svgString);
3359
- if (parsed.paths.length === 0) {
3360
- return {
3361
- type: "svg",
3362
- shape: {
3363
- type: "path",
3364
- d: ""
3365
- },
3366
- width: parsed.width || void 0,
3367
- height: parsed.height || void 0
3368
- };
3369
- }
3370
- if (parsed.paths.length === 1) {
3371
- return pathToAsset(parsed.paths[0], parsed.width, parsed.height);
3372
- }
3373
- return parsed.paths.map((path) => pathToAsset(path, parsed.width, parsed.height));
3065
+ function generateCrossPath(width, height, thickness) {
3066
+ const hw = width / 2;
3067
+ const hh = height / 2;
3068
+ const ht = thickness / 2;
3069
+ return [
3070
+ `M ${-ht} ${-hh}`,
3071
+ `L ${ht} ${-hh}`,
3072
+ `L ${ht} ${-ht}`,
3073
+ `L ${hw} ${-ht}`,
3074
+ `L ${hw} ${ht}`,
3075
+ `L ${ht} ${ht}`,
3076
+ `L ${ht} ${hh}`,
3077
+ `L ${-ht} ${hh}`,
3078
+ `L ${-ht} ${ht}`,
3079
+ `L ${-hw} ${ht}`,
3080
+ `L ${-hw} ${-ht}`,
3081
+ `L ${-ht} ${-ht}`,
3082
+ "Z"
3083
+ ].join(" ");
3374
3084
  }
3375
- function pathToAsset(path, width, height) {
3376
- const asset = {
3377
- type: "svg",
3378
- shape: {
3379
- type: "path",
3380
- d: path.d
3381
- }
3382
- };
3383
- const fillColor = parseColor(path.fill);
3384
- if (fillColor) {
3385
- asset.fill = {
3386
- type: "solid",
3387
- color: fillColor
3388
- };
3389
- if (path.fillOpacity !== void 0 && path.fillOpacity !== 1) {
3390
- asset.fill.opacity = path.fillOpacity;
3391
- }
3392
- }
3393
- const strokeColor = parseColor(path.stroke);
3394
- if (strokeColor && path.strokeWidth && path.strokeWidth > 0) {
3395
- asset.stroke = {
3396
- color: strokeColor,
3397
- width: path.strokeWidth
3398
- };
3399
- if (path.strokeOpacity !== void 0 && path.strokeOpacity !== 1) {
3400
- asset.stroke.opacity = path.strokeOpacity;
3085
+ function generateRingPath(outerRadius, innerRadius) {
3086
+ const outerPath = generateCirclePath(outerRadius);
3087
+ const innerPath = generateCirclePathReversed(innerRadius);
3088
+ return `${outerPath} ${innerPath}`;
3089
+ }
3090
+ function computeSimplePathBounds(d) {
3091
+ const numberRegex = /-?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/g;
3092
+ const numbers = [];
3093
+ let match;
3094
+ while ((match = numberRegex.exec(d)) !== null) {
3095
+ const num = parseFloat(match[0]);
3096
+ if (!isNaN(num) && isFinite(num)) {
3097
+ numbers.push(num);
3401
3098
  }
3402
3099
  }
3403
- if (path.opacity !== void 0 && path.opacity !== 1) {
3404
- asset.opacity = path.opacity;
3405
- }
3406
- if (width > 0) {
3407
- asset.width = width;
3100
+ if (numbers.length < 2) {
3101
+ return { x: 0, y: 0, w: 0, h: 0 };
3408
3102
  }
3409
- if (height > 0) {
3410
- asset.height = height;
3103
+ let minX = Infinity;
3104
+ let minY = Infinity;
3105
+ let maxX = -Infinity;
3106
+ let maxY = -Infinity;
3107
+ let currentX = 0;
3108
+ let currentY = 0;
3109
+ const commands = d.match(/[MmLlHhVvCcSsQqTtAaZz]/g) || [];
3110
+ let cmdIndex = 0;
3111
+ let numIndex = 0;
3112
+ while (cmdIndex < commands.length) {
3113
+ const cmd = commands[cmdIndex];
3114
+ switch (cmd) {
3115
+ case "M":
3116
+ case "L":
3117
+ case "T":
3118
+ if (numIndex + 1 < numbers.length) {
3119
+ currentX = numbers[numIndex++];
3120
+ currentY = numbers[numIndex++];
3121
+ minX = Math.min(minX, currentX);
3122
+ maxX = Math.max(maxX, currentX);
3123
+ minY = Math.min(minY, currentY);
3124
+ maxY = Math.max(maxY, currentY);
3125
+ }
3126
+ break;
3127
+ case "m":
3128
+ case "l":
3129
+ case "t":
3130
+ if (numIndex + 1 < numbers.length) {
3131
+ currentX += numbers[numIndex++];
3132
+ currentY += numbers[numIndex++];
3133
+ minX = Math.min(minX, currentX);
3134
+ maxX = Math.max(maxX, currentX);
3135
+ minY = Math.min(minY, currentY);
3136
+ maxY = Math.max(maxY, currentY);
3137
+ }
3138
+ break;
3139
+ case "H":
3140
+ if (numIndex < numbers.length) {
3141
+ currentX = numbers[numIndex++];
3142
+ minX = Math.min(minX, currentX);
3143
+ maxX = Math.max(maxX, currentX);
3144
+ }
3145
+ break;
3146
+ case "h":
3147
+ if (numIndex < numbers.length) {
3148
+ currentX += numbers[numIndex++];
3149
+ minX = Math.min(minX, currentX);
3150
+ maxX = Math.max(maxX, currentX);
3151
+ }
3152
+ break;
3153
+ case "V":
3154
+ if (numIndex < numbers.length) {
3155
+ currentY = numbers[numIndex++];
3156
+ minY = Math.min(minY, currentY);
3157
+ maxY = Math.max(maxY, currentY);
3158
+ }
3159
+ break;
3160
+ case "v":
3161
+ if (numIndex < numbers.length) {
3162
+ currentY += numbers[numIndex++];
3163
+ minY = Math.min(minY, currentY);
3164
+ maxY = Math.max(maxY, currentY);
3165
+ }
3166
+ break;
3167
+ case "C":
3168
+ if (numIndex + 5 < numbers.length) {
3169
+ for (let j = 0; j < 3; j++) {
3170
+ const x = numbers[numIndex++];
3171
+ const y = numbers[numIndex++];
3172
+ minX = Math.min(minX, x);
3173
+ maxX = Math.max(maxX, x);
3174
+ minY = Math.min(minY, y);
3175
+ maxY = Math.max(maxY, y);
3176
+ if (j === 2) {
3177
+ currentX = x;
3178
+ currentY = y;
3179
+ }
3180
+ }
3181
+ }
3182
+ break;
3183
+ case "c":
3184
+ if (numIndex + 5 < numbers.length) {
3185
+ for (let j = 0; j < 3; j++) {
3186
+ const x = currentX + numbers[numIndex++];
3187
+ const y = currentY + numbers[numIndex++];
3188
+ minX = Math.min(minX, x);
3189
+ maxX = Math.max(maxX, x);
3190
+ minY = Math.min(minY, y);
3191
+ maxY = Math.max(maxY, y);
3192
+ if (j === 2) {
3193
+ currentX = x;
3194
+ currentY = y;
3195
+ }
3196
+ }
3197
+ }
3198
+ break;
3199
+ case "S":
3200
+ case "Q":
3201
+ if (numIndex + 3 < numbers.length) {
3202
+ for (let j = 0; j < 2; j++) {
3203
+ const x = numbers[numIndex++];
3204
+ const y = numbers[numIndex++];
3205
+ minX = Math.min(minX, x);
3206
+ maxX = Math.max(maxX, x);
3207
+ minY = Math.min(minY, y);
3208
+ maxY = Math.max(maxY, y);
3209
+ if (j === 1) {
3210
+ currentX = x;
3211
+ currentY = y;
3212
+ }
3213
+ }
3214
+ }
3215
+ break;
3216
+ case "s":
3217
+ case "q":
3218
+ if (numIndex + 3 < numbers.length) {
3219
+ for (let j = 0; j < 2; j++) {
3220
+ const x = currentX + numbers[numIndex++];
3221
+ const y = currentY + numbers[numIndex++];
3222
+ minX = Math.min(minX, x);
3223
+ maxX = Math.max(maxX, x);
3224
+ minY = Math.min(minY, y);
3225
+ maxY = Math.max(maxY, y);
3226
+ if (j === 1) {
3227
+ currentX = x;
3228
+ currentY = y;
3229
+ }
3230
+ }
3231
+ }
3232
+ break;
3233
+ case "A":
3234
+ if (numIndex + 6 < numbers.length) {
3235
+ numIndex += 5;
3236
+ currentX = numbers[numIndex++];
3237
+ currentY = numbers[numIndex++];
3238
+ minX = Math.min(minX, currentX);
3239
+ maxX = Math.max(maxX, currentX);
3240
+ minY = Math.min(minY, currentY);
3241
+ maxY = Math.max(maxY, currentY);
3242
+ }
3243
+ break;
3244
+ case "a":
3245
+ if (numIndex + 6 < numbers.length) {
3246
+ numIndex += 5;
3247
+ currentX += numbers[numIndex++];
3248
+ currentY += numbers[numIndex++];
3249
+ minX = Math.min(minX, currentX);
3250
+ maxX = Math.max(maxX, currentX);
3251
+ minY = Math.min(minY, currentY);
3252
+ maxY = Math.max(maxY, currentY);
3253
+ }
3254
+ break;
3255
+ case "Z":
3256
+ case "z":
3257
+ break;
3258
+ }
3259
+ cmdIndex++;
3411
3260
  }
3412
- return asset;
3413
- }
3414
- function svgToSingleAsset(svgString) {
3415
- const parsed = parseSvgMarkup(svgString);
3416
- if (parsed.paths.length === 0) {
3417
- return {
3418
- type: "svg",
3419
- shape: {
3420
- type: "path",
3421
- d: ""
3422
- },
3423
- width: parsed.width || void 0,
3424
- height: parsed.height || void 0
3425
- };
3261
+ if (minX === Infinity) {
3262
+ return { x: 0, y: 0, w: 0, h: 0 };
3426
3263
  }
3427
- const combinedD = parsed.paths.map((p) => p.d).join(" ");
3428
- const firstPathWithFill = parsed.paths.find((p) => p.fill && p.fill !== "none");
3429
- const firstPathWithStroke = parsed.paths.find((p) => p.stroke && p.stroke !== "none");
3430
- const asset = {
3431
- type: "svg",
3432
- shape: {
3433
- type: "path",
3434
- d: combinedD
3435
- }
3264
+ return {
3265
+ x: minX,
3266
+ y: minY,
3267
+ w: maxX - minX,
3268
+ h: maxY - minY
3436
3269
  };
3437
- if (firstPathWithFill) {
3438
- const fillColor = parseColor(firstPathWithFill.fill);
3439
- if (fillColor) {
3440
- asset.fill = {
3441
- type: "solid",
3442
- color: fillColor
3443
- };
3444
- if (firstPathWithFill.fillOpacity !== void 0 && firstPathWithFill.fillOpacity !== 1) {
3445
- asset.fill.opacity = firstPathWithFill.fillOpacity;
3446
- }
3447
- }
3270
+ }
3271
+ async function renderSvgAssetToPng(asset, options = {}) {
3272
+ const defaultWidth = options.defaultWidth ?? 1920;
3273
+ const defaultHeight = options.defaultHeight ?? 1080;
3274
+ let svgString;
3275
+ let targetWidth;
3276
+ let targetHeight;
3277
+ if (asset.src) {
3278
+ svgString = asset.src;
3279
+ const dimensions = extractSvgDimensions(svgString);
3280
+ targetWidth = dimensions.width || defaultWidth;
3281
+ targetHeight = dimensions.height || defaultHeight;
3282
+ } else if (asset.shape) {
3283
+ targetWidth = asset.width ?? defaultWidth;
3284
+ targetHeight = asset.height ?? defaultHeight;
3285
+ svgString = shapeToSvgString(asset, targetWidth, targetHeight);
3286
+ } else {
3287
+ throw new Error("Either 'src' or 'shape' must be provided");
3448
3288
  }
3449
- if (firstPathWithStroke) {
3450
- const strokeColor = parseColor(firstPathWithStroke.stroke);
3451
- if (strokeColor && firstPathWithStroke.strokeWidth && firstPathWithStroke.strokeWidth > 0) {
3452
- asset.stroke = {
3453
- color: strokeColor,
3454
- width: firstPathWithStroke.strokeWidth
3455
- };
3456
- if (firstPathWithStroke.strokeOpacity !== void 0 && firstPathWithStroke.strokeOpacity !== 1) {
3457
- asset.stroke.opacity = firstPathWithStroke.strokeOpacity;
3289
+ return renderSvgToPng(svgString, {
3290
+ width: targetWidth,
3291
+ height: targetHeight,
3292
+ background: options.background
3293
+ });
3294
+ }
3295
+ function extractSvgDimensions(svgString) {
3296
+ const widthMatch = svgString.match(/width\s*=\s*["']?(\d+(?:\.\d+)?)/i);
3297
+ const heightMatch = svgString.match(/height\s*=\s*["']?(\d+(?:\.\d+)?)/i);
3298
+ let width = widthMatch ? parseFloat(widthMatch[1]) : 0;
3299
+ let height = heightMatch ? parseFloat(heightMatch[1]) : 0;
3300
+ if (!width || !height) {
3301
+ const viewBoxMatch = svgString.match(/viewBox\s*=\s*["']([^"']+)["']/i);
3302
+ if (viewBoxMatch) {
3303
+ const parts = viewBoxMatch[1].trim().split(/[\s,]+/).map(parseFloat);
3304
+ if (parts.length === 4) {
3305
+ width = width || parts[2];
3306
+ height = height || parts[3];
3458
3307
  }
3459
3308
  }
3460
3309
  }
3461
- const firstPathWithOpacity = parsed.paths.find((p) => p.opacity !== void 0 && p.opacity !== 1);
3462
- if (firstPathWithOpacity) {
3463
- asset.opacity = firstPathWithOpacity.opacity;
3464
- }
3465
- if (parsed.width > 0) {
3466
- asset.width = parsed.width;
3467
- }
3468
- if (parsed.height > 0) {
3469
- asset.height = parsed.height;
3470
- }
3471
- return asset;
3472
- }
3473
- function importSvg(svgString) {
3474
- return svgToSingleAsset(svgString);
3310
+ return { width, height };
3475
3311
  }
3476
3312
 
3477
3313
  // src/env/entry.web.ts
@@ -3738,38 +3574,21 @@ export {
3738
3574
  CanvasRichTextAssetSchema,
3739
3575
  CanvasSvgAssetSchema,
3740
3576
  arcToCubicBeziers,
3741
- centerPath,
3742
3577
  commandsToPathString,
3743
- computePathBounds3 as computePathBounds,
3578
+ computeSimplePathBounds,
3744
3579
  createTextEngine,
3745
3580
  createWebPainter,
3746
- generateArrowPath,
3747
- generateCirclePath,
3748
- generateCrossPath,
3749
- generateEllipsePath,
3750
- generateHeartPath,
3751
- generateLinePath,
3752
- generatePolygonPath,
3753
- generatePolygonPoints,
3754
- generateRectanglePath,
3755
- generateRingPath,
3756
- generateShapePath,
3757
- generateStarPath,
3758
- generateStarPoints,
3759
- generateTrianglePath,
3760
- importSvg,
3581
+ generateShapePathData,
3582
+ initResvg,
3761
3583
  isGlyphFill2 as isGlyphFill,
3762
3584
  isShadowFill2 as isShadowFill,
3763
3585
  normalizePath,
3764
3586
  normalizePathString,
3765
- normalizePathToSize,
3766
- parseSvgMarkup,
3767
3587
  parseSvgPath,
3768
- pointsToPath,
3769
3588
  quadraticToCubic,
3770
- reverseWindingOrder,
3771
- rotatePath,
3772
- scalePath,
3589
+ renderSvgAssetToPng,
3590
+ renderSvgToPng,
3591
+ shapeToSvgString,
3773
3592
  svgAssetSchema,
3774
3593
  svgGradientStopSchema,
3775
3594
  svgLinearGradientFillSchema,
@@ -3778,8 +3597,5 @@ export {
3778
3597
  svgShapeSchema,
3779
3598
  svgSolidFillSchema,
3780
3599
  svgStrokeSchema,
3781
- svgToAsset,
3782
- svgToSingleAsset,
3783
- svgTransformSchema,
3784
- translatePath
3600
+ svgTransformSchema
3785
3601
  };