@nasser-sw/fabric 7.0.1-beta8 → 7.0.1-beta9

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 (138) hide show
  1. package/debug/konva-master/CHANGELOG.md +1475 -0
  2. package/debug/konva-master/LICENSE +22 -0
  3. package/debug/konva-master/README.md +209 -0
  4. package/debug/konva-master/gulpfile.mjs +110 -0
  5. package/debug/konva-master/package.json +139 -0
  6. package/debug/konva-master/release.sh +62 -0
  7. package/debug/konva-master/resources/doc-includes/ContainerParams.txt +6 -0
  8. package/debug/konva-master/resources/doc-includes/NodeParams.txt +20 -0
  9. package/debug/konva-master/resources/doc-includes/ShapeParams.txt +53 -0
  10. package/debug/konva-master/resources/jsdoc.conf.json +28 -0
  11. package/debug/konva-master/rollup.config.mjs +32 -0
  12. package/debug/konva-master/src/Animation.ts +237 -0
  13. package/debug/konva-master/src/BezierFunctions.ts +826 -0
  14. package/debug/konva-master/src/Canvas.ts +230 -0
  15. package/debug/konva-master/src/Container.ts +649 -0
  16. package/debug/konva-master/src/Context.ts +1017 -0
  17. package/debug/konva-master/src/Core.ts +5 -0
  18. package/debug/konva-master/src/DragAndDrop.ts +173 -0
  19. package/debug/konva-master/src/Factory.ts +246 -0
  20. package/debug/konva-master/src/FastLayer.ts +29 -0
  21. package/debug/konva-master/src/Global.ts +210 -0
  22. package/debug/konva-master/src/Group.ts +31 -0
  23. package/debug/konva-master/src/Layer.ts +546 -0
  24. package/debug/konva-master/src/Node.ts +3477 -0
  25. package/debug/konva-master/src/PointerEvents.ts +67 -0
  26. package/debug/konva-master/src/Shape.ts +2081 -0
  27. package/debug/konva-master/src/Stage.ts +1000 -0
  28. package/debug/konva-master/src/Tween.ts +811 -0
  29. package/debug/konva-master/src/Util.ts +1123 -0
  30. package/debug/konva-master/src/Validators.ts +210 -0
  31. package/debug/konva-master/src/_CoreInternals.ts +85 -0
  32. package/debug/konva-master/src/_FullInternals.ts +171 -0
  33. package/debug/konva-master/src/canvas-backend.ts +36 -0
  34. package/debug/konva-master/src/filters/Blur.ts +388 -0
  35. package/debug/konva-master/src/filters/Brighten.ts +48 -0
  36. package/debug/konva-master/src/filters/Brightness.ts +30 -0
  37. package/debug/konva-master/src/filters/Contrast.ts +75 -0
  38. package/debug/konva-master/src/filters/Emboss.ts +207 -0
  39. package/debug/konva-master/src/filters/Enhance.ts +154 -0
  40. package/debug/konva-master/src/filters/Grayscale.ts +25 -0
  41. package/debug/konva-master/src/filters/HSL.ts +108 -0
  42. package/debug/konva-master/src/filters/HSV.ts +106 -0
  43. package/debug/konva-master/src/filters/Invert.ts +23 -0
  44. package/debug/konva-master/src/filters/Kaleidoscope.ts +274 -0
  45. package/debug/konva-master/src/filters/Mask.ts +220 -0
  46. package/debug/konva-master/src/filters/Noise.ts +44 -0
  47. package/debug/konva-master/src/filters/Pixelate.ts +107 -0
  48. package/debug/konva-master/src/filters/Posterize.ts +46 -0
  49. package/debug/konva-master/src/filters/RGB.ts +82 -0
  50. package/debug/konva-master/src/filters/RGBA.ts +103 -0
  51. package/debug/konva-master/src/filters/Sepia.ts +27 -0
  52. package/debug/konva-master/src/filters/Solarize.ts +29 -0
  53. package/debug/konva-master/src/filters/Threshold.ts +44 -0
  54. package/debug/konva-master/src/index.ts +3 -0
  55. package/debug/konva-master/src/shapes/Arc.ts +176 -0
  56. package/debug/konva-master/src/shapes/Arrow.ts +231 -0
  57. package/debug/konva-master/src/shapes/Circle.ts +76 -0
  58. package/debug/konva-master/src/shapes/Ellipse.ts +121 -0
  59. package/debug/konva-master/src/shapes/Image.ts +319 -0
  60. package/debug/konva-master/src/shapes/Label.ts +386 -0
  61. package/debug/konva-master/src/shapes/Line.ts +364 -0
  62. package/debug/konva-master/src/shapes/Path.ts +1013 -0
  63. package/debug/konva-master/src/shapes/Rect.ts +79 -0
  64. package/debug/konva-master/src/shapes/RegularPolygon.ts +167 -0
  65. package/debug/konva-master/src/shapes/Ring.ts +94 -0
  66. package/debug/konva-master/src/shapes/Sprite.ts +370 -0
  67. package/debug/konva-master/src/shapes/Star.ts +125 -0
  68. package/debug/konva-master/src/shapes/Text.ts +1065 -0
  69. package/debug/konva-master/src/shapes/TextPath.ts +583 -0
  70. package/debug/konva-master/src/shapes/Transformer.ts +1889 -0
  71. package/debug/konva-master/src/shapes/Wedge.ts +129 -0
  72. package/debug/konva-master/src/skia-backend.ts +35 -0
  73. package/debug/konva-master/src/types.ts +84 -0
  74. package/debug/konva-master/tsconfig.json +31 -0
  75. package/debug/konva-master/tsconfig.test.json +7 -0
  76. package/dist/index.js +915 -23
  77. package/dist/index.js.map +1 -1
  78. package/dist/index.min.js +1 -1
  79. package/dist/index.min.js.map +1 -1
  80. package/dist/index.min.mjs +1 -1
  81. package/dist/index.min.mjs.map +1 -1
  82. package/dist/index.mjs +915 -23
  83. package/dist/index.mjs.map +1 -1
  84. package/dist/index.node.cjs +915 -23
  85. package/dist/index.node.cjs.map +1 -1
  86. package/dist/index.node.mjs +915 -23
  87. package/dist/index.node.mjs.map +1 -1
  88. package/dist/package.json.min.mjs +1 -1
  89. package/dist/package.json.mjs +1 -1
  90. package/dist/src/shapes/Text/Text.d.ts +19 -0
  91. package/dist/src/shapes/Text/Text.d.ts.map +1 -1
  92. package/dist/src/shapes/Text/Text.min.mjs +1 -1
  93. package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
  94. package/dist/src/shapes/Text/Text.mjs +238 -4
  95. package/dist/src/shapes/Text/Text.mjs.map +1 -1
  96. package/dist/src/shapes/Textbox.d.ts +38 -1
  97. package/dist/src/shapes/Textbox.d.ts.map +1 -1
  98. package/dist/src/shapes/Textbox.min.mjs +1 -1
  99. package/dist/src/shapes/Textbox.min.mjs.map +1 -1
  100. package/dist/src/shapes/Textbox.mjs +497 -15
  101. package/dist/src/shapes/Textbox.mjs.map +1 -1
  102. package/dist/src/text/examples/arabicTextExample.d.ts +60 -0
  103. package/dist/src/text/examples/arabicTextExample.d.ts.map +1 -0
  104. package/dist/src/text/measure.d.ts +9 -0
  105. package/dist/src/text/measure.d.ts.map +1 -1
  106. package/dist/src/text/measure.min.mjs +1 -1
  107. package/dist/src/text/measure.min.mjs.map +1 -1
  108. package/dist/src/text/measure.mjs +175 -4
  109. package/dist/src/text/measure.mjs.map +1 -1
  110. package/dist/src/text/overlayEditor.d.ts.map +1 -1
  111. package/dist/src/text/overlayEditor.min.mjs +1 -1
  112. package/dist/src/text/overlayEditor.min.mjs.map +1 -1
  113. package/dist/src/text/overlayEditor.mjs +7 -0
  114. package/dist/src/text/overlayEditor.mjs.map +1 -1
  115. package/dist/src/text/scriptUtils.d.ts +142 -0
  116. package/dist/src/text/scriptUtils.d.ts.map +1 -0
  117. package/dist/src/text/scriptUtils.min.mjs +2 -0
  118. package/dist/src/text/scriptUtils.min.mjs.map +1 -0
  119. package/dist/src/text/scriptUtils.mjs +212 -0
  120. package/dist/src/text/scriptUtils.mjs.map +1 -0
  121. package/dist-extensions/src/shapes/Text/Text.d.ts +19 -0
  122. package/dist-extensions/src/shapes/Text/Text.d.ts.map +1 -1
  123. package/dist-extensions/src/shapes/Textbox.d.ts +38 -1
  124. package/dist-extensions/src/shapes/Textbox.d.ts.map +1 -1
  125. package/dist-extensions/src/text/measure.d.ts +9 -0
  126. package/dist-extensions/src/text/measure.d.ts.map +1 -1
  127. package/dist-extensions/src/text/overlayEditor.d.ts.map +1 -1
  128. package/dist-extensions/src/text/scriptUtils.d.ts +142 -0
  129. package/dist-extensions/src/text/scriptUtils.d.ts.map +1 -0
  130. package/fabric-test-editor.html +2401 -46
  131. package/fonts/STV Bold.ttf +0 -0
  132. package/fonts/STV Light.ttf +0 -0
  133. package/fonts/STV Regular.ttf +0 -0
  134. package/package.json +1 -1
  135. package/src/shapes/Text/Text.ts +238 -5
  136. package/src/shapes/Textbox.ts +521 -11
  137. package/src/text/measure.ts +200 -50
  138. package/src/text/overlayEditor.ts +7 -0
@@ -0,0 +1,1065 @@
1
+ import { Util } from '../Util.ts';
2
+ import type { Context } from '../Context.ts';
3
+ import { Factory } from '../Factory.ts';
4
+ import type { ShapeConfig } from '../Shape.ts';
5
+ import { Shape } from '../Shape.ts';
6
+ import { Konva } from '../Global.ts';
7
+ import {
8
+ getNumberValidator,
9
+ getStringValidator,
10
+ getNumberOrAutoValidator,
11
+ getBooleanValidator,
12
+ } from '../Validators.ts';
13
+ import { _registerNode } from '../Global.ts';
14
+
15
+ import type { GetSet } from '../types.ts';
16
+
17
+ export interface CharRenderProps {
18
+ char: string;
19
+ index: number;
20
+ x: number;
21
+ y: number;
22
+ lineIndex: number;
23
+ column: number;
24
+ isLastInLine: boolean;
25
+ width: number;
26
+ context: Context;
27
+ }
28
+
29
+ export function stringToArray(string: string): string[] {
30
+ // Use Unicode-aware splitting
31
+ return [...string].reduce((acc, char, index, array) => {
32
+ // Handle emoji with skin tone modifiers and ZWJ sequences
33
+ if (/\p{Emoji}/u.test(char)) {
34
+ // Check if next character is a modifier or ZWJ sequence
35
+ const nextChar = array[index + 1];
36
+ if (nextChar && /\p{Emoji_Modifier}|\u200D/u.test(nextChar)) {
37
+ // If we have a modifier, combine with current emoji
38
+ acc.push(char + nextChar);
39
+ // Skip the next character since we've used it
40
+ array[index + 1] = '';
41
+ } else {
42
+ // No modifier - treat as separate emoji
43
+ acc.push(char);
44
+ }
45
+ }
46
+ // Handle regional indicator symbols (flags)
47
+ else if (
48
+ /\p{Regional_Indicator}{2}/u.test(char + (array[index + 1] || ''))
49
+ ) {
50
+ acc.push(char + array[index + 1]);
51
+ }
52
+ // Handle Indic scripts and other combining characters
53
+ else if (index > 0 && /\p{Mn}|\p{Me}|\p{Mc}/u.test(char)) {
54
+ acc[acc.length - 1] += char;
55
+ }
56
+ // Handle other characters
57
+ else if (char) {
58
+ // Only push if not an empty string (skipped modifier)
59
+ acc.push(char);
60
+ }
61
+ return acc;
62
+ }, [] as string[]);
63
+ }
64
+
65
+ export interface TextConfig extends ShapeConfig {
66
+ direction?: string;
67
+ text?: string;
68
+ fontFamily?: string;
69
+ fontSize?: number;
70
+ fontStyle?: string;
71
+ fontVariant?: string;
72
+ textDecoration?: string;
73
+ align?: string;
74
+ verticalAlign?: string;
75
+ padding?: number;
76
+ lineHeight?: number;
77
+ letterSpacing?: number;
78
+ wrap?: string;
79
+ ellipsis?: boolean;
80
+ }
81
+
82
+ // constants
83
+ const AUTO = 'auto',
84
+ //CANVAS = 'canvas',
85
+ CENTER = 'center',
86
+ INHERIT = 'inherit',
87
+ JUSTIFY = 'justify',
88
+ CHANGE_KONVA = 'Change.konva',
89
+ CONTEXT_2D = '2d',
90
+ DASH = '-',
91
+ LEFT = 'left',
92
+ TEXT = 'text',
93
+ TEXT_UPPER = 'Text',
94
+ TOP = 'top',
95
+ BOTTOM = 'bottom',
96
+ MIDDLE = 'middle',
97
+ NORMAL = 'normal',
98
+ PX_SPACE = 'px ',
99
+ SPACE = ' ',
100
+ RIGHT = 'right',
101
+ RTL = 'rtl',
102
+ WORD = 'word',
103
+ CHAR = 'char',
104
+ NONE = 'none',
105
+ ELLIPSIS = '…',
106
+ ATTR_CHANGE_LIST = [
107
+ 'direction',
108
+ 'fontFamily',
109
+ 'fontSize',
110
+ 'fontStyle',
111
+ 'fontVariant',
112
+ 'padding',
113
+ 'align',
114
+ 'verticalAlign',
115
+ 'lineHeight',
116
+ 'text',
117
+ 'width',
118
+ 'height',
119
+ 'wrap',
120
+ 'ellipsis',
121
+ 'letterSpacing',
122
+ ],
123
+ // cached variables
124
+ attrChangeListLen = ATTR_CHANGE_LIST.length;
125
+
126
+ function normalizeFontFamily(fontFamily: string) {
127
+ return fontFamily
128
+ .split(',')
129
+ .map((family) => {
130
+ family = family.trim();
131
+ const hasSpace = family.indexOf(' ') >= 0;
132
+ const hasQuotes = family.indexOf('"') >= 0 || family.indexOf("'") >= 0;
133
+ if (hasSpace && !hasQuotes) {
134
+ family = `"${family}"`;
135
+ }
136
+ return family;
137
+ })
138
+ .join(', ');
139
+ }
140
+
141
+ let dummyContext: CanvasRenderingContext2D;
142
+ function getDummyContext() {
143
+ if (dummyContext) {
144
+ return dummyContext;
145
+ }
146
+ dummyContext = Util.createCanvasElement().getContext(
147
+ CONTEXT_2D
148
+ ) as CanvasRenderingContext2D;
149
+ return dummyContext;
150
+ }
151
+
152
+ function _fillFunc(this: Text, context: Context) {
153
+ context.fillText(this._partialText, this._partialTextX, this._partialTextY);
154
+ }
155
+ function _strokeFunc(this: Text, context: Context) {
156
+ context.setAttr('miterLimit', 2);
157
+ context.strokeText(this._partialText, this._partialTextX, this._partialTextY);
158
+ }
159
+
160
+ function checkDefaultFill(config?: TextConfig) {
161
+ config = config || {};
162
+
163
+ // set default color to black
164
+ if (
165
+ !config.fillLinearGradientColorStops &&
166
+ !config.fillRadialGradientColorStops &&
167
+ !config.fillPatternImage
168
+ ) {
169
+ config.fill = config.fill || 'black';
170
+ }
171
+ return config;
172
+ }
173
+
174
+ /**
175
+ * Text constructor
176
+ * @constructor
177
+ * @memberof Konva
178
+ * @augments Konva.Shape
179
+ * @param {Object} config
180
+ * @param {String} [config.direction] default is inherit
181
+ * @param {String} [config.fontFamily] default is Arial
182
+ * @param {Number} [config.fontSize] in pixels. Default is 12
183
+ * @param {String} [config.fontStyle] can be 'normal', 'italic', or 'bold', '500' or even 'italic bold'. 'normal' is the default.
184
+ * @param {String} [config.fontVariant] can be normal or small-caps. Default is normal
185
+ * @param {String} [config.textDecoration] can be line-through, underline or empty string. Default is empty string.
186
+ * @param {String} config.text
187
+ * @param {String} [config.align] can be left, center, right or justify
188
+ * @param {String} [config.verticalAlign] can be top, middle or bottom
189
+ * @param {Number} [config.padding]
190
+ * @param {Number} [config.lineHeight] default is 1
191
+ * @param {String} [config.wrap] can be "word", "char", or "none". Default is word
192
+ * @param {Boolean} [config.ellipsis] can be true or false. Default is false. if Konva.Text config is set to wrap="none" and ellipsis=true, then it will add "..." to the end
193
+ * @@shapeParams
194
+ * @@nodeParams
195
+ * @example
196
+ * var text = new Konva.Text({
197
+ * x: 10,
198
+ * y: 15,
199
+ * text: 'Simple Text',
200
+ * fontSize: 30,
201
+ * fontFamily: 'Calibri',
202
+ * fill: 'green'
203
+ * });
204
+ */
205
+ export class Text extends Shape<TextConfig> {
206
+ textArr: Array<{ text: string; width: number; lastInParagraph: boolean }>;
207
+ _partialText: string;
208
+ _partialTextX = 0;
209
+ _partialTextY = 0;
210
+
211
+ textWidth: number;
212
+ textHeight: number;
213
+ constructor(config?: TextConfig) {
214
+ super(checkDefaultFill(config));
215
+ // update text data for certain attr changes
216
+ for (let n = 0; n < attrChangeListLen; n++) {
217
+ this.on(ATTR_CHANGE_LIST[n] + CHANGE_KONVA, this._setTextData);
218
+ }
219
+ this._setTextData();
220
+ }
221
+
222
+ _sceneFunc(context: Context) {
223
+ const textArr = this.textArr,
224
+ textArrLen = textArr.length;
225
+
226
+ if (!this.text()) {
227
+ return;
228
+ }
229
+
230
+ let padding = this.padding(),
231
+ fontSize = this.fontSize(),
232
+ lineHeightPx = this.lineHeight() * fontSize,
233
+ verticalAlign = this.verticalAlign(),
234
+ direction = this.direction(),
235
+ alignY = 0,
236
+ align = this.align(),
237
+ totalWidth = this.getWidth(),
238
+ letterSpacing = this.letterSpacing(),
239
+ charRenderFunc = this.charRenderFunc(),
240
+ fill = this.fill(),
241
+ textDecoration = this.textDecoration(),
242
+ shouldUnderline = textDecoration.indexOf('underline') !== -1,
243
+ shouldLineThrough = textDecoration.indexOf('line-through') !== -1,
244
+ n;
245
+
246
+ direction = direction === INHERIT ? context.direction : direction;
247
+
248
+ let translateY = lineHeightPx / 2;
249
+ let baseline = MIDDLE;
250
+ if (!Konva.legacyTextRendering) {
251
+ const metrics = this.measureSize('M'); // Use a sample character to get the ascent
252
+
253
+ baseline = 'alphabetic';
254
+ const ascent =
255
+ metrics.fontBoundingBoxAscent ?? metrics.actualBoundingBoxAscent;
256
+ const descent =
257
+ metrics.fontBoundingBoxDescent ?? metrics.actualBoundingBoxDescent;
258
+
259
+ translateY = (ascent - descent) / 2 + lineHeightPx / 2;
260
+ }
261
+
262
+ if (direction === RTL) {
263
+ context.setAttr('direction', direction);
264
+ }
265
+
266
+ context.setAttr('font', this._getContextFont());
267
+
268
+ context.setAttr('textBaseline', baseline);
269
+
270
+ context.setAttr('textAlign', LEFT);
271
+
272
+ // handle vertical alignment
273
+ if (verticalAlign === MIDDLE) {
274
+ alignY = (this.getHeight() - textArrLen * lineHeightPx - padding * 2) / 2;
275
+ } else if (verticalAlign === BOTTOM) {
276
+ alignY = this.getHeight() - textArrLen * lineHeightPx - padding * 2;
277
+ }
278
+
279
+ context.translate(padding, alignY + padding);
280
+
281
+ // draw text lines
282
+ for (n = 0; n < textArrLen; n++) {
283
+ let lineTranslateX = 0;
284
+ let lineTranslateY = 0;
285
+ const obj = textArr[n],
286
+ text = obj.text,
287
+ width = obj.width,
288
+ lastLine = obj.lastInParagraph;
289
+
290
+ // horizontal alignment
291
+ context.save();
292
+ if (align === RIGHT) {
293
+ lineTranslateX += totalWidth - width - padding * 2;
294
+ } else if (align === CENTER) {
295
+ lineTranslateX += (totalWidth - width - padding * 2) / 2;
296
+ }
297
+
298
+ if (shouldUnderline) {
299
+ context.save();
300
+ context.beginPath();
301
+
302
+ const yOffset = !Konva.legacyTextRendering
303
+ ? Math.round(fontSize / 4)
304
+ : Math.round(fontSize / 2);
305
+ const x = lineTranslateX;
306
+ const y = translateY + lineTranslateY + yOffset;
307
+ context.moveTo(x, y);
308
+ const lineWidth =
309
+ align === JUSTIFY && !lastLine ? totalWidth - padding * 2 : width;
310
+ context.lineTo(x + Math.round(lineWidth), y);
311
+
312
+ // I have no idea what is real ratio
313
+ // just /15 looks good enough
314
+ context.lineWidth = fontSize / 15;
315
+
316
+ const gradient = this._getLinearGradient();
317
+ context.strokeStyle = gradient || fill;
318
+ context.stroke();
319
+ context.restore();
320
+ }
321
+ // draw line-through above the text content
322
+
323
+ // As `letterSpacing` isn't supported on Safari, we use this polyfill.
324
+ // The exception is for RTL text, which we rely on native as it cannot
325
+ // be supported otherwise.
326
+ if (
327
+ direction !== RTL &&
328
+ (letterSpacing !== 0 || align === JUSTIFY || charRenderFunc)
329
+ ) {
330
+ // var words = text.split(' ');
331
+ const spacesNumber = text.split(' ').length - 1;
332
+ const array = stringToArray(text);
333
+ for (let li = 0; li < array.length; li++) {
334
+ const letter = array[li];
335
+ // skip justify for the last line
336
+ if (letter === ' ' && !lastLine && align === JUSTIFY) {
337
+ lineTranslateX += (totalWidth - padding * 2 - width) / spacesNumber;
338
+ // context.translate(
339
+ // Math.floor((totalWidth - padding * 2 - width) / spacesNumber),
340
+ // 0
341
+ // );
342
+ }
343
+ this._partialTextX = lineTranslateX;
344
+ this._partialTextY = translateY + lineTranslateY;
345
+ this._partialText = letter;
346
+
347
+ if (charRenderFunc) {
348
+ context.save();
349
+ const previousLines = textArr.slice(0, n);
350
+ const previousGraphemes = previousLines.reduce(
351
+ (acc, line) => acc + stringToArray(line.text).length,
352
+ 0
353
+ );
354
+ const charIndex = li + previousGraphemes;
355
+ charRenderFunc({
356
+ char: letter,
357
+ index: charIndex,
358
+ x: lineTranslateX,
359
+ y: translateY + lineTranslateY,
360
+ lineIndex: n,
361
+ column: li,
362
+ isLastInLine: lastLine,
363
+ width: this.measureSize(letter).width,
364
+ context,
365
+ });
366
+ }
367
+ context.fillStrokeShape(this);
368
+ if (charRenderFunc) {
369
+ context.restore();
370
+ }
371
+ lineTranslateX += this.measureSize(letter).width + letterSpacing;
372
+ }
373
+ } else {
374
+ if (letterSpacing !== 0) {
375
+ context.setAttr('letterSpacing', `${letterSpacing}px`);
376
+ }
377
+ this._partialTextX = lineTranslateX;
378
+ this._partialTextY = translateY + lineTranslateY;
379
+ this._partialText = text;
380
+
381
+ context.fillStrokeShape(this);
382
+ }
383
+ // draw line-through above the text content
384
+ if (shouldLineThrough) {
385
+ context.save();
386
+ context.beginPath();
387
+ const yOffset = !Konva.legacyTextRendering
388
+ ? -Math.round(fontSize / 4)
389
+ : 0;
390
+ context.moveTo(0, translateY + lineTranslateY + yOffset);
391
+ const lineWidth =
392
+ align === JUSTIFY && !lastLine ? totalWidth - padding * 2 : width;
393
+ context.lineTo(
394
+ Math.round(lineWidth),
395
+ translateY + lineTranslateY + yOffset
396
+ );
397
+ context.lineWidth = fontSize / 15;
398
+ const gradient = this._getLinearGradient();
399
+ context.strokeStyle = gradient || fill;
400
+ context.stroke();
401
+ context.restore();
402
+ }
403
+ context.restore();
404
+ if (textArrLen > 1) {
405
+ translateY += lineHeightPx;
406
+ }
407
+ }
408
+ }
409
+ _hitFunc(context: Context) {
410
+ const width = this.getWidth(),
411
+ height = this.getHeight();
412
+
413
+ context.beginPath();
414
+ context.rect(0, 0, width, height);
415
+ context.closePath();
416
+ context.fillStrokeShape(this);
417
+ }
418
+ setText(text: string) {
419
+ const str = Util._isString(text)
420
+ ? text
421
+ : text === null || text === undefined
422
+ ? ''
423
+ : text + '';
424
+ this._setAttr(TEXT, str);
425
+ return this;
426
+ }
427
+ getWidth() {
428
+ const isAuto = this.attrs.width === AUTO || this.attrs.width === undefined;
429
+ return isAuto ? this.getTextWidth() + this.padding() * 2 : this.attrs.width;
430
+ }
431
+ getHeight() {
432
+ const isAuto =
433
+ this.attrs.height === AUTO || this.attrs.height === undefined;
434
+ return isAuto
435
+ ? this.fontSize() * this.textArr.length * this.lineHeight() +
436
+ this.padding() * 2
437
+ : this.attrs.height;
438
+ }
439
+ /**
440
+ * get pure text width without padding
441
+ * @method
442
+ * @name Konva.Text#getTextWidth
443
+ * @returns {Number}
444
+ */
445
+ getTextWidth() {
446
+ return this.textWidth;
447
+ }
448
+ getTextHeight() {
449
+ Util.warn(
450
+ 'text.getTextHeight() method is deprecated. Use text.height() - for full height and text.fontSize() - for one line height.'
451
+ );
452
+ return this.textHeight;
453
+ }
454
+
455
+ /**
456
+ * measure string with the font of current text shape.
457
+ * That method can't handle multiline text.
458
+ * @method
459
+ * @name Konva.Text#measureSize
460
+ * @param {String} text text to measure
461
+ * @returns {Object} { width , height } of measured text
462
+ */
463
+ measureSize(text: string) {
464
+ let _context = getDummyContext(),
465
+ fontSize = this.fontSize(),
466
+ metrics: TextMetrics;
467
+
468
+ _context.save();
469
+ _context.font = this._getContextFont();
470
+
471
+ metrics = _context.measureText(text);
472
+ _context.restore();
473
+
474
+ // Scale the fallback values based on the provided fontSize compared to the sample size (100 in your new case)
475
+ const scaleFactor = fontSize / 100;
476
+
477
+ // Note, fallback values are from chrome browser with 100px font size and font-family "Arial"
478
+ return {
479
+ actualBoundingBoxAscent:
480
+ metrics.actualBoundingBoxAscent ?? 71.58203125 * scaleFactor,
481
+ actualBoundingBoxDescent: metrics.actualBoundingBoxDescent ?? 0, // Remains zero as there is no descent in the provided metrics
482
+ actualBoundingBoxLeft:
483
+ metrics.actualBoundingBoxLeft ?? -7.421875 * scaleFactor,
484
+ actualBoundingBoxRight:
485
+ metrics.actualBoundingBoxRight ?? 75.732421875 * scaleFactor,
486
+ alphabeticBaseline: metrics.alphabeticBaseline ?? 0, // Remains zero as it's typically relative to the baseline itself
487
+ emHeightAscent: metrics.emHeightAscent ?? 100 * scaleFactor,
488
+ emHeightDescent: metrics.emHeightDescent ?? -20 * scaleFactor,
489
+ fontBoundingBoxAscent: metrics.fontBoundingBoxAscent ?? 91 * scaleFactor,
490
+ fontBoundingBoxDescent:
491
+ metrics.fontBoundingBoxDescent ?? 21 * scaleFactor,
492
+ hangingBaseline:
493
+ metrics.hangingBaseline ?? 72.80000305175781 * scaleFactor,
494
+ ideographicBaseline: metrics.ideographicBaseline ?? -21 * scaleFactor,
495
+ width: metrics.width,
496
+ height: fontSize, // Typically set to the font size
497
+ };
498
+ }
499
+ _getContextFont() {
500
+ return (
501
+ this.fontStyle() +
502
+ SPACE +
503
+ this.fontVariant() +
504
+ SPACE +
505
+ (this.fontSize() + PX_SPACE) +
506
+ // wrap font family into " so font families with spaces works ok
507
+ normalizeFontFamily(this.fontFamily())
508
+ );
509
+ }
510
+ _addTextLine(line: string) {
511
+ const align = this.align();
512
+ if (align === JUSTIFY) {
513
+ line = line.trim();
514
+ }
515
+ const width = this._getTextWidth(line);
516
+ return this.textArr.push({
517
+ text: line,
518
+ width: width,
519
+ lastInParagraph: false,
520
+ });
521
+ }
522
+ _getTextWidth(text: string) {
523
+ const letterSpacing = this.letterSpacing();
524
+ const length = text.length;
525
+ // letterSpacing * length is the total letter spacing for the text
526
+ // previously we used letterSpacing * (length - 1) but it doesn't match DOM behavior
527
+ return getDummyContext().measureText(text).width + letterSpacing * length;
528
+ }
529
+ _setTextData() {
530
+ let lines = this.text().split('\n'),
531
+ fontSize = +this.fontSize(),
532
+ textWidth = 0,
533
+ lineHeightPx = this.lineHeight() * fontSize,
534
+ width = this.attrs.width,
535
+ height = this.attrs.height,
536
+ fixedWidth = width !== AUTO && width !== undefined,
537
+ fixedHeight = height !== AUTO && height !== undefined,
538
+ padding = this.padding(),
539
+ maxWidth = width - padding * 2,
540
+ maxHeightPx = height - padding * 2,
541
+ currentHeightPx = 0,
542
+ wrap = this.wrap(),
543
+ // align = this.align(),
544
+ shouldWrap = wrap !== NONE,
545
+ wrapAtWord = wrap !== CHAR && shouldWrap,
546
+ shouldAddEllipsis = this.ellipsis();
547
+
548
+ this.textArr = [];
549
+ getDummyContext().font = this._getContextFont();
550
+ const additionalWidth = shouldAddEllipsis
551
+ ? this._getTextWidth(ELLIPSIS)
552
+ : 0;
553
+ for (let i = 0, max = lines.length; i < max; ++i) {
554
+ let line = lines[i];
555
+
556
+ let lineWidth = this._getTextWidth(line);
557
+ if (fixedWidth && lineWidth > maxWidth) {
558
+ /*
559
+ * if width is fixed and line does not fit entirely
560
+ * break the line into multiple fitting lines
561
+ */
562
+ while (line.length > 0) {
563
+ /*
564
+ * use binary search to find the longest substring that
565
+ * that would fit in the specified width
566
+ */
567
+ let low = 0,
568
+ high = stringToArray(line).length, // Convert to array for proper emoji handling
569
+ match = '',
570
+ matchWidth = 0;
571
+ while (low < high) {
572
+ const mid = (low + high) >>> 1,
573
+ // Convert array indices to string
574
+ lineArray = stringToArray(line),
575
+ substr = lineArray.slice(0, mid + 1).join(''),
576
+ substrWidth = this._getTextWidth(substr);
577
+
578
+ // Only add ellipsis width when we need to consider truncation
579
+ // for the current line (when it might be the last visible line)
580
+ const shouldConsiderEllipsis =
581
+ shouldAddEllipsis &&
582
+ fixedHeight &&
583
+ currentHeightPx + lineHeightPx > maxHeightPx;
584
+
585
+ const effectiveWidth = shouldConsiderEllipsis
586
+ ? substrWidth + additionalWidth
587
+ : substrWidth;
588
+
589
+ if (effectiveWidth <= maxWidth) {
590
+ low = mid + 1;
591
+ match = substr;
592
+ matchWidth = substrWidth; // Store actual text width without ellipsis
593
+ } else {
594
+ high = mid;
595
+ }
596
+ }
597
+ /*
598
+ * 'low' is now the index of the substring end
599
+ * 'match' is the substring
600
+ * 'matchWidth' is the substring width in px
601
+ */
602
+ if (match) {
603
+ // a fitting substring was found
604
+ if (wrapAtWord) {
605
+ // try to find a space or dash where wrapping could be done
606
+ const lineArray = stringToArray(line);
607
+ const matchArray = stringToArray(match);
608
+ const nextChar = lineArray[matchArray.length];
609
+ const nextIsSpaceOrDash = nextChar === SPACE || nextChar === DASH;
610
+
611
+ let wrapIndex;
612
+ if (nextIsSpaceOrDash && matchWidth <= maxWidth) {
613
+ wrapIndex = matchArray.length;
614
+ } else {
615
+ // Find last space or dash in the array
616
+ const lastSpaceIndex = matchArray.lastIndexOf(SPACE);
617
+ const lastDashIndex = matchArray.lastIndexOf(DASH);
618
+ wrapIndex = Math.max(lastSpaceIndex, lastDashIndex) + 1;
619
+ }
620
+
621
+ if (wrapIndex > 0) {
622
+ low = wrapIndex;
623
+ match = lineArray.slice(0, low).join('');
624
+ matchWidth = this._getTextWidth(match);
625
+ }
626
+ }
627
+ // if (align === 'right') {
628
+ match = match.trimRight();
629
+ // }
630
+ this._addTextLine(match);
631
+ textWidth = Math.max(textWidth, matchWidth);
632
+ currentHeightPx += lineHeightPx;
633
+
634
+ const shouldHandleEllipsis =
635
+ this._shouldHandleEllipsis(currentHeightPx);
636
+ if (shouldHandleEllipsis) {
637
+ this._tryToAddEllipsisToLastLine();
638
+ /*
639
+ * stop wrapping if wrapping is disabled or if adding
640
+ * one more line would overflow the fixed height
641
+ */
642
+ break;
643
+ }
644
+
645
+ // Convert remaining text using array operations
646
+ const lineArray = stringToArray(line);
647
+ line = lineArray.slice(low).join('').trimLeft();
648
+
649
+ if (line.length > 0) {
650
+ lineWidth = this._getTextWidth(line);
651
+ if (lineWidth <= maxWidth) {
652
+ this._addTextLine(line);
653
+ currentHeightPx += lineHeightPx;
654
+ textWidth = Math.max(textWidth, lineWidth);
655
+ break;
656
+ }
657
+ }
658
+ } else {
659
+ // not even one character could fit in the element, abort
660
+ break;
661
+ }
662
+ }
663
+ } else {
664
+ // element width is automatically adjusted to max line width
665
+ this._addTextLine(line);
666
+ currentHeightPx += lineHeightPx;
667
+ textWidth = Math.max(textWidth, lineWidth);
668
+ if (this._shouldHandleEllipsis(currentHeightPx) && i < max - 1) {
669
+ this._tryToAddEllipsisToLastLine();
670
+ }
671
+ }
672
+ // if element height is fixed, abort if adding one more line would overflow
673
+ if (this.textArr[this.textArr.length - 1]) {
674
+ this.textArr[this.textArr.length - 1].lastInParagraph = true;
675
+ }
676
+ if (fixedHeight && currentHeightPx + lineHeightPx > maxHeightPx) {
677
+ break;
678
+ }
679
+ }
680
+ this.textHeight = fontSize;
681
+ // var maxTextWidth = 0;
682
+ // for(var j = 0; j < this.textArr.length; j++) {
683
+ // maxTextWidth = Math.max(maxTextWidth, this.textArr[j].width);
684
+ // }
685
+ this.textWidth = textWidth;
686
+ }
687
+
688
+ /**
689
+ * whether to handle ellipsis, there are two cases:
690
+ * 1. the current line is the last line
691
+ * 2. wrap is NONE
692
+ * @param {Number} currentHeightPx
693
+ * @returns {Boolean}
694
+ */
695
+ _shouldHandleEllipsis(currentHeightPx: number): boolean {
696
+ const fontSize = +this.fontSize(),
697
+ lineHeightPx = this.lineHeight() * fontSize,
698
+ height = this.attrs.height,
699
+ fixedHeight = height !== AUTO && height !== undefined,
700
+ padding = this.padding(),
701
+ maxHeightPx = height - padding * 2,
702
+ wrap = this.wrap(),
703
+ shouldWrap = wrap !== NONE;
704
+
705
+ return (
706
+ !shouldWrap ||
707
+ (fixedHeight && currentHeightPx + lineHeightPx > maxHeightPx)
708
+ );
709
+ }
710
+
711
+ _tryToAddEllipsisToLastLine(): void {
712
+ const width = this.attrs.width,
713
+ fixedWidth = width !== AUTO && width !== undefined,
714
+ padding = this.padding(),
715
+ maxWidth = width - padding * 2,
716
+ shouldAddEllipsis = this.ellipsis();
717
+
718
+ const lastLine = this.textArr[this.textArr.length - 1];
719
+ if (!lastLine || !shouldAddEllipsis) {
720
+ return;
721
+ }
722
+
723
+ if (fixedWidth) {
724
+ const haveSpace = this._getTextWidth(lastLine.text + ELLIPSIS) < maxWidth;
725
+ if (!haveSpace) {
726
+ lastLine.text = lastLine.text.slice(0, lastLine.text.length - 3);
727
+ }
728
+ }
729
+
730
+ this.textArr.splice(this.textArr.length - 1, 1);
731
+ this._addTextLine(lastLine.text + ELLIPSIS);
732
+ }
733
+
734
+ // for text we can't disable stroke scaling
735
+ // if we do, the result will be unexpected
736
+ getStrokeScaleEnabled() {
737
+ return true;
738
+ }
739
+
740
+ _useBufferCanvas() {
741
+ const hasLine =
742
+ this.textDecoration().indexOf('underline') !== -1 ||
743
+ this.textDecoration().indexOf('line-through') !== -1;
744
+ const hasShadow = this.hasShadow();
745
+ if (hasLine && hasShadow) {
746
+ return true;
747
+ }
748
+ return super._useBufferCanvas();
749
+ }
750
+
751
+ direction: GetSet<string, this>;
752
+ fontFamily: GetSet<string, this>;
753
+ fontSize: GetSet<number, this>;
754
+ fontStyle: GetSet<string, this>;
755
+ fontVariant: GetSet<string, this>;
756
+ align: GetSet<string, this>;
757
+ letterSpacing: GetSet<number, this>;
758
+ verticalAlign: GetSet<string, this>;
759
+ padding: GetSet<number, this>;
760
+ lineHeight: GetSet<number, this>;
761
+ textDecoration: GetSet<string, this>;
762
+ text: GetSet<string, this>;
763
+ wrap: GetSet<string, this>;
764
+ ellipsis: GetSet<boolean, this>;
765
+ charRenderFunc: GetSet<null | ((props: CharRenderProps) => void), this>;
766
+ }
767
+
768
+ Text.prototype._fillFunc = _fillFunc;
769
+ Text.prototype._strokeFunc = _strokeFunc;
770
+ Text.prototype.className = TEXT_UPPER;
771
+ Text.prototype._attrsAffectingSize = [
772
+ 'text',
773
+ 'fontSize',
774
+ 'padding',
775
+ 'wrap',
776
+ 'lineHeight',
777
+ 'letterSpacing',
778
+ ];
779
+ _registerNode(Text);
780
+
781
+ /**
782
+ * get/set width of text area, which includes padding.
783
+ * @name Konva.Text#width
784
+ * @method
785
+ * @param {Number} width
786
+ * @returns {Number}
787
+ * @example
788
+ * // get width
789
+ * var width = text.width();
790
+ *
791
+ * // set width
792
+ * text.width(20);
793
+ *
794
+ * // set to auto
795
+ * text.width('auto');
796
+ * text.width() // will return calculated width, and not "auto"
797
+ */
798
+ Factory.overWriteSetter(Text, 'width', getNumberOrAutoValidator());
799
+
800
+ /**
801
+ * get/set the height of the text area, which takes into account multi-line text, line heights, and padding.
802
+ * @name Konva.Text#height
803
+ * @method
804
+ * @param {Number} height
805
+ * @returns {Number}
806
+ * @example
807
+ * // get height
808
+ * var height = text.height();
809
+ *
810
+ * // set height
811
+ * text.height(20);
812
+ *
813
+ * // set to auto
814
+ * text.height('auto');
815
+ * text.height() // will return calculated height, and not "auto"
816
+ */
817
+
818
+ Factory.overWriteSetter(Text, 'height', getNumberOrAutoValidator());
819
+
820
+ /**
821
+ * get/set direction
822
+ * @name Konva.Text#direction
823
+ * @method
824
+ * @param {String} direction
825
+ * @returns {String}
826
+ * @example
827
+ * // get direction
828
+ * var direction = text.direction();
829
+ *
830
+ * // set direction
831
+ * text.direction('rtl');
832
+ */
833
+ Factory.addGetterSetter(Text, 'direction', INHERIT);
834
+
835
+ /**
836
+ * get/set font family
837
+ * @name Konva.Text#fontFamily
838
+ * @method
839
+ * @param {String} fontFamily
840
+ * @returns {String}
841
+ * @example
842
+ * // get font family
843
+ * var fontFamily = text.fontFamily();
844
+ *
845
+ * // set font family
846
+ * text.fontFamily('Arial');
847
+ */
848
+ Factory.addGetterSetter(Text, 'fontFamily', 'Arial');
849
+
850
+ /**
851
+ * get/set font size in pixels
852
+ * @name Konva.Text#fontSize
853
+ * @method
854
+ * @param {Number} fontSize
855
+ * @returns {Number}
856
+ * @example
857
+ * // get font size
858
+ * var fontSize = text.fontSize();
859
+ *
860
+ * // set font size to 22px
861
+ * text.fontSize(22);
862
+ */
863
+ Factory.addGetterSetter(Text, 'fontSize', 12, getNumberValidator());
864
+
865
+ /**
866
+ * get/set font style. Can be 'normal', 'italic', or 'bold', '500' or even 'italic bold'. 'normal' is the default.
867
+ * @name Konva.Text#fontStyle
868
+ * @method
869
+ * @param {String} fontStyle
870
+ * @returns {String}
871
+ * @example
872
+ * // get font style
873
+ * var fontStyle = text.fontStyle();
874
+ *
875
+ * // set font style
876
+ * text.fontStyle('bold');
877
+ */
878
+
879
+ Factory.addGetterSetter(Text, 'fontStyle', NORMAL);
880
+
881
+ /**
882
+ * get/set font variant. Can be 'normal' or 'small-caps'. 'normal' is the default.
883
+ * @name Konva.Text#fontVariant
884
+ * @method
885
+ * @param {String} fontVariant
886
+ * @returns {String}
887
+ * @example
888
+ * // get font variant
889
+ * var fontVariant = text.fontVariant();
890
+ *
891
+ * // set font variant
892
+ * text.fontVariant('small-caps');
893
+ */
894
+
895
+ Factory.addGetterSetter(Text, 'fontVariant', NORMAL);
896
+
897
+ /**
898
+ * get/set padding
899
+ * @name Konva.Text#padding
900
+ * @method
901
+ * @param {Number} padding
902
+ * @returns {Number}
903
+ * @example
904
+ * // get padding
905
+ * var padding = text.padding();
906
+ *
907
+ * // set padding to 10 pixels
908
+ * text.padding(10);
909
+ */
910
+
911
+ Factory.addGetterSetter(Text, 'padding', 0, getNumberValidator());
912
+
913
+ /**
914
+ * get/set horizontal align of text. Can be 'left', 'center', 'right' or 'justify'
915
+ * @name Konva.Text#align
916
+ * @method
917
+ * @param {String} align
918
+ * @returns {String}
919
+ * @example
920
+ * // get text align
921
+ * var align = text.align();
922
+ *
923
+ * // center text
924
+ * text.align('center');
925
+ *
926
+ * // align text to right
927
+ * text.align('right');
928
+ *
929
+ * // justify text
930
+ */
931
+
932
+ Factory.addGetterSetter(Text, 'align', LEFT);
933
+
934
+ /**
935
+ * get/set vertical align of text. Can be 'top', 'middle', 'bottom'.
936
+ * @name Konva.Text#verticalAlign
937
+ * @method
938
+ * @param {String} verticalAlign
939
+ * @returns {String}
940
+ * @example
941
+ * // get text vertical align
942
+ * var verticalAlign = text.verticalAlign();
943
+ *
944
+ * // center text
945
+ * text.verticalAlign('middle');
946
+ */
947
+
948
+ Factory.addGetterSetter(Text, 'verticalAlign', TOP);
949
+
950
+ /**
951
+ * get/set line height. The default is 1.
952
+ * @name Konva.Text#lineHeight
953
+ * @method
954
+ * @param {Number} lineHeight
955
+ * @returns {Number}
956
+ * @example
957
+ * // get line height
958
+ * var lineHeight = text.lineHeight();
959
+ *
960
+ * // set the line height
961
+ * text.lineHeight(2);
962
+ */
963
+
964
+ Factory.addGetterSetter(Text, 'lineHeight', 1, getNumberValidator());
965
+
966
+ /**
967
+ * get/set wrap. Can be "word", "char", or "none". Default is "word".
968
+ * In "word" wrapping any word still can be wrapped if it can't be placed in the required width
969
+ * without breaks.
970
+ * @name Konva.Text#wrap
971
+ * @method
972
+ * @param {String} wrap
973
+ * @returns {String}
974
+ * @example
975
+ * // get wrap
976
+ * var wrap = text.wrap();
977
+ *
978
+ * // set wrap
979
+ * text.wrap('word');
980
+ */
981
+
982
+ Factory.addGetterSetter(Text, 'wrap', WORD);
983
+
984
+ /**
985
+ * get/set ellipsis. Can be true or false. Default is false. If ellipses is true,
986
+ * Konva will add "..." at the end of the text if it doesn't have enough space to write characters.
987
+ * That is possible only when you limit both width and height of the text
988
+ * @name Konva.Text#ellipsis
989
+ * @method
990
+ * @param {Boolean} ellipsis
991
+ * @returns {Boolean}
992
+ * @example
993
+ * // get ellipsis param, returns true or false
994
+ * var ellipsis = text.ellipsis();
995
+ *
996
+ * // set ellipsis
997
+ * text.ellipsis(true);
998
+ */
999
+
1000
+ Factory.addGetterSetter(Text, 'ellipsis', false, getBooleanValidator());
1001
+
1002
+ /**
1003
+ * set letter spacing property. Default value is 0.
1004
+ * @name Konva.Text#letterSpacing
1005
+ * @method
1006
+ * @param {Number} letterSpacing
1007
+ */
1008
+
1009
+ Factory.addGetterSetter(Text, 'letterSpacing', 0, getNumberValidator());
1010
+
1011
+ /**
1012
+ * get/set text
1013
+ * @name Konva.Text#text
1014
+ * @method
1015
+ * @param {String} text
1016
+ * @returns {String}
1017
+ * @example
1018
+ * // get text
1019
+ * var text = text.text();
1020
+ *
1021
+ * // set text
1022
+ * text.text('Hello world!');
1023
+ */
1024
+
1025
+ Factory.addGetterSetter(Text, 'text', '', getStringValidator());
1026
+
1027
+ /**
1028
+ * get/set text decoration of a text. Possible values are 'underline', 'line-through' or combination of these values separated by space
1029
+ * @name Konva.Text#textDecoration
1030
+ * @method
1031
+ * @param {String} textDecoration
1032
+ * @returns {String}
1033
+ * @example
1034
+ * // get text decoration
1035
+ * var textDecoration = text.textDecoration();
1036
+ *
1037
+ * // underline text
1038
+ * text.textDecoration('underline');
1039
+ *
1040
+ * // strike text
1041
+ * text.textDecoration('line-through');
1042
+ *
1043
+ * // underline and strike text
1044
+ * text.textDecoration('underline line-through');
1045
+ */
1046
+
1047
+ Factory.addGetterSetter(Text, 'textDecoration', '');
1048
+
1049
+ /**
1050
+ * get/set per-character render hook. The callback is invoked for each grapheme before drawing.
1051
+ * It can mutate the provided context (e.g. translate, rotate, change styles) and should return void.
1052
+ * Note: per-character rendering may disable native kerning/ligatures.
1053
+ * @name Konva.Text#charRenderFunc
1054
+ * @method
1055
+ * @param {Function} charRenderFunc
1056
+ * @returns {Function}
1057
+ * @example
1058
+ * // apply small x-translation to every second character
1059
+ * text.charRenderFunc(function(props) {
1060
+ * if (props.index % 2 === 1) {
1061
+ * props.context.translate(2, 0);
1062
+ * }
1063
+ * });
1064
+ */
1065
+ Factory.addGetterSetter(Text, 'charRenderFunc', undefined);