fabric 5.3.0 → 6.0.0-beta1

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 (533) hide show
  1. package/.babelrc +11 -0
  2. package/.babelrcAlt +19 -0
  3. package/.browserslistrc +5 -0
  4. package/.eslintrc.js +43 -0
  5. package/.eslintrc.json +38 -46
  6. package/.eslintrc_tests +12 -0
  7. package/.gitattributes +2 -0
  8. package/.prettierignore +16 -0
  9. package/.prettierrc +4 -0
  10. package/CHANGELOG.md +491 -189
  11. package/CONTRIBUTING.md +226 -58
  12. package/README.md +188 -239
  13. package/bower.json +1 -3
  14. package/dist/fabric.d.ts +1021 -0
  15. package/dist/index.cjs +28027 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.min.js +1 -0
  19. package/dist/index.mjs +27964 -0
  20. package/dist/index.mjs.map +1 -0
  21. package/dist/index.node.cjs +28092 -0
  22. package/dist/index.node.cjs.map +1 -0
  23. package/dist/index.node.d.ts +15 -0
  24. package/dist/index.node.mjs +28035 -0
  25. package/dist/index.node.mjs.map +1 -0
  26. package/dist/src/Collection.d.ts +126 -0
  27. package/dist/src/CommonMethods.d.ts +32 -0
  28. package/dist/src/EventTypeDefs.d.ts +226 -0
  29. package/dist/src/Intersection.d.ts +100 -0
  30. package/dist/src/Observable.d.ts +54 -0
  31. package/dist/src/Pattern.d.ts +104 -0
  32. package/dist/src/Point.d.ts +232 -0
  33. package/dist/src/Shadow.d.ts +87 -0
  34. package/dist/src/brushes/BaseBrush.d.ts +105 -0
  35. package/dist/src/brushes/CircleBrush.d.ts +49 -0
  36. package/dist/src/brushes/PatternBrush.d.ts +23 -0
  37. package/dist/src/brushes/PencilBrush.d.ts +92 -0
  38. package/dist/src/brushes/SprayBrush.d.ts +79 -0
  39. package/dist/src/cache.d.ts +46 -0
  40. package/dist/src/canvas/Canvas.d.ts +567 -0
  41. package/dist/src/canvas/SelectableCanvas.d.ts +723 -0
  42. package/dist/src/canvas/StaticCanvas.d.ts +751 -0
  43. package/dist/src/canvas/TextEditingManager.d.ts +17 -0
  44. package/dist/src/canvas/canvas_gestures.mixin.d.ts +2 -0
  45. package/dist/src/color/Color.d.ts +174 -0
  46. package/dist/src/color/color_map.d.ts +155 -0
  47. package/dist/src/color/constants.d.ts +22 -0
  48. package/dist/src/color/util.d.ts +12 -0
  49. package/dist/src/config.d.ts +115 -0
  50. package/dist/src/constants.d.ts +14 -0
  51. package/dist/src/controls/Control.d.ts +228 -0
  52. package/dist/src/controls/changeWidth.d.ts +13 -0
  53. package/dist/src/controls/controls.render.d.ts +29 -0
  54. package/dist/src/controls/default_controls.d.ts +50 -0
  55. package/dist/src/controls/drag.d.ts +12 -0
  56. package/dist/src/controls/polyControl.d.ts +5 -0
  57. package/dist/src/controls/rotate.d.ts +12 -0
  58. package/dist/src/controls/scale.d.ts +47 -0
  59. package/dist/src/controls/scaleSkew.d.ts +39 -0
  60. package/dist/src/controls/skew.d.ts +33 -0
  61. package/dist/src/controls/util.d.ts +41 -0
  62. package/dist/src/controls/wrapWithFireEvent.d.ts +8 -0
  63. package/dist/src/controls/wrapWithFixedAnchor.d.ts +9 -0
  64. package/dist/src/env/browser.d.ts +3 -0
  65. package/dist/src/env/index.d.ts +7 -0
  66. package/dist/src/env/node.d.ts +3 -0
  67. package/dist/src/env/types.d.ts +15 -0
  68. package/dist/src/filters/BaseFilter.d.ts +156 -0
  69. package/dist/src/filters/BlendColor.d.ts +80 -0
  70. package/dist/src/filters/BlendImage.d.ts +90 -0
  71. package/dist/src/filters/Blur.d.ts +50 -0
  72. package/dist/src/filters/Boilerplate.d.ts +46 -0
  73. package/dist/src/filters/Brightness.d.ts +46 -0
  74. package/dist/src/filters/Canvas2dFilterBackend.d.ts +27 -0
  75. package/dist/src/filters/ColorMatrix.d.ts +62 -0
  76. package/dist/src/filters/ColorMatrixFilters.d.ts +1169 -0
  77. package/dist/src/filters/Composed.d.ts +47 -0
  78. package/dist/src/filters/Contrast.d.ts +44 -0
  79. package/dist/src/filters/Convolute.d.ts +82 -0
  80. package/dist/src/filters/FilterBackend.d.ts +14 -0
  81. package/dist/src/filters/Gamma.d.ts +53 -0
  82. package/dist/src/filters/Grayscale.d.ts +45 -0
  83. package/dist/src/filters/HueRotation.d.ts +24 -0
  84. package/dist/src/filters/Invert.d.ts +54 -0
  85. package/dist/src/filters/Noise.d.ts +53 -0
  86. package/dist/src/filters/Pixelate.d.ts +43 -0
  87. package/dist/src/filters/RemoveColor.d.ts +62 -0
  88. package/dist/src/filters/Resize.d.ts +134 -0
  89. package/dist/src/filters/Saturation.d.ts +47 -0
  90. package/dist/src/filters/Vibrance.d.ts +47 -0
  91. package/dist/src/filters/WebGLFilterBackend.d.ts +126 -0
  92. package/dist/src/filters/WebGLProbe.d.ts +28 -0
  93. package/dist/src/filters/typedefs.d.ts +49 -0
  94. package/dist/src/gradient/Gradient.d.ts +149 -0
  95. package/dist/src/gradient/constants.d.ts +15 -0
  96. package/dist/src/gradient/parser/index.d.ts +4 -0
  97. package/dist/src/gradient/parser/misc.d.ts +4 -0
  98. package/dist/src/gradient/parser/parseColorStops.d.ts +6 -0
  99. package/dist/src/gradient/parser/parseCoords.d.ts +17 -0
  100. package/dist/src/gradient/typedefs.d.ts +93 -0
  101. package/dist/src/mixins/eraser_brush.mixin.d.ts +2 -0
  102. package/dist/src/mixins/stateful.mixin.d.ts +18 -0
  103. package/dist/src/parkinglot/canvas_animation.mixin.d.ts +2 -0
  104. package/dist/src/parkinglot/straighten.d.ts +2 -0
  105. package/dist/src/parser/applyViewboxTransform.d.ts +5 -0
  106. package/dist/src/parser/attributes.d.ts +6 -0
  107. package/dist/src/parser/constants.d.ts +54 -0
  108. package/dist/src/parser/doesSomeParentMatch.d.ts +2 -0
  109. package/dist/src/parser/elementById.d.ts +6 -0
  110. package/dist/src/parser/elementMatchesRule.d.ts +5 -0
  111. package/dist/src/parser/elements_parser.d.ts +3 -0
  112. package/dist/src/parser/getCSSRules.d.ts +7 -0
  113. package/dist/src/parser/getGlobalStylesForElement.d.ts +5 -0
  114. package/dist/src/parser/getGradientDefs.d.ts +7 -0
  115. package/dist/src/parser/getMultipleNodes.d.ts +2 -0
  116. package/dist/src/parser/getSvgRegex.d.ts +2 -0
  117. package/dist/src/parser/hasAncestorWithNodeName.d.ts +2 -0
  118. package/dist/src/parser/loadSVGFromString.d.ts +12 -0
  119. package/dist/src/parser/loadSVGFromURL.d.ts +13 -0
  120. package/dist/src/parser/normalizeAttr.d.ts +2 -0
  121. package/dist/src/parser/normalizeValue.d.ts +2 -0
  122. package/dist/src/parser/parseAttributes.d.ts +9 -0
  123. package/dist/src/parser/parseElements.d.ts +11 -0
  124. package/dist/src/parser/parseFontDeclaration.d.ts +10 -0
  125. package/dist/src/parser/parsePointsAttribute.d.ts +12 -0
  126. package/dist/src/parser/parseSVGDocument.d.ts +15 -0
  127. package/dist/src/parser/parseStyleAttribute.d.ts +9 -0
  128. package/dist/src/parser/parseStyleObject.d.ts +2 -0
  129. package/dist/src/parser/parseStyleString.d.ts +2 -0
  130. package/dist/src/parser/parseTransformAttribute.d.ts +10 -0
  131. package/dist/src/parser/parseUseDirectives.d.ts +2 -0
  132. package/dist/src/parser/percent.d.ts +9 -0
  133. package/dist/src/parser/recursivelyParseGradientsXlink.d.ts +2 -0
  134. package/dist/src/parser/rotateMatrix.d.ts +2 -0
  135. package/dist/src/parser/scaleMatrix.d.ts +2 -0
  136. package/dist/src/parser/selectorMatches.d.ts +2 -0
  137. package/dist/src/parser/setStrokeFillOpacity.d.ts +6 -0
  138. package/dist/src/parser/skewMatrix.d.ts +2 -0
  139. package/dist/src/parser/translateMatrix.d.ts +2 -0
  140. package/dist/src/shapes/ActiveSelection.d.ts +66 -0
  141. package/dist/src/shapes/Circle.d.ts +80 -0
  142. package/dist/src/shapes/Ellipse.d.ts +74 -0
  143. package/dist/src/shapes/Group.d.ts +381 -0
  144. package/dist/src/shapes/IText/DraggableTextDelegate.d.ts +78 -0
  145. package/dist/src/shapes/IText/IText.d.ts +322 -0
  146. package/dist/src/shapes/IText/ITextBehavior.d.ts +285 -0
  147. package/dist/src/shapes/IText/ITextClickBehavior.d.ts +81 -0
  148. package/dist/src/shapes/IText/ITextKeyBehavior.d.ts +193 -0
  149. package/dist/src/shapes/IText/constants.d.ts +13 -0
  150. package/dist/src/shapes/Image.d.ts +283 -0
  151. package/dist/src/shapes/Line.d.ts +117 -0
  152. package/dist/src/shapes/Object/AnimatableObject.d.ts +33 -0
  153. package/dist/src/shapes/Object/FabricObject.d.ts +9 -0
  154. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.d.ts +74 -0
  155. package/dist/src/shapes/Object/InteractiveObject.d.ts +282 -0
  156. package/dist/src/shapes/Object/Object.d.ts +906 -0
  157. package/dist/src/shapes/Object/ObjectGeometry.d.ts +343 -0
  158. package/dist/src/shapes/Object/ObjectOrigin.d.ts +178 -0
  159. package/dist/src/shapes/Object/StackedObject.d.ts +62 -0
  160. package/dist/src/shapes/Object/defaultValues.d.ts +77 -0
  161. package/dist/src/shapes/Path.d.ts +123 -0
  162. package/dist/src/shapes/Polygon.d.ts +7 -0
  163. package/dist/src/shapes/Polyline.d.ts +130 -0
  164. package/dist/src/shapes/Rect.d.ts +62 -0
  165. package/dist/src/shapes/Text/StyledText.d.ts +118 -0
  166. package/dist/src/shapes/Text/Text.d.ts +656 -0
  167. package/dist/src/shapes/Text/TextSVGExportMixin.d.ts +32 -0
  168. package/dist/src/shapes/Textbox.d.ts +170 -0
  169. package/dist/src/shapes/Triangle.d.ts +17 -0
  170. package/dist/src/typedefs.d.ts +93 -0
  171. package/dist/src/util/animation/AnimationBase.d.ts +54 -0
  172. package/dist/src/util/animation/AnimationFrameProvider.d.ts +8 -0
  173. package/dist/src/util/animation/AnimationRegistry.d.ts +29 -0
  174. package/dist/src/util/animation/ArrayAnimation.d.ts +10 -0
  175. package/dist/src/util/animation/ColorAnimation.d.ts +11 -0
  176. package/dist/src/util/animation/ValueAnimation.d.ts +10 -0
  177. package/dist/src/util/animation/animate.d.ts +37 -0
  178. package/dist/src/util/animation/easing.d.ts +130 -0
  179. package/dist/src/util/animation/types.d.ts +87 -0
  180. package/dist/src/util/applyMixins.d.ts +8 -0
  181. package/dist/src/util/class_registry.d.ts +14 -0
  182. package/dist/src/util/dom_event.d.ts +5 -0
  183. package/dist/src/util/dom_misc.d.ts +40 -0
  184. package/dist/src/util/dom_request.d.ts +14 -0
  185. package/dist/src/util/dom_style.d.ts +7 -0
  186. package/dist/src/util/fireEvent.d.ts +3 -0
  187. package/dist/src/util/internals/cloneDeep.d.ts +2 -0
  188. package/dist/src/util/internals/getRandomInt.d.ts +8 -0
  189. package/dist/src/util/internals/ifNaN.d.ts +8 -0
  190. package/dist/src/util/internals/index.d.ts +4 -0
  191. package/dist/src/util/internals/removeFromArray.d.ts +9 -0
  192. package/dist/src/util/internals/uid.d.ts +2 -0
  193. package/dist/src/util/lang_class.d.ts +12 -0
  194. package/dist/src/util/lang_string.d.ts +22 -0
  195. package/dist/src/util/misc/boundingBoxFromPoints.d.ts +9 -0
  196. package/dist/src/util/misc/capValue.d.ts +2 -0
  197. package/dist/src/util/misc/cos.d.ts +10 -0
  198. package/dist/src/util/misc/dom.d.ts +28 -0
  199. package/dist/src/util/misc/findScaleTo.d.ts +29 -0
  200. package/dist/src/util/misc/groupSVGElements.d.ts +9 -0
  201. package/dist/src/util/misc/isTransparent.d.ts +11 -0
  202. package/dist/src/util/misc/matrix.d.ts +92 -0
  203. package/dist/src/util/misc/mergeClipPaths.d.ts +23 -0
  204. package/dist/src/util/misc/objectEnlive.d.ts +56 -0
  205. package/dist/src/util/misc/objectTransforms.d.ts +68 -0
  206. package/dist/src/util/misc/pick.d.ts +8 -0
  207. package/dist/src/util/misc/planeChange.d.ts +84 -0
  208. package/dist/src/util/misc/projectStroke/StrokeLineCapProjections.d.ts +51 -0
  209. package/dist/src/util/misc/projectStroke/StrokeLineJoinProjections.d.ts +81 -0
  210. package/dist/src/util/misc/projectStroke/StrokeProjectionsBase.d.ts +25 -0
  211. package/dist/src/util/misc/projectStroke/index.d.ts +11 -0
  212. package/dist/src/util/misc/projectStroke/types.d.ts +23 -0
  213. package/dist/src/util/misc/radiansDegreesConversion.d.ts +14 -0
  214. package/dist/src/util/misc/resolveOrigin.d.ts +9 -0
  215. package/dist/src/util/misc/rotatePoint.d.ts +12 -0
  216. package/dist/src/util/misc/sin.d.ts +10 -0
  217. package/dist/src/util/misc/svgParsing.d.ts +53 -0
  218. package/dist/src/util/misc/textStyles.d.ts +32 -0
  219. package/dist/src/util/misc/toFixed.d.ts +8 -0
  220. package/dist/src/util/misc/vectors.d.ts +57 -0
  221. package/dist/src/util/path.d.ts +108 -0
  222. package/dist/src/util/transform_matrix_removal.d.ts +14 -0
  223. package/dist/src/util/types.d.ts +19 -0
  224. package/fabric.ts +427 -0
  225. package/index.node.ts +43 -0
  226. package/index.ts +1 -0
  227. package/package.json +90 -39
  228. package/publish.js +0 -26
  229. package/rollup.config.mjs +79 -0
  230. package/rollup.test.config.js +24 -0
  231. package/scripts/build.mjs +50 -0
  232. package/scripts/buildLock.mjs +115 -0
  233. package/scripts/buildReporter.mjs +15 -0
  234. package/scripts/buildStats.mjs +139 -0
  235. package/scripts/dirname.mjs +14 -0
  236. package/scripts/index.mjs +679 -0
  237. package/scripts/transform_files.mjs +504 -0
  238. package/src/Collection.ts +311 -0
  239. package/src/CommonMethods.ts +63 -0
  240. package/src/EventTypeDefs.ts +294 -0
  241. package/src/Intersection.ts +273 -0
  242. package/src/Observable.ts +175 -0
  243. package/src/Pattern.ts +211 -0
  244. package/src/Point.ts +388 -0
  245. package/src/Shadow.ts +215 -0
  246. package/src/brushes/{base_brush.class.js → BaseBrush.ts} +65 -42
  247. package/src/brushes/CircleBrush.ts +145 -0
  248. package/src/brushes/PatternBrush.ts +70 -0
  249. package/src/brushes/PencilBrush.ts +300 -0
  250. package/src/brushes/SprayBrush.ts +219 -0
  251. package/src/cache.ts +89 -0
  252. package/src/canvas/Canvas.ts +1672 -0
  253. package/src/canvas/SelectableCanvas.ts +1636 -0
  254. package/src/canvas/StaticCanvas.ts +1709 -0
  255. package/src/canvas/TextEditingManager.ts +48 -0
  256. package/src/canvas/canvas_gestures.mixin.ts +207 -0
  257. package/src/color/Color.ts +404 -0
  258. package/src/color/color_map.ts +154 -0
  259. package/src/color/constants.ts +26 -0
  260. package/src/color/util.ts +32 -0
  261. package/src/config.ts +159 -0
  262. package/src/constants.ts +19 -0
  263. package/src/controls/Control.ts +377 -0
  264. package/src/controls/changeWidth.ts +52 -0
  265. package/src/controls/controls.render.ts +138 -0
  266. package/src/controls/default_controls.ts +132 -0
  267. package/src/controls/drag.ts +31 -0
  268. package/src/controls/polyControl.ts +135 -0
  269. package/src/controls/rotate.ts +87 -0
  270. package/src/controls/scale.ts +277 -0
  271. package/src/controls/scaleSkew.ts +92 -0
  272. package/src/controls/skew.ts +242 -0
  273. package/src/controls/util.ts +154 -0
  274. package/src/controls/wrapWithFireEvent.ts +25 -0
  275. package/src/controls/wrapWithFixedAnchor.ts +20 -0
  276. package/src/env/browser.ts +28 -0
  277. package/src/env/index.ts +19 -0
  278. package/src/env/node.ts +52 -0
  279. package/src/env/types.ts +15 -0
  280. package/src/filters/{base_filter.class.js → BaseFilter.ts} +201 -149
  281. package/src/filters/BlendColor.ts +258 -0
  282. package/src/filters/BlendImage.ts +254 -0
  283. package/src/filters/Blur.ts +202 -0
  284. package/src/filters/Boilerplate.ts +91 -0
  285. package/src/filters/Brightness.ts +93 -0
  286. package/src/filters/Canvas2dFilterBackend.ts +65 -0
  287. package/src/filters/ColorMatrix.ts +156 -0
  288. package/src/filters/ColorMatrixFilters.ts +106 -0
  289. package/src/filters/Composed.ts +85 -0
  290. package/src/filters/Contrast.ts +94 -0
  291. package/src/filters/Convolute.ts +341 -0
  292. package/src/filters/FilterBackend.ts +32 -0
  293. package/src/filters/Gamma.ts +127 -0
  294. package/src/filters/Grayscale.ts +137 -0
  295. package/src/filters/HueRotation.ts +66 -0
  296. package/src/filters/Invert.ts +117 -0
  297. package/src/filters/Noise.ts +109 -0
  298. package/src/filters/Pixelate.ts +114 -0
  299. package/src/filters/RemoveColor.ts +148 -0
  300. package/src/filters/Resize.ts +540 -0
  301. package/src/filters/Saturation.ts +100 -0
  302. package/src/filters/Vibrance.ts +103 -0
  303. package/src/filters/WebGLFilterBackend.ts +430 -0
  304. package/src/filters/WebGLProbe.ts +62 -0
  305. package/src/filters/typedefs.ts +65 -0
  306. package/src/gradient/Gradient.ts +401 -0
  307. package/src/gradient/constants.ts +12 -0
  308. package/src/gradient/parser/index.ts +3 -0
  309. package/src/gradient/parser/misc.ts +13 -0
  310. package/src/gradient/parser/parseColorStops.ts +55 -0
  311. package/src/gradient/parser/parseCoords.ts +73 -0
  312. package/src/gradient/typedefs.ts +104 -0
  313. package/src/mixins/{eraser_brush.mixin.js → eraser_brush.mixin.ts} +350 -239
  314. package/src/mixins/stateful.mixin.ts +104 -0
  315. package/src/parkinglot/canvas_animation.mixin.ts +121 -0
  316. package/src/parkinglot/straighten.ts +58 -0
  317. package/src/parser/applyViewboxTransform.ts +162 -0
  318. package/src/parser/attributes.ts +25 -0
  319. package/src/parser/constants.ts +118 -0
  320. package/src/parser/doesSomeParentMatch.ts +19 -0
  321. package/src/parser/elementById.ts +23 -0
  322. package/src/parser/elementMatchesRule.ts +19 -0
  323. package/src/parser/elements_parser.ts +191 -0
  324. package/src/parser/getCSSRules.ts +62 -0
  325. package/src/parser/getGlobalStylesForElement.ts +19 -0
  326. package/src/parser/getGradientDefs.ts +32 -0
  327. package/src/parser/getMultipleNodes.ts +15 -0
  328. package/src/parser/getSvgRegex.ts +5 -0
  329. package/src/parser/hasAncestorWithNodeName.ts +14 -0
  330. package/src/parser/loadSVGFromString.ts +26 -0
  331. package/src/parser/loadSVGFromURL.ts +40 -0
  332. package/src/parser/normalizeAttr.ts +10 -0
  333. package/src/parser/normalizeValue.ts +63 -0
  334. package/src/parser/parseAttributes.ts +90 -0
  335. package/src/parser/parseElements.ts +28 -0
  336. package/src/parser/parseFontDeclaration.ts +44 -0
  337. package/src/parser/parsePointsAttribute.ts +36 -0
  338. package/src/parser/parseSVGDocument.ts +106 -0
  339. package/src/parser/parseStyleAttribute.ts +27 -0
  340. package/src/parser/parseStyleObject.ts +15 -0
  341. package/src/parser/parseStyleString.ts +16 -0
  342. package/src/parser/parseTransformAttribute.ts +155 -0
  343. package/src/parser/parseUseDirectives.ts +79 -0
  344. package/src/parser/percent.ts +27 -0
  345. package/src/parser/recursivelyParseGradientsXlink.ts +42 -0
  346. package/src/parser/rotateMatrix.ts +21 -0
  347. package/src/parser/scaleMatrix.ts +9 -0
  348. package/src/parser/selectorMatches.ts +25 -0
  349. package/src/parser/setStrokeFillOpacity.ts +40 -0
  350. package/src/parser/skewMatrix.ts +6 -0
  351. package/src/parser/translateMatrix.ts +8 -0
  352. package/src/shapes/ActiveSelection.ts +157 -0
  353. package/src/shapes/Circle.ts +205 -0
  354. package/src/shapes/Ellipse.ts +154 -0
  355. package/src/shapes/Group.ts +1053 -0
  356. package/src/shapes/IText/DraggableTextDelegate.ts +394 -0
  357. package/src/shapes/IText/IText.ts +666 -0
  358. package/src/shapes/IText/ITextBehavior.ts +1055 -0
  359. package/src/shapes/IText/ITextClickBehavior.ts +321 -0
  360. package/src/shapes/IText/ITextKeyBehavior.ts +683 -0
  361. package/src/shapes/IText/constants.ts +47 -0
  362. package/src/shapes/Image.ts +789 -0
  363. package/src/shapes/Line.ts +329 -0
  364. package/src/shapes/Object/AnimatableObject.ts +106 -0
  365. package/src/shapes/Object/FabricObject.ts +19 -0
  366. package/src/shapes/Object/FabricObjectSVGExportMixin.ts +277 -0
  367. package/src/shapes/Object/InteractiveObject.ts +645 -0
  368. package/src/shapes/Object/Object.ts +1896 -0
  369. package/src/shapes/Object/ObjectGeometry.ts +830 -0
  370. package/src/shapes/Object/ObjectOrigin.ts +354 -0
  371. package/src/shapes/Object/StackedObject.ts +191 -0
  372. package/src/shapes/Object/defaultValues.ts +112 -0
  373. package/src/shapes/Path.ts +395 -0
  374. package/src/shapes/Polygon.ts +19 -0
  375. package/src/shapes/Polyline.ts +333 -0
  376. package/src/shapes/Rect.ts +199 -0
  377. package/src/shapes/Text/StyledText.ts +319 -0
  378. package/src/shapes/Text/Text.ts +1948 -0
  379. package/src/shapes/Text/TextSVGExportMixin.ts +288 -0
  380. package/src/shapes/Textbox.ts +471 -0
  381. package/src/shapes/Triangle.ts +45 -0
  382. package/src/typedefs.ts +119 -0
  383. package/src/util/animation/AnimationBase.ts +166 -0
  384. package/src/util/animation/AnimationFrameProvider.ts +28 -0
  385. package/src/util/animation/AnimationRegistry.ts +58 -0
  386. package/src/util/animation/ArrayAnimation.ts +27 -0
  387. package/src/util/animation/ColorAnimation.ts +74 -0
  388. package/src/util/animation/ValueAnimation.ts +29 -0
  389. package/src/util/animation/animate.ts +74 -0
  390. package/src/util/animation/easing.ts +327 -0
  391. package/src/util/animation/types.ts +136 -0
  392. package/src/util/applyMixins.ts +22 -0
  393. package/src/util/class_registry.ts +54 -0
  394. package/src/util/dom_event.ts +28 -0
  395. package/src/util/dom_misc.ts +138 -0
  396. package/src/util/dom_request.ts +64 -0
  397. package/src/util/dom_style.ts +20 -0
  398. package/src/util/fireEvent.ts +15 -0
  399. package/src/util/internals/cloneDeep.ts +2 -0
  400. package/src/util/internals/getRandomInt.ts +8 -0
  401. package/src/util/internals/ifNaN.ts +9 -0
  402. package/src/util/internals/index.ts +3 -0
  403. package/src/util/internals/removeFromArray.ts +14 -0
  404. package/src/util/internals/uid.ts +3 -0
  405. package/src/util/lang_class.ts +90 -0
  406. package/src/util/lang_string.ts +79 -0
  407. package/src/util/misc/boundingBoxFromPoints.ts +37 -0
  408. package/src/util/misc/capValue.ts +2 -0
  409. package/src/util/misc/cos.ts +24 -0
  410. package/src/util/misc/dom.ts +50 -0
  411. package/src/util/misc/findScaleTo.ts +44 -0
  412. package/src/util/misc/groupSVGElements.ts +15 -0
  413. package/src/util/misc/isTransparent.ts +51 -0
  414. package/src/util/misc/matrix.ts +207 -0
  415. package/src/util/misc/mergeClipPaths.ts +40 -0
  416. package/src/util/misc/objectEnlive.ts +189 -0
  417. package/src/util/misc/objectTransforms.ts +129 -0
  418. package/src/util/misc/pick.ts +17 -0
  419. package/src/util/misc/planeChange.ts +136 -0
  420. package/src/util/misc/projectStroke/StrokeLineCapProjections.ts +112 -0
  421. package/src/util/misc/projectStroke/StrokeLineJoinProjections.ts +231 -0
  422. package/src/util/misc/projectStroke/StrokeProjectionsBase.ts +75 -0
  423. package/src/util/misc/projectStroke/index.ts +53 -0
  424. package/src/util/misc/projectStroke/types.ts +24 -0
  425. package/src/util/misc/radiansDegreesConversion.ts +18 -0
  426. package/src/util/misc/resolveOrigin.ts +22 -0
  427. package/src/util/misc/rotatePoint.ts +15 -0
  428. package/src/util/misc/sin.ts +26 -0
  429. package/src/util/misc/svgParsing.ts +181 -0
  430. package/src/util/misc/textStyles.ts +133 -0
  431. package/src/util/misc/toFixed.ts +8 -0
  432. package/src/util/misc/vectors.ts +82 -0
  433. package/src/util/path.ts +941 -0
  434. package/src/util/transform_matrix_removal.ts +60 -0
  435. package/src/util/types.ts +78 -0
  436. package/tsconfig.json +106 -0
  437. package/HEADER.js +0 -203
  438. package/build.js +0 -287
  439. package/dist/fabric.js +0 -31187
  440. package/dist/fabric.min.js +0 -1
  441. package/old-travis-reference.yml +0 -97
  442. package/src/brushes/circle_brush.class.js +0 -144
  443. package/src/brushes/pattern_brush.class.js +0 -61
  444. package/src/brushes/pencil_brush.class.js +0 -310
  445. package/src/brushes/spray_brush.class.js +0 -219
  446. package/src/canvas.class.js +0 -1312
  447. package/src/color.class.js +0 -636
  448. package/src/control.class.js +0 -339
  449. package/src/controls.actions.js +0 -740
  450. package/src/controls.render.js +0 -99
  451. package/src/elements_parser.js +0 -152
  452. package/src/filters/2d_backend.class.js +0 -65
  453. package/src/filters/blendcolor_filter.class.js +0 -251
  454. package/src/filters/blendimage_filter.class.js +0 -247
  455. package/src/filters/blur_filter.class.js +0 -217
  456. package/src/filters/brightness_filter.class.js +0 -113
  457. package/src/filters/colormatrix_filter.class.js +0 -159
  458. package/src/filters/composed_filter.class.js +0 -72
  459. package/src/filters/contrast_filter.class.js +0 -113
  460. package/src/filters/convolute_filter.class.js +0 -352
  461. package/src/filters/filter_boilerplate.js +0 -111
  462. package/src/filters/filter_generator.js +0 -85
  463. package/src/filters/gamma_filter.class.js +0 -136
  464. package/src/filters/grayscale_filter.class.js +0 -154
  465. package/src/filters/hue_rotation.class.js +0 -107
  466. package/src/filters/invert_filter.class.js +0 -111
  467. package/src/filters/noise_filter.class.js +0 -134
  468. package/src/filters/pixelate_filter.class.js +0 -137
  469. package/src/filters/removecolor_filter.class.js +0 -173
  470. package/src/filters/resize_filter.class.js +0 -490
  471. package/src/filters/saturate_filter.class.js +0 -119
  472. package/src/filters/vibrance_filter.class.js +0 -122
  473. package/src/filters/webgl_backend.class.js +0 -396
  474. package/src/globalFabric.js +0 -4
  475. package/src/gradient.class.js +0 -490
  476. package/src/intersection.class.js +0 -172
  477. package/src/log.js +0 -11
  478. package/src/mixins/animation.mixin.js +0 -231
  479. package/src/mixins/canvas_dataurl_exporter.mixin.js +0 -97
  480. package/src/mixins/canvas_events.mixin.js +0 -974
  481. package/src/mixins/canvas_gestures.mixin.js +0 -149
  482. package/src/mixins/canvas_grouping.mixin.js +0 -177
  483. package/src/mixins/canvas_serialization.mixin.js +0 -228
  484. package/src/mixins/collection.mixin.js +0 -170
  485. package/src/mixins/default_controls.js +0 -114
  486. package/src/mixins/itext.svg_export.js +0 -241
  487. package/src/mixins/itext_behavior.mixin.js +0 -940
  488. package/src/mixins/itext_click_behavior.mixin.js +0 -278
  489. package/src/mixins/itext_key_behavior.mixin.js +0 -694
  490. package/src/mixins/object.svg_export.js +0 -258
  491. package/src/mixins/object_geometry.mixin.js +0 -683
  492. package/src/mixins/object_interactivity.mixin.js +0 -314
  493. package/src/mixins/object_origin.mixin.js +0 -255
  494. package/src/mixins/object_stacking.mixin.js +0 -80
  495. package/src/mixins/object_straightening.mixin.js +0 -80
  496. package/src/mixins/observable.mixin.js +0 -141
  497. package/src/mixins/shared_methods.mixin.js +0 -94
  498. package/src/mixins/stateful.mixin.js +0 -107
  499. package/src/mixins/text_style.mixin.js +0 -324
  500. package/src/parser.js +0 -1090
  501. package/src/pattern.class.js +0 -189
  502. package/src/point.class.js +0 -337
  503. package/src/shadow.class.js +0 -195
  504. package/src/shapes/active_selection.class.js +0 -155
  505. package/src/shapes/circle.class.js +0 -210
  506. package/src/shapes/ellipse.class.js +0 -181
  507. package/src/shapes/group.class.js +0 -593
  508. package/src/shapes/image.class.js +0 -764
  509. package/src/shapes/itext.class.js +0 -526
  510. package/src/shapes/line.class.js +0 -324
  511. package/src/shapes/object.class.js +0 -2008
  512. package/src/shapes/path.class.js +0 -384
  513. package/src/shapes/polygon.class.js +0 -81
  514. package/src/shapes/polyline.class.js +0 -268
  515. package/src/shapes/rect.class.js +0 -187
  516. package/src/shapes/text.class.js +0 -1696
  517. package/src/shapes/textbox.class.js +0 -461
  518. package/src/shapes/triangle.class.js +0 -93
  519. package/src/static_canvas.class.js +0 -1881
  520. package/src/util/anim_ease.js +0 -398
  521. package/src/util/animate.js +0 -254
  522. package/src/util/animate_color.js +0 -74
  523. package/src/util/dom_event.js +0 -50
  524. package/src/util/dom_misc.js +0 -300
  525. package/src/util/dom_request.js +0 -54
  526. package/src/util/dom_style.js +0 -70
  527. package/src/util/lang_array.js +0 -94
  528. package/src/util/lang_class.js +0 -115
  529. package/src/util/lang_object.js +0 -75
  530. package/src/util/lang_string.js +0 -110
  531. package/src/util/misc.js +0 -1330
  532. package/src/util/named_accessors.mixin.js +0 -428
  533. package/src/util/path.js +0 -829
@@ -0,0 +1,1948 @@
1
+ // @ts-nocheck
2
+ import { cache } from '../../cache';
3
+ import { DEFAULT_SVG_FONT_SIZE } from '../../constants';
4
+ import { ObjectEvents } from '../../EventTypeDefs';
5
+ import { TextStyle, TextStyleDeclaration, StyledText } from './StyledText';
6
+ import { SHARED_ATTRIBUTES } from '../../parser/attributes';
7
+ import { parseAttributes } from '../../parser/parseAttributes';
8
+ import type { Point } from '../../Point';
9
+ import type {
10
+ TCacheCanvasDimensions,
11
+ TClassProperties,
12
+ TFiller,
13
+ } from '../../typedefs';
14
+ import { classRegistry } from '../../util/class_registry';
15
+ import { graphemeSplit } from '../../util/lang_string';
16
+ import { createCanvasElement } from '../../util/misc/dom';
17
+ import {
18
+ hasStyleChanged,
19
+ stylesFromArray,
20
+ stylesToArray,
21
+ } from '../../util/misc/textStyles';
22
+ import { getPathSegmentsInfo, getPointOnPath } from '../../util/path';
23
+ import { cacheProperties } from '../Object/FabricObject';
24
+ import { Path } from '../Path';
25
+ import { TextSVGExportMixin } from './TextSVGExportMixin';
26
+ import { applyMixins } from '../../util/applyMixins';
27
+
28
+ let measuringContext: CanvasRenderingContext2D | null;
29
+
30
+ /**
31
+ * Return a context for measurement of text string.
32
+ * if created it gets stored for reuse
33
+ */
34
+ function getMeasuringContext() {
35
+ if (!measuringContext) {
36
+ measuringContext = createCanvasElement().getContext('2d');
37
+ }
38
+ return measuringContext;
39
+ }
40
+
41
+ /**
42
+ * Measure and return the info of a single grapheme.
43
+ * needs the the info of previous graphemes already filled
44
+ * Override to customize measuring
45
+ */
46
+ export type GraphemeBBox<onPath = false> = {
47
+ width: number;
48
+ height: number;
49
+ kernedWidth: number;
50
+ left: number;
51
+ deltaY: number;
52
+ } & (onPath extends true
53
+ ? {
54
+ // on path
55
+ renderLeft: number;
56
+ renderTop: number;
57
+ angle: number;
58
+ }
59
+ : Record<string, never>);
60
+
61
+ const additionalProps = [
62
+ 'fontFamily',
63
+ 'fontWeight',
64
+ 'fontSize',
65
+ 'text',
66
+ 'underline',
67
+ 'overline',
68
+ 'linethrough',
69
+ 'textAlign',
70
+ 'fontStyle',
71
+ 'lineHeight',
72
+ 'textBackgroundColor',
73
+ 'charSpacing',
74
+ 'styles',
75
+ 'direction',
76
+ 'path',
77
+ 'pathStartOffset',
78
+ 'pathSide',
79
+ 'pathAlign',
80
+ ] as const;
81
+
82
+ /**
83
+ * Text class
84
+ * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#text}
85
+ */
86
+ export class Text<
87
+ EventSpec extends ObjectEvents = ObjectEvents
88
+ > extends StyledText<EventSpec> {
89
+ /**
90
+ * Properties which when set cause object to change dimensions
91
+ * @type Array
92
+ * @private
93
+ */
94
+ declare _dimensionAffectingProps: string[];
95
+
96
+ /**
97
+ * @private
98
+ */
99
+ declare _reNewline: RegExp;
100
+
101
+ /**
102
+ * Use this regular expression to filter for whitespaces that is not a new line.
103
+ * Mostly used when text is 'justify' aligned.
104
+ * @private
105
+ */
106
+ declare _reSpacesAndTabs: RegExp;
107
+
108
+ /**
109
+ * Use this regular expression to filter for whitespace that is not a new line.
110
+ * Mostly used when text is 'justify' aligned.
111
+ * @private
112
+ */
113
+ declare _reSpaceAndTab: RegExp;
114
+
115
+ /**
116
+ * Use this regular expression to filter consecutive groups of non spaces.
117
+ * Mostly used when text is 'justify' aligned.
118
+ * @private
119
+ */
120
+ declare _reWords: RegExp;
121
+
122
+ declare text: string;
123
+
124
+ /**
125
+ * Font size (in pixels)
126
+ * @type Number
127
+ * @default
128
+ */
129
+ declare fontSize: number;
130
+
131
+ /**
132
+ * Font weight (e.g. bold, normal, 400, 600, 800)
133
+ * @type {(Number|String)}
134
+ * @default
135
+ */
136
+ declare fontWeight: string;
137
+
138
+ /**
139
+ * Font family
140
+ * @type String
141
+ * @default
142
+ */
143
+ declare fontFamily: string;
144
+
145
+ /**
146
+ * Text decoration underline.
147
+ * @type Boolean
148
+ * @default
149
+ */
150
+ declare underline: boolean;
151
+
152
+ /**
153
+ * Text decoration overline.
154
+ * @type Boolean
155
+ * @default
156
+ */
157
+ declare overline: boolean;
158
+
159
+ /**
160
+ * Text decoration linethrough.
161
+ * @type Boolean
162
+ * @default
163
+ */
164
+ declare linethrough: boolean;
165
+
166
+ /**
167
+ * Text alignment. Possible values: "left", "center", "right", "justify",
168
+ * "justify-left", "justify-center" or "justify-right".
169
+ * @type String
170
+ * @default
171
+ */
172
+ declare textAlign: string;
173
+
174
+ /**
175
+ * Font style . Possible values: "", "normal", "italic" or "oblique".
176
+ * @type String
177
+ * @default
178
+ */
179
+ declare fontStyle: string;
180
+
181
+ /**
182
+ * Line height
183
+ * @type Number
184
+ * @default
185
+ */
186
+ declare lineHeight: number;
187
+
188
+ /**
189
+ * Superscript schema object (minimum overlap)
190
+ */
191
+ declare superscript: {
192
+ /**
193
+ * fontSize factor
194
+ * @default 0.6
195
+ */
196
+ size: number;
197
+ /**
198
+ * baseline-shift factor (upwards)
199
+ * @default -0.35
200
+ */
201
+ baseline: number;
202
+ };
203
+
204
+ /**
205
+ * Subscript schema object (minimum overlap)
206
+ */
207
+ declare subscript: {
208
+ /**
209
+ * fontSize factor
210
+ * @default 0.6
211
+ */
212
+ size: number;
213
+ /**
214
+ * baseline-shift factor (downwards)
215
+ * @default 0.11
216
+ */
217
+ baseline: number;
218
+ };
219
+
220
+ /**
221
+ * Background color of text lines
222
+ * @type String
223
+ * @default
224
+ */
225
+ declare textBackgroundColor: string;
226
+
227
+ protected declare _styleProperties: string[];
228
+
229
+ declare styles: TextStyle;
230
+
231
+ /**
232
+ * Path that the text should follow.
233
+ * since 4.6.0 the path will be drawn automatically.
234
+ * if you want to make the path visible, give it a stroke and strokeWidth or fill value
235
+ * if you want it to be hidden, assign visible = false to the path.
236
+ * This feature is in BETA, and SVG import/export is not yet supported.
237
+ * @type Path
238
+ * @example
239
+ * const textPath = new Text('Text on a path', {
240
+ * top: 150,
241
+ * left: 150,
242
+ * textAlign: 'center',
243
+ * charSpacing: -50,
244
+ * path: new Path('M 0 0 C 50 -100 150 -100 200 0', {
245
+ * strokeWidth: 1,
246
+ * visible: false
247
+ * }),
248
+ * pathSide: 'left',
249
+ * pathStartOffset: 0
250
+ * });
251
+ * @default
252
+ */
253
+ declare path: Path;
254
+
255
+ /**
256
+ * Offset amount for text path starting position
257
+ * Only used when text has a path
258
+ * @type Number
259
+ * @default
260
+ */
261
+ declare pathStartOffset: number;
262
+
263
+ /**
264
+ * Which side of the path the text should be drawn on.
265
+ * Only used when text has a path
266
+ * @type {String} 'left|right'
267
+ * @default
268
+ */
269
+ declare pathSide: string;
270
+
271
+ /**
272
+ * How text is aligned to the path. This property determines
273
+ * the perpendicular position of each character relative to the path.
274
+ * (one of "baseline", "center", "ascender", "descender")
275
+ * This feature is in BETA, and its behavior may change
276
+ * @type String
277
+ * @default
278
+ */
279
+ declare pathAlign: string;
280
+
281
+ /**
282
+ * @private
283
+ */
284
+ declare _fontSizeFraction: number;
285
+
286
+ /**
287
+ * @private
288
+ */
289
+ declare offsets: { underline: number; linethrough: number; overline: number };
290
+
291
+ /**
292
+ * Text Line proportion to font Size (in pixels)
293
+ * @type Number
294
+ * @default
295
+ */
296
+ declare _fontSizeMult: number;
297
+
298
+ /**
299
+ * additional space between characters
300
+ * expressed in thousands of em unit
301
+ * @type Number
302
+ * @default
303
+ */
304
+ declare charSpacing: number;
305
+
306
+ /**
307
+ * Baseline shift, styles only, keep at 0 for the main text object
308
+ * @type {Number}
309
+ * @default
310
+ */
311
+ declare deltaY: number;
312
+
313
+ /**
314
+ * WARNING: EXPERIMENTAL. NOT SUPPORTED YET
315
+ * determine the direction of the text.
316
+ * This has to be set manually together with textAlign and originX for proper
317
+ * experience.
318
+ * some interesting link for the future
319
+ * https://www.w3.org/International/questions/qa-bidi-unicode-controls
320
+ * @since 4.5.0
321
+ * @type {String} 'ltr|rtl'
322
+ * @default
323
+ */
324
+ declare direction: string;
325
+
326
+ /**
327
+ * contains characters bounding boxes
328
+ */
329
+ protected __charBounds: GraphemeBBox[][] = [];
330
+
331
+ /**
332
+ * use this size when measuring text. To avoid IE11 rounding errors
333
+ * @type {Number}
334
+ * @default
335
+ * @readonly
336
+ * @private
337
+ */
338
+ declare CACHE_FONT_SIZE: number;
339
+
340
+ /**
341
+ * contains the min text width to avoid getting 0
342
+ * @type {Number}
343
+ * @default
344
+ */
345
+ declare MIN_TEXT_WIDTH: number;
346
+
347
+ /**
348
+ * contains the the text of the object, divided in lines as they are displayed
349
+ * on screen. Wrapping will divide the text independently of line breaks
350
+ * @type {string[]}
351
+ * @default
352
+ */
353
+ declare textLines: string[];
354
+
355
+ /**
356
+ * same as textlines, but each line is an array of graphemes as split by splitByGrapheme
357
+ * @type {string[]}
358
+ * @default
359
+ */
360
+ declare _textLines: string[][];
361
+
362
+ declare _unwrappedTextLines: string[][];
363
+ declare _text: string[];
364
+ declare cursorWidth: number;
365
+ declare __lineHeights: number[];
366
+ declare __lineWidths: number[];
367
+ declare _forceClearCache: boolean;
368
+
369
+ declare initialized?: true;
370
+
371
+ constructor(text: string, options: any) {
372
+ super({ ...options, text, styles: options?.styles || {} });
373
+ this.initialized = true;
374
+ if (this.path) {
375
+ this.setPathInfo();
376
+ }
377
+ this.initDimensions();
378
+ this.setCoords();
379
+ this.saveState({ propertySet: '_dimensionAffectingProps' });
380
+ }
381
+
382
+ /**
383
+ * If text has a path, it will add the extra information needed
384
+ * for path and text calculations
385
+ */
386
+ setPathInfo() {
387
+ const path = this.path;
388
+ if (path) {
389
+ path.segmentsInfo = getPathSegmentsInfo(path.path);
390
+ }
391
+ }
392
+
393
+ /**
394
+ * @private
395
+ * Divides text into lines of text and lines of graphemes.
396
+ */
397
+ _splitText() {
398
+ const newLines = this._splitTextIntoLines(this.text);
399
+ this.textLines = newLines.lines;
400
+ this._textLines = newLines.graphemeLines;
401
+ this._unwrappedTextLines = newLines._unwrappedLines;
402
+ this._text = newLines.graphemeText;
403
+ return newLines;
404
+ }
405
+
406
+ /**
407
+ * Initialize or update text dimensions.
408
+ * Updates this.width and this.height with the proper values.
409
+ * Does not return dimensions.
410
+ */
411
+ initDimensions() {
412
+ this._splitText();
413
+ this._clearCache();
414
+ if (this.path) {
415
+ this.width = this.path.width;
416
+ this.height = this.path.height;
417
+ } else {
418
+ this.width =
419
+ this.calcTextWidth() || this.cursorWidth || this.MIN_TEXT_WIDTH;
420
+ this.height = this.calcTextHeight();
421
+ }
422
+ if (this.textAlign.indexOf('justify') !== -1) {
423
+ // once text is measured we need to make space fatter to make justified text.
424
+ this.enlargeSpaces();
425
+ }
426
+ this.saveState({ propertySet: '_dimensionAffectingProps' });
427
+ }
428
+
429
+ /**
430
+ * Enlarge space boxes and shift the others
431
+ */
432
+ enlargeSpaces() {
433
+ let diffSpace,
434
+ currentLineWidth,
435
+ numberOfSpaces,
436
+ accumulatedSpace,
437
+ line,
438
+ charBound,
439
+ spaces;
440
+ for (let i = 0, len = this._textLines.length; i < len; i++) {
441
+ if (
442
+ this.textAlign !== 'justify' &&
443
+ (i === len - 1 || this.isEndOfWrapping(i))
444
+ ) {
445
+ continue;
446
+ }
447
+ accumulatedSpace = 0;
448
+ line = this._textLines[i];
449
+ currentLineWidth = this.getLineWidth(i);
450
+ if (
451
+ currentLineWidth < this.width &&
452
+ (spaces = this.textLines[i].match(this._reSpacesAndTabs))
453
+ ) {
454
+ numberOfSpaces = spaces.length;
455
+ diffSpace = (this.width - currentLineWidth) / numberOfSpaces;
456
+ for (let j = 0; j <= line.length; j++) {
457
+ charBound = this.__charBounds[i][j];
458
+ if (this._reSpaceAndTab.test(line[j])) {
459
+ charBound.width += diffSpace;
460
+ charBound.kernedWidth += diffSpace;
461
+ charBound.left += accumulatedSpace;
462
+ accumulatedSpace += diffSpace;
463
+ } else {
464
+ charBound.left += accumulatedSpace;
465
+ }
466
+ }
467
+ }
468
+ }
469
+ }
470
+
471
+ /**
472
+ * Detect if the text line is ended with an hard break
473
+ * text and itext do not have wrapping, return false
474
+ * @return {Boolean}
475
+ */
476
+ isEndOfWrapping(lineIndex: number): boolean {
477
+ return lineIndex === this._textLines.length - 1;
478
+ }
479
+
480
+ /**
481
+ * Detect if a line has a linebreak and so we need to account for it when moving
482
+ * and counting style.
483
+ * It return always for text and Itext.
484
+ * @return Number
485
+ */
486
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
487
+ missingNewlineOffset(lineIndex: number) {
488
+ return 1;
489
+ }
490
+
491
+ /**
492
+ * Returns 2d representation (lineIndex and charIndex) of cursor
493
+ * @param {Number} selectionStart
494
+ * @param {Boolean} [skipWrapping] consider the location for unwrapped lines. useful to manage styles.
495
+ */
496
+ get2DCursorLocation(selectionStart: number, skipWrapping?: boolean) {
497
+ const lines = skipWrapping ? this._unwrappedTextLines : this._textLines;
498
+ let i: number;
499
+ for (i = 0; i < lines.length; i++) {
500
+ if (selectionStart <= lines[i].length) {
501
+ return {
502
+ lineIndex: i,
503
+ charIndex: selectionStart,
504
+ };
505
+ }
506
+ selectionStart -= lines[i].length + this.missingNewlineOffset(i);
507
+ }
508
+ return {
509
+ lineIndex: i - 1,
510
+ charIndex:
511
+ lines[i - 1].length < selectionStart
512
+ ? lines[i - 1].length
513
+ : selectionStart,
514
+ };
515
+ }
516
+
517
+ /**
518
+ * Returns string representation of an instance
519
+ * @return {String} String representation of text object
520
+ */
521
+ toString(): string {
522
+ return `#<Text (${this.complexity()}): { "text": "${
523
+ this.text
524
+ }", "fontFamily": "${this.fontFamily}" }>`;
525
+ }
526
+
527
+ /**
528
+ * Return the dimension and the zoom level needed to create a cache canvas
529
+ * big enough to host the object to be cached.
530
+ * @private
531
+ * @param {Object} dim.x width of object to be cached
532
+ * @param {Object} dim.y height of object to be cached
533
+ * @return {Object}.width width of canvas
534
+ * @return {Object}.height height of canvas
535
+ * @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache
536
+ * @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache
537
+ */
538
+ _getCacheCanvasDimensions(): TCacheCanvasDimensions {
539
+ const dims = super._getCacheCanvasDimensions();
540
+ const fontSize = this.fontSize;
541
+ dims.width += fontSize * dims.zoomX;
542
+ dims.height += fontSize * dims.zoomY;
543
+ return dims;
544
+ }
545
+
546
+ /**
547
+ * @private
548
+ * @param {CanvasRenderingContext2D} ctx Context to render on
549
+ */
550
+ _render(ctx: CanvasRenderingContext2D) {
551
+ const path = this.path;
552
+ path && !path.isNotVisible() && path._render(ctx);
553
+ this._setTextStyles(ctx);
554
+ this._renderTextLinesBackground(ctx);
555
+ this._renderTextDecoration(ctx, 'underline');
556
+ this._renderText(ctx);
557
+ this._renderTextDecoration(ctx, 'overline');
558
+ this._renderTextDecoration(ctx, 'linethrough');
559
+ }
560
+
561
+ /**
562
+ * @private
563
+ * @param {CanvasRenderingContext2D} ctx Context to render on
564
+ */
565
+ _renderText(ctx: CanvasRenderingContext2D) {
566
+ if (this.paintFirst === 'stroke') {
567
+ this._renderTextStroke(ctx);
568
+ this._renderTextFill(ctx);
569
+ } else {
570
+ this._renderTextFill(ctx);
571
+ this._renderTextStroke(ctx);
572
+ }
573
+ }
574
+
575
+ /**
576
+ * Set the font parameter of the context with the object properties or with charStyle
577
+ * @private
578
+ * @param {CanvasRenderingContext2D} ctx Context to render on
579
+ * @param {Object} [charStyle] object with font style properties
580
+ * @param {String} [charStyle.fontFamily] Font Family
581
+ * @param {Number} [charStyle.fontSize] Font size in pixels. ( without px suffix )
582
+ * @param {String} [charStyle.fontWeight] Font weight
583
+ * @param {String} [charStyle.fontStyle] Font style (italic|normal)
584
+ */
585
+ _setTextStyles(
586
+ ctx: CanvasRenderingContext2D,
587
+ charStyle?: any,
588
+ forMeasuring?: boolean
589
+ ) {
590
+ ctx.textBaseline = 'alphabetic';
591
+ if (this.path) {
592
+ switch (this.pathAlign) {
593
+ case 'center':
594
+ ctx.textBaseline = 'middle';
595
+ break;
596
+ case 'ascender':
597
+ ctx.textBaseline = 'top';
598
+ break;
599
+ case 'descender':
600
+ ctx.textBaseline = 'bottom';
601
+ break;
602
+ }
603
+ }
604
+ ctx.font = this._getFontDeclaration(charStyle, forMeasuring);
605
+ }
606
+
607
+ /**
608
+ * calculate and return the text Width measuring each line.
609
+ * @private
610
+ * @param {CanvasRenderingContext2D} ctx Context to render on
611
+ * @return {Number} Maximum width of Text object
612
+ */
613
+ calcTextWidth(): number {
614
+ let maxWidth = this.getLineWidth(0);
615
+
616
+ for (let i = 1, len = this._textLines.length; i < len; i++) {
617
+ const currentLineWidth = this.getLineWidth(i);
618
+ if (currentLineWidth > maxWidth) {
619
+ maxWidth = currentLineWidth;
620
+ }
621
+ }
622
+ return maxWidth;
623
+ }
624
+
625
+ /**
626
+ * @private
627
+ * @param {String} method Method name ("fillText" or "strokeText")
628
+ * @param {CanvasRenderingContext2D} ctx Context to render on
629
+ * @param {String} line Text to render
630
+ * @param {Number} left Left position of text
631
+ * @param {Number} top Top position of text
632
+ * @param {Number} lineIndex Index of a line in a text
633
+ */
634
+ _renderTextLine(
635
+ method: 'fillText' | 'strokeText',
636
+ ctx: CanvasRenderingContext2D,
637
+ line: string[],
638
+ left: number,
639
+ top: number,
640
+ lineIndex: number
641
+ ) {
642
+ this._renderChars(method, ctx, line, left, top, lineIndex);
643
+ }
644
+
645
+ /**
646
+ * Renders the text background for lines, taking care of style
647
+ * @private
648
+ * @param {CanvasRenderingContext2D} ctx Context to render on
649
+ */
650
+ _renderTextLinesBackground(ctx: CanvasRenderingContext2D) {
651
+ if (!this.textBackgroundColor && !this.styleHas('textBackgroundColor')) {
652
+ return;
653
+ }
654
+ const originalFill = ctx.fillStyle,
655
+ leftOffset = this._getLeftOffset();
656
+ let lineTopOffset = this._getTopOffset();
657
+
658
+ for (let i = 0, len = this._textLines.length; i < len; i++) {
659
+ const heightOfLine = this.getHeightOfLine(i);
660
+ if (
661
+ !this.textBackgroundColor &&
662
+ !this.styleHas('textBackgroundColor', i)
663
+ ) {
664
+ lineTopOffset += heightOfLine;
665
+ continue;
666
+ }
667
+ const jlen = this._textLines[i].length;
668
+ const lineLeftOffset = this._getLineLeftOffset(i);
669
+ let boxWidth = 0;
670
+ let boxStart = 0;
671
+ let drawStart;
672
+ let currentColor;
673
+ let lastColor = this.getValueOfPropertyAt(i, 0, 'textBackgroundColor');
674
+ for (let j = 0; j < jlen; j++) {
675
+ const charBox = this.__charBounds[i][j];
676
+ currentColor = this.getValueOfPropertyAt(i, j, 'textBackgroundColor');
677
+ if (this.path) {
678
+ ctx.save();
679
+ ctx.translate(charBox.renderLeft, charBox.renderTop);
680
+ ctx.rotate(charBox.angle);
681
+ ctx.fillStyle = currentColor;
682
+ currentColor &&
683
+ ctx.fillRect(
684
+ -charBox.width / 2,
685
+ (-heightOfLine / this.lineHeight) * (1 - this._fontSizeFraction),
686
+ charBox.width,
687
+ heightOfLine / this.lineHeight
688
+ );
689
+ ctx.restore();
690
+ } else if (currentColor !== lastColor) {
691
+ drawStart = leftOffset + lineLeftOffset + boxStart;
692
+ if (this.direction === 'rtl') {
693
+ drawStart = this.width - drawStart - boxWidth;
694
+ }
695
+ ctx.fillStyle = lastColor;
696
+ lastColor &&
697
+ ctx.fillRect(
698
+ drawStart,
699
+ lineTopOffset,
700
+ boxWidth,
701
+ heightOfLine / this.lineHeight
702
+ );
703
+ boxStart = charBox.left;
704
+ boxWidth = charBox.width;
705
+ lastColor = currentColor;
706
+ } else {
707
+ boxWidth += charBox.kernedWidth;
708
+ }
709
+ }
710
+ if (currentColor && !this.path) {
711
+ drawStart = leftOffset + lineLeftOffset + boxStart;
712
+ if (this.direction === 'rtl') {
713
+ drawStart = this.width - drawStart - boxWidth;
714
+ }
715
+ ctx.fillStyle = currentColor;
716
+ ctx.fillRect(
717
+ drawStart,
718
+ lineTopOffset,
719
+ boxWidth,
720
+ heightOfLine / this.lineHeight
721
+ );
722
+ }
723
+ lineTopOffset += heightOfLine;
724
+ }
725
+ ctx.fillStyle = originalFill;
726
+ // if there is text background color no
727
+ // other shadows should be casted
728
+ this._removeShadow(ctx);
729
+ }
730
+
731
+ /**
732
+ * measure and return the width of a single character.
733
+ * possibly overridden to accommodate different measure logic or
734
+ * to hook some external lib for character measurement
735
+ * @private
736
+ * @param {String} _char, char to be measured
737
+ * @param {Object} charStyle style of char to be measured
738
+ * @param {String} [previousChar] previous char
739
+ * @param {Object} [prevCharStyle] style of previous char
740
+ */
741
+ _measureChar(
742
+ _char: string,
743
+ charStyle: TextStyleDeclaration,
744
+ previousChar: string | undefined,
745
+ prevCharStyle: any
746
+ ) {
747
+ const fontCache = cache.getFontCache(charStyle),
748
+ fontDeclaration = this._getFontDeclaration(charStyle),
749
+ previousFontDeclaration = this._getFontDeclaration(prevCharStyle),
750
+ couple = previousChar + _char,
751
+ stylesAreEqual = fontDeclaration === previousFontDeclaration,
752
+ fontMultiplier = charStyle.fontSize / this.CACHE_FONT_SIZE;
753
+ let width: number | undefined,
754
+ coupleWidth: number | undefined,
755
+ previousWidth: number | undefined,
756
+ kernedWidth: number | undefined;
757
+
758
+ if (previousChar && fontCache[previousChar] !== undefined) {
759
+ previousWidth = fontCache[previousChar];
760
+ }
761
+ if (fontCache[_char] !== undefined) {
762
+ kernedWidth = width = fontCache[_char];
763
+ }
764
+ if (stylesAreEqual && fontCache[couple] !== undefined) {
765
+ coupleWidth = fontCache[couple];
766
+ kernedWidth = coupleWidth - previousWidth;
767
+ }
768
+ if (
769
+ width === undefined ||
770
+ previousWidth === undefined ||
771
+ coupleWidth === undefined
772
+ ) {
773
+ const ctx = getMeasuringContext()!;
774
+ // send a TRUE to specify measuring font size CACHE_FONT_SIZE
775
+ this._setTextStyles(ctx, charStyle, true);
776
+ if (width === undefined) {
777
+ kernedWidth = width = ctx.measureText(_char).width;
778
+ fontCache[_char] = width;
779
+ }
780
+ if (previousWidth === undefined && stylesAreEqual && previousChar) {
781
+ previousWidth = ctx.measureText(previousChar).width;
782
+ fontCache[previousChar] = previousWidth;
783
+ }
784
+ if (stylesAreEqual && coupleWidth === undefined) {
785
+ // we can measure the kerning couple and subtract the width of the previous character
786
+ coupleWidth = ctx.measureText(couple).width;
787
+ fontCache[couple] = coupleWidth;
788
+ kernedWidth = coupleWidth - previousWidth;
789
+ }
790
+ }
791
+ return {
792
+ width: width * fontMultiplier,
793
+ kernedWidth: kernedWidth * fontMultiplier,
794
+ };
795
+ }
796
+
797
+ /**
798
+ * Computes height of character at given position
799
+ * @param {Number} line the line index number
800
+ * @param {Number} _char the character index number
801
+ * @return {Number} fontSize of the character
802
+ */
803
+ getHeightOfChar(line: number, _char: number): number {
804
+ return this.getValueOfPropertyAt(line, _char, 'fontSize');
805
+ }
806
+
807
+ /**
808
+ * measure a text line measuring all characters.
809
+ * @param {Number} lineIndex line number
810
+ */
811
+ measureLine(lineIndex: number) {
812
+ const lineInfo = this._measureLine(lineIndex);
813
+ if (this.charSpacing !== 0) {
814
+ lineInfo.width -= this._getWidthOfCharSpacing();
815
+ }
816
+ if (lineInfo.width < 0) {
817
+ lineInfo.width = 0;
818
+ }
819
+ return lineInfo;
820
+ }
821
+
822
+ /**
823
+ * measure every grapheme of a line, populating __charBounds
824
+ * @param {Number} lineIndex
825
+ * @return {Object} object.width total width of characters
826
+ * @return {Object} object.numOfSpaces length of chars that match this._reSpacesAndTabs
827
+ */
828
+ _measureLine(lineIndex: number) {
829
+ let width = 0,
830
+ prevGrapheme: string | undefined,
831
+ graphemeInfo: GraphemeBBox | undefined;
832
+
833
+ const reverse = this.pathSide === 'right',
834
+ path = this.path,
835
+ line = this._textLines[lineIndex],
836
+ llength = line.length,
837
+ lineBounds = new Array<GraphemeBBox>(llength);
838
+
839
+ this.__charBounds[lineIndex] = lineBounds;
840
+ for (let i = 0; i < llength; i++) {
841
+ const grapheme = line[i];
842
+ graphemeInfo = this._getGraphemeBox(grapheme, lineIndex, i, prevGrapheme);
843
+ lineBounds[i] = graphemeInfo;
844
+ width += graphemeInfo.kernedWidth;
845
+ prevGrapheme = grapheme;
846
+ }
847
+ // this latest bound box represent the last character of the line
848
+ // to simplify cursor handling in interactive mode.
849
+ lineBounds[llength] = {
850
+ left: graphemeInfo ? graphemeInfo.left + graphemeInfo.width : 0,
851
+ width: 0,
852
+ kernedWidth: 0,
853
+ height: this.fontSize,
854
+ };
855
+ if (path && path.segmentsInfo) {
856
+ let positionInPath = 0;
857
+ const totalPathLength =
858
+ path.segmentsInfo[path.segmentsInfo.length - 1].length;
859
+ const startingPoint = getPointOnPath(path.path, 0, path.segmentsInfo);
860
+ startingPoint.x += path.pathOffset.x;
861
+ startingPoint.y += path.pathOffset.y;
862
+ switch (this.textAlign) {
863
+ case 'left':
864
+ positionInPath = reverse ? totalPathLength - width : 0;
865
+ break;
866
+ case 'center':
867
+ positionInPath = (totalPathLength - width) / 2;
868
+ break;
869
+ case 'right':
870
+ positionInPath = reverse ? 0 : totalPathLength - width;
871
+ break;
872
+ //todo - add support for justify
873
+ }
874
+ positionInPath += this.pathStartOffset * (reverse ? -1 : 1);
875
+ for (
876
+ let i = reverse ? llength - 1 : 0;
877
+ reverse ? i >= 0 : i < llength;
878
+ reverse ? i-- : i++
879
+ ) {
880
+ graphemeInfo = lineBounds[i];
881
+ if (positionInPath > totalPathLength) {
882
+ positionInPath %= totalPathLength;
883
+ } else if (positionInPath < 0) {
884
+ positionInPath += totalPathLength;
885
+ }
886
+ // it would probably much faster to send all the grapheme position for a line
887
+ // and calculate path position/angle at once.
888
+ this._setGraphemeOnPath(positionInPath, graphemeInfo, startingPoint);
889
+ positionInPath += graphemeInfo.kernedWidth;
890
+ }
891
+ }
892
+ return { width: width, numOfSpaces: 0 };
893
+ }
894
+
895
+ /**
896
+ * Calculate the angle and the left,top position of the char that follow a path.
897
+ * It appends it to graphemeInfo to be reused later at rendering
898
+ * @private
899
+ * @param {Number} positionInPath to be measured
900
+ * @param {GraphemeBBox} graphemeInfo current grapheme box information
901
+ * @param {Object} startingPoint position of the point
902
+ */
903
+ _setGraphemeOnPath(
904
+ positionInPath: number,
905
+ graphemeInfo: GraphemeBBox<true>,
906
+ startingPoint: Point
907
+ ) {
908
+ const centerPosition = positionInPath + graphemeInfo.kernedWidth / 2,
909
+ path = this.path;
910
+
911
+ // we are at currentPositionOnPath. we want to know what point on the path is.
912
+ const info = getPointOnPath(path.path, centerPosition, path.segmentsInfo);
913
+ graphemeInfo.renderLeft = info.x - startingPoint.x;
914
+ graphemeInfo.renderTop = info.y - startingPoint.y;
915
+ graphemeInfo.angle = info.angle + (this.pathSide === 'right' ? Math.PI : 0);
916
+ }
917
+
918
+ /**
919
+ *
920
+ * @param {String} grapheme to be measured
921
+ * @param {Number} lineIndex index of the line where the char is
922
+ * @param {Number} charIndex position in the line
923
+ * @param {String} [prevGrapheme] character preceding the one to be measured
924
+ * @returns {GraphemeBBox} grapheme bbox
925
+ */
926
+ _getGraphemeBox(
927
+ grapheme: string,
928
+ lineIndex: number,
929
+ charIndex: number,
930
+ prevGrapheme?: string,
931
+ skipLeft?: boolean
932
+ ): GraphemeBBox {
933
+ const style = this.getCompleteStyleDeclaration(lineIndex, charIndex),
934
+ prevStyle = prevGrapheme
935
+ ? this.getCompleteStyleDeclaration(lineIndex, charIndex - 1)
936
+ : {},
937
+ info = this._measureChar(grapheme, style, prevGrapheme, prevStyle);
938
+ let kernedWidth = info.kernedWidth,
939
+ width = info.width,
940
+ charSpacing;
941
+
942
+ if (this.charSpacing !== 0) {
943
+ charSpacing = this._getWidthOfCharSpacing();
944
+ width += charSpacing;
945
+ kernedWidth += charSpacing;
946
+ }
947
+
948
+ const box: GraphemeBBox = {
949
+ width,
950
+ left: 0,
951
+ height: style.fontSize,
952
+ kernedWidth,
953
+ deltaY: style.deltaY,
954
+ };
955
+ if (charIndex > 0 && !skipLeft) {
956
+ const previousBox = this.__charBounds[lineIndex][charIndex - 1];
957
+ box.left =
958
+ previousBox.left + previousBox.width + info.kernedWidth - info.width;
959
+ }
960
+ return box;
961
+ }
962
+
963
+ /**
964
+ * Calculate height of line at 'lineIndex'
965
+ * @param {Number} lineIndex index of line to calculate
966
+ * @return {Number}
967
+ */
968
+ getHeightOfLine(lineIndex: number): number {
969
+ if (this.__lineHeights[lineIndex]) {
970
+ return this.__lineHeights[lineIndex];
971
+ }
972
+
973
+ // char 0 is measured before the line cycle because it needs to char
974
+ // emptylines
975
+ let maxHeight = this.getHeightOfChar(lineIndex, 0);
976
+ for (let i = 1, len = this._textLines[lineIndex].length; i < len; i++) {
977
+ maxHeight = Math.max(this.getHeightOfChar(lineIndex, i), maxHeight);
978
+ }
979
+
980
+ return (this.__lineHeights[lineIndex] =
981
+ maxHeight * this.lineHeight * this._fontSizeMult);
982
+ }
983
+
984
+ /**
985
+ * Calculate text box height
986
+ */
987
+ calcTextHeight() {
988
+ let lineHeight,
989
+ height = 0;
990
+ for (let i = 0, len = this._textLines.length; i < len; i++) {
991
+ lineHeight = this.getHeightOfLine(i);
992
+ height += i === len - 1 ? lineHeight / this.lineHeight : lineHeight;
993
+ }
994
+ return height;
995
+ }
996
+
997
+ /**
998
+ * @private
999
+ * @return {Number} Left offset
1000
+ */
1001
+ _getLeftOffset(): number {
1002
+ return this.direction === 'ltr' ? -this.width / 2 : this.width / 2;
1003
+ }
1004
+
1005
+ /**
1006
+ * @private
1007
+ * @return {Number} Top offset
1008
+ */
1009
+ _getTopOffset(): number {
1010
+ return -this.height / 2;
1011
+ }
1012
+
1013
+ /**
1014
+ * @private
1015
+ * @param {CanvasRenderingContext2D} ctx Context to render on
1016
+ * @param {String} method Method name ("fillText" or "strokeText")
1017
+ */
1018
+ _renderTextCommon(
1019
+ ctx: CanvasRenderingContext2D,
1020
+ method: 'fillText' | 'strokeText'
1021
+ ) {
1022
+ ctx.save();
1023
+ let lineHeights = 0;
1024
+ const left = this._getLeftOffset(),
1025
+ top = this._getTopOffset();
1026
+ for (let i = 0, len = this._textLines.length; i < len; i++) {
1027
+ const heightOfLine = this.getHeightOfLine(i),
1028
+ maxHeight = heightOfLine / this.lineHeight,
1029
+ leftOffset = this._getLineLeftOffset(i);
1030
+ this._renderTextLine(
1031
+ method,
1032
+ ctx,
1033
+ this._textLines[i],
1034
+ left + leftOffset,
1035
+ top + lineHeights + maxHeight,
1036
+ i
1037
+ );
1038
+ lineHeights += heightOfLine;
1039
+ }
1040
+ ctx.restore();
1041
+ }
1042
+
1043
+ /**
1044
+ * @private
1045
+ * @param {CanvasRenderingContext2D} ctx Context to render on
1046
+ */
1047
+ _renderTextFill(ctx: CanvasRenderingContext2D) {
1048
+ if (!this.fill && !this.styleHas('fill')) {
1049
+ return;
1050
+ }
1051
+
1052
+ this._renderTextCommon(ctx, 'fillText');
1053
+ }
1054
+
1055
+ /**
1056
+ * @private
1057
+ * @param {CanvasRenderingContext2D} ctx Context to render on
1058
+ */
1059
+ _renderTextStroke(ctx: CanvasRenderingContext2D) {
1060
+ if ((!this.stroke || this.strokeWidth === 0) && this.isEmptyStyles()) {
1061
+ return;
1062
+ }
1063
+
1064
+ if (this.shadow && !this.shadow.affectStroke) {
1065
+ this._removeShadow(ctx);
1066
+ }
1067
+
1068
+ ctx.save();
1069
+ this._setLineDash(ctx, this.strokeDashArray);
1070
+ ctx.beginPath();
1071
+ this._renderTextCommon(ctx, 'strokeText');
1072
+ ctx.closePath();
1073
+ ctx.restore();
1074
+ }
1075
+
1076
+ /**
1077
+ * @private
1078
+ * @param {String} method fillText or strokeText.
1079
+ * @param {CanvasRenderingContext2D} ctx Context to render on
1080
+ * @param {Array} line Content of the line, splitted in an array by grapheme
1081
+ * @param {Number} left
1082
+ * @param {Number} top
1083
+ * @param {Number} lineIndex
1084
+ */
1085
+ _renderChars(
1086
+ method: 'fillText' | 'strokeText',
1087
+ ctx: CanvasRenderingContext2D,
1088
+ line: Array<any>,
1089
+ left: number,
1090
+ top: number,
1091
+ lineIndex: number
1092
+ ) {
1093
+ const lineHeight = this.getHeightOfLine(lineIndex),
1094
+ isJustify = this.textAlign.indexOf('justify') !== -1,
1095
+ path = this.path,
1096
+ shortCut =
1097
+ !isJustify &&
1098
+ this.charSpacing === 0 &&
1099
+ this.isEmptyStyles(lineIndex) &&
1100
+ !path,
1101
+ isLtr = this.direction === 'ltr',
1102
+ sign = this.direction === 'ltr' ? 1 : -1,
1103
+ // this was changed in the PR #7674
1104
+ // currentDirection = ctx.canvas.getAttribute('dir');
1105
+ currentDirection = ctx.direction;
1106
+
1107
+ let actualStyle,
1108
+ nextStyle,
1109
+ charsToRender = '',
1110
+ charBox,
1111
+ boxWidth = 0,
1112
+ timeToRender,
1113
+ drawingLeft;
1114
+
1115
+ ctx.save();
1116
+ if (currentDirection !== this.direction) {
1117
+ ctx.canvas.setAttribute('dir', isLtr ? 'ltr' : 'rtl');
1118
+ ctx.direction = isLtr ? 'ltr' : 'rtl';
1119
+ ctx.textAlign = isLtr ? 'left' : 'right';
1120
+ }
1121
+ top -= (lineHeight * this._fontSizeFraction) / this.lineHeight;
1122
+ if (shortCut) {
1123
+ // render all the line in one pass without checking
1124
+ // drawingLeft = isLtr ? left : left - this.getLineWidth(lineIndex);
1125
+ this._renderChar(method, ctx, lineIndex, 0, line.join(''), left, top);
1126
+ ctx.restore();
1127
+ return;
1128
+ }
1129
+ for (let i = 0, len = line.length - 1; i <= len; i++) {
1130
+ timeToRender = i === len || this.charSpacing || path;
1131
+ charsToRender += line[i];
1132
+ charBox = this.__charBounds[lineIndex][i];
1133
+ if (boxWidth === 0) {
1134
+ left += sign * (charBox.kernedWidth - charBox.width);
1135
+ boxWidth += charBox.width;
1136
+ } else {
1137
+ boxWidth += charBox.kernedWidth;
1138
+ }
1139
+ if (isJustify && !timeToRender) {
1140
+ if (this._reSpaceAndTab.test(line[i])) {
1141
+ timeToRender = true;
1142
+ }
1143
+ }
1144
+ if (!timeToRender) {
1145
+ // if we have charSpacing, we render char by char
1146
+ actualStyle =
1147
+ actualStyle || this.getCompleteStyleDeclaration(lineIndex, i);
1148
+ nextStyle = this.getCompleteStyleDeclaration(lineIndex, i + 1);
1149
+ timeToRender = hasStyleChanged(actualStyle, nextStyle, false);
1150
+ }
1151
+ if (timeToRender) {
1152
+ if (path) {
1153
+ ctx.save();
1154
+ ctx.translate(charBox.renderLeft, charBox.renderTop);
1155
+ ctx.rotate(charBox.angle);
1156
+ this._renderChar(
1157
+ method,
1158
+ ctx,
1159
+ lineIndex,
1160
+ i,
1161
+ charsToRender,
1162
+ -boxWidth / 2,
1163
+ 0
1164
+ );
1165
+ ctx.restore();
1166
+ } else {
1167
+ drawingLeft = left;
1168
+ this._renderChar(
1169
+ method,
1170
+ ctx,
1171
+ lineIndex,
1172
+ i,
1173
+ charsToRender,
1174
+ drawingLeft,
1175
+ top
1176
+ );
1177
+ }
1178
+ charsToRender = '';
1179
+ actualStyle = nextStyle;
1180
+ left += sign * boxWidth;
1181
+ boxWidth = 0;
1182
+ }
1183
+ }
1184
+ ctx.restore();
1185
+ }
1186
+
1187
+ /**
1188
+ * This function try to patch the missing gradientTransform on canvas gradients.
1189
+ * transforming a context to transform the gradient, is going to transform the stroke too.
1190
+ * we want to transform the gradient but not the stroke operation, so we create
1191
+ * a transformed gradient on a pattern and then we use the pattern instead of the gradient.
1192
+ * this method has drawbacks: is slow, is in low resolution, needs a patch for when the size
1193
+ * is limited.
1194
+ * @private
1195
+ * @param {TFiller} filler a fabric gradient instance
1196
+ * @return {CanvasPattern} a pattern to use as fill/stroke style
1197
+ */
1198
+ _applyPatternGradientTransformText(filler: TFiller) {
1199
+ const pCanvas = createCanvasElement(),
1200
+ // TODO: verify compatibility with strokeUniform
1201
+ width = this.width + this.strokeWidth,
1202
+ height = this.height + this.strokeWidth,
1203
+ pCtx = pCanvas.getContext('2d')!;
1204
+ pCanvas.width = width;
1205
+ pCanvas.height = height;
1206
+ pCtx.beginPath();
1207
+ pCtx.moveTo(0, 0);
1208
+ pCtx.lineTo(width, 0);
1209
+ pCtx.lineTo(width, height);
1210
+ pCtx.lineTo(0, height);
1211
+ pCtx.closePath();
1212
+ pCtx.translate(width / 2, height / 2);
1213
+ pCtx.fillStyle = filler.toLive(pCtx)!;
1214
+ this._applyPatternGradientTransform(pCtx, filler);
1215
+ pCtx.fill();
1216
+ return pCtx.createPattern(pCanvas, 'no-repeat')!;
1217
+ }
1218
+
1219
+ handleFiller<T extends 'fill' | 'stroke'>(
1220
+ ctx: CanvasRenderingContext2D,
1221
+ property: `${T}Style`,
1222
+ filler: TFiller | string
1223
+ ) {
1224
+ let offsetX, offsetY;
1225
+ if (filler.toLive) {
1226
+ if (
1227
+ filler.gradientUnits === 'percentage' ||
1228
+ filler.gradientTransform ||
1229
+ filler.patternTransform
1230
+ ) {
1231
+ // need to transform gradient in a pattern.
1232
+ // this is a slow process. If you are hitting this codepath, and the object
1233
+ // is not using caching, you should consider switching it on.
1234
+ // we need a canvas as big as the current object caching canvas.
1235
+ offsetX = -this.width / 2;
1236
+ offsetY = -this.height / 2;
1237
+ ctx.translate(offsetX, offsetY);
1238
+ ctx[property] = this._applyPatternGradientTransformText(filler);
1239
+ return { offsetX: offsetX, offsetY: offsetY };
1240
+ } else {
1241
+ // is a simple gradient or pattern
1242
+ ctx[property] = filler.toLive(ctx, this)!;
1243
+ return this._applyPatternGradientTransform(ctx, filler);
1244
+ }
1245
+ } else {
1246
+ // is a color
1247
+ ctx[property] = filler;
1248
+ }
1249
+ return { offsetX: 0, offsetY: 0 };
1250
+ }
1251
+
1252
+ _setStrokeStyles(
1253
+ ctx: CanvasRenderingContext2D,
1254
+ { stroke, strokeWidth }: Pick<this, 'stroke' | 'strokeWidth'>
1255
+ ) {
1256
+ ctx.lineWidth = strokeWidth;
1257
+ ctx.lineCap = this.strokeLineCap;
1258
+ ctx.lineDashOffset = this.strokeDashOffset;
1259
+ ctx.lineJoin = this.strokeLineJoin;
1260
+ ctx.miterLimit = this.strokeMiterLimit;
1261
+ return this.handleFiller(ctx, 'strokeStyle', stroke);
1262
+ }
1263
+
1264
+ _setFillStyles(ctx: CanvasRenderingContext2D, { fill }: Pick<this, 'fill'>) {
1265
+ return this.handleFiller(ctx, 'fillStyle', fill);
1266
+ }
1267
+
1268
+ /**
1269
+ * @private
1270
+ * @param {String} method
1271
+ * @param {CanvasRenderingContext2D} ctx Context to render on
1272
+ * @param {Number} lineIndex
1273
+ * @param {Number} charIndex
1274
+ * @param {String} _char
1275
+ * @param {Number} left Left coordinate
1276
+ * @param {Number} top Top coordinate
1277
+ * @param {Number} lineHeight Height of the line
1278
+ */
1279
+ _renderChar(
1280
+ method: 'fillText' | 'strokeText',
1281
+ ctx: CanvasRenderingContext2D,
1282
+ lineIndex: number,
1283
+ charIndex: number,
1284
+ _char: string,
1285
+ left: number,
1286
+ top: number
1287
+ ) {
1288
+ const decl = this._getStyleDeclaration(lineIndex, charIndex),
1289
+ fullDecl = this.getCompleteStyleDeclaration(lineIndex, charIndex),
1290
+ shouldFill = method === 'fillText' && fullDecl.fill,
1291
+ shouldStroke =
1292
+ method === 'strokeText' && fullDecl.stroke && fullDecl.strokeWidth;
1293
+ let fillOffsets, strokeOffsets;
1294
+
1295
+ if (!shouldStroke && !shouldFill) {
1296
+ return;
1297
+ }
1298
+ ctx.save();
1299
+
1300
+ shouldFill && (fillOffsets = this._setFillStyles(ctx, fullDecl));
1301
+ shouldStroke && (strokeOffsets = this._setStrokeStyles(ctx, fullDecl));
1302
+
1303
+ ctx.font = this._getFontDeclaration(fullDecl);
1304
+
1305
+ if (decl && decl.textBackgroundColor) {
1306
+ this._removeShadow(ctx);
1307
+ }
1308
+ if (decl && decl.deltaY) {
1309
+ top += decl.deltaY;
1310
+ }
1311
+ shouldFill &&
1312
+ ctx.fillText(
1313
+ _char,
1314
+ left - fillOffsets.offsetX,
1315
+ top - fillOffsets.offsetY
1316
+ );
1317
+ shouldStroke &&
1318
+ ctx.strokeText(
1319
+ _char,
1320
+ left - strokeOffsets.offsetX,
1321
+ top - strokeOffsets.offsetY
1322
+ );
1323
+ ctx.restore();
1324
+ }
1325
+
1326
+ /**
1327
+ * Turns the character into a 'superior figure' (i.e. 'superscript')
1328
+ * @param {Number} start selection start
1329
+ * @param {Number} end selection end
1330
+ */
1331
+ setSuperscript(start: number, end: number) {
1332
+ this._setScript(start, end, this.superscript);
1333
+ }
1334
+
1335
+ /**
1336
+ * Turns the character into an 'inferior figure' (i.e. 'subscript')
1337
+ * @param {Number} start selection start
1338
+ * @param {Number} end selection end
1339
+ */
1340
+ setSubscript(start: number, end: number) {
1341
+ this._setScript(start, end, this.subscript);
1342
+ }
1343
+
1344
+ /**
1345
+ * Applies 'schema' at given position
1346
+ * @private
1347
+ * @param {Number} start selection start
1348
+ * @param {Number} end selection end
1349
+ * @param {Number} schema
1350
+ */
1351
+ protected _setScript(
1352
+ start: number,
1353
+ end: number,
1354
+ schema: {
1355
+ size: number;
1356
+ baseline: number;
1357
+ }
1358
+ ) {
1359
+ const loc = this.get2DCursorLocation(start, true),
1360
+ fontSize = this.getValueOfPropertyAt(
1361
+ loc.lineIndex,
1362
+ loc.charIndex,
1363
+ 'fontSize'
1364
+ ),
1365
+ dy = this.getValueOfPropertyAt(loc.lineIndex, loc.charIndex, 'deltaY'),
1366
+ style = {
1367
+ fontSize: fontSize * schema.size,
1368
+ deltaY: dy + fontSize * schema.baseline,
1369
+ };
1370
+ this.setSelectionStyles(style, start, end);
1371
+ }
1372
+
1373
+ /**
1374
+ * @private
1375
+ * @param {Number} lineIndex index text line
1376
+ * @return {Number} Line left offset
1377
+ */
1378
+ _getLineLeftOffset(lineIndex: number): number {
1379
+ const lineWidth = this.getLineWidth(lineIndex),
1380
+ lineDiff = this.width - lineWidth,
1381
+ textAlign = this.textAlign,
1382
+ direction = this.direction,
1383
+ isEndOfWrapping = this.isEndOfWrapping(lineIndex);
1384
+ let leftOffset = 0;
1385
+ if (
1386
+ textAlign === 'justify' ||
1387
+ (textAlign === 'justify-center' && !isEndOfWrapping) ||
1388
+ (textAlign === 'justify-right' && !isEndOfWrapping) ||
1389
+ (textAlign === 'justify-left' && !isEndOfWrapping)
1390
+ ) {
1391
+ return 0;
1392
+ }
1393
+ if (textAlign === 'center') {
1394
+ leftOffset = lineDiff / 2;
1395
+ }
1396
+ if (textAlign === 'right') {
1397
+ leftOffset = lineDiff;
1398
+ }
1399
+ if (textAlign === 'justify-center') {
1400
+ leftOffset = lineDiff / 2;
1401
+ }
1402
+ if (textAlign === 'justify-right') {
1403
+ leftOffset = lineDiff;
1404
+ }
1405
+ if (direction === 'rtl') {
1406
+ if (
1407
+ textAlign === 'right' ||
1408
+ textAlign === 'justify' ||
1409
+ textAlign === 'justify-right'
1410
+ ) {
1411
+ leftOffset = 0;
1412
+ } else if (textAlign === 'left' || textAlign === 'justify-left') {
1413
+ leftOffset = -lineDiff;
1414
+ } else if (textAlign === 'center' || textAlign === 'justify-center') {
1415
+ leftOffset = -lineDiff / 2;
1416
+ }
1417
+ }
1418
+ return leftOffset;
1419
+ }
1420
+
1421
+ /**
1422
+ * @private
1423
+ */
1424
+ _clearCache() {
1425
+ this.__lineWidths = [];
1426
+ this.__lineHeights = [];
1427
+ this.__charBounds = [];
1428
+ }
1429
+
1430
+ /**
1431
+ * @private
1432
+ */
1433
+ _shouldClearDimensionCache() {
1434
+ const shouldClear =
1435
+ this._forceClearCache || this.hasStateChanged('_dimensionAffectingProps');
1436
+ if (shouldClear) {
1437
+ this.dirty = true;
1438
+ this._forceClearCache = false;
1439
+ }
1440
+ return shouldClear;
1441
+ }
1442
+
1443
+ /**
1444
+ * Measure a single line given its index. Used to calculate the initial
1445
+ * text bounding box. The values are calculated and stored in __lineWidths cache.
1446
+ * @private
1447
+ * @param {Number} lineIndex line number
1448
+ * @return {Number} Line width
1449
+ */
1450
+ getLineWidth(lineIndex: number): number {
1451
+ if (this.__lineWidths[lineIndex] !== undefined) {
1452
+ return this.__lineWidths[lineIndex];
1453
+ }
1454
+
1455
+ const { width } = this.measureLine(lineIndex);
1456
+ this.__lineWidths[lineIndex] = width;
1457
+ return width;
1458
+ }
1459
+
1460
+ _getWidthOfCharSpacing() {
1461
+ if (this.charSpacing !== 0) {
1462
+ return (this.fontSize * this.charSpacing) / 1000;
1463
+ }
1464
+ return 0;
1465
+ }
1466
+
1467
+ /**
1468
+ * Retrieves the value of property at given character position
1469
+ * @param {Number} lineIndex the line number
1470
+ * @param {Number} charIndex the character number
1471
+ * @param {String} property the property name
1472
+ * @returns the value of 'property'
1473
+ */
1474
+ getValueOfPropertyAt(lineIndex: number, charIndex: number, property: string) {
1475
+ const charStyle = this._getStyleDeclaration(lineIndex, charIndex);
1476
+ if (charStyle && typeof charStyle[property] !== 'undefined') {
1477
+ return charStyle[property];
1478
+ }
1479
+ return this[property];
1480
+ }
1481
+
1482
+ /**
1483
+ * @private
1484
+ * @param {CanvasRenderingContext2D} ctx Context to render on
1485
+ */
1486
+ _renderTextDecoration(
1487
+ ctx: CanvasRenderingContext2D,
1488
+ type: 'underline' | 'linethrough' | 'overline'
1489
+ ) {
1490
+ if (!this[type] && !this.styleHas(type)) {
1491
+ return;
1492
+ }
1493
+ let topOffset = this._getTopOffset();
1494
+ const leftOffset = this._getLeftOffset(),
1495
+ path = this.path,
1496
+ charSpacing = this._getWidthOfCharSpacing(),
1497
+ offsetY = this.offsets[type];
1498
+
1499
+ for (let i = 0, len = this._textLines.length; i < len; i++) {
1500
+ const heightOfLine = this.getHeightOfLine(i);
1501
+ if (!this[type] && !this.styleHas(type, i)) {
1502
+ topOffset += heightOfLine;
1503
+ continue;
1504
+ }
1505
+ const line = this._textLines[i];
1506
+ const maxHeight = heightOfLine / this.lineHeight;
1507
+ const lineLeftOffset = this._getLineLeftOffset(i);
1508
+ let boxStart = 0;
1509
+ let boxWidth = 0;
1510
+ let lastDecoration = this.getValueOfPropertyAt(i, 0, type);
1511
+ let lastFill = this.getValueOfPropertyAt(i, 0, 'fill');
1512
+ let currentDecoration;
1513
+ let currentFill;
1514
+ const top = topOffset + maxHeight * (1 - this._fontSizeFraction);
1515
+ let size = this.getHeightOfChar(i, 0);
1516
+ let dy = this.getValueOfPropertyAt(i, 0, 'deltaY');
1517
+ for (let j = 0, jlen = line.length; j < jlen; j++) {
1518
+ const charBox = this.__charBounds[i][j];
1519
+ currentDecoration = this.getValueOfPropertyAt(i, j, type);
1520
+ currentFill = this.getValueOfPropertyAt(i, j, 'fill');
1521
+ const currentSize = this.getHeightOfChar(i, j);
1522
+ const currentDy = this.getValueOfPropertyAt(i, j, 'deltaY');
1523
+ if (path && currentDecoration && currentFill) {
1524
+ ctx.save();
1525
+ ctx.fillStyle = lastFill;
1526
+ ctx.translate(charBox.renderLeft, charBox.renderTop);
1527
+ ctx.rotate(charBox.angle);
1528
+ ctx.fillRect(
1529
+ -charBox.kernedWidth / 2,
1530
+ offsetY * currentSize + currentDy,
1531
+ charBox.kernedWidth,
1532
+ this.fontSize / 15
1533
+ );
1534
+ ctx.restore();
1535
+ } else if (
1536
+ (currentDecoration !== lastDecoration ||
1537
+ currentFill !== lastFill ||
1538
+ currentSize !== size ||
1539
+ currentDy !== dy) &&
1540
+ boxWidth > 0
1541
+ ) {
1542
+ let drawStart = leftOffset + lineLeftOffset + boxStart;
1543
+ if (this.direction === 'rtl') {
1544
+ drawStart = this.width - drawStart - boxWidth;
1545
+ }
1546
+ if (lastDecoration && lastFill) {
1547
+ ctx.fillStyle = lastFill;
1548
+ ctx.fillRect(
1549
+ drawStart,
1550
+ top + offsetY * size + dy,
1551
+ boxWidth,
1552
+ this.fontSize / 15
1553
+ );
1554
+ }
1555
+ boxStart = charBox.left;
1556
+ boxWidth = charBox.width;
1557
+ lastDecoration = currentDecoration;
1558
+ lastFill = currentFill;
1559
+ size = currentSize;
1560
+ dy = currentDy;
1561
+ } else {
1562
+ boxWidth += charBox.kernedWidth;
1563
+ }
1564
+ }
1565
+ let drawStart = leftOffset + lineLeftOffset + boxStart;
1566
+ if (this.direction === 'rtl') {
1567
+ drawStart = this.width - drawStart - boxWidth;
1568
+ }
1569
+ ctx.fillStyle = currentFill;
1570
+ currentDecoration &&
1571
+ currentFill &&
1572
+ ctx.fillRect(
1573
+ drawStart,
1574
+ top + offsetY * size + dy,
1575
+ boxWidth - charSpacing,
1576
+ this.fontSize / 15
1577
+ );
1578
+ topOffset += heightOfLine;
1579
+ }
1580
+ // if there is text background color no
1581
+ // other shadows should be casted
1582
+ this._removeShadow(ctx);
1583
+ }
1584
+
1585
+ /**
1586
+ * return font declaration string for canvas context
1587
+ * @param {Object} [styleObject] object
1588
+ * @returns {String} font declaration formatted for canvas context.
1589
+ */
1590
+ _getFontDeclaration(
1591
+ styleObject?: TextStyleDeclaration,
1592
+ forMeasuring?: boolean
1593
+ ): string {
1594
+ const style = styleObject || this,
1595
+ family = this.fontFamily,
1596
+ fontIsGeneric = Text.genericFonts.indexOf(family.toLowerCase()) > -1;
1597
+ const fontFamily =
1598
+ family === undefined ||
1599
+ family.indexOf("'") > -1 ||
1600
+ family.indexOf(',') > -1 ||
1601
+ family.indexOf('"') > -1 ||
1602
+ fontIsGeneric
1603
+ ? style.fontFamily
1604
+ : `"${style.fontFamily}"`;
1605
+ return [
1606
+ style.fontStyle,
1607
+ style.fontWeight,
1608
+ forMeasuring ? this.CACHE_FONT_SIZE + 'px' : style.fontSize + 'px',
1609
+ fontFamily,
1610
+ ].join(' ');
1611
+ }
1612
+
1613
+ /**
1614
+ * Renders text instance on a specified context
1615
+ * @param {CanvasRenderingContext2D} ctx Context to render on
1616
+ */
1617
+ render(ctx: CanvasRenderingContext2D) {
1618
+ if (!this.visible) {
1619
+ return;
1620
+ }
1621
+ if (
1622
+ this.canvas &&
1623
+ this.canvas.skipOffscreen &&
1624
+ !this.group &&
1625
+ !this.isOnScreen()
1626
+ ) {
1627
+ return;
1628
+ }
1629
+ if (this._shouldClearDimensionCache()) {
1630
+ this.initDimensions();
1631
+ }
1632
+ super.render(ctx);
1633
+ }
1634
+
1635
+ /**
1636
+ * Override this method to customize grapheme splitting
1637
+ * @todo the util `graphemeSplit` needs to be injectable in some way.
1638
+ * is more comfortable to inject the correct util rather than having to override text
1639
+ * in the middle of the prototype chain
1640
+ * @param {string} value
1641
+ * @returns {string[]} array of graphemes
1642
+ */
1643
+ graphemeSplit(value: string): string[] {
1644
+ return graphemeSplit(value);
1645
+ }
1646
+
1647
+ /**
1648
+ * Returns the text as an array of lines.
1649
+ * @param {String} text text to split
1650
+ * @returns Lines in the text
1651
+ */
1652
+ _splitTextIntoLines(text: string) {
1653
+ const lines = text.split(this._reNewline),
1654
+ newLines = new Array<string[]>(lines.length),
1655
+ newLine = ['\n'];
1656
+ let newText: string[] = [];
1657
+ for (let i = 0; i < lines.length; i++) {
1658
+ newLines[i] = this.graphemeSplit(lines[i]);
1659
+ newText = newText.concat(newLines[i], newLine);
1660
+ }
1661
+ newText.pop();
1662
+ return {
1663
+ _unwrappedLines: newLines,
1664
+ lines: lines,
1665
+ graphemeText: newText,
1666
+ graphemeLines: newLines,
1667
+ };
1668
+ }
1669
+
1670
+ /**
1671
+ * Returns object representation of an instance
1672
+ * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
1673
+ * @return {Object} Object representation of an instance
1674
+ */
1675
+ toObject(propertiesToInclude: (keyof this)[] = []) {
1676
+ return {
1677
+ ...super.toObject([...additionalProps, ...propertiesToInclude]),
1678
+ styles: stylesToArray(this.styles, this.text),
1679
+ ...(this.path ? { path: this.path.toObject() } : {}),
1680
+ };
1681
+ }
1682
+
1683
+ set(key: string | any, value?: any) {
1684
+ super.set(key, value);
1685
+ let needsDims = false;
1686
+ let isAddingPath = false;
1687
+ if (typeof key === 'object') {
1688
+ for (const _key in key) {
1689
+ if (_key === 'path') {
1690
+ this.setPathInfo();
1691
+ }
1692
+ needsDims =
1693
+ needsDims || this._dimensionAffectingProps.indexOf(_key) !== -1;
1694
+ isAddingPath = isAddingPath || _key === 'path';
1695
+ }
1696
+ } else {
1697
+ needsDims = this._dimensionAffectingProps.indexOf(key) !== -1;
1698
+ isAddingPath = key === 'path';
1699
+ }
1700
+ if (isAddingPath) {
1701
+ this.setPathInfo();
1702
+ }
1703
+ if (needsDims && this.initialized) {
1704
+ this.initDimensions();
1705
+ this.setCoords();
1706
+ }
1707
+ return this;
1708
+ }
1709
+
1710
+ /**
1711
+ * Returns complexity of an instance
1712
+ * @return {Number} complexity
1713
+ */
1714
+ complexity(): number {
1715
+ return 1;
1716
+ }
1717
+
1718
+ static genericFonts = [
1719
+ 'sans-serif',
1720
+ 'serif',
1721
+ 'cursive',
1722
+ 'fantasy',
1723
+ 'monospace',
1724
+ ];
1725
+
1726
+ /* _FROM_SVG_START_ */
1727
+
1728
+ /**
1729
+ * List of attribute names to account for when parsing SVG element (used by {@link Text.fromElement})
1730
+ * @static
1731
+ * @memberOf Text
1732
+ * @see: http://www.w3.org/TR/SVG/text.html#TextElement
1733
+ */
1734
+ static ATTRIBUTE_NAMES = SHARED_ATTRIBUTES.concat(
1735
+ 'x',
1736
+ 'y',
1737
+ 'dx',
1738
+ 'dy',
1739
+ 'font-family',
1740
+ 'font-style',
1741
+ 'font-weight',
1742
+ 'font-size',
1743
+ 'letter-spacing',
1744
+ 'text-decoration',
1745
+ 'text-anchor'
1746
+ );
1747
+
1748
+ /**
1749
+ * Returns Text instance from an SVG element (<b>not yet implemented</b>)
1750
+ * @static
1751
+ * @memberOf Text
1752
+ * @param {SVGElement} element Element to parse
1753
+ * @param {Function} callback callback function invoked after parsing
1754
+ * @param {Object} [options] Options object
1755
+ */
1756
+ static fromElement(
1757
+ element: SVGElement,
1758
+ callback: (text: Text | null) => any,
1759
+ options: object
1760
+ ) {
1761
+ if (!element) {
1762
+ return callback(null);
1763
+ }
1764
+
1765
+ const parsedAttributes = parseAttributes(element, Text.ATTRIBUTE_NAMES),
1766
+ parsedAnchor = parsedAttributes.textAnchor || 'left';
1767
+ options = Object.assign({}, options, parsedAttributes);
1768
+
1769
+ options.top = options.top || 0;
1770
+ options.left = options.left || 0;
1771
+ if (parsedAttributes.textDecoration) {
1772
+ const textDecoration = parsedAttributes.textDecoration;
1773
+ if (textDecoration.indexOf('underline') !== -1) {
1774
+ options.underline = true;
1775
+ }
1776
+ if (textDecoration.indexOf('overline') !== -1) {
1777
+ options.overline = true;
1778
+ }
1779
+ if (textDecoration.indexOf('line-through') !== -1) {
1780
+ options.linethrough = true;
1781
+ }
1782
+ delete options.textDecoration;
1783
+ }
1784
+ if ('dx' in parsedAttributes) {
1785
+ options.left += parsedAttributes.dx;
1786
+ }
1787
+ if ('dy' in parsedAttributes) {
1788
+ options.top += parsedAttributes.dy;
1789
+ }
1790
+ if (!('fontSize' in options)) {
1791
+ options.fontSize = DEFAULT_SVG_FONT_SIZE;
1792
+ }
1793
+
1794
+ let textContent = '';
1795
+
1796
+ // The XML is not properly parsed in IE9 so a workaround to get
1797
+ // textContent is through firstChild.data. Another workaround would be
1798
+ // to convert XML loaded from a file to be converted using DOMParser (same way loadSVGFromString() does)
1799
+ if (!('textContent' in element)) {
1800
+ if ('firstChild' in element && element.firstChild !== null) {
1801
+ if ('data' in element.firstChild && element.firstChild.data !== null) {
1802
+ textContent = element.firstChild.data;
1803
+ }
1804
+ }
1805
+ } else {
1806
+ textContent = element.textContent;
1807
+ }
1808
+
1809
+ textContent = textContent
1810
+ .replace(/^\s+|\s+$|\n+/g, '')
1811
+ .replace(/\s+/g, ' ');
1812
+ const originalStrokeWidth = options.strokeWidth;
1813
+ options.strokeWidth = 0;
1814
+
1815
+ const text = new this(textContent, options),
1816
+ textHeightScaleFactor = text.getScaledHeight() / text.height,
1817
+ lineHeightDiff =
1818
+ (text.height + text.strokeWidth) * text.lineHeight - text.height,
1819
+ scaledDiff = lineHeightDiff * textHeightScaleFactor,
1820
+ textHeight = text.getScaledHeight() + scaledDiff;
1821
+
1822
+ let offX = 0;
1823
+ /*
1824
+ Adjust positioning:
1825
+ x/y attributes in SVG correspond to the bottom-left corner of text bounding box
1826
+ fabric output by default at top, left.
1827
+ */
1828
+ if (parsedAnchor === 'center') {
1829
+ offX = text.getScaledWidth() / 2;
1830
+ }
1831
+ if (parsedAnchor === 'right') {
1832
+ offX = text.getScaledWidth();
1833
+ }
1834
+ text.set({
1835
+ left: text.left - offX,
1836
+ top:
1837
+ text.top -
1838
+ (textHeight - text.fontSize * (0.07 + text._fontSizeFraction)) /
1839
+ text.lineHeight,
1840
+ strokeWidth:
1841
+ typeof originalStrokeWidth !== 'undefined' ? originalStrokeWidth : 1,
1842
+ });
1843
+ callback(text);
1844
+ }
1845
+
1846
+ /* _FROM_SVG_END_ */
1847
+
1848
+ /**
1849
+ * Returns Text instance from an object representation
1850
+ * @param {Object} object plain js Object to create an instance from
1851
+ * @returns {Promise<Text>}
1852
+ */
1853
+ static fromObject(object: Record<string, any>): Promise<Text> {
1854
+ return this._fromObject(
1855
+ {
1856
+ ...object,
1857
+ styles: stylesFromArray(object.styles || {}, object.text),
1858
+ },
1859
+ {
1860
+ extraParam: 'text',
1861
+ }
1862
+ );
1863
+ }
1864
+ }
1865
+
1866
+ // @TODO: Many things here are configuration related and shouldn't be on the class nor prototype
1867
+ // regexes, list of properties that are not suppose to change by instances, magic consts.
1868
+ // this will be a separated effort
1869
+ export const textDefaultValues: Partial<TClassProperties<Text>> = {
1870
+ _dimensionAffectingProps: [
1871
+ 'fontSize',
1872
+ 'fontWeight',
1873
+ 'fontFamily',
1874
+ 'fontStyle',
1875
+ 'lineHeight',
1876
+ 'text',
1877
+ 'charSpacing',
1878
+ 'textAlign',
1879
+ 'styles',
1880
+ 'path',
1881
+ 'pathStartOffset',
1882
+ 'pathSide',
1883
+ 'pathAlign',
1884
+ ],
1885
+ _styleProperties: [
1886
+ 'stroke',
1887
+ 'strokeWidth',
1888
+ 'fill',
1889
+ 'fontFamily',
1890
+ 'fontSize',
1891
+ 'fontWeight',
1892
+ 'fontStyle',
1893
+ 'underline',
1894
+ 'overline',
1895
+ 'linethrough',
1896
+ 'deltaY',
1897
+ 'textBackgroundColor',
1898
+ ],
1899
+ _reNewline: /\r?\n/,
1900
+ _reSpacesAndTabs: /[ \t\r]/g,
1901
+ _reSpaceAndTab: /[ \t\r]/,
1902
+ _reWords: /\S+/g,
1903
+ type: 'text',
1904
+ fontSize: 40,
1905
+ fontWeight: 'normal',
1906
+ fontFamily: 'Times New Roman',
1907
+ underline: false,
1908
+ overline: false,
1909
+ linethrough: false,
1910
+ textAlign: 'left',
1911
+ fontStyle: 'normal',
1912
+ lineHeight: 1.16,
1913
+ superscript: {
1914
+ size: 0.6, // fontSize factor
1915
+ baseline: -0.35, // baseline-shift factor (upwards)
1916
+ },
1917
+ subscript: {
1918
+ size: 0.6, // fontSize factor
1919
+ baseline: 0.11, // baseline-shift factor (downwards)
1920
+ },
1921
+ textBackgroundColor: '',
1922
+ cacheProperties: [...cacheProperties, ...additionalProps],
1923
+ stroke: null,
1924
+ shadow: null,
1925
+ path: null,
1926
+ pathStartOffset: 0,
1927
+ pathSide: 'left',
1928
+ pathAlign: 'baseline',
1929
+ _fontSizeFraction: 0.222,
1930
+ offsets: {
1931
+ underline: 0.1,
1932
+ linethrough: -0.315,
1933
+ overline: -0.88,
1934
+ },
1935
+ _fontSizeMult: 1.13,
1936
+ charSpacing: 0,
1937
+ styles: null,
1938
+ deltaY: 0,
1939
+ direction: 'ltr',
1940
+ CACHE_FONT_SIZE: 400,
1941
+ MIN_TEXT_WIDTH: 2,
1942
+ };
1943
+
1944
+ Object.assign(Text.prototype, textDefaultValues);
1945
+
1946
+ applyMixins(Text, [TextSVGExportMixin]);
1947
+ classRegistry.setClass(Text);
1948
+ classRegistry.setSVGClass(Text);