@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
@@ -94,6 +94,97 @@ function measureGraphemeWithKerning(grapheme, previousGrapheme, options, ctx) {
94
94
  };
95
95
  }
96
96
 
97
+ /**
98
+ * Get a representative character for font metrics measurement
99
+ * Uses canvas to test which scripts the font actually supports
100
+ */
101
+ function getRepresentativeCharacter(fontFamily) {
102
+ const context = getMeasurementContext();
103
+
104
+ // Wait for font to be ready if possible
105
+ if (typeof document !== 'undefined' && 'fonts' in document) {
106
+ try {
107
+ // Check if font is ready, if not, use fallback immediately
108
+ if (!document.fonts.check(`16px ${fontFamily}`)) {
109
+ return 'M'; // Use safe fallback while font loads
110
+ }
111
+ } catch (e) {
112
+ // Font check failed, use fallback
113
+ return 'M';
114
+ }
115
+ }
116
+
117
+ // Test characters for different scripts
118
+ const testChars = [{
119
+ char: 'م',
120
+ script: 'Arabic'
121
+ },
122
+ // Arabic
123
+ {
124
+ char: 'א',
125
+ script: 'Hebrew'
126
+ },
127
+ // Hebrew
128
+ {
129
+ char: 'अ',
130
+ script: 'Devanagari'
131
+ },
132
+ // Hindi/Sanskrit
133
+ {
134
+ char: 'ا',
135
+ script: 'Urdu'
136
+ },
137
+ // Urdu
138
+ {
139
+ char: 'ک',
140
+ script: 'Persian'
141
+ },
142
+ // Persian
143
+ {
144
+ char: 'த',
145
+ script: 'Tamil'
146
+ },
147
+ // Tamil
148
+ {
149
+ char: 'ก',
150
+ script: 'Thai'
151
+ },
152
+ // Thai
153
+ {
154
+ char: 'М',
155
+ script: 'Cyrillic'
156
+ },
157
+ // Cyrillic
158
+ {
159
+ char: 'Ω',
160
+ script: 'Greek'
161
+ },
162
+ // Greek
163
+ {
164
+ char: 'M',
165
+ script: 'Latin'
166
+ } // Latin (fallback)
167
+ ];
168
+
169
+ // Set the font
170
+ context.font = `16px ${fontFamily}`;
171
+
172
+ // Test each character to see which ones render properly
173
+ // Use a more robust width check to avoid false positives
174
+ const fallbackWidth = context.measureText('M').width;
175
+ for (const test of testChars) {
176
+ const metrics = context.measureText(test.char);
177
+
178
+ // Character is valid if it has width and isn't just a fallback glyph
179
+ if (metrics.width > 0 && Math.abs(metrics.width - fallbackWidth) > 0.1) {
180
+ return test.char;
181
+ }
182
+ }
183
+
184
+ // Fallback to Latin 'M'
185
+ return 'M';
186
+ }
187
+
97
188
  /**
98
189
  * Get font metrics for layout calculations
99
190
  */
@@ -107,8 +198,9 @@ function getFontMetrics(options) {
107
198
  const context = getMeasurementContext();
108
199
  applyFontStyle(context, options);
109
200
 
110
- // Use 'M' as sample character for metrics
111
- const metrics = context.measureText('M');
201
+ // Use representative character based on font's primary script
202
+ const sample = getRepresentativeCharacter(options.fontFamily);
203
+ const metrics = context.measureText(sample);
112
204
  const fontSize = options.fontSize;
113
205
 
114
206
  // Calculate metrics with fallbacks
@@ -160,7 +252,11 @@ function getFontDeclaration(options) {
160
252
  } = options;
161
253
 
162
254
  // Normalize font family (add quotes if needed)
163
- const normalizedFamily = fontFamily.includes(' ') && !fontFamily.includes('"') && !fontFamily.includes("'") ? `"${fontFamily}"` : fontFamily;
255
+ let normalizedFamily = fontFamily.includes(' ') && !fontFamily.includes('"') && !fontFamily.includes("'") ? `"${fontFamily}"` : fontFamily;
256
+
257
+ // Note: Font fallbacks are handled in the rendering phase only
258
+ // to avoid affecting measurement calculations for text wrapping
259
+
164
260
  return `${fontStyle} ${fontWeight} ${fontSize}px ${normalizedFamily}`;
165
261
  }
166
262
 
@@ -312,5 +408,80 @@ const measurementCache = new MeasurementCache();
312
408
  const kerningCache = new KerningCache();
313
409
  const fontMetricsCache = new FontMetricsCache();
314
410
 
315
- export { MeasurementCache, fontMetricsCache, getFontMetrics, kerningCache, measureGrapheme, measureGraphemeWithKerning, measurementCache };
411
+ // Set up font loading listener to clear caches when fonts change
412
+ if (typeof document !== 'undefined' && 'fonts' in document) {
413
+ document.fonts.addEventListener('loadingdone', () => {
414
+ // Clear all caches when fonts finish loading
415
+ clearAllCaches();
416
+ });
417
+ }
418
+
419
+ /**
420
+ * Clear all measurement caches
421
+ */
422
+ function clearAllCaches() {
423
+ measurementCache.clear();
424
+ kerningCache.clear();
425
+ fontMetricsCache.clear();
426
+ }
427
+
428
+ /**
429
+ * Detect if a font lacks English glyph support
430
+ * These fonts should use browser-native measurement instead of Fabric's character-by-character measurement
431
+ */
432
+ function fontLacksEnglishGlyphs(fontFamily) {
433
+ if (typeof document === 'undefined') return false;
434
+
435
+ // Known fonts that lack English glyphs
436
+ const knownNonEnglishFonts = ['stv', 'arabic', 'naskh', 'thuluth', 'kufi', 'diwani', 'nastaliq', 'kufic', 'hijazi', 'madinah', 'makkah'];
437
+ const lowerFontFamily = fontFamily.toLowerCase();
438
+
439
+ // Check known list first
440
+ if (knownNonEnglishFonts.some(font => lowerFontFamily.includes(font))) {
441
+ return true;
442
+ }
443
+
444
+ // Dynamic glyph support detection
445
+ const context = getMeasurementContext();
446
+ context.font = `16px ${fontFamily}`;
447
+
448
+ // Test English characters
449
+ const englishChars = ['A', 'B', 'C', 'a', 'b', 'c', 'M', 'W'];
450
+ const fallbackFont = 'Arial, sans-serif';
451
+
452
+ // Measure with target font
453
+ const targetWidths = englishChars.map(char => context.measureText(char).width);
454
+
455
+ // Measure with fallback font
456
+ context.font = `16px ${fallbackFont}`;
457
+ const fallbackWidths = englishChars.map(char => context.measureText(char).width);
458
+
459
+ // If most measurements are identical, the font likely doesn't have English glyphs
460
+ let identicalCount = 0;
461
+ for (let i = 0; i < englishChars.length; i++) {
462
+ if (Math.abs(targetWidths[i] - fallbackWidths[i]) < 0.5) {
463
+ identicalCount++;
464
+ }
465
+ }
466
+ const lacksSupportThreshold = englishChars.length * 0.7; // 70% identical = lacks support
467
+ const lacksSupport = identicalCount >= lacksSupportThreshold;
468
+ return lacksSupport;
469
+ }
470
+
471
+ // Cache for font glyph detection results
472
+ const fontGlyphCache = new Map();
473
+
474
+ /**
475
+ * Cached version of font glyph detection
476
+ */
477
+ function fontLacksEnglishGlyphsCached(fontFamily) {
478
+ if (fontGlyphCache.has(fontFamily)) {
479
+ return fontGlyphCache.get(fontFamily);
480
+ }
481
+ const result = fontLacksEnglishGlyphs(fontFamily);
482
+ fontGlyphCache.set(fontFamily, result);
483
+ return result;
484
+ }
485
+
486
+ export { MeasurementCache, clearAllCaches, fontLacksEnglishGlyphs, fontLacksEnglishGlyphsCached, fontMetricsCache, getFontMetrics, kerningCache, measureGrapheme, measureGraphemeWithKerning, measurementCache };
316
487
  //# sourceMappingURL=measure.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"measure.mjs","sources":["../../../src/text/measure.ts"],"sourcesContent":["/**\r\n * Advanced Text Measurement System\r\n * \r\n * Provides precise text measurement with caching, font metrics,\r\n * and DPI awareness for optimal performance and accuracy.\r\n */\r\n\r\nimport { createCanvasElementFor } from '../util/misc/dom';\r\n\r\nexport interface MeasurementOptions {\r\n fontFamily: string;\r\n fontSize: number;\r\n fontStyle: string;\r\n fontWeight: string | number;\r\n letterSpacing?: number;\r\n direction?: 'ltr' | 'rtl';\r\n}\r\n\r\nexport interface GraphemeMeasurement {\r\n width: number;\r\n height: number;\r\n ascent: number;\r\n descent: number;\r\n baseline: number;\r\n}\r\n\r\nexport interface KerningMeasurement extends GraphemeMeasurement {\r\n kernedWidth: number; // width accounting for kerning with previous char\r\n}\r\n\r\nexport interface FontMetrics {\r\n ascent: number;\r\n descent: number;\r\n lineHeight: number;\r\n baseline: string;\r\n fontBoundingBoxAscent?: number;\r\n fontBoundingBoxDescent?: number;\r\n actualBoundingBoxAscent?: number;\r\n actualBoundingBoxDescent?: number;\r\n}\r\n\r\n// Global measurement context - reused for performance\r\nlet measurementContext: CanvasRenderingContext2D | null = null;\r\n\r\n/**\r\n * Get or create the shared measurement context\r\n */\r\nfunction getMeasurementContext(): CanvasRenderingContext2D {\r\n if (!measurementContext) {\r\n const canvas = createCanvasElementFor({\r\n width: 0,\r\n height: 0,\r\n });\r\n measurementContext = canvas.getContext('2d')!;\r\n }\r\n return measurementContext;\r\n}\r\n\r\n/**\r\n * Measure a single grapheme\r\n */\r\nexport function measureGrapheme(\r\n grapheme: string,\r\n options: MeasurementOptions,\r\n ctx?: CanvasRenderingContext2D\r\n): GraphemeMeasurement {\r\n // Check cache first\r\n const cached = measurementCache.get(grapheme, options);\r\n if (cached) {\r\n return cached;\r\n }\r\n\r\n // Use provided context or get global one\r\n const context = ctx || getMeasurementContext();\r\n \r\n // Set font properties\r\n applyFontStyle(context, options);\r\n \r\n // Measure the grapheme\r\n const metrics = context.measureText(grapheme);\r\n const fontMetrics = getFontMetrics(options);\r\n \r\n // Calculate comprehensive measurements\r\n const measurement: GraphemeMeasurement = {\r\n width: metrics.width,\r\n height: fontMetrics.lineHeight,\r\n ascent: fontMetrics.ascent,\r\n descent: fontMetrics.descent,\r\n baseline: fontMetrics.ascent,\r\n };\r\n \r\n // Cache the result\r\n measurementCache.set(grapheme, options, measurement);\r\n \r\n return measurement;\r\n}\r\n\r\n/**\r\n * Measure a grapheme with kerning relative to previous character\r\n */\r\nexport function measureGraphemeWithKerning(\r\n grapheme: string,\r\n previousGrapheme: string | undefined,\r\n options: MeasurementOptions,\r\n ctx?: CanvasRenderingContext2D\r\n): KerningMeasurement {\r\n // Get individual measurement\r\n const individual = measureGrapheme(grapheme, options, ctx);\r\n \r\n // If no previous character, kerning width equals regular width\r\n if (!previousGrapheme) {\r\n return {\r\n ...individual,\r\n kernedWidth: individual.width,\r\n };\r\n }\r\n \r\n // Check kerning cache\r\n const kerningPair = `${previousGrapheme}${grapheme}`;\r\n const cachedKerning = kerningCache.get(kerningPair, options);\r\n if (cachedKerning) {\r\n return {\r\n ...individual,\r\n kernedWidth: cachedKerning,\r\n };\r\n }\r\n \r\n // Use provided context or get global one\r\n const context = ctx || getMeasurementContext();\r\n applyFontStyle(context, options);\r\n \r\n // Measure the pair\r\n const pairWidth = context.measureText(previousGrapheme + grapheme).width;\r\n const previousWidth = measureGrapheme(previousGrapheme, options, context).width;\r\n const kernedWidth = pairWidth - previousWidth;\r\n \r\n // Cache kerning result\r\n kerningCache.set(kerningPair, options, kernedWidth);\r\n \r\n return {\r\n ...individual,\r\n kernedWidth,\r\n };\r\n}\r\n\r\n/**\r\n * Get font metrics for layout calculations\r\n */\r\nexport function getFontMetrics(options: MeasurementOptions): FontMetrics {\r\n const cacheKey = getFontDeclaration(options);\r\n const cached = fontMetricsCache.get(cacheKey);\r\n if (cached) {\r\n return cached;\r\n }\r\n \r\n const context = getMeasurementContext();\r\n applyFontStyle(context, options);\r\n \r\n // Use 'M' as sample character for metrics\r\n const metrics = context.measureText('M');\r\n const fontSize = options.fontSize;\r\n \r\n // Calculate metrics with fallbacks\r\n const fontBoundingBoxAscent = metrics.fontBoundingBoxAscent ?? fontSize * 0.91;\r\n const fontBoundingBoxDescent = metrics.fontBoundingBoxDescent ?? fontSize * 0.21;\r\n const actualBoundingBoxAscent = metrics.actualBoundingBoxAscent ?? fontSize * 0.716;\r\n const actualBoundingBoxDescent = metrics.actualBoundingBoxDescent ?? 0;\r\n \r\n const result: FontMetrics = {\r\n ascent: fontBoundingBoxAscent,\r\n descent: fontBoundingBoxDescent,\r\n lineHeight: fontSize,\r\n baseline: 'alphabetic',\r\n fontBoundingBoxAscent,\r\n fontBoundingBoxDescent,\r\n actualBoundingBoxAscent,\r\n actualBoundingBoxDescent,\r\n };\r\n \r\n fontMetricsCache.set(cacheKey, result);\r\n return result;\r\n}\r\n\r\n/**\r\n * Apply font styling to canvas context\r\n */\r\nfunction applyFontStyle(ctx: CanvasRenderingContext2D, options: MeasurementOptions): void {\r\n const fontDeclaration = getFontDeclaration(options);\r\n ctx.font = fontDeclaration;\r\n \r\n if (options.letterSpacing) {\r\n // Modern browsers support letterSpacing\r\n if ('letterSpacing' in ctx) {\r\n (ctx as any).letterSpacing = `${options.letterSpacing}px`;\r\n }\r\n }\r\n \r\n if (options.direction) {\r\n ctx.direction = options.direction;\r\n }\r\n \r\n ctx.textBaseline = 'alphabetic';\r\n}\r\n\r\n/**\r\n * Generate font declaration string\r\n */\r\nfunction getFontDeclaration(options: MeasurementOptions): string {\r\n const { fontStyle, fontWeight, fontSize, fontFamily } = options;\r\n \r\n // Normalize font family (add quotes if needed)\r\n const normalizedFamily = fontFamily.includes(' ') && \r\n !fontFamily.includes('\"') && \r\n !fontFamily.includes(\"'\")\r\n ? `\"${fontFamily}\"`\r\n : fontFamily;\r\n \r\n return `${fontStyle} ${fontWeight} ${fontSize}px ${normalizedFamily}`;\r\n}\r\n\r\n/**\r\n * LRU Cache implementation for measurements\r\n */\r\nclass LRUCache<T> {\r\n private cache = new Map<string, { value: T; timestamp: number }>();\r\n private maxSize: number;\r\n private hits = 0;\r\n private misses = 0;\r\n\r\n constructor(maxSize = 1000) {\r\n this.maxSize = maxSize;\r\n }\r\n\r\n get(key: string): T | undefined {\r\n const entry = this.cache.get(key);\r\n if (entry) {\r\n // Update timestamp for LRU\r\n entry.timestamp = Date.now();\r\n this.hits++;\r\n return entry.value;\r\n }\r\n this.misses++;\r\n return undefined;\r\n }\r\n\r\n set(key: string, value: T): void {\r\n // Remove oldest entries if at capacity\r\n if (this.cache.size >= this.maxSize) {\r\n const oldestKey = this.findOldestKey();\r\n if (oldestKey) {\r\n this.cache.delete(oldestKey);\r\n }\r\n }\r\n\r\n this.cache.set(key, {\r\n value,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n private findOldestKey(): string | undefined {\r\n let oldestKey: string | undefined;\r\n let oldestTime = Infinity;\r\n\r\n for (const [key, entry] of this.cache.entries()) {\r\n if (entry.timestamp < oldestTime) {\r\n oldestTime = entry.timestamp;\r\n oldestKey = key;\r\n }\r\n }\r\n\r\n return oldestKey;\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n this.hits = 0;\r\n this.misses = 0;\r\n }\r\n\r\n getStats(): { size: number; hitRate: number; hits: number; misses: number } {\r\n const total = this.hits + this.misses;\r\n return {\r\n size: this.cache.size,\r\n hitRate: total > 0 ? this.hits / total : 0,\r\n hits: this.hits,\r\n misses: this.misses,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Advanced measurement cache with font-aware keys\r\n */\r\nexport class MeasurementCache {\r\n private cache = new LRUCache<GraphemeMeasurement>(1000);\r\n\r\n getCacheKey(grapheme: string, options: MeasurementOptions): string {\r\n const fontDecl = getFontDeclaration(options);\r\n const letterSpacing = options.letterSpacing || 0;\r\n return `${fontDecl}|${grapheme}|${letterSpacing}`;\r\n }\r\n\r\n get(grapheme: string, options: MeasurementOptions): GraphemeMeasurement | undefined {\r\n const key = this.getCacheKey(grapheme, options);\r\n return this.cache.get(key);\r\n }\r\n\r\n set(grapheme: string, options: MeasurementOptions, measurement: GraphemeMeasurement): void {\r\n const key = this.getCacheKey(grapheme, options);\r\n this.cache.set(key, measurement);\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n\r\n getStats() {\r\n return this.cache.getStats();\r\n }\r\n}\r\n\r\n/**\r\n * Kerning cache for character pairs\r\n */\r\nclass KerningCache {\r\n private cache = new LRUCache<number>(5000); // More entries for pairs\r\n\r\n getCacheKey(pair: string, options: MeasurementOptions): string {\r\n const fontDecl = getFontDeclaration(options);\r\n return `${fontDecl}|${pair}`;\r\n }\r\n\r\n get(pair: string, options: MeasurementOptions): number | undefined {\r\n const key = this.getCacheKey(pair, options);\r\n return this.cache.get(key);\r\n }\r\n\r\n set(pair: string, options: MeasurementOptions, kerning: number): void {\r\n const key = this.getCacheKey(pair, options);\r\n this.cache.set(key, kerning);\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n\r\n getStats() {\r\n return this.cache.getStats();\r\n }\r\n}\r\n\r\n/**\r\n * Font metrics cache\r\n */\r\nclass FontMetricsCache {\r\n private cache = new Map<string, FontMetrics>();\r\n\r\n get(fontDeclaration: string): FontMetrics | undefined {\r\n return this.cache.get(fontDeclaration);\r\n }\r\n\r\n set(fontDeclaration: string, metrics: FontMetrics): void {\r\n this.cache.set(fontDeclaration, metrics);\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n\r\n getStats() {\r\n return {\r\n size: this.cache.size,\r\n };\r\n }\r\n}\r\n\r\n// Global cache instances\r\nexport const measurementCache = new MeasurementCache();\r\nexport const kerningCache = new KerningCache();\r\nexport const fontMetricsCache = new FontMetricsCache();\r\n\r\n/**\r\n * Clear all measurement caches\r\n */\r\nexport function clearAllCaches(): void {\r\n measurementCache.clear();\r\n kerningCache.clear();\r\n fontMetricsCache.clear();\r\n}\r\n\r\n/**\r\n * Get combined cache statistics\r\n */\r\nexport function getCacheStats() {\r\n return {\r\n measurement: measurementCache.getStats(),\r\n kerning: kerningCache.getStats(),\r\n fontMetrics: fontMetricsCache.getStats(),\r\n };\r\n}\r\n\r\n/**\r\n * Batch measure multiple graphemes efficiently\r\n */\r\nexport function batchMeasureGraphemes(\r\n graphemes: string[],\r\n options: MeasurementOptions,\r\n ctx?: CanvasRenderingContext2D\r\n): GraphemeMeasurement[] {\r\n const context = ctx || getMeasurementContext();\r\n applyFontStyle(context, options);\r\n \r\n // Separate cached and uncached measurements\r\n const results: GraphemeMeasurement[] = new Array(graphemes.length);\r\n const uncachedIndices: number[] = [];\r\n \r\n // Check cache for all graphemes\r\n graphemes.forEach((grapheme, index) => {\r\n const cached = measurementCache.get(grapheme, options);\r\n if (cached) {\r\n results[index] = cached;\r\n } else {\r\n uncachedIndices.push(index);\r\n }\r\n });\r\n \r\n // Measure uncached graphemes\r\n const fontMetrics = getFontMetrics(options);\r\n uncachedIndices.forEach(index => {\r\n const grapheme = graphemes[index];\r\n const metrics = context.measureText(grapheme);\r\n \r\n const measurement: GraphemeMeasurement = {\r\n width: metrics.width,\r\n height: fontMetrics.lineHeight,\r\n ascent: fontMetrics.ascent,\r\n descent: fontMetrics.descent,\r\n baseline: fontMetrics.ascent,\r\n };\r\n \r\n measurementCache.set(grapheme, options, measurement);\r\n results[index] = measurement;\r\n });\r\n \r\n return results;\r\n}\r\n\r\n/**\r\n * Estimate text width without full layout (for performance)\r\n */\r\nexport function estimateTextWidth(\r\n text: string,\r\n options: MeasurementOptions\r\n): number {\r\n // Use average character width for estimation\r\n const avgChar = 'n'; // Representative character\r\n const avgMeasurement = measureGrapheme(avgChar, options);\r\n const letterSpacing = options.letterSpacing || 0;\r\n \r\n return text.length * (avgMeasurement.width + letterSpacing);\r\n}\r\n\r\n/**\r\n * Check if font is loaded and ready for measurement\r\n */\r\nexport function isFontReady(fontFamily: string): boolean {\r\n if (typeof document === 'undefined') return true;\r\n \r\n if ('fonts' in document) {\r\n return document.fonts.check(`16px ${fontFamily}`);\r\n }\r\n \r\n // Fallback - assume font is ready\r\n return true;\r\n}"],"names":["measurementContext","getMeasurementContext","canvas","createCanvasElementFor","width","height","getContext","measureGrapheme","grapheme","options","ctx","cached","measurementCache","get","context","applyFontStyle","metrics","measureText","fontMetrics","getFontMetrics","measurement","lineHeight","ascent","descent","baseline","set","measureGraphemeWithKerning","previousGrapheme","individual","kernedWidth","kerningPair","cachedKerning","kerningCache","pairWidth","previousWidth","_metrics$fontBounding","_metrics$fontBounding2","_metrics$actualBoundi","_metrics$actualBoundi2","cacheKey","getFontDeclaration","fontMetricsCache","fontSize","fontBoundingBoxAscent","fontBoundingBoxDescent","actualBoundingBoxAscent","actualBoundingBoxDescent","result","fontDeclaration","font","letterSpacing","direction","textBaseline","fontStyle","fontWeight","fontFamily","normalizedFamily","includes","LRUCache","constructor","maxSize","arguments","length","undefined","_defineProperty","Map","key","entry","cache","timestamp","Date","now","hits","value","misses","size","oldestKey","findOldestKey","delete","oldestTime","Infinity","entries","clear","getStats","total","hitRate","MeasurementCache","getCacheKey","fontDecl","KerningCache","pair","kerning","FontMetricsCache"],"mappings":"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"}
1
+ {"version":3,"file":"measure.mjs","sources":["../../../src/text/measure.ts"],"sourcesContent":["/**\r\n * Advanced Text Measurement System\r\n *\r\n * Provides precise text measurement with caching, font metrics,\r\n * and DPI awareness for optimal performance and accuracy.\r\n */\r\n\r\nimport { createCanvasElementFor } from '../util/misc/dom';\r\n\r\nexport interface MeasurementOptions {\r\n fontFamily: string;\r\n fontSize: number;\r\n fontStyle: string;\r\n fontWeight: string | number;\r\n letterSpacing?: number;\r\n direction?: 'ltr' | 'rtl';\r\n}\r\n\r\nexport interface GraphemeMeasurement {\r\n width: number;\r\n height: number;\r\n ascent: number;\r\n descent: number;\r\n baseline: number;\r\n}\r\n\r\nexport interface KerningMeasurement extends GraphemeMeasurement {\r\n kernedWidth: number; // width accounting for kerning with previous char\r\n}\r\n\r\nexport interface FontMetrics {\r\n ascent: number;\r\n descent: number;\r\n lineHeight: number;\r\n baseline: string;\r\n fontBoundingBoxAscent?: number;\r\n fontBoundingBoxDescent?: number;\r\n actualBoundingBoxAscent?: number;\r\n actualBoundingBoxDescent?: number;\r\n}\r\n\r\n// Global measurement context - reused for performance\r\nlet measurementContext: CanvasRenderingContext2D | null = null;\r\n\r\n/**\r\n * Get or create the shared measurement context\r\n */\r\nfunction getMeasurementContext(): CanvasRenderingContext2D {\r\n if (!measurementContext) {\r\n const canvas = createCanvasElementFor({\r\n width: 0,\r\n height: 0,\r\n });\r\n measurementContext = canvas.getContext('2d')!;\r\n }\r\n return measurementContext;\r\n}\r\n\r\n/**\r\n * Measure a single grapheme\r\n */\r\nexport function measureGrapheme(\r\n grapheme: string,\r\n options: MeasurementOptions,\r\n ctx?: CanvasRenderingContext2D,\r\n): GraphemeMeasurement {\r\n // Check cache first\r\n const cached = measurementCache.get(grapheme, options);\r\n if (cached) {\r\n return cached;\r\n }\r\n\r\n // Use provided context or get global one\r\n const context = ctx || getMeasurementContext();\r\n\r\n // Set font properties\r\n applyFontStyle(context, options);\r\n\r\n // Measure the grapheme\r\n const metrics = context.measureText(grapheme);\r\n const fontMetrics = getFontMetrics(options);\r\n\r\n // Calculate comprehensive measurements\r\n const measurement: GraphemeMeasurement = {\r\n width: metrics.width,\r\n height: fontMetrics.lineHeight,\r\n ascent: fontMetrics.ascent,\r\n descent: fontMetrics.descent,\r\n baseline: fontMetrics.ascent,\r\n };\r\n\r\n // Cache the result\r\n measurementCache.set(grapheme, options, measurement);\r\n\r\n return measurement;\r\n}\r\n\r\n/**\r\n * Measure a grapheme with kerning relative to previous character\r\n */\r\nexport function measureGraphemeWithKerning(\r\n grapheme: string,\r\n previousGrapheme: string | undefined,\r\n options: MeasurementOptions,\r\n ctx?: CanvasRenderingContext2D,\r\n): KerningMeasurement {\r\n // Get individual measurement\r\n const individual = measureGrapheme(grapheme, options, ctx);\r\n\r\n // If no previous character, kerning width equals regular width\r\n if (!previousGrapheme) {\r\n return {\r\n ...individual,\r\n kernedWidth: individual.width,\r\n };\r\n }\r\n\r\n // Check kerning cache\r\n const kerningPair = `${previousGrapheme}${grapheme}`;\r\n const cachedKerning = kerningCache.get(kerningPair, options);\r\n if (cachedKerning) {\r\n return {\r\n ...individual,\r\n kernedWidth: cachedKerning,\r\n };\r\n }\r\n\r\n // Use provided context or get global one\r\n const context = ctx || getMeasurementContext();\r\n applyFontStyle(context, options);\r\n\r\n // Measure the pair\r\n const pairWidth = context.measureText(previousGrapheme + grapheme).width;\r\n const previousWidth = measureGrapheme(\r\n previousGrapheme,\r\n options,\r\n context,\r\n ).width;\r\n const kernedWidth = pairWidth - previousWidth;\r\n\r\n // Cache kerning result\r\n kerningCache.set(kerningPair, options, kernedWidth);\r\n\r\n return {\r\n ...individual,\r\n kernedWidth,\r\n };\r\n}\r\n\r\n/**\r\n * Get a representative character for font metrics measurement\r\n * Uses canvas to test which scripts the font actually supports\r\n */\r\nfunction getRepresentativeCharacter(fontFamily: string): string {\r\n const context = getMeasurementContext();\r\n \r\n // Wait for font to be ready if possible\r\n if (typeof document !== 'undefined' && 'fonts' in document) {\r\n try {\r\n // Check if font is ready, if not, use fallback immediately\r\n if (!document.fonts.check(`16px ${fontFamily}`)) {\r\n return 'M'; // Use safe fallback while font loads\r\n }\r\n } catch (e) {\r\n // Font check failed, use fallback\r\n return 'M';\r\n }\r\n }\r\n \r\n // Test characters for different scripts\r\n const testChars = [\r\n { char: 'م', script: 'Arabic' }, // Arabic\r\n { char: 'א', script: 'Hebrew' }, // Hebrew \r\n { char: 'अ', script: 'Devanagari' }, // Hindi/Sanskrit\r\n { char: 'ا', script: 'Urdu' }, // Urdu\r\n { char: 'ک', script: 'Persian' }, // Persian\r\n { char: 'த', script: 'Tamil' }, // Tamil\r\n { char: 'ก', script: 'Thai' }, // Thai\r\n { char: 'М', script: 'Cyrillic' }, // Cyrillic\r\n { char: 'Ω', script: 'Greek' }, // Greek\r\n { char: 'M', script: 'Latin' } // Latin (fallback)\r\n ];\r\n \r\n // Set the font\r\n context.font = `16px ${fontFamily}`;\r\n \r\n // Test each character to see which ones render properly\r\n // Use a more robust width check to avoid false positives\r\n const fallbackWidth = context.measureText('M').width;\r\n \r\n for (const test of testChars) {\r\n const metrics = context.measureText(test.char);\r\n \r\n // Character is valid if it has width and isn't just a fallback glyph\r\n if (metrics.width > 0 && Math.abs(metrics.width - fallbackWidth) > 0.1) {\r\n return test.char;\r\n }\r\n }\r\n \r\n // Fallback to Latin 'M'\r\n return 'M';\r\n}\r\n\r\n/**\r\n * Get font metrics for layout calculations\r\n */\r\nexport function getFontMetrics(options: MeasurementOptions): FontMetrics {\r\n const cacheKey = getFontDeclaration(options);\r\n const cached = fontMetricsCache.get(cacheKey);\r\n if (cached) {\r\n return cached;\r\n }\r\n\r\n const context = getMeasurementContext();\r\n applyFontStyle(context, options);\r\n\r\n // Use representative character based on font's primary script\r\n const sample = getRepresentativeCharacter(options.fontFamily);\r\n const metrics = context.measureText(sample);\r\n const fontSize = options.fontSize;\r\n\r\n // Calculate metrics with fallbacks\r\n const fontBoundingBoxAscent =\r\n metrics.fontBoundingBoxAscent ?? fontSize * 0.91;\r\n const fontBoundingBoxDescent =\r\n metrics.fontBoundingBoxDescent ?? fontSize * 0.21;\r\n const actualBoundingBoxAscent =\r\n metrics.actualBoundingBoxAscent ?? fontSize * 0.716;\r\n const actualBoundingBoxDescent = metrics.actualBoundingBoxDescent ?? 0;\r\n\r\n const result: FontMetrics = {\r\n ascent: fontBoundingBoxAscent,\r\n descent: fontBoundingBoxDescent,\r\n lineHeight: fontSize,\r\n baseline: 'alphabetic',\r\n fontBoundingBoxAscent,\r\n fontBoundingBoxDescent,\r\n actualBoundingBoxAscent,\r\n actualBoundingBoxDescent,\r\n };\r\n\r\n fontMetricsCache.set(cacheKey, result);\r\n return result;\r\n}\r\n\r\n/**\r\n * Apply font styling to canvas context\r\n */\r\nfunction applyFontStyle(\r\n ctx: CanvasRenderingContext2D,\r\n options: MeasurementOptions,\r\n): void {\r\n const fontDeclaration = getFontDeclaration(options);\r\n ctx.font = fontDeclaration;\r\n\r\n if (options.letterSpacing) {\r\n // Modern browsers support letterSpacing\r\n if ('letterSpacing' in ctx) {\r\n (ctx as any).letterSpacing = `${options.letterSpacing}px`;\r\n }\r\n }\r\n\r\n if (options.direction) {\r\n ctx.direction = options.direction;\r\n }\r\n\r\n ctx.textBaseline = 'alphabetic';\r\n}\r\n\r\n/**\r\n * Generate font declaration string\r\n */\r\nfunction getFontDeclaration(options: MeasurementOptions): string {\r\n const { fontStyle, fontWeight, fontSize, fontFamily } = options;\r\n\r\n // Normalize font family (add quotes if needed)\r\n let normalizedFamily =\r\n fontFamily.includes(' ') &&\r\n !fontFamily.includes('\"') &&\r\n !fontFamily.includes(\"'\")\r\n ? `\"${fontFamily}\"`\r\n : fontFamily;\r\n\r\n // Note: Font fallbacks are handled in the rendering phase only\r\n // to avoid affecting measurement calculations for text wrapping\r\n\r\n return `${fontStyle} ${fontWeight} ${fontSize}px ${normalizedFamily}`;\r\n}\r\n\r\n/**\r\n * LRU Cache implementation for measurements\r\n */\r\nclass LRUCache<T> {\r\n private cache = new Map<string, { value: T; timestamp: number }>();\r\n private maxSize: number;\r\n private hits = 0;\r\n private misses = 0;\r\n\r\n constructor(maxSize = 1000) {\r\n this.maxSize = maxSize;\r\n }\r\n\r\n get(key: string): T | undefined {\r\n const entry = this.cache.get(key);\r\n if (entry) {\r\n // Update timestamp for LRU\r\n entry.timestamp = Date.now();\r\n this.hits++;\r\n return entry.value;\r\n }\r\n this.misses++;\r\n return undefined;\r\n }\r\n\r\n set(key: string, value: T): void {\r\n // Remove oldest entries if at capacity\r\n if (this.cache.size >= this.maxSize) {\r\n const oldestKey = this.findOldestKey();\r\n if (oldestKey) {\r\n this.cache.delete(oldestKey);\r\n }\r\n }\r\n\r\n this.cache.set(key, {\r\n value,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n private findOldestKey(): string | undefined {\r\n let oldestKey: string | undefined;\r\n let oldestTime = Infinity;\r\n\r\n for (const [key, entry] of this.cache.entries()) {\r\n if (entry.timestamp < oldestTime) {\r\n oldestTime = entry.timestamp;\r\n oldestKey = key;\r\n }\r\n }\r\n\r\n return oldestKey;\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n this.hits = 0;\r\n this.misses = 0;\r\n }\r\n\r\n getStats(): { size: number; hitRate: number; hits: number; misses: number } {\r\n const total = this.hits + this.misses;\r\n return {\r\n size: this.cache.size,\r\n hitRate: total > 0 ? this.hits / total : 0,\r\n hits: this.hits,\r\n misses: this.misses,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Advanced measurement cache with font-aware keys\r\n */\r\nexport class MeasurementCache {\r\n private cache = new LRUCache<GraphemeMeasurement>(1000);\r\n\r\n getCacheKey(grapheme: string, options: MeasurementOptions): string {\r\n const fontDecl = getFontDeclaration(options);\r\n const letterSpacing = options.letterSpacing || 0;\r\n return `${fontDecl}|${grapheme}|${letterSpacing}`;\r\n }\r\n\r\n get(\r\n grapheme: string,\r\n options: MeasurementOptions,\r\n ): GraphemeMeasurement | undefined {\r\n const key = this.getCacheKey(grapheme, options);\r\n return this.cache.get(key);\r\n }\r\n\r\n set(\r\n grapheme: string,\r\n options: MeasurementOptions,\r\n measurement: GraphemeMeasurement,\r\n ): void {\r\n const key = this.getCacheKey(grapheme, options);\r\n this.cache.set(key, measurement);\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n\r\n getStats() {\r\n return this.cache.getStats();\r\n }\r\n}\r\n\r\n/**\r\n * Kerning cache for character pairs\r\n */\r\nclass KerningCache {\r\n private cache = new LRUCache<number>(5000); // More entries for pairs\r\n\r\n getCacheKey(pair: string, options: MeasurementOptions): string {\r\n const fontDecl = getFontDeclaration(options);\r\n return `${fontDecl}|${pair}`;\r\n }\r\n\r\n get(pair: string, options: MeasurementOptions): number | undefined {\r\n const key = this.getCacheKey(pair, options);\r\n return this.cache.get(key);\r\n }\r\n\r\n set(pair: string, options: MeasurementOptions, kerning: number): void {\r\n const key = this.getCacheKey(pair, options);\r\n this.cache.set(key, kerning);\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n\r\n getStats() {\r\n return this.cache.getStats();\r\n }\r\n}\r\n\r\n/**\r\n * Font metrics cache\r\n */\r\nclass FontMetricsCache {\r\n private cache = new Map<string, FontMetrics>();\r\n\r\n get(fontDeclaration: string): FontMetrics | undefined {\r\n return this.cache.get(fontDeclaration);\r\n }\r\n\r\n set(fontDeclaration: string, metrics: FontMetrics): void {\r\n this.cache.set(fontDeclaration, metrics);\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n\r\n getStats() {\r\n return {\r\n size: this.cache.size,\r\n };\r\n }\r\n}\r\n\r\n// Global cache instances\r\nexport const measurementCache = new MeasurementCache();\r\nexport const kerningCache = new KerningCache();\r\nexport const fontMetricsCache = new FontMetricsCache();\r\n\r\n// Set up font loading listener to clear caches when fonts change\r\nif (typeof document !== 'undefined' && 'fonts' in document) {\r\n document.fonts.addEventListener('loadingdone', () => {\r\n // Clear all caches when fonts finish loading\r\n clearAllCaches();\r\n });\r\n}\r\n\r\n/**\r\n * Clear all measurement caches\r\n */\r\nexport function clearAllCaches(): void {\r\n measurementCache.clear();\r\n kerningCache.clear();\r\n fontMetricsCache.clear();\r\n}\r\n\r\n/**\r\n * Get combined cache statistics\r\n */\r\nexport function getCacheStats() {\r\n return {\r\n measurement: measurementCache.getStats(),\r\n kerning: kerningCache.getStats(),\r\n fontMetrics: fontMetricsCache.getStats(),\r\n };\r\n}\r\n\r\n/**\r\n * Batch measure multiple graphemes efficiently\r\n */\r\nexport function batchMeasureGraphemes(\r\n graphemes: string[],\r\n options: MeasurementOptions,\r\n ctx?: CanvasRenderingContext2D,\r\n): GraphemeMeasurement[] {\r\n const context = ctx || getMeasurementContext();\r\n applyFontStyle(context, options);\r\n\r\n // Separate cached and uncached measurements\r\n const results: GraphemeMeasurement[] = new Array(graphemes.length);\r\n const uncachedIndices: number[] = [];\r\n\r\n // Check cache for all graphemes\r\n graphemes.forEach((grapheme, index) => {\r\n const cached = measurementCache.get(grapheme, options);\r\n if (cached) {\r\n results[index] = cached;\r\n } else {\r\n uncachedIndices.push(index);\r\n }\r\n });\r\n\r\n // Measure uncached graphemes\r\n const fontMetrics = getFontMetrics(options);\r\n uncachedIndices.forEach((index) => {\r\n const grapheme = graphemes[index];\r\n const metrics = context.measureText(grapheme);\r\n\r\n const measurement: GraphemeMeasurement = {\r\n width: metrics.width,\r\n height: fontMetrics.lineHeight,\r\n ascent: fontMetrics.ascent,\r\n descent: fontMetrics.descent,\r\n baseline: fontMetrics.ascent,\r\n };\r\n\r\n measurementCache.set(grapheme, options, measurement);\r\n results[index] = measurement;\r\n });\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * Estimate text width without full layout (for performance)\r\n */\r\nexport function estimateTextWidth(\r\n text: string,\r\n options: MeasurementOptions,\r\n): number {\r\n // Use average character width for estimation\r\n const avgChar = 'n'; // Representative character\r\n const avgMeasurement = measureGrapheme(avgChar, options);\r\n const letterSpacing = options.letterSpacing || 0;\r\n\r\n return text.length * (avgMeasurement.width + letterSpacing);\r\n}\r\n\r\n/**\r\n * Check if font is loaded and ready for measurement\r\n */\r\nexport function isFontReady(fontFamily: string): boolean {\r\n if (typeof document === 'undefined') return true;\r\n\r\n if ('fonts' in document) {\r\n return document.fonts.check(`16px ${fontFamily}`);\r\n }\r\n\r\n // Fallback - assume font is ready\r\n return true;\r\n}\r\n\r\n/**\r\n * Detect if a font lacks English glyph support\r\n * These fonts should use browser-native measurement instead of Fabric's character-by-character measurement\r\n */\r\nexport function fontLacksEnglishGlyphs(fontFamily: string): boolean {\r\n if (typeof document === 'undefined') return false;\r\n \r\n // Known fonts that lack English glyphs\r\n const knownNonEnglishFonts = [\r\n 'stv', 'arabic', 'naskh', 'thuluth', 'kufi', 'diwani',\r\n 'nastaliq', 'kufic', 'hijazi', 'madinah', 'makkah'\r\n ];\r\n \r\n const lowerFontFamily = fontFamily.toLowerCase();\r\n \r\n // Check known list first\r\n if (knownNonEnglishFonts.some(font => lowerFontFamily.includes(font))) {\r\n return true;\r\n }\r\n \r\n // Dynamic glyph support detection\r\n const context = getMeasurementContext();\r\n context.font = `16px ${fontFamily}`;\r\n \r\n // Test English characters\r\n const englishChars = ['A', 'B', 'C', 'a', 'b', 'c', 'M', 'W'];\r\n const fallbackFont = 'Arial, sans-serif';\r\n \r\n // Measure with target font\r\n const targetWidths = englishChars.map(char => context.measureText(char).width);\r\n \r\n // Measure with fallback font\r\n context.font = `16px ${fallbackFont}`;\r\n const fallbackWidths = englishChars.map(char => context.measureText(char).width);\r\n \r\n // If most measurements are identical, the font likely doesn't have English glyphs\r\n let identicalCount = 0;\r\n for (let i = 0; i < englishChars.length; i++) {\r\n if (Math.abs(targetWidths[i] - fallbackWidths[i]) < 0.5) {\r\n identicalCount++;\r\n }\r\n }\r\n \r\n const lacksSupportThreshold = englishChars.length * 0.7; // 70% identical = lacks support\r\n const lacksSupport = identicalCount >= lacksSupportThreshold;\r\n \r\n \r\n return lacksSupport;\r\n}\r\n\r\n// Cache for font glyph detection results\r\nconst fontGlyphCache = new Map<string, boolean>();\r\n\r\n/**\r\n * Cached version of font glyph detection\r\n */\r\nexport function fontLacksEnglishGlyphsCached(fontFamily: string): boolean {\r\n if (fontGlyphCache.has(fontFamily)) {\r\n return fontGlyphCache.get(fontFamily)!;\r\n }\r\n \r\n const result = fontLacksEnglishGlyphs(fontFamily);\r\n fontGlyphCache.set(fontFamily, result);\r\n return result;\r\n}\r\n"],"names":["measurementContext","getMeasurementContext","canvas","createCanvasElementFor","width","height","getContext","measureGrapheme","grapheme","options","ctx","cached","measurementCache","get","context","applyFontStyle","metrics","measureText","fontMetrics","getFontMetrics","measurement","lineHeight","ascent","descent","baseline","set","measureGraphemeWithKerning","previousGrapheme","individual","kernedWidth","kerningPair","cachedKerning","kerningCache","pairWidth","previousWidth","getRepresentativeCharacter","fontFamily","document","fonts","check","e","testChars","char","script","font","fallbackWidth","test","Math","abs","_metrics$fontBounding","_metrics$fontBounding2","_metrics$actualBoundi","_metrics$actualBoundi2","cacheKey","getFontDeclaration","fontMetricsCache","sample","fontSize","fontBoundingBoxAscent","fontBoundingBoxDescent","actualBoundingBoxAscent","actualBoundingBoxDescent","result","fontDeclaration","letterSpacing","direction","textBaseline","fontStyle","fontWeight","normalizedFamily","includes","LRUCache","constructor","maxSize","arguments","length","undefined","_defineProperty","Map","key","entry","cache","timestamp","Date","now","hits","value","misses","size","oldestKey","findOldestKey","delete","oldestTime","Infinity","entries","clear","getStats","total","hitRate","MeasurementCache","getCacheKey","fontDecl","KerningCache","pair","kerning","FontMetricsCache","addEventListener","clearAllCaches","fontLacksEnglishGlyphs","knownNonEnglishFonts","lowerFontFamily","toLowerCase","some","englishChars","fallbackFont","targetWidths","map","fallbackWidths","identicalCount","i","lacksSupportThreshold","lacksSupport","fontGlyphCache","fontLacksEnglishGlyphsCached","has"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"overlayEditor.d.ts","sourceRoot":"","sources":["../../../src/text/overlayEditor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAKjD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,GAAG,KAAK,GAAG,OAAO,CAAC;IACrC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,CAAyB;IAC1C,OAAO,CAAC,QAAQ,CAAC,CAAa;IAG9B,OAAO,CAAC,aAAa,CAUnB;gBAEU,OAAO,EAAE,oBAAoB;IAoBzC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA0DxB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAwB5B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAsB5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAmC1B;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,iBAAiB;IAsOzB;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAiGlC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA2GzB;;OAEG;IACH,OAAO,CAAC,aAAa;IAuCrB;;OAEG;IACI,OAAO,IAAI,IAAI;IAMtB;;OAEG;IACI,OAAO,CAAC,MAAM,GAAE,OAAc,GAAG,IAAI;IAoE5C,OAAO,CAAC,WAAW;IAiBnB,OAAO,CAAC,kBAAkB;IAqC1B,OAAO,CAAC,aAAa;IA2BrB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,sBAAsB;IAI9B,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,eAAe;IAMvB;;OAEG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;OAEG;IACH,OAAO,CAAC,8BAA8B;CAavC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,GAAG,KAAK,GAAG,OAAO,EACpC,OAAO,CAAC,EAAE;IACR,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB,GACA,aAAa,CAsBf;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,KAAK,GAAG,OAAO,GAAG,OAAO,CAE7E"}
1
+ {"version":3,"file":"overlayEditor.d.ts","sourceRoot":"","sources":["../../../src/text/overlayEditor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAKjD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,GAAG,KAAK,GAAG,OAAO,CAAC;IACrC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,CAAyB;IAC1C,OAAO,CAAC,QAAQ,CAAC,CAAa;IAG9B,OAAO,CAAC,aAAa,CAUnB;gBAEU,OAAO,EAAE,oBAAoB;IAoBzC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA0DxB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAwB5B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAsB5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAmC1B;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,iBAAiB;IA6OzB;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAiGlC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA2GzB;;OAEG;IACH,OAAO,CAAC,aAAa;IAuCrB;;OAEG;IACI,OAAO,IAAI,IAAI;IAMtB;;OAEG;IACI,OAAO,CAAC,MAAM,GAAE,OAAc,GAAG,IAAI;IAoE5C,OAAO,CAAC,WAAW;IAiBnB,OAAO,CAAC,kBAAkB;IAqC1B,OAAO,CAAC,aAAa;IA2BrB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,sBAAsB;IAI9B,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,eAAe;IAMvB;;OAEG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;OAEG;IACH,OAAO,CAAC,8BAA8B;CAavC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,GAAG,KAAK,GAAG,OAAO,EACpC,OAAO,CAAC,EAAE;IACR,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB,GACA,aAAa,CAsBf;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,KAAK,GAAG,OAAO,GAAG,OAAO,CAE7E"}
@@ -1,2 +1,2 @@
1
- import{defineProperty as t}from"../../_virtual/_rollupPluginBabelHelpers.min.mjs";import{transformPoint as e}from"../util/misc/matrix.min.mjs";class o{constructor(e){t(this,"canvas",void 0),t(this,"target",void 0),t(this,"container",void 0),t(this,"textarea",void 0),t(this,"hostDiv",void 0),t(this,"isDestroyed",!1),t(this,"isComposing",!1),t(this,"lastText",void 0),t(this,"onCommit",void 0),t(this,"onCancel",void 0),t(this,"boundHandlers",{onInput:this.handleInput.bind(this),onKeyDown:this.handleKeyDown.bind(this),onBlur:this.handleBlur.bind(this),onCompositionStart:this.handleCompositionStart.bind(this),onCompositionEnd:this.handleCompositionEnd.bind(this),onAfterRender:this.handleAfterRender.bind(this),onMouseWheel:this.handleMouseWheel.bind(this),onFocus:this.handleFocus.bind(this),onMouseDown:this.handleMouseDown.bind(this)}),this.canvas=e.canvas,this.target=e.target,this.onCommit=e.onCommit,this.onCancel=e.onCancel,this.lastText=this.target.text||"",this.container=this.getCanvasContainer(),this.createOverlayDOM(),this.attachEventListeners(),this.refresh(),this.focusTextarea()}getCanvasContainer(){const t=this.canvas.upperCanvasEl.parentElement;if(!t)throw new Error("Canvas must be mounted in DOM to use overlay editing");return t.style.position="relative",t}createOverlayDOM(){this.hostDiv=document.createElement("div"),this.hostDiv.style.position="absolute",this.hostDiv.style.pointerEvents="none",this.hostDiv.style.zIndex="1000",this.hostDiv.style.transformOrigin="left top",this.textarea=document.createElement("textarea"),this.textarea.style.position="absolute",this.textarea.style.left="0",this.textarea.style.top="0",this.textarea.style.margin="0",this.textarea.style.resize="none",this.textarea.style.pointerEvents="auto";const t=/[\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF]/.test(this.target.text||""),e=/[a-zA-Z]/.test(this.target.text||""),o="ltr"===this.target.direction;this.textarea.style.unicodeBidi=t&&e&&o||t&&o?"embed":"plaintext",this.textarea.style.caretColor="auto",this.textarea.style.border="none",this.textarea.style.padding="0",this.textarea.style.background="transparent",this.textarea.style.outline="none",this.textarea.style.overflow="hidden",this.textarea.style.whiteSpace="pre-wrap",this.textarea.style.wordBreak="normal",this.textarea.style.overflowWrap="break-word",this.textarea.style.userSelect="text",this.textarea.style.textTransform="none",this.textarea.style.opacity="1",this.textarea.value=this.target.text||"",this.hostDiv.appendChild(this.textarea),document.body.appendChild(this.hostDiv)}attachEventListeners(){this.textarea.addEventListener("input",this.boundHandlers.onInput),this.textarea.addEventListener("keydown",this.boundHandlers.onKeyDown),this.textarea.addEventListener("blur",this.boundHandlers.onBlur),this.textarea.addEventListener("compositionstart",this.boundHandlers.onCompositionStart),this.textarea.addEventListener("compositionend",this.boundHandlers.onCompositionEnd),this.textarea.addEventListener("focus",this.boundHandlers.onFocus),this.canvas.on("after:render",this.boundHandlers.onAfterRender),this.canvas.on("mouse:wheel",this.boundHandlers.onMouseWheel),this.canvas.on("mouse:down",this.boundHandlers.onMouseDown),this.setupViewportChangeDetection()}removeEventListeners(){this.textarea.removeEventListener("input",this.boundHandlers.onInput),this.textarea.removeEventListener("keydown",this.boundHandlers.onKeyDown),this.textarea.removeEventListener("blur",this.boundHandlers.onBlur),this.textarea.removeEventListener("compositionstart",this.boundHandlers.onCompositionStart),this.textarea.removeEventListener("compositionend",this.boundHandlers.onCompositionEnd),this.textarea.removeEventListener("focus",this.boundHandlers.onFocus),this.canvas.off("after:render",this.boundHandlers.onAfterRender),this.canvas.off("mouse:wheel",this.boundHandlers.onMouseWheel),this.canvas.off("mouse:down",this.boundHandlers.onMouseDown),this.restoreViewportChangeDetection()}updatePosition(){this.applyOverlayStyle()}updateObjectBounds(){if(this.isDestroyed)return;const t=this.target,e=this.canvas.getZoom();parseFloat(this.hostDiv.style.width);const o=parseFloat(this.hostDiv.style.height)/e;Math.abs(o-t.height)>.5&&(t.height,t.height=o,t.setCoords(),t.dirty=!0,this.canvas.requestRenderAll(),requestAnimationFrame(()=>{this.isDestroyed||(this.applyOverlayStyle(),console.log("📐 Height changed - rechecking alignment after repositioning:"))}))}letterSpacingPx(t,e){return t/1e3*e}firstStrongDir(t){return/[\u0590-\u05FF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF]/.test(t)?"rtl":"ltr"}applyOverlayStyle(){var t,o;const s=this.target,i=this.canvas;s.setCoords();const a=s.aCoords,n=i.upperCanvasEl.getBoundingClientRect(),r=window.scrollX||window.pageXOffset,l=window.scrollY||window.pageYOffset,h=i.getZoom(),c=i.viewportTransform,d=s.padding||0,g=d*(s.scaleX||1)*h,u=d*(s.scaleY||1)*h,x=e({x:a.tl.x,y:a.tl.y},c),y=n.left+r+x.x,f=n.top+l+x.y,p=s.getBoundingRect(),v=Math.round(p.width*h),m=Math.round(p.height*h);this.hostDiv.style.position="absolute",this.hostDiv.style.left=`${y}px`,this.hostDiv.style.top=`${f}px`,this.hostDiv.style.width=`${v}px`,this.hostDiv.style.height=`${m}px`,this.hostDiv.style.overflow="hidden",s.angle?(this.hostDiv.style.transform=`rotate(${s.angle}deg)`,this.hostDiv.style.transformOrigin="top left"):(this.hostDiv.style.transform="",this.hostDiv.style.transformOrigin="");const w=(null!==(t=s.fontSize)&&void 0!==t?t:16)*(s.scaleX||1)*h,b=s.lineHeight||1.16;this.textarea.style.boxSizing="border-box",this.textarea.style.width=`${v}px`,this.textarea.style.height="100%",this.textarea.style.padding=`${u}px ${g}px`;const D=(s.charSpacing||0)/1e3*w;this.textarea.style.fontSize=`${w}px`,this.textarea.style.lineHeight=String(b),this.textarea.style.fontFamily=s.fontFamily||"Arial",this.textarea.style.fontWeight=String(s.fontWeight||"normal"),this.textarea.style.fontStyle=s.fontStyle||"normal";const F=s.textAlign||"left";let S=F;const C=this.firstStrongDir(this.textarea.value||"");if(console.log("🔍 ALIGNMENT DEBUG:"),console.log(" Fabric textAlign:",F),console.log(" Fabric direction:",s.direction),console.log(" Text content:",JSON.stringify(s.text)),console.log(" Detected direction:",C),F.includes("justify"))try{S="justify","justify"===F?this.textarea.style.textAlignLast="rtl"===C?"right":"left":"justify-left"===F?"rtl"===C?(this.textarea.style.textAlignLast="right",console.log(" → Overrode justify-left to justify-right for RTL text")):this.textarea.style.textAlignLast="left":"justify-right"===F?"ltr"===C?(this.textarea.style.textAlignLast="left",console.log(" → Overrode justify-right to justify-left for LTR text")):this.textarea.style.textAlignLast="right":"justify-center"===F&&(this.textarea.style.textAlignLast="center"),this.textarea.style.textJustify="inter-word",this.textarea.style.wordSpacing="normal",this.textarea.style.textAlign="justify",this.textarea.style.textAlignLast=this.textarea.style.textAlignLast,this.textarea.style.textJustifyTrim="none",this.textarea.style.textAutospace="none",console.log(" → Applied justify alignment:",F,"with last-line:",this.textarea.style.textAlignLast)}catch(t){console.warn(" → Justify setup failed, falling back to standard alignment:",t),S=F.replace("justify-","").replace("justify","left")}else this.textarea.style.textAlignLast="auto",this.textarea.style.textJustify="auto",this.textarea.style.wordSpacing="normal",console.log(" → Applied standard alignment:",S);this.textarea.style.textAlign=S,this.textarea.style.color=(null===(o=s.fill)||void 0===o?void 0:o.toString())||"#000",this.textarea.style.letterSpacing=`${D}px`;const E=s.direction;var A,T;(this.textarea.style.direction=C||E||"ltr",this.textarea.style.fontVariant="normal",this.textarea.style.fontStretch="normal",this.textarea.style.textRendering="auto",this.textarea.style.fontKerning="normal",this.textarea.style.fontFeatureSettings="normal",this.textarea.style.fontVariationSettings="normal",this.textarea.style.margin="0",this.textarea.style.border="none",this.textarea.style.outline="none",this.textarea.style.background="transparent",this.textarea.style.overflowWrap="break-word",this.textarea.style.whiteSpace="pre-wrap",this.textarea.style.hyphens="none",console.log("🎨 FINAL TEXTAREA CSS:"),console.log(" textAlign:",this.textarea.style.textAlign),console.log(" textAlignLast:",this.textarea.style.textAlignLast),console.log(" direction:",this.textarea.style.direction),console.log(" unicodeBidi:",this.textarea.style.unicodeBidi),console.log(" width:",this.textarea.style.width),console.log(" textJustify:",this.textarea.style.textJustify),console.log(" wordSpacing:",this.textarea.style.wordSpacing),console.log(" whiteSpace:",this.textarea.style.whiteSpace),F.includes("justify"))&&(console.log("🔧 FABRIC OBJECT JUSTIFY INFO:"),console.log(" Fabric width:",s.width),console.log(" Fabric calcTextWidth:",null===(A=(T=s).calcTextWidth)||void 0===A?void 0:A.call(T)),console.log(" Fabric textAlign:",s.textAlign),console.log(" Text lines:",s.textLines));console.log("🔤 FONT PROPERTIES COMPARISON:"),console.log(" Fabric fontFamily:",s.fontFamily),console.log(" Fabric fontWeight:",s.fontWeight),console.log(" Fabric fontStyle:",s.fontStyle),console.log(" Fabric fontSize:",s.fontSize),console.log(" → Textarea fontFamily:",this.textarea.style.fontFamily),console.log(" → Textarea fontWeight:",this.textarea.style.fontWeight),console.log(" → Textarea fontStyle:",this.textarea.style.fontStyle),console.log(" → Textarea fontSize:",this.textarea.style.fontSize),console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"),this.textarea.style.webkitFontSmoothing="auto",this.textarea.style.mozOsxFontSmoothing="auto",this.textarea.style.fontSmooth="auto",this.textarea.style.textSizeAdjust="none";const M=String(s.fontWeight||"normal");("bold"===M||"700"===M||parseInt(M)>=600)&&(this.textarea.style.webkitFontSmoothing="subpixel-antialiased",this.textarea.style.mozOsxFontSmoothing="unset",console.log("🔤 Applied enhanced bold rendering for better thickness matching")),console.log("🎨 FONT SMOOTHING APPLIED:"),console.log(" webkitFontSmoothing:",this.textarea.style.webkitFontSmoothing),console.log(" mozOsxFontSmoothing:",this.textarea.style.mozOsxFontSmoothing)}debugBoundingBoxComparison(){const t=this.target,e=this.canvas,o=e.getZoom(),s=this.textarea.getBoundingClientRect(),i=this.hostDiv.getBoundingClientRect(),a=t.getBoundingRect(),n=e.upperCanvasEl.getBoundingClientRect(),r=e.viewportTransform,l=n.left+a.left*o+r[4],h=n.top+a.top*o+r[5],c=a.width*o,d=a.height*o;console.log("🔍 BOUNDING BOX COMPARISON:"),console.log("📦 Textarea Rect:",{left:Math.round(100*s.left)/100,top:Math.round(100*s.top)/100,width:Math.round(100*s.width)/100,height:Math.round(100*s.height)/100}),console.log("📦 Host Div Rect:",{left:Math.round(100*i.left)/100,top:Math.round(100*i.top)/100,width:Math.round(100*i.width)/100,height:Math.round(100*i.height)/100}),console.log("📦 Canvas Object Bounds (screen):",{left:Math.round(100*l)/100,top:Math.round(100*h)/100,width:Math.round(100*c)/100,height:Math.round(100*d)/100}),console.log("📦 Canvas Object Bounds (canvas):",a);const g={leftDiff:Math.round(100*(i.left-l))/100,topDiff:Math.round(100*(i.top-h))/100,widthDiff:Math.round(100*(i.width-c))/100,heightDiff:Math.round(100*(i.height-d))/100},u={leftDiff:Math.round(100*(s.left-l))/100,topDiff:Math.round(100*(s.top-h))/100,widthDiff:Math.round(100*(s.width-c))/100,heightDiff:Math.round(100*(s.height-d))/100};console.log("📏 Host Div vs Canvas Object Diff:",g),console.log("📏 Textarea vs Canvas Object Diff:",u);const x=Math.abs(g.leftDiff)<2&&Math.abs(g.topDiff)<2&&Math.abs(g.widthDiff)<2&&Math.abs(g.heightDiff)<2,y=Math.abs(u.leftDiff)<2&&Math.abs(u.topDiff)<2&&Math.abs(u.widthDiff)<2&&Math.abs(u.heightDiff)<2;console.log(x?"✅ Host Div ALIGNED with canvas object":"❌ Host Div MISALIGNED with canvas object"),console.log(y?"✅ Textarea ALIGNED with canvas object":"❌ Textarea MISALIGNED with canvas object"),console.log("🔍 Zoom:",o,"Viewport Transform:",r),console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")}debugTextWrapping(){const t=this.target,e=this.textarea.value;console.log("📝 TEXT WRAPPING COMPARISON:"),console.log("📄 Text Content:",`"${e}"`),console.log("📄 Text Length:",e.length);const o=e.split("\n");console.log("📄 Explicit Lines (\\n):",o.length),o.forEach((t,e)=>{console.log(` Line ${e+1}: "${t}" (${t.length} chars)`)});const s=window.getComputedStyle(this.textarea);console.log("📐 Textarea Wrapping Styles:"),console.log(" width:",s.width),console.log(" fontSize:",s.fontSize),console.log(" fontFamily:",s.fontFamily),console.log(" fontWeight:",s.fontWeight),console.log(" letterSpacing:",s.letterSpacing),console.log(" lineHeight:",s.lineHeight),console.log(" whiteSpace:",s.whiteSpace),console.log(" wordWrap:",s.wordWrap),console.log(" overflowWrap:",s.overflowWrap),console.log(" direction:",s.direction),console.log(" textAlign:",s.textAlign),console.log("📐 Fabric Text Object Properties:"),console.log(" width:",t.width),console.log(" fontSize:",t.fontSize),console.log(" fontFamily:",t.fontFamily),console.log(" fontWeight:",t.fontWeight),console.log(" charSpacing:",t.charSpacing),console.log(" lineHeight:",t.lineHeight),console.log(" direction:",t.direction),console.log(" textAlign:",t.textAlign),console.log(" scaleX:",t.scaleX),console.log(" scaleY:",t.scaleY);const i=this.target.getBoundingRect().width,a=parseFloat(window.getComputedStyle(this.textarea).width)/this.canvas.getZoom(),n=Math.abs(a-i);console.log("📏 Effective Width Comparison:"),console.log(" Textarea Effective Width:",a),console.log(" Fabric Effective Width:",i),console.log(" Width Difference:",n.toFixed(2)+"px"),console.log(n<1?"✅ Widths MATCH for wrapping":"❌ Width MISMATCH may cause different wrapping");const r=/[\u0590-\u05FF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF]/.test(e),l=/[\u0590-\u06FF]/.test(e)&&/[a-zA-Z]/.test(e);console.log("🌍 Text Direction Analysis:"),console.log(" Has RTL characters:",r),console.log(" Has mixed Bidi text:",l),console.log(" Textarea direction:",s.direction),console.log(" Fabric direction:",t.direction||"auto"),console.log(" Textarea unicodeBidi:",s.unicodeBidi);const h=this.textarea.scrollHeight,c=parseFloat(s.lineHeight)||1.2*parseFloat(s.fontSize),d=Math.round(h/c);console.log("📊 Line Count Analysis:"),console.log(" Textarea scrollHeight:",h),console.log(" Textarea lineHeight:",c),console.log(" Estimated rendered lines:",d),console.log(" Explicit line breaks:",o.length),d>o.length?(console.log("🔄 Text wrapping detected in textarea"),console.log(" Wrapped lines:",d-o.length)):console.log("📏 No text wrapping in textarea"),console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")}focusTextarea(){this.target.opacity=.01,this.target.selected=!0,this.target.isEditing=!1,this.target.set({hasControls:!0,hasBorders:!0,selectable:!0,lockMovementX:!1,lockMovementY:!1}),this.canvas.setActiveObject(this.target),this.canvas.requestRenderAll(),this.target.setCoords(),this.applyOverlayStyle(),this.textarea.focus(),this.textarea.setSelectionRange(this.textarea.value.length,this.textarea.value.length),this.canvas.setActiveObject(this.target),this.canvas.requestRenderAll()}refresh(){this.isDestroyed||this.updatePosition()}destroy(){let t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];if(!this.isDestroyed){if(this.isDestroyed=!0,this.removeEventListeners(),this.target.__overlayEditor===this&&(this.target.__overlayEditor=void 0,void 0!==this.target.__overlayOriginalOpacity&&(this.target.opacity=this.target.__overlayOriginalOpacity,delete this.target.__overlayOriginalOpacity)),this.hostDiv.parentNode&&this.hostDiv.parentNode.removeChild(this.hostDiv),t&&!this.isComposing){const t=this.textarea.value,e=this.firstStrongDir(t),o=this.target.direction||"ltr";e&&e!==o?(console.log(`🔄 Overlay Exit: Auto-detected direction change from "${o}" to "${e}"`),console.log(` Text content: "${t.substring(0,50)}..."`),this.target.set("direction",e),this.canvas.requestRenderAll(),console.log(`✅ Fabric object direction updated to: ${e}`)):console.log(`📝 Overlay Exit: Direction unchanged (${o}), text: "${t.substring(0,30)}..."`),this.onCommit&&this.onCommit(t)}else!t&&this.onCancel&&this.onCancel();setTimeout(()=>{this.canvas.upperCanvasEl.style.cursor="",this.canvas.setCursor(this.canvas.defaultCursor)},0),this.canvas.requestRenderAll()}}handleInput(){this.isComposing||this.target.text===this.textarea.value||(this.target.text=this.textarea.value,this.autoResizeTextarea(),this.target.selected=!0,this.target.isEditing=!1,this.canvas.setActiveObject(this.target),this.canvas.requestRenderAll())}autoResizeTextarea(){const t=this.textarea.scrollTop,e=parseFloat(this.hostDiv.style.height||"0");this.textarea.style.height="1px";const o=this.textarea.scrollHeight+2;Math.abs(o-e)>1?(this.textarea.style.height=`${o}px`,this.hostDiv.style.height=`${o}px`,this.updateObjectBounds()):this.textarea.style.height=this.hostDiv.style.height,this.textarea.scrollTop=t}handleKeyDown(t){"Escape"===t.key?(t.preventDefault(),this.destroy(!1)):(t.ctrlKey||t.metaKey)&&"Enter"===t.key?(t.preventDefault(),this.destroy(!0)):"Enter"!==t.key&&"Backspace"!==t.key&&"Delete"!==t.key||(requestAnimationFrame(()=>{this.isDestroyed||this.autoResizeTextarea()}),setTimeout(()=>{this.isDestroyed||this.autoResizeTextarea()},10))}handleFocus(){}handleBlur(){this.isComposing||this.destroy(!0)}handleCompositionStart(){this.isComposing=!0}handleCompositionEnd(){this.isComposing=!1,this.handleInput()}handleAfterRender(){this.refresh()}handleMouseWheel(){this.refresh()}handleMouseDown(t){t.target!==this.target&&this.destroy(!0)}setupViewportChangeDetection(){this.canvas.__originalSetZoom=this.canvas.setZoom,this.canvas.__originalSetViewportTransform=this.canvas.setViewportTransform,this.canvas.__overlayEditor=this;const t=this.canvas.setZoom.bind(this.canvas);this.canvas.setZoom=e=>{const o=t(e);return this.canvas.__overlayEditor&&!this.isDestroyed&&this.refresh(),o};const e=this.canvas.setViewportTransform.bind(this.canvas);this.canvas.setViewportTransform=t=>{const o=e(t);return this.canvas.__overlayEditor&&!this.isDestroyed&&this.refresh(),o}}restoreViewportChangeDetection(){this.canvas.__originalSetZoom&&(this.canvas.setZoom=this.canvas.__originalSetZoom,delete this.canvas.__originalSetZoom),this.canvas.__originalSetViewportTransform&&(this.canvas.setViewportTransform=this.canvas.__originalSetViewportTransform,delete this.canvas.__originalSetViewportTransform),delete this.canvas.__overlayEditor}}function s(t,e,s){e.__overlayEditor&&e.__overlayEditor.destroy(!1),e.__overlayOriginalOpacity=e.opacity;const i=new o({canvas:t,target:e,onCommit:null==s?void 0:s.onCommit,onCancel:null==s?void 0:s.onCancel});return e.__overlayEditor=i,i}export{o as OverlayEditor,s as enterTextOverlayEdit};
1
+ import{defineProperty as t}from"../../_virtual/_rollupPluginBabelHelpers.min.mjs";import{transformPoint as e}from"../util/misc/matrix.min.mjs";class o{constructor(e){t(this,"canvas",void 0),t(this,"target",void 0),t(this,"container",void 0),t(this,"textarea",void 0),t(this,"hostDiv",void 0),t(this,"isDestroyed",!1),t(this,"isComposing",!1),t(this,"lastText",void 0),t(this,"onCommit",void 0),t(this,"onCancel",void 0),t(this,"boundHandlers",{onInput:this.handleInput.bind(this),onKeyDown:this.handleKeyDown.bind(this),onBlur:this.handleBlur.bind(this),onCompositionStart:this.handleCompositionStart.bind(this),onCompositionEnd:this.handleCompositionEnd.bind(this),onAfterRender:this.handleAfterRender.bind(this),onMouseWheel:this.handleMouseWheel.bind(this),onFocus:this.handleFocus.bind(this),onMouseDown:this.handleMouseDown.bind(this)}),this.canvas=e.canvas,this.target=e.target,this.onCommit=e.onCommit,this.onCancel=e.onCancel,this.lastText=this.target.text||"",this.container=this.getCanvasContainer(),this.createOverlayDOM(),this.attachEventListeners(),this.refresh(),this.focusTextarea()}getCanvasContainer(){const t=this.canvas.upperCanvasEl.parentElement;if(!t)throw new Error("Canvas must be mounted in DOM to use overlay editing");return t.style.position="relative",t}createOverlayDOM(){this.hostDiv=document.createElement("div"),this.hostDiv.style.position="absolute",this.hostDiv.style.pointerEvents="none",this.hostDiv.style.zIndex="1000",this.hostDiv.style.transformOrigin="left top",this.textarea=document.createElement("textarea"),this.textarea.style.position="absolute",this.textarea.style.left="0",this.textarea.style.top="0",this.textarea.style.margin="0",this.textarea.style.resize="none",this.textarea.style.pointerEvents="auto";const t=/[\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF]/.test(this.target.text||""),e=/[a-zA-Z]/.test(this.target.text||""),o="ltr"===this.target.direction;this.textarea.style.unicodeBidi=t&&e&&o||t&&o?"embed":"plaintext",this.textarea.style.caretColor="auto",this.textarea.style.border="none",this.textarea.style.padding="0",this.textarea.style.background="transparent",this.textarea.style.outline="none",this.textarea.style.overflow="hidden",this.textarea.style.whiteSpace="pre-wrap",this.textarea.style.wordBreak="normal",this.textarea.style.overflowWrap="break-word",this.textarea.style.userSelect="text",this.textarea.style.textTransform="none",this.textarea.style.opacity="1",this.textarea.value=this.target.text||"",this.hostDiv.appendChild(this.textarea),document.body.appendChild(this.hostDiv)}attachEventListeners(){this.textarea.addEventListener("input",this.boundHandlers.onInput),this.textarea.addEventListener("keydown",this.boundHandlers.onKeyDown),this.textarea.addEventListener("blur",this.boundHandlers.onBlur),this.textarea.addEventListener("compositionstart",this.boundHandlers.onCompositionStart),this.textarea.addEventListener("compositionend",this.boundHandlers.onCompositionEnd),this.textarea.addEventListener("focus",this.boundHandlers.onFocus),this.canvas.on("after:render",this.boundHandlers.onAfterRender),this.canvas.on("mouse:wheel",this.boundHandlers.onMouseWheel),this.canvas.on("mouse:down",this.boundHandlers.onMouseDown),this.setupViewportChangeDetection()}removeEventListeners(){this.textarea.removeEventListener("input",this.boundHandlers.onInput),this.textarea.removeEventListener("keydown",this.boundHandlers.onKeyDown),this.textarea.removeEventListener("blur",this.boundHandlers.onBlur),this.textarea.removeEventListener("compositionstart",this.boundHandlers.onCompositionStart),this.textarea.removeEventListener("compositionend",this.boundHandlers.onCompositionEnd),this.textarea.removeEventListener("focus",this.boundHandlers.onFocus),this.canvas.off("after:render",this.boundHandlers.onAfterRender),this.canvas.off("mouse:wheel",this.boundHandlers.onMouseWheel),this.canvas.off("mouse:down",this.boundHandlers.onMouseDown),this.restoreViewportChangeDetection()}updatePosition(){this.applyOverlayStyle()}updateObjectBounds(){if(this.isDestroyed)return;const t=this.target,e=this.canvas.getZoom();parseFloat(this.hostDiv.style.width);const o=parseFloat(this.hostDiv.style.height)/e;Math.abs(o-t.height)>.5&&(t.height,t.height=o,t.setCoords(),t.dirty=!0,this.canvas.requestRenderAll(),requestAnimationFrame(()=>{this.isDestroyed||(this.applyOverlayStyle(),console.log("📐 Height changed - rechecking alignment after repositioning:"))}))}letterSpacingPx(t,e){return t/1e3*e}firstStrongDir(t){return/[\u0590-\u05FF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF]/.test(t)?"rtl":"ltr"}applyOverlayStyle(){var t,o;const s=this.target,i=this.canvas;s.setCoords();const a=s.aCoords,n=i.upperCanvasEl.getBoundingClientRect(),r=window.scrollX||window.pageXOffset,l=window.scrollY||window.pageYOffset,h=i.getZoom(),c=i.viewportTransform,d=s.padding||0,g=d*(s.scaleX||1)*h,u=d*(s.scaleY||1)*h,x=e({x:a.tl.x,y:a.tl.y},c),y=n.left+r+x.x,f=n.top+l+x.y,p=s.getBoundingRect(),v=Math.round(p.width*h),m=Math.round(p.height*h);this.hostDiv.style.position="absolute",this.hostDiv.style.left=`${y}px`,this.hostDiv.style.top=`${f}px`,this.hostDiv.style.width=`${v}px`,this.hostDiv.style.height=`${m}px`,this.hostDiv.style.overflow="hidden",s.angle?(this.hostDiv.style.transform=`rotate(${s.angle}deg)`,this.hostDiv.style.transformOrigin="top left"):(this.hostDiv.style.transform="",this.hostDiv.style.transformOrigin="");const w=(null!==(t=s.fontSize)&&void 0!==t?t:16)*(s.scaleX||1)*h,b=s.lineHeight||1.16;this.textarea.style.boxSizing="border-box",this.textarea.style.width=`${v}px`,this.textarea.style.height="100%",this.textarea.style.padding=`${u}px ${g}px`;const D=(s.charSpacing||0)/1e3*w;!1!==s.dirty&&s.initDimensions&&(console.log("🔧 Ensuring text object is properly initialized before overlay editing"),s.initDimensions()),this.textarea.style.fontSize=`${w}px`,this.textarea.style.lineHeight=String(b),this.textarea.style.fontFamily=s.fontFamily||"Arial",this.textarea.style.fontWeight=String(s.fontWeight||"normal"),this.textarea.style.fontStyle=s.fontStyle||"normal";const F=s.textAlign||"left";let S=F;const C=this.firstStrongDir(this.textarea.value||"");if(console.log("🔍 ALIGNMENT DEBUG:"),console.log(" Fabric textAlign:",F),console.log(" Fabric direction:",s.direction),console.log(" Text content:",JSON.stringify(s.text)),console.log(" Detected direction:",C),F.includes("justify"))try{S="justify","justify"===F?this.textarea.style.textAlignLast="rtl"===C?"right":"left":"justify-left"===F?"rtl"===C?(this.textarea.style.textAlignLast="right",console.log(" → Overrode justify-left to justify-right for RTL text")):this.textarea.style.textAlignLast="left":"justify-right"===F?"ltr"===C?(this.textarea.style.textAlignLast="left",console.log(" → Overrode justify-right to justify-left for LTR text")):this.textarea.style.textAlignLast="right":"justify-center"===F&&(this.textarea.style.textAlignLast="center"),this.textarea.style.textJustify="inter-word",this.textarea.style.wordSpacing="normal",this.textarea.style.textAlign="justify",this.textarea.style.textAlignLast=this.textarea.style.textAlignLast,this.textarea.style.textJustifyTrim="none",this.textarea.style.textAutospace="none",console.log(" → Applied justify alignment:",F,"with last-line:",this.textarea.style.textAlignLast)}catch(t){console.warn(" → Justify setup failed, falling back to standard alignment:",t),S=F.replace("justify-","").replace("justify","left")}else this.textarea.style.textAlignLast="auto",this.textarea.style.textJustify="auto",this.textarea.style.wordSpacing="normal",console.log(" → Applied standard alignment:",S);this.textarea.style.textAlign=S,this.textarea.style.color=(null===(o=s.fill)||void 0===o?void 0:o.toString())||"#000",this.textarea.style.letterSpacing=`${D}px`;const E=s.direction;var A,T;(this.textarea.style.direction=C||E||"ltr",this.textarea.style.fontVariant="normal",this.textarea.style.fontStretch="normal",this.textarea.style.textRendering="auto",this.textarea.style.fontKerning="normal",this.textarea.style.fontFeatureSettings="normal",this.textarea.style.fontVariationSettings="normal",this.textarea.style.margin="0",this.textarea.style.border="none",this.textarea.style.outline="none",this.textarea.style.background="transparent",this.textarea.style.overflowWrap="break-word",this.textarea.style.whiteSpace="pre-wrap",this.textarea.style.hyphens="none",console.log("🎨 FINAL TEXTAREA CSS:"),console.log(" textAlign:",this.textarea.style.textAlign),console.log(" textAlignLast:",this.textarea.style.textAlignLast),console.log(" direction:",this.textarea.style.direction),console.log(" unicodeBidi:",this.textarea.style.unicodeBidi),console.log(" width:",this.textarea.style.width),console.log(" textJustify:",this.textarea.style.textJustify),console.log(" wordSpacing:",this.textarea.style.wordSpacing),console.log(" whiteSpace:",this.textarea.style.whiteSpace),F.includes("justify"))&&(console.log("🔧 FABRIC OBJECT JUSTIFY INFO:"),console.log(" Fabric width:",s.width),console.log(" Fabric calcTextWidth:",null===(A=(T=s).calcTextWidth)||void 0===A?void 0:A.call(T)),console.log(" Fabric textAlign:",s.textAlign),console.log(" Text lines:",s.textLines));console.log("🔤 FONT PROPERTIES COMPARISON:"),console.log(" Fabric fontFamily:",s.fontFamily),console.log(" Fabric fontWeight:",s.fontWeight),console.log(" Fabric fontStyle:",s.fontStyle),console.log(" Fabric fontSize:",s.fontSize),console.log(" → Textarea fontFamily:",this.textarea.style.fontFamily),console.log(" → Textarea fontWeight:",this.textarea.style.fontWeight),console.log(" → Textarea fontStyle:",this.textarea.style.fontStyle),console.log(" → Textarea fontSize:",this.textarea.style.fontSize),console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"),this.textarea.style.webkitFontSmoothing="auto",this.textarea.style.mozOsxFontSmoothing="auto",this.textarea.style.fontSmooth="auto",this.textarea.style.textSizeAdjust="none";const M=String(s.fontWeight||"normal");("bold"===M||"700"===M||parseInt(M)>=600)&&(this.textarea.style.webkitFontSmoothing="subpixel-antialiased",this.textarea.style.mozOsxFontSmoothing="unset",console.log("🔤 Applied enhanced bold rendering for better thickness matching")),console.log("🎨 FONT SMOOTHING APPLIED:"),console.log(" webkitFontSmoothing:",this.textarea.style.webkitFontSmoothing),console.log(" mozOsxFontSmoothing:",this.textarea.style.mozOsxFontSmoothing)}debugBoundingBoxComparison(){const t=this.target,e=this.canvas,o=e.getZoom(),s=this.textarea.getBoundingClientRect(),i=this.hostDiv.getBoundingClientRect(),a=t.getBoundingRect(),n=e.upperCanvasEl.getBoundingClientRect(),r=e.viewportTransform,l=n.left+a.left*o+r[4],h=n.top+a.top*o+r[5],c=a.width*o,d=a.height*o;console.log("🔍 BOUNDING BOX COMPARISON:"),console.log("📦 Textarea Rect:",{left:Math.round(100*s.left)/100,top:Math.round(100*s.top)/100,width:Math.round(100*s.width)/100,height:Math.round(100*s.height)/100}),console.log("📦 Host Div Rect:",{left:Math.round(100*i.left)/100,top:Math.round(100*i.top)/100,width:Math.round(100*i.width)/100,height:Math.round(100*i.height)/100}),console.log("📦 Canvas Object Bounds (screen):",{left:Math.round(100*l)/100,top:Math.round(100*h)/100,width:Math.round(100*c)/100,height:Math.round(100*d)/100}),console.log("📦 Canvas Object Bounds (canvas):",a);const g={leftDiff:Math.round(100*(i.left-l))/100,topDiff:Math.round(100*(i.top-h))/100,widthDiff:Math.round(100*(i.width-c))/100,heightDiff:Math.round(100*(i.height-d))/100},u={leftDiff:Math.round(100*(s.left-l))/100,topDiff:Math.round(100*(s.top-h))/100,widthDiff:Math.round(100*(s.width-c))/100,heightDiff:Math.round(100*(s.height-d))/100};console.log("📏 Host Div vs Canvas Object Diff:",g),console.log("📏 Textarea vs Canvas Object Diff:",u);const x=Math.abs(g.leftDiff)<2&&Math.abs(g.topDiff)<2&&Math.abs(g.widthDiff)<2&&Math.abs(g.heightDiff)<2,y=Math.abs(u.leftDiff)<2&&Math.abs(u.topDiff)<2&&Math.abs(u.widthDiff)<2&&Math.abs(u.heightDiff)<2;console.log(x?"✅ Host Div ALIGNED with canvas object":"❌ Host Div MISALIGNED with canvas object"),console.log(y?"✅ Textarea ALIGNED with canvas object":"❌ Textarea MISALIGNED with canvas object"),console.log("🔍 Zoom:",o,"Viewport Transform:",r),console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")}debugTextWrapping(){const t=this.target,e=this.textarea.value;console.log("📝 TEXT WRAPPING COMPARISON:"),console.log("📄 Text Content:",`"${e}"`),console.log("📄 Text Length:",e.length);const o=e.split("\n");console.log("📄 Explicit Lines (\\n):",o.length),o.forEach((t,e)=>{console.log(` Line ${e+1}: "${t}" (${t.length} chars)`)});const s=window.getComputedStyle(this.textarea);console.log("📐 Textarea Wrapping Styles:"),console.log(" width:",s.width),console.log(" fontSize:",s.fontSize),console.log(" fontFamily:",s.fontFamily),console.log(" fontWeight:",s.fontWeight),console.log(" letterSpacing:",s.letterSpacing),console.log(" lineHeight:",s.lineHeight),console.log(" whiteSpace:",s.whiteSpace),console.log(" wordWrap:",s.wordWrap),console.log(" overflowWrap:",s.overflowWrap),console.log(" direction:",s.direction),console.log(" textAlign:",s.textAlign),console.log("📐 Fabric Text Object Properties:"),console.log(" width:",t.width),console.log(" fontSize:",t.fontSize),console.log(" fontFamily:",t.fontFamily),console.log(" fontWeight:",t.fontWeight),console.log(" charSpacing:",t.charSpacing),console.log(" lineHeight:",t.lineHeight),console.log(" direction:",t.direction),console.log(" textAlign:",t.textAlign),console.log(" scaleX:",t.scaleX),console.log(" scaleY:",t.scaleY);const i=this.target.getBoundingRect().width,a=parseFloat(window.getComputedStyle(this.textarea).width)/this.canvas.getZoom(),n=Math.abs(a-i);console.log("📏 Effective Width Comparison:"),console.log(" Textarea Effective Width:",a),console.log(" Fabric Effective Width:",i),console.log(" Width Difference:",n.toFixed(2)+"px"),console.log(n<1?"✅ Widths MATCH for wrapping":"❌ Width MISMATCH may cause different wrapping");const r=/[\u0590-\u05FF\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF]/.test(e),l=/[\u0590-\u06FF]/.test(e)&&/[a-zA-Z]/.test(e);console.log("🌍 Text Direction Analysis:"),console.log(" Has RTL characters:",r),console.log(" Has mixed Bidi text:",l),console.log(" Textarea direction:",s.direction),console.log(" Fabric direction:",t.direction||"auto"),console.log(" Textarea unicodeBidi:",s.unicodeBidi);const h=this.textarea.scrollHeight,c=parseFloat(s.lineHeight)||1.2*parseFloat(s.fontSize),d=Math.round(h/c);console.log("📊 Line Count Analysis:"),console.log(" Textarea scrollHeight:",h),console.log(" Textarea lineHeight:",c),console.log(" Estimated rendered lines:",d),console.log(" Explicit line breaks:",o.length),d>o.length?(console.log("🔄 Text wrapping detected in textarea"),console.log(" Wrapped lines:",d-o.length)):console.log("📏 No text wrapping in textarea"),console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")}focusTextarea(){this.target.opacity=.01,this.target.selected=!0,this.target.isEditing=!1,this.target.set({hasControls:!0,hasBorders:!0,selectable:!0,lockMovementX:!1,lockMovementY:!1}),this.canvas.setActiveObject(this.target),this.canvas.requestRenderAll(),this.target.setCoords(),this.applyOverlayStyle(),this.textarea.focus(),this.textarea.setSelectionRange(this.textarea.value.length,this.textarea.value.length),this.canvas.setActiveObject(this.target),this.canvas.requestRenderAll()}refresh(){this.isDestroyed||this.updatePosition()}destroy(){let t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];if(!this.isDestroyed){if(this.isDestroyed=!0,this.removeEventListeners(),this.target.__overlayEditor===this&&(this.target.__overlayEditor=void 0,void 0!==this.target.__overlayOriginalOpacity&&(this.target.opacity=this.target.__overlayOriginalOpacity,delete this.target.__overlayOriginalOpacity)),this.hostDiv.parentNode&&this.hostDiv.parentNode.removeChild(this.hostDiv),t&&!this.isComposing){const t=this.textarea.value,e=this.firstStrongDir(t),o=this.target.direction||"ltr";e&&e!==o?(console.log(`🔄 Overlay Exit: Auto-detected direction change from "${o}" to "${e}"`),console.log(` Text content: "${t.substring(0,50)}..."`),this.target.set("direction",e),this.canvas.requestRenderAll(),console.log(`✅ Fabric object direction updated to: ${e}`)):console.log(`📝 Overlay Exit: Direction unchanged (${o}), text: "${t.substring(0,30)}..."`),this.onCommit&&this.onCommit(t)}else!t&&this.onCancel&&this.onCancel();setTimeout(()=>{this.canvas.upperCanvasEl.style.cursor="",this.canvas.setCursor(this.canvas.defaultCursor)},0),this.canvas.requestRenderAll()}}handleInput(){this.isComposing||this.target.text===this.textarea.value||(this.target.text=this.textarea.value,this.autoResizeTextarea(),this.target.selected=!0,this.target.isEditing=!1,this.canvas.setActiveObject(this.target),this.canvas.requestRenderAll())}autoResizeTextarea(){const t=this.textarea.scrollTop,e=parseFloat(this.hostDiv.style.height||"0");this.textarea.style.height="1px";const o=this.textarea.scrollHeight+2;Math.abs(o-e)>1?(this.textarea.style.height=`${o}px`,this.hostDiv.style.height=`${o}px`,this.updateObjectBounds()):this.textarea.style.height=this.hostDiv.style.height,this.textarea.scrollTop=t}handleKeyDown(t){"Escape"===t.key?(t.preventDefault(),this.destroy(!1)):(t.ctrlKey||t.metaKey)&&"Enter"===t.key?(t.preventDefault(),this.destroy(!0)):"Enter"!==t.key&&"Backspace"!==t.key&&"Delete"!==t.key||(requestAnimationFrame(()=>{this.isDestroyed||this.autoResizeTextarea()}),setTimeout(()=>{this.isDestroyed||this.autoResizeTextarea()},10))}handleFocus(){}handleBlur(){this.isComposing||this.destroy(!0)}handleCompositionStart(){this.isComposing=!0}handleCompositionEnd(){this.isComposing=!1,this.handleInput()}handleAfterRender(){this.refresh()}handleMouseWheel(){this.refresh()}handleMouseDown(t){t.target!==this.target&&this.destroy(!0)}setupViewportChangeDetection(){this.canvas.__originalSetZoom=this.canvas.setZoom,this.canvas.__originalSetViewportTransform=this.canvas.setViewportTransform,this.canvas.__overlayEditor=this;const t=this.canvas.setZoom.bind(this.canvas);this.canvas.setZoom=e=>{const o=t(e);return this.canvas.__overlayEditor&&!this.isDestroyed&&this.refresh(),o};const e=this.canvas.setViewportTransform.bind(this.canvas);this.canvas.setViewportTransform=t=>{const o=e(t);return this.canvas.__overlayEditor&&!this.isDestroyed&&this.refresh(),o}}restoreViewportChangeDetection(){this.canvas.__originalSetZoom&&(this.canvas.setZoom=this.canvas.__originalSetZoom,delete this.canvas.__originalSetZoom),this.canvas.__originalSetViewportTransform&&(this.canvas.setViewportTransform=this.canvas.__originalSetViewportTransform,delete this.canvas.__originalSetViewportTransform),delete this.canvas.__overlayEditor}}function s(t,e,s){e.__overlayEditor&&e.__overlayEditor.destroy(!1),e.__overlayOriginalOpacity=e.opacity;const i=new o({canvas:t,target:e,onCommit:null==s?void 0:s.onCommit,onCancel:null==s?void 0:s.onCancel});return e.__overlayEditor=i,i}export{o as OverlayEditor,s as enterTextOverlayEdit};
2
2
  //# sourceMappingURL=overlayEditor.min.mjs.map