apexify.js 4.9.28 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +727 -456
  2. package/dist/cjs/Canvas/ApexPainter.d.ts +96 -145
  3. package/dist/cjs/Canvas/ApexPainter.d.ts.map +1 -1
  4. package/dist/cjs/Canvas/ApexPainter.js +1416 -420
  5. package/dist/cjs/Canvas/ApexPainter.js.map +1 -1
  6. package/dist/cjs/Canvas/utils/Charts/charts.d.ts +7 -2
  7. package/dist/cjs/Canvas/utils/Charts/charts.d.ts.map +1 -1
  8. package/dist/cjs/Canvas/utils/Charts/charts.js +3 -1
  9. package/dist/cjs/Canvas/utils/Charts/charts.js.map +1 -1
  10. package/dist/cjs/Canvas/utils/Custom/advancedLines.d.ts +75 -0
  11. package/dist/cjs/Canvas/utils/Custom/advancedLines.d.ts.map +1 -0
  12. package/dist/cjs/Canvas/utils/Custom/advancedLines.js +263 -0
  13. package/dist/cjs/Canvas/utils/Custom/advancedLines.js.map +1 -0
  14. package/dist/cjs/Canvas/utils/Custom/customLines.d.ts +2 -1
  15. package/dist/cjs/Canvas/utils/Custom/customLines.d.ts.map +1 -1
  16. package/dist/cjs/Canvas/utils/Custom/customLines.js +73 -6
  17. package/dist/cjs/Canvas/utils/Custom/customLines.js.map +1 -1
  18. package/dist/cjs/Canvas/utils/General/batchOperations.d.ts +17 -0
  19. package/dist/cjs/Canvas/utils/General/batchOperations.d.ts.map +1 -0
  20. package/dist/cjs/Canvas/utils/General/batchOperations.js +88 -0
  21. package/dist/cjs/Canvas/utils/General/batchOperations.js.map +1 -0
  22. package/dist/cjs/Canvas/utils/General/general functions.d.ts +25 -3
  23. package/dist/cjs/Canvas/utils/General/general functions.d.ts.map +1 -1
  24. package/dist/cjs/Canvas/utils/General/general functions.js +37 -9
  25. package/dist/cjs/Canvas/utils/General/general functions.js.map +1 -1
  26. package/dist/cjs/Canvas/utils/General/imageCompression.d.ts +19 -0
  27. package/dist/cjs/Canvas/utils/General/imageCompression.d.ts.map +1 -0
  28. package/dist/cjs/Canvas/utils/General/imageCompression.js +262 -0
  29. package/dist/cjs/Canvas/utils/General/imageCompression.js.map +1 -0
  30. package/dist/cjs/Canvas/utils/General/imageStitching.d.ts +20 -0
  31. package/dist/cjs/Canvas/utils/General/imageStitching.d.ts.map +1 -0
  32. package/dist/cjs/Canvas/utils/General/imageStitching.js +227 -0
  33. package/dist/cjs/Canvas/utils/General/imageStitching.js.map +1 -0
  34. package/dist/cjs/Canvas/utils/Image/imageEffects.d.ts +37 -0
  35. package/dist/cjs/Canvas/utils/Image/imageEffects.d.ts.map +1 -0
  36. package/dist/cjs/Canvas/utils/Image/imageEffects.js +128 -0
  37. package/dist/cjs/Canvas/utils/Image/imageEffects.js.map +1 -0
  38. package/dist/cjs/Canvas/utils/Image/imageMasking.d.ts +67 -0
  39. package/dist/cjs/Canvas/utils/Image/imageMasking.d.ts.map +1 -0
  40. package/dist/cjs/Canvas/utils/Image/imageMasking.js +276 -0
  41. package/dist/cjs/Canvas/utils/Image/imageMasking.js.map +1 -0
  42. package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
  43. package/dist/cjs/Canvas/utils/Image/imageProperties.js +181 -2
  44. package/dist/cjs/Canvas/utils/Image/imageProperties.js.map +1 -1
  45. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -1
  46. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js +16 -8
  47. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -1
  48. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts +33 -0
  49. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -1
  50. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js +237 -32
  51. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -1
  52. package/dist/cjs/Canvas/utils/Texts/textPathRenderer.d.ts +17 -0
  53. package/dist/cjs/Canvas/utils/Texts/textPathRenderer.d.ts.map +1 -0
  54. package/dist/cjs/Canvas/utils/Texts/textPathRenderer.js +233 -0
  55. package/dist/cjs/Canvas/utils/Texts/textPathRenderer.js.map +1 -0
  56. package/dist/cjs/Canvas/utils/types.d.ts +171 -10
  57. package/dist/cjs/Canvas/utils/types.d.ts.map +1 -1
  58. package/dist/cjs/Canvas/utils/types.js.map +1 -1
  59. package/dist/cjs/Canvas/utils/utils.d.ts +9 -2
  60. package/dist/cjs/Canvas/utils/utils.d.ts.map +1 -1
  61. package/dist/cjs/Canvas/utils/utils.js +32 -1
  62. package/dist/cjs/Canvas/utils/utils.js.map +1 -1
  63. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  64. package/dist/esm/Canvas/ApexPainter.d.ts +96 -145
  65. package/dist/esm/Canvas/ApexPainter.d.ts.map +1 -1
  66. package/dist/esm/Canvas/ApexPainter.js +1416 -420
  67. package/dist/esm/Canvas/ApexPainter.js.map +1 -1
  68. package/dist/esm/Canvas/utils/Charts/charts.d.ts +7 -2
  69. package/dist/esm/Canvas/utils/Charts/charts.d.ts.map +1 -1
  70. package/dist/esm/Canvas/utils/Charts/charts.js +3 -1
  71. package/dist/esm/Canvas/utils/Charts/charts.js.map +1 -1
  72. package/dist/esm/Canvas/utils/Custom/advancedLines.d.ts +75 -0
  73. package/dist/esm/Canvas/utils/Custom/advancedLines.d.ts.map +1 -0
  74. package/dist/esm/Canvas/utils/Custom/advancedLines.js +263 -0
  75. package/dist/esm/Canvas/utils/Custom/advancedLines.js.map +1 -0
  76. package/dist/esm/Canvas/utils/Custom/customLines.d.ts +2 -1
  77. package/dist/esm/Canvas/utils/Custom/customLines.d.ts.map +1 -1
  78. package/dist/esm/Canvas/utils/Custom/customLines.js +73 -6
  79. package/dist/esm/Canvas/utils/Custom/customLines.js.map +1 -1
  80. package/dist/esm/Canvas/utils/General/batchOperations.d.ts +17 -0
  81. package/dist/esm/Canvas/utils/General/batchOperations.d.ts.map +1 -0
  82. package/dist/esm/Canvas/utils/General/batchOperations.js +88 -0
  83. package/dist/esm/Canvas/utils/General/batchOperations.js.map +1 -0
  84. package/dist/esm/Canvas/utils/General/general functions.d.ts +25 -3
  85. package/dist/esm/Canvas/utils/General/general functions.d.ts.map +1 -1
  86. package/dist/esm/Canvas/utils/General/general functions.js +37 -9
  87. package/dist/esm/Canvas/utils/General/general functions.js.map +1 -1
  88. package/dist/esm/Canvas/utils/General/imageCompression.d.ts +19 -0
  89. package/dist/esm/Canvas/utils/General/imageCompression.d.ts.map +1 -0
  90. package/dist/esm/Canvas/utils/General/imageCompression.js +262 -0
  91. package/dist/esm/Canvas/utils/General/imageCompression.js.map +1 -0
  92. package/dist/esm/Canvas/utils/General/imageStitching.d.ts +20 -0
  93. package/dist/esm/Canvas/utils/General/imageStitching.d.ts.map +1 -0
  94. package/dist/esm/Canvas/utils/General/imageStitching.js +227 -0
  95. package/dist/esm/Canvas/utils/General/imageStitching.js.map +1 -0
  96. package/dist/esm/Canvas/utils/Image/imageEffects.d.ts +37 -0
  97. package/dist/esm/Canvas/utils/Image/imageEffects.d.ts.map +1 -0
  98. package/dist/esm/Canvas/utils/Image/imageEffects.js +128 -0
  99. package/dist/esm/Canvas/utils/Image/imageEffects.js.map +1 -0
  100. package/dist/esm/Canvas/utils/Image/imageMasking.d.ts +67 -0
  101. package/dist/esm/Canvas/utils/Image/imageMasking.d.ts.map +1 -0
  102. package/dist/esm/Canvas/utils/Image/imageMasking.js +276 -0
  103. package/dist/esm/Canvas/utils/Image/imageMasking.js.map +1 -0
  104. package/dist/esm/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
  105. package/dist/esm/Canvas/utils/Image/imageProperties.js +181 -2
  106. package/dist/esm/Canvas/utils/Image/imageProperties.js.map +1 -1
  107. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -1
  108. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js +16 -8
  109. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -1
  110. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts +33 -0
  111. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -1
  112. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js +237 -32
  113. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -1
  114. package/dist/esm/Canvas/utils/Texts/textPathRenderer.d.ts +17 -0
  115. package/dist/esm/Canvas/utils/Texts/textPathRenderer.d.ts.map +1 -0
  116. package/dist/esm/Canvas/utils/Texts/textPathRenderer.js +233 -0
  117. package/dist/esm/Canvas/utils/Texts/textPathRenderer.js.map +1 -0
  118. package/dist/esm/Canvas/utils/types.d.ts +171 -10
  119. package/dist/esm/Canvas/utils/types.d.ts.map +1 -1
  120. package/dist/esm/Canvas/utils/types.js.map +1 -1
  121. package/dist/esm/Canvas/utils/utils.d.ts +9 -2
  122. package/dist/esm/Canvas/utils/utils.d.ts.map +1 -1
  123. package/dist/esm/Canvas/utils/utils.js +32 -1
  124. package/dist/esm/Canvas/utils/utils.js.map +1 -1
  125. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  126. package/lib/Canvas/ApexPainter.ts +1309 -267
  127. package/lib/Canvas/utils/Charts/charts.ts +16 -7
  128. package/lib/Canvas/utils/Custom/advancedLines.ts +335 -0
  129. package/lib/Canvas/utils/Custom/customLines.ts +84 -9
  130. package/lib/Canvas/utils/General/batchOperations.ts +103 -0
  131. package/lib/Canvas/utils/General/general functions.ts +85 -41
  132. package/lib/Canvas/utils/General/imageCompression.ts +316 -0
  133. package/lib/Canvas/utils/General/imageStitching.ts +252 -0
  134. package/lib/Canvas/utils/Image/imageEffects.ts +175 -0
  135. package/lib/Canvas/utils/Image/imageMasking.ts +335 -0
  136. package/lib/Canvas/utils/Image/imageProperties.ts +207 -2
  137. package/lib/Canvas/utils/Patterns/enhancedPatternRenderer.ts +455 -444
  138. package/lib/Canvas/utils/Texts/enhancedTextRenderer.ts +274 -36
  139. package/lib/Canvas/utils/Texts/textPathRenderer.ts +320 -0
  140. package/lib/Canvas/utils/types.ts +173 -10
  141. package/lib/Canvas/utils/utils.ts +49 -2
  142. package/package.json +69 -34
@@ -16,9 +16,12 @@ export class EnhancedTextRenderer {
16
16
  ctx.save();
17
17
 
18
18
  try {
19
- // 1. Register custom font if provided
20
- if (textProps.fontPath) {
21
- await this.registerCustomFont(textProps.fontPath, textProps.fontName || 'customFont');
19
+ // 1. Register custom font if provided (support both new and legacy properties)
20
+ const fontPath = textProps.font?.path || textProps.fontPath;
21
+ const fontName = textProps.font?.name || textProps.fontName;
22
+
23
+ if (fontPath) {
24
+ await this.registerCustomFont(fontPath, fontName || 'customFont');
22
25
  }
23
26
 
24
27
  // 2. Apply transformations
@@ -81,8 +84,9 @@ export class EnhancedTextRenderer {
81
84
  * @param textProps - Text properties
82
85
  */
83
86
  private static setupFont(ctx: SKRSContext2D, textProps: TextProperties): void {
84
- const fontSize = textProps.fontSize || 16;
85
- const fontFamily = textProps.fontName || textProps.fontFamily || 'Arial';
87
+ // Support both new font object and legacy properties
88
+ const fontSize = textProps.font?.size || textProps.fontSize || 16;
89
+ const fontFamily = textProps.font?.name || textProps.fontName || textProps.font?.family || textProps.fontFamily || 'Arial';
86
90
 
87
91
  // Build font string with decorations
88
92
  let fontString = '';
@@ -121,7 +125,7 @@ export class EnhancedTextRenderer {
121
125
  * @param textProps - Text properties
122
126
  */
123
127
  private static async renderWrappedText(ctx: SKRSContext2D, textProps: TextProperties): Promise<void> {
124
- const fontSize = textProps.fontSize || 16;
128
+ const fontSize = textProps.font?.size || textProps.fontSize || 16;
125
129
  const lineHeight = (textProps.lineHeight || 1.4) * fontSize;
126
130
  const maxHeight = textProps.maxHeight;
127
131
  const maxLines = maxHeight ? Math.floor(maxHeight / lineHeight) : Infinity;
@@ -186,7 +190,7 @@ export class EnhancedTextRenderer {
186
190
  // Calculate text dimensions
187
191
  const metrics = ctx.measureText(text);
188
192
  const textWidth = metrics.width;
189
- const fontSize = textProps.fontSize || 16;
193
+ const fontSize = textProps.font?.size || textProps.fontSize || 16;
190
194
  const textHeight = fontSize;
191
195
 
192
196
  // Apply highlight background
@@ -231,13 +235,19 @@ export class EnhancedTextRenderer {
231
235
  y: number,
232
236
  width: number,
233
237
  height: number,
234
- highlight: { color?: string; opacity?: number }
238
+ highlight: { color?: string; gradient?: gradient; opacity?: number }
235
239
  ): void {
236
240
  ctx.save();
237
241
 
238
242
  const opacity = highlight.opacity !== undefined ? highlight.opacity : 0.3;
239
243
  ctx.globalAlpha = opacity;
240
- ctx.fillStyle = highlight.color || '#ffff00';
244
+
245
+ // Set fill style (gradient or color)
246
+ if (highlight.gradient) {
247
+ ctx.fillStyle = this.createGradient(ctx, highlight.gradient, x, y, x + width, y + height);
248
+ } else {
249
+ ctx.fillStyle = highlight.color || '#ffff00';
250
+ }
241
251
 
242
252
  // Adjust highlight position based on text baseline
243
253
  const highlightY = y - height * 0.8; // Adjust for different baselines
@@ -259,18 +269,33 @@ export class EnhancedTextRenderer {
259
269
  text: string,
260
270
  x: number,
261
271
  y: number,
262
- glow: { color?: string; intensity?: number; opacity?: number }
272
+ glow: { color?: string; gradient?: gradient; intensity?: number; opacity?: number }
263
273
  ): void {
264
274
  ctx.save();
265
275
 
266
276
  const intensity = glow.intensity || 10;
267
277
  const opacity = glow.opacity !== undefined ? glow.opacity : 0.8;
268
278
 
269
- ctx.shadowColor = glow.color || '#ffffff';
270
- ctx.shadowBlur = intensity;
271
- ctx.globalAlpha = opacity;
272
-
273
- ctx.fillText(text, x, y);
279
+ // For glow, we need to use shadowColor which only supports solid colors
280
+ // So we'll render the glow with the base color and then overlay with gradient if needed
281
+ if (glow.gradient) {
282
+ // First render with shadow for glow effect
283
+ ctx.shadowColor = '#ffffff'; // Use white as base for glow
284
+ ctx.shadowBlur = intensity;
285
+ ctx.globalAlpha = opacity;
286
+ ctx.fillText(text, x, y);
287
+
288
+ // Then overlay with gradient
289
+ ctx.shadowColor = 'transparent';
290
+ ctx.shadowBlur = 0;
291
+ ctx.fillStyle = this.createGradient(ctx, glow.gradient, x, y, x + ctx.measureText(text).width, y);
292
+ ctx.fillText(text, x, y);
293
+ } else {
294
+ ctx.shadowColor = glow.color || '#ffffff';
295
+ ctx.shadowBlur = intensity;
296
+ ctx.globalAlpha = opacity;
297
+ ctx.fillText(text, x, y);
298
+ }
274
299
 
275
300
  ctx.restore();
276
301
  }
@@ -319,11 +344,14 @@ export class EnhancedTextRenderer {
319
344
  text: string,
320
345
  x: number,
321
346
  y: number,
322
- stroke: { color?: string; width?: number; gradient?: gradient; opacity?: number }
347
+ stroke: { color?: string; width?: number; gradient?: gradient; opacity?: number; style?: 'solid' | 'dashed' | 'dotted' | 'groove' | 'ridge' | 'double' }
323
348
  ): void {
324
349
  ctx.save();
325
350
 
326
- ctx.lineWidth = stroke.width || 1;
351
+ const strokeWidth = stroke.width || 1;
352
+ const strokeStyle = stroke.style || 'solid';
353
+
354
+ ctx.lineWidth = strokeWidth;
327
355
 
328
356
  if (stroke.gradient) {
329
357
  ctx.strokeStyle = this.createGradient(ctx, stroke.gradient, x, y, x + ctx.measureText(text).width, y);
@@ -335,7 +363,15 @@ export class EnhancedTextRenderer {
335
363
  ctx.globalAlpha = stroke.opacity;
336
364
  }
337
365
 
338
- ctx.strokeText(text, x, y);
366
+ // Apply stroke style
367
+ this.applyTextStrokeStyle(ctx, strokeStyle, strokeWidth);
368
+
369
+ // Handle complex stroke styles
370
+ if (strokeStyle === 'groove' || strokeStyle === 'ridge' || strokeStyle === 'double') {
371
+ this.renderComplexTextStroke(ctx, text, x, y, strokeStyle, strokeWidth, stroke.color, stroke.gradient);
372
+ } else {
373
+ ctx.strokeText(text, x, y);
374
+ }
339
375
 
340
376
  ctx.restore();
341
377
  }
@@ -387,44 +423,69 @@ export class EnhancedTextRenderer {
387
423
  height: number,
388
424
  textProps: TextProperties
389
425
  ): void {
390
- if (!textProps.underline && !textProps.overline && !textProps.strikethrough) {
426
+ const hasDecorations = textProps.underline || textProps.overline || textProps.strikethrough;
427
+ if (!hasDecorations) {
391
428
  return;
392
429
  }
393
430
 
394
431
  ctx.save();
395
432
 
396
- const fontSize = textProps.fontSize || 16;
397
- const decorationColor = textProps.color || '#000000';
398
- const decorationWidth = Math.max(1, fontSize * 0.05); // 5% of font size
433
+ const fontSize = textProps.font?.size || textProps.fontSize || 16;
434
+ const defaultColor = textProps.color || '#000000';
399
435
 
400
- ctx.strokeStyle = decorationColor;
401
- ctx.lineWidth = decorationWidth;
436
+ // Helper function to render a decoration line
437
+ const renderDecorationLine = (
438
+ decorationY: number,
439
+ decoration: boolean | { color?: string; gradient?: gradient; width?: number } | undefined,
440
+ lineName: string
441
+ ) => {
442
+ if (!decoration) return;
443
+
444
+ ctx.save();
445
+
446
+ let decorationColor = defaultColor;
447
+ let decorationWidth = Math.max(1, fontSize * 0.05); // 5% of font size
448
+
449
+ if (typeof decoration === 'object') {
450
+ decorationColor = decoration.color || defaultColor;
451
+ decorationWidth = decoration.width || decorationWidth;
452
+
453
+ // Set stroke style (gradient or color)
454
+ if (decoration.gradient) {
455
+ ctx.strokeStyle = this.createGradient(ctx, decoration.gradient, x, decorationY, x + width, decorationY);
456
+ } else {
457
+ ctx.strokeStyle = decorationColor;
458
+ }
459
+ } else {
460
+ ctx.strokeStyle = decorationColor;
461
+ }
462
+
463
+ ctx.lineWidth = decorationWidth;
464
+
465
+ ctx.beginPath();
466
+ ctx.moveTo(x, decorationY);
467
+ ctx.lineTo(x + width, decorationY);
468
+ ctx.stroke();
469
+
470
+ ctx.restore();
471
+ };
402
472
 
403
473
  // Underline
404
474
  if (textProps.underline) {
405
475
  const underlineY = y + fontSize * 0.1;
406
- ctx.beginPath();
407
- ctx.moveTo(x, underlineY);
408
- ctx.lineTo(x + width, underlineY);
409
- ctx.stroke();
476
+ renderDecorationLine(underlineY, textProps.underline, 'underline');
410
477
  }
411
478
 
412
479
  // Overline
413
480
  if (textProps.overline) {
414
481
  const overlineY = y - fontSize * 0.8;
415
- ctx.beginPath();
416
- ctx.moveTo(x, overlineY);
417
- ctx.lineTo(x + width, overlineY);
418
- ctx.stroke();
482
+ renderDecorationLine(overlineY, textProps.overline, 'overline');
419
483
  }
420
484
 
421
485
  // Strikethrough
422
486
  if (textProps.strikethrough) {
423
487
  const strikethroughY = y - fontSize * 0.3;
424
- ctx.beginPath();
425
- ctx.moveTo(x, strikethroughY);
426
- ctx.lineTo(x + width, strikethroughY);
427
- ctx.stroke();
488
+ renderDecorationLine(strikethroughY, textProps.strikethrough, 'strikethrough');
428
489
  }
429
490
 
430
491
  ctx.restore();
@@ -475,4 +536,181 @@ export class EnhancedTextRenderer {
475
536
 
476
537
  return gradient;
477
538
  }
539
+
540
+ /**
541
+ * Applies stroke style to text context
542
+ * @param ctx - Canvas 2D context
543
+ * @param style - Stroke style type
544
+ * @param width - Stroke width for calculating dash patterns
545
+ */
546
+ private static applyTextStrokeStyle(
547
+ ctx: SKRSContext2D,
548
+ style: 'solid' | 'dashed' | 'dotted' | 'groove' | 'ridge' | 'double',
549
+ width: number
550
+ ): void {
551
+ switch (style) {
552
+ case 'solid':
553
+ ctx.setLineDash([]);
554
+ ctx.lineCap = 'butt';
555
+ ctx.lineJoin = 'miter';
556
+ break;
557
+
558
+ case 'dashed':
559
+ ctx.setLineDash([width * 3, width * 2]);
560
+ ctx.lineCap = 'butt';
561
+ ctx.lineJoin = 'miter';
562
+ break;
563
+
564
+ case 'dotted':
565
+ ctx.setLineDash([width, width]);
566
+ ctx.lineCap = 'round';
567
+ ctx.lineJoin = 'round';
568
+ break;
569
+
570
+ case 'groove':
571
+ case 'ridge':
572
+ case 'double':
573
+ ctx.setLineDash([]);
574
+ ctx.lineCap = 'butt';
575
+ ctx.lineJoin = 'miter';
576
+ break;
577
+
578
+ default:
579
+ ctx.setLineDash([]);
580
+ ctx.lineCap = 'butt';
581
+ ctx.lineJoin = 'miter';
582
+ break;
583
+ }
584
+ }
585
+
586
+ /**
587
+ * Renders complex text stroke styles that require multiple passes
588
+ * @param ctx - Canvas 2D context
589
+ * @param text - Text to render
590
+ * @param x - X position
591
+ * @param y - Y position
592
+ * @param style - Complex stroke style type
593
+ * @param width - Stroke width
594
+ * @param color - Base stroke color
595
+ * @param gradient - Optional gradient
596
+ */
597
+ private static renderComplexTextStroke(
598
+ ctx: SKRSContext2D,
599
+ text: string,
600
+ x: number,
601
+ y: number,
602
+ style: 'groove' | 'ridge' | 'double',
603
+ width: number,
604
+ color?: string,
605
+ gradient?: gradient
606
+ ): void {
607
+ const halfWidth = width / 2;
608
+ const textWidth = ctx.measureText(text).width;
609
+
610
+ switch (style) {
611
+ case 'groove':
612
+ // Groove: dark outer, light inner
613
+ ctx.lineWidth = halfWidth;
614
+
615
+ // Outer dark stroke
616
+ if (gradient) {
617
+ ctx.strokeStyle = this.createGradient(ctx, gradient, x, y, x + textWidth, y);
618
+ } else {
619
+ ctx.strokeStyle = this.darkenColor(color || '#000000', 0.3);
620
+ }
621
+ ctx.strokeText(text, x, y);
622
+
623
+ // Inner light stroke
624
+ ctx.lineWidth = halfWidth;
625
+ if (gradient) {
626
+ ctx.strokeStyle = this.createGradient(ctx, gradient, x, y, x + textWidth, y);
627
+ } else {
628
+ ctx.strokeStyle = this.lightenColor(color || '#000000', 0.3);
629
+ }
630
+ ctx.strokeText(text, x, y);
631
+ break;
632
+
633
+ case 'ridge':
634
+ // Ridge: light outer, dark inner
635
+ ctx.lineWidth = halfWidth;
636
+
637
+ // Outer light stroke
638
+ if (gradient) {
639
+ ctx.strokeStyle = this.createGradient(ctx, gradient, x, y, x + textWidth, y);
640
+ } else {
641
+ ctx.strokeStyle = this.lightenColor(color || '#000000', 0.3);
642
+ }
643
+ ctx.strokeText(text, x, y);
644
+
645
+ // Inner dark stroke
646
+ ctx.lineWidth = halfWidth;
647
+ if (gradient) {
648
+ ctx.strokeStyle = this.createGradient(ctx, gradient, x, y, x + textWidth, y);
649
+ } else {
650
+ ctx.strokeStyle = this.darkenColor(color || '#000000', 0.3);
651
+ }
652
+ ctx.strokeText(text, x, y);
653
+ break;
654
+
655
+ case 'double':
656
+ // Double: two parallel strokes
657
+ ctx.lineWidth = halfWidth;
658
+
659
+ // First stroke (outer)
660
+ if (gradient) {
661
+ ctx.strokeStyle = this.createGradient(ctx, gradient, x, y, x + textWidth, y);
662
+ } else {
663
+ ctx.strokeStyle = color || '#000000';
664
+ }
665
+ ctx.strokeText(text, x, y);
666
+
667
+ // Second stroke (inner)
668
+ ctx.lineWidth = halfWidth;
669
+ if (gradient) {
670
+ ctx.strokeStyle = this.createGradient(ctx, gradient, x, y, x + textWidth, y);
671
+ } else {
672
+ ctx.strokeStyle = color || '#000000';
673
+ }
674
+ ctx.strokeText(text, x, y);
675
+ break;
676
+ }
677
+ }
678
+
679
+ /**
680
+ * Darkens a color by a factor
681
+ * @param color - Color string
682
+ * @param factor - Darkening factor (0-1)
683
+ * @returns Darkened color string
684
+ */
685
+ private static darkenColor(color: string, factor: number): string {
686
+ // Simple darkening for hex colors
687
+ if (color.startsWith('#')) {
688
+ const hex = color.slice(1);
689
+ const num = parseInt(hex, 16);
690
+ const r = Math.max(0, Math.floor((num >> 16) * (1 - factor)));
691
+ const g = Math.max(0, Math.floor(((num >> 8) & 0x00FF) * (1 - factor)));
692
+ const b = Math.max(0, Math.floor((num & 0x0000FF) * (1 - factor)));
693
+ return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;
694
+ }
695
+ return color; // Return original for non-hex colors
696
+ }
697
+
698
+ /**
699
+ * Lightens a color by a factor
700
+ * @param color - Color string
701
+ * @param factor - Lightening factor (0-1)
702
+ * @returns Lightened color string
703
+ */
704
+ private static lightenColor(color: string, factor: number): string {
705
+ // Simple lightening for hex colors
706
+ if (color.startsWith('#')) {
707
+ const hex = color.slice(1);
708
+ const num = parseInt(hex, 16);
709
+ const r = Math.min(255, Math.floor((num >> 16) + (255 - (num >> 16)) * factor));
710
+ const g = Math.min(255, Math.floor(((num >> 8) & 0x00FF) + (255 - ((num >> 8) & 0x00FF)) * factor));
711
+ const b = Math.min(255, Math.floor((num & 0x0000FF) + (255 - (num & 0x0000FF)) * factor));
712
+ return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;
713
+ }
714
+ return color; // Return original for non-hex colors
715
+ }
478
716
  }