fabric 7.0.0 → 7.2.0

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 (323) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/CHANGELOG.md +21 -0
  3. package/dist/extensions/cropping_controls/croppingControls.d.ts +16 -0
  4. package/dist/extensions/cropping_controls/croppingControls.d.ts.map +1 -0
  5. package/dist/extensions/cropping_controls/croppingHandlers.d.ts +39 -0
  6. package/dist/extensions/cropping_controls/croppingHandlers.d.ts.map +1 -0
  7. package/dist/extensions/cropping_controls/enterCropMode.d.ts +7 -0
  8. package/dist/extensions/cropping_controls/enterCropMode.d.ts.map +1 -0
  9. package/dist/extensions/cropping_controls/renderCornerControl.d.ts +14 -0
  10. package/dist/extensions/cropping_controls/renderCornerControl.d.ts.map +1 -0
  11. package/dist/extensions/index.d.ts +3 -0
  12. package/dist/extensions/index.d.ts.map +1 -1
  13. package/dist/fabric.d.ts +1 -0
  14. package/dist/fabric.d.ts.map +1 -1
  15. package/dist/index.js +628 -537
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.min.js +1 -1
  18. package/dist/index.min.js.map +1 -1
  19. package/dist/index.min.mjs +1 -1
  20. package/dist/index.min.mjs.map +1 -1
  21. package/dist/index.mjs +628 -537
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/index.node.cjs +628 -537
  24. package/dist/index.node.cjs.map +1 -1
  25. package/dist/index.node.mjs +628 -537
  26. package/dist/index.node.mjs.map +1 -1
  27. package/dist/package.json.min.mjs +1 -1
  28. package/dist/package.json.mjs +1 -1
  29. package/dist/src/EventTypeDefs.d.ts +5 -0
  30. package/dist/src/EventTypeDefs.d.ts.map +1 -1
  31. package/dist/src/Pattern/Pattern.d.ts.map +1 -1
  32. package/dist/src/Pattern/Pattern.min.mjs +1 -1
  33. package/dist/src/Pattern/Pattern.min.mjs.map +1 -1
  34. package/dist/src/Pattern/Pattern.mjs +2 -1
  35. package/dist/src/Pattern/Pattern.mjs.map +1 -1
  36. package/dist/src/Shadow.d.ts +1 -1
  37. package/dist/src/Shadow.d.ts.map +1 -1
  38. package/dist/src/Shadow.min.mjs +1 -1
  39. package/dist/src/Shadow.min.mjs.map +1 -1
  40. package/dist/src/Shadow.mjs +5 -4
  41. package/dist/src/Shadow.mjs.map +1 -1
  42. package/dist/src/canvas/CanvasOptions.d.ts.map +1 -1
  43. package/dist/src/canvas/CanvasOptions.min.mjs.map +1 -1
  44. package/dist/src/canvas/CanvasOptions.mjs.map +1 -1
  45. package/dist/src/canvas/SelectableCanvas.d.ts +2 -0
  46. package/dist/src/canvas/SelectableCanvas.d.ts.map +1 -1
  47. package/dist/src/canvas/SelectableCanvas.min.mjs +1 -1
  48. package/dist/src/canvas/SelectableCanvas.min.mjs.map +1 -1
  49. package/dist/src/canvas/SelectableCanvas.mjs +33 -13
  50. package/dist/src/canvas/SelectableCanvas.mjs.map +1 -1
  51. package/dist/src/canvas/StaticCanvas.d.ts.map +1 -1
  52. package/dist/src/canvas/StaticCanvas.min.mjs +1 -1
  53. package/dist/src/canvas/StaticCanvas.min.mjs.map +1 -1
  54. package/dist/src/canvas/StaticCanvas.mjs +3 -1
  55. package/dist/src/canvas/StaticCanvas.mjs.map +1 -1
  56. package/dist/src/canvas/StaticCanvasOptions.d.ts.map +1 -1
  57. package/dist/src/canvas/StaticCanvasOptions.min.mjs.map +1 -1
  58. package/dist/src/canvas/StaticCanvasOptions.mjs.map +1 -1
  59. package/dist/src/constants.d.ts +1 -0
  60. package/dist/src/constants.d.ts.map +1 -1
  61. package/dist/src/constants.min.mjs.map +1 -1
  62. package/dist/src/constants.mjs.map +1 -1
  63. package/dist/src/controls/Control.d.ts +22 -1
  64. package/dist/src/controls/Control.d.ts.map +1 -1
  65. package/dist/src/controls/Control.min.mjs +1 -1
  66. package/dist/src/controls/Control.min.mjs.map +1 -1
  67. package/dist/src/controls/Control.mjs +45 -1
  68. package/dist/src/controls/Control.mjs.map +1 -1
  69. package/dist/src/controls/changeWidth.d.ts +22 -0
  70. package/dist/src/controls/changeWidth.d.ts.map +1 -1
  71. package/dist/src/controls/changeWidth.min.mjs +1 -1
  72. package/dist/src/controls/changeWidth.min.mjs.map +1 -1
  73. package/dist/src/controls/changeWidth.mjs +46 -18
  74. package/dist/src/controls/changeWidth.mjs.map +1 -1
  75. package/dist/src/controls/controlRendering.d.ts.map +1 -1
  76. package/dist/src/controls/controlRendering.min.mjs +1 -1
  77. package/dist/src/controls/controlRendering.min.mjs.map +1 -1
  78. package/dist/src/controls/controlRendering.mjs +18 -34
  79. package/dist/src/controls/controlRendering.mjs.map +1 -1
  80. package/dist/src/controls/index.d.ts +2 -1
  81. package/dist/src/controls/index.d.ts.map +1 -1
  82. package/dist/src/controls/index.min.mjs +1 -1
  83. package/dist/src/controls/index.mjs +1 -1
  84. package/dist/src/gradient/Gradient.d.ts.map +1 -1
  85. package/dist/src/gradient/Gradient.min.mjs +1 -1
  86. package/dist/src/gradient/Gradient.min.mjs.map +1 -1
  87. package/dist/src/gradient/Gradient.mjs +19 -6
  88. package/dist/src/gradient/Gradient.mjs.map +1 -1
  89. package/dist/src/shapes/Circle.d.ts.map +1 -1
  90. package/dist/src/shapes/Circle.min.mjs +1 -1
  91. package/dist/src/shapes/Circle.min.mjs.map +1 -1
  92. package/dist/src/shapes/Circle.mjs +10 -7
  93. package/dist/src/shapes/Circle.mjs.map +1 -1
  94. package/dist/src/shapes/Ellipse.d.ts.map +1 -1
  95. package/dist/src/shapes/Ellipse.min.mjs +1 -1
  96. package/dist/src/shapes/Ellipse.min.mjs.map +1 -1
  97. package/dist/src/shapes/Ellipse.mjs +2 -1
  98. package/dist/src/shapes/Ellipse.mjs.map +1 -1
  99. package/dist/src/shapes/Group.d.ts.map +1 -1
  100. package/dist/src/shapes/Group.min.mjs +1 -1
  101. package/dist/src/shapes/Group.min.mjs.map +1 -1
  102. package/dist/src/shapes/Group.mjs +2 -1
  103. package/dist/src/shapes/Group.mjs.map +1 -1
  104. package/dist/src/shapes/IText/IText.d.ts.map +1 -1
  105. package/dist/src/shapes/IText/IText.min.mjs.map +1 -1
  106. package/dist/src/shapes/IText/IText.mjs.map +1 -1
  107. package/dist/src/shapes/Image.d.ts +1 -1
  108. package/dist/src/shapes/Image.d.ts.map +1 -1
  109. package/dist/src/shapes/Image.min.mjs +1 -1
  110. package/dist/src/shapes/Image.min.mjs.map +1 -1
  111. package/dist/src/shapes/Image.mjs +4 -3
  112. package/dist/src/shapes/Image.mjs.map +1 -1
  113. package/dist/src/shapes/Line.d.ts.map +1 -1
  114. package/dist/src/shapes/Line.min.mjs +1 -1
  115. package/dist/src/shapes/Line.min.mjs.map +1 -1
  116. package/dist/src/shapes/Line.mjs +6 -10
  117. package/dist/src/shapes/Line.mjs.map +1 -1
  118. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
  119. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs +1 -1
  120. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs.map +1 -1
  121. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs +5 -4
  122. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs.map +1 -1
  123. package/dist/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
  124. package/dist/src/shapes/Object/InteractiveObject.min.mjs.map +1 -1
  125. package/dist/src/shapes/Object/InteractiveObject.mjs.map +1 -1
  126. package/dist/src/shapes/Object/Object.d.ts.map +1 -1
  127. package/dist/src/shapes/Object/Object.min.mjs +1 -1
  128. package/dist/src/shapes/Object/Object.min.mjs.map +1 -1
  129. package/dist/src/shapes/Object/Object.mjs +3 -0
  130. package/dist/src/shapes/Object/Object.mjs.map +1 -1
  131. package/dist/src/shapes/Object/ObjectGeometry.min.mjs +1 -1
  132. package/dist/src/shapes/Object/ObjectGeometry.min.mjs.map +1 -1
  133. package/dist/src/shapes/Object/ObjectGeometry.mjs +1 -1
  134. package/dist/src/shapes/Object/ObjectGeometry.mjs.map +1 -1
  135. package/dist/src/shapes/Object/types/FabricObjectProps.d.ts.map +1 -1
  136. package/dist/src/shapes/Object/types/ObjectProps.d.ts.map +1 -1
  137. package/dist/src/shapes/Path.d.ts.map +1 -1
  138. package/dist/src/shapes/Path.min.mjs.map +1 -1
  139. package/dist/src/shapes/Path.mjs +1 -2
  140. package/dist/src/shapes/Path.mjs.map +1 -1
  141. package/dist/src/shapes/Polyline.d.ts.map +1 -1
  142. package/dist/src/shapes/Polyline.min.mjs +1 -1
  143. package/dist/src/shapes/Polyline.min.mjs.map +1 -1
  144. package/dist/src/shapes/Polyline.mjs +10 -6
  145. package/dist/src/shapes/Polyline.mjs.map +1 -1
  146. package/dist/src/shapes/Rect.d.ts.map +1 -1
  147. package/dist/src/shapes/Rect.min.mjs +1 -1
  148. package/dist/src/shapes/Rect.min.mjs.map +1 -1
  149. package/dist/src/shapes/Rect.mjs +2 -1
  150. package/dist/src/shapes/Rect.mjs.map +1 -1
  151. package/dist/src/shapes/Text/StyledText.d.ts.map +1 -1
  152. package/dist/src/shapes/Text/StyledText.min.mjs.map +1 -1
  153. package/dist/src/shapes/Text/StyledText.mjs +0 -3
  154. package/dist/src/shapes/Text/StyledText.mjs.map +1 -1
  155. package/dist/src/shapes/Text/Text.d.ts.map +1 -1
  156. package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
  157. package/dist/src/shapes/Text/Text.mjs.map +1 -1
  158. package/dist/src/shapes/Text/TextSVGExportMixin.d.ts.map +1 -1
  159. package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs +1 -1
  160. package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs.map +1 -1
  161. package/dist/src/shapes/Text/TextSVGExportMixin.mjs +5 -6
  162. package/dist/src/shapes/Text/TextSVGExportMixin.mjs.map +1 -1
  163. package/dist/src/shapes/Textbox.d.ts.map +1 -1
  164. package/dist/src/shapes/Textbox.min.mjs.map +1 -1
  165. package/dist/src/shapes/Textbox.mjs.map +1 -1
  166. package/dist/src/shapes/Triangle.d.ts.map +1 -1
  167. package/dist/src/shapes/Triangle.min.mjs.map +1 -1
  168. package/dist/src/shapes/Triangle.mjs.map +1 -1
  169. package/dist/src/util/lang_string.d.ts +1 -1
  170. package/dist/src/util/lang_string.d.ts.map +1 -1
  171. package/dist/src/util/lang_string.min.mjs +1 -1
  172. package/dist/src/util/lang_string.min.mjs.map +1 -1
  173. package/dist/src/util/lang_string.mjs +1 -1
  174. package/dist/src/util/lang_string.mjs.map +1 -1
  175. package/dist/src/util/misc/svgParsing.d.ts.map +1 -1
  176. package/dist/src/util/misc/svgParsing.min.mjs +1 -1
  177. package/dist/src/util/misc/svgParsing.min.mjs.map +1 -1
  178. package/dist/src/util/misc/svgParsing.mjs +2 -1
  179. package/dist/src/util/misc/svgParsing.mjs.map +1 -1
  180. package/dist-extensions/cropping_controls/croppingControls.mjs +140 -0
  181. package/dist-extensions/cropping_controls/croppingControls.mjs.map +1 -0
  182. package/dist-extensions/cropping_controls/croppingHandlers.mjs +228 -0
  183. package/dist-extensions/cropping_controls/croppingHandlers.mjs.map +1 -0
  184. package/dist-extensions/cropping_controls/enterCropMode.mjs +38 -0
  185. package/dist-extensions/cropping_controls/enterCropMode.mjs.map +1 -0
  186. package/dist-extensions/cropping_controls/renderCornerControl.mjs +45 -0
  187. package/dist-extensions/cropping_controls/renderCornerControl.mjs.map +1 -0
  188. package/dist-extensions/extensions/cropping_controls/croppingControls.d.ts +16 -0
  189. package/dist-extensions/extensions/cropping_controls/croppingControls.d.ts.map +1 -0
  190. package/dist-extensions/extensions/cropping_controls/croppingHandlers.d.ts +39 -0
  191. package/dist-extensions/extensions/cropping_controls/croppingHandlers.d.ts.map +1 -0
  192. package/dist-extensions/extensions/cropping_controls/enterCropMode.d.ts +7 -0
  193. package/dist-extensions/extensions/cropping_controls/enterCropMode.d.ts.map +1 -0
  194. package/dist-extensions/extensions/cropping_controls/renderCornerControl.d.ts +14 -0
  195. package/dist-extensions/extensions/cropping_controls/renderCornerControl.d.ts.map +1 -0
  196. package/dist-extensions/extensions/index.d.ts +3 -0
  197. package/dist-extensions/extensions/index.d.ts.map +1 -1
  198. package/dist-extensions/fabric-extensions.min.js +1 -1
  199. package/dist-extensions/fabric-extensions.min.js.map +1 -1
  200. package/dist-extensions/fabric.d.ts +1 -0
  201. package/dist-extensions/fabric.d.ts.map +1 -1
  202. package/dist-extensions/index.mjs +3 -0
  203. package/dist-extensions/index.mjs.map +1 -1
  204. package/dist-extensions/src/EventTypeDefs.d.ts +5 -0
  205. package/dist-extensions/src/EventTypeDefs.d.ts.map +1 -1
  206. package/dist-extensions/src/Pattern/Pattern.d.ts.map +1 -1
  207. package/dist-extensions/src/Shadow.d.ts +1 -1
  208. package/dist-extensions/src/Shadow.d.ts.map +1 -1
  209. package/dist-extensions/src/canvas/CanvasOptions.d.ts.map +1 -1
  210. package/dist-extensions/src/canvas/SelectableCanvas.d.ts +2 -0
  211. package/dist-extensions/src/canvas/SelectableCanvas.d.ts.map +1 -1
  212. package/dist-extensions/src/canvas/StaticCanvas.d.ts.map +1 -1
  213. package/dist-extensions/src/canvas/StaticCanvasOptions.d.ts.map +1 -1
  214. package/dist-extensions/src/constants.d.ts +1 -0
  215. package/dist-extensions/src/constants.d.ts.map +1 -1
  216. package/dist-extensions/src/controls/Control.d.ts +22 -1
  217. package/dist-extensions/src/controls/Control.d.ts.map +1 -1
  218. package/dist-extensions/src/controls/changeWidth.d.ts +22 -0
  219. package/dist-extensions/src/controls/changeWidth.d.ts.map +1 -1
  220. package/dist-extensions/src/controls/controlRendering.d.ts.map +1 -1
  221. package/dist-extensions/src/controls/index.d.ts +2 -1
  222. package/dist-extensions/src/controls/index.d.ts.map +1 -1
  223. package/dist-extensions/src/gradient/Gradient.d.ts.map +1 -1
  224. package/dist-extensions/src/shapes/Circle.d.ts.map +1 -1
  225. package/dist-extensions/src/shapes/Ellipse.d.ts.map +1 -1
  226. package/dist-extensions/src/shapes/Group.d.ts.map +1 -1
  227. package/dist-extensions/src/shapes/IText/IText.d.ts.map +1 -1
  228. package/dist-extensions/src/shapes/Image.d.ts.map +1 -1
  229. package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
  230. package/dist-extensions/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
  231. package/dist-extensions/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
  232. package/dist-extensions/src/shapes/Object/Object.d.ts.map +1 -1
  233. package/dist-extensions/src/shapes/Object/types/FabricObjectProps.d.ts.map +1 -1
  234. package/dist-extensions/src/shapes/Object/types/ObjectProps.d.ts.map +1 -1
  235. package/dist-extensions/src/shapes/Path.d.ts +1 -1
  236. package/dist-extensions/src/shapes/Path.d.ts.map +1 -1
  237. package/dist-extensions/src/shapes/Polyline.d.ts.map +1 -1
  238. package/dist-extensions/src/shapes/Rect.d.ts.map +1 -1
  239. package/dist-extensions/src/shapes/Text/StyledText.d.ts.map +1 -1
  240. package/dist-extensions/src/shapes/Text/Text.d.ts.map +1 -1
  241. package/dist-extensions/src/shapes/Text/TextSVGExportMixin.d.ts.map +1 -1
  242. package/dist-extensions/src/shapes/Textbox.d.ts.map +1 -1
  243. package/dist-extensions/src/shapes/Triangle.d.ts.map +1 -1
  244. package/dist-extensions/src/util/lang_string.d.ts +1 -1
  245. package/dist-extensions/src/util/lang_string.d.ts.map +1 -1
  246. package/dist-extensions/src/util/misc/svgParsing.d.ts.map +1 -1
  247. package/eslint.config.mjs +2 -0
  248. package/extensions/cropping_controls/croppingControls.spec.ts +115 -0
  249. package/extensions/cropping_controls/croppingControls.ts +150 -0
  250. package/extensions/cropping_controls/croppingHandlers.spec.ts +579 -0
  251. package/extensions/cropping_controls/croppingHandlers.ts +285 -0
  252. package/extensions/cropping_controls/enterCropMode.ts +30 -0
  253. package/extensions/cropping_controls/renderCornerControl.ts +53 -0
  254. package/extensions/index.ts +9 -0
  255. package/fabric.ts +1 -0
  256. package/package.json +17 -8
  257. package/src/ClassRegistry.spec.ts +18 -19
  258. package/src/EventTypeDefs.ts +15 -11
  259. package/src/Pattern/Pattern.spec.ts +12 -0
  260. package/src/Pattern/Pattern.ts +3 -2
  261. package/src/Shadow.ts +9 -8
  262. package/src/brushes/PencilBrush.spec.ts +11 -11
  263. package/src/canvas/Canvas-dispose.spec.ts +8 -7
  264. package/src/canvas/Canvas.spec.ts +27 -29
  265. package/src/canvas/CanvasOptions.ts +2 -1
  266. package/src/canvas/SelectableCanvas.ts +38 -15
  267. package/src/canvas/StaticCanvas.spec.ts +20 -0
  268. package/src/canvas/StaticCanvas.ts +7 -4
  269. package/src/canvas/StaticCanvasOptions.ts +1 -3
  270. package/src/constants.ts +1 -0
  271. package/src/controls/Control.spec.ts +102 -0
  272. package/src/controls/Control.ts +71 -2
  273. package/src/controls/changeHeight.spec.ts +147 -0
  274. package/src/controls/changeWidth.ts +68 -35
  275. package/src/controls/controlRendering.ts +20 -48
  276. package/src/controls/index.ts +7 -1
  277. package/src/gradient/Gradient.spec.ts +101 -46
  278. package/src/gradient/Gradient.ts +27 -14
  279. package/src/shapes/Circle.spec.ts +10 -39
  280. package/src/shapes/Circle.ts +11 -11
  281. package/src/shapes/Ellipse.spec.ts +8 -37
  282. package/src/shapes/Ellipse.ts +7 -7
  283. package/src/shapes/Group.ts +3 -3
  284. package/src/shapes/IText/IText-click-behavior.spec.ts +36 -49
  285. package/src/shapes/IText/IText.ts +5 -6
  286. package/src/shapes/IText/ITextKeyBehavior.test.ts +0 -1
  287. package/src/shapes/IText/__snapshots__/ITextBehavior.test.ts.snap +6 -6
  288. package/src/shapes/Image.spec.ts +17 -33
  289. package/src/shapes/Image.ts +15 -11
  290. package/src/shapes/Line.spec.ts +4 -30
  291. package/src/shapes/Line.ts +11 -16
  292. package/src/shapes/Object/FabricObjectSVGExportMixin.ts +11 -4
  293. package/src/shapes/Object/InteractiveObject.ts +4 -4
  294. package/src/shapes/Object/Object.ts +6 -5
  295. package/src/shapes/Object/ObjectGeometry.spec.ts +15 -0
  296. package/src/shapes/Object/ObjectGeometry.ts +1 -1
  297. package/src/shapes/Object/objectSvgExport.spec.ts +112 -0
  298. package/src/shapes/Object/types/FabricObjectProps.ts +1 -4
  299. package/src/shapes/Object/types/ObjectProps.ts +1 -3
  300. package/src/shapes/Path.spec.ts +4 -27
  301. package/src/shapes/Path.ts +2 -4
  302. package/src/shapes/Polygon.spec.ts +4 -31
  303. package/src/shapes/Polyline.spec.ts +4 -31
  304. package/src/shapes/Polyline.ts +11 -12
  305. package/src/shapes/Rect.spec.ts +25 -33
  306. package/src/shapes/Rect.ts +7 -7
  307. package/src/shapes/Text/StyledText.ts +0 -3
  308. package/src/shapes/Text/Text.spec.ts +3 -32
  309. package/src/shapes/Text/Text.ts +5 -6
  310. package/src/shapes/Text/TextSVGExportMixin.spec.ts +9 -0
  311. package/src/shapes/Text/TextSVGExportMixin.ts +14 -16
  312. package/src/shapes/Text/__snapshots__/Text.spec.ts.snap +1 -1
  313. package/src/shapes/Text/__snapshots__/TextSVGExportMixin.spec.ts.snap +1 -1
  314. package/src/shapes/Textbox.spec.ts +5 -5
  315. package/src/shapes/Textbox.ts +6 -5
  316. package/src/shapes/Triangle.ts +4 -4
  317. package/src/shapes/__snapshots__/Image.spec.ts.snap +4 -4
  318. package/src/shapes/__snapshots__/Textbox.spec.ts.snap +5 -5
  319. package/src/util/lang_string.ts +3 -2
  320. package/src/util/misc/svgParsing.ts +2 -1
  321. package/tsconfig.spec.json +1 -0
  322. package/vitest.config.ts +12 -2
  323. package/vitest.extend.ts +6 -2
@@ -414,7 +414,7 @@ class Cache {
414
414
  }
415
415
  const cache = new Cache();
416
416
 
417
- var version = "7.0.0";
417
+ var version = "7.2.0";
418
418
 
419
419
  // use this syntax so babel plugin see this import here
420
420
  const VERSION = version;
@@ -2249,6 +2249,111 @@ const staticCanvasDefaults = {
2249
2249
  patternQuality: 'best'
2250
2250
  };
2251
2251
 
2252
+ /**
2253
+ * Capitalizes a string
2254
+ * @param {String} string String to capitalize
2255
+ * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
2256
+ * and other letters stay untouched, if false first letter is capitalized
2257
+ * and other letters are converted to lowercase.
2258
+ * @return {String} Capitalized version of a string
2259
+ */
2260
+ const capitalize = function (string) {
2261
+ let firstLetterOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2262
+ return `${string.charAt(0).toUpperCase()}${firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase()}`;
2263
+ };
2264
+
2265
+ /**
2266
+ * Escapes XML in a string
2267
+ * @param {String} string String to escape
2268
+ * @return {String} Escaped version of a string
2269
+ */
2270
+ const escapeXml = stringOrNumber => stringOrNumber.toString().replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
2271
+ let segmenter;
2272
+ const getSegmenter = () => {
2273
+ if (!segmenter) {
2274
+ segmenter = 'Intl' in getFabricWindow() && 'Segmenter' in Intl && new Intl.Segmenter(undefined, {
2275
+ granularity: 'grapheme'
2276
+ });
2277
+ }
2278
+ return segmenter;
2279
+ };
2280
+
2281
+ /**
2282
+ * Divide a string in the user perceived single units
2283
+ * @param {String} textstring String to escape
2284
+ * @return {Array} array containing the graphemes
2285
+ */
2286
+ const graphemeSplit = textstring => {
2287
+ segmenter || getSegmenter();
2288
+ if (segmenter) {
2289
+ const segments = segmenter.segment(textstring);
2290
+ return Array.from(segments).map(_ref => {
2291
+ let {
2292
+ segment
2293
+ } = _ref;
2294
+ return segment;
2295
+ });
2296
+ }
2297
+
2298
+ //Fallback
2299
+ return graphemeSplitImpl(textstring);
2300
+ };
2301
+ const graphemeSplitImpl = textstring => {
2302
+ const graphemes = [];
2303
+ for (let i = 0, chr; i < textstring.length; i++) {
2304
+ if ((chr = getWholeChar(textstring, i)) === false) {
2305
+ continue;
2306
+ }
2307
+ graphemes.push(chr);
2308
+ }
2309
+ return graphemes;
2310
+ };
2311
+
2312
+ // taken from mdn in the charAt doc page.
2313
+ const getWholeChar = (str, i) => {
2314
+ const code = str.charCodeAt(i);
2315
+ if (isNaN(code)) {
2316
+ return ''; // Position not found
2317
+ }
2318
+ if (code < 0xd800 || code > 0xdfff) {
2319
+ return str.charAt(i);
2320
+ }
2321
+
2322
+ // High surrogate (could change last hex to 0xDB7F to treat high private
2323
+ // surrogates as single characters)
2324
+ if (0xd800 <= code && code <= 0xdbff) {
2325
+ if (str.length <= i + 1) {
2326
+ throw 'High surrogate without following low surrogate';
2327
+ }
2328
+ const next = str.charCodeAt(i + 1);
2329
+ if (0xdc00 > next || next > 0xdfff) {
2330
+ throw 'High surrogate without following low surrogate';
2331
+ }
2332
+ return str.charAt(i) + str.charAt(i + 1);
2333
+ }
2334
+ // Low surrogate (0xDC00 <= code && code <= 0xDFFF)
2335
+ if (i === 0) {
2336
+ throw 'Low surrogate without preceding high surrogate';
2337
+ }
2338
+ const prev = str.charCodeAt(i - 1);
2339
+
2340
+ // (could change last hex to 0xDB7F to treat high private
2341
+ // surrogates as single characters)
2342
+ if (0xd800 > prev || prev > 0xdbff) {
2343
+ throw 'Low surrogate without preceding high surrogate';
2344
+ }
2345
+ // We can pass over low surrogates now as the second component
2346
+ // in a pair which we have already processed
2347
+ return false;
2348
+ };
2349
+
2350
+ var lang_string = /*#__PURE__*/Object.freeze({
2351
+ __proto__: null,
2352
+ capitalize: capitalize,
2353
+ escapeXml: escapeXml,
2354
+ graphemeSplit: graphemeSplit
2355
+ });
2356
+
2252
2357
  /**
2253
2358
  * Having both options in TCanvasSizeOptions set to true transform the call in a calcOffset
2254
2359
  * Better try to restrict with types to avoid confusion.
@@ -3018,7 +3123,8 @@ let StaticCanvas$1 = class StaticCanvas extends createCollectionMixin(CommonMeth
3018
3123
  this._setSVGPreamble(markup, options);
3019
3124
  this._setSVGHeader(markup, options);
3020
3125
  if (this.clipPath) {
3021
- markup.push(`<g clip-path="url(#${this.clipPath.clipPathId})" >\n`);
3126
+ var _this$clipPath$clipPa;
3127
+ markup.push(`<g clip-path="url(#${escapeXml((_this$clipPath$clipPa = this.clipPath.clipPathId) !== null && _this$clipPath$clipPa !== void 0 ? _this$clipPath$clipPa : '')})" >\n`);
3022
3128
  }
3023
3129
  this._setSVGBgOverlayColor(markup, 'background');
3024
3130
  this._setSVGBgOverlayImage(markup, 'backgroundImage', reviver);
@@ -3697,20 +3803,6 @@ const sendObjectToPlane = (object, from, to) => {
3697
3803
  return t;
3698
3804
  };
3699
3805
 
3700
- const fireEvent = (eventName, options) => {
3701
- var _target$canvas;
3702
- const {
3703
- transform: {
3704
- target
3705
- }
3706
- } = options;
3707
- (_target$canvas = target.canvas) === null || _target$canvas === void 0 || _target$canvas.fire(`object:${eventName}`, {
3708
- ...options,
3709
- target
3710
- });
3711
- target.fire(eventName, options);
3712
- };
3713
-
3714
3806
  const originOffset = {
3715
3807
  left: -0.5,
3716
3808
  top: -0.5,
@@ -3913,33 +4005,6 @@ function getLocalPoint(_ref, originX, originY, x, y) {
3913
4005
  return localPoint;
3914
4006
  }
3915
4007
 
3916
- /**
3917
- * Action handler
3918
- * @private
3919
- * @param {Event} eventData javascript event that is doing the transform
3920
- * @param {Object} transform javascript object containing a series of information around the current transform
3921
- * @param {number} x current mouse x position, canvas normalized
3922
- * @param {number} y current mouse y position, canvas normalized
3923
- * @return {Boolean} true if the translation occurred
3924
- */
3925
- const dragHandler = (eventData, transform, x, y) => {
3926
- const {
3927
- target,
3928
- offsetX,
3929
- offsetY
3930
- } = transform,
3931
- newLeft = x - offsetX,
3932
- newTop = y - offsetY,
3933
- moveX = !isLocked(target, 'lockMovementX') && target.left !== newLeft,
3934
- moveY = !isLocked(target, 'lockMovementY') && target.top !== newTop;
3935
- moveX && target.set(LEFT, newLeft);
3936
- moveY && target.set(TOP, newTop);
3937
- if (moveX || moveY) {
3938
- fireEvent(MOVING, commonEventInfo(eventData, transform, x, y));
3939
- }
3940
- return moveX || moveY;
3941
- };
3942
-
3943
4008
  const normalizeWs = value => value.replace(/\s+/g, ' ');
3944
4009
 
3945
4010
  /**
@@ -4686,7 +4751,7 @@ const colorPropToSVG = function (prop, value) {
4686
4751
  if (!value) {
4687
4752
  colorValue = 'none';
4688
4753
  } else if (value.toLive) {
4689
- colorValue = `url(#SVGID_${value.id})`;
4754
+ colorValue = `url(#SVGID_${escapeXml(value.id)})`;
4690
4755
  } else {
4691
4756
  const color = new Color(value),
4692
4757
  opacity = color.getAlpha();
@@ -4739,7 +4804,7 @@ class FabricObjectSVGExportMixin {
4739
4804
  filter = skipShadow ? '' : this.getSvgFilter(),
4740
4805
  fill = colorPropToSVG(FILL, this.fill),
4741
4806
  stroke = colorPropToSVG(STROKE, this.stroke);
4742
- return [stroke, 'stroke-width: ', strokeWidth, '; ', 'stroke-dasharray: ', strokeDashArray, '; ', 'stroke-linecap: ', strokeLineCap, '; ', 'stroke-dashoffset: ', strokeDashOffset, '; ', 'stroke-linejoin: ', strokeLineJoin, '; ', 'stroke-miterlimit: ', strokeMiterLimit, '; ', fill, 'fill-rule: ', fillRule, '; ', 'opacity: ', opacity, ';', filter, visibility].join('');
4807
+ return [stroke, 'stroke-width: ', strokeWidth, '; ', 'stroke-dasharray: ', strokeDashArray, '; ', 'stroke-linecap: ', strokeLineCap, '; ', 'stroke-dashoffset: ', strokeDashOffset, '; ', 'stroke-linejoin: ', strokeLineJoin, '; ', 'stroke-miterlimit: ', strokeMiterLimit, '; ', fill, 'fill-rule: ', fillRule, '; ', 'opacity: ', opacity, ';', filter, visibility].map(v => escapeXml(v)).join('');
4743
4808
  }
4744
4809
 
4745
4810
  /**
@@ -4747,7 +4812,7 @@ class FabricObjectSVGExportMixin {
4747
4812
  * @return {String}
4748
4813
  */
4749
4814
  getSvgFilter() {
4750
- return this.shadow ? `filter: url(#SVGID_${this.shadow.id});` : '';
4815
+ return this.shadow ? `filter: url(#SVGID_${escapeXml(this.shadow.id)});` : '';
4751
4816
  }
4752
4817
 
4753
4818
  /**
@@ -4755,7 +4820,7 @@ class FabricObjectSVGExportMixin {
4755
4820
  * @return {String}
4756
4821
  */
4757
4822
  getSvgCommons() {
4758
- return [this.id ? `id="${this.id}" ` : '', this.clipPath ? `clip-path="url(#${this.clipPath.clipPathId})" ` : ''].join('');
4823
+ return [this.id ? `id="${escapeXml(String(this.id))}" ` : '', this.clipPath ? `clip-path="url(#${this.clipPath.clipPathId})" ` : ''].join('');
4759
4824
  }
4760
4825
 
4761
4826
  /**
@@ -4868,7 +4933,7 @@ class FabricObjectSVGExportMixin {
4868
4933
  return reviver ? reviver(markup.join('')) : markup.join('');
4869
4934
  }
4870
4935
  addPaintOrder() {
4871
- return this.paintFirst !== FILL ? ` paint-order="${this.paintFirst}" ` : '';
4936
+ return this.paintFirst !== FILL ? ` paint-order="${escapeXml(this.paintFirst)}" ` : '';
4872
4937
  }
4873
4938
  }
4874
4939
 
@@ -5006,7 +5071,6 @@ const reViewBoxAttrValue = new RegExp(String.raw`^\s*(${reNum})${viewportSeparat
5006
5071
 
5007
5072
  (?:$|\s): This captures either the end of the line or a whitespace character. It ensures that the match ends either at the end of the string or with a whitespace character.
5008
5073
  */
5009
- // eslint-disable-next-line max-len
5010
5074
 
5011
5075
  const shadowOffsetRegex = '(-?\\d+(?:\\.\\d*)?(?:px)?(?:\\s?|$))?';
5012
5076
  const reOffsetsAndBlur = new RegExp('(?:\\s|^)' + shadowOffsetRegex + shadowOffsetRegex + '(' + reNum + '?(?:px)?)?(?:\\s?|$)(?:$|\\s)');
@@ -5065,14 +5129,15 @@ class Shadow {
5065
5129
  toSVG(object) {
5066
5130
  const offset = rotateVector(new Point(this.offsetX, this.offsetY), degreesToRadians(-object.angle)),
5067
5131
  BLUR_BOX = 20,
5132
+ NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS,
5068
5133
  color = new Color(this.color);
5069
5134
  let fBoxX = 40,
5070
5135
  fBoxY = 40;
5071
5136
  if (object.width && object.height) {
5072
5137
  //http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
5073
5138
  // we add some extra space to filter box to contain the blur ( 20 )
5074
- fBoxX = toFixed((Math.abs(offset.x) + this.blur) / object.width, config.NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5075
- fBoxY = toFixed((Math.abs(offset.y) + this.blur) / object.height, config.NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5139
+ fBoxX = toFixed((Math.abs(offset.x) + this.blur) / object.width, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5140
+ fBoxY = toFixed((Math.abs(offset.y) + this.blur) / object.height, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5076
5141
  }
5077
5142
  if (object.flipX) {
5078
5143
  offset.x *= -1;
@@ -5080,7 +5145,7 @@ class Shadow {
5080
5145
  if (object.flipY) {
5081
5146
  offset.y *= -1;
5082
5147
  }
5083
- return `<filter id="SVGID_${this.id}" y="-${fBoxY}%" height="${100 + 2 * fBoxY}%" x="-${fBoxX}%" width="${100 + 2 * fBoxX}%" >\n\t<feGaussianBlur in="SourceAlpha" stdDeviation="${toFixed(this.blur ? this.blur / 2 : 0, config.NUM_FRACTION_DIGITS)}"></feGaussianBlur>\n\t<feOffset dx="${toFixed(offset.x, config.NUM_FRACTION_DIGITS)}" dy="${toFixed(offset.y, config.NUM_FRACTION_DIGITS)}" result="oBlur" ></feOffset>\n\t<feFlood flood-color="${color.toRgb()}" flood-opacity="${color.getAlpha()}"/>\n\t<feComposite in2="oBlur" operator="in" />\n\t<feMerge>\n\t\t<feMergeNode></feMergeNode>\n\t\t<feMergeNode in="SourceGraphic"></feMergeNode>\n\t</feMerge>\n</filter>\n`;
5148
+ return `<filter id="SVGID_${escapeXml(this.id)}" y="-${fBoxY}%" height="${100 + 2 * fBoxY}%" x="-${fBoxX}%" width="${100 + 2 * fBoxX}%" >\n\t<feGaussianBlur in="SourceAlpha" stdDeviation="${toFixed(this.blur ? this.blur / 2 : 0, NUM_FRACTION_DIGITS)}"></feGaussianBlur>\n\t<feOffset dx="${toFixed(offset.x, NUM_FRACTION_DIGITS)}" dy="${toFixed(offset.y, NUM_FRACTION_DIGITS)}" result="oBlur" ></feOffset>\n\t<feFlood flood-color="${color.toRgb()}" flood-opacity="${color.getAlpha()}"/>\n\t<feComposite in2="oBlur" operator="in" />\n\t<feMerge>\n\t\t<feMergeNode></feMergeNode>\n\t\t<feMergeNode in="SourceGraphic"></feMergeNode>\n\t</feMerge>\n</filter>\n`;
5084
5149
  }
5085
5150
 
5086
5151
  /**
@@ -6490,7 +6555,7 @@ class ObjectGeometry extends CommonMethods {
6490
6555
  calcOwnMatrix() {
6491
6556
  const key = this.transformMatrixKey(true),
6492
6557
  cache = this.ownMatrixCache;
6493
- if (cache && cache.key === key) {
6558
+ if (cache && cache.key.every((x, i) => x === key[i])) {
6494
6559
  return cache.value;
6495
6560
  }
6496
6561
  const center = this.getRelativeCenterPoint(),
@@ -7231,6 +7296,9 @@ let FabricObject$1 = class FabricObject extends ObjectGeometry {
7231
7296
  } else {
7232
7297
  this._renderBackground(ctx);
7233
7298
  }
7299
+ this.fire('before:render', {
7300
+ ctx
7301
+ });
7234
7302
  this._render(ctx);
7235
7303
  this._drawClipPath(ctx, this.clipPath, context);
7236
7304
  this.fill = originalFill;
@@ -8231,6 +8299,20 @@ _defineProperty(FabricObject$1, "customProperties", []);
8231
8299
  classRegistry.setClass(FabricObject$1);
8232
8300
  classRegistry.setClass(FabricObject$1, 'object');
8233
8301
 
8302
+ const fireEvent = (eventName, options) => {
8303
+ var _target$canvas;
8304
+ const {
8305
+ transform: {
8306
+ target
8307
+ }
8308
+ } = options;
8309
+ (_target$canvas = target.canvas) === null || _target$canvas === void 0 || _target$canvas.fire(`object:${eventName}`, {
8310
+ ...options,
8311
+ target
8312
+ });
8313
+ target.fire(eventName, options);
8314
+ };
8315
+
8234
8316
  /**
8235
8317
  * Wrap an action handler with firing an event if the action is performed
8236
8318
  * @param {TModificationEvents} eventName the event we want to fire
@@ -8273,34 +8355,62 @@ function wrapWithFixedAnchor(actionHandler) {
8273
8355
  };
8274
8356
  }
8275
8357
 
8276
- /**
8277
- * Action handler to change object's width
8278
- * Needs to be wrapped with `wrapWithFixedAnchor` to be effective
8279
- * @param {Event} eventData javascript event that is doing the transform
8280
- * @param {Object} transform javascript object containing a series of information around the current transform
8281
- * @param {number} x current mouse x position, canvas normalized
8282
- * @param {number} y current mouse y position, canvas normalized
8283
- * @return {Boolean} true if some change happened
8284
- */
8285
- const changeObjectWidth = (eventData, transform, x, y) => {
8358
+ const changeObjectDimensionGen = (dimension, origin, xorY, scale) => (eventData, transform, x, y) => {
8286
8359
  const localPoint = getLocalPoint(transform, transform.originX, transform.originY, x, y);
8360
+ const localPointValue = localPoint[xorY];
8287
8361
  // make sure the control changes width ONLY from it's side of target
8288
- if (resolveOrigin(transform.originX) === resolveOrigin(CENTER) || resolveOrigin(transform.originX) === resolveOrigin(RIGHT) && localPoint.x < 0 || resolveOrigin(transform.originX) === resolveOrigin(LEFT) && localPoint.x > 0) {
8362
+ const originValue = resolveOrigin(transform[origin]);
8363
+ if (originValue === 0 || originValue > 0 && localPointValue < 0 || originValue < 0 && localPointValue > 0) {
8289
8364
  const {
8290
8365
  target
8291
8366
  } = transform,
8292
- strokePadding = target.strokeWidth / (target.strokeUniform ? target.scaleX : 1),
8367
+ strokePadding = target.strokeWidth / (target.strokeUniform ? target[scale] : 1),
8293
8368
  multiplier = isTransformCentered(transform) ? 2 : 1,
8294
- oldWidth = target.width,
8295
- newWidth = Math.abs(localPoint.x * multiplier / target.scaleX) - strokePadding;
8296
- target.set('width', Math.max(newWidth, 1));
8369
+ oldWidth = target[dimension],
8370
+ newWidth = Math.abs(localPointValue * multiplier / target[scale]) - strokePadding;
8371
+ target.set(dimension, Math.max(newWidth, 1));
8297
8372
  // check against actual target width in case `newWidth` was rejected
8298
- return oldWidth !== target.width;
8373
+ return oldWidth !== target[dimension];
8299
8374
  }
8300
8375
  return false;
8301
8376
  };
8377
+
8378
+ /**
8379
+ * Action handler to change object's width
8380
+ * Needs to be wrapped with `wrapWithFixedAnchor` to be effective
8381
+ * You want to use this only if you are building a new control handler and you want
8382
+ * to reuse some logic. use "changeWidth" if you are looking to just use a control for width
8383
+ * @param {Event} eventData javascript event that is doing the transform
8384
+ * @param {Object} transform javascript object containing a series of information around the current transform
8385
+ * @param {number} x current mouse x position, canvas normalized
8386
+ * @param {number} y current mouse y position, canvas normalized
8387
+ * @return {Boolean} true if some change happened
8388
+ */
8389
+ const changeObjectWidth = changeObjectDimensionGen('width', 'originX', 'x', 'scaleX');
8390
+
8391
+ /**
8392
+ * Action handler to change object's height
8393
+ * Needs to be wrapped with `wrapWithFixedAnchor` to be effective
8394
+ * You want to use this only if you are building a new control handler and you want
8395
+ * to reuse some logic. use "changeHeight" if you are looking to just use a control for height
8396
+ * @param {Event} eventData javascript event that is doing the transform
8397
+ * @param {Object} transform javascript object containing a series of information around the current transform
8398
+ * @param {number} x current mouse x position, canvas normalized
8399
+ * @param {number} y current mouse y position, canvas normalized
8400
+ * @return {Boolean} true if some change happened
8401
+ */
8402
+ const changeObjectHeight = changeObjectDimensionGen('height', 'originY', 'y', 'scaleY');
8403
+
8404
+ /**
8405
+ * Control handler for changing width
8406
+ */
8302
8407
  const changeWidth = wrapWithFireEvent(RESIZING, wrapWithFixedAnchor(changeObjectWidth));
8303
8408
 
8409
+ /**
8410
+ * Control handler for changing height
8411
+ */
8412
+ const changeHeight = wrapWithFireEvent(RESIZING, wrapWithFixedAnchor(changeObjectHeight));
8413
+
8304
8414
  /**
8305
8415
  * Render a round control, as per fabric features.
8306
8416
  * This function is written to respect object properties like transparentCorners, cornerSize
@@ -8313,33 +8423,24 @@ const changeWidth = wrapWithFireEvent(RESIZING, wrapWithFixedAnchor(changeObject
8313
8423
  * @param {FabricObject} fabricObject the fabric object for which we are rendering controls
8314
8424
  */
8315
8425
  function renderCircleControl(ctx, left, top, styleOverride, fabricObject) {
8316
- styleOverride = styleOverride || {};
8317
- const xSize = this.sizeX || styleOverride.cornerSize || fabricObject.cornerSize,
8318
- ySize = this.sizeY || styleOverride.cornerSize || fabricObject.cornerSize,
8319
- transparentCorners = typeof styleOverride.transparentCorners !== 'undefined' ? styleOverride.transparentCorners : fabricObject.transparentCorners,
8320
- methodName = transparentCorners ? STROKE : FILL,
8321
- stroke = !transparentCorners && (styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor);
8322
- let myLeft = left,
8323
- myTop = top,
8324
- size;
8325
8426
  ctx.save();
8326
- ctx.fillStyle = styleOverride.cornerColor || fabricObject.cornerColor || '';
8327
- ctx.strokeStyle = styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor || '';
8427
+ const {
8428
+ stroke,
8429
+ xSize,
8430
+ ySize,
8431
+ opName
8432
+ } = this.commonRenderProps(ctx, left, top, fabricObject, styleOverride);
8433
+ let size = xSize;
8328
8434
  // TODO: use proper ellipse code.
8329
8435
  if (xSize > ySize) {
8330
- size = xSize;
8331
8436
  ctx.scale(1.0, ySize / xSize);
8332
- myTop = top * xSize / ySize;
8333
8437
  } else if (ySize > xSize) {
8334
8438
  size = ySize;
8335
8439
  ctx.scale(xSize / ySize, 1.0);
8336
- myLeft = left * ySize / xSize;
8337
- } else {
8338
- size = xSize;
8339
8440
  }
8340
8441
  ctx.beginPath();
8341
- ctx.arc(myLeft, myTop, size / 2, 0, twoMathPi, false);
8342
- ctx[methodName]();
8442
+ ctx.arc(0, 0, size / 2, 0, twoMathPi, false);
8443
+ ctx[opName]();
8343
8444
  if (stroke) {
8344
8445
  ctx.stroke();
8345
8446
  }
@@ -8358,25 +8459,19 @@ function renderCircleControl(ctx, left, top, styleOverride, fabricObject) {
8358
8459
  * @param {FabricObject} fabricObject the fabric object for which we are rendering controls
8359
8460
  */
8360
8461
  function renderSquareControl(ctx, left, top, styleOverride, fabricObject) {
8361
- styleOverride = styleOverride || {};
8362
- const xSize = this.sizeX || styleOverride.cornerSize || fabricObject.cornerSize,
8363
- ySize = this.sizeY || styleOverride.cornerSize || fabricObject.cornerSize,
8364
- transparentCorners = typeof styleOverride.transparentCorners !== 'undefined' ? styleOverride.transparentCorners : fabricObject.transparentCorners,
8365
- methodName = transparentCorners ? STROKE : FILL,
8366
- stroke = !transparentCorners && (styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor),
8462
+ ctx.save();
8463
+ const {
8464
+ stroke,
8465
+ xSize,
8466
+ ySize,
8467
+ opName
8468
+ } = this.commonRenderProps(ctx, left, top, fabricObject, styleOverride),
8367
8469
  xSizeBy2 = xSize / 2,
8368
8470
  ySizeBy2 = ySize / 2;
8369
- ctx.save();
8370
- ctx.fillStyle = styleOverride.cornerColor || fabricObject.cornerColor || '';
8371
- ctx.strokeStyle = styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor || '';
8372
- ctx.translate(left, top);
8373
- // angle is relative to canvas plane
8374
- const angle = fabricObject.getTotalAngle();
8375
- ctx.rotate(degreesToRadians(angle));
8376
8471
  // this does not work, and fixed with ( && ) does not make sense.
8377
8472
  // to have real transparent corners we need the controls on upperCanvas
8378
8473
  // transparentCorners || ctx.clearRect(-xSizeBy2, -ySizeBy2, xSize, ySize);
8379
- ctx[`${methodName}Rect`](-xSizeBy2, -ySizeBy2, xSize, ySize);
8474
+ ctx[`${opName}Rect`](-xSizeBy2, -ySizeBy2, xSize, ySize);
8380
8475
  if (stroke) {
8381
8476
  ctx.strokeRect(-xSizeBy2, -ySizeBy2, xSize, ySize);
8382
8477
  }
@@ -8494,6 +8589,14 @@ class Control {
8494
8589
  _defineProperty(this, "withConnection", false);
8495
8590
  Object.assign(this, options);
8496
8591
  }
8592
+ getTransformAnchorPoint() {
8593
+ var _this$transformAnchor;
8594
+ return (// return the control transformAnchorPoint
8595
+ (_this$transformAnchor = this.transformAnchorPoint) !== null && _this$transformAnchor !== void 0 ? _this$transformAnchor :
8596
+ // otherwise will return the opposite origin of where the control is located.
8597
+ new Point(-this.x + 0.5, -this.y + 0.5)
8598
+ );
8599
+ }
8497
8600
 
8498
8601
  /**
8499
8602
  * The control actionHandler, provide one to handle action ( control being moved )
@@ -8636,6 +8739,41 @@ class Control {
8636
8739
  };
8637
8740
  }
8638
8741
 
8742
+ /**
8743
+ * This is an helper method to prepare the canvas to render a control
8744
+ * It detectes common control properties and sets the correct fill and
8745
+ * stroke styles on the context. It does not execute translations or
8746
+ * rotations since different controls need differnt combination of these.
8747
+ */
8748
+ commonRenderProps(ctx, left, top, fabricObject) {
8749
+ let styleOverride = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
8750
+ const {
8751
+ cornerSize,
8752
+ cornerColor,
8753
+ transparentCorners,
8754
+ cornerStrokeColor
8755
+ } = styleOverride,
8756
+ sizeFromProps = cornerSize || fabricObject.cornerSize,
8757
+ xSize = this.sizeX || sizeFromProps,
8758
+ ySize = this.sizeY || sizeFromProps,
8759
+ transparent = typeof transparentCorners !== 'undefined' ? transparentCorners : fabricObject.transparentCorners,
8760
+ opName = transparent ? STROKE : FILL,
8761
+ strokeColor = cornerStrokeColor || fabricObject.cornerStrokeColor,
8762
+ stroke = !transparent && !!strokeColor;
8763
+ ctx.fillStyle = cornerColor || fabricObject.cornerColor || '';
8764
+ ctx.strokeStyle = strokeColor || '';
8765
+ ctx.translate(left, top);
8766
+ // angle is relative to canvas plane
8767
+ ctx.rotate(degreesToRadians(fabricObject.getTotalAngle()));
8768
+ return {
8769
+ stroke,
8770
+ xSize,
8771
+ ySize,
8772
+ transparentCorners: transparent,
8773
+ opName
8774
+ };
8775
+ }
8776
+
8639
8777
  /**
8640
8778
  * Render function for the control.
8641
8779
  * When this function runs the context is unscaled. unrotate. Just retina scaled.
@@ -10275,136 +10413,31 @@ const cloneStyles = style => {
10275
10413
  };
10276
10414
 
10277
10415
  /**
10278
- * Capitalizes a string
10279
- * @param {String} string String to capitalize
10280
- * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
10281
- * and other letters stay untouched, if false first letter is capitalized
10282
- * and other letters are converted to lowercase.
10283
- * @return {String} Capitalized version of a string
10416
+ * @param {Object} prevStyle first style to compare
10417
+ * @param {Object} thisStyle second style to compare
10418
+ * @param {boolean} forTextSpans whether to check overline, underline, and line-through properties
10419
+ * @return {boolean} true if the style changed
10284
10420
  */
10285
- const capitalize = function (string) {
10286
- let firstLetterOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
10287
- return `${string.charAt(0).toUpperCase()}${firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase()}`;
10421
+ const hasStyleChanged = function (prevStyle, thisStyle) {
10422
+ let forTextSpans = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
10423
+ return prevStyle.fill !== thisStyle.fill || prevStyle.stroke !== thisStyle.stroke || prevStyle.strokeWidth !== thisStyle.strokeWidth || prevStyle.fontSize !== thisStyle.fontSize || prevStyle.fontFamily !== thisStyle.fontFamily || prevStyle.fontWeight !== thisStyle.fontWeight || prevStyle.fontStyle !== thisStyle.fontStyle || prevStyle.textDecorationThickness !== thisStyle.textDecorationThickness || prevStyle.textBackgroundColor !== thisStyle.textBackgroundColor || prevStyle.deltaY !== thisStyle.deltaY || forTextSpans && (prevStyle.overline !== thisStyle.overline || prevStyle.underline !== thisStyle.underline || prevStyle.linethrough !== thisStyle.linethrough);
10288
10424
  };
10289
10425
 
10290
10426
  /**
10291
- * Escapes XML in a string
10292
- * @param {String} string String to escape
10293
- * @return {String} Escaped version of a string
10427
+ * Returns the array form of a text object's inline styles property with styles grouped in ranges
10428
+ * rather than per character. This format is less verbose, and is better suited for storage
10429
+ * so it is used in serialization (not during runtime).
10430
+ * @param {object} styles per character styles for a text object
10431
+ * @param {String} text the text string that the styles are applied to
10432
+ * @return {{start: number, end: number, style: object}[]}
10294
10433
  */
10295
- const escapeXml = string => string.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
10296
- let segmenter;
10297
- const getSegmenter = () => {
10298
- if (!segmenter) {
10299
- segmenter = 'Intl' in getFabricWindow() && 'Segmenter' in Intl && new Intl.Segmenter(undefined, {
10300
- granularity: 'grapheme'
10301
- });
10302
- }
10303
- return segmenter;
10304
- };
10305
-
10306
- /**
10307
- * Divide a string in the user perceived single units
10308
- * @param {String} textstring String to escape
10309
- * @return {Array} array containing the graphemes
10310
- */
10311
- const graphemeSplit = textstring => {
10312
- segmenter || getSegmenter();
10313
- if (segmenter) {
10314
- const segments = segmenter.segment(textstring);
10315
- return Array.from(segments).map(_ref => {
10316
- let {
10317
- segment
10318
- } = _ref;
10319
- return segment;
10320
- });
10321
- }
10322
-
10323
- //Fallback
10324
- return graphemeSplitImpl(textstring);
10325
- };
10326
- const graphemeSplitImpl = textstring => {
10327
- const graphemes = [];
10328
- for (let i = 0, chr; i < textstring.length; i++) {
10329
- if ((chr = getWholeChar(textstring, i)) === false) {
10330
- continue;
10331
- }
10332
- graphemes.push(chr);
10333
- }
10334
- return graphemes;
10335
- };
10336
-
10337
- // taken from mdn in the charAt doc page.
10338
- const getWholeChar = (str, i) => {
10339
- const code = str.charCodeAt(i);
10340
- if (isNaN(code)) {
10341
- return ''; // Position not found
10342
- }
10343
- if (code < 0xd800 || code > 0xdfff) {
10344
- return str.charAt(i);
10345
- }
10346
-
10347
- // High surrogate (could change last hex to 0xDB7F to treat high private
10348
- // surrogates as single characters)
10349
- if (0xd800 <= code && code <= 0xdbff) {
10350
- if (str.length <= i + 1) {
10351
- throw 'High surrogate without following low surrogate';
10352
- }
10353
- const next = str.charCodeAt(i + 1);
10354
- if (0xdc00 > next || next > 0xdfff) {
10355
- throw 'High surrogate without following low surrogate';
10356
- }
10357
- return str.charAt(i) + str.charAt(i + 1);
10358
- }
10359
- // Low surrogate (0xDC00 <= code && code <= 0xDFFF)
10360
- if (i === 0) {
10361
- throw 'Low surrogate without preceding high surrogate';
10362
- }
10363
- const prev = str.charCodeAt(i - 1);
10364
-
10365
- // (could change last hex to 0xDB7F to treat high private
10366
- // surrogates as single characters)
10367
- if (0xd800 > prev || prev > 0xdbff) {
10368
- throw 'Low surrogate without preceding high surrogate';
10369
- }
10370
- // We can pass over low surrogates now as the second component
10371
- // in a pair which we have already processed
10372
- return false;
10373
- };
10374
-
10375
- var lang_string = /*#__PURE__*/Object.freeze({
10376
- __proto__: null,
10377
- capitalize: capitalize,
10378
- escapeXml: escapeXml,
10379
- graphemeSplit: graphemeSplit
10380
- });
10381
-
10382
- /**
10383
- * @param {Object} prevStyle first style to compare
10384
- * @param {Object} thisStyle second style to compare
10385
- * @param {boolean} forTextSpans whether to check overline, underline, and line-through properties
10386
- * @return {boolean} true if the style changed
10387
- */
10388
- const hasStyleChanged = function (prevStyle, thisStyle) {
10389
- let forTextSpans = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
10390
- return prevStyle.fill !== thisStyle.fill || prevStyle.stroke !== thisStyle.stroke || prevStyle.strokeWidth !== thisStyle.strokeWidth || prevStyle.fontSize !== thisStyle.fontSize || prevStyle.fontFamily !== thisStyle.fontFamily || prevStyle.fontWeight !== thisStyle.fontWeight || prevStyle.fontStyle !== thisStyle.fontStyle || prevStyle.textDecorationThickness !== thisStyle.textDecorationThickness || prevStyle.textBackgroundColor !== thisStyle.textBackgroundColor || prevStyle.deltaY !== thisStyle.deltaY || forTextSpans && (prevStyle.overline !== thisStyle.overline || prevStyle.underline !== thisStyle.underline || prevStyle.linethrough !== thisStyle.linethrough);
10391
- };
10392
-
10393
- /**
10394
- * Returns the array form of a text object's inline styles property with styles grouped in ranges
10395
- * rather than per character. This format is less verbose, and is better suited for storage
10396
- * so it is used in serialization (not during runtime).
10397
- * @param {object} styles per character styles for a text object
10398
- * @param {String} text the text string that the styles are applied to
10399
- * @return {{start: number, end: number, style: object}[]}
10400
- */
10401
- const stylesToArray = (styles, text) => {
10402
- const textLines = text.split('\n'),
10403
- stylesArray = [];
10404
- let charIndex = -1,
10405
- prevStyle = {};
10406
- // clone style structure to prevent mutation
10407
- styles = cloneStyles(styles);
10434
+ const stylesToArray = (styles, text) => {
10435
+ const textLines = text.split('\n'),
10436
+ stylesArray = [];
10437
+ let charIndex = -1,
10438
+ prevStyle = {};
10439
+ // clone style structure to prevent mutation
10440
+ styles = cloneStyles(styles);
10408
10441
 
10409
10442
  //loop through each textLine
10410
10443
  for (let i = 0; i < textLines.length; i++) {
@@ -10962,7 +10995,7 @@ class Rect extends FabricObject {
10962
10995
  rx,
10963
10996
  ry
10964
10997
  } = this;
10965
- return ['<rect ', 'COMMON_PARTS', `x="${-width / 2}" y="${-height / 2}" rx="${rx}" ry="${ry}" width="${width}" height="${height}" />\n`];
10998
+ return ['<rect ', 'COMMON_PARTS', `x="${-width / 2}" y="${-height / 2}" rx="${escapeXml(rx)}" ry="${escapeXml(ry)}" width="${escapeXml(width)}" height="${escapeXml(height)}" />\n`];
10966
10999
  }
10967
11000
 
10968
11001
  /**
@@ -11927,7 +11960,7 @@ class Group extends createCollectionMixin(FabricObject) {
11927
11960
  * @return {String}
11928
11961
  */
11929
11962
  getSvgStyles() {
11930
- const opacity = typeof this.opacity !== 'undefined' && this.opacity !== 1 ? `opacity: ${this.opacity};` : '',
11963
+ const opacity = typeof this.opacity !== 'undefined' && this.opacity !== 1 ? `opacity: ${escapeXml(this.opacity)};` : '',
11931
11964
  visibility = this.visible ? '' : ' visibility: hidden;';
11932
11965
  return [opacity, this.getSvgFilter(), visibility].join('');
11933
11966
  }
@@ -13229,6 +13262,288 @@ const canvasDefaults = {
13229
13262
  preserveObjectStacking: true
13230
13263
  };
13231
13264
 
13265
+ /**
13266
+ * Action handler
13267
+ * @private
13268
+ * @param {Event} eventData javascript event that is doing the transform
13269
+ * @param {Object} transform javascript object containing a series of information around the current transform
13270
+ * @param {number} x current mouse x position, canvas normalized
13271
+ * @param {number} y current mouse y position, canvas normalized
13272
+ * @return {Boolean} true if the translation occurred
13273
+ */
13274
+ const dragHandler = (eventData, transform, x, y) => {
13275
+ const {
13276
+ target,
13277
+ offsetX,
13278
+ offsetY
13279
+ } = transform,
13280
+ newLeft = x - offsetX,
13281
+ newTop = y - offsetY,
13282
+ moveX = !isLocked(target, 'lockMovementX') && target.left !== newLeft,
13283
+ moveY = !isLocked(target, 'lockMovementY') && target.top !== newTop;
13284
+ moveX && target.set(LEFT, newLeft);
13285
+ moveY && target.set(TOP, newTop);
13286
+ if (moveX || moveY) {
13287
+ fireEvent(MOVING, commonEventInfo(eventData, transform, x, y));
13288
+ }
13289
+ return moveX || moveY;
13290
+ };
13291
+
13292
+ const ACTION_NAME$1 = MODIFY_POLY;
13293
+ /**
13294
+ * This function locates the controls.
13295
+ * It'll be used both for drawing and for interaction.
13296
+ */
13297
+ const createPolyPositionHandler = pointIndex => {
13298
+ return function (dim, finalMatrix, polyObject) {
13299
+ const {
13300
+ points,
13301
+ pathOffset
13302
+ } = polyObject;
13303
+ return new Point(points[pointIndex]).subtract(pathOffset).transform(multiplyTransformMatrices(polyObject.getViewportTransform(), polyObject.calcTransformMatrix()));
13304
+ };
13305
+ };
13306
+
13307
+ /**
13308
+ * This function defines what the control does.
13309
+ * It'll be called on every mouse move after a control has been clicked and is being dragged.
13310
+ * The function receives as argument the mouse event, the current transform object
13311
+ * and the current position in canvas coordinate `transform.target` is a reference to the
13312
+ * current object being transformed.
13313
+ */
13314
+ const polyActionHandler = (eventData, transform, x, y) => {
13315
+ const {
13316
+ target,
13317
+ pointIndex
13318
+ } = transform;
13319
+ const poly = target;
13320
+ const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, poly.calcOwnMatrix());
13321
+ poly.points[pointIndex] = mouseLocalPosition.add(poly.pathOffset);
13322
+ poly.setDimensions();
13323
+ poly.set('dirty', true);
13324
+ return true;
13325
+ };
13326
+
13327
+ /**
13328
+ * Keep the polygon in the same position when we change its `width`/`height`/`top`/`left`.
13329
+ */
13330
+ const factoryPolyActionHandler = (pointIndex, fn) => {
13331
+ return function (eventData, transform, x, y) {
13332
+ const poly = transform.target,
13333
+ anchorPoint = new Point(poly.points[(pointIndex > 0 ? pointIndex : poly.points.length) - 1]),
13334
+ anchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix()),
13335
+ actionPerformed = fn(eventData, {
13336
+ ...transform,
13337
+ pointIndex
13338
+ }, x, y);
13339
+ const newAnchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix());
13340
+ const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
13341
+ poly.left -= diff.x;
13342
+ poly.top -= diff.y;
13343
+ return actionPerformed;
13344
+ };
13345
+ };
13346
+ const createPolyActionHandler = pointIndex => wrapWithFireEvent(ACTION_NAME$1, factoryPolyActionHandler(pointIndex, polyActionHandler));
13347
+ function createPolyControls(arg0) {
13348
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13349
+ const controls = {};
13350
+ for (let idx = 0; idx < (typeof arg0 === 'number' ? arg0 : arg0.points.length); idx++) {
13351
+ controls[`p${idx}`] = new Control({
13352
+ actionName: ACTION_NAME$1,
13353
+ positionHandler: createPolyPositionHandler(idx),
13354
+ actionHandler: createPolyActionHandler(idx),
13355
+ ...options
13356
+ });
13357
+ }
13358
+ return controls;
13359
+ }
13360
+
13361
+ const ACTION_NAME = 'modifyPath';
13362
+ const calcPathPointPosition = (pathObject, commandIndex, pointIndex) => {
13363
+ const {
13364
+ path,
13365
+ pathOffset
13366
+ } = pathObject;
13367
+ const command = path[commandIndex];
13368
+ return new Point(command[pointIndex] - pathOffset.x, command[pointIndex + 1] - pathOffset.y).transform(multiplyTransformMatrices(pathObject.getViewportTransform(), pathObject.calcTransformMatrix()));
13369
+ };
13370
+ const movePathPoint = (pathObject, x, y, commandIndex, pointIndex) => {
13371
+ const {
13372
+ path,
13373
+ pathOffset
13374
+ } = pathObject;
13375
+ const anchorCommand = path[(commandIndex > 0 ? commandIndex : path.length) - 1];
13376
+ const anchorPoint = new Point(anchorCommand[pointIndex], anchorCommand[pointIndex + 1]);
13377
+ const anchorPointInParentPlane = anchorPoint.subtract(pathOffset).transform(pathObject.calcOwnMatrix());
13378
+ const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, pathObject.calcOwnMatrix());
13379
+ path[commandIndex][pointIndex] = mouseLocalPosition.x + pathOffset.x;
13380
+ path[commandIndex][pointIndex + 1] = mouseLocalPosition.y + pathOffset.y;
13381
+ pathObject.setDimensions();
13382
+ const newAnchorPointInParentPlane = anchorPoint.subtract(pathObject.pathOffset).transform(pathObject.calcOwnMatrix());
13383
+ const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
13384
+ pathObject.left -= diff.x;
13385
+ pathObject.top -= diff.y;
13386
+ pathObject.set('dirty', true);
13387
+ return true;
13388
+ };
13389
+
13390
+ /**
13391
+ * This function locates the controls.
13392
+ * It'll be used both for drawing and for interaction.
13393
+ */
13394
+ function pathPositionHandler(dim, finalMatrix, pathObject) {
13395
+ const {
13396
+ commandIndex,
13397
+ pointIndex
13398
+ } = this;
13399
+ return calcPathPointPosition(pathObject, commandIndex, pointIndex);
13400
+ }
13401
+
13402
+ /**
13403
+ * This function defines what the control does.
13404
+ * It'll be called on every mouse move after a control has been clicked and is being dragged.
13405
+ * The function receives as argument the mouse event, the current transform object
13406
+ * and the current position in canvas coordinate `transform.target` is a reference to the
13407
+ * current object being transformed.
13408
+ */
13409
+ function pathActionHandler(eventData, transform, x, y) {
13410
+ const {
13411
+ target
13412
+ } = transform;
13413
+ const {
13414
+ commandIndex,
13415
+ pointIndex
13416
+ } = this;
13417
+ const actionPerformed = movePathPoint(target, x, y, commandIndex, pointIndex);
13418
+ {
13419
+ fireEvent(this.actionName, {
13420
+ ...commonEventInfo(eventData, transform, x, y),
13421
+ commandIndex,
13422
+ pointIndex
13423
+ });
13424
+ }
13425
+ return actionPerformed;
13426
+ }
13427
+ const indexFromPrevCommand = previousCommandType => previousCommandType === 'C' ? 5 : previousCommandType === 'Q' ? 3 : 1;
13428
+ class PathPointControl extends Control {
13429
+ constructor(options) {
13430
+ super(options);
13431
+ }
13432
+ render(ctx, left, top, styleOverride, fabricObject) {
13433
+ const overrides = {
13434
+ ...styleOverride,
13435
+ cornerColor: this.controlFill,
13436
+ cornerStrokeColor: this.controlStroke,
13437
+ transparentCorners: !this.controlFill
13438
+ };
13439
+ super.render(ctx, left, top, overrides, fabricObject);
13440
+ }
13441
+ }
13442
+ class PathControlPointControl extends PathPointControl {
13443
+ constructor(options) {
13444
+ super(options);
13445
+ }
13446
+ render(ctx, left, top, styleOverride, fabricObject) {
13447
+ const {
13448
+ path
13449
+ } = fabricObject;
13450
+ const {
13451
+ commandIndex,
13452
+ pointIndex,
13453
+ connectToCommandIndex,
13454
+ connectToPointIndex
13455
+ } = this;
13456
+ ctx.save();
13457
+ ctx.strokeStyle = this.controlStroke;
13458
+ if (this.connectionDashArray) {
13459
+ ctx.setLineDash(this.connectionDashArray);
13460
+ }
13461
+ const [commandType] = path[commandIndex];
13462
+ const point = calcPathPointPosition(fabricObject, connectToCommandIndex, connectToPointIndex);
13463
+ if (commandType === 'Q') {
13464
+ // one control point connects to 2 points
13465
+ const point2 = calcPathPointPosition(fabricObject, commandIndex, pointIndex + 2);
13466
+ ctx.moveTo(point2.x, point2.y);
13467
+ ctx.lineTo(left, top);
13468
+ } else {
13469
+ ctx.moveTo(left, top);
13470
+ }
13471
+ ctx.lineTo(point.x, point.y);
13472
+ ctx.stroke();
13473
+ ctx.restore();
13474
+ super.render(ctx, left, top, styleOverride, fabricObject);
13475
+ }
13476
+ }
13477
+ const createControl = (commandIndexPos, pointIndexPos, isControlPoint, options, connectToCommandIndex, connectToPointIndex) => new (isControlPoint ? PathControlPointControl : PathPointControl)({
13478
+ commandIndex: commandIndexPos,
13479
+ pointIndex: pointIndexPos,
13480
+ actionName: ACTION_NAME,
13481
+ positionHandler: pathPositionHandler,
13482
+ actionHandler: pathActionHandler,
13483
+ connectToCommandIndex,
13484
+ connectToPointIndex,
13485
+ ...options,
13486
+ ...(isControlPoint ? options.controlPointStyle : options.pointStyle)
13487
+ });
13488
+ function createPathControls(path) {
13489
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13490
+ const controls = {};
13491
+ let previousCommandType = 'M';
13492
+ path.path.forEach((command, commandIndex) => {
13493
+ const commandType = command[0];
13494
+ if (commandType !== 'Z') {
13495
+ controls[`c_${commandIndex}_${commandType}`] = createControl(commandIndex, command.length - 2, false, options);
13496
+ }
13497
+ switch (commandType) {
13498
+ case 'C':
13499
+ controls[`c_${commandIndex}_C_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex - 1, indexFromPrevCommand(previousCommandType));
13500
+ controls[`c_${commandIndex}_C_CP_2`] = createControl(commandIndex, 3, true, options, commandIndex, 5);
13501
+ break;
13502
+ case 'Q':
13503
+ controls[`c_${commandIndex}_Q_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex, 3);
13504
+ break;
13505
+ }
13506
+ previousCommandType = commandType;
13507
+ });
13508
+ return controls;
13509
+ }
13510
+
13511
+ var index = /*#__PURE__*/Object.freeze({
13512
+ __proto__: null,
13513
+ changeHeight: changeHeight,
13514
+ changeObjectHeight: changeObjectHeight,
13515
+ changeObjectWidth: changeObjectWidth,
13516
+ changeWidth: changeWidth,
13517
+ createObjectDefaultControls: createObjectDefaultControls,
13518
+ createPathControls: createPathControls,
13519
+ createPolyActionHandler: createPolyActionHandler,
13520
+ createPolyControls: createPolyControls,
13521
+ createPolyPositionHandler: createPolyPositionHandler,
13522
+ createResizeControls: createResizeControls,
13523
+ createTextboxDefaultControls: createTextboxDefaultControls,
13524
+ dragHandler: dragHandler,
13525
+ factoryPolyActionHandler: factoryPolyActionHandler,
13526
+ getLocalPoint: getLocalPoint,
13527
+ polyActionHandler: polyActionHandler,
13528
+ renderCircleControl: renderCircleControl,
13529
+ renderSquareControl: renderSquareControl,
13530
+ rotationStyleHandler: rotationStyleHandler,
13531
+ rotationWithSnapping: rotationWithSnapping,
13532
+ scaleCursorStyleHandler: scaleCursorStyleHandler,
13533
+ scaleOrSkewActionName: scaleOrSkewActionName,
13534
+ scaleSkewCursorStyleHandler: scaleSkewCursorStyleHandler,
13535
+ scalingEqually: scalingEqually,
13536
+ scalingX: scalingX,
13537
+ scalingXOrSkewingY: scalingXOrSkewingY,
13538
+ scalingY: scalingY,
13539
+ scalingYOrSkewingX: scalingYOrSkewingX,
13540
+ skewCursorStyleHandler: skewCursorStyleHandler,
13541
+ skewHandlerX: skewHandlerX,
13542
+ skewHandlerY: skewHandlerY,
13543
+ wrapWithFireEvent: wrapWithFireEvent,
13544
+ wrapWithFixedAnchor: wrapWithFixedAnchor
13545
+ });
13546
+
13232
13547
  /**
13233
13548
  * Canvas class
13234
13549
  * @class Canvas
@@ -13599,11 +13914,13 @@ class SelectableCanvas extends StaticCanvas$1 {
13599
13914
  * Given the control clicked, determine the origin of the transform.
13600
13915
  * This is bad because controls can totally have custom names
13601
13916
  * should disappear before release 4.0
13917
+ * Fabric 7.1, jan 2026 we are still using this.
13918
+ * Needs to go.
13602
13919
  * @private
13603
13920
  * @deprecated
13604
13921
  */
13605
13922
  _getOriginFromCorner(target, controlName) {
13606
- const origin = {
13923
+ const origin = controlName ? target.controls[controlName].getTransformAnchorPoint() : {
13607
13924
  x: target.originX,
13608
13925
  y: target.originY
13609
13926
  };
@@ -13611,6 +13928,9 @@ class SelectableCanvas extends StaticCanvas$1 {
13611
13928
  return origin;
13612
13929
  }
13613
13930
 
13931
+ // this part down here is deprecated.
13932
+ // It is left to do not change the standard behavior in the middle of a major version
13933
+ // but when possible `getTransformAnchorPoint` will be the only source of truth
13614
13934
  // is a left control ?
13615
13935
  if (['ml', 'tl', 'bl'].includes(controlName)) {
13616
13936
  origin.x = RIGHT;
@@ -13650,37 +13970,52 @@ class SelectableCanvas extends StaticCanvas$1 {
13650
13970
  x: CENTER,
13651
13971
  y: CENTER
13652
13972
  } : this._getOriginFromCorner(target, corner),
13973
+ {
13974
+ scaleX,
13975
+ scaleY,
13976
+ skewX,
13977
+ skewY,
13978
+ left,
13979
+ top,
13980
+ angle,
13981
+ width,
13982
+ height,
13983
+ cropX,
13984
+ cropY
13985
+ } = target,
13653
13986
  /**
13654
13987
  * relative to target's containing coordinate plane
13655
13988
  * both agree on every point
13656
13989
  **/
13657
13990
  transform = {
13658
- target: target,
13991
+ target,
13659
13992
  action,
13660
13993
  actionHandler,
13661
13994
  actionPerformed: false,
13662
13995
  corner,
13663
- scaleX: target.scaleX,
13664
- scaleY: target.scaleY,
13665
- skewX: target.skewX,
13666
- skewY: target.skewY,
13667
- offsetX: pointer.x - target.left,
13668
- offsetY: pointer.y - target.top,
13996
+ scaleX,
13997
+ scaleY,
13998
+ skewX,
13999
+ skewY,
14000
+ offsetX: pointer.x - left,
14001
+ offsetY: pointer.y - top,
13669
14002
  originX: origin.x,
13670
14003
  originY: origin.y,
13671
14004
  ex: pointer.x,
13672
14005
  ey: pointer.y,
13673
14006
  lastX: pointer.x,
13674
14007
  lastY: pointer.y,
13675
- theta: degreesToRadians(target.angle),
13676
- width: target.width,
13677
- height: target.height,
14008
+ theta: degreesToRadians(angle),
14009
+ width,
14010
+ height,
13678
14011
  shiftKey: e.shiftKey,
13679
14012
  altKey,
13680
14013
  original: {
13681
14014
  ...saveObjectTransform(target),
13682
14015
  originX: origin.x,
13683
- originY: origin.y
14016
+ originY: origin.y,
14017
+ cropX,
14018
+ cropY
13684
14019
  }
13685
14020
  };
13686
14021
  this._currentTransform = transform;
@@ -16093,7 +16428,8 @@ class Gradient {
16093
16428
  }
16094
16429
  transform[4] -= offsetX;
16095
16430
  transform[5] -= offsetY;
16096
- const commonAttributes = [`id="SVGID_${this.id}"`, `gradientUnits="${gradientUnits}"`, `gradientTransform="${preTransform ? preTransform + ' ' : ''}${matrixToSVG(transform)}"`, ''].join(' ');
16431
+ const commonAttributes = [`id="SVGID_${escapeXml(String(this.id))}"`, `gradientUnits="${gradientUnits}"`, `gradientTransform="${preTransform ? preTransform + ' ' : ''}${matrixToSVG(transform)}"`, ''].join(' ');
16432
+ const sanitizeCoord = value => parseFloat(String(value));
16097
16433
  if (this.type === 'linear') {
16098
16434
  const {
16099
16435
  x1,
@@ -16101,7 +16437,11 @@ class Gradient {
16101
16437
  x2,
16102
16438
  y2
16103
16439
  } = this.coords;
16104
- markup.push('<linearGradient ', commonAttributes, ' x1="', x1, '" y1="', y1, '" x2="', x2, '" y2="', y2, '">\n');
16440
+ const sx1 = sanitizeCoord(x1);
16441
+ const sy1 = sanitizeCoord(y1);
16442
+ const sx2 = sanitizeCoord(x2);
16443
+ const sy2 = sanitizeCoord(y2);
16444
+ markup.push('<linearGradient ', commonAttributes, ' x1="', sx1, '" y1="', sy1, '" x2="', sx2, '" y2="', sy2, '">\n');
16105
16445
  } else if (this.type === 'radial') {
16106
16446
  const {
16107
16447
  x1,
@@ -16111,9 +16451,15 @@ class Gradient {
16111
16451
  r1,
16112
16452
  r2
16113
16453
  } = this.coords;
16114
- const needsSwap = r1 > r2;
16454
+ const sx1 = sanitizeCoord(x1);
16455
+ const sy1 = sanitizeCoord(y1);
16456
+ const sx2 = sanitizeCoord(x2);
16457
+ const sy2 = sanitizeCoord(y2);
16458
+ const sr1 = sanitizeCoord(r1);
16459
+ const sr2 = sanitizeCoord(r2);
16460
+ const needsSwap = sr1 > sr2;
16115
16461
  // svg radial gradient has just 1 radius. the biggest.
16116
- markup.push('<radialGradient ', commonAttributes, ' cx="', needsSwap ? x1 : x2, '" cy="', needsSwap ? y1 : y2, '" r="', needsSwap ? r1 : r2, '" fx="', needsSwap ? x2 : x1, '" fy="', needsSwap ? y2 : y1, '">\n');
16462
+ markup.push('<radialGradient ', commonAttributes, ' cx="', needsSwap ? sx1 : sx2, '" cy="', needsSwap ? sy1 : sy2, '" r="', needsSwap ? sr1 : sr2, '" fx="', needsSwap ? sx2 : sx1, '" fy="', needsSwap ? sy2 : sy1, '">\n');
16117
16463
  if (needsSwap) {
16118
16464
  // svg goes from internal to external radius. if radius are inverted, swap color stops.
16119
16465
  colorStops.reverse(); // mutates array
@@ -16121,16 +16467,17 @@ class Gradient {
16121
16467
  colorStop.offset = 1 - colorStop.offset;
16122
16468
  });
16123
16469
  }
16124
- const minRadius = Math.min(r1, r2);
16470
+ const minRadius = Math.min(sr1, sr2);
16125
16471
  if (minRadius > 0) {
16126
16472
  // i have to shift all colorStops and add new one in 0.
16127
- const maxRadius = Math.max(r1, r2),
16473
+ const maxRadius = Math.max(sr1, sr2),
16128
16474
  percentageShift = minRadius / maxRadius;
16129
16475
  colorStops.forEach(colorStop => {
16130
16476
  colorStop.offset += percentageShift * (1 - colorStop.offset);
16131
16477
  });
16132
16478
  }
16133
16479
  }
16480
+ // todo make a malicious script tag injection test with color and also apply a fix with escapeXml
16134
16481
  colorStops.forEach(_ref => {
16135
16482
  let {
16136
16483
  color,
@@ -16446,7 +16793,7 @@ class Pattern {
16446
16793
  patternOffsetY = ifNaN(this.offsetY / height, 0),
16447
16794
  patternWidth = repeat === 'repeat-y' || repeat === 'no-repeat' ? 1 + Math.abs(patternOffsetX || 0) : ifNaN(patternSource.width / width, 0),
16448
16795
  patternHeight = repeat === 'repeat-x' || repeat === 'no-repeat' ? 1 + Math.abs(patternOffsetY || 0) : ifNaN(patternSource.height / height, 0);
16449
- return [`<pattern id="SVGID_${id}" x="${patternOffsetX}" y="${patternOffsetY}" width="${patternWidth}" height="${patternHeight}">`, `<image x="0" y="0" width="${patternSource.width}" height="${patternSource.height}" xlink:href="${this.sourceToString()}"></image>`, `</pattern>`, ''].join('\n');
16796
+ return [`<pattern id="SVGID_${escapeXml(id)}" x="${patternOffsetX}" y="${patternOffsetY}" width="${patternWidth}" height="${patternHeight}">`, `<image x="0" y="0" width="${patternSource.width}" height="${patternSource.height}" xlink:href="${escapeXml(this.sourceToString())}"></image>`, `</pattern>`, ''].join('\n');
16450
16797
  }
16451
16798
  /* _TO_SVG_END_ */
16452
16799
 
@@ -16728,8 +17075,7 @@ class Path extends FabricObject {
16728
17075
  * of the instance
16729
17076
  */
16730
17077
  _toSVG() {
16731
- const path = joinPath(this.path, config.NUM_FRACTION_DIGITS);
16732
- return ['<path ', 'COMMON_PARTS', `d="${path}" stroke-linecap="round" />\n`];
17078
+ return ['<path ', 'COMMON_PARTS', `d="${joinPath(this.path, config.NUM_FRACTION_DIGITS)}" stroke-linecap="round" />\n`];
16733
17079
  }
16734
17080
 
16735
17081
  /**
@@ -17280,15 +17626,17 @@ class Circle extends FabricObject {
17280
17626
  * of the instance
17281
17627
  */
17282
17628
  _toSVG() {
17283
- const angle = (this.endAngle - this.startAngle) % 360;
17629
+ const {
17630
+ radius,
17631
+ startAngle,
17632
+ endAngle
17633
+ } = this;
17634
+ const angle = (endAngle - startAngle) % 360;
17284
17635
  if (angle === 0) {
17285
- return ['<circle ', 'COMMON_PARTS', 'cx="0" cy="0" ', 'r="', `${this.radius}`, '" />\n'];
17636
+ return ['<circle ', 'COMMON_PARTS', 'cx="0" cy="0" ', 'r="', `${escapeXml(radius)}`, '" />\n'];
17286
17637
  } else {
17287
- const {
17288
- radius
17289
- } = this;
17290
- const start = degreesToRadians(this.startAngle),
17291
- end = degreesToRadians(this.endAngle),
17638
+ const start = degreesToRadians(startAngle),
17639
+ end = degreesToRadians(endAngle),
17292
17640
  startX = cos(start) * radius,
17293
17641
  startY = sin(start) * radius,
17294
17642
  endX = cos(end) * radius,
@@ -17860,17 +18208,13 @@ class Line extends FabricObject {
17860
18208
  width,
17861
18209
  height
17862
18210
  } = this;
17863
- const xMult = _x1 <= _x2 ? -1 : 1,
17864
- yMult = _y1 <= _y2 ? -1 : 1,
17865
- x1 = xMult * width / 2,
17866
- y1 = yMult * height / 2,
17867
- x2 = xMult * -width / 2,
17868
- y2 = yMult * -height / 2;
18211
+ const xMult = _x1 <= _x2 ? -0.5 : 0.5,
18212
+ yMult = _y1 <= _y2 ? -0.5 : 0.5;
17869
18213
  return {
17870
- x1,
17871
- x2,
17872
- y1,
17873
- y2
18214
+ x1: xMult * width,
18215
+ x2: xMult * -width,
18216
+ y1: yMult * height,
18217
+ y2: yMult * -height
17874
18218
  };
17875
18219
  }
17876
18220
 
@@ -18088,7 +18432,7 @@ class Ellipse extends FabricObject {
18088
18432
  * of the instance
18089
18433
  */
18090
18434
  _toSVG() {
18091
- return ['<ellipse ', 'COMMON_PARTS', `cx="0" cy="0" rx="${this.rx}" ry="${this.ry}" />\n`];
18435
+ return ['<ellipse ', 'COMMON_PARTS', `cx="0" cy="0" rx="${escapeXml(this.rx)}" ry="${escapeXml(this.ry)}" />\n`];
18092
18436
  }
18093
18437
 
18094
18438
  /**
@@ -18406,14 +18750,17 @@ class Polyline extends FabricObject {
18406
18750
  * of the instance
18407
18751
  */
18408
18752
  _toSVG() {
18409
- const points = [],
18410
- diffX = this.pathOffset.x,
18753
+ const diffX = this.pathOffset.x,
18411
18754
  diffY = this.pathOffset.y,
18412
18755
  NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS;
18413
- for (let i = 0, len = this.points.length; i < len; i++) {
18414
- points.push(toFixed(this.points[i].x - diffX, NUM_FRACTION_DIGITS), ',', toFixed(this.points[i].y - diffY, NUM_FRACTION_DIGITS), ' ');
18415
- }
18416
- return [`<${this.constructor.type.toLowerCase()} `, 'COMMON_PARTS', `points="${points.join('')}" />\n`];
18756
+ const points = this.points.map(_ref2 => {
18757
+ let {
18758
+ x,
18759
+ y
18760
+ } = _ref2;
18761
+ return `${toFixed(x - diffX, NUM_FRACTION_DIGITS)},${toFixed(y - diffY, NUM_FRACTION_DIGITS)}`;
18762
+ }).join(' ');
18763
+ return [`<${escapeXml(this.constructor.type).toLowerCase()} `, 'COMMON_PARTS', `points="${points}" />\n`];
18417
18764
  }
18418
18765
 
18419
18766
  /**
@@ -18536,7 +18883,6 @@ class StyledText extends FabricObject {
18536
18883
  };
18537
18884
  for (const p1 in obj) {
18538
18885
  for (const p2 in obj[p1]) {
18539
- // eslint-disable-next-line no-unused-vars
18540
18886
  for (const p3 in obj[p1][p2]) {
18541
18887
  return false;
18542
18888
  }
@@ -18562,9 +18908,7 @@ class StyledText extends FabricObject {
18562
18908
  const obj = typeof lineIndex === 'undefined' ? this.styles : {
18563
18909
  0: this.styles[lineIndex]
18564
18910
  };
18565
- // eslint-disable-next-line
18566
18911
  for (const p1 in obj) {
18567
- // eslint-disable-next-line
18568
18912
  for (const p2 in obj[p1]) {
18569
18913
  if (typeof obj[p1][p2][property] !== 'undefined') {
18570
18914
  return true;
@@ -18840,7 +19184,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18840
19184
  } = _ref;
18841
19185
  const noShadow = true,
18842
19186
  textDecoration = this.getSvgTextDecoration(this);
18843
- return [textBgRects.join(''), '\t\t<text xml:space="preserve" ', `font-family="${this.fontFamily.replace(dblQuoteRegex, "'")}" `, `font-size="${this.fontSize}" `, this.fontStyle ? `font-style="${this.fontStyle}" ` : '', this.fontWeight ? `font-weight="${this.fontWeight}" ` : '', textDecoration ? `text-decoration="${textDecoration}" ` : '', this.direction === 'rtl' ? `direction="${this.direction}" ` : '', 'style="', this.getSvgStyles(noShadow), '"', this.addPaintOrder(), ' >', textSpans.join(''), '</text>\n'];
19187
+ return [textBgRects.join(''), '\t\t<text xml:space="preserve" ', `font-family="${escapeXml(this.fontFamily.replace(dblQuoteRegex, "'"))}" `, `font-size="${escapeXml(this.fontSize)}" `, this.fontStyle ? `font-style="${escapeXml(this.fontStyle)}" ` : '', this.fontWeight ? `font-weight="${escapeXml(this.fontWeight)}" ` : '', textDecoration ? `text-decoration="${textDecoration}" ` : '', this.direction === 'rtl' ? `direction="rtl" ` : '', 'style="', this.getSvgStyles(noShadow), '"', this.addPaintOrder(), ' >', textSpans.join(''), '</text>\n'];
18844
19188
  }
18845
19189
 
18846
19190
  /**
@@ -18856,7 +19200,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18856
19200
  lineOffset;
18857
19201
 
18858
19202
  // bounding-box background
18859
- this.backgroundColor && textBgRects.push(...createSVGInlineRect(this.backgroundColor, -this.width / 2, -this.height / 2, this.width, this.height));
19203
+ this.backgroundColor && textBgRects.push(createSVGInlineRect(this.backgroundColor, -this.width / 2, -this.height / 2, this.width, this.height));
18860
19204
 
18861
19205
  // text and text-background
18862
19206
  for (let i = 0, len = this._textLines.length; i < len; i++) {
@@ -18964,7 +19308,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18964
19308
  } = this.__charBounds[i][j];
18965
19309
  currentColor = this.getValueOfPropertyAt(i, j, 'textBackgroundColor');
18966
19310
  if (currentColor !== lastColor) {
18967
- lastColor && textBgRects.push(...createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
19311
+ lastColor && textBgRects.push(createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
18968
19312
  boxStart = left;
18969
19313
  boxWidth = width;
18970
19314
  lastColor = currentColor;
@@ -18972,7 +19316,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18972
19316
  boxWidth += kernedWidth;
18973
19317
  }
18974
19318
  }
18975
- currentColor && textBgRects.push(...createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
19319
+ currentColor && textBgRects.push(createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
18976
19320
  }
18977
19321
 
18978
19322
  /**
@@ -18999,7 +19343,6 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18999
19343
  fontSize,
19000
19344
  fontStyle,
19001
19345
  fontWeight,
19002
- deltaY,
19003
19346
  textDecorationThickness,
19004
19347
  linethrough,
19005
19348
  overline,
@@ -19011,7 +19354,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
19011
19354
  linethrough: linethrough !== null && linethrough !== void 0 ? linethrough : this.linethrough
19012
19355
  });
19013
19356
  const thickness = textDecorationThickness || this.textDecorationThickness;
19014
- return [stroke ? colorPropToSVG(STROKE, stroke) : '', strokeWidth ? `stroke-width: ${strokeWidth}; ` : '', fontFamily ? `font-family: ${!fontFamily.includes("'") && !fontFamily.includes('"') ? `'${fontFamily}'` : fontFamily}; ` : '', fontSize ? `font-size: ${fontSize}px; ` : '', fontStyle ? `font-style: ${fontStyle}; ` : '', fontWeight ? `font-weight: ${fontWeight}; ` : '', textDecoration ? `text-decoration: ${textDecoration}; text-decoration-thickness: ${toFixed(thickness * this.getObjectScaling().y / 10, config.NUM_FRACTION_DIGITS)}%; ` : '', fill ? colorPropToSVG(FILL, fill) : '', deltaY ? `baseline-shift: ${-deltaY}; ` : '', useWhiteSpace ? 'white-space: pre; ' : ''].join('');
19357
+ return [stroke ? colorPropToSVG(STROKE, stroke) : '', strokeWidth ? `stroke-width: ${escapeXml(strokeWidth)}; ` : '', fontFamily ? `font-family: ${!fontFamily.includes("'") && !fontFamily.includes('"') ? `'${escapeXml(fontFamily)}'` : escapeXml(fontFamily)}; ` : '', fontSize ? `font-size: ${escapeXml(fontSize)}px; ` : '', fontStyle ? `font-style: ${escapeXml(fontStyle)}; ` : '', fontWeight ? `font-weight: ${escapeXml(fontWeight)}; ` : '', textDecoration ? `text-decoration: ${textDecoration}; text-decoration-thickness: ${toFixed(thickness * this.getObjectScaling().y / 10, config.NUM_FRACTION_DIGITS)}%; ` : '', fill ? colorPropToSVG(FILL, fill) : '', useWhiteSpace ? 'white-space: pre; ' : ''].join('');
19015
19358
  }
19016
19359
 
19017
19360
  /**
@@ -24575,13 +24918,13 @@ class FabricImage extends FabricObject {
24575
24918
  }
24576
24919
  if (this.hasCrop()) {
24577
24920
  const clipPathId = uid();
24578
- svgString.push('<clipPath id="imageCrop_' + clipPathId + '">\n', '\t<rect x="' + x + '" y="' + y + '" width="' + this.width + '" height="' + this.height + '" />\n', '</clipPath>\n');
24921
+ svgString.push('<clipPath id="imageCrop_' + clipPathId + '">\n', '\t<rect x="' + x + '" y="' + y + '" width="' + escapeXml(this.width) + '" height="' + escapeXml(this.height) + '" />\n', '</clipPath>\n');
24579
24922
  clipPath = ' clip-path="url(#imageCrop_' + clipPathId + ')" ';
24580
24923
  }
24581
24924
  if (!this.imageSmoothing) {
24582
24925
  imageRendering = ' image-rendering="optimizeSpeed"';
24583
24926
  }
24584
- imageMarkup.push('\t<image ', 'COMMON_PARTS', `xlink:href="${this.getSvgSrc(true)}" x="${x - this.cropX}" y="${y - this.cropY
24927
+ imageMarkup.push('\t<image ', 'COMMON_PARTS', `xlink:href="${escapeXml(this.getSrc(true))}" x="${x - this.cropX}" y="${y - this.cropY
24585
24928
  // we're essentially moving origin of transformation from top/left corner to the center of the shape
24586
24929
  // by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
24587
24930
  // so that object's center aligns with container's left/top
@@ -24589,7 +24932,7 @@ class FabricImage extends FabricObject {
24589
24932
  if (this.stroke || this.strokeDashArray) {
24590
24933
  const origFill = this.fill;
24591
24934
  this.fill = null;
24592
- strokeSvg = [`\t<rect x="${x}" y="${y}" width="${this.width}" height="${this.height}" style="${this.getSvgStyles()}" />\n`];
24935
+ strokeSvg = [`\t<rect x="${x}" y="${y}" width="${escapeXml(this.width)}" height="${escapeXml(this.height)}" style="${this.getSvgStyles()}" />\n`];
24593
24936
  this.fill = origFill;
24594
24937
  }
24595
24938
  if (this.paintFirst !== FILL) {
@@ -25572,258 +25915,6 @@ function loadSVGFromURL(url, reviver) {
25572
25915
  });
25573
25916
  }
25574
25917
 
25575
- const ACTION_NAME$1 = MODIFY_POLY;
25576
- /**
25577
- * This function locates the controls.
25578
- * It'll be used both for drawing and for interaction.
25579
- */
25580
- const createPolyPositionHandler = pointIndex => {
25581
- return function (dim, finalMatrix, polyObject) {
25582
- const {
25583
- points,
25584
- pathOffset
25585
- } = polyObject;
25586
- return new Point(points[pointIndex]).subtract(pathOffset).transform(multiplyTransformMatrices(polyObject.getViewportTransform(), polyObject.calcTransformMatrix()));
25587
- };
25588
- };
25589
-
25590
- /**
25591
- * This function defines what the control does.
25592
- * It'll be called on every mouse move after a control has been clicked and is being dragged.
25593
- * The function receives as argument the mouse event, the current transform object
25594
- * and the current position in canvas coordinate `transform.target` is a reference to the
25595
- * current object being transformed.
25596
- */
25597
- const polyActionHandler = (eventData, transform, x, y) => {
25598
- const {
25599
- target,
25600
- pointIndex
25601
- } = transform;
25602
- const poly = target;
25603
- const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, poly.calcOwnMatrix());
25604
- poly.points[pointIndex] = mouseLocalPosition.add(poly.pathOffset);
25605
- poly.setDimensions();
25606
- poly.set('dirty', true);
25607
- return true;
25608
- };
25609
-
25610
- /**
25611
- * Keep the polygon in the same position when we change its `width`/`height`/`top`/`left`.
25612
- */
25613
- const factoryPolyActionHandler = (pointIndex, fn) => {
25614
- return function (eventData, transform, x, y) {
25615
- const poly = transform.target,
25616
- anchorPoint = new Point(poly.points[(pointIndex > 0 ? pointIndex : poly.points.length) - 1]),
25617
- anchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix()),
25618
- actionPerformed = fn(eventData, {
25619
- ...transform,
25620
- pointIndex
25621
- }, x, y);
25622
- const newAnchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix());
25623
- const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
25624
- poly.left -= diff.x;
25625
- poly.top -= diff.y;
25626
- return actionPerformed;
25627
- };
25628
- };
25629
- const createPolyActionHandler = pointIndex => wrapWithFireEvent(ACTION_NAME$1, factoryPolyActionHandler(pointIndex, polyActionHandler));
25630
- function createPolyControls(arg0) {
25631
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
25632
- const controls = {};
25633
- for (let idx = 0; idx < (typeof arg0 === 'number' ? arg0 : arg0.points.length); idx++) {
25634
- controls[`p${idx}`] = new Control({
25635
- actionName: ACTION_NAME$1,
25636
- positionHandler: createPolyPositionHandler(idx),
25637
- actionHandler: createPolyActionHandler(idx),
25638
- ...options
25639
- });
25640
- }
25641
- return controls;
25642
- }
25643
-
25644
- const ACTION_NAME = 'modifyPath';
25645
- const calcPathPointPosition = (pathObject, commandIndex, pointIndex) => {
25646
- const {
25647
- path,
25648
- pathOffset
25649
- } = pathObject;
25650
- const command = path[commandIndex];
25651
- return new Point(command[pointIndex] - pathOffset.x, command[pointIndex + 1] - pathOffset.y).transform(multiplyTransformMatrices(pathObject.getViewportTransform(), pathObject.calcTransformMatrix()));
25652
- };
25653
- const movePathPoint = (pathObject, x, y, commandIndex, pointIndex) => {
25654
- const {
25655
- path,
25656
- pathOffset
25657
- } = pathObject;
25658
- const anchorCommand = path[(commandIndex > 0 ? commandIndex : path.length) - 1];
25659
- const anchorPoint = new Point(anchorCommand[pointIndex], anchorCommand[pointIndex + 1]);
25660
- const anchorPointInParentPlane = anchorPoint.subtract(pathOffset).transform(pathObject.calcOwnMatrix());
25661
- const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, pathObject.calcOwnMatrix());
25662
- path[commandIndex][pointIndex] = mouseLocalPosition.x + pathOffset.x;
25663
- path[commandIndex][pointIndex + 1] = mouseLocalPosition.y + pathOffset.y;
25664
- pathObject.setDimensions();
25665
- const newAnchorPointInParentPlane = anchorPoint.subtract(pathObject.pathOffset).transform(pathObject.calcOwnMatrix());
25666
- const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
25667
- pathObject.left -= diff.x;
25668
- pathObject.top -= diff.y;
25669
- pathObject.set('dirty', true);
25670
- return true;
25671
- };
25672
-
25673
- /**
25674
- * This function locates the controls.
25675
- * It'll be used both for drawing and for interaction.
25676
- */
25677
- function pathPositionHandler(dim, finalMatrix, pathObject) {
25678
- const {
25679
- commandIndex,
25680
- pointIndex
25681
- } = this;
25682
- return calcPathPointPosition(pathObject, commandIndex, pointIndex);
25683
- }
25684
-
25685
- /**
25686
- * This function defines what the control does.
25687
- * It'll be called on every mouse move after a control has been clicked and is being dragged.
25688
- * The function receives as argument the mouse event, the current transform object
25689
- * and the current position in canvas coordinate `transform.target` is a reference to the
25690
- * current object being transformed.
25691
- */
25692
- function pathActionHandler(eventData, transform, x, y) {
25693
- const {
25694
- target
25695
- } = transform;
25696
- const {
25697
- commandIndex,
25698
- pointIndex
25699
- } = this;
25700
- const actionPerformed = movePathPoint(target, x, y, commandIndex, pointIndex);
25701
- {
25702
- fireEvent(this.actionName, {
25703
- ...commonEventInfo(eventData, transform, x, y),
25704
- commandIndex,
25705
- pointIndex
25706
- });
25707
- }
25708
- return actionPerformed;
25709
- }
25710
- const indexFromPrevCommand = previousCommandType => previousCommandType === 'C' ? 5 : previousCommandType === 'Q' ? 3 : 1;
25711
- class PathPointControl extends Control {
25712
- constructor(options) {
25713
- super(options);
25714
- }
25715
- render(ctx, left, top, styleOverride, fabricObject) {
25716
- const overrides = {
25717
- ...styleOverride,
25718
- cornerColor: this.controlFill,
25719
- cornerStrokeColor: this.controlStroke,
25720
- transparentCorners: !this.controlFill
25721
- };
25722
- super.render(ctx, left, top, overrides, fabricObject);
25723
- }
25724
- }
25725
- class PathControlPointControl extends PathPointControl {
25726
- constructor(options) {
25727
- super(options);
25728
- }
25729
- render(ctx, left, top, styleOverride, fabricObject) {
25730
- const {
25731
- path
25732
- } = fabricObject;
25733
- const {
25734
- commandIndex,
25735
- pointIndex,
25736
- connectToCommandIndex,
25737
- connectToPointIndex
25738
- } = this;
25739
- ctx.save();
25740
- ctx.strokeStyle = this.controlStroke;
25741
- if (this.connectionDashArray) {
25742
- ctx.setLineDash(this.connectionDashArray);
25743
- }
25744
- const [commandType] = path[commandIndex];
25745
- const point = calcPathPointPosition(fabricObject, connectToCommandIndex, connectToPointIndex);
25746
- if (commandType === 'Q') {
25747
- // one control point connects to 2 points
25748
- const point2 = calcPathPointPosition(fabricObject, commandIndex, pointIndex + 2);
25749
- ctx.moveTo(point2.x, point2.y);
25750
- ctx.lineTo(left, top);
25751
- } else {
25752
- ctx.moveTo(left, top);
25753
- }
25754
- ctx.lineTo(point.x, point.y);
25755
- ctx.stroke();
25756
- ctx.restore();
25757
- super.render(ctx, left, top, styleOverride, fabricObject);
25758
- }
25759
- }
25760
- const createControl = (commandIndexPos, pointIndexPos, isControlPoint, options, connectToCommandIndex, connectToPointIndex) => new (isControlPoint ? PathControlPointControl : PathPointControl)({
25761
- commandIndex: commandIndexPos,
25762
- pointIndex: pointIndexPos,
25763
- actionName: ACTION_NAME,
25764
- positionHandler: pathPositionHandler,
25765
- actionHandler: pathActionHandler,
25766
- connectToCommandIndex,
25767
- connectToPointIndex,
25768
- ...options,
25769
- ...(isControlPoint ? options.controlPointStyle : options.pointStyle)
25770
- });
25771
- function createPathControls(path) {
25772
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
25773
- const controls = {};
25774
- let previousCommandType = 'M';
25775
- path.path.forEach((command, commandIndex) => {
25776
- const commandType = command[0];
25777
- if (commandType !== 'Z') {
25778
- controls[`c_${commandIndex}_${commandType}`] = createControl(commandIndex, command.length - 2, false, options);
25779
- }
25780
- switch (commandType) {
25781
- case 'C':
25782
- controls[`c_${commandIndex}_C_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex - 1, indexFromPrevCommand(previousCommandType));
25783
- controls[`c_${commandIndex}_C_CP_2`] = createControl(commandIndex, 3, true, options, commandIndex, 5);
25784
- break;
25785
- case 'Q':
25786
- controls[`c_${commandIndex}_Q_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex, 3);
25787
- break;
25788
- }
25789
- previousCommandType = commandType;
25790
- });
25791
- return controls;
25792
- }
25793
-
25794
- var index = /*#__PURE__*/Object.freeze({
25795
- __proto__: null,
25796
- changeWidth: changeWidth,
25797
- createObjectDefaultControls: createObjectDefaultControls,
25798
- createPathControls: createPathControls,
25799
- createPolyActionHandler: createPolyActionHandler,
25800
- createPolyControls: createPolyControls,
25801
- createPolyPositionHandler: createPolyPositionHandler,
25802
- createResizeControls: createResizeControls,
25803
- createTextboxDefaultControls: createTextboxDefaultControls,
25804
- dragHandler: dragHandler,
25805
- factoryPolyActionHandler: factoryPolyActionHandler,
25806
- getLocalPoint: getLocalPoint,
25807
- polyActionHandler: polyActionHandler,
25808
- renderCircleControl: renderCircleControl,
25809
- renderSquareControl: renderSquareControl,
25810
- rotationStyleHandler: rotationStyleHandler,
25811
- rotationWithSnapping: rotationWithSnapping,
25812
- scaleCursorStyleHandler: scaleCursorStyleHandler,
25813
- scaleOrSkewActionName: scaleOrSkewActionName,
25814
- scaleSkewCursorStyleHandler: scaleSkewCursorStyleHandler,
25815
- scalingEqually: scalingEqually,
25816
- scalingX: scalingX,
25817
- scalingXOrSkewingY: scalingXOrSkewingY,
25818
- scalingY: scalingY,
25819
- scalingYOrSkewingX: scalingYOrSkewingX,
25820
- skewCursorStyleHandler: skewCursorStyleHandler,
25821
- skewHandlerX: skewHandlerX,
25822
- skewHandlerY: skewHandlerY,
25823
- wrapWithFireEvent: wrapWithFireEvent,
25824
- wrapWithFixedAnchor: wrapWithFixedAnchor
25825
- });
25826
-
25827
25918
  const isWebGLPipelineState = options => {
25828
25919
  return options.webgl !== undefined;
25829
25920
  };