apexify.js 4.9.26 → 4.9.27

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 (108) hide show
  1. package/README.md +358 -47
  2. package/dist/cjs/Canvas/ApexPainter.d.ts +122 -78
  3. package/dist/cjs/Canvas/ApexPainter.d.ts.map +1 -1
  4. package/dist/cjs/Canvas/ApexPainter.js +461 -352
  5. package/dist/cjs/Canvas/ApexPainter.js.map +1 -1
  6. package/dist/cjs/Canvas/utils/Background/bg.d.ts +23 -11
  7. package/dist/cjs/Canvas/utils/Background/bg.d.ts.map +1 -1
  8. package/dist/cjs/Canvas/utils/Background/bg.js +174 -107
  9. package/dist/cjs/Canvas/utils/Background/bg.js.map +1 -1
  10. package/dist/cjs/Canvas/utils/Custom/customLines.js +2 -2
  11. package/dist/cjs/Canvas/utils/Custom/customLines.js.map +1 -1
  12. package/dist/cjs/Canvas/utils/Image/imageFilters.d.ts +11 -0
  13. package/dist/cjs/Canvas/utils/Image/imageFilters.d.ts.map +1 -0
  14. package/dist/cjs/Canvas/utils/Image/imageFilters.js +307 -0
  15. package/dist/cjs/Canvas/utils/Image/imageFilters.js.map +1 -0
  16. package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts +47 -112
  17. package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
  18. package/dist/cjs/Canvas/utils/Image/imageProperties.js +229 -560
  19. package/dist/cjs/Canvas/utils/Image/imageProperties.js.map +1 -1
  20. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.d.ts +11 -0
  21. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.d.ts.map +1 -0
  22. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.js +351 -0
  23. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.js.map +1 -0
  24. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.d.ts +11 -0
  25. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.d.ts.map +1 -0
  26. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.js +215 -0
  27. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.js.map +1 -0
  28. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts +71 -0
  29. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -0
  30. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js +392 -0
  31. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -0
  32. package/dist/cjs/Canvas/utils/Shapes/shapes.d.ts +29 -0
  33. package/dist/cjs/Canvas/utils/Shapes/shapes.d.ts.map +1 -0
  34. package/dist/cjs/Canvas/utils/Shapes/shapes.js +334 -0
  35. package/dist/cjs/Canvas/utils/Shapes/shapes.js.map +1 -0
  36. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts +127 -0
  37. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -0
  38. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js +365 -0
  39. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -0
  40. package/dist/cjs/Canvas/utils/types.d.ts +227 -131
  41. package/dist/cjs/Canvas/utils/types.d.ts.map +1 -1
  42. package/dist/cjs/Canvas/utils/types.js +0 -1
  43. package/dist/cjs/Canvas/utils/types.js.map +1 -1
  44. package/dist/cjs/Canvas/utils/utils.d.ts +7 -4
  45. package/dist/cjs/Canvas/utils/utils.d.ts.map +1 -1
  46. package/dist/cjs/Canvas/utils/utils.js +17 -7
  47. package/dist/cjs/Canvas/utils/utils.js.map +1 -1
  48. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  49. package/dist/esm/Canvas/ApexPainter.d.ts +122 -78
  50. package/dist/esm/Canvas/ApexPainter.d.ts.map +1 -1
  51. package/dist/esm/Canvas/ApexPainter.js +461 -352
  52. package/dist/esm/Canvas/ApexPainter.js.map +1 -1
  53. package/dist/esm/Canvas/utils/Background/bg.d.ts +23 -11
  54. package/dist/esm/Canvas/utils/Background/bg.d.ts.map +1 -1
  55. package/dist/esm/Canvas/utils/Background/bg.js +174 -107
  56. package/dist/esm/Canvas/utils/Background/bg.js.map +1 -1
  57. package/dist/esm/Canvas/utils/Custom/customLines.js +2 -2
  58. package/dist/esm/Canvas/utils/Custom/customLines.js.map +1 -1
  59. package/dist/esm/Canvas/utils/Image/imageFilters.d.ts +11 -0
  60. package/dist/esm/Canvas/utils/Image/imageFilters.d.ts.map +1 -0
  61. package/dist/esm/Canvas/utils/Image/imageFilters.js +307 -0
  62. package/dist/esm/Canvas/utils/Image/imageFilters.js.map +1 -0
  63. package/dist/esm/Canvas/utils/Image/imageProperties.d.ts +47 -112
  64. package/dist/esm/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
  65. package/dist/esm/Canvas/utils/Image/imageProperties.js +229 -560
  66. package/dist/esm/Canvas/utils/Image/imageProperties.js.map +1 -1
  67. package/dist/esm/Canvas/utils/Image/professionalImageFilters.d.ts +11 -0
  68. package/dist/esm/Canvas/utils/Image/professionalImageFilters.d.ts.map +1 -0
  69. package/dist/esm/Canvas/utils/Image/professionalImageFilters.js +351 -0
  70. package/dist/esm/Canvas/utils/Image/professionalImageFilters.js.map +1 -0
  71. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.d.ts +11 -0
  72. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.d.ts.map +1 -0
  73. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.js +215 -0
  74. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.js.map +1 -0
  75. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts +71 -0
  76. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -0
  77. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js +392 -0
  78. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -0
  79. package/dist/esm/Canvas/utils/Shapes/shapes.d.ts +29 -0
  80. package/dist/esm/Canvas/utils/Shapes/shapes.d.ts.map +1 -0
  81. package/dist/esm/Canvas/utils/Shapes/shapes.js +334 -0
  82. package/dist/esm/Canvas/utils/Shapes/shapes.js.map +1 -0
  83. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts +127 -0
  84. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -0
  85. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js +365 -0
  86. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -0
  87. package/dist/esm/Canvas/utils/types.d.ts +227 -131
  88. package/dist/esm/Canvas/utils/types.d.ts.map +1 -1
  89. package/dist/esm/Canvas/utils/types.js +0 -1
  90. package/dist/esm/Canvas/utils/types.js.map +1 -1
  91. package/dist/esm/Canvas/utils/utils.d.ts +7 -4
  92. package/dist/esm/Canvas/utils/utils.d.ts.map +1 -1
  93. package/dist/esm/Canvas/utils/utils.js +17 -7
  94. package/dist/esm/Canvas/utils/utils.js.map +1 -1
  95. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  96. package/lib/Canvas/ApexPainter.ts +1325 -1218
  97. package/lib/Canvas/utils/Background/bg.ts +247 -173
  98. package/lib/Canvas/utils/Custom/customLines.ts +3 -3
  99. package/lib/Canvas/utils/Image/imageFilters.ts +356 -0
  100. package/lib/Canvas/utils/Image/imageProperties.ts +322 -775
  101. package/lib/Canvas/utils/Image/professionalImageFilters.ts +391 -0
  102. package/lib/Canvas/utils/Image/simpleProfessionalFilters.ts +229 -0
  103. package/lib/Canvas/utils/Patterns/enhancedPatternRenderer.ts +444 -0
  104. package/lib/Canvas/utils/Shapes/shapes.ts +528 -0
  105. package/lib/Canvas/utils/Texts/enhancedTextRenderer.ts +478 -0
  106. package/lib/Canvas/utils/types.ts +301 -117
  107. package/lib/Canvas/utils/utils.ts +85 -72
  108. package/package.json +106 -188
@@ -0,0 +1,478 @@
1
+ import { SKRSContext2D } from '@napi-rs/canvas';
2
+ import { TextProperties, gradient } from '../types';
3
+ import { GlobalFonts } from '@napi-rs/canvas';
4
+ import path from 'path';
5
+
6
+ /**
7
+ * Enhanced text renderer with comprehensive styling options
8
+ */
9
+ export class EnhancedTextRenderer {
10
+ /**
11
+ * Renders text with all enhanced features
12
+ * @param ctx - Canvas 2D context
13
+ * @param textProps - Text properties configuration
14
+ */
15
+ static async renderText(ctx: SKRSContext2D, textProps: TextProperties): Promise<void> {
16
+ ctx.save();
17
+
18
+ try {
19
+ // 1. Register custom font if provided
20
+ if (textProps.fontPath) {
21
+ await this.registerCustomFont(textProps.fontPath, textProps.fontName || 'customFont');
22
+ }
23
+
24
+ // 2. Apply transformations
25
+ this.applyTransformations(ctx, textProps);
26
+
27
+ // 3. Setup font and spacing
28
+ this.setupFont(ctx, textProps);
29
+
30
+ // 4. Apply text alignment
31
+ this.setupAlignment(ctx, textProps);
32
+
33
+ // 5. Handle text wrapping or single line rendering
34
+ if (textProps.maxWidth) {
35
+ await this.renderWrappedText(ctx, textProps);
36
+ } else {
37
+ await this.renderSingleLine(ctx, textProps);
38
+ }
39
+
40
+ } finally {
41
+ ctx.restore();
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Registers a custom font from file path
47
+ * @param fontPath - Path to font file
48
+ * @param fontName - Name to register the font as
49
+ */
50
+ private static async registerCustomFont(fontPath: string, fontName: string): Promise<void> {
51
+ try {
52
+ const fullPath = path.join(process.cwd(), fontPath);
53
+ GlobalFonts.registerFromPath(fullPath, fontName);
54
+ } catch (error) {
55
+ console.warn(`Failed to register font from path: ${fontPath}`, error);
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Applies transformations (rotation, opacity)
61
+ * @param ctx - Canvas 2D context
62
+ * @param textProps - Text properties
63
+ */
64
+ private static applyTransformations(ctx: SKRSContext2D, textProps: TextProperties): void {
65
+ // Apply rotation
66
+ if (textProps.rotation && textProps.rotation !== 0) {
67
+ ctx.translate(textProps.x, textProps.y);
68
+ ctx.rotate((textProps.rotation * Math.PI) / 180);
69
+ ctx.translate(-textProps.x, -textProps.y);
70
+ }
71
+
72
+ // Apply global opacity
73
+ if (textProps.opacity !== undefined) {
74
+ ctx.globalAlpha = Math.max(0, Math.min(1, textProps.opacity));
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Sets up font properties and spacing
80
+ * @param ctx - Canvas 2D context
81
+ * @param textProps - Text properties
82
+ */
83
+ private static setupFont(ctx: SKRSContext2D, textProps: TextProperties): void {
84
+ const fontSize = textProps.fontSize || 16;
85
+ const fontFamily = textProps.fontName || textProps.fontFamily || 'Arial';
86
+
87
+ // Build font string with decorations
88
+ let fontString = '';
89
+
90
+ if (textProps.bold) fontString += 'bold ';
91
+ if (textProps.italic) fontString += 'italic ';
92
+
93
+ fontString += `${fontSize}px "${fontFamily}"`;
94
+
95
+ ctx.font = fontString;
96
+
97
+ // Apply letter spacing
98
+ if (textProps.letterSpacing !== undefined) {
99
+ ctx.letterSpacing = `${textProps.letterSpacing}px`;
100
+ }
101
+
102
+ // Apply word spacing
103
+ if (textProps.wordSpacing !== undefined) {
104
+ ctx.wordSpacing = `${textProps.wordSpacing}px`;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Sets up text alignment
110
+ * @param ctx - Canvas 2D context
111
+ * @param textProps - Text properties
112
+ */
113
+ private static setupAlignment(ctx: SKRSContext2D, textProps: TextProperties): void {
114
+ ctx.textAlign = textProps.textAlign || 'left';
115
+ ctx.textBaseline = textProps.textBaseline || 'alphabetic';
116
+ }
117
+
118
+ /**
119
+ * Renders wrapped text with all effects
120
+ * @param ctx - Canvas 2D context
121
+ * @param textProps - Text properties
122
+ */
123
+ private static async renderWrappedText(ctx: SKRSContext2D, textProps: TextProperties): Promise<void> {
124
+ const fontSize = textProps.fontSize || 16;
125
+ const lineHeight = (textProps.lineHeight || 1.4) * fontSize;
126
+ const maxHeight = textProps.maxHeight;
127
+ const maxLines = maxHeight ? Math.floor(maxHeight / lineHeight) : Infinity;
128
+
129
+ // Split text into words and wrap
130
+ const words = textProps.text.split(' ');
131
+ const lines: string[] = [];
132
+ let currentLine = '';
133
+
134
+ for (const word of words) {
135
+ const testLine = currentLine ? currentLine + ' ' + word : word;
136
+ const testWidth = ctx.measureText(testLine).width;
137
+
138
+ if (testWidth > (textProps.maxWidth || Infinity) && currentLine) {
139
+ lines.push(currentLine);
140
+ currentLine = word;
141
+
142
+ if (lines.length >= maxLines) {
143
+ currentLine = '...';
144
+ break;
145
+ }
146
+ } else {
147
+ currentLine = testLine;
148
+ }
149
+ }
150
+
151
+ if (currentLine && lines.length < maxLines) {
152
+ lines.push(currentLine);
153
+ }
154
+
155
+ // Render each line
156
+ for (let i = 0; i < lines.length; i++) {
157
+ const y = textProps.y + (i * lineHeight);
158
+ await this.renderTextLine(ctx, lines[i], textProps.x, y, textProps);
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Renders single line text with all effects
164
+ * @param ctx - Canvas 2D context
165
+ * @param textProps - Text properties
166
+ */
167
+ private static async renderSingleLine(ctx: SKRSContext2D, textProps: TextProperties): Promise<void> {
168
+ await this.renderTextLine(ctx, textProps.text, textProps.x, textProps.y, textProps);
169
+ }
170
+
171
+ /**
172
+ * Renders a single line of text with all effects applied
173
+ * @param ctx - Canvas 2D context
174
+ * @param text - Text to render
175
+ * @param x - X position
176
+ * @param y - Y position
177
+ * @param textProps - Text properties
178
+ */
179
+ private static async renderTextLine(
180
+ ctx: SKRSContext2D,
181
+ text: string,
182
+ x: number,
183
+ y: number,
184
+ textProps: TextProperties
185
+ ): Promise<void> {
186
+ // Calculate text dimensions
187
+ const metrics = ctx.measureText(text);
188
+ const textWidth = metrics.width;
189
+ const fontSize = textProps.fontSize || 16;
190
+ const textHeight = fontSize;
191
+
192
+ // Apply highlight background
193
+ if (textProps.highlight) {
194
+ this.renderHighlight(ctx, x, y, textWidth, textHeight, textProps.highlight);
195
+ }
196
+
197
+ // Apply glow effect
198
+ if (textProps.glow) {
199
+ this.renderGlow(ctx, text, x, y, textProps.glow);
200
+ }
201
+
202
+ // Apply shadow effect
203
+ if (textProps.shadow) {
204
+ this.renderShadow(ctx, text, x, y, textProps.shadow);
205
+ }
206
+
207
+ // Apply stroke
208
+ if (textProps.stroke) {
209
+ this.renderStroke(ctx, text, x, y, textProps.stroke);
210
+ }
211
+
212
+ // Apply fill
213
+ this.renderFill(ctx, text, x, y, textProps);
214
+
215
+ // Apply text decorations
216
+ this.renderDecorations(ctx, text, x, y, textWidth, textHeight, textProps);
217
+ }
218
+
219
+ /**
220
+ * Renders highlight background
221
+ * @param ctx - Canvas 2D context
222
+ * @param x - X position
223
+ * @param y - Y position
224
+ * @param width - Text width
225
+ * @param height - Text height
226
+ * @param highlight - Highlight options
227
+ */
228
+ private static renderHighlight(
229
+ ctx: SKRSContext2D,
230
+ x: number,
231
+ y: number,
232
+ width: number,
233
+ height: number,
234
+ highlight: { color?: string; opacity?: number }
235
+ ): void {
236
+ ctx.save();
237
+
238
+ const opacity = highlight.opacity !== undefined ? highlight.opacity : 0.3;
239
+ ctx.globalAlpha = opacity;
240
+ ctx.fillStyle = highlight.color || '#ffff00';
241
+
242
+ // Adjust highlight position based on text baseline
243
+ const highlightY = y - height * 0.8; // Adjust for different baselines
244
+ ctx.fillRect(x, highlightY, width, height);
245
+
246
+ ctx.restore();
247
+ }
248
+
249
+ /**
250
+ * Renders glow effect
251
+ * @param ctx - Canvas 2D context
252
+ * @param text - Text to render
253
+ * @param x - X position
254
+ * @param y - Y position
255
+ * @param glow - Glow options
256
+ */
257
+ private static renderGlow(
258
+ ctx: SKRSContext2D,
259
+ text: string,
260
+ x: number,
261
+ y: number,
262
+ glow: { color?: string; intensity?: number; opacity?: number }
263
+ ): void {
264
+ ctx.save();
265
+
266
+ const intensity = glow.intensity || 10;
267
+ const opacity = glow.opacity !== undefined ? glow.opacity : 0.8;
268
+
269
+ ctx.shadowColor = glow.color || '#ffffff';
270
+ ctx.shadowBlur = intensity;
271
+ ctx.globalAlpha = opacity;
272
+
273
+ ctx.fillText(text, x, y);
274
+
275
+ ctx.restore();
276
+ }
277
+
278
+ /**
279
+ * Renders shadow effect
280
+ * @param ctx - Canvas 2D context
281
+ * @param text - Text to render
282
+ * @param x - X position
283
+ * @param y - Y position
284
+ * @param shadow - Shadow options
285
+ */
286
+ private static renderShadow(
287
+ ctx: SKRSContext2D,
288
+ text: string,
289
+ x: number,
290
+ y: number,
291
+ shadow: { color?: string; offsetX?: number; offsetY?: number; blur?: number; opacity?: number }
292
+ ): void {
293
+ ctx.save();
294
+
295
+ ctx.shadowColor = shadow.color || 'rgba(0, 0, 0, 0.5)';
296
+ ctx.shadowOffsetX = shadow.offsetX || 2;
297
+ ctx.shadowOffsetY = shadow.offsetY || 2;
298
+ ctx.shadowBlur = shadow.blur || 4;
299
+
300
+ if (shadow.opacity !== undefined) {
301
+ ctx.globalAlpha = shadow.opacity;
302
+ }
303
+
304
+ ctx.fillText(text, x, y);
305
+
306
+ ctx.restore();
307
+ }
308
+
309
+ /**
310
+ * Renders stroke/outline
311
+ * @param ctx - Canvas 2D context
312
+ * @param text - Text to render
313
+ * @param x - X position
314
+ * @param y - Y position
315
+ * @param stroke - Stroke options
316
+ */
317
+ private static renderStroke(
318
+ ctx: SKRSContext2D,
319
+ text: string,
320
+ x: number,
321
+ y: number,
322
+ stroke: { color?: string; width?: number; gradient?: gradient; opacity?: number }
323
+ ): void {
324
+ ctx.save();
325
+
326
+ ctx.lineWidth = stroke.width || 1;
327
+
328
+ if (stroke.gradient) {
329
+ ctx.strokeStyle = this.createGradient(ctx, stroke.gradient, x, y, x + ctx.measureText(text).width, y);
330
+ } else {
331
+ ctx.strokeStyle = stroke.color || '#000000';
332
+ }
333
+
334
+ if (stroke.opacity !== undefined) {
335
+ ctx.globalAlpha = stroke.opacity;
336
+ }
337
+
338
+ ctx.strokeText(text, x, y);
339
+
340
+ ctx.restore();
341
+ }
342
+
343
+ /**
344
+ * Renders text fill
345
+ * @param ctx - Canvas 2D context
346
+ * @param text - Text to render
347
+ * @param x - X position
348
+ * @param y - Y position
349
+ * @param textProps - Text properties
350
+ */
351
+ private static renderFill(
352
+ ctx: SKRSContext2D,
353
+ text: string,
354
+ x: number,
355
+ y: number,
356
+ textProps: TextProperties
357
+ ): void {
358
+ ctx.save();
359
+
360
+ if (textProps.gradient) {
361
+ ctx.fillStyle = this.createGradient(ctx, textProps.gradient, x, y, x + ctx.measureText(text).width, y);
362
+ } else {
363
+ ctx.fillStyle = textProps.color || '#000000';
364
+ }
365
+
366
+ ctx.fillText(text, x, y);
367
+
368
+ ctx.restore();
369
+ }
370
+
371
+ /**
372
+ * Renders text decorations (underline, overline, strikethrough)
373
+ * @param ctx - Canvas 2D context
374
+ * @param text - Text to render
375
+ * @param x - X position
376
+ * @param y - Y position
377
+ * @param width - Text width
378
+ * @param height - Text height
379
+ * @param textProps - Text properties
380
+ */
381
+ private static renderDecorations(
382
+ ctx: SKRSContext2D,
383
+ text: string,
384
+ x: number,
385
+ y: number,
386
+ width: number,
387
+ height: number,
388
+ textProps: TextProperties
389
+ ): void {
390
+ if (!textProps.underline && !textProps.overline && !textProps.strikethrough) {
391
+ return;
392
+ }
393
+
394
+ ctx.save();
395
+
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
399
+
400
+ ctx.strokeStyle = decorationColor;
401
+ ctx.lineWidth = decorationWidth;
402
+
403
+ // Underline
404
+ if (textProps.underline) {
405
+ const underlineY = y + fontSize * 0.1;
406
+ ctx.beginPath();
407
+ ctx.moveTo(x, underlineY);
408
+ ctx.lineTo(x + width, underlineY);
409
+ ctx.stroke();
410
+ }
411
+
412
+ // Overline
413
+ if (textProps.overline) {
414
+ const overlineY = y - fontSize * 0.8;
415
+ ctx.beginPath();
416
+ ctx.moveTo(x, overlineY);
417
+ ctx.lineTo(x + width, overlineY);
418
+ ctx.stroke();
419
+ }
420
+
421
+ // Strikethrough
422
+ if (textProps.strikethrough) {
423
+ const strikethroughY = y - fontSize * 0.3;
424
+ ctx.beginPath();
425
+ ctx.moveTo(x, strikethroughY);
426
+ ctx.lineTo(x + width, strikethroughY);
427
+ ctx.stroke();
428
+ }
429
+
430
+ ctx.restore();
431
+ }
432
+
433
+ /**
434
+ * Creates a gradient for text fill or stroke
435
+ * @param ctx - Canvas 2D context
436
+ * @param gradientOptions - Gradient configuration
437
+ * @param startX - Start X position
438
+ * @param startY - Start Y position
439
+ * @param endX - End X position
440
+ * @param endY - End Y position
441
+ * @returns Canvas gradient
442
+ */
443
+ private static createGradient(
444
+ ctx: SKRSContext2D,
445
+ gradientOptions: gradient,
446
+ startX: number,
447
+ startY: number,
448
+ endX: number,
449
+ endY: number
450
+ ): CanvasGradient {
451
+ if (!gradientOptions || !gradientOptions.type || !gradientOptions.colors) {
452
+ throw new Error("Invalid gradient options. Provide a valid object with type and colors properties.");
453
+ }
454
+
455
+ let gradient: CanvasGradient;
456
+
457
+ if (gradientOptions.type === "linear") {
458
+ gradient = ctx.createLinearGradient(startX, startY, endX, endY);
459
+ } else if (gradientOptions.type === "radial") {
460
+ gradient = ctx.createRadialGradient(
461
+ gradientOptions.startX || startX,
462
+ gradientOptions.startY || startY,
463
+ gradientOptions.startRadius || 0,
464
+ gradientOptions.endX || endX,
465
+ gradientOptions.endY || endY,
466
+ gradientOptions.endRadius || 0
467
+ );
468
+ } else {
469
+ throw new Error('Unsupported gradient type. Use "linear" or "radial".');
470
+ }
471
+
472
+ for (const colorStop of gradientOptions.colors) {
473
+ gradient.addColorStop(colorStop.stop, colorStop.color);
474
+ }
475
+
476
+ return gradient;
477
+ }
478
+ }