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
package/dist/index.mjs CHANGED
@@ -356,7 +356,7 @@ class Cache {
356
356
  }
357
357
  const cache = new Cache();
358
358
 
359
- var version = "7.0.0";
359
+ var version = "7.2.0";
360
360
 
361
361
  // use this syntax so babel plugin see this import here
362
362
  const VERSION = version;
@@ -2191,6 +2191,111 @@ const staticCanvasDefaults = {
2191
2191
  patternQuality: 'best'
2192
2192
  };
2193
2193
 
2194
+ /**
2195
+ * Capitalizes a string
2196
+ * @param {String} string String to capitalize
2197
+ * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
2198
+ * and other letters stay untouched, if false first letter is capitalized
2199
+ * and other letters are converted to lowercase.
2200
+ * @return {String} Capitalized version of a string
2201
+ */
2202
+ const capitalize = function (string) {
2203
+ let firstLetterOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2204
+ return `${string.charAt(0).toUpperCase()}${firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase()}`;
2205
+ };
2206
+
2207
+ /**
2208
+ * Escapes XML in a string
2209
+ * @param {String} string String to escape
2210
+ * @return {String} Escaped version of a string
2211
+ */
2212
+ const escapeXml = stringOrNumber => stringOrNumber.toString().replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
2213
+ let segmenter;
2214
+ const getSegmenter = () => {
2215
+ if (!segmenter) {
2216
+ segmenter = 'Intl' in getFabricWindow() && 'Segmenter' in Intl && new Intl.Segmenter(undefined, {
2217
+ granularity: 'grapheme'
2218
+ });
2219
+ }
2220
+ return segmenter;
2221
+ };
2222
+
2223
+ /**
2224
+ * Divide a string in the user perceived single units
2225
+ * @param {String} textstring String to escape
2226
+ * @return {Array} array containing the graphemes
2227
+ */
2228
+ const graphemeSplit = textstring => {
2229
+ segmenter || getSegmenter();
2230
+ if (segmenter) {
2231
+ const segments = segmenter.segment(textstring);
2232
+ return Array.from(segments).map(_ref => {
2233
+ let {
2234
+ segment
2235
+ } = _ref;
2236
+ return segment;
2237
+ });
2238
+ }
2239
+
2240
+ //Fallback
2241
+ return graphemeSplitImpl(textstring);
2242
+ };
2243
+ const graphemeSplitImpl = textstring => {
2244
+ const graphemes = [];
2245
+ for (let i = 0, chr; i < textstring.length; i++) {
2246
+ if ((chr = getWholeChar(textstring, i)) === false) {
2247
+ continue;
2248
+ }
2249
+ graphemes.push(chr);
2250
+ }
2251
+ return graphemes;
2252
+ };
2253
+
2254
+ // taken from mdn in the charAt doc page.
2255
+ const getWholeChar = (str, i) => {
2256
+ const code = str.charCodeAt(i);
2257
+ if (isNaN(code)) {
2258
+ return ''; // Position not found
2259
+ }
2260
+ if (code < 0xd800 || code > 0xdfff) {
2261
+ return str.charAt(i);
2262
+ }
2263
+
2264
+ // High surrogate (could change last hex to 0xDB7F to treat high private
2265
+ // surrogates as single characters)
2266
+ if (0xd800 <= code && code <= 0xdbff) {
2267
+ if (str.length <= i + 1) {
2268
+ throw 'High surrogate without following low surrogate';
2269
+ }
2270
+ const next = str.charCodeAt(i + 1);
2271
+ if (0xdc00 > next || next > 0xdfff) {
2272
+ throw 'High surrogate without following low surrogate';
2273
+ }
2274
+ return str.charAt(i) + str.charAt(i + 1);
2275
+ }
2276
+ // Low surrogate (0xDC00 <= code && code <= 0xDFFF)
2277
+ if (i === 0) {
2278
+ throw 'Low surrogate without preceding high surrogate';
2279
+ }
2280
+ const prev = str.charCodeAt(i - 1);
2281
+
2282
+ // (could change last hex to 0xDB7F to treat high private
2283
+ // surrogates as single characters)
2284
+ if (0xd800 > prev || prev > 0xdbff) {
2285
+ throw 'Low surrogate without preceding high surrogate';
2286
+ }
2287
+ // We can pass over low surrogates now as the second component
2288
+ // in a pair which we have already processed
2289
+ return false;
2290
+ };
2291
+
2292
+ var lang_string = /*#__PURE__*/Object.freeze({
2293
+ __proto__: null,
2294
+ capitalize: capitalize,
2295
+ escapeXml: escapeXml,
2296
+ graphemeSplit: graphemeSplit
2297
+ });
2298
+
2194
2299
  /**
2195
2300
  * Having both options in TCanvasSizeOptions set to true transform the call in a calcOffset
2196
2301
  * Better try to restrict with types to avoid confusion.
@@ -2960,7 +3065,8 @@ class StaticCanvas extends createCollectionMixin(CommonMethods) {
2960
3065
  this._setSVGPreamble(markup, options);
2961
3066
  this._setSVGHeader(markup, options);
2962
3067
  if (this.clipPath) {
2963
- markup.push(`<g clip-path="url(#${this.clipPath.clipPathId})" >\n`);
3068
+ var _this$clipPath$clipPa;
3069
+ markup.push(`<g clip-path="url(#${escapeXml((_this$clipPath$clipPa = this.clipPath.clipPathId) !== null && _this$clipPath$clipPa !== void 0 ? _this$clipPath$clipPa : '')})" >\n`);
2964
3070
  }
2965
3071
  this._setSVGBgOverlayColor(markup, 'background');
2966
3072
  this._setSVGBgOverlayImage(markup, 'backgroundImage', reviver);
@@ -3639,20 +3745,6 @@ const sendObjectToPlane = (object, from, to) => {
3639
3745
  return t;
3640
3746
  };
3641
3747
 
3642
- const fireEvent = (eventName, options) => {
3643
- var _target$canvas;
3644
- const {
3645
- transform: {
3646
- target
3647
- }
3648
- } = options;
3649
- (_target$canvas = target.canvas) === null || _target$canvas === void 0 || _target$canvas.fire(`object:${eventName}`, {
3650
- ...options,
3651
- target
3652
- });
3653
- target.fire(eventName, options);
3654
- };
3655
-
3656
3748
  const originOffset = {
3657
3749
  left: -0.5,
3658
3750
  top: -0.5,
@@ -3855,33 +3947,6 @@ function getLocalPoint(_ref, originX, originY, x, y) {
3855
3947
  return localPoint;
3856
3948
  }
3857
3949
 
3858
- /**
3859
- * Action handler
3860
- * @private
3861
- * @param {Event} eventData javascript event that is doing the transform
3862
- * @param {Object} transform javascript object containing a series of information around the current transform
3863
- * @param {number} x current mouse x position, canvas normalized
3864
- * @param {number} y current mouse y position, canvas normalized
3865
- * @return {Boolean} true if the translation occurred
3866
- */
3867
- const dragHandler = (eventData, transform, x, y) => {
3868
- const {
3869
- target,
3870
- offsetX,
3871
- offsetY
3872
- } = transform,
3873
- newLeft = x - offsetX,
3874
- newTop = y - offsetY,
3875
- moveX = !isLocked(target, 'lockMovementX') && target.left !== newLeft,
3876
- moveY = !isLocked(target, 'lockMovementY') && target.top !== newTop;
3877
- moveX && target.set(LEFT, newLeft);
3878
- moveY && target.set(TOP, newTop);
3879
- if (moveX || moveY) {
3880
- fireEvent(MOVING, commonEventInfo(eventData, transform, x, y));
3881
- }
3882
- return moveX || moveY;
3883
- };
3884
-
3885
3950
  const normalizeWs = value => value.replace(/\s+/g, ' ');
3886
3951
 
3887
3952
  /**
@@ -4628,7 +4693,7 @@ const colorPropToSVG = function (prop, value) {
4628
4693
  if (!value) {
4629
4694
  colorValue = 'none';
4630
4695
  } else if (value.toLive) {
4631
- colorValue = `url(#SVGID_${value.id})`;
4696
+ colorValue = `url(#SVGID_${escapeXml(value.id)})`;
4632
4697
  } else {
4633
4698
  const color = new Color(value),
4634
4699
  opacity = color.getAlpha();
@@ -4681,7 +4746,7 @@ class FabricObjectSVGExportMixin {
4681
4746
  filter = skipShadow ? '' : this.getSvgFilter(),
4682
4747
  fill = colorPropToSVG(FILL, this.fill),
4683
4748
  stroke = colorPropToSVG(STROKE, this.stroke);
4684
- 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('');
4749
+ 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('');
4685
4750
  }
4686
4751
 
4687
4752
  /**
@@ -4689,7 +4754,7 @@ class FabricObjectSVGExportMixin {
4689
4754
  * @return {String}
4690
4755
  */
4691
4756
  getSvgFilter() {
4692
- return this.shadow ? `filter: url(#SVGID_${this.shadow.id});` : '';
4757
+ return this.shadow ? `filter: url(#SVGID_${escapeXml(this.shadow.id)});` : '';
4693
4758
  }
4694
4759
 
4695
4760
  /**
@@ -4697,7 +4762,7 @@ class FabricObjectSVGExportMixin {
4697
4762
  * @return {String}
4698
4763
  */
4699
4764
  getSvgCommons() {
4700
- return [this.id ? `id="${this.id}" ` : '', this.clipPath ? `clip-path="url(#${this.clipPath.clipPathId})" ` : ''].join('');
4765
+ return [this.id ? `id="${escapeXml(String(this.id))}" ` : '', this.clipPath ? `clip-path="url(#${this.clipPath.clipPathId})" ` : ''].join('');
4701
4766
  }
4702
4767
 
4703
4768
  /**
@@ -4810,7 +4875,7 @@ class FabricObjectSVGExportMixin {
4810
4875
  return reviver ? reviver(markup.join('')) : markup.join('');
4811
4876
  }
4812
4877
  addPaintOrder() {
4813
- return this.paintFirst !== FILL ? ` paint-order="${this.paintFirst}" ` : '';
4878
+ return this.paintFirst !== FILL ? ` paint-order="${escapeXml(this.paintFirst)}" ` : '';
4814
4879
  }
4815
4880
  }
4816
4881
 
@@ -4948,7 +5013,6 @@ const reViewBoxAttrValue = new RegExp(String.raw`^\s*(${reNum})${viewportSeparat
4948
5013
 
4949
5014
  (?:$|\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.
4950
5015
  */
4951
- // eslint-disable-next-line max-len
4952
5016
 
4953
5017
  const shadowOffsetRegex = '(-?\\d+(?:\\.\\d*)?(?:px)?(?:\\s?|$))?';
4954
5018
  const reOffsetsAndBlur = new RegExp('(?:\\s|^)' + shadowOffsetRegex + shadowOffsetRegex + '(' + reNum + '?(?:px)?)?(?:\\s?|$)(?:$|\\s)');
@@ -5007,14 +5071,15 @@ class Shadow {
5007
5071
  toSVG(object) {
5008
5072
  const offset = rotateVector(new Point(this.offsetX, this.offsetY), degreesToRadians(-object.angle)),
5009
5073
  BLUR_BOX = 20,
5074
+ NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS,
5010
5075
  color = new Color(this.color);
5011
5076
  let fBoxX = 40,
5012
5077
  fBoxY = 40;
5013
5078
  if (object.width && object.height) {
5014
5079
  //http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
5015
5080
  // we add some extra space to filter box to contain the blur ( 20 )
5016
- fBoxX = toFixed((Math.abs(offset.x) + this.blur) / object.width, config.NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5017
- fBoxY = toFixed((Math.abs(offset.y) + this.blur) / object.height, config.NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5081
+ fBoxX = toFixed((Math.abs(offset.x) + this.blur) / object.width, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5082
+ fBoxY = toFixed((Math.abs(offset.y) + this.blur) / object.height, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5018
5083
  }
5019
5084
  if (object.flipX) {
5020
5085
  offset.x *= -1;
@@ -5022,7 +5087,7 @@ class Shadow {
5022
5087
  if (object.flipY) {
5023
5088
  offset.y *= -1;
5024
5089
  }
5025
- 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`;
5090
+ 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`;
5026
5091
  }
5027
5092
 
5028
5093
  /**
@@ -6432,7 +6497,7 @@ class ObjectGeometry extends CommonMethods {
6432
6497
  calcOwnMatrix() {
6433
6498
  const key = this.transformMatrixKey(true),
6434
6499
  cache = this.ownMatrixCache;
6435
- if (cache && cache.key === key) {
6500
+ if (cache && cache.key.every((x, i) => x === key[i])) {
6436
6501
  return cache.value;
6437
6502
  }
6438
6503
  const center = this.getRelativeCenterPoint(),
@@ -7173,6 +7238,9 @@ let FabricObject$1 = class FabricObject extends ObjectGeometry {
7173
7238
  } else {
7174
7239
  this._renderBackground(ctx);
7175
7240
  }
7241
+ this.fire('before:render', {
7242
+ ctx
7243
+ });
7176
7244
  this._render(ctx);
7177
7245
  this._drawClipPath(ctx, this.clipPath, context);
7178
7246
  this.fill = originalFill;
@@ -8173,6 +8241,20 @@ _defineProperty(FabricObject$1, "customProperties", []);
8173
8241
  classRegistry.setClass(FabricObject$1);
8174
8242
  classRegistry.setClass(FabricObject$1, 'object');
8175
8243
 
8244
+ const fireEvent = (eventName, options) => {
8245
+ var _target$canvas;
8246
+ const {
8247
+ transform: {
8248
+ target
8249
+ }
8250
+ } = options;
8251
+ (_target$canvas = target.canvas) === null || _target$canvas === void 0 || _target$canvas.fire(`object:${eventName}`, {
8252
+ ...options,
8253
+ target
8254
+ });
8255
+ target.fire(eventName, options);
8256
+ };
8257
+
8176
8258
  /**
8177
8259
  * Wrap an action handler with firing an event if the action is performed
8178
8260
  * @param {TModificationEvents} eventName the event we want to fire
@@ -8215,34 +8297,62 @@ function wrapWithFixedAnchor(actionHandler) {
8215
8297
  };
8216
8298
  }
8217
8299
 
8218
- /**
8219
- * Action handler to change object's width
8220
- * Needs to be wrapped with `wrapWithFixedAnchor` to be effective
8221
- * @param {Event} eventData javascript event that is doing the transform
8222
- * @param {Object} transform javascript object containing a series of information around the current transform
8223
- * @param {number} x current mouse x position, canvas normalized
8224
- * @param {number} y current mouse y position, canvas normalized
8225
- * @return {Boolean} true if some change happened
8226
- */
8227
- const changeObjectWidth = (eventData, transform, x, y) => {
8300
+ const changeObjectDimensionGen = (dimension, origin, xorY, scale) => (eventData, transform, x, y) => {
8228
8301
  const localPoint = getLocalPoint(transform, transform.originX, transform.originY, x, y);
8302
+ const localPointValue = localPoint[xorY];
8229
8303
  // make sure the control changes width ONLY from it's side of target
8230
- if (resolveOrigin(transform.originX) === resolveOrigin(CENTER) || resolveOrigin(transform.originX) === resolveOrigin(RIGHT) && localPoint.x < 0 || resolveOrigin(transform.originX) === resolveOrigin(LEFT) && localPoint.x > 0) {
8304
+ const originValue = resolveOrigin(transform[origin]);
8305
+ if (originValue === 0 || originValue > 0 && localPointValue < 0 || originValue < 0 && localPointValue > 0) {
8231
8306
  const {
8232
8307
  target
8233
8308
  } = transform,
8234
- strokePadding = target.strokeWidth / (target.strokeUniform ? target.scaleX : 1),
8309
+ strokePadding = target.strokeWidth / (target.strokeUniform ? target[scale] : 1),
8235
8310
  multiplier = isTransformCentered(transform) ? 2 : 1,
8236
- oldWidth = target.width,
8237
- newWidth = Math.abs(localPoint.x * multiplier / target.scaleX) - strokePadding;
8238
- target.set('width', Math.max(newWidth, 1));
8311
+ oldWidth = target[dimension],
8312
+ newWidth = Math.abs(localPointValue * multiplier / target[scale]) - strokePadding;
8313
+ target.set(dimension, Math.max(newWidth, 1));
8239
8314
  // check against actual target width in case `newWidth` was rejected
8240
- return oldWidth !== target.width;
8315
+ return oldWidth !== target[dimension];
8241
8316
  }
8242
8317
  return false;
8243
8318
  };
8319
+
8320
+ /**
8321
+ * Action handler to change object's width
8322
+ * Needs to be wrapped with `wrapWithFixedAnchor` to be effective
8323
+ * You want to use this only if you are building a new control handler and you want
8324
+ * to reuse some logic. use "changeWidth" if you are looking to just use a control for width
8325
+ * @param {Event} eventData javascript event that is doing the transform
8326
+ * @param {Object} transform javascript object containing a series of information around the current transform
8327
+ * @param {number} x current mouse x position, canvas normalized
8328
+ * @param {number} y current mouse y position, canvas normalized
8329
+ * @return {Boolean} true if some change happened
8330
+ */
8331
+ const changeObjectWidth = changeObjectDimensionGen('width', 'originX', 'x', 'scaleX');
8332
+
8333
+ /**
8334
+ * Action handler to change object's height
8335
+ * Needs to be wrapped with `wrapWithFixedAnchor` to be effective
8336
+ * You want to use this only if you are building a new control handler and you want
8337
+ * to reuse some logic. use "changeHeight" if you are looking to just use a control for height
8338
+ * @param {Event} eventData javascript event that is doing the transform
8339
+ * @param {Object} transform javascript object containing a series of information around the current transform
8340
+ * @param {number} x current mouse x position, canvas normalized
8341
+ * @param {number} y current mouse y position, canvas normalized
8342
+ * @return {Boolean} true if some change happened
8343
+ */
8344
+ const changeObjectHeight = changeObjectDimensionGen('height', 'originY', 'y', 'scaleY');
8345
+
8346
+ /**
8347
+ * Control handler for changing width
8348
+ */
8244
8349
  const changeWidth = wrapWithFireEvent(RESIZING, wrapWithFixedAnchor(changeObjectWidth));
8245
8350
 
8351
+ /**
8352
+ * Control handler for changing height
8353
+ */
8354
+ const changeHeight = wrapWithFireEvent(RESIZING, wrapWithFixedAnchor(changeObjectHeight));
8355
+
8246
8356
  /**
8247
8357
  * Render a round control, as per fabric features.
8248
8358
  * This function is written to respect object properties like transparentCorners, cornerSize
@@ -8255,33 +8365,24 @@ const changeWidth = wrapWithFireEvent(RESIZING, wrapWithFixedAnchor(changeObject
8255
8365
  * @param {FabricObject} fabricObject the fabric object for which we are rendering controls
8256
8366
  */
8257
8367
  function renderCircleControl(ctx, left, top, styleOverride, fabricObject) {
8258
- styleOverride = styleOverride || {};
8259
- const xSize = this.sizeX || styleOverride.cornerSize || fabricObject.cornerSize,
8260
- ySize = this.sizeY || styleOverride.cornerSize || fabricObject.cornerSize,
8261
- transparentCorners = typeof styleOverride.transparentCorners !== 'undefined' ? styleOverride.transparentCorners : fabricObject.transparentCorners,
8262
- methodName = transparentCorners ? STROKE : FILL,
8263
- stroke = !transparentCorners && (styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor);
8264
- let myLeft = left,
8265
- myTop = top,
8266
- size;
8267
8368
  ctx.save();
8268
- ctx.fillStyle = styleOverride.cornerColor || fabricObject.cornerColor || '';
8269
- ctx.strokeStyle = styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor || '';
8369
+ const {
8370
+ stroke,
8371
+ xSize,
8372
+ ySize,
8373
+ opName
8374
+ } = this.commonRenderProps(ctx, left, top, fabricObject, styleOverride);
8375
+ let size = xSize;
8270
8376
  // TODO: use proper ellipse code.
8271
8377
  if (xSize > ySize) {
8272
- size = xSize;
8273
8378
  ctx.scale(1.0, ySize / xSize);
8274
- myTop = top * xSize / ySize;
8275
8379
  } else if (ySize > xSize) {
8276
8380
  size = ySize;
8277
8381
  ctx.scale(xSize / ySize, 1.0);
8278
- myLeft = left * ySize / xSize;
8279
- } else {
8280
- size = xSize;
8281
8382
  }
8282
8383
  ctx.beginPath();
8283
- ctx.arc(myLeft, myTop, size / 2, 0, twoMathPi, false);
8284
- ctx[methodName]();
8384
+ ctx.arc(0, 0, size / 2, 0, twoMathPi, false);
8385
+ ctx[opName]();
8285
8386
  if (stroke) {
8286
8387
  ctx.stroke();
8287
8388
  }
@@ -8300,25 +8401,19 @@ function renderCircleControl(ctx, left, top, styleOverride, fabricObject) {
8300
8401
  * @param {FabricObject} fabricObject the fabric object for which we are rendering controls
8301
8402
  */
8302
8403
  function renderSquareControl(ctx, left, top, styleOverride, fabricObject) {
8303
- styleOverride = styleOverride || {};
8304
- const xSize = this.sizeX || styleOverride.cornerSize || fabricObject.cornerSize,
8305
- ySize = this.sizeY || styleOverride.cornerSize || fabricObject.cornerSize,
8306
- transparentCorners = typeof styleOverride.transparentCorners !== 'undefined' ? styleOverride.transparentCorners : fabricObject.transparentCorners,
8307
- methodName = transparentCorners ? STROKE : FILL,
8308
- stroke = !transparentCorners && (styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor),
8404
+ ctx.save();
8405
+ const {
8406
+ stroke,
8407
+ xSize,
8408
+ ySize,
8409
+ opName
8410
+ } = this.commonRenderProps(ctx, left, top, fabricObject, styleOverride),
8309
8411
  xSizeBy2 = xSize / 2,
8310
8412
  ySizeBy2 = ySize / 2;
8311
- ctx.save();
8312
- ctx.fillStyle = styleOverride.cornerColor || fabricObject.cornerColor || '';
8313
- ctx.strokeStyle = styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor || '';
8314
- ctx.translate(left, top);
8315
- // angle is relative to canvas plane
8316
- const angle = fabricObject.getTotalAngle();
8317
- ctx.rotate(degreesToRadians(angle));
8318
8413
  // this does not work, and fixed with ( && ) does not make sense.
8319
8414
  // to have real transparent corners we need the controls on upperCanvas
8320
8415
  // transparentCorners || ctx.clearRect(-xSizeBy2, -ySizeBy2, xSize, ySize);
8321
- ctx[`${methodName}Rect`](-xSizeBy2, -ySizeBy2, xSize, ySize);
8416
+ ctx[`${opName}Rect`](-xSizeBy2, -ySizeBy2, xSize, ySize);
8322
8417
  if (stroke) {
8323
8418
  ctx.strokeRect(-xSizeBy2, -ySizeBy2, xSize, ySize);
8324
8419
  }
@@ -8436,6 +8531,14 @@ class Control {
8436
8531
  _defineProperty(this, "withConnection", false);
8437
8532
  Object.assign(this, options);
8438
8533
  }
8534
+ getTransformAnchorPoint() {
8535
+ var _this$transformAnchor;
8536
+ return (// return the control transformAnchorPoint
8537
+ (_this$transformAnchor = this.transformAnchorPoint) !== null && _this$transformAnchor !== void 0 ? _this$transformAnchor :
8538
+ // otherwise will return the opposite origin of where the control is located.
8539
+ new Point(-this.x + 0.5, -this.y + 0.5)
8540
+ );
8541
+ }
8439
8542
 
8440
8543
  /**
8441
8544
  * The control actionHandler, provide one to handle action ( control being moved )
@@ -8578,6 +8681,41 @@ class Control {
8578
8681
  };
8579
8682
  }
8580
8683
 
8684
+ /**
8685
+ * This is an helper method to prepare the canvas to render a control
8686
+ * It detectes common control properties and sets the correct fill and
8687
+ * stroke styles on the context. It does not execute translations or
8688
+ * rotations since different controls need differnt combination of these.
8689
+ */
8690
+ commonRenderProps(ctx, left, top, fabricObject) {
8691
+ let styleOverride = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
8692
+ const {
8693
+ cornerSize,
8694
+ cornerColor,
8695
+ transparentCorners,
8696
+ cornerStrokeColor
8697
+ } = styleOverride,
8698
+ sizeFromProps = cornerSize || fabricObject.cornerSize,
8699
+ xSize = this.sizeX || sizeFromProps,
8700
+ ySize = this.sizeY || sizeFromProps,
8701
+ transparent = typeof transparentCorners !== 'undefined' ? transparentCorners : fabricObject.transparentCorners,
8702
+ opName = transparent ? STROKE : FILL,
8703
+ strokeColor = cornerStrokeColor || fabricObject.cornerStrokeColor,
8704
+ stroke = !transparent && !!strokeColor;
8705
+ ctx.fillStyle = cornerColor || fabricObject.cornerColor || '';
8706
+ ctx.strokeStyle = strokeColor || '';
8707
+ ctx.translate(left, top);
8708
+ // angle is relative to canvas plane
8709
+ ctx.rotate(degreesToRadians(fabricObject.getTotalAngle()));
8710
+ return {
8711
+ stroke,
8712
+ xSize,
8713
+ ySize,
8714
+ transparentCorners: transparent,
8715
+ opName
8716
+ };
8717
+ }
8718
+
8581
8719
  /**
8582
8720
  * Render function for the control.
8583
8721
  * When this function runs the context is unscaled. unrotate. Just retina scaled.
@@ -10217,136 +10355,31 @@ const cloneStyles = style => {
10217
10355
  };
10218
10356
 
10219
10357
  /**
10220
- * Capitalizes a string
10221
- * @param {String} string String to capitalize
10222
- * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
10223
- * and other letters stay untouched, if false first letter is capitalized
10224
- * and other letters are converted to lowercase.
10225
- * @return {String} Capitalized version of a string
10358
+ * @param {Object} prevStyle first style to compare
10359
+ * @param {Object} thisStyle second style to compare
10360
+ * @param {boolean} forTextSpans whether to check overline, underline, and line-through properties
10361
+ * @return {boolean} true if the style changed
10226
10362
  */
10227
- const capitalize = function (string) {
10228
- let firstLetterOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
10229
- return `${string.charAt(0).toUpperCase()}${firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase()}`;
10363
+ const hasStyleChanged = function (prevStyle, thisStyle) {
10364
+ let forTextSpans = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
10365
+ 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);
10230
10366
  };
10231
10367
 
10232
10368
  /**
10233
- * Escapes XML in a string
10234
- * @param {String} string String to escape
10235
- * @return {String} Escaped version of a string
10369
+ * Returns the array form of a text object's inline styles property with styles grouped in ranges
10370
+ * rather than per character. This format is less verbose, and is better suited for storage
10371
+ * so it is used in serialization (not during runtime).
10372
+ * @param {object} styles per character styles for a text object
10373
+ * @param {String} text the text string that the styles are applied to
10374
+ * @return {{start: number, end: number, style: object}[]}
10236
10375
  */
10237
- const escapeXml = string => string.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
10238
- let segmenter;
10239
- const getSegmenter = () => {
10240
- if (!segmenter) {
10241
- segmenter = 'Intl' in getFabricWindow() && 'Segmenter' in Intl && new Intl.Segmenter(undefined, {
10242
- granularity: 'grapheme'
10243
- });
10244
- }
10245
- return segmenter;
10246
- };
10247
-
10248
- /**
10249
- * Divide a string in the user perceived single units
10250
- * @param {String} textstring String to escape
10251
- * @return {Array} array containing the graphemes
10252
- */
10253
- const graphemeSplit = textstring => {
10254
- segmenter || getSegmenter();
10255
- if (segmenter) {
10256
- const segments = segmenter.segment(textstring);
10257
- return Array.from(segments).map(_ref => {
10258
- let {
10259
- segment
10260
- } = _ref;
10261
- return segment;
10262
- });
10263
- }
10264
-
10265
- //Fallback
10266
- return graphemeSplitImpl(textstring);
10267
- };
10268
- const graphemeSplitImpl = textstring => {
10269
- const graphemes = [];
10270
- for (let i = 0, chr; i < textstring.length; i++) {
10271
- if ((chr = getWholeChar(textstring, i)) === false) {
10272
- continue;
10273
- }
10274
- graphemes.push(chr);
10275
- }
10276
- return graphemes;
10277
- };
10278
-
10279
- // taken from mdn in the charAt doc page.
10280
- const getWholeChar = (str, i) => {
10281
- const code = str.charCodeAt(i);
10282
- if (isNaN(code)) {
10283
- return ''; // Position not found
10284
- }
10285
- if (code < 0xd800 || code > 0xdfff) {
10286
- return str.charAt(i);
10287
- }
10288
-
10289
- // High surrogate (could change last hex to 0xDB7F to treat high private
10290
- // surrogates as single characters)
10291
- if (0xd800 <= code && code <= 0xdbff) {
10292
- if (str.length <= i + 1) {
10293
- throw 'High surrogate without following low surrogate';
10294
- }
10295
- const next = str.charCodeAt(i + 1);
10296
- if (0xdc00 > next || next > 0xdfff) {
10297
- throw 'High surrogate without following low surrogate';
10298
- }
10299
- return str.charAt(i) + str.charAt(i + 1);
10300
- }
10301
- // Low surrogate (0xDC00 <= code && code <= 0xDFFF)
10302
- if (i === 0) {
10303
- throw 'Low surrogate without preceding high surrogate';
10304
- }
10305
- const prev = str.charCodeAt(i - 1);
10306
-
10307
- // (could change last hex to 0xDB7F to treat high private
10308
- // surrogates as single characters)
10309
- if (0xd800 > prev || prev > 0xdbff) {
10310
- throw 'Low surrogate without preceding high surrogate';
10311
- }
10312
- // We can pass over low surrogates now as the second component
10313
- // in a pair which we have already processed
10314
- return false;
10315
- };
10316
-
10317
- var lang_string = /*#__PURE__*/Object.freeze({
10318
- __proto__: null,
10319
- capitalize: capitalize,
10320
- escapeXml: escapeXml,
10321
- graphemeSplit: graphemeSplit
10322
- });
10323
-
10324
- /**
10325
- * @param {Object} prevStyle first style to compare
10326
- * @param {Object} thisStyle second style to compare
10327
- * @param {boolean} forTextSpans whether to check overline, underline, and line-through properties
10328
- * @return {boolean} true if the style changed
10329
- */
10330
- const hasStyleChanged = function (prevStyle, thisStyle) {
10331
- let forTextSpans = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
10332
- 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);
10333
- };
10334
-
10335
- /**
10336
- * Returns the array form of a text object's inline styles property with styles grouped in ranges
10337
- * rather than per character. This format is less verbose, and is better suited for storage
10338
- * so it is used in serialization (not during runtime).
10339
- * @param {object} styles per character styles for a text object
10340
- * @param {String} text the text string that the styles are applied to
10341
- * @return {{start: number, end: number, style: object}[]}
10342
- */
10343
- const stylesToArray = (styles, text) => {
10344
- const textLines = text.split('\n'),
10345
- stylesArray = [];
10346
- let charIndex = -1,
10347
- prevStyle = {};
10348
- // clone style structure to prevent mutation
10349
- styles = cloneStyles(styles);
10376
+ const stylesToArray = (styles, text) => {
10377
+ const textLines = text.split('\n'),
10378
+ stylesArray = [];
10379
+ let charIndex = -1,
10380
+ prevStyle = {};
10381
+ // clone style structure to prevent mutation
10382
+ styles = cloneStyles(styles);
10350
10383
 
10351
10384
  //loop through each textLine
10352
10385
  for (let i = 0; i < textLines.length; i++) {
@@ -10904,7 +10937,7 @@ class Rect extends FabricObject {
10904
10937
  rx,
10905
10938
  ry
10906
10939
  } = this;
10907
- return ['<rect ', 'COMMON_PARTS', `x="${-width / 2}" y="${-height / 2}" rx="${rx}" ry="${ry}" width="${width}" height="${height}" />\n`];
10940
+ return ['<rect ', 'COMMON_PARTS', `x="${-width / 2}" y="${-height / 2}" rx="${escapeXml(rx)}" ry="${escapeXml(ry)}" width="${escapeXml(width)}" height="${escapeXml(height)}" />\n`];
10908
10941
  }
10909
10942
 
10910
10943
  /**
@@ -11869,7 +11902,7 @@ class Group extends createCollectionMixin(FabricObject) {
11869
11902
  * @return {String}
11870
11903
  */
11871
11904
  getSvgStyles() {
11872
- const opacity = typeof this.opacity !== 'undefined' && this.opacity !== 1 ? `opacity: ${this.opacity};` : '',
11905
+ const opacity = typeof this.opacity !== 'undefined' && this.opacity !== 1 ? `opacity: ${escapeXml(this.opacity)};` : '',
11873
11906
  visibility = this.visible ? '' : ' visibility: hidden;';
11874
11907
  return [opacity, this.getSvgFilter(), visibility].join('');
11875
11908
  }
@@ -13171,6 +13204,288 @@ const canvasDefaults = {
13171
13204
  preserveObjectStacking: true
13172
13205
  };
13173
13206
 
13207
+ /**
13208
+ * Action handler
13209
+ * @private
13210
+ * @param {Event} eventData javascript event that is doing the transform
13211
+ * @param {Object} transform javascript object containing a series of information around the current transform
13212
+ * @param {number} x current mouse x position, canvas normalized
13213
+ * @param {number} y current mouse y position, canvas normalized
13214
+ * @return {Boolean} true if the translation occurred
13215
+ */
13216
+ const dragHandler = (eventData, transform, x, y) => {
13217
+ const {
13218
+ target,
13219
+ offsetX,
13220
+ offsetY
13221
+ } = transform,
13222
+ newLeft = x - offsetX,
13223
+ newTop = y - offsetY,
13224
+ moveX = !isLocked(target, 'lockMovementX') && target.left !== newLeft,
13225
+ moveY = !isLocked(target, 'lockMovementY') && target.top !== newTop;
13226
+ moveX && target.set(LEFT, newLeft);
13227
+ moveY && target.set(TOP, newTop);
13228
+ if (moveX || moveY) {
13229
+ fireEvent(MOVING, commonEventInfo(eventData, transform, x, y));
13230
+ }
13231
+ return moveX || moveY;
13232
+ };
13233
+
13234
+ const ACTION_NAME$1 = MODIFY_POLY;
13235
+ /**
13236
+ * This function locates the controls.
13237
+ * It'll be used both for drawing and for interaction.
13238
+ */
13239
+ const createPolyPositionHandler = pointIndex => {
13240
+ return function (dim, finalMatrix, polyObject) {
13241
+ const {
13242
+ points,
13243
+ pathOffset
13244
+ } = polyObject;
13245
+ return new Point(points[pointIndex]).subtract(pathOffset).transform(multiplyTransformMatrices(polyObject.getViewportTransform(), polyObject.calcTransformMatrix()));
13246
+ };
13247
+ };
13248
+
13249
+ /**
13250
+ * This function defines what the control does.
13251
+ * It'll be called on every mouse move after a control has been clicked and is being dragged.
13252
+ * The function receives as argument the mouse event, the current transform object
13253
+ * and the current position in canvas coordinate `transform.target` is a reference to the
13254
+ * current object being transformed.
13255
+ */
13256
+ const polyActionHandler = (eventData, transform, x, y) => {
13257
+ const {
13258
+ target,
13259
+ pointIndex
13260
+ } = transform;
13261
+ const poly = target;
13262
+ const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, poly.calcOwnMatrix());
13263
+ poly.points[pointIndex] = mouseLocalPosition.add(poly.pathOffset);
13264
+ poly.setDimensions();
13265
+ poly.set('dirty', true);
13266
+ return true;
13267
+ };
13268
+
13269
+ /**
13270
+ * Keep the polygon in the same position when we change its `width`/`height`/`top`/`left`.
13271
+ */
13272
+ const factoryPolyActionHandler = (pointIndex, fn) => {
13273
+ return function (eventData, transform, x, y) {
13274
+ const poly = transform.target,
13275
+ anchorPoint = new Point(poly.points[(pointIndex > 0 ? pointIndex : poly.points.length) - 1]),
13276
+ anchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix()),
13277
+ actionPerformed = fn(eventData, {
13278
+ ...transform,
13279
+ pointIndex
13280
+ }, x, y);
13281
+ const newAnchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix());
13282
+ const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
13283
+ poly.left -= diff.x;
13284
+ poly.top -= diff.y;
13285
+ return actionPerformed;
13286
+ };
13287
+ };
13288
+ const createPolyActionHandler = pointIndex => wrapWithFireEvent(ACTION_NAME$1, factoryPolyActionHandler(pointIndex, polyActionHandler));
13289
+ function createPolyControls(arg0) {
13290
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13291
+ const controls = {};
13292
+ for (let idx = 0; idx < (typeof arg0 === 'number' ? arg0 : arg0.points.length); idx++) {
13293
+ controls[`p${idx}`] = new Control({
13294
+ actionName: ACTION_NAME$1,
13295
+ positionHandler: createPolyPositionHandler(idx),
13296
+ actionHandler: createPolyActionHandler(idx),
13297
+ ...options
13298
+ });
13299
+ }
13300
+ return controls;
13301
+ }
13302
+
13303
+ const ACTION_NAME = 'modifyPath';
13304
+ const calcPathPointPosition = (pathObject, commandIndex, pointIndex) => {
13305
+ const {
13306
+ path,
13307
+ pathOffset
13308
+ } = pathObject;
13309
+ const command = path[commandIndex];
13310
+ return new Point(command[pointIndex] - pathOffset.x, command[pointIndex + 1] - pathOffset.y).transform(multiplyTransformMatrices(pathObject.getViewportTransform(), pathObject.calcTransformMatrix()));
13311
+ };
13312
+ const movePathPoint = (pathObject, x, y, commandIndex, pointIndex) => {
13313
+ const {
13314
+ path,
13315
+ pathOffset
13316
+ } = pathObject;
13317
+ const anchorCommand = path[(commandIndex > 0 ? commandIndex : path.length) - 1];
13318
+ const anchorPoint = new Point(anchorCommand[pointIndex], anchorCommand[pointIndex + 1]);
13319
+ const anchorPointInParentPlane = anchorPoint.subtract(pathOffset).transform(pathObject.calcOwnMatrix());
13320
+ const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, pathObject.calcOwnMatrix());
13321
+ path[commandIndex][pointIndex] = mouseLocalPosition.x + pathOffset.x;
13322
+ path[commandIndex][pointIndex + 1] = mouseLocalPosition.y + pathOffset.y;
13323
+ pathObject.setDimensions();
13324
+ const newAnchorPointInParentPlane = anchorPoint.subtract(pathObject.pathOffset).transform(pathObject.calcOwnMatrix());
13325
+ const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
13326
+ pathObject.left -= diff.x;
13327
+ pathObject.top -= diff.y;
13328
+ pathObject.set('dirty', true);
13329
+ return true;
13330
+ };
13331
+
13332
+ /**
13333
+ * This function locates the controls.
13334
+ * It'll be used both for drawing and for interaction.
13335
+ */
13336
+ function pathPositionHandler(dim, finalMatrix, pathObject) {
13337
+ const {
13338
+ commandIndex,
13339
+ pointIndex
13340
+ } = this;
13341
+ return calcPathPointPosition(pathObject, commandIndex, pointIndex);
13342
+ }
13343
+
13344
+ /**
13345
+ * This function defines what the control does.
13346
+ * It'll be called on every mouse move after a control has been clicked and is being dragged.
13347
+ * The function receives as argument the mouse event, the current transform object
13348
+ * and the current position in canvas coordinate `transform.target` is a reference to the
13349
+ * current object being transformed.
13350
+ */
13351
+ function pathActionHandler(eventData, transform, x, y) {
13352
+ const {
13353
+ target
13354
+ } = transform;
13355
+ const {
13356
+ commandIndex,
13357
+ pointIndex
13358
+ } = this;
13359
+ const actionPerformed = movePathPoint(target, x, y, commandIndex, pointIndex);
13360
+ {
13361
+ fireEvent(this.actionName, {
13362
+ ...commonEventInfo(eventData, transform, x, y),
13363
+ commandIndex,
13364
+ pointIndex
13365
+ });
13366
+ }
13367
+ return actionPerformed;
13368
+ }
13369
+ const indexFromPrevCommand = previousCommandType => previousCommandType === 'C' ? 5 : previousCommandType === 'Q' ? 3 : 1;
13370
+ class PathPointControl extends Control {
13371
+ constructor(options) {
13372
+ super(options);
13373
+ }
13374
+ render(ctx, left, top, styleOverride, fabricObject) {
13375
+ const overrides = {
13376
+ ...styleOverride,
13377
+ cornerColor: this.controlFill,
13378
+ cornerStrokeColor: this.controlStroke,
13379
+ transparentCorners: !this.controlFill
13380
+ };
13381
+ super.render(ctx, left, top, overrides, fabricObject);
13382
+ }
13383
+ }
13384
+ class PathControlPointControl extends PathPointControl {
13385
+ constructor(options) {
13386
+ super(options);
13387
+ }
13388
+ render(ctx, left, top, styleOverride, fabricObject) {
13389
+ const {
13390
+ path
13391
+ } = fabricObject;
13392
+ const {
13393
+ commandIndex,
13394
+ pointIndex,
13395
+ connectToCommandIndex,
13396
+ connectToPointIndex
13397
+ } = this;
13398
+ ctx.save();
13399
+ ctx.strokeStyle = this.controlStroke;
13400
+ if (this.connectionDashArray) {
13401
+ ctx.setLineDash(this.connectionDashArray);
13402
+ }
13403
+ const [commandType] = path[commandIndex];
13404
+ const point = calcPathPointPosition(fabricObject, connectToCommandIndex, connectToPointIndex);
13405
+ if (commandType === 'Q') {
13406
+ // one control point connects to 2 points
13407
+ const point2 = calcPathPointPosition(fabricObject, commandIndex, pointIndex + 2);
13408
+ ctx.moveTo(point2.x, point2.y);
13409
+ ctx.lineTo(left, top);
13410
+ } else {
13411
+ ctx.moveTo(left, top);
13412
+ }
13413
+ ctx.lineTo(point.x, point.y);
13414
+ ctx.stroke();
13415
+ ctx.restore();
13416
+ super.render(ctx, left, top, styleOverride, fabricObject);
13417
+ }
13418
+ }
13419
+ const createControl = (commandIndexPos, pointIndexPos, isControlPoint, options, connectToCommandIndex, connectToPointIndex) => new (isControlPoint ? PathControlPointControl : PathPointControl)({
13420
+ commandIndex: commandIndexPos,
13421
+ pointIndex: pointIndexPos,
13422
+ actionName: ACTION_NAME,
13423
+ positionHandler: pathPositionHandler,
13424
+ actionHandler: pathActionHandler,
13425
+ connectToCommandIndex,
13426
+ connectToPointIndex,
13427
+ ...options,
13428
+ ...(isControlPoint ? options.controlPointStyle : options.pointStyle)
13429
+ });
13430
+ function createPathControls(path) {
13431
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13432
+ const controls = {};
13433
+ let previousCommandType = 'M';
13434
+ path.path.forEach((command, commandIndex) => {
13435
+ const commandType = command[0];
13436
+ if (commandType !== 'Z') {
13437
+ controls[`c_${commandIndex}_${commandType}`] = createControl(commandIndex, command.length - 2, false, options);
13438
+ }
13439
+ switch (commandType) {
13440
+ case 'C':
13441
+ controls[`c_${commandIndex}_C_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex - 1, indexFromPrevCommand(previousCommandType));
13442
+ controls[`c_${commandIndex}_C_CP_2`] = createControl(commandIndex, 3, true, options, commandIndex, 5);
13443
+ break;
13444
+ case 'Q':
13445
+ controls[`c_${commandIndex}_Q_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex, 3);
13446
+ break;
13447
+ }
13448
+ previousCommandType = commandType;
13449
+ });
13450
+ return controls;
13451
+ }
13452
+
13453
+ var index = /*#__PURE__*/Object.freeze({
13454
+ __proto__: null,
13455
+ changeHeight: changeHeight,
13456
+ changeObjectHeight: changeObjectHeight,
13457
+ changeObjectWidth: changeObjectWidth,
13458
+ changeWidth: changeWidth,
13459
+ createObjectDefaultControls: createObjectDefaultControls,
13460
+ createPathControls: createPathControls,
13461
+ createPolyActionHandler: createPolyActionHandler,
13462
+ createPolyControls: createPolyControls,
13463
+ createPolyPositionHandler: createPolyPositionHandler,
13464
+ createResizeControls: createResizeControls,
13465
+ createTextboxDefaultControls: createTextboxDefaultControls,
13466
+ dragHandler: dragHandler,
13467
+ factoryPolyActionHandler: factoryPolyActionHandler,
13468
+ getLocalPoint: getLocalPoint,
13469
+ polyActionHandler: polyActionHandler,
13470
+ renderCircleControl: renderCircleControl,
13471
+ renderSquareControl: renderSquareControl,
13472
+ rotationStyleHandler: rotationStyleHandler,
13473
+ rotationWithSnapping: rotationWithSnapping,
13474
+ scaleCursorStyleHandler: scaleCursorStyleHandler,
13475
+ scaleOrSkewActionName: scaleOrSkewActionName,
13476
+ scaleSkewCursorStyleHandler: scaleSkewCursorStyleHandler,
13477
+ scalingEqually: scalingEqually,
13478
+ scalingX: scalingX,
13479
+ scalingXOrSkewingY: scalingXOrSkewingY,
13480
+ scalingY: scalingY,
13481
+ scalingYOrSkewingX: scalingYOrSkewingX,
13482
+ skewCursorStyleHandler: skewCursorStyleHandler,
13483
+ skewHandlerX: skewHandlerX,
13484
+ skewHandlerY: skewHandlerY,
13485
+ wrapWithFireEvent: wrapWithFireEvent,
13486
+ wrapWithFixedAnchor: wrapWithFixedAnchor
13487
+ });
13488
+
13174
13489
  /**
13175
13490
  * Canvas class
13176
13491
  * @class Canvas
@@ -13541,11 +13856,13 @@ class SelectableCanvas extends StaticCanvas {
13541
13856
  * Given the control clicked, determine the origin of the transform.
13542
13857
  * This is bad because controls can totally have custom names
13543
13858
  * should disappear before release 4.0
13859
+ * Fabric 7.1, jan 2026 we are still using this.
13860
+ * Needs to go.
13544
13861
  * @private
13545
13862
  * @deprecated
13546
13863
  */
13547
13864
  _getOriginFromCorner(target, controlName) {
13548
- const origin = {
13865
+ const origin = controlName ? target.controls[controlName].getTransformAnchorPoint() : {
13549
13866
  x: target.originX,
13550
13867
  y: target.originY
13551
13868
  };
@@ -13553,6 +13870,9 @@ class SelectableCanvas extends StaticCanvas {
13553
13870
  return origin;
13554
13871
  }
13555
13872
 
13873
+ // this part down here is deprecated.
13874
+ // It is left to do not change the standard behavior in the middle of a major version
13875
+ // but when possible `getTransformAnchorPoint` will be the only source of truth
13556
13876
  // is a left control ?
13557
13877
  if (['ml', 'tl', 'bl'].includes(controlName)) {
13558
13878
  origin.x = RIGHT;
@@ -13592,37 +13912,52 @@ class SelectableCanvas extends StaticCanvas {
13592
13912
  x: CENTER,
13593
13913
  y: CENTER
13594
13914
  } : this._getOriginFromCorner(target, corner),
13915
+ {
13916
+ scaleX,
13917
+ scaleY,
13918
+ skewX,
13919
+ skewY,
13920
+ left,
13921
+ top,
13922
+ angle,
13923
+ width,
13924
+ height,
13925
+ cropX,
13926
+ cropY
13927
+ } = target,
13595
13928
  /**
13596
13929
  * relative to target's containing coordinate plane
13597
13930
  * both agree on every point
13598
13931
  **/
13599
13932
  transform = {
13600
- target: target,
13933
+ target,
13601
13934
  action,
13602
13935
  actionHandler,
13603
13936
  actionPerformed: false,
13604
13937
  corner,
13605
- scaleX: target.scaleX,
13606
- scaleY: target.scaleY,
13607
- skewX: target.skewX,
13608
- skewY: target.skewY,
13609
- offsetX: pointer.x - target.left,
13610
- offsetY: pointer.y - target.top,
13938
+ scaleX,
13939
+ scaleY,
13940
+ skewX,
13941
+ skewY,
13942
+ offsetX: pointer.x - left,
13943
+ offsetY: pointer.y - top,
13611
13944
  originX: origin.x,
13612
13945
  originY: origin.y,
13613
13946
  ex: pointer.x,
13614
13947
  ey: pointer.y,
13615
13948
  lastX: pointer.x,
13616
13949
  lastY: pointer.y,
13617
- theta: degreesToRadians(target.angle),
13618
- width: target.width,
13619
- height: target.height,
13950
+ theta: degreesToRadians(angle),
13951
+ width,
13952
+ height,
13620
13953
  shiftKey: e.shiftKey,
13621
13954
  altKey,
13622
13955
  original: {
13623
13956
  ...saveObjectTransform(target),
13624
13957
  originX: origin.x,
13625
- originY: origin.y
13958
+ originY: origin.y,
13959
+ cropX,
13960
+ cropY
13626
13961
  }
13627
13962
  };
13628
13963
  this._currentTransform = transform;
@@ -16035,7 +16370,8 @@ class Gradient {
16035
16370
  }
16036
16371
  transform[4] -= offsetX;
16037
16372
  transform[5] -= offsetY;
16038
- const commonAttributes = [`id="SVGID_${this.id}"`, `gradientUnits="${gradientUnits}"`, `gradientTransform="${preTransform ? preTransform + ' ' : ''}${matrixToSVG(transform)}"`, ''].join(' ');
16373
+ const commonAttributes = [`id="SVGID_${escapeXml(String(this.id))}"`, `gradientUnits="${gradientUnits}"`, `gradientTransform="${preTransform ? preTransform + ' ' : ''}${matrixToSVG(transform)}"`, ''].join(' ');
16374
+ const sanitizeCoord = value => parseFloat(String(value));
16039
16375
  if (this.type === 'linear') {
16040
16376
  const {
16041
16377
  x1,
@@ -16043,7 +16379,11 @@ class Gradient {
16043
16379
  x2,
16044
16380
  y2
16045
16381
  } = this.coords;
16046
- markup.push('<linearGradient ', commonAttributes, ' x1="', x1, '" y1="', y1, '" x2="', x2, '" y2="', y2, '">\n');
16382
+ const sx1 = sanitizeCoord(x1);
16383
+ const sy1 = sanitizeCoord(y1);
16384
+ const sx2 = sanitizeCoord(x2);
16385
+ const sy2 = sanitizeCoord(y2);
16386
+ markup.push('<linearGradient ', commonAttributes, ' x1="', sx1, '" y1="', sy1, '" x2="', sx2, '" y2="', sy2, '">\n');
16047
16387
  } else if (this.type === 'radial') {
16048
16388
  const {
16049
16389
  x1,
@@ -16053,9 +16393,15 @@ class Gradient {
16053
16393
  r1,
16054
16394
  r2
16055
16395
  } = this.coords;
16056
- const needsSwap = r1 > r2;
16396
+ const sx1 = sanitizeCoord(x1);
16397
+ const sy1 = sanitizeCoord(y1);
16398
+ const sx2 = sanitizeCoord(x2);
16399
+ const sy2 = sanitizeCoord(y2);
16400
+ const sr1 = sanitizeCoord(r1);
16401
+ const sr2 = sanitizeCoord(r2);
16402
+ const needsSwap = sr1 > sr2;
16057
16403
  // svg radial gradient has just 1 radius. the biggest.
16058
- 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');
16404
+ 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');
16059
16405
  if (needsSwap) {
16060
16406
  // svg goes from internal to external radius. if radius are inverted, swap color stops.
16061
16407
  colorStops.reverse(); // mutates array
@@ -16063,16 +16409,17 @@ class Gradient {
16063
16409
  colorStop.offset = 1 - colorStop.offset;
16064
16410
  });
16065
16411
  }
16066
- const minRadius = Math.min(r1, r2);
16412
+ const minRadius = Math.min(sr1, sr2);
16067
16413
  if (minRadius > 0) {
16068
16414
  // i have to shift all colorStops and add new one in 0.
16069
- const maxRadius = Math.max(r1, r2),
16415
+ const maxRadius = Math.max(sr1, sr2),
16070
16416
  percentageShift = minRadius / maxRadius;
16071
16417
  colorStops.forEach(colorStop => {
16072
16418
  colorStop.offset += percentageShift * (1 - colorStop.offset);
16073
16419
  });
16074
16420
  }
16075
16421
  }
16422
+ // todo make a malicious script tag injection test with color and also apply a fix with escapeXml
16076
16423
  colorStops.forEach(_ref => {
16077
16424
  let {
16078
16425
  color,
@@ -16388,7 +16735,7 @@ class Pattern {
16388
16735
  patternOffsetY = ifNaN(this.offsetY / height, 0),
16389
16736
  patternWidth = repeat === 'repeat-y' || repeat === 'no-repeat' ? 1 + Math.abs(patternOffsetX || 0) : ifNaN(patternSource.width / width, 0),
16390
16737
  patternHeight = repeat === 'repeat-x' || repeat === 'no-repeat' ? 1 + Math.abs(patternOffsetY || 0) : ifNaN(patternSource.height / height, 0);
16391
- 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');
16738
+ 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');
16392
16739
  }
16393
16740
  /* _TO_SVG_END_ */
16394
16741
 
@@ -16670,8 +17017,7 @@ class Path extends FabricObject {
16670
17017
  * of the instance
16671
17018
  */
16672
17019
  _toSVG() {
16673
- const path = joinPath(this.path, config.NUM_FRACTION_DIGITS);
16674
- return ['<path ', 'COMMON_PARTS', `d="${path}" stroke-linecap="round" />\n`];
17020
+ return ['<path ', 'COMMON_PARTS', `d="${joinPath(this.path, config.NUM_FRACTION_DIGITS)}" stroke-linecap="round" />\n`];
16675
17021
  }
16676
17022
 
16677
17023
  /**
@@ -17222,15 +17568,17 @@ class Circle extends FabricObject {
17222
17568
  * of the instance
17223
17569
  */
17224
17570
  _toSVG() {
17225
- const angle = (this.endAngle - this.startAngle) % 360;
17571
+ const {
17572
+ radius,
17573
+ startAngle,
17574
+ endAngle
17575
+ } = this;
17576
+ const angle = (endAngle - startAngle) % 360;
17226
17577
  if (angle === 0) {
17227
- return ['<circle ', 'COMMON_PARTS', 'cx="0" cy="0" ', 'r="', `${this.radius}`, '" />\n'];
17578
+ return ['<circle ', 'COMMON_PARTS', 'cx="0" cy="0" ', 'r="', `${escapeXml(radius)}`, '" />\n'];
17228
17579
  } else {
17229
- const {
17230
- radius
17231
- } = this;
17232
- const start = degreesToRadians(this.startAngle),
17233
- end = degreesToRadians(this.endAngle),
17580
+ const start = degreesToRadians(startAngle),
17581
+ end = degreesToRadians(endAngle),
17234
17582
  startX = cos(start) * radius,
17235
17583
  startY = sin(start) * radius,
17236
17584
  endX = cos(end) * radius,
@@ -17802,17 +18150,13 @@ class Line extends FabricObject {
17802
18150
  width,
17803
18151
  height
17804
18152
  } = this;
17805
- const xMult = _x1 <= _x2 ? -1 : 1,
17806
- yMult = _y1 <= _y2 ? -1 : 1,
17807
- x1 = xMult * width / 2,
17808
- y1 = yMult * height / 2,
17809
- x2 = xMult * -width / 2,
17810
- y2 = yMult * -height / 2;
18153
+ const xMult = _x1 <= _x2 ? -0.5 : 0.5,
18154
+ yMult = _y1 <= _y2 ? -0.5 : 0.5;
17811
18155
  return {
17812
- x1,
17813
- x2,
17814
- y1,
17815
- y2
18156
+ x1: xMult * width,
18157
+ x2: xMult * -width,
18158
+ y1: yMult * height,
18159
+ y2: yMult * -height
17816
18160
  };
17817
18161
  }
17818
18162
 
@@ -18030,7 +18374,7 @@ class Ellipse extends FabricObject {
18030
18374
  * of the instance
18031
18375
  */
18032
18376
  _toSVG() {
18033
- return ['<ellipse ', 'COMMON_PARTS', `cx="0" cy="0" rx="${this.rx}" ry="${this.ry}" />\n`];
18377
+ return ['<ellipse ', 'COMMON_PARTS', `cx="0" cy="0" rx="${escapeXml(this.rx)}" ry="${escapeXml(this.ry)}" />\n`];
18034
18378
  }
18035
18379
 
18036
18380
  /**
@@ -18348,14 +18692,17 @@ class Polyline extends FabricObject {
18348
18692
  * of the instance
18349
18693
  */
18350
18694
  _toSVG() {
18351
- const points = [],
18352
- diffX = this.pathOffset.x,
18695
+ const diffX = this.pathOffset.x,
18353
18696
  diffY = this.pathOffset.y,
18354
18697
  NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS;
18355
- for (let i = 0, len = this.points.length; i < len; i++) {
18356
- points.push(toFixed(this.points[i].x - diffX, NUM_FRACTION_DIGITS), ',', toFixed(this.points[i].y - diffY, NUM_FRACTION_DIGITS), ' ');
18357
- }
18358
- return [`<${this.constructor.type.toLowerCase()} `, 'COMMON_PARTS', `points="${points.join('')}" />\n`];
18698
+ const points = this.points.map(_ref2 => {
18699
+ let {
18700
+ x,
18701
+ y
18702
+ } = _ref2;
18703
+ return `${toFixed(x - diffX, NUM_FRACTION_DIGITS)},${toFixed(y - diffY, NUM_FRACTION_DIGITS)}`;
18704
+ }).join(' ');
18705
+ return [`<${escapeXml(this.constructor.type).toLowerCase()} `, 'COMMON_PARTS', `points="${points}" />\n`];
18359
18706
  }
18360
18707
 
18361
18708
  /**
@@ -18478,7 +18825,6 @@ class StyledText extends FabricObject {
18478
18825
  };
18479
18826
  for (const p1 in obj) {
18480
18827
  for (const p2 in obj[p1]) {
18481
- // eslint-disable-next-line no-unused-vars
18482
18828
  for (const p3 in obj[p1][p2]) {
18483
18829
  return false;
18484
18830
  }
@@ -18504,9 +18850,7 @@ class StyledText extends FabricObject {
18504
18850
  const obj = typeof lineIndex === 'undefined' ? this.styles : {
18505
18851
  0: this.styles[lineIndex]
18506
18852
  };
18507
- // eslint-disable-next-line
18508
18853
  for (const p1 in obj) {
18509
- // eslint-disable-next-line
18510
18854
  for (const p2 in obj[p1]) {
18511
18855
  if (typeof obj[p1][p2][property] !== 'undefined') {
18512
18856
  return true;
@@ -18782,7 +19126,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18782
19126
  } = _ref;
18783
19127
  const noShadow = true,
18784
19128
  textDecoration = this.getSvgTextDecoration(this);
18785
- 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'];
19129
+ 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'];
18786
19130
  }
18787
19131
 
18788
19132
  /**
@@ -18798,7 +19142,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18798
19142
  lineOffset;
18799
19143
 
18800
19144
  // bounding-box background
18801
- this.backgroundColor && textBgRects.push(...createSVGInlineRect(this.backgroundColor, -this.width / 2, -this.height / 2, this.width, this.height));
19145
+ this.backgroundColor && textBgRects.push(createSVGInlineRect(this.backgroundColor, -this.width / 2, -this.height / 2, this.width, this.height));
18802
19146
 
18803
19147
  // text and text-background
18804
19148
  for (let i = 0, len = this._textLines.length; i < len; i++) {
@@ -18906,7 +19250,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18906
19250
  } = this.__charBounds[i][j];
18907
19251
  currentColor = this.getValueOfPropertyAt(i, j, 'textBackgroundColor');
18908
19252
  if (currentColor !== lastColor) {
18909
- lastColor && textBgRects.push(...createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
19253
+ lastColor && textBgRects.push(createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
18910
19254
  boxStart = left;
18911
19255
  boxWidth = width;
18912
19256
  lastColor = currentColor;
@@ -18914,7 +19258,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18914
19258
  boxWidth += kernedWidth;
18915
19259
  }
18916
19260
  }
18917
- currentColor && textBgRects.push(...createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
19261
+ currentColor && textBgRects.push(createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
18918
19262
  }
18919
19263
 
18920
19264
  /**
@@ -18941,7 +19285,6 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18941
19285
  fontSize,
18942
19286
  fontStyle,
18943
19287
  fontWeight,
18944
- deltaY,
18945
19288
  textDecorationThickness,
18946
19289
  linethrough,
18947
19290
  overline,
@@ -18953,7 +19296,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18953
19296
  linethrough: linethrough !== null && linethrough !== void 0 ? linethrough : this.linethrough
18954
19297
  });
18955
19298
  const thickness = textDecorationThickness || this.textDecorationThickness;
18956
- 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('');
19299
+ 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('');
18957
19300
  }
18958
19301
 
18959
19302
  /**
@@ -24517,13 +24860,13 @@ class FabricImage extends FabricObject {
24517
24860
  }
24518
24861
  if (this.hasCrop()) {
24519
24862
  const clipPathId = uid();
24520
- svgString.push('<clipPath id="imageCrop_' + clipPathId + '">\n', '\t<rect x="' + x + '" y="' + y + '" width="' + this.width + '" height="' + this.height + '" />\n', '</clipPath>\n');
24863
+ svgString.push('<clipPath id="imageCrop_' + clipPathId + '">\n', '\t<rect x="' + x + '" y="' + y + '" width="' + escapeXml(this.width) + '" height="' + escapeXml(this.height) + '" />\n', '</clipPath>\n');
24521
24864
  clipPath = ' clip-path="url(#imageCrop_' + clipPathId + ')" ';
24522
24865
  }
24523
24866
  if (!this.imageSmoothing) {
24524
24867
  imageRendering = ' image-rendering="optimizeSpeed"';
24525
24868
  }
24526
- imageMarkup.push('\t<image ', 'COMMON_PARTS', `xlink:href="${this.getSvgSrc(true)}" x="${x - this.cropX}" y="${y - this.cropY
24869
+ imageMarkup.push('\t<image ', 'COMMON_PARTS', `xlink:href="${escapeXml(this.getSrc(true))}" x="${x - this.cropX}" y="${y - this.cropY
24527
24870
  // we're essentially moving origin of transformation from top/left corner to the center of the shape
24528
24871
  // by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
24529
24872
  // so that object's center aligns with container's left/top
@@ -24531,7 +24874,7 @@ class FabricImage extends FabricObject {
24531
24874
  if (this.stroke || this.strokeDashArray) {
24532
24875
  const origFill = this.fill;
24533
24876
  this.fill = null;
24534
- strokeSvg = [`\t<rect x="${x}" y="${y}" width="${this.width}" height="${this.height}" style="${this.getSvgStyles()}" />\n`];
24877
+ strokeSvg = [`\t<rect x="${x}" y="${y}" width="${escapeXml(this.width)}" height="${escapeXml(this.height)}" style="${this.getSvgStyles()}" />\n`];
24535
24878
  this.fill = origFill;
24536
24879
  }
24537
24880
  if (this.paintFirst !== FILL) {
@@ -25514,258 +25857,6 @@ function loadSVGFromURL(url, reviver) {
25514
25857
  });
25515
25858
  }
25516
25859
 
25517
- const ACTION_NAME$1 = MODIFY_POLY;
25518
- /**
25519
- * This function locates the controls.
25520
- * It'll be used both for drawing and for interaction.
25521
- */
25522
- const createPolyPositionHandler = pointIndex => {
25523
- return function (dim, finalMatrix, polyObject) {
25524
- const {
25525
- points,
25526
- pathOffset
25527
- } = polyObject;
25528
- return new Point(points[pointIndex]).subtract(pathOffset).transform(multiplyTransformMatrices(polyObject.getViewportTransform(), polyObject.calcTransformMatrix()));
25529
- };
25530
- };
25531
-
25532
- /**
25533
- * This function defines what the control does.
25534
- * It'll be called on every mouse move after a control has been clicked and is being dragged.
25535
- * The function receives as argument the mouse event, the current transform object
25536
- * and the current position in canvas coordinate `transform.target` is a reference to the
25537
- * current object being transformed.
25538
- */
25539
- const polyActionHandler = (eventData, transform, x, y) => {
25540
- const {
25541
- target,
25542
- pointIndex
25543
- } = transform;
25544
- const poly = target;
25545
- const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, poly.calcOwnMatrix());
25546
- poly.points[pointIndex] = mouseLocalPosition.add(poly.pathOffset);
25547
- poly.setDimensions();
25548
- poly.set('dirty', true);
25549
- return true;
25550
- };
25551
-
25552
- /**
25553
- * Keep the polygon in the same position when we change its `width`/`height`/`top`/`left`.
25554
- */
25555
- const factoryPolyActionHandler = (pointIndex, fn) => {
25556
- return function (eventData, transform, x, y) {
25557
- const poly = transform.target,
25558
- anchorPoint = new Point(poly.points[(pointIndex > 0 ? pointIndex : poly.points.length) - 1]),
25559
- anchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix()),
25560
- actionPerformed = fn(eventData, {
25561
- ...transform,
25562
- pointIndex
25563
- }, x, y);
25564
- const newAnchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix());
25565
- const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
25566
- poly.left -= diff.x;
25567
- poly.top -= diff.y;
25568
- return actionPerformed;
25569
- };
25570
- };
25571
- const createPolyActionHandler = pointIndex => wrapWithFireEvent(ACTION_NAME$1, factoryPolyActionHandler(pointIndex, polyActionHandler));
25572
- function createPolyControls(arg0) {
25573
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
25574
- const controls = {};
25575
- for (let idx = 0; idx < (typeof arg0 === 'number' ? arg0 : arg0.points.length); idx++) {
25576
- controls[`p${idx}`] = new Control({
25577
- actionName: ACTION_NAME$1,
25578
- positionHandler: createPolyPositionHandler(idx),
25579
- actionHandler: createPolyActionHandler(idx),
25580
- ...options
25581
- });
25582
- }
25583
- return controls;
25584
- }
25585
-
25586
- const ACTION_NAME = 'modifyPath';
25587
- const calcPathPointPosition = (pathObject, commandIndex, pointIndex) => {
25588
- const {
25589
- path,
25590
- pathOffset
25591
- } = pathObject;
25592
- const command = path[commandIndex];
25593
- return new Point(command[pointIndex] - pathOffset.x, command[pointIndex + 1] - pathOffset.y).transform(multiplyTransformMatrices(pathObject.getViewportTransform(), pathObject.calcTransformMatrix()));
25594
- };
25595
- const movePathPoint = (pathObject, x, y, commandIndex, pointIndex) => {
25596
- const {
25597
- path,
25598
- pathOffset
25599
- } = pathObject;
25600
- const anchorCommand = path[(commandIndex > 0 ? commandIndex : path.length) - 1];
25601
- const anchorPoint = new Point(anchorCommand[pointIndex], anchorCommand[pointIndex + 1]);
25602
- const anchorPointInParentPlane = anchorPoint.subtract(pathOffset).transform(pathObject.calcOwnMatrix());
25603
- const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, pathObject.calcOwnMatrix());
25604
- path[commandIndex][pointIndex] = mouseLocalPosition.x + pathOffset.x;
25605
- path[commandIndex][pointIndex + 1] = mouseLocalPosition.y + pathOffset.y;
25606
- pathObject.setDimensions();
25607
- const newAnchorPointInParentPlane = anchorPoint.subtract(pathObject.pathOffset).transform(pathObject.calcOwnMatrix());
25608
- const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
25609
- pathObject.left -= diff.x;
25610
- pathObject.top -= diff.y;
25611
- pathObject.set('dirty', true);
25612
- return true;
25613
- };
25614
-
25615
- /**
25616
- * This function locates the controls.
25617
- * It'll be used both for drawing and for interaction.
25618
- */
25619
- function pathPositionHandler(dim, finalMatrix, pathObject) {
25620
- const {
25621
- commandIndex,
25622
- pointIndex
25623
- } = this;
25624
- return calcPathPointPosition(pathObject, commandIndex, pointIndex);
25625
- }
25626
-
25627
- /**
25628
- * This function defines what the control does.
25629
- * It'll be called on every mouse move after a control has been clicked and is being dragged.
25630
- * The function receives as argument the mouse event, the current transform object
25631
- * and the current position in canvas coordinate `transform.target` is a reference to the
25632
- * current object being transformed.
25633
- */
25634
- function pathActionHandler(eventData, transform, x, y) {
25635
- const {
25636
- target
25637
- } = transform;
25638
- const {
25639
- commandIndex,
25640
- pointIndex
25641
- } = this;
25642
- const actionPerformed = movePathPoint(target, x, y, commandIndex, pointIndex);
25643
- {
25644
- fireEvent(this.actionName, {
25645
- ...commonEventInfo(eventData, transform, x, y),
25646
- commandIndex,
25647
- pointIndex
25648
- });
25649
- }
25650
- return actionPerformed;
25651
- }
25652
- const indexFromPrevCommand = previousCommandType => previousCommandType === 'C' ? 5 : previousCommandType === 'Q' ? 3 : 1;
25653
- class PathPointControl extends Control {
25654
- constructor(options) {
25655
- super(options);
25656
- }
25657
- render(ctx, left, top, styleOverride, fabricObject) {
25658
- const overrides = {
25659
- ...styleOverride,
25660
- cornerColor: this.controlFill,
25661
- cornerStrokeColor: this.controlStroke,
25662
- transparentCorners: !this.controlFill
25663
- };
25664
- super.render(ctx, left, top, overrides, fabricObject);
25665
- }
25666
- }
25667
- class PathControlPointControl extends PathPointControl {
25668
- constructor(options) {
25669
- super(options);
25670
- }
25671
- render(ctx, left, top, styleOverride, fabricObject) {
25672
- const {
25673
- path
25674
- } = fabricObject;
25675
- const {
25676
- commandIndex,
25677
- pointIndex,
25678
- connectToCommandIndex,
25679
- connectToPointIndex
25680
- } = this;
25681
- ctx.save();
25682
- ctx.strokeStyle = this.controlStroke;
25683
- if (this.connectionDashArray) {
25684
- ctx.setLineDash(this.connectionDashArray);
25685
- }
25686
- const [commandType] = path[commandIndex];
25687
- const point = calcPathPointPosition(fabricObject, connectToCommandIndex, connectToPointIndex);
25688
- if (commandType === 'Q') {
25689
- // one control point connects to 2 points
25690
- const point2 = calcPathPointPosition(fabricObject, commandIndex, pointIndex + 2);
25691
- ctx.moveTo(point2.x, point2.y);
25692
- ctx.lineTo(left, top);
25693
- } else {
25694
- ctx.moveTo(left, top);
25695
- }
25696
- ctx.lineTo(point.x, point.y);
25697
- ctx.stroke();
25698
- ctx.restore();
25699
- super.render(ctx, left, top, styleOverride, fabricObject);
25700
- }
25701
- }
25702
- const createControl = (commandIndexPos, pointIndexPos, isControlPoint, options, connectToCommandIndex, connectToPointIndex) => new (isControlPoint ? PathControlPointControl : PathPointControl)({
25703
- commandIndex: commandIndexPos,
25704
- pointIndex: pointIndexPos,
25705
- actionName: ACTION_NAME,
25706
- positionHandler: pathPositionHandler,
25707
- actionHandler: pathActionHandler,
25708
- connectToCommandIndex,
25709
- connectToPointIndex,
25710
- ...options,
25711
- ...(isControlPoint ? options.controlPointStyle : options.pointStyle)
25712
- });
25713
- function createPathControls(path) {
25714
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
25715
- const controls = {};
25716
- let previousCommandType = 'M';
25717
- path.path.forEach((command, commandIndex) => {
25718
- const commandType = command[0];
25719
- if (commandType !== 'Z') {
25720
- controls[`c_${commandIndex}_${commandType}`] = createControl(commandIndex, command.length - 2, false, options);
25721
- }
25722
- switch (commandType) {
25723
- case 'C':
25724
- controls[`c_${commandIndex}_C_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex - 1, indexFromPrevCommand(previousCommandType));
25725
- controls[`c_${commandIndex}_C_CP_2`] = createControl(commandIndex, 3, true, options, commandIndex, 5);
25726
- break;
25727
- case 'Q':
25728
- controls[`c_${commandIndex}_Q_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex, 3);
25729
- break;
25730
- }
25731
- previousCommandType = commandType;
25732
- });
25733
- return controls;
25734
- }
25735
-
25736
- var index = /*#__PURE__*/Object.freeze({
25737
- __proto__: null,
25738
- changeWidth: changeWidth,
25739
- createObjectDefaultControls: createObjectDefaultControls,
25740
- createPathControls: createPathControls,
25741
- createPolyActionHandler: createPolyActionHandler,
25742
- createPolyControls: createPolyControls,
25743
- createPolyPositionHandler: createPolyPositionHandler,
25744
- createResizeControls: createResizeControls,
25745
- createTextboxDefaultControls: createTextboxDefaultControls,
25746
- dragHandler: dragHandler,
25747
- factoryPolyActionHandler: factoryPolyActionHandler,
25748
- getLocalPoint: getLocalPoint,
25749
- polyActionHandler: polyActionHandler,
25750
- renderCircleControl: renderCircleControl,
25751
- renderSquareControl: renderSquareControl,
25752
- rotationStyleHandler: rotationStyleHandler,
25753
- rotationWithSnapping: rotationWithSnapping,
25754
- scaleCursorStyleHandler: scaleCursorStyleHandler,
25755
- scaleOrSkewActionName: scaleOrSkewActionName,
25756
- scaleSkewCursorStyleHandler: scaleSkewCursorStyleHandler,
25757
- scalingEqually: scalingEqually,
25758
- scalingX: scalingX,
25759
- scalingXOrSkewingY: scalingXOrSkewingY,
25760
- scalingY: scalingY,
25761
- scalingYOrSkewingX: scalingYOrSkewingX,
25762
- skewCursorStyleHandler: skewCursorStyleHandler,
25763
- skewHandlerX: skewHandlerX,
25764
- skewHandlerY: skewHandlerY,
25765
- wrapWithFireEvent: wrapWithFireEvent,
25766
- wrapWithFixedAnchor: wrapWithFixedAnchor
25767
- });
25768
-
25769
25860
  const isWebGLPipelineState = options => {
25770
25861
  return options.webgl !== undefined;
25771
25862
  };