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
@@ -412,7 +412,7 @@ class Cache {
412
412
  }
413
413
  const cache = new Cache();
414
414
 
415
- var version = "7.0.0";
415
+ var version = "7.2.0";
416
416
 
417
417
  // use this syntax so babel plugin see this import here
418
418
  const VERSION = version;
@@ -2247,6 +2247,111 @@ const staticCanvasDefaults = {
2247
2247
  patternQuality: 'best'
2248
2248
  };
2249
2249
 
2250
+ /**
2251
+ * Capitalizes a string
2252
+ * @param {String} string String to capitalize
2253
+ * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
2254
+ * and other letters stay untouched, if false first letter is capitalized
2255
+ * and other letters are converted to lowercase.
2256
+ * @return {String} Capitalized version of a string
2257
+ */
2258
+ const capitalize = function (string) {
2259
+ let firstLetterOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2260
+ return `${string.charAt(0).toUpperCase()}${firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase()}`;
2261
+ };
2262
+
2263
+ /**
2264
+ * Escapes XML in a string
2265
+ * @param {String} string String to escape
2266
+ * @return {String} Escaped version of a string
2267
+ */
2268
+ const escapeXml = stringOrNumber => stringOrNumber.toString().replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
2269
+ let segmenter;
2270
+ const getSegmenter = () => {
2271
+ if (!segmenter) {
2272
+ segmenter = 'Intl' in getFabricWindow() && 'Segmenter' in Intl && new Intl.Segmenter(undefined, {
2273
+ granularity: 'grapheme'
2274
+ });
2275
+ }
2276
+ return segmenter;
2277
+ };
2278
+
2279
+ /**
2280
+ * Divide a string in the user perceived single units
2281
+ * @param {String} textstring String to escape
2282
+ * @return {Array} array containing the graphemes
2283
+ */
2284
+ const graphemeSplit = textstring => {
2285
+ segmenter || getSegmenter();
2286
+ if (segmenter) {
2287
+ const segments = segmenter.segment(textstring);
2288
+ return Array.from(segments).map(_ref => {
2289
+ let {
2290
+ segment
2291
+ } = _ref;
2292
+ return segment;
2293
+ });
2294
+ }
2295
+
2296
+ //Fallback
2297
+ return graphemeSplitImpl(textstring);
2298
+ };
2299
+ const graphemeSplitImpl = textstring => {
2300
+ const graphemes = [];
2301
+ for (let i = 0, chr; i < textstring.length; i++) {
2302
+ if ((chr = getWholeChar(textstring, i)) === false) {
2303
+ continue;
2304
+ }
2305
+ graphemes.push(chr);
2306
+ }
2307
+ return graphemes;
2308
+ };
2309
+
2310
+ // taken from mdn in the charAt doc page.
2311
+ const getWholeChar = (str, i) => {
2312
+ const code = str.charCodeAt(i);
2313
+ if (isNaN(code)) {
2314
+ return ''; // Position not found
2315
+ }
2316
+ if (code < 0xd800 || code > 0xdfff) {
2317
+ return str.charAt(i);
2318
+ }
2319
+
2320
+ // High surrogate (could change last hex to 0xDB7F to treat high private
2321
+ // surrogates as single characters)
2322
+ if (0xd800 <= code && code <= 0xdbff) {
2323
+ if (str.length <= i + 1) {
2324
+ throw 'High surrogate without following low surrogate';
2325
+ }
2326
+ const next = str.charCodeAt(i + 1);
2327
+ if (0xdc00 > next || next > 0xdfff) {
2328
+ throw 'High surrogate without following low surrogate';
2329
+ }
2330
+ return str.charAt(i) + str.charAt(i + 1);
2331
+ }
2332
+ // Low surrogate (0xDC00 <= code && code <= 0xDFFF)
2333
+ if (i === 0) {
2334
+ throw 'Low surrogate without preceding high surrogate';
2335
+ }
2336
+ const prev = str.charCodeAt(i - 1);
2337
+
2338
+ // (could change last hex to 0xDB7F to treat high private
2339
+ // surrogates as single characters)
2340
+ if (0xd800 > prev || prev > 0xdbff) {
2341
+ throw 'Low surrogate without preceding high surrogate';
2342
+ }
2343
+ // We can pass over low surrogates now as the second component
2344
+ // in a pair which we have already processed
2345
+ return false;
2346
+ };
2347
+
2348
+ var lang_string = /*#__PURE__*/Object.freeze({
2349
+ __proto__: null,
2350
+ capitalize: capitalize,
2351
+ escapeXml: escapeXml,
2352
+ graphemeSplit: graphemeSplit
2353
+ });
2354
+
2250
2355
  /**
2251
2356
  * Having both options in TCanvasSizeOptions set to true transform the call in a calcOffset
2252
2357
  * Better try to restrict with types to avoid confusion.
@@ -3016,7 +3121,8 @@ let StaticCanvas$1 = class StaticCanvas extends createCollectionMixin(CommonMeth
3016
3121
  this._setSVGPreamble(markup, options);
3017
3122
  this._setSVGHeader(markup, options);
3018
3123
  if (this.clipPath) {
3019
- markup.push(`<g clip-path="url(#${this.clipPath.clipPathId})" >\n`);
3124
+ var _this$clipPath$clipPa;
3125
+ markup.push(`<g clip-path="url(#${escapeXml((_this$clipPath$clipPa = this.clipPath.clipPathId) !== null && _this$clipPath$clipPa !== void 0 ? _this$clipPath$clipPa : '')})" >\n`);
3020
3126
  }
3021
3127
  this._setSVGBgOverlayColor(markup, 'background');
3022
3128
  this._setSVGBgOverlayImage(markup, 'backgroundImage', reviver);
@@ -3695,20 +3801,6 @@ const sendObjectToPlane = (object, from, to) => {
3695
3801
  return t;
3696
3802
  };
3697
3803
 
3698
- const fireEvent = (eventName, options) => {
3699
- var _target$canvas;
3700
- const {
3701
- transform: {
3702
- target
3703
- }
3704
- } = options;
3705
- (_target$canvas = target.canvas) === null || _target$canvas === void 0 || _target$canvas.fire(`object:${eventName}`, {
3706
- ...options,
3707
- target
3708
- });
3709
- target.fire(eventName, options);
3710
- };
3711
-
3712
3804
  const originOffset = {
3713
3805
  left: -0.5,
3714
3806
  top: -0.5,
@@ -3911,33 +4003,6 @@ function getLocalPoint(_ref, originX, originY, x, y) {
3911
4003
  return localPoint;
3912
4004
  }
3913
4005
 
3914
- /**
3915
- * Action handler
3916
- * @private
3917
- * @param {Event} eventData javascript event that is doing the transform
3918
- * @param {Object} transform javascript object containing a series of information around the current transform
3919
- * @param {number} x current mouse x position, canvas normalized
3920
- * @param {number} y current mouse y position, canvas normalized
3921
- * @return {Boolean} true if the translation occurred
3922
- */
3923
- const dragHandler = (eventData, transform, x, y) => {
3924
- const {
3925
- target,
3926
- offsetX,
3927
- offsetY
3928
- } = transform,
3929
- newLeft = x - offsetX,
3930
- newTop = y - offsetY,
3931
- moveX = !isLocked(target, 'lockMovementX') && target.left !== newLeft,
3932
- moveY = !isLocked(target, 'lockMovementY') && target.top !== newTop;
3933
- moveX && target.set(LEFT, newLeft);
3934
- moveY && target.set(TOP, newTop);
3935
- if (moveX || moveY) {
3936
- fireEvent(MOVING, commonEventInfo(eventData, transform, x, y));
3937
- }
3938
- return moveX || moveY;
3939
- };
3940
-
3941
4006
  const normalizeWs = value => value.replace(/\s+/g, ' ');
3942
4007
 
3943
4008
  /**
@@ -4684,7 +4749,7 @@ const colorPropToSVG = function (prop, value) {
4684
4749
  if (!value) {
4685
4750
  colorValue = 'none';
4686
4751
  } else if (value.toLive) {
4687
- colorValue = `url(#SVGID_${value.id})`;
4752
+ colorValue = `url(#SVGID_${escapeXml(value.id)})`;
4688
4753
  } else {
4689
4754
  const color = new Color(value),
4690
4755
  opacity = color.getAlpha();
@@ -4737,7 +4802,7 @@ class FabricObjectSVGExportMixin {
4737
4802
  filter = skipShadow ? '' : this.getSvgFilter(),
4738
4803
  fill = colorPropToSVG(FILL, this.fill),
4739
4804
  stroke = colorPropToSVG(STROKE, this.stroke);
4740
- 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('');
4805
+ 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('');
4741
4806
  }
4742
4807
 
4743
4808
  /**
@@ -4745,7 +4810,7 @@ class FabricObjectSVGExportMixin {
4745
4810
  * @return {String}
4746
4811
  */
4747
4812
  getSvgFilter() {
4748
- return this.shadow ? `filter: url(#SVGID_${this.shadow.id});` : '';
4813
+ return this.shadow ? `filter: url(#SVGID_${escapeXml(this.shadow.id)});` : '';
4749
4814
  }
4750
4815
 
4751
4816
  /**
@@ -4753,7 +4818,7 @@ class FabricObjectSVGExportMixin {
4753
4818
  * @return {String}
4754
4819
  */
4755
4820
  getSvgCommons() {
4756
- return [this.id ? `id="${this.id}" ` : '', this.clipPath ? `clip-path="url(#${this.clipPath.clipPathId})" ` : ''].join('');
4821
+ return [this.id ? `id="${escapeXml(String(this.id))}" ` : '', this.clipPath ? `clip-path="url(#${this.clipPath.clipPathId})" ` : ''].join('');
4757
4822
  }
4758
4823
 
4759
4824
  /**
@@ -4866,7 +4931,7 @@ class FabricObjectSVGExportMixin {
4866
4931
  return reviver ? reviver(markup.join('')) : markup.join('');
4867
4932
  }
4868
4933
  addPaintOrder() {
4869
- return this.paintFirst !== FILL ? ` paint-order="${this.paintFirst}" ` : '';
4934
+ return this.paintFirst !== FILL ? ` paint-order="${escapeXml(this.paintFirst)}" ` : '';
4870
4935
  }
4871
4936
  }
4872
4937
 
@@ -5004,7 +5069,6 @@ const reViewBoxAttrValue = new RegExp(String.raw`^\s*(${reNum})${viewportSeparat
5004
5069
 
5005
5070
  (?:$|\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.
5006
5071
  */
5007
- // eslint-disable-next-line max-len
5008
5072
 
5009
5073
  const shadowOffsetRegex = '(-?\\d+(?:\\.\\d*)?(?:px)?(?:\\s?|$))?';
5010
5074
  const reOffsetsAndBlur = new RegExp('(?:\\s|^)' + shadowOffsetRegex + shadowOffsetRegex + '(' + reNum + '?(?:px)?)?(?:\\s?|$)(?:$|\\s)');
@@ -5063,14 +5127,15 @@ class Shadow {
5063
5127
  toSVG(object) {
5064
5128
  const offset = rotateVector(new Point(this.offsetX, this.offsetY), degreesToRadians(-object.angle)),
5065
5129
  BLUR_BOX = 20,
5130
+ NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS,
5066
5131
  color = new Color(this.color);
5067
5132
  let fBoxX = 40,
5068
5133
  fBoxY = 40;
5069
5134
  if (object.width && object.height) {
5070
5135
  //http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
5071
5136
  // we add some extra space to filter box to contain the blur ( 20 )
5072
- fBoxX = toFixed((Math.abs(offset.x) + this.blur) / object.width, config.NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5073
- fBoxY = toFixed((Math.abs(offset.y) + this.blur) / object.height, config.NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5137
+ fBoxX = toFixed((Math.abs(offset.x) + this.blur) / object.width, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5138
+ fBoxY = toFixed((Math.abs(offset.y) + this.blur) / object.height, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
5074
5139
  }
5075
5140
  if (object.flipX) {
5076
5141
  offset.x *= -1;
@@ -5078,7 +5143,7 @@ class Shadow {
5078
5143
  if (object.flipY) {
5079
5144
  offset.y *= -1;
5080
5145
  }
5081
- 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`;
5146
+ 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`;
5082
5147
  }
5083
5148
 
5084
5149
  /**
@@ -6488,7 +6553,7 @@ class ObjectGeometry extends CommonMethods {
6488
6553
  calcOwnMatrix() {
6489
6554
  const key = this.transformMatrixKey(true),
6490
6555
  cache = this.ownMatrixCache;
6491
- if (cache && cache.key === key) {
6556
+ if (cache && cache.key.every((x, i) => x === key[i])) {
6492
6557
  return cache.value;
6493
6558
  }
6494
6559
  const center = this.getRelativeCenterPoint(),
@@ -7229,6 +7294,9 @@ let FabricObject$1 = class FabricObject extends ObjectGeometry {
7229
7294
  } else {
7230
7295
  this._renderBackground(ctx);
7231
7296
  }
7297
+ this.fire('before:render', {
7298
+ ctx
7299
+ });
7232
7300
  this._render(ctx);
7233
7301
  this._drawClipPath(ctx, this.clipPath, context);
7234
7302
  this.fill = originalFill;
@@ -8229,6 +8297,20 @@ _defineProperty(FabricObject$1, "customProperties", []);
8229
8297
  classRegistry.setClass(FabricObject$1);
8230
8298
  classRegistry.setClass(FabricObject$1, 'object');
8231
8299
 
8300
+ const fireEvent = (eventName, options) => {
8301
+ var _target$canvas;
8302
+ const {
8303
+ transform: {
8304
+ target
8305
+ }
8306
+ } = options;
8307
+ (_target$canvas = target.canvas) === null || _target$canvas === void 0 || _target$canvas.fire(`object:${eventName}`, {
8308
+ ...options,
8309
+ target
8310
+ });
8311
+ target.fire(eventName, options);
8312
+ };
8313
+
8232
8314
  /**
8233
8315
  * Wrap an action handler with firing an event if the action is performed
8234
8316
  * @param {TModificationEvents} eventName the event we want to fire
@@ -8271,34 +8353,62 @@ function wrapWithFixedAnchor(actionHandler) {
8271
8353
  };
8272
8354
  }
8273
8355
 
8274
- /**
8275
- * Action handler to change object's width
8276
- * Needs to be wrapped with `wrapWithFixedAnchor` to be effective
8277
- * @param {Event} eventData javascript event that is doing the transform
8278
- * @param {Object} transform javascript object containing a series of information around the current transform
8279
- * @param {number} x current mouse x position, canvas normalized
8280
- * @param {number} y current mouse y position, canvas normalized
8281
- * @return {Boolean} true if some change happened
8282
- */
8283
- const changeObjectWidth = (eventData, transform, x, y) => {
8356
+ const changeObjectDimensionGen = (dimension, origin, xorY, scale) => (eventData, transform, x, y) => {
8284
8357
  const localPoint = getLocalPoint(transform, transform.originX, transform.originY, x, y);
8358
+ const localPointValue = localPoint[xorY];
8285
8359
  // make sure the control changes width ONLY from it's side of target
8286
- if (resolveOrigin(transform.originX) === resolveOrigin(CENTER) || resolveOrigin(transform.originX) === resolveOrigin(RIGHT) && localPoint.x < 0 || resolveOrigin(transform.originX) === resolveOrigin(LEFT) && localPoint.x > 0) {
8360
+ const originValue = resolveOrigin(transform[origin]);
8361
+ if (originValue === 0 || originValue > 0 && localPointValue < 0 || originValue < 0 && localPointValue > 0) {
8287
8362
  const {
8288
8363
  target
8289
8364
  } = transform,
8290
- strokePadding = target.strokeWidth / (target.strokeUniform ? target.scaleX : 1),
8365
+ strokePadding = target.strokeWidth / (target.strokeUniform ? target[scale] : 1),
8291
8366
  multiplier = isTransformCentered(transform) ? 2 : 1,
8292
- oldWidth = target.width,
8293
- newWidth = Math.abs(localPoint.x * multiplier / target.scaleX) - strokePadding;
8294
- target.set('width', Math.max(newWidth, 1));
8367
+ oldWidth = target[dimension],
8368
+ newWidth = Math.abs(localPointValue * multiplier / target[scale]) - strokePadding;
8369
+ target.set(dimension, Math.max(newWidth, 1));
8295
8370
  // check against actual target width in case `newWidth` was rejected
8296
- return oldWidth !== target.width;
8371
+ return oldWidth !== target[dimension];
8297
8372
  }
8298
8373
  return false;
8299
8374
  };
8375
+
8376
+ /**
8377
+ * Action handler to change object's width
8378
+ * Needs to be wrapped with `wrapWithFixedAnchor` to be effective
8379
+ * You want to use this only if you are building a new control handler and you want
8380
+ * to reuse some logic. use "changeWidth" if you are looking to just use a control for width
8381
+ * @param {Event} eventData javascript event that is doing the transform
8382
+ * @param {Object} transform javascript object containing a series of information around the current transform
8383
+ * @param {number} x current mouse x position, canvas normalized
8384
+ * @param {number} y current mouse y position, canvas normalized
8385
+ * @return {Boolean} true if some change happened
8386
+ */
8387
+ const changeObjectWidth = changeObjectDimensionGen('width', 'originX', 'x', 'scaleX');
8388
+
8389
+ /**
8390
+ * Action handler to change object's height
8391
+ * Needs to be wrapped with `wrapWithFixedAnchor` to be effective
8392
+ * You want to use this only if you are building a new control handler and you want
8393
+ * to reuse some logic. use "changeHeight" if you are looking to just use a control for height
8394
+ * @param {Event} eventData javascript event that is doing the transform
8395
+ * @param {Object} transform javascript object containing a series of information around the current transform
8396
+ * @param {number} x current mouse x position, canvas normalized
8397
+ * @param {number} y current mouse y position, canvas normalized
8398
+ * @return {Boolean} true if some change happened
8399
+ */
8400
+ const changeObjectHeight = changeObjectDimensionGen('height', 'originY', 'y', 'scaleY');
8401
+
8402
+ /**
8403
+ * Control handler for changing width
8404
+ */
8300
8405
  const changeWidth = wrapWithFireEvent(RESIZING, wrapWithFixedAnchor(changeObjectWidth));
8301
8406
 
8407
+ /**
8408
+ * Control handler for changing height
8409
+ */
8410
+ const changeHeight = wrapWithFireEvent(RESIZING, wrapWithFixedAnchor(changeObjectHeight));
8411
+
8302
8412
  /**
8303
8413
  * Render a round control, as per fabric features.
8304
8414
  * This function is written to respect object properties like transparentCorners, cornerSize
@@ -8311,33 +8421,24 @@ const changeWidth = wrapWithFireEvent(RESIZING, wrapWithFixedAnchor(changeObject
8311
8421
  * @param {FabricObject} fabricObject the fabric object for which we are rendering controls
8312
8422
  */
8313
8423
  function renderCircleControl(ctx, left, top, styleOverride, fabricObject) {
8314
- styleOverride = styleOverride || {};
8315
- const xSize = this.sizeX || styleOverride.cornerSize || fabricObject.cornerSize,
8316
- ySize = this.sizeY || styleOverride.cornerSize || fabricObject.cornerSize,
8317
- transparentCorners = typeof styleOverride.transparentCorners !== 'undefined' ? styleOverride.transparentCorners : fabricObject.transparentCorners,
8318
- methodName = transparentCorners ? STROKE : FILL,
8319
- stroke = !transparentCorners && (styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor);
8320
- let myLeft = left,
8321
- myTop = top,
8322
- size;
8323
8424
  ctx.save();
8324
- ctx.fillStyle = styleOverride.cornerColor || fabricObject.cornerColor || '';
8325
- ctx.strokeStyle = styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor || '';
8425
+ const {
8426
+ stroke,
8427
+ xSize,
8428
+ ySize,
8429
+ opName
8430
+ } = this.commonRenderProps(ctx, left, top, fabricObject, styleOverride);
8431
+ let size = xSize;
8326
8432
  // TODO: use proper ellipse code.
8327
8433
  if (xSize > ySize) {
8328
- size = xSize;
8329
8434
  ctx.scale(1.0, ySize / xSize);
8330
- myTop = top * xSize / ySize;
8331
8435
  } else if (ySize > xSize) {
8332
8436
  size = ySize;
8333
8437
  ctx.scale(xSize / ySize, 1.0);
8334
- myLeft = left * ySize / xSize;
8335
- } else {
8336
- size = xSize;
8337
8438
  }
8338
8439
  ctx.beginPath();
8339
- ctx.arc(myLeft, myTop, size / 2, 0, twoMathPi, false);
8340
- ctx[methodName]();
8440
+ ctx.arc(0, 0, size / 2, 0, twoMathPi, false);
8441
+ ctx[opName]();
8341
8442
  if (stroke) {
8342
8443
  ctx.stroke();
8343
8444
  }
@@ -8356,25 +8457,19 @@ function renderCircleControl(ctx, left, top, styleOverride, fabricObject) {
8356
8457
  * @param {FabricObject} fabricObject the fabric object for which we are rendering controls
8357
8458
  */
8358
8459
  function renderSquareControl(ctx, left, top, styleOverride, fabricObject) {
8359
- styleOverride = styleOverride || {};
8360
- const xSize = this.sizeX || styleOverride.cornerSize || fabricObject.cornerSize,
8361
- ySize = this.sizeY || styleOverride.cornerSize || fabricObject.cornerSize,
8362
- transparentCorners = typeof styleOverride.transparentCorners !== 'undefined' ? styleOverride.transparentCorners : fabricObject.transparentCorners,
8363
- methodName = transparentCorners ? STROKE : FILL,
8364
- stroke = !transparentCorners && (styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor),
8460
+ ctx.save();
8461
+ const {
8462
+ stroke,
8463
+ xSize,
8464
+ ySize,
8465
+ opName
8466
+ } = this.commonRenderProps(ctx, left, top, fabricObject, styleOverride),
8365
8467
  xSizeBy2 = xSize / 2,
8366
8468
  ySizeBy2 = ySize / 2;
8367
- ctx.save();
8368
- ctx.fillStyle = styleOverride.cornerColor || fabricObject.cornerColor || '';
8369
- ctx.strokeStyle = styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor || '';
8370
- ctx.translate(left, top);
8371
- // angle is relative to canvas plane
8372
- const angle = fabricObject.getTotalAngle();
8373
- ctx.rotate(degreesToRadians(angle));
8374
8469
  // this does not work, and fixed with ( && ) does not make sense.
8375
8470
  // to have real transparent corners we need the controls on upperCanvas
8376
8471
  // transparentCorners || ctx.clearRect(-xSizeBy2, -ySizeBy2, xSize, ySize);
8377
- ctx[`${methodName}Rect`](-xSizeBy2, -ySizeBy2, xSize, ySize);
8472
+ ctx[`${opName}Rect`](-xSizeBy2, -ySizeBy2, xSize, ySize);
8378
8473
  if (stroke) {
8379
8474
  ctx.strokeRect(-xSizeBy2, -ySizeBy2, xSize, ySize);
8380
8475
  }
@@ -8492,6 +8587,14 @@ class Control {
8492
8587
  _defineProperty(this, "withConnection", false);
8493
8588
  Object.assign(this, options);
8494
8589
  }
8590
+ getTransformAnchorPoint() {
8591
+ var _this$transformAnchor;
8592
+ return (// return the control transformAnchorPoint
8593
+ (_this$transformAnchor = this.transformAnchorPoint) !== null && _this$transformAnchor !== void 0 ? _this$transformAnchor :
8594
+ // otherwise will return the opposite origin of where the control is located.
8595
+ new Point(-this.x + 0.5, -this.y + 0.5)
8596
+ );
8597
+ }
8495
8598
 
8496
8599
  /**
8497
8600
  * The control actionHandler, provide one to handle action ( control being moved )
@@ -8634,6 +8737,41 @@ class Control {
8634
8737
  };
8635
8738
  }
8636
8739
 
8740
+ /**
8741
+ * This is an helper method to prepare the canvas to render a control
8742
+ * It detectes common control properties and sets the correct fill and
8743
+ * stroke styles on the context. It does not execute translations or
8744
+ * rotations since different controls need differnt combination of these.
8745
+ */
8746
+ commonRenderProps(ctx, left, top, fabricObject) {
8747
+ let styleOverride = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
8748
+ const {
8749
+ cornerSize,
8750
+ cornerColor,
8751
+ transparentCorners,
8752
+ cornerStrokeColor
8753
+ } = styleOverride,
8754
+ sizeFromProps = cornerSize || fabricObject.cornerSize,
8755
+ xSize = this.sizeX || sizeFromProps,
8756
+ ySize = this.sizeY || sizeFromProps,
8757
+ transparent = typeof transparentCorners !== 'undefined' ? transparentCorners : fabricObject.transparentCorners,
8758
+ opName = transparent ? STROKE : FILL,
8759
+ strokeColor = cornerStrokeColor || fabricObject.cornerStrokeColor,
8760
+ stroke = !transparent && !!strokeColor;
8761
+ ctx.fillStyle = cornerColor || fabricObject.cornerColor || '';
8762
+ ctx.strokeStyle = strokeColor || '';
8763
+ ctx.translate(left, top);
8764
+ // angle is relative to canvas plane
8765
+ ctx.rotate(degreesToRadians(fabricObject.getTotalAngle()));
8766
+ return {
8767
+ stroke,
8768
+ xSize,
8769
+ ySize,
8770
+ transparentCorners: transparent,
8771
+ opName
8772
+ };
8773
+ }
8774
+
8637
8775
  /**
8638
8776
  * Render function for the control.
8639
8777
  * When this function runs the context is unscaled. unrotate. Just retina scaled.
@@ -10273,136 +10411,31 @@ const cloneStyles = style => {
10273
10411
  };
10274
10412
 
10275
10413
  /**
10276
- * Capitalizes a string
10277
- * @param {String} string String to capitalize
10278
- * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
10279
- * and other letters stay untouched, if false first letter is capitalized
10280
- * and other letters are converted to lowercase.
10281
- * @return {String} Capitalized version of a string
10414
+ * @param {Object} prevStyle first style to compare
10415
+ * @param {Object} thisStyle second style to compare
10416
+ * @param {boolean} forTextSpans whether to check overline, underline, and line-through properties
10417
+ * @return {boolean} true if the style changed
10282
10418
  */
10283
- const capitalize = function (string) {
10284
- let firstLetterOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
10285
- return `${string.charAt(0).toUpperCase()}${firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase()}`;
10419
+ const hasStyleChanged = function (prevStyle, thisStyle) {
10420
+ let forTextSpans = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
10421
+ 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);
10286
10422
  };
10287
10423
 
10288
10424
  /**
10289
- * Escapes XML in a string
10290
- * @param {String} string String to escape
10291
- * @return {String} Escaped version of a string
10425
+ * Returns the array form of a text object's inline styles property with styles grouped in ranges
10426
+ * rather than per character. This format is less verbose, and is better suited for storage
10427
+ * so it is used in serialization (not during runtime).
10428
+ * @param {object} styles per character styles for a text object
10429
+ * @param {String} text the text string that the styles are applied to
10430
+ * @return {{start: number, end: number, style: object}[]}
10292
10431
  */
10293
- const escapeXml = string => string.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&apos;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
10294
- let segmenter;
10295
- const getSegmenter = () => {
10296
- if (!segmenter) {
10297
- segmenter = 'Intl' in getFabricWindow() && 'Segmenter' in Intl && new Intl.Segmenter(undefined, {
10298
- granularity: 'grapheme'
10299
- });
10300
- }
10301
- return segmenter;
10302
- };
10303
-
10304
- /**
10305
- * Divide a string in the user perceived single units
10306
- * @param {String} textstring String to escape
10307
- * @return {Array} array containing the graphemes
10308
- */
10309
- const graphemeSplit = textstring => {
10310
- segmenter || getSegmenter();
10311
- if (segmenter) {
10312
- const segments = segmenter.segment(textstring);
10313
- return Array.from(segments).map(_ref => {
10314
- let {
10315
- segment
10316
- } = _ref;
10317
- return segment;
10318
- });
10319
- }
10320
-
10321
- //Fallback
10322
- return graphemeSplitImpl(textstring);
10323
- };
10324
- const graphemeSplitImpl = textstring => {
10325
- const graphemes = [];
10326
- for (let i = 0, chr; i < textstring.length; i++) {
10327
- if ((chr = getWholeChar(textstring, i)) === false) {
10328
- continue;
10329
- }
10330
- graphemes.push(chr);
10331
- }
10332
- return graphemes;
10333
- };
10334
-
10335
- // taken from mdn in the charAt doc page.
10336
- const getWholeChar = (str, i) => {
10337
- const code = str.charCodeAt(i);
10338
- if (isNaN(code)) {
10339
- return ''; // Position not found
10340
- }
10341
- if (code < 0xd800 || code > 0xdfff) {
10342
- return str.charAt(i);
10343
- }
10344
-
10345
- // High surrogate (could change last hex to 0xDB7F to treat high private
10346
- // surrogates as single characters)
10347
- if (0xd800 <= code && code <= 0xdbff) {
10348
- if (str.length <= i + 1) {
10349
- throw 'High surrogate without following low surrogate';
10350
- }
10351
- const next = str.charCodeAt(i + 1);
10352
- if (0xdc00 > next || next > 0xdfff) {
10353
- throw 'High surrogate without following low surrogate';
10354
- }
10355
- return str.charAt(i) + str.charAt(i + 1);
10356
- }
10357
- // Low surrogate (0xDC00 <= code && code <= 0xDFFF)
10358
- if (i === 0) {
10359
- throw 'Low surrogate without preceding high surrogate';
10360
- }
10361
- const prev = str.charCodeAt(i - 1);
10362
-
10363
- // (could change last hex to 0xDB7F to treat high private
10364
- // surrogates as single characters)
10365
- if (0xd800 > prev || prev > 0xdbff) {
10366
- throw 'Low surrogate without preceding high surrogate';
10367
- }
10368
- // We can pass over low surrogates now as the second component
10369
- // in a pair which we have already processed
10370
- return false;
10371
- };
10372
-
10373
- var lang_string = /*#__PURE__*/Object.freeze({
10374
- __proto__: null,
10375
- capitalize: capitalize,
10376
- escapeXml: escapeXml,
10377
- graphemeSplit: graphemeSplit
10378
- });
10379
-
10380
- /**
10381
- * @param {Object} prevStyle first style to compare
10382
- * @param {Object} thisStyle second style to compare
10383
- * @param {boolean} forTextSpans whether to check overline, underline, and line-through properties
10384
- * @return {boolean} true if the style changed
10385
- */
10386
- const hasStyleChanged = function (prevStyle, thisStyle) {
10387
- let forTextSpans = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
10388
- 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);
10389
- };
10390
-
10391
- /**
10392
- * Returns the array form of a text object's inline styles property with styles grouped in ranges
10393
- * rather than per character. This format is less verbose, and is better suited for storage
10394
- * so it is used in serialization (not during runtime).
10395
- * @param {object} styles per character styles for a text object
10396
- * @param {String} text the text string that the styles are applied to
10397
- * @return {{start: number, end: number, style: object}[]}
10398
- */
10399
- const stylesToArray = (styles, text) => {
10400
- const textLines = text.split('\n'),
10401
- stylesArray = [];
10402
- let charIndex = -1,
10403
- prevStyle = {};
10404
- // clone style structure to prevent mutation
10405
- styles = cloneStyles(styles);
10432
+ const stylesToArray = (styles, text) => {
10433
+ const textLines = text.split('\n'),
10434
+ stylesArray = [];
10435
+ let charIndex = -1,
10436
+ prevStyle = {};
10437
+ // clone style structure to prevent mutation
10438
+ styles = cloneStyles(styles);
10406
10439
 
10407
10440
  //loop through each textLine
10408
10441
  for (let i = 0; i < textLines.length; i++) {
@@ -10960,7 +10993,7 @@ class Rect extends FabricObject {
10960
10993
  rx,
10961
10994
  ry
10962
10995
  } = this;
10963
- return ['<rect ', 'COMMON_PARTS', `x="${-width / 2}" y="${-height / 2}" rx="${rx}" ry="${ry}" width="${width}" height="${height}" />\n`];
10996
+ return ['<rect ', 'COMMON_PARTS', `x="${-width / 2}" y="${-height / 2}" rx="${escapeXml(rx)}" ry="${escapeXml(ry)}" width="${escapeXml(width)}" height="${escapeXml(height)}" />\n`];
10964
10997
  }
10965
10998
 
10966
10999
  /**
@@ -11925,7 +11958,7 @@ class Group extends createCollectionMixin(FabricObject) {
11925
11958
  * @return {String}
11926
11959
  */
11927
11960
  getSvgStyles() {
11928
- const opacity = typeof this.opacity !== 'undefined' && this.opacity !== 1 ? `opacity: ${this.opacity};` : '',
11961
+ const opacity = typeof this.opacity !== 'undefined' && this.opacity !== 1 ? `opacity: ${escapeXml(this.opacity)};` : '',
11929
11962
  visibility = this.visible ? '' : ' visibility: hidden;';
11930
11963
  return [opacity, this.getSvgFilter(), visibility].join('');
11931
11964
  }
@@ -13227,6 +13260,288 @@ const canvasDefaults = {
13227
13260
  preserveObjectStacking: true
13228
13261
  };
13229
13262
 
13263
+ /**
13264
+ * Action handler
13265
+ * @private
13266
+ * @param {Event} eventData javascript event that is doing the transform
13267
+ * @param {Object} transform javascript object containing a series of information around the current transform
13268
+ * @param {number} x current mouse x position, canvas normalized
13269
+ * @param {number} y current mouse y position, canvas normalized
13270
+ * @return {Boolean} true if the translation occurred
13271
+ */
13272
+ const dragHandler = (eventData, transform, x, y) => {
13273
+ const {
13274
+ target,
13275
+ offsetX,
13276
+ offsetY
13277
+ } = transform,
13278
+ newLeft = x - offsetX,
13279
+ newTop = y - offsetY,
13280
+ moveX = !isLocked(target, 'lockMovementX') && target.left !== newLeft,
13281
+ moveY = !isLocked(target, 'lockMovementY') && target.top !== newTop;
13282
+ moveX && target.set(LEFT, newLeft);
13283
+ moveY && target.set(TOP, newTop);
13284
+ if (moveX || moveY) {
13285
+ fireEvent(MOVING, commonEventInfo(eventData, transform, x, y));
13286
+ }
13287
+ return moveX || moveY;
13288
+ };
13289
+
13290
+ const ACTION_NAME$1 = MODIFY_POLY;
13291
+ /**
13292
+ * This function locates the controls.
13293
+ * It'll be used both for drawing and for interaction.
13294
+ */
13295
+ const createPolyPositionHandler = pointIndex => {
13296
+ return function (dim, finalMatrix, polyObject) {
13297
+ const {
13298
+ points,
13299
+ pathOffset
13300
+ } = polyObject;
13301
+ return new Point(points[pointIndex]).subtract(pathOffset).transform(multiplyTransformMatrices(polyObject.getViewportTransform(), polyObject.calcTransformMatrix()));
13302
+ };
13303
+ };
13304
+
13305
+ /**
13306
+ * This function defines what the control does.
13307
+ * It'll be called on every mouse move after a control has been clicked and is being dragged.
13308
+ * The function receives as argument the mouse event, the current transform object
13309
+ * and the current position in canvas coordinate `transform.target` is a reference to the
13310
+ * current object being transformed.
13311
+ */
13312
+ const polyActionHandler = (eventData, transform, x, y) => {
13313
+ const {
13314
+ target,
13315
+ pointIndex
13316
+ } = transform;
13317
+ const poly = target;
13318
+ const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, poly.calcOwnMatrix());
13319
+ poly.points[pointIndex] = mouseLocalPosition.add(poly.pathOffset);
13320
+ poly.setDimensions();
13321
+ poly.set('dirty', true);
13322
+ return true;
13323
+ };
13324
+
13325
+ /**
13326
+ * Keep the polygon in the same position when we change its `width`/`height`/`top`/`left`.
13327
+ */
13328
+ const factoryPolyActionHandler = (pointIndex, fn) => {
13329
+ return function (eventData, transform, x, y) {
13330
+ const poly = transform.target,
13331
+ anchorPoint = new Point(poly.points[(pointIndex > 0 ? pointIndex : poly.points.length) - 1]),
13332
+ anchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix()),
13333
+ actionPerformed = fn(eventData, {
13334
+ ...transform,
13335
+ pointIndex
13336
+ }, x, y);
13337
+ const newAnchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix());
13338
+ const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
13339
+ poly.left -= diff.x;
13340
+ poly.top -= diff.y;
13341
+ return actionPerformed;
13342
+ };
13343
+ };
13344
+ const createPolyActionHandler = pointIndex => wrapWithFireEvent(ACTION_NAME$1, factoryPolyActionHandler(pointIndex, polyActionHandler));
13345
+ function createPolyControls(arg0) {
13346
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13347
+ const controls = {};
13348
+ for (let idx = 0; idx < (typeof arg0 === 'number' ? arg0 : arg0.points.length); idx++) {
13349
+ controls[`p${idx}`] = new Control({
13350
+ actionName: ACTION_NAME$1,
13351
+ positionHandler: createPolyPositionHandler(idx),
13352
+ actionHandler: createPolyActionHandler(idx),
13353
+ ...options
13354
+ });
13355
+ }
13356
+ return controls;
13357
+ }
13358
+
13359
+ const ACTION_NAME = 'modifyPath';
13360
+ const calcPathPointPosition = (pathObject, commandIndex, pointIndex) => {
13361
+ const {
13362
+ path,
13363
+ pathOffset
13364
+ } = pathObject;
13365
+ const command = path[commandIndex];
13366
+ return new Point(command[pointIndex] - pathOffset.x, command[pointIndex + 1] - pathOffset.y).transform(multiplyTransformMatrices(pathObject.getViewportTransform(), pathObject.calcTransformMatrix()));
13367
+ };
13368
+ const movePathPoint = (pathObject, x, y, commandIndex, pointIndex) => {
13369
+ const {
13370
+ path,
13371
+ pathOffset
13372
+ } = pathObject;
13373
+ const anchorCommand = path[(commandIndex > 0 ? commandIndex : path.length) - 1];
13374
+ const anchorPoint = new Point(anchorCommand[pointIndex], anchorCommand[pointIndex + 1]);
13375
+ const anchorPointInParentPlane = anchorPoint.subtract(pathOffset).transform(pathObject.calcOwnMatrix());
13376
+ const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, pathObject.calcOwnMatrix());
13377
+ path[commandIndex][pointIndex] = mouseLocalPosition.x + pathOffset.x;
13378
+ path[commandIndex][pointIndex + 1] = mouseLocalPosition.y + pathOffset.y;
13379
+ pathObject.setDimensions();
13380
+ const newAnchorPointInParentPlane = anchorPoint.subtract(pathObject.pathOffset).transform(pathObject.calcOwnMatrix());
13381
+ const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
13382
+ pathObject.left -= diff.x;
13383
+ pathObject.top -= diff.y;
13384
+ pathObject.set('dirty', true);
13385
+ return true;
13386
+ };
13387
+
13388
+ /**
13389
+ * This function locates the controls.
13390
+ * It'll be used both for drawing and for interaction.
13391
+ */
13392
+ function pathPositionHandler(dim, finalMatrix, pathObject) {
13393
+ const {
13394
+ commandIndex,
13395
+ pointIndex
13396
+ } = this;
13397
+ return calcPathPointPosition(pathObject, commandIndex, pointIndex);
13398
+ }
13399
+
13400
+ /**
13401
+ * This function defines what the control does.
13402
+ * It'll be called on every mouse move after a control has been clicked and is being dragged.
13403
+ * The function receives as argument the mouse event, the current transform object
13404
+ * and the current position in canvas coordinate `transform.target` is a reference to the
13405
+ * current object being transformed.
13406
+ */
13407
+ function pathActionHandler(eventData, transform, x, y) {
13408
+ const {
13409
+ target
13410
+ } = transform;
13411
+ const {
13412
+ commandIndex,
13413
+ pointIndex
13414
+ } = this;
13415
+ const actionPerformed = movePathPoint(target, x, y, commandIndex, pointIndex);
13416
+ {
13417
+ fireEvent(this.actionName, {
13418
+ ...commonEventInfo(eventData, transform, x, y),
13419
+ commandIndex,
13420
+ pointIndex
13421
+ });
13422
+ }
13423
+ return actionPerformed;
13424
+ }
13425
+ const indexFromPrevCommand = previousCommandType => previousCommandType === 'C' ? 5 : previousCommandType === 'Q' ? 3 : 1;
13426
+ class PathPointControl extends Control {
13427
+ constructor(options) {
13428
+ super(options);
13429
+ }
13430
+ render(ctx, left, top, styleOverride, fabricObject) {
13431
+ const overrides = {
13432
+ ...styleOverride,
13433
+ cornerColor: this.controlFill,
13434
+ cornerStrokeColor: this.controlStroke,
13435
+ transparentCorners: !this.controlFill
13436
+ };
13437
+ super.render(ctx, left, top, overrides, fabricObject);
13438
+ }
13439
+ }
13440
+ class PathControlPointControl extends PathPointControl {
13441
+ constructor(options) {
13442
+ super(options);
13443
+ }
13444
+ render(ctx, left, top, styleOverride, fabricObject) {
13445
+ const {
13446
+ path
13447
+ } = fabricObject;
13448
+ const {
13449
+ commandIndex,
13450
+ pointIndex,
13451
+ connectToCommandIndex,
13452
+ connectToPointIndex
13453
+ } = this;
13454
+ ctx.save();
13455
+ ctx.strokeStyle = this.controlStroke;
13456
+ if (this.connectionDashArray) {
13457
+ ctx.setLineDash(this.connectionDashArray);
13458
+ }
13459
+ const [commandType] = path[commandIndex];
13460
+ const point = calcPathPointPosition(fabricObject, connectToCommandIndex, connectToPointIndex);
13461
+ if (commandType === 'Q') {
13462
+ // one control point connects to 2 points
13463
+ const point2 = calcPathPointPosition(fabricObject, commandIndex, pointIndex + 2);
13464
+ ctx.moveTo(point2.x, point2.y);
13465
+ ctx.lineTo(left, top);
13466
+ } else {
13467
+ ctx.moveTo(left, top);
13468
+ }
13469
+ ctx.lineTo(point.x, point.y);
13470
+ ctx.stroke();
13471
+ ctx.restore();
13472
+ super.render(ctx, left, top, styleOverride, fabricObject);
13473
+ }
13474
+ }
13475
+ const createControl = (commandIndexPos, pointIndexPos, isControlPoint, options, connectToCommandIndex, connectToPointIndex) => new (isControlPoint ? PathControlPointControl : PathPointControl)({
13476
+ commandIndex: commandIndexPos,
13477
+ pointIndex: pointIndexPos,
13478
+ actionName: ACTION_NAME,
13479
+ positionHandler: pathPositionHandler,
13480
+ actionHandler: pathActionHandler,
13481
+ connectToCommandIndex,
13482
+ connectToPointIndex,
13483
+ ...options,
13484
+ ...(isControlPoint ? options.controlPointStyle : options.pointStyle)
13485
+ });
13486
+ function createPathControls(path) {
13487
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13488
+ const controls = {};
13489
+ let previousCommandType = 'M';
13490
+ path.path.forEach((command, commandIndex) => {
13491
+ const commandType = command[0];
13492
+ if (commandType !== 'Z') {
13493
+ controls[`c_${commandIndex}_${commandType}`] = createControl(commandIndex, command.length - 2, false, options);
13494
+ }
13495
+ switch (commandType) {
13496
+ case 'C':
13497
+ controls[`c_${commandIndex}_C_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex - 1, indexFromPrevCommand(previousCommandType));
13498
+ controls[`c_${commandIndex}_C_CP_2`] = createControl(commandIndex, 3, true, options, commandIndex, 5);
13499
+ break;
13500
+ case 'Q':
13501
+ controls[`c_${commandIndex}_Q_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex, 3);
13502
+ break;
13503
+ }
13504
+ previousCommandType = commandType;
13505
+ });
13506
+ return controls;
13507
+ }
13508
+
13509
+ var index = /*#__PURE__*/Object.freeze({
13510
+ __proto__: null,
13511
+ changeHeight: changeHeight,
13512
+ changeObjectHeight: changeObjectHeight,
13513
+ changeObjectWidth: changeObjectWidth,
13514
+ changeWidth: changeWidth,
13515
+ createObjectDefaultControls: createObjectDefaultControls,
13516
+ createPathControls: createPathControls,
13517
+ createPolyActionHandler: createPolyActionHandler,
13518
+ createPolyControls: createPolyControls,
13519
+ createPolyPositionHandler: createPolyPositionHandler,
13520
+ createResizeControls: createResizeControls,
13521
+ createTextboxDefaultControls: createTextboxDefaultControls,
13522
+ dragHandler: dragHandler,
13523
+ factoryPolyActionHandler: factoryPolyActionHandler,
13524
+ getLocalPoint: getLocalPoint,
13525
+ polyActionHandler: polyActionHandler,
13526
+ renderCircleControl: renderCircleControl,
13527
+ renderSquareControl: renderSquareControl,
13528
+ rotationStyleHandler: rotationStyleHandler,
13529
+ rotationWithSnapping: rotationWithSnapping,
13530
+ scaleCursorStyleHandler: scaleCursorStyleHandler,
13531
+ scaleOrSkewActionName: scaleOrSkewActionName,
13532
+ scaleSkewCursorStyleHandler: scaleSkewCursorStyleHandler,
13533
+ scalingEqually: scalingEqually,
13534
+ scalingX: scalingX,
13535
+ scalingXOrSkewingY: scalingXOrSkewingY,
13536
+ scalingY: scalingY,
13537
+ scalingYOrSkewingX: scalingYOrSkewingX,
13538
+ skewCursorStyleHandler: skewCursorStyleHandler,
13539
+ skewHandlerX: skewHandlerX,
13540
+ skewHandlerY: skewHandlerY,
13541
+ wrapWithFireEvent: wrapWithFireEvent,
13542
+ wrapWithFixedAnchor: wrapWithFixedAnchor
13543
+ });
13544
+
13230
13545
  /**
13231
13546
  * Canvas class
13232
13547
  * @class Canvas
@@ -13597,11 +13912,13 @@ class SelectableCanvas extends StaticCanvas$1 {
13597
13912
  * Given the control clicked, determine the origin of the transform.
13598
13913
  * This is bad because controls can totally have custom names
13599
13914
  * should disappear before release 4.0
13915
+ * Fabric 7.1, jan 2026 we are still using this.
13916
+ * Needs to go.
13600
13917
  * @private
13601
13918
  * @deprecated
13602
13919
  */
13603
13920
  _getOriginFromCorner(target, controlName) {
13604
- const origin = {
13921
+ const origin = controlName ? target.controls[controlName].getTransformAnchorPoint() : {
13605
13922
  x: target.originX,
13606
13923
  y: target.originY
13607
13924
  };
@@ -13609,6 +13926,9 @@ class SelectableCanvas extends StaticCanvas$1 {
13609
13926
  return origin;
13610
13927
  }
13611
13928
 
13929
+ // this part down here is deprecated.
13930
+ // It is left to do not change the standard behavior in the middle of a major version
13931
+ // but when possible `getTransformAnchorPoint` will be the only source of truth
13612
13932
  // is a left control ?
13613
13933
  if (['ml', 'tl', 'bl'].includes(controlName)) {
13614
13934
  origin.x = RIGHT;
@@ -13648,37 +13968,52 @@ class SelectableCanvas extends StaticCanvas$1 {
13648
13968
  x: CENTER,
13649
13969
  y: CENTER
13650
13970
  } : this._getOriginFromCorner(target, corner),
13971
+ {
13972
+ scaleX,
13973
+ scaleY,
13974
+ skewX,
13975
+ skewY,
13976
+ left,
13977
+ top,
13978
+ angle,
13979
+ width,
13980
+ height,
13981
+ cropX,
13982
+ cropY
13983
+ } = target,
13651
13984
  /**
13652
13985
  * relative to target's containing coordinate plane
13653
13986
  * both agree on every point
13654
13987
  **/
13655
13988
  transform = {
13656
- target: target,
13989
+ target,
13657
13990
  action,
13658
13991
  actionHandler,
13659
13992
  actionPerformed: false,
13660
13993
  corner,
13661
- scaleX: target.scaleX,
13662
- scaleY: target.scaleY,
13663
- skewX: target.skewX,
13664
- skewY: target.skewY,
13665
- offsetX: pointer.x - target.left,
13666
- offsetY: pointer.y - target.top,
13994
+ scaleX,
13995
+ scaleY,
13996
+ skewX,
13997
+ skewY,
13998
+ offsetX: pointer.x - left,
13999
+ offsetY: pointer.y - top,
13667
14000
  originX: origin.x,
13668
14001
  originY: origin.y,
13669
14002
  ex: pointer.x,
13670
14003
  ey: pointer.y,
13671
14004
  lastX: pointer.x,
13672
14005
  lastY: pointer.y,
13673
- theta: degreesToRadians(target.angle),
13674
- width: target.width,
13675
- height: target.height,
14006
+ theta: degreesToRadians(angle),
14007
+ width,
14008
+ height,
13676
14009
  shiftKey: e.shiftKey,
13677
14010
  altKey,
13678
14011
  original: {
13679
14012
  ...saveObjectTransform(target),
13680
14013
  originX: origin.x,
13681
- originY: origin.y
14014
+ originY: origin.y,
14015
+ cropX,
14016
+ cropY
13682
14017
  }
13683
14018
  };
13684
14019
  this._currentTransform = transform;
@@ -16091,7 +16426,8 @@ class Gradient {
16091
16426
  }
16092
16427
  transform[4] -= offsetX;
16093
16428
  transform[5] -= offsetY;
16094
- const commonAttributes = [`id="SVGID_${this.id}"`, `gradientUnits="${gradientUnits}"`, `gradientTransform="${preTransform ? preTransform + ' ' : ''}${matrixToSVG(transform)}"`, ''].join(' ');
16429
+ const commonAttributes = [`id="SVGID_${escapeXml(String(this.id))}"`, `gradientUnits="${gradientUnits}"`, `gradientTransform="${preTransform ? preTransform + ' ' : ''}${matrixToSVG(transform)}"`, ''].join(' ');
16430
+ const sanitizeCoord = value => parseFloat(String(value));
16095
16431
  if (this.type === 'linear') {
16096
16432
  const {
16097
16433
  x1,
@@ -16099,7 +16435,11 @@ class Gradient {
16099
16435
  x2,
16100
16436
  y2
16101
16437
  } = this.coords;
16102
- markup.push('<linearGradient ', commonAttributes, ' x1="', x1, '" y1="', y1, '" x2="', x2, '" y2="', y2, '">\n');
16438
+ const sx1 = sanitizeCoord(x1);
16439
+ const sy1 = sanitizeCoord(y1);
16440
+ const sx2 = sanitizeCoord(x2);
16441
+ const sy2 = sanitizeCoord(y2);
16442
+ markup.push('<linearGradient ', commonAttributes, ' x1="', sx1, '" y1="', sy1, '" x2="', sx2, '" y2="', sy2, '">\n');
16103
16443
  } else if (this.type === 'radial') {
16104
16444
  const {
16105
16445
  x1,
@@ -16109,9 +16449,15 @@ class Gradient {
16109
16449
  r1,
16110
16450
  r2
16111
16451
  } = this.coords;
16112
- const needsSwap = r1 > r2;
16452
+ const sx1 = sanitizeCoord(x1);
16453
+ const sy1 = sanitizeCoord(y1);
16454
+ const sx2 = sanitizeCoord(x2);
16455
+ const sy2 = sanitizeCoord(y2);
16456
+ const sr1 = sanitizeCoord(r1);
16457
+ const sr2 = sanitizeCoord(r2);
16458
+ const needsSwap = sr1 > sr2;
16113
16459
  // svg radial gradient has just 1 radius. the biggest.
16114
- 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');
16460
+ 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');
16115
16461
  if (needsSwap) {
16116
16462
  // svg goes from internal to external radius. if radius are inverted, swap color stops.
16117
16463
  colorStops.reverse(); // mutates array
@@ -16119,16 +16465,17 @@ class Gradient {
16119
16465
  colorStop.offset = 1 - colorStop.offset;
16120
16466
  });
16121
16467
  }
16122
- const minRadius = Math.min(r1, r2);
16468
+ const minRadius = Math.min(sr1, sr2);
16123
16469
  if (minRadius > 0) {
16124
16470
  // i have to shift all colorStops and add new one in 0.
16125
- const maxRadius = Math.max(r1, r2),
16471
+ const maxRadius = Math.max(sr1, sr2),
16126
16472
  percentageShift = minRadius / maxRadius;
16127
16473
  colorStops.forEach(colorStop => {
16128
16474
  colorStop.offset += percentageShift * (1 - colorStop.offset);
16129
16475
  });
16130
16476
  }
16131
16477
  }
16478
+ // todo make a malicious script tag injection test with color and also apply a fix with escapeXml
16132
16479
  colorStops.forEach(_ref => {
16133
16480
  let {
16134
16481
  color,
@@ -16444,7 +16791,7 @@ class Pattern {
16444
16791
  patternOffsetY = ifNaN(this.offsetY / height, 0),
16445
16792
  patternWidth = repeat === 'repeat-y' || repeat === 'no-repeat' ? 1 + Math.abs(patternOffsetX || 0) : ifNaN(patternSource.width / width, 0),
16446
16793
  patternHeight = repeat === 'repeat-x' || repeat === 'no-repeat' ? 1 + Math.abs(patternOffsetY || 0) : ifNaN(patternSource.height / height, 0);
16447
- 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');
16794
+ 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');
16448
16795
  }
16449
16796
  /* _TO_SVG_END_ */
16450
16797
 
@@ -16726,8 +17073,7 @@ class Path extends FabricObject {
16726
17073
  * of the instance
16727
17074
  */
16728
17075
  _toSVG() {
16729
- const path = joinPath(this.path, config.NUM_FRACTION_DIGITS);
16730
- return ['<path ', 'COMMON_PARTS', `d="${path}" stroke-linecap="round" />\n`];
17076
+ return ['<path ', 'COMMON_PARTS', `d="${joinPath(this.path, config.NUM_FRACTION_DIGITS)}" stroke-linecap="round" />\n`];
16731
17077
  }
16732
17078
 
16733
17079
  /**
@@ -17278,15 +17624,17 @@ class Circle extends FabricObject {
17278
17624
  * of the instance
17279
17625
  */
17280
17626
  _toSVG() {
17281
- const angle = (this.endAngle - this.startAngle) % 360;
17627
+ const {
17628
+ radius,
17629
+ startAngle,
17630
+ endAngle
17631
+ } = this;
17632
+ const angle = (endAngle - startAngle) % 360;
17282
17633
  if (angle === 0) {
17283
- return ['<circle ', 'COMMON_PARTS', 'cx="0" cy="0" ', 'r="', `${this.radius}`, '" />\n'];
17634
+ return ['<circle ', 'COMMON_PARTS', 'cx="0" cy="0" ', 'r="', `${escapeXml(radius)}`, '" />\n'];
17284
17635
  } else {
17285
- const {
17286
- radius
17287
- } = this;
17288
- const start = degreesToRadians(this.startAngle),
17289
- end = degreesToRadians(this.endAngle),
17636
+ const start = degreesToRadians(startAngle),
17637
+ end = degreesToRadians(endAngle),
17290
17638
  startX = cos(start) * radius,
17291
17639
  startY = sin(start) * radius,
17292
17640
  endX = cos(end) * radius,
@@ -17858,17 +18206,13 @@ class Line extends FabricObject {
17858
18206
  width,
17859
18207
  height
17860
18208
  } = this;
17861
- const xMult = _x1 <= _x2 ? -1 : 1,
17862
- yMult = _y1 <= _y2 ? -1 : 1,
17863
- x1 = xMult * width / 2,
17864
- y1 = yMult * height / 2,
17865
- x2 = xMult * -width / 2,
17866
- y2 = yMult * -height / 2;
18209
+ const xMult = _x1 <= _x2 ? -0.5 : 0.5,
18210
+ yMult = _y1 <= _y2 ? -0.5 : 0.5;
17867
18211
  return {
17868
- x1,
17869
- x2,
17870
- y1,
17871
- y2
18212
+ x1: xMult * width,
18213
+ x2: xMult * -width,
18214
+ y1: yMult * height,
18215
+ y2: yMult * -height
17872
18216
  };
17873
18217
  }
17874
18218
 
@@ -18086,7 +18430,7 @@ class Ellipse extends FabricObject {
18086
18430
  * of the instance
18087
18431
  */
18088
18432
  _toSVG() {
18089
- return ['<ellipse ', 'COMMON_PARTS', `cx="0" cy="0" rx="${this.rx}" ry="${this.ry}" />\n`];
18433
+ return ['<ellipse ', 'COMMON_PARTS', `cx="0" cy="0" rx="${escapeXml(this.rx)}" ry="${escapeXml(this.ry)}" />\n`];
18090
18434
  }
18091
18435
 
18092
18436
  /**
@@ -18404,14 +18748,17 @@ class Polyline extends FabricObject {
18404
18748
  * of the instance
18405
18749
  */
18406
18750
  _toSVG() {
18407
- const points = [],
18408
- diffX = this.pathOffset.x,
18751
+ const diffX = this.pathOffset.x,
18409
18752
  diffY = this.pathOffset.y,
18410
18753
  NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS;
18411
- for (let i = 0, len = this.points.length; i < len; i++) {
18412
- points.push(toFixed(this.points[i].x - diffX, NUM_FRACTION_DIGITS), ',', toFixed(this.points[i].y - diffY, NUM_FRACTION_DIGITS), ' ');
18413
- }
18414
- return [`<${this.constructor.type.toLowerCase()} `, 'COMMON_PARTS', `points="${points.join('')}" />\n`];
18754
+ const points = this.points.map(_ref2 => {
18755
+ let {
18756
+ x,
18757
+ y
18758
+ } = _ref2;
18759
+ return `${toFixed(x - diffX, NUM_FRACTION_DIGITS)},${toFixed(y - diffY, NUM_FRACTION_DIGITS)}`;
18760
+ }).join(' ');
18761
+ return [`<${escapeXml(this.constructor.type).toLowerCase()} `, 'COMMON_PARTS', `points="${points}" />\n`];
18415
18762
  }
18416
18763
 
18417
18764
  /**
@@ -18534,7 +18881,6 @@ class StyledText extends FabricObject {
18534
18881
  };
18535
18882
  for (const p1 in obj) {
18536
18883
  for (const p2 in obj[p1]) {
18537
- // eslint-disable-next-line no-unused-vars
18538
18884
  for (const p3 in obj[p1][p2]) {
18539
18885
  return false;
18540
18886
  }
@@ -18560,9 +18906,7 @@ class StyledText extends FabricObject {
18560
18906
  const obj = typeof lineIndex === 'undefined' ? this.styles : {
18561
18907
  0: this.styles[lineIndex]
18562
18908
  };
18563
- // eslint-disable-next-line
18564
18909
  for (const p1 in obj) {
18565
- // eslint-disable-next-line
18566
18910
  for (const p2 in obj[p1]) {
18567
18911
  if (typeof obj[p1][p2][property] !== 'undefined') {
18568
18912
  return true;
@@ -18838,7 +19182,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18838
19182
  } = _ref;
18839
19183
  const noShadow = true,
18840
19184
  textDecoration = this.getSvgTextDecoration(this);
18841
- 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'];
19185
+ 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'];
18842
19186
  }
18843
19187
 
18844
19188
  /**
@@ -18854,7 +19198,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18854
19198
  lineOffset;
18855
19199
 
18856
19200
  // bounding-box background
18857
- this.backgroundColor && textBgRects.push(...createSVGInlineRect(this.backgroundColor, -this.width / 2, -this.height / 2, this.width, this.height));
19201
+ this.backgroundColor && textBgRects.push(createSVGInlineRect(this.backgroundColor, -this.width / 2, -this.height / 2, this.width, this.height));
18858
19202
 
18859
19203
  // text and text-background
18860
19204
  for (let i = 0, len = this._textLines.length; i < len; i++) {
@@ -18962,7 +19306,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18962
19306
  } = this.__charBounds[i][j];
18963
19307
  currentColor = this.getValueOfPropertyAt(i, j, 'textBackgroundColor');
18964
19308
  if (currentColor !== lastColor) {
18965
- lastColor && textBgRects.push(...createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
19309
+ lastColor && textBgRects.push(createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
18966
19310
  boxStart = left;
18967
19311
  boxWidth = width;
18968
19312
  lastColor = currentColor;
@@ -18970,7 +19314,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18970
19314
  boxWidth += kernedWidth;
18971
19315
  }
18972
19316
  }
18973
- currentColor && textBgRects.push(...createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
19317
+ currentColor && textBgRects.push(createSVGInlineRect(lastColor, leftOffset + boxStart, textTopOffset, boxWidth, heightOfLine));
18974
19318
  }
18975
19319
 
18976
19320
  /**
@@ -18997,7 +19341,6 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
18997
19341
  fontSize,
18998
19342
  fontStyle,
18999
19343
  fontWeight,
19000
- deltaY,
19001
19344
  textDecorationThickness,
19002
19345
  linethrough,
19003
19346
  overline,
@@ -19009,7 +19352,7 @@ class TextSVGExportMixin extends FabricObjectSVGExportMixin {
19009
19352
  linethrough: linethrough !== null && linethrough !== void 0 ? linethrough : this.linethrough
19010
19353
  });
19011
19354
  const thickness = textDecorationThickness || this.textDecorationThickness;
19012
- 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('');
19355
+ 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('');
19013
19356
  }
19014
19357
 
19015
19358
  /**
@@ -24573,13 +24916,13 @@ class FabricImage extends FabricObject {
24573
24916
  }
24574
24917
  if (this.hasCrop()) {
24575
24918
  const clipPathId = uid();
24576
- svgString.push('<clipPath id="imageCrop_' + clipPathId + '">\n', '\t<rect x="' + x + '" y="' + y + '" width="' + this.width + '" height="' + this.height + '" />\n', '</clipPath>\n');
24919
+ svgString.push('<clipPath id="imageCrop_' + clipPathId + '">\n', '\t<rect x="' + x + '" y="' + y + '" width="' + escapeXml(this.width) + '" height="' + escapeXml(this.height) + '" />\n', '</clipPath>\n');
24577
24920
  clipPath = ' clip-path="url(#imageCrop_' + clipPathId + ')" ';
24578
24921
  }
24579
24922
  if (!this.imageSmoothing) {
24580
24923
  imageRendering = ' image-rendering="optimizeSpeed"';
24581
24924
  }
24582
- imageMarkup.push('\t<image ', 'COMMON_PARTS', `xlink:href="${this.getSvgSrc(true)}" x="${x - this.cropX}" y="${y - this.cropY
24925
+ imageMarkup.push('\t<image ', 'COMMON_PARTS', `xlink:href="${escapeXml(this.getSrc(true))}" x="${x - this.cropX}" y="${y - this.cropY
24583
24926
  // we're essentially moving origin of transformation from top/left corner to the center of the shape
24584
24927
  // by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
24585
24928
  // so that object's center aligns with container's left/top
@@ -24587,7 +24930,7 @@ class FabricImage extends FabricObject {
24587
24930
  if (this.stroke || this.strokeDashArray) {
24588
24931
  const origFill = this.fill;
24589
24932
  this.fill = null;
24590
- strokeSvg = [`\t<rect x="${x}" y="${y}" width="${this.width}" height="${this.height}" style="${this.getSvgStyles()}" />\n`];
24933
+ strokeSvg = [`\t<rect x="${x}" y="${y}" width="${escapeXml(this.width)}" height="${escapeXml(this.height)}" style="${this.getSvgStyles()}" />\n`];
24591
24934
  this.fill = origFill;
24592
24935
  }
24593
24936
  if (this.paintFirst !== FILL) {
@@ -25570,258 +25913,6 @@ function loadSVGFromURL(url, reviver) {
25570
25913
  });
25571
25914
  }
25572
25915
 
25573
- const ACTION_NAME$1 = MODIFY_POLY;
25574
- /**
25575
- * This function locates the controls.
25576
- * It'll be used both for drawing and for interaction.
25577
- */
25578
- const createPolyPositionHandler = pointIndex => {
25579
- return function (dim, finalMatrix, polyObject) {
25580
- const {
25581
- points,
25582
- pathOffset
25583
- } = polyObject;
25584
- return new Point(points[pointIndex]).subtract(pathOffset).transform(multiplyTransformMatrices(polyObject.getViewportTransform(), polyObject.calcTransformMatrix()));
25585
- };
25586
- };
25587
-
25588
- /**
25589
- * This function defines what the control does.
25590
- * It'll be called on every mouse move after a control has been clicked and is being dragged.
25591
- * The function receives as argument the mouse event, the current transform object
25592
- * and the current position in canvas coordinate `transform.target` is a reference to the
25593
- * current object being transformed.
25594
- */
25595
- const polyActionHandler = (eventData, transform, x, y) => {
25596
- const {
25597
- target,
25598
- pointIndex
25599
- } = transform;
25600
- const poly = target;
25601
- const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, poly.calcOwnMatrix());
25602
- poly.points[pointIndex] = mouseLocalPosition.add(poly.pathOffset);
25603
- poly.setDimensions();
25604
- poly.set('dirty', true);
25605
- return true;
25606
- };
25607
-
25608
- /**
25609
- * Keep the polygon in the same position when we change its `width`/`height`/`top`/`left`.
25610
- */
25611
- const factoryPolyActionHandler = (pointIndex, fn) => {
25612
- return function (eventData, transform, x, y) {
25613
- const poly = transform.target,
25614
- anchorPoint = new Point(poly.points[(pointIndex > 0 ? pointIndex : poly.points.length) - 1]),
25615
- anchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix()),
25616
- actionPerformed = fn(eventData, {
25617
- ...transform,
25618
- pointIndex
25619
- }, x, y);
25620
- const newAnchorPointInParentPlane = anchorPoint.subtract(poly.pathOffset).transform(poly.calcOwnMatrix());
25621
- const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
25622
- poly.left -= diff.x;
25623
- poly.top -= diff.y;
25624
- return actionPerformed;
25625
- };
25626
- };
25627
- const createPolyActionHandler = pointIndex => wrapWithFireEvent(ACTION_NAME$1, factoryPolyActionHandler(pointIndex, polyActionHandler));
25628
- function createPolyControls(arg0) {
25629
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
25630
- const controls = {};
25631
- for (let idx = 0; idx < (typeof arg0 === 'number' ? arg0 : arg0.points.length); idx++) {
25632
- controls[`p${idx}`] = new Control({
25633
- actionName: ACTION_NAME$1,
25634
- positionHandler: createPolyPositionHandler(idx),
25635
- actionHandler: createPolyActionHandler(idx),
25636
- ...options
25637
- });
25638
- }
25639
- return controls;
25640
- }
25641
-
25642
- const ACTION_NAME = 'modifyPath';
25643
- const calcPathPointPosition = (pathObject, commandIndex, pointIndex) => {
25644
- const {
25645
- path,
25646
- pathOffset
25647
- } = pathObject;
25648
- const command = path[commandIndex];
25649
- return new Point(command[pointIndex] - pathOffset.x, command[pointIndex + 1] - pathOffset.y).transform(multiplyTransformMatrices(pathObject.getViewportTransform(), pathObject.calcTransformMatrix()));
25650
- };
25651
- const movePathPoint = (pathObject, x, y, commandIndex, pointIndex) => {
25652
- const {
25653
- path,
25654
- pathOffset
25655
- } = pathObject;
25656
- const anchorCommand = path[(commandIndex > 0 ? commandIndex : path.length) - 1];
25657
- const anchorPoint = new Point(anchorCommand[pointIndex], anchorCommand[pointIndex + 1]);
25658
- const anchorPointInParentPlane = anchorPoint.subtract(pathOffset).transform(pathObject.calcOwnMatrix());
25659
- const mouseLocalPosition = sendPointToPlane(new Point(x, y), undefined, pathObject.calcOwnMatrix());
25660
- path[commandIndex][pointIndex] = mouseLocalPosition.x + pathOffset.x;
25661
- path[commandIndex][pointIndex + 1] = mouseLocalPosition.y + pathOffset.y;
25662
- pathObject.setDimensions();
25663
- const newAnchorPointInParentPlane = anchorPoint.subtract(pathObject.pathOffset).transform(pathObject.calcOwnMatrix());
25664
- const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);
25665
- pathObject.left -= diff.x;
25666
- pathObject.top -= diff.y;
25667
- pathObject.set('dirty', true);
25668
- return true;
25669
- };
25670
-
25671
- /**
25672
- * This function locates the controls.
25673
- * It'll be used both for drawing and for interaction.
25674
- */
25675
- function pathPositionHandler(dim, finalMatrix, pathObject) {
25676
- const {
25677
- commandIndex,
25678
- pointIndex
25679
- } = this;
25680
- return calcPathPointPosition(pathObject, commandIndex, pointIndex);
25681
- }
25682
-
25683
- /**
25684
- * This function defines what the control does.
25685
- * It'll be called on every mouse move after a control has been clicked and is being dragged.
25686
- * The function receives as argument the mouse event, the current transform object
25687
- * and the current position in canvas coordinate `transform.target` is a reference to the
25688
- * current object being transformed.
25689
- */
25690
- function pathActionHandler(eventData, transform, x, y) {
25691
- const {
25692
- target
25693
- } = transform;
25694
- const {
25695
- commandIndex,
25696
- pointIndex
25697
- } = this;
25698
- const actionPerformed = movePathPoint(target, x, y, commandIndex, pointIndex);
25699
- {
25700
- fireEvent(this.actionName, {
25701
- ...commonEventInfo(eventData, transform, x, y),
25702
- commandIndex,
25703
- pointIndex
25704
- });
25705
- }
25706
- return actionPerformed;
25707
- }
25708
- const indexFromPrevCommand = previousCommandType => previousCommandType === 'C' ? 5 : previousCommandType === 'Q' ? 3 : 1;
25709
- class PathPointControl extends Control {
25710
- constructor(options) {
25711
- super(options);
25712
- }
25713
- render(ctx, left, top, styleOverride, fabricObject) {
25714
- const overrides = {
25715
- ...styleOverride,
25716
- cornerColor: this.controlFill,
25717
- cornerStrokeColor: this.controlStroke,
25718
- transparentCorners: !this.controlFill
25719
- };
25720
- super.render(ctx, left, top, overrides, fabricObject);
25721
- }
25722
- }
25723
- class PathControlPointControl extends PathPointControl {
25724
- constructor(options) {
25725
- super(options);
25726
- }
25727
- render(ctx, left, top, styleOverride, fabricObject) {
25728
- const {
25729
- path
25730
- } = fabricObject;
25731
- const {
25732
- commandIndex,
25733
- pointIndex,
25734
- connectToCommandIndex,
25735
- connectToPointIndex
25736
- } = this;
25737
- ctx.save();
25738
- ctx.strokeStyle = this.controlStroke;
25739
- if (this.connectionDashArray) {
25740
- ctx.setLineDash(this.connectionDashArray);
25741
- }
25742
- const [commandType] = path[commandIndex];
25743
- const point = calcPathPointPosition(fabricObject, connectToCommandIndex, connectToPointIndex);
25744
- if (commandType === 'Q') {
25745
- // one control point connects to 2 points
25746
- const point2 = calcPathPointPosition(fabricObject, commandIndex, pointIndex + 2);
25747
- ctx.moveTo(point2.x, point2.y);
25748
- ctx.lineTo(left, top);
25749
- } else {
25750
- ctx.moveTo(left, top);
25751
- }
25752
- ctx.lineTo(point.x, point.y);
25753
- ctx.stroke();
25754
- ctx.restore();
25755
- super.render(ctx, left, top, styleOverride, fabricObject);
25756
- }
25757
- }
25758
- const createControl = (commandIndexPos, pointIndexPos, isControlPoint, options, connectToCommandIndex, connectToPointIndex) => new (isControlPoint ? PathControlPointControl : PathPointControl)({
25759
- commandIndex: commandIndexPos,
25760
- pointIndex: pointIndexPos,
25761
- actionName: ACTION_NAME,
25762
- positionHandler: pathPositionHandler,
25763
- actionHandler: pathActionHandler,
25764
- connectToCommandIndex,
25765
- connectToPointIndex,
25766
- ...options,
25767
- ...(isControlPoint ? options.controlPointStyle : options.pointStyle)
25768
- });
25769
- function createPathControls(path) {
25770
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
25771
- const controls = {};
25772
- let previousCommandType = 'M';
25773
- path.path.forEach((command, commandIndex) => {
25774
- const commandType = command[0];
25775
- if (commandType !== 'Z') {
25776
- controls[`c_${commandIndex}_${commandType}`] = createControl(commandIndex, command.length - 2, false, options);
25777
- }
25778
- switch (commandType) {
25779
- case 'C':
25780
- controls[`c_${commandIndex}_C_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex - 1, indexFromPrevCommand(previousCommandType));
25781
- controls[`c_${commandIndex}_C_CP_2`] = createControl(commandIndex, 3, true, options, commandIndex, 5);
25782
- break;
25783
- case 'Q':
25784
- controls[`c_${commandIndex}_Q_CP_1`] = createControl(commandIndex, 1, true, options, commandIndex, 3);
25785
- break;
25786
- }
25787
- previousCommandType = commandType;
25788
- });
25789
- return controls;
25790
- }
25791
-
25792
- var index = /*#__PURE__*/Object.freeze({
25793
- __proto__: null,
25794
- changeWidth: changeWidth,
25795
- createObjectDefaultControls: createObjectDefaultControls,
25796
- createPathControls: createPathControls,
25797
- createPolyActionHandler: createPolyActionHandler,
25798
- createPolyControls: createPolyControls,
25799
- createPolyPositionHandler: createPolyPositionHandler,
25800
- createResizeControls: createResizeControls,
25801
- createTextboxDefaultControls: createTextboxDefaultControls,
25802
- dragHandler: dragHandler,
25803
- factoryPolyActionHandler: factoryPolyActionHandler,
25804
- getLocalPoint: getLocalPoint,
25805
- polyActionHandler: polyActionHandler,
25806
- renderCircleControl: renderCircleControl,
25807
- renderSquareControl: renderSquareControl,
25808
- rotationStyleHandler: rotationStyleHandler,
25809
- rotationWithSnapping: rotationWithSnapping,
25810
- scaleCursorStyleHandler: scaleCursorStyleHandler,
25811
- scaleOrSkewActionName: scaleOrSkewActionName,
25812
- scaleSkewCursorStyleHandler: scaleSkewCursorStyleHandler,
25813
- scalingEqually: scalingEqually,
25814
- scalingX: scalingX,
25815
- scalingXOrSkewingY: scalingXOrSkewingY,
25816
- scalingY: scalingY,
25817
- scalingYOrSkewingX: scalingYOrSkewingX,
25818
- skewCursorStyleHandler: skewCursorStyleHandler,
25819
- skewHandlerX: skewHandlerX,
25820
- skewHandlerY: skewHandlerY,
25821
- wrapWithFireEvent: wrapWithFireEvent,
25822
- wrapWithFixedAnchor: wrapWithFixedAnchor
25823
- });
25824
-
25825
25916
  const isWebGLPipelineState = options => {
25826
25917
  return options.webgl !== undefined;
25827
25918
  };