@nasser-sw/fabric 7.0.1-beta1 → 7.0.1-beta10

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 (181) hide show
  1. package/0 +0 -0
  2. package/debug/{konva → konva-master}/CHANGELOG.md +2 -1
  3. package/debug/{konva → konva-master}/README.md +7 -3
  4. package/debug/{konva → konva-master}/package.json +1 -1
  5. package/debug/{konva → konva-master}/release.sh +1 -4
  6. package/debug/{konva → konva-master}/src/Canvas.ts +37 -0
  7. package/debug/{konva → konva-master}/src/shapes/Text.ts +2 -2
  8. package/dist/index.js +1853 -288
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.min.js +1 -1
  11. package/dist/index.min.js.map +1 -1
  12. package/dist/index.min.mjs +1 -1
  13. package/dist/index.min.mjs.map +1 -1
  14. package/dist/index.mjs +1853 -288
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/index.node.cjs +1853 -288
  17. package/dist/index.node.cjs.map +1 -1
  18. package/dist/index.node.mjs +1853 -288
  19. package/dist/index.node.mjs.map +1 -1
  20. package/dist/package.json.min.mjs +1 -1
  21. package/dist/package.json.mjs +1 -1
  22. package/dist/src/shapes/Line.d.ts +33 -86
  23. package/dist/src/shapes/Line.d.ts.map +1 -1
  24. package/dist/src/shapes/Line.min.mjs +1 -1
  25. package/dist/src/shapes/Line.min.mjs.map +1 -1
  26. package/dist/src/shapes/Line.mjs +405 -159
  27. package/dist/src/shapes/Line.mjs.map +1 -1
  28. package/dist/src/shapes/Polyline.d.ts +7 -0
  29. package/dist/src/shapes/Polyline.d.ts.map +1 -1
  30. package/dist/src/shapes/Polyline.min.mjs +1 -1
  31. package/dist/src/shapes/Polyline.min.mjs.map +1 -1
  32. package/dist/src/shapes/Polyline.mjs +48 -16
  33. package/dist/src/shapes/Polyline.mjs.map +1 -1
  34. package/dist/src/shapes/Text/Text.d.ts +19 -0
  35. package/dist/src/shapes/Text/Text.d.ts.map +1 -1
  36. package/dist/src/shapes/Text/Text.min.mjs +1 -1
  37. package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
  38. package/dist/src/shapes/Text/Text.mjs +302 -16
  39. package/dist/src/shapes/Text/Text.mjs.map +1 -1
  40. package/dist/src/shapes/Textbox.d.ts +43 -1
  41. package/dist/src/shapes/Textbox.d.ts.map +1 -1
  42. package/dist/src/shapes/Textbox.min.mjs +1 -1
  43. package/dist/src/shapes/Textbox.min.mjs.map +1 -1
  44. package/dist/src/shapes/Textbox.mjs +521 -67
  45. package/dist/src/shapes/Textbox.mjs.map +1 -1
  46. package/dist/src/shapes/Triangle.d.ts +27 -2
  47. package/dist/src/shapes/Triangle.d.ts.map +1 -1
  48. package/dist/src/shapes/Triangle.min.mjs +1 -1
  49. package/dist/src/shapes/Triangle.min.mjs.map +1 -1
  50. package/dist/src/shapes/Triangle.mjs +72 -12
  51. package/dist/src/shapes/Triangle.mjs.map +1 -1
  52. package/dist/src/text/examples/arabicTextExample.d.ts +60 -0
  53. package/dist/src/text/examples/arabicTextExample.d.ts.map +1 -0
  54. package/dist/src/text/measure.d.ts +9 -0
  55. package/dist/src/text/measure.d.ts.map +1 -1
  56. package/dist/src/text/measure.min.mjs +1 -1
  57. package/dist/src/text/measure.min.mjs.map +1 -1
  58. package/dist/src/text/measure.mjs +175 -4
  59. package/dist/src/text/measure.mjs.map +1 -1
  60. package/dist/src/text/overlayEditor.d.ts.map +1 -1
  61. package/dist/src/text/overlayEditor.min.mjs +1 -1
  62. package/dist/src/text/overlayEditor.min.mjs.map +1 -1
  63. package/dist/src/text/overlayEditor.mjs +155 -9
  64. package/dist/src/text/overlayEditor.mjs.map +1 -1
  65. package/dist/src/text/scriptUtils.d.ts +142 -0
  66. package/dist/src/text/scriptUtils.d.ts.map +1 -0
  67. package/dist/src/text/scriptUtils.min.mjs +2 -0
  68. package/dist/src/text/scriptUtils.min.mjs.map +1 -0
  69. package/dist/src/text/scriptUtils.mjs +212 -0
  70. package/dist/src/text/scriptUtils.mjs.map +1 -0
  71. package/dist/src/util/misc/cornerRadius.d.ts +70 -0
  72. package/dist/src/util/misc/cornerRadius.d.ts.map +1 -0
  73. package/dist/src/util/misc/cornerRadius.min.mjs +2 -0
  74. package/dist/src/util/misc/cornerRadius.min.mjs.map +1 -0
  75. package/dist/src/util/misc/cornerRadius.mjs +181 -0
  76. package/dist/src/util/misc/cornerRadius.mjs.map +1 -0
  77. package/dist-extensions/src/shapes/CustomLine.d.ts +10 -0
  78. package/dist-extensions/src/shapes/CustomLine.d.ts.map +1 -0
  79. package/dist-extensions/src/shapes/Line.d.ts +33 -86
  80. package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
  81. package/dist-extensions/src/shapes/Polyline.d.ts +7 -0
  82. package/dist-extensions/src/shapes/Polyline.d.ts.map +1 -1
  83. package/dist-extensions/src/shapes/Text/Text.d.ts +19 -0
  84. package/dist-extensions/src/shapes/Text/Text.d.ts.map +1 -1
  85. package/dist-extensions/src/shapes/Textbox.d.ts +43 -1
  86. package/dist-extensions/src/shapes/Textbox.d.ts.map +1 -1
  87. package/dist-extensions/src/shapes/Triangle.d.ts +27 -2
  88. package/dist-extensions/src/shapes/Triangle.d.ts.map +1 -1
  89. package/dist-extensions/src/text/measure.d.ts +9 -0
  90. package/dist-extensions/src/text/measure.d.ts.map +1 -1
  91. package/dist-extensions/src/text/overlayEditor.d.ts.map +1 -1
  92. package/dist-extensions/src/text/scriptUtils.d.ts +142 -0
  93. package/dist-extensions/src/text/scriptUtils.d.ts.map +1 -0
  94. package/dist-extensions/src/util/misc/cornerRadius.d.ts +70 -0
  95. package/dist-extensions/src/util/misc/cornerRadius.d.ts.map +1 -0
  96. package/fabric-test-editor.html +3552 -0
  97. package/fabric-test2.html +647 -0
  98. package/fabric.ts +182 -182
  99. package/fonts/STV Bold.ttf +0 -0
  100. package/fonts/STV Light.ttf +0 -0
  101. package/fonts/STV Regular.ttf +0 -0
  102. package/package.json +164 -164
  103. package/src/shapes/Line.ts +484 -157
  104. package/src/shapes/Polyline.ts +70 -29
  105. package/src/shapes/Text/Text.ts +317 -19
  106. package/src/shapes/Textbox.ts +544 -12
  107. package/src/shapes/Triangle.spec.ts +76 -0
  108. package/src/shapes/Triangle.ts +85 -15
  109. package/src/text/measure.ts +200 -50
  110. package/src/text/overlayEditor.ts +164 -12
  111. package/src/util/misc/cornerRadius.spec.ts +141 -0
  112. package/src/util/misc/cornerRadius.ts +269 -0
  113. /package/debug/{konva → konva-master}/LICENSE +0 -0
  114. /package/debug/{konva → konva-master}/gulpfile.mjs +0 -0
  115. /package/debug/{konva → konva-master}/resources/doc-includes/ContainerParams.txt +0 -0
  116. /package/debug/{konva → konva-master}/resources/doc-includes/NodeParams.txt +0 -0
  117. /package/debug/{konva → konva-master}/resources/doc-includes/ShapeParams.txt +0 -0
  118. /package/debug/{konva → konva-master}/resources/jsdoc.conf.json +0 -0
  119. /package/debug/{konva → konva-master}/rollup.config.mjs +0 -0
  120. /package/debug/{konva → konva-master}/src/Animation.ts +0 -0
  121. /package/debug/{konva → konva-master}/src/BezierFunctions.ts +0 -0
  122. /package/debug/{konva → konva-master}/src/Container.ts +0 -0
  123. /package/debug/{konva → konva-master}/src/Context.ts +0 -0
  124. /package/debug/{konva → konva-master}/src/Core.ts +0 -0
  125. /package/debug/{konva → konva-master}/src/DragAndDrop.ts +0 -0
  126. /package/debug/{konva → konva-master}/src/Factory.ts +0 -0
  127. /package/debug/{konva → konva-master}/src/FastLayer.ts +0 -0
  128. /package/debug/{konva → konva-master}/src/Global.ts +0 -0
  129. /package/debug/{konva → konva-master}/src/Group.ts +0 -0
  130. /package/debug/{konva → konva-master}/src/Layer.ts +0 -0
  131. /package/debug/{konva → konva-master}/src/Node.ts +0 -0
  132. /package/debug/{konva → konva-master}/src/PointerEvents.ts +0 -0
  133. /package/debug/{konva → konva-master}/src/Shape.ts +0 -0
  134. /package/debug/{konva → konva-master}/src/Stage.ts +0 -0
  135. /package/debug/{konva → konva-master}/src/Tween.ts +0 -0
  136. /package/debug/{konva → konva-master}/src/Util.ts +0 -0
  137. /package/debug/{konva → konva-master}/src/Validators.ts +0 -0
  138. /package/debug/{konva → konva-master}/src/_CoreInternals.ts +0 -0
  139. /package/debug/{konva → konva-master}/src/_FullInternals.ts +0 -0
  140. /package/debug/{konva → konva-master}/src/canvas-backend.ts +0 -0
  141. /package/debug/{konva → konva-master}/src/filters/Blur.ts +0 -0
  142. /package/debug/{konva → konva-master}/src/filters/Brighten.ts +0 -0
  143. /package/debug/{konva → konva-master}/src/filters/Brightness.ts +0 -0
  144. /package/debug/{konva → konva-master}/src/filters/Contrast.ts +0 -0
  145. /package/debug/{konva → konva-master}/src/filters/Emboss.ts +0 -0
  146. /package/debug/{konva → konva-master}/src/filters/Enhance.ts +0 -0
  147. /package/debug/{konva → konva-master}/src/filters/Grayscale.ts +0 -0
  148. /package/debug/{konva → konva-master}/src/filters/HSL.ts +0 -0
  149. /package/debug/{konva → konva-master}/src/filters/HSV.ts +0 -0
  150. /package/debug/{konva → konva-master}/src/filters/Invert.ts +0 -0
  151. /package/debug/{konva → konva-master}/src/filters/Kaleidoscope.ts +0 -0
  152. /package/debug/{konva → konva-master}/src/filters/Mask.ts +0 -0
  153. /package/debug/{konva → konva-master}/src/filters/Noise.ts +0 -0
  154. /package/debug/{konva → konva-master}/src/filters/Pixelate.ts +0 -0
  155. /package/debug/{konva → konva-master}/src/filters/Posterize.ts +0 -0
  156. /package/debug/{konva → konva-master}/src/filters/RGB.ts +0 -0
  157. /package/debug/{konva → konva-master}/src/filters/RGBA.ts +0 -0
  158. /package/debug/{konva → konva-master}/src/filters/Sepia.ts +0 -0
  159. /package/debug/{konva → konva-master}/src/filters/Solarize.ts +0 -0
  160. /package/debug/{konva → konva-master}/src/filters/Threshold.ts +0 -0
  161. /package/debug/{konva → konva-master}/src/index.ts +0 -0
  162. /package/debug/{konva → konva-master}/src/shapes/Arc.ts +0 -0
  163. /package/debug/{konva → konva-master}/src/shapes/Arrow.ts +0 -0
  164. /package/debug/{konva → konva-master}/src/shapes/Circle.ts +0 -0
  165. /package/debug/{konva → konva-master}/src/shapes/Ellipse.ts +0 -0
  166. /package/debug/{konva → konva-master}/src/shapes/Image.ts +0 -0
  167. /package/debug/{konva → konva-master}/src/shapes/Label.ts +0 -0
  168. /package/debug/{konva → konva-master}/src/shapes/Line.ts +0 -0
  169. /package/debug/{konva → konva-master}/src/shapes/Path.ts +0 -0
  170. /package/debug/{konva → konva-master}/src/shapes/Rect.ts +0 -0
  171. /package/debug/{konva → konva-master}/src/shapes/RegularPolygon.ts +0 -0
  172. /package/debug/{konva → konva-master}/src/shapes/Ring.ts +0 -0
  173. /package/debug/{konva → konva-master}/src/shapes/Sprite.ts +0 -0
  174. /package/debug/{konva → konva-master}/src/shapes/Star.ts +0 -0
  175. /package/debug/{konva → konva-master}/src/shapes/TextPath.ts +0 -0
  176. /package/debug/{konva → konva-master}/src/shapes/Transformer.ts +0 -0
  177. /package/debug/{konva → konva-master}/src/shapes/Wedge.ts +0 -0
  178. /package/debug/{konva → konva-master}/src/skia-backend.ts +0 -0
  179. /package/debug/{konva → konva-master}/src/types.ts +0 -0
  180. /package/debug/{konva → konva-master}/tsconfig.json +0 -0
  181. /package/debug/{konva → konva-master}/tsconfig.test.json +0 -0
@@ -8,6 +8,7 @@ import { classRegistry } from '../../ClassRegistry.mjs';
8
8
  import { graphemeSplit } from '../../util/lang_string.mjs';
9
9
  import { createCanvasElementFor } from '../../util/misc/dom.mjs';
10
10
  import { layoutText } from '../../text/layout.mjs';
11
+ import { segmentGraphemes } from '../../text/unicode.mjs';
11
12
  import { hasStyleChanged, stylesToArray, stylesFromArray } from '../../util/misc/textStyles.mjs';
12
13
  import { getPathSegmentsInfo, getPointOnPath } from '../../util/path/index.mjs';
13
14
  import '../Object/FabricObject.mjs';
@@ -149,6 +150,15 @@ class FabricText extends StyledText {
149
150
  * Does not return dimensions.
150
151
  */
151
152
  initDimensions() {
153
+ // Check if font is ready for accurate measurements
154
+ // Only block initialization if it's a critical font loading situation
155
+ const fontReady = this._isFontReady();
156
+ if (!fontReady && !this.initialized) {
157
+ // Only schedule font loading on first initialization
158
+ this._scheduleInitAfterFontLoad();
159
+ // Continue with fallback measurements for now
160
+ }
161
+
152
162
  // Use advanced layout if enabled
153
163
  if (this.enableAdvancedLayout && !this.path) {
154
164
  return this.initDimensionsAdvanced();
@@ -165,7 +175,21 @@ class FabricText extends StyledText {
165
175
  }
166
176
  if (this.textAlign.includes(JUSTIFY)) {
167
177
  // once text is measured we need to make space fatter to make justified text.
168
- this.enlargeSpaces();
178
+ // Ensure __charBounds exists before calling enlargeSpaces
179
+ if (this.__charBounds && this.__charBounds.length > 0) {
180
+ this.enlargeSpaces();
181
+ } else {
182
+ console.warn('⚠️ __charBounds not ready for justify alignment, deferring enlargeSpaces');
183
+ // Defer the justify calculation until the next frame
184
+ setTimeout(() => {
185
+ if (this.__charBounds && this.__charBounds.length > 0 && this.enlargeSpaces) {
186
+ var _this$canvas;
187
+ console.log('🔧 Applying deferred justify alignment');
188
+ this.enlargeSpaces();
189
+ (_this$canvas = this.canvas) === null || _this$canvas === void 0 || _this$canvas.requestRenderAll();
190
+ }
191
+ }, 0);
192
+ }
169
193
  }
170
194
  }
171
195
 
@@ -174,8 +198,9 @@ class FabricText extends StyledText {
174
198
  */
175
199
  enlargeSpaces() {
176
200
  let diffSpace, currentLineWidth, numberOfSpaces, accumulatedSpace, line, charBound, spaces;
201
+ const isRtl = this.direction === 'rtl';
177
202
  for (let i = 0, len = this._textLines.length; i < len; i++) {
178
- if (this.textAlign !== JUSTIFY && (i === len - 1 || this.isEndOfWrapping(i))) {
203
+ if (!this.textAlign.includes('justify') && (i === len - 1 || this.isEndOfWrapping(i))) {
179
204
  continue;
180
205
  }
181
206
  accumulatedSpace = 0;
@@ -184,15 +209,47 @@ class FabricText extends StyledText {
184
209
  if (currentLineWidth < this.width && (spaces = this.textLines[i].match(this._reSpacesAndTabs))) {
185
210
  numberOfSpaces = spaces.length;
186
211
  diffSpace = (this.width - currentLineWidth) / numberOfSpaces;
187
- for (let j = 0; j <= line.length; j++) {
188
- charBound = this.__charBounds[i][j];
189
- if (this._reSpaceAndTab.test(line[j])) {
190
- charBound.width += diffSpace;
191
- charBound.kernedWidth += diffSpace;
192
- charBound.left += accumulatedSpace;
193
- accumulatedSpace += diffSpace;
194
- } else {
195
- charBound.left += accumulatedSpace;
212
+ console.log(`🔧 EnlargeSpaces Line ${i}:`);
213
+ console.log(` Current width: ${currentLineWidth}, Target: ${this.width}`);
214
+ console.log(` Spaces: ${numberOfSpaces}, diffSpace: ${diffSpace.toFixed(2)}`);
215
+ if (isRtl) {
216
+ for (let j = 0; j < line.length; j++) {
217
+ if (this._reSpaceAndTab.test(line[j])) ;
218
+ }
219
+
220
+ // For RTL, we need to work backwards through the visual positions
221
+ // but still update logical positions correctly
222
+ let spaceCount = 0;
223
+ for (let j = 0; j <= line.length; j++) {
224
+ charBound = this.__charBounds[i][j];
225
+ if (charBound) {
226
+ if (this._reSpaceAndTab.test(line[j])) {
227
+ charBound.width += diffSpace;
228
+ charBound.kernedWidth += diffSpace;
229
+ spaceCount++;
230
+ }
231
+
232
+ // For RTL, shift all characters to the right by the total expansion
233
+ // minus the expansion that comes after this character
234
+ const remainingSpaces = numberOfSpaces - spaceCount;
235
+ const shiftAmount = remainingSpaces * diffSpace;
236
+ charBound.left += shiftAmount;
237
+ }
238
+ }
239
+ } else {
240
+ // LTR processing (original logic)
241
+ for (let j = 0; j <= line.length; j++) {
242
+ charBound = this.__charBounds[i][j];
243
+ if (charBound) {
244
+ if (this._reSpaceAndTab.test(line[j])) {
245
+ charBound.width += diffSpace;
246
+ charBound.kernedWidth += diffSpace;
247
+ charBound.left += accumulatedSpace;
248
+ accumulatedSpace += diffSpace;
249
+ } else {
250
+ charBound.left += accumulatedSpace;
251
+ }
252
+ }
196
253
  }
197
254
  }
198
255
  }
@@ -270,6 +327,18 @@ class FabricText extends StyledText {
270
327
 
271
328
  // Convert layout to legacy format for compatibility
272
329
  this._convertLayoutToLegacyFormat(layout);
330
+
331
+ // Ensure justify alignment is properly applied for compatibility with legacy rendering
332
+ if (this.textAlign.includes(JUSTIFY)) {
333
+ // Force enlarge spaces after advanced layout calculation
334
+ setTimeout(() => {
335
+ if (this.enlargeSpaces) {
336
+ var _this$canvas2;
337
+ this.enlargeSpaces();
338
+ (_this$canvas2 = this.canvas) === null || _this$canvas2 === void 0 || _this$canvas2.renderAll();
339
+ }
340
+ }, 0);
341
+ }
273
342
  this.dirty = true;
274
343
  }
275
344
 
@@ -850,7 +919,15 @@ class FabricText extends StyledText {
850
919
  if (currentDirection !== this.direction) {
851
920
  ctx.canvas.setAttribute('dir', isLtr ? 'ltr' : 'rtl');
852
921
  ctx.direction = isLtr ? 'ltr' : 'rtl';
853
- ctx.textAlign = isLtr ? LEFT : RIGHT;
922
+
923
+ // For justify alignments, we need to set the correct canvas text alignment
924
+ // This is crucial for RTL text to render in the correct order
925
+ if (isJustify) {
926
+ // Justify uses LEFT alignment as a base, letting the character positioning handle justification
927
+ ctx.textAlign = LEFT;
928
+ } else {
929
+ ctx.textAlign = isLtr ? LEFT : RIGHT;
930
+ }
854
931
  }
855
932
  top -= lineHeight * this._fontSizeFraction / this.lineHeight;
856
933
  if (shortCut) {
@@ -1086,9 +1163,21 @@ class FabricText extends StyledText {
1086
1163
  direction = this.direction,
1087
1164
  isEndOfWrapping = this.isEndOfWrapping(lineIndex);
1088
1165
  let leftOffset = 0;
1089
- if (textAlign === JUSTIFY || textAlign === JUSTIFY_CENTER && !isEndOfWrapping || textAlign === JUSTIFY_RIGHT && !isEndOfWrapping || textAlign === JUSTIFY_LEFT && !isEndOfWrapping) {
1090
- return 0;
1166
+
1167
+ // Handle justify alignments (excluding last lines and wrapped line ends)
1168
+ const isJustifyLine = textAlign === JUSTIFY || textAlign === JUSTIFY_CENTER && !isEndOfWrapping || textAlign === JUSTIFY_RIGHT && !isEndOfWrapping || textAlign === JUSTIFY_LEFT && !isEndOfWrapping;
1169
+ if (isJustifyLine) {
1170
+ // Justify lines should start at the left edge for LTR and right edge for RTL
1171
+ // The space distribution is handled by enlargeSpaces()
1172
+ if (direction === 'rtl') {
1173
+ // For RTL justify, we need to account for the line being right-aligned
1174
+ return 0;
1175
+ } else {
1176
+ return 0;
1177
+ }
1091
1178
  }
1179
+
1180
+ // Handle non-justify alignments
1092
1181
  if (textAlign === CENTER) {
1093
1182
  leftOffset = lineDiff / 2;
1094
1183
  }
@@ -1101,6 +1190,8 @@ class FabricText extends StyledText {
1101
1190
  if (textAlign === JUSTIFY_RIGHT) {
1102
1191
  leftOffset = lineDiff;
1103
1192
  }
1193
+
1194
+ // Apply RTL adjustments for non-justify alignments
1104
1195
  if (direction === 'rtl') {
1105
1196
  if (textAlign === RIGHT || textAlign === JUSTIFY || textAlign === JUSTIFY_RIGHT) {
1106
1197
  leftOffset = 0;
@@ -1259,7 +1350,19 @@ class FabricText extends StyledText {
1259
1350
  fontSize = this.fontSize
1260
1351
  } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1261
1352
  let forMeasuring = arguments.length > 1 ? arguments[1] : undefined;
1262
- const parsedFontFamily = fontFamily.includes("'") || fontFamily.includes('"') || fontFamily.includes(',') || FabricText.genericFonts.includes(fontFamily.toLowerCase()) ? fontFamily : `"${fontFamily}"`;
1353
+ let parsedFontFamily = fontFamily.includes("'") || fontFamily.includes('"') || fontFamily.includes(',') || FabricText.genericFonts.includes(fontFamily.toLowerCase()) ? fontFamily : `"${fontFamily}"`;
1354
+
1355
+ // For fonts like STV that don't support English/Latin characters,
1356
+ // add fallback fonts for consistent rendering of unsupported characters
1357
+ // Only add fallbacks during actual rendering, not for measurements
1358
+ if (!forMeasuring &&
1359
+ // Only during rendering, not measuring
1360
+ !fontFamily.includes(',') && (
1361
+ // Don't add fallbacks if already has them
1362
+ fontFamily.toLowerCase().includes('stv') || fontFamily.toLowerCase().includes('arabic') || fontFamily.toLowerCase().includes('naskh') || fontFamily.toLowerCase().includes('kufi'))) {
1363
+ // Add fallback fonts for unsupported characters (spaces, punctuation, etc.)
1364
+ parsedFontFamily = `${parsedFontFamily}, "Arial Unicode MS", Arial, sans-serif`;
1365
+ }
1263
1366
  return [fontStyle, fontWeight, `${forMeasuring ? this.CACHE_FONT_SIZE : fontSize}px`, parsedFontFamily].join(' ');
1264
1367
  }
1265
1368
 
@@ -1303,7 +1406,13 @@ class FabricText extends StyledText {
1303
1406
  newLine = ['\n'];
1304
1407
  let newText = [];
1305
1408
  for (let i = 0; i < lines.length; i++) {
1306
- newLines[i] = this.graphemeSplit(lines[i]);
1409
+ // Use BiDi-aware grapheme splitting for RTL text
1410
+ if (this.direction === 'rtl' || this._containsArabicText(lines[i])) {
1411
+ newLines[i] = segmentGraphemes(lines[i]);
1412
+ console.log(`🔤 BiDi-aware split line ${i}: "${lines[i]}" -> [${newLines[i].join(', ')}]`);
1413
+ } else {
1414
+ newLines[i] = this.graphemeSplit(lines[i]);
1415
+ }
1307
1416
  newText = newText.concat(newLines[i], newLine);
1308
1417
  }
1309
1418
  newText.pop();
@@ -1315,6 +1424,14 @@ class FabricText extends StyledText {
1315
1424
  };
1316
1425
  }
1317
1426
 
1427
+ /**
1428
+ * Check if text contains Arabic characters
1429
+ * @private
1430
+ */
1431
+ _containsArabicText(text) {
1432
+ return /[\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF]/.test(text);
1433
+ }
1434
+
1318
1435
  /**
1319
1436
  * Returns object representation of an instance
1320
1437
  * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
@@ -1437,6 +1554,88 @@ class FabricText extends StyledText {
1437
1554
 
1438
1555
  /* _FROM_SVG_END_ */
1439
1556
 
1557
+ /**
1558
+ * Check if the font is ready for accurate measurements
1559
+ * @private
1560
+ */
1561
+ _isFontReady() {
1562
+ if (typeof document === 'undefined' || !('fonts' in document)) {
1563
+ return true; // Assume ready in non-browser environments
1564
+ }
1565
+ try {
1566
+ return document.fonts.check(`${this.fontSize}px ${this.fontFamily}`);
1567
+ } catch (e) {
1568
+ return true; // Fallback to assuming ready if check fails
1569
+ }
1570
+ }
1571
+
1572
+ /**
1573
+ * Schedule re-initialization after font loads
1574
+ * @private
1575
+ */
1576
+ _scheduleInitAfterFontLoad() {
1577
+ if (typeof document === 'undefined' || !('fonts' in document)) {
1578
+ return;
1579
+ }
1580
+
1581
+ // Only schedule if not already waiting
1582
+ if (this._fontLoadScheduled) {
1583
+ return;
1584
+ }
1585
+ this._fontLoadScheduled = true;
1586
+ const fontSpec = `${this.fontSize}px ${this.fontFamily}`;
1587
+ document.fonts.load(fontSpec).then(() => {
1588
+ this._fontLoadScheduled = false;
1589
+ // Re-initialize dimensions with proper font metrics
1590
+ this.initDimensions();
1591
+
1592
+ // Extra step for justify alignment after font loading
1593
+ if (this.textAlign && this.textAlign.includes(JUSTIFY)) {
1594
+ setTimeout(() => {
1595
+ var _this$canvas3;
1596
+ if (this.enlargeSpaces) {
1597
+ this.enlargeSpaces();
1598
+ }
1599
+ (_this$canvas3 = this.canvas) === null || _this$canvas3 === void 0 || _this$canvas3.requestRenderAll();
1600
+ }, 10);
1601
+ } else {
1602
+ var _this$canvas4;
1603
+ (_this$canvas4 = this.canvas) === null || _this$canvas4 === void 0 || _this$canvas4.requestRenderAll();
1604
+ }
1605
+ }).catch(() => {
1606
+ this._fontLoadScheduled = false;
1607
+ });
1608
+ }
1609
+
1610
+ /**
1611
+ * Force complete text re-initialization (useful after JSON loading)
1612
+ */
1613
+ forceTextReinitialization() {
1614
+ console.log('🔄 Force reinitializing text object');
1615
+
1616
+ // Clear all caches
1617
+ this._clearCache();
1618
+ this.dirty = true;
1619
+
1620
+ // Force text splitting to rebuild internal structures
1621
+ this._splitText();
1622
+
1623
+ // Re-initialize dimensions
1624
+ this.initDimensions();
1625
+
1626
+ // Special handling for justify alignment
1627
+ if (this.textAlign && this.textAlign.includes(JUSTIFY)) {
1628
+ // Ensure justify is applied after dimensions are set
1629
+ setTimeout(() => {
1630
+ if (this.__charBounds && this.__charBounds.length > 0 && this.enlargeSpaces) {
1631
+ var _this$canvas5;
1632
+ this.enlargeSpaces();
1633
+ (_this$canvas5 = this.canvas) === null || _this$canvas5 === void 0 || _this$canvas5.requestRenderAll();
1634
+ }
1635
+ }, 10);
1636
+ }
1637
+ }
1638
+
1440
1639
  /**
1441
1640
  * Returns FabricText instance from an object representation
1442
1641
  * @param {Object} object plain js Object to create an instance from
@@ -1448,6 +1647,93 @@ class FabricText extends StyledText {
1448
1647
  styles: stylesFromArray(object.styles || {}, object.text)
1449
1648
  }, {
1450
1649
  extraParam: 'text'
1650
+ }).then(textObject => {
1651
+ // Ensure text object is properly initialized after JSON deserialization
1652
+ // This is critical for justify alignment and other text layout features
1653
+ textObject.initialized = true;
1654
+
1655
+ // Force reinitialization to ensure proper layout
1656
+ if (textObject._clearCache) {
1657
+ textObject._clearCache();
1658
+ }
1659
+ textObject.dirty = true;
1660
+
1661
+ // Check if we need to wait for font loading (especially for custom fonts like STV)
1662
+ const fontSpec = `${textObject.fontSize}px ${textObject.fontFamily}`;
1663
+
1664
+ // For custom fonts, ensure they're loaded before initializing dimensions
1665
+ if (typeof document !== 'undefined' && 'fonts' in document && textObject.fontFamily !== 'Arial' && textObject.fontFamily !== 'Times New Roman') {
1666
+ return document.fonts.load(fontSpec).then(() => {
1667
+ var _textObject$fontFamil;
1668
+ console.log(`🔤 Font loaded for JSON object: ${fontSpec}`);
1669
+ // Ensure initialized flag is set again (in case constructor reset it)
1670
+ textObject.initialized = true;
1671
+
1672
+ // Special handling for STV fonts which have measurement issues
1673
+ const isStvFont = (_textObject$fontFamil = textObject.fontFamily) === null || _textObject$fontFamil === void 0 ? void 0 : _textObject$fontFamil.toLowerCase().includes('stv');
1674
+ if (isStvFont) {
1675
+ console.log(`🔤 STV font detected, using enhanced reinitialization`);
1676
+
1677
+ // Clear all cached state that might interfere with browser wrapping
1678
+ textObject._browserWrapCache = null;
1679
+ textObject._lastDimensionState = null;
1680
+ textObject._browserWrapInitialized = false;
1681
+ console.log(`🔤 STV font: Cleared all cached states for fresh initialization`);
1682
+
1683
+ // Force browser wrapping flag for STV fonts
1684
+ textObject._usingBrowserWrapping = true;
1685
+ console.log(`🔤 STV font: Forcing browser wrapping flag during JSON load`);
1686
+
1687
+ // Multiple initialization attempts for STV fonts
1688
+ const reinitWithDelay = attempt => {
1689
+ if (textObject.forceTextReinitialization) {
1690
+ textObject.forceTextReinitialization();
1691
+ } else {
1692
+ textObject.initDimensions();
1693
+ }
1694
+
1695
+ // Check if width is still problematic after initialization
1696
+ if (textObject.width < 50 && attempt < 3) {
1697
+ console.log(`🔤 STV font width still ${textObject.width}px, retrying in ${100 * attempt}ms (attempt ${attempt + 1}/3)`);
1698
+ setTimeout(() => reinitWithDelay(attempt + 1), 100 * attempt);
1699
+ }
1700
+ };
1701
+ reinitWithDelay(0);
1702
+ } else {
1703
+ // Use specialized reinitialization for Textbox objects
1704
+ if (textObject.forceTextReinitialization) {
1705
+ console.log(`🔤 Using Textbox specialized reinitialization`);
1706
+ textObject.forceTextReinitialization();
1707
+ } else {
1708
+ // Reinitialize dimensions with proper font metrics
1709
+ textObject.initDimensions();
1710
+ }
1711
+ }
1712
+ return textObject;
1713
+ }).catch(() => {
1714
+ console.warn(`⚠️ Font loading failed for ${fontSpec}, proceeding with fallback`);
1715
+ // Ensure initialized flag is set again
1716
+ textObject.initialized = true;
1717
+
1718
+ // Still initialize dimensions even if font loading fails
1719
+ if (textObject.forceTextReinitialization) {
1720
+ textObject.forceTextReinitialization();
1721
+ } else {
1722
+ textObject.initDimensions();
1723
+ }
1724
+ return textObject;
1725
+ });
1726
+ } else {
1727
+ // Standard fonts - ensure initialized and use appropriate method
1728
+ textObject.initialized = true;
1729
+ if (textObject.forceTextReinitialization) {
1730
+ console.log(`🔤 Using Textbox specialized reinitialization for standard font`);
1731
+ textObject.forceTextReinitialization();
1732
+ } else {
1733
+ textObject.initDimensions();
1734
+ }
1735
+ return textObject;
1736
+ }
1451
1737
  });
1452
1738
  }
1453
1739
  }