fabric 7.0.0 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/CHANGELOG.md +21 -0
  3. package/dist/extensions/cropping_controls/croppingControls.d.ts +16 -0
  4. package/dist/extensions/cropping_controls/croppingControls.d.ts.map +1 -0
  5. package/dist/extensions/cropping_controls/croppingHandlers.d.ts +39 -0
  6. package/dist/extensions/cropping_controls/croppingHandlers.d.ts.map +1 -0
  7. package/dist/extensions/cropping_controls/enterCropMode.d.ts +7 -0
  8. package/dist/extensions/cropping_controls/enterCropMode.d.ts.map +1 -0
  9. package/dist/extensions/cropping_controls/renderCornerControl.d.ts +14 -0
  10. package/dist/extensions/cropping_controls/renderCornerControl.d.ts.map +1 -0
  11. package/dist/extensions/index.d.ts +3 -0
  12. package/dist/extensions/index.d.ts.map +1 -1
  13. package/dist/fabric.d.ts +1 -0
  14. package/dist/fabric.d.ts.map +1 -1
  15. package/dist/index.js +628 -537
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.min.js +1 -1
  18. package/dist/index.min.js.map +1 -1
  19. package/dist/index.min.mjs +1 -1
  20. package/dist/index.min.mjs.map +1 -1
  21. package/dist/index.mjs +628 -537
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/index.node.cjs +628 -537
  24. package/dist/index.node.cjs.map +1 -1
  25. package/dist/index.node.mjs +628 -537
  26. package/dist/index.node.mjs.map +1 -1
  27. package/dist/package.json.min.mjs +1 -1
  28. package/dist/package.json.mjs +1 -1
  29. package/dist/src/EventTypeDefs.d.ts +5 -0
  30. package/dist/src/EventTypeDefs.d.ts.map +1 -1
  31. package/dist/src/Pattern/Pattern.d.ts.map +1 -1
  32. package/dist/src/Pattern/Pattern.min.mjs +1 -1
  33. package/dist/src/Pattern/Pattern.min.mjs.map +1 -1
  34. package/dist/src/Pattern/Pattern.mjs +2 -1
  35. package/dist/src/Pattern/Pattern.mjs.map +1 -1
  36. package/dist/src/Shadow.d.ts +1 -1
  37. package/dist/src/Shadow.d.ts.map +1 -1
  38. package/dist/src/Shadow.min.mjs +1 -1
  39. package/dist/src/Shadow.min.mjs.map +1 -1
  40. package/dist/src/Shadow.mjs +5 -4
  41. package/dist/src/Shadow.mjs.map +1 -1
  42. package/dist/src/canvas/CanvasOptions.d.ts.map +1 -1
  43. package/dist/src/canvas/CanvasOptions.min.mjs.map +1 -1
  44. package/dist/src/canvas/CanvasOptions.mjs.map +1 -1
  45. package/dist/src/canvas/SelectableCanvas.d.ts +2 -0
  46. package/dist/src/canvas/SelectableCanvas.d.ts.map +1 -1
  47. package/dist/src/canvas/SelectableCanvas.min.mjs +1 -1
  48. package/dist/src/canvas/SelectableCanvas.min.mjs.map +1 -1
  49. package/dist/src/canvas/SelectableCanvas.mjs +33 -13
  50. package/dist/src/canvas/SelectableCanvas.mjs.map +1 -1
  51. package/dist/src/canvas/StaticCanvas.d.ts.map +1 -1
  52. package/dist/src/canvas/StaticCanvas.min.mjs +1 -1
  53. package/dist/src/canvas/StaticCanvas.min.mjs.map +1 -1
  54. package/dist/src/canvas/StaticCanvas.mjs +3 -1
  55. package/dist/src/canvas/StaticCanvas.mjs.map +1 -1
  56. package/dist/src/canvas/StaticCanvasOptions.d.ts.map +1 -1
  57. package/dist/src/canvas/StaticCanvasOptions.min.mjs.map +1 -1
  58. package/dist/src/canvas/StaticCanvasOptions.mjs.map +1 -1
  59. package/dist/src/constants.d.ts +1 -0
  60. package/dist/src/constants.d.ts.map +1 -1
  61. package/dist/src/constants.min.mjs.map +1 -1
  62. package/dist/src/constants.mjs.map +1 -1
  63. package/dist/src/controls/Control.d.ts +22 -1
  64. package/dist/src/controls/Control.d.ts.map +1 -1
  65. package/dist/src/controls/Control.min.mjs +1 -1
  66. package/dist/src/controls/Control.min.mjs.map +1 -1
  67. package/dist/src/controls/Control.mjs +45 -1
  68. package/dist/src/controls/Control.mjs.map +1 -1
  69. package/dist/src/controls/changeWidth.d.ts +22 -0
  70. package/dist/src/controls/changeWidth.d.ts.map +1 -1
  71. package/dist/src/controls/changeWidth.min.mjs +1 -1
  72. package/dist/src/controls/changeWidth.min.mjs.map +1 -1
  73. package/dist/src/controls/changeWidth.mjs +46 -18
  74. package/dist/src/controls/changeWidth.mjs.map +1 -1
  75. package/dist/src/controls/controlRendering.d.ts.map +1 -1
  76. package/dist/src/controls/controlRendering.min.mjs +1 -1
  77. package/dist/src/controls/controlRendering.min.mjs.map +1 -1
  78. package/dist/src/controls/controlRendering.mjs +18 -34
  79. package/dist/src/controls/controlRendering.mjs.map +1 -1
  80. package/dist/src/controls/index.d.ts +2 -1
  81. package/dist/src/controls/index.d.ts.map +1 -1
  82. package/dist/src/controls/index.min.mjs +1 -1
  83. package/dist/src/controls/index.mjs +1 -1
  84. package/dist/src/gradient/Gradient.d.ts.map +1 -1
  85. package/dist/src/gradient/Gradient.min.mjs +1 -1
  86. package/dist/src/gradient/Gradient.min.mjs.map +1 -1
  87. package/dist/src/gradient/Gradient.mjs +19 -6
  88. package/dist/src/gradient/Gradient.mjs.map +1 -1
  89. package/dist/src/shapes/Circle.d.ts.map +1 -1
  90. package/dist/src/shapes/Circle.min.mjs +1 -1
  91. package/dist/src/shapes/Circle.min.mjs.map +1 -1
  92. package/dist/src/shapes/Circle.mjs +10 -7
  93. package/dist/src/shapes/Circle.mjs.map +1 -1
  94. package/dist/src/shapes/Ellipse.d.ts.map +1 -1
  95. package/dist/src/shapes/Ellipse.min.mjs +1 -1
  96. package/dist/src/shapes/Ellipse.min.mjs.map +1 -1
  97. package/dist/src/shapes/Ellipse.mjs +2 -1
  98. package/dist/src/shapes/Ellipse.mjs.map +1 -1
  99. package/dist/src/shapes/Group.d.ts.map +1 -1
  100. package/dist/src/shapes/Group.min.mjs +1 -1
  101. package/dist/src/shapes/Group.min.mjs.map +1 -1
  102. package/dist/src/shapes/Group.mjs +2 -1
  103. package/dist/src/shapes/Group.mjs.map +1 -1
  104. package/dist/src/shapes/IText/IText.d.ts.map +1 -1
  105. package/dist/src/shapes/IText/IText.min.mjs.map +1 -1
  106. package/dist/src/shapes/IText/IText.mjs.map +1 -1
  107. package/dist/src/shapes/Image.d.ts +1 -1
  108. package/dist/src/shapes/Image.d.ts.map +1 -1
  109. package/dist/src/shapes/Image.min.mjs +1 -1
  110. package/dist/src/shapes/Image.min.mjs.map +1 -1
  111. package/dist/src/shapes/Image.mjs +4 -3
  112. package/dist/src/shapes/Image.mjs.map +1 -1
  113. package/dist/src/shapes/Line.d.ts.map +1 -1
  114. package/dist/src/shapes/Line.min.mjs +1 -1
  115. package/dist/src/shapes/Line.min.mjs.map +1 -1
  116. package/dist/src/shapes/Line.mjs +6 -10
  117. package/dist/src/shapes/Line.mjs.map +1 -1
  118. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
  119. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs +1 -1
  120. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs.map +1 -1
  121. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs +5 -4
  122. package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs.map +1 -1
  123. package/dist/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
  124. package/dist/src/shapes/Object/InteractiveObject.min.mjs.map +1 -1
  125. package/dist/src/shapes/Object/InteractiveObject.mjs.map +1 -1
  126. package/dist/src/shapes/Object/Object.d.ts.map +1 -1
  127. package/dist/src/shapes/Object/Object.min.mjs +1 -1
  128. package/dist/src/shapes/Object/Object.min.mjs.map +1 -1
  129. package/dist/src/shapes/Object/Object.mjs +3 -0
  130. package/dist/src/shapes/Object/Object.mjs.map +1 -1
  131. package/dist/src/shapes/Object/ObjectGeometry.min.mjs +1 -1
  132. package/dist/src/shapes/Object/ObjectGeometry.min.mjs.map +1 -1
  133. package/dist/src/shapes/Object/ObjectGeometry.mjs +1 -1
  134. package/dist/src/shapes/Object/ObjectGeometry.mjs.map +1 -1
  135. package/dist/src/shapes/Object/types/FabricObjectProps.d.ts.map +1 -1
  136. package/dist/src/shapes/Object/types/ObjectProps.d.ts.map +1 -1
  137. package/dist/src/shapes/Path.d.ts.map +1 -1
  138. package/dist/src/shapes/Path.min.mjs.map +1 -1
  139. package/dist/src/shapes/Path.mjs +1 -2
  140. package/dist/src/shapes/Path.mjs.map +1 -1
  141. package/dist/src/shapes/Polyline.d.ts.map +1 -1
  142. package/dist/src/shapes/Polyline.min.mjs +1 -1
  143. package/dist/src/shapes/Polyline.min.mjs.map +1 -1
  144. package/dist/src/shapes/Polyline.mjs +10 -6
  145. package/dist/src/shapes/Polyline.mjs.map +1 -1
  146. package/dist/src/shapes/Rect.d.ts.map +1 -1
  147. package/dist/src/shapes/Rect.min.mjs +1 -1
  148. package/dist/src/shapes/Rect.min.mjs.map +1 -1
  149. package/dist/src/shapes/Rect.mjs +2 -1
  150. package/dist/src/shapes/Rect.mjs.map +1 -1
  151. package/dist/src/shapes/Text/StyledText.d.ts.map +1 -1
  152. package/dist/src/shapes/Text/StyledText.min.mjs.map +1 -1
  153. package/dist/src/shapes/Text/StyledText.mjs +0 -3
  154. package/dist/src/shapes/Text/StyledText.mjs.map +1 -1
  155. package/dist/src/shapes/Text/Text.d.ts.map +1 -1
  156. package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
  157. package/dist/src/shapes/Text/Text.mjs.map +1 -1
  158. package/dist/src/shapes/Text/TextSVGExportMixin.d.ts.map +1 -1
  159. package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs +1 -1
  160. package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs.map +1 -1
  161. package/dist/src/shapes/Text/TextSVGExportMixin.mjs +5 -6
  162. package/dist/src/shapes/Text/TextSVGExportMixin.mjs.map +1 -1
  163. package/dist/src/shapes/Textbox.d.ts.map +1 -1
  164. package/dist/src/shapes/Textbox.min.mjs.map +1 -1
  165. package/dist/src/shapes/Textbox.mjs.map +1 -1
  166. package/dist/src/shapes/Triangle.d.ts.map +1 -1
  167. package/dist/src/shapes/Triangle.min.mjs.map +1 -1
  168. package/dist/src/shapes/Triangle.mjs.map +1 -1
  169. package/dist/src/util/lang_string.d.ts +1 -1
  170. package/dist/src/util/lang_string.d.ts.map +1 -1
  171. package/dist/src/util/lang_string.min.mjs +1 -1
  172. package/dist/src/util/lang_string.min.mjs.map +1 -1
  173. package/dist/src/util/lang_string.mjs +1 -1
  174. package/dist/src/util/lang_string.mjs.map +1 -1
  175. package/dist/src/util/misc/svgParsing.d.ts.map +1 -1
  176. package/dist/src/util/misc/svgParsing.min.mjs +1 -1
  177. package/dist/src/util/misc/svgParsing.min.mjs.map +1 -1
  178. package/dist/src/util/misc/svgParsing.mjs +2 -1
  179. package/dist/src/util/misc/svgParsing.mjs.map +1 -1
  180. package/dist-extensions/cropping_controls/croppingControls.mjs +140 -0
  181. package/dist-extensions/cropping_controls/croppingControls.mjs.map +1 -0
  182. package/dist-extensions/cropping_controls/croppingHandlers.mjs +228 -0
  183. package/dist-extensions/cropping_controls/croppingHandlers.mjs.map +1 -0
  184. package/dist-extensions/cropping_controls/enterCropMode.mjs +38 -0
  185. package/dist-extensions/cropping_controls/enterCropMode.mjs.map +1 -0
  186. package/dist-extensions/cropping_controls/renderCornerControl.mjs +45 -0
  187. package/dist-extensions/cropping_controls/renderCornerControl.mjs.map +1 -0
  188. package/dist-extensions/extensions/cropping_controls/croppingControls.d.ts +16 -0
  189. package/dist-extensions/extensions/cropping_controls/croppingControls.d.ts.map +1 -0
  190. package/dist-extensions/extensions/cropping_controls/croppingHandlers.d.ts +39 -0
  191. package/dist-extensions/extensions/cropping_controls/croppingHandlers.d.ts.map +1 -0
  192. package/dist-extensions/extensions/cropping_controls/enterCropMode.d.ts +7 -0
  193. package/dist-extensions/extensions/cropping_controls/enterCropMode.d.ts.map +1 -0
  194. package/dist-extensions/extensions/cropping_controls/renderCornerControl.d.ts +14 -0
  195. package/dist-extensions/extensions/cropping_controls/renderCornerControl.d.ts.map +1 -0
  196. package/dist-extensions/extensions/index.d.ts +3 -0
  197. package/dist-extensions/extensions/index.d.ts.map +1 -1
  198. package/dist-extensions/fabric-extensions.min.js +1 -1
  199. package/dist-extensions/fabric-extensions.min.js.map +1 -1
  200. package/dist-extensions/fabric.d.ts +1 -0
  201. package/dist-extensions/fabric.d.ts.map +1 -1
  202. package/dist-extensions/index.mjs +3 -0
  203. package/dist-extensions/index.mjs.map +1 -1
  204. package/dist-extensions/src/EventTypeDefs.d.ts +5 -0
  205. package/dist-extensions/src/EventTypeDefs.d.ts.map +1 -1
  206. package/dist-extensions/src/Pattern/Pattern.d.ts.map +1 -1
  207. package/dist-extensions/src/Shadow.d.ts +1 -1
  208. package/dist-extensions/src/Shadow.d.ts.map +1 -1
  209. package/dist-extensions/src/canvas/CanvasOptions.d.ts.map +1 -1
  210. package/dist-extensions/src/canvas/SelectableCanvas.d.ts +2 -0
  211. package/dist-extensions/src/canvas/SelectableCanvas.d.ts.map +1 -1
  212. package/dist-extensions/src/canvas/StaticCanvas.d.ts.map +1 -1
  213. package/dist-extensions/src/canvas/StaticCanvasOptions.d.ts.map +1 -1
  214. package/dist-extensions/src/constants.d.ts +1 -0
  215. package/dist-extensions/src/constants.d.ts.map +1 -1
  216. package/dist-extensions/src/controls/Control.d.ts +22 -1
  217. package/dist-extensions/src/controls/Control.d.ts.map +1 -1
  218. package/dist-extensions/src/controls/changeWidth.d.ts +22 -0
  219. package/dist-extensions/src/controls/changeWidth.d.ts.map +1 -1
  220. package/dist-extensions/src/controls/controlRendering.d.ts.map +1 -1
  221. package/dist-extensions/src/controls/index.d.ts +2 -1
  222. package/dist-extensions/src/controls/index.d.ts.map +1 -1
  223. package/dist-extensions/src/gradient/Gradient.d.ts.map +1 -1
  224. package/dist-extensions/src/shapes/Circle.d.ts.map +1 -1
  225. package/dist-extensions/src/shapes/Ellipse.d.ts.map +1 -1
  226. package/dist-extensions/src/shapes/Group.d.ts.map +1 -1
  227. package/dist-extensions/src/shapes/IText/IText.d.ts.map +1 -1
  228. package/dist-extensions/src/shapes/Image.d.ts.map +1 -1
  229. package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
  230. package/dist-extensions/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
  231. package/dist-extensions/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
  232. package/dist-extensions/src/shapes/Object/Object.d.ts.map +1 -1
  233. package/dist-extensions/src/shapes/Object/types/FabricObjectProps.d.ts.map +1 -1
  234. package/dist-extensions/src/shapes/Object/types/ObjectProps.d.ts.map +1 -1
  235. package/dist-extensions/src/shapes/Path.d.ts +1 -1
  236. package/dist-extensions/src/shapes/Path.d.ts.map +1 -1
  237. package/dist-extensions/src/shapes/Polyline.d.ts.map +1 -1
  238. package/dist-extensions/src/shapes/Rect.d.ts.map +1 -1
  239. package/dist-extensions/src/shapes/Text/StyledText.d.ts.map +1 -1
  240. package/dist-extensions/src/shapes/Text/Text.d.ts.map +1 -1
  241. package/dist-extensions/src/shapes/Text/TextSVGExportMixin.d.ts.map +1 -1
  242. package/dist-extensions/src/shapes/Textbox.d.ts.map +1 -1
  243. package/dist-extensions/src/shapes/Triangle.d.ts.map +1 -1
  244. package/dist-extensions/src/util/lang_string.d.ts +1 -1
  245. package/dist-extensions/src/util/lang_string.d.ts.map +1 -1
  246. package/dist-extensions/src/util/misc/svgParsing.d.ts.map +1 -1
  247. package/eslint.config.mjs +2 -0
  248. package/extensions/cropping_controls/croppingControls.spec.ts +115 -0
  249. package/extensions/cropping_controls/croppingControls.ts +150 -0
  250. package/extensions/cropping_controls/croppingHandlers.spec.ts +579 -0
  251. package/extensions/cropping_controls/croppingHandlers.ts +285 -0
  252. package/extensions/cropping_controls/enterCropMode.ts +30 -0
  253. package/extensions/cropping_controls/renderCornerControl.ts +53 -0
  254. package/extensions/index.ts +9 -0
  255. package/fabric.ts +1 -0
  256. package/package.json +17 -8
  257. package/src/ClassRegistry.spec.ts +18 -19
  258. package/src/EventTypeDefs.ts +15 -11
  259. package/src/Pattern/Pattern.spec.ts +12 -0
  260. package/src/Pattern/Pattern.ts +3 -2
  261. package/src/Shadow.ts +9 -8
  262. package/src/brushes/PencilBrush.spec.ts +11 -11
  263. package/src/canvas/Canvas-dispose.spec.ts +8 -7
  264. package/src/canvas/Canvas.spec.ts +27 -29
  265. package/src/canvas/CanvasOptions.ts +2 -1
  266. package/src/canvas/SelectableCanvas.ts +38 -15
  267. package/src/canvas/StaticCanvas.spec.ts +20 -0
  268. package/src/canvas/StaticCanvas.ts +7 -4
  269. package/src/canvas/StaticCanvasOptions.ts +1 -3
  270. package/src/constants.ts +1 -0
  271. package/src/controls/Control.spec.ts +102 -0
  272. package/src/controls/Control.ts +71 -2
  273. package/src/controls/changeHeight.spec.ts +147 -0
  274. package/src/controls/changeWidth.ts +68 -35
  275. package/src/controls/controlRendering.ts +20 -48
  276. package/src/controls/index.ts +7 -1
  277. package/src/gradient/Gradient.spec.ts +101 -46
  278. package/src/gradient/Gradient.ts +27 -14
  279. package/src/shapes/Circle.spec.ts +10 -39
  280. package/src/shapes/Circle.ts +11 -11
  281. package/src/shapes/Ellipse.spec.ts +8 -37
  282. package/src/shapes/Ellipse.ts +7 -7
  283. package/src/shapes/Group.ts +3 -3
  284. package/src/shapes/IText/IText-click-behavior.spec.ts +36 -49
  285. package/src/shapes/IText/IText.ts +5 -6
  286. package/src/shapes/IText/ITextKeyBehavior.test.ts +0 -1
  287. package/src/shapes/IText/__snapshots__/ITextBehavior.test.ts.snap +6 -6
  288. package/src/shapes/Image.spec.ts +17 -33
  289. package/src/shapes/Image.ts +15 -11
  290. package/src/shapes/Line.spec.ts +4 -30
  291. package/src/shapes/Line.ts +11 -16
  292. package/src/shapes/Object/FabricObjectSVGExportMixin.ts +11 -4
  293. package/src/shapes/Object/InteractiveObject.ts +4 -4
  294. package/src/shapes/Object/Object.ts +6 -5
  295. package/src/shapes/Object/ObjectGeometry.spec.ts +15 -0
  296. package/src/shapes/Object/ObjectGeometry.ts +1 -1
  297. package/src/shapes/Object/objectSvgExport.spec.ts +112 -0
  298. package/src/shapes/Object/types/FabricObjectProps.ts +1 -4
  299. package/src/shapes/Object/types/ObjectProps.ts +1 -3
  300. package/src/shapes/Path.spec.ts +4 -27
  301. package/src/shapes/Path.ts +2 -4
  302. package/src/shapes/Polygon.spec.ts +4 -31
  303. package/src/shapes/Polyline.spec.ts +4 -31
  304. package/src/shapes/Polyline.ts +11 -12
  305. package/src/shapes/Rect.spec.ts +25 -33
  306. package/src/shapes/Rect.ts +7 -7
  307. package/src/shapes/Text/StyledText.ts +0 -3
  308. package/src/shapes/Text/Text.spec.ts +3 -32
  309. package/src/shapes/Text/Text.ts +5 -6
  310. package/src/shapes/Text/TextSVGExportMixin.spec.ts +9 -0
  311. package/src/shapes/Text/TextSVGExportMixin.ts +14 -16
  312. package/src/shapes/Text/__snapshots__/Text.spec.ts.snap +1 -1
  313. package/src/shapes/Text/__snapshots__/TextSVGExportMixin.spec.ts.snap +1 -1
  314. package/src/shapes/Textbox.spec.ts +5 -5
  315. package/src/shapes/Textbox.ts +6 -5
  316. package/src/shapes/Triangle.ts +4 -4
  317. package/src/shapes/__snapshots__/Image.spec.ts.snap +4 -4
  318. package/src/shapes/__snapshots__/Textbox.spec.ts.snap +5 -5
  319. package/src/util/lang_string.ts +3 -2
  320. package/src/util/misc/svgParsing.ts +2 -1
  321. package/tsconfig.spec.json +1 -0
  322. package/vitest.config.ts +12 -2
  323. package/vitest.extend.ts +6 -2
package/src/Shadow.ts CHANGED
@@ -6,6 +6,7 @@ import { Point } from './Point';
6
6
  import type { FabricObject } from './shapes/Object/FabricObject';
7
7
  import type { TClassProperties } from './typedefs';
8
8
  import { uid } from './util/internals/uid';
9
+ import { escapeXml } from './util/lang_string';
9
10
  import { pickBy } from './util/misc/pick';
10
11
  import { degreesToRadians } from './util/misc/radiansDegreesConversion';
11
12
  import { toFixed } from './util/misc/toFixed';
@@ -27,7 +28,6 @@ import { rotateVector } from './util/misc/vectors';
27
28
 
28
29
  (?:$|\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.
29
30
  */
30
- // eslint-disable-next-line max-len
31
31
 
32
32
  const shadowOffsetRegex = '(-?\\d+(?:\\.\\d*)?(?:px)?(?:\\s?|$))?';
33
33
 
@@ -105,7 +105,7 @@ export class Shadow {
105
105
  */
106
106
  declare nonScaling: boolean;
107
107
 
108
- declare id: number;
108
+ declare id: number | string;
109
109
 
110
110
  static ownDefaults = shadowDefaultValues;
111
111
 
@@ -163,6 +163,7 @@ export class Shadow {
163
163
  degreesToRadians(-object.angle),
164
164
  ),
165
165
  BLUR_BOX = 20,
166
+ NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS,
166
167
  color = new Color(this.color);
167
168
  let fBoxX = 40,
168
169
  fBoxY = 40;
@@ -173,14 +174,14 @@ export class Shadow {
173
174
  fBoxX =
174
175
  toFixed(
175
176
  (Math.abs(offset.x) + this.blur) / object.width,
176
- config.NUM_FRACTION_DIGITS,
177
+ NUM_FRACTION_DIGITS,
177
178
  ) *
178
179
  100 +
179
180
  BLUR_BOX;
180
181
  fBoxY =
181
182
  toFixed(
182
183
  (Math.abs(offset.y) + this.blur) / object.height,
183
- config.NUM_FRACTION_DIGITS,
184
+ NUM_FRACTION_DIGITS,
184
185
  ) *
185
186
  100 +
186
187
  BLUR_BOX;
@@ -192,19 +193,19 @@ export class Shadow {
192
193
  offset.y *= -1;
193
194
  }
194
195
 
195
- return `<filter id="SVGID_${this.id}" y="-${fBoxY}%" height="${
196
+ return `<filter id="SVGID_${escapeXml(this.id)}" y="-${fBoxY}%" height="${
196
197
  100 + 2 * fBoxY
197
198
  }%" x="-${fBoxX}%" width="${
198
199
  100 + 2 * fBoxX
199
200
  }%" >\n\t<feGaussianBlur in="SourceAlpha" stdDeviation="${toFixed(
200
201
  this.blur ? this.blur / 2 : 0,
201
- config.NUM_FRACTION_DIGITS,
202
+ NUM_FRACTION_DIGITS,
202
203
  )}"></feGaussianBlur>\n\t<feOffset dx="${toFixed(
203
204
  offset.x,
204
- config.NUM_FRACTION_DIGITS,
205
+ NUM_FRACTION_DIGITS,
205
206
  )}" dy="${toFixed(
206
207
  offset.y,
207
- config.NUM_FRACTION_DIGITS,
208
+ NUM_FRACTION_DIGITS,
208
209
  )}" 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`;
209
210
  }
210
211
 
@@ -2,9 +2,9 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
2
  import { Canvas } from '../canvas/Canvas';
3
3
  import { PencilBrush } from './PencilBrush';
4
4
  import { parsePath } from '../util';
5
- import type { TPointerEvent } from '../EventTypeDefs';
6
5
  import { Path } from '../shapes/Path';
7
6
  import { Point } from '../Point';
7
+ import { createPointerEvent } from '../../test/utils';
8
8
 
9
9
  describe('PencilBrush', () => {
10
10
  let canvas: Canvas;
@@ -55,12 +55,12 @@ describe('PencilBrush', () => {
55
55
 
56
56
  it('draws a point correctly', () => {
57
57
  const brush = new PencilBrush(canvas);
58
- const e = { target: canvas.upperCanvasEl } as unknown as TPointerEvent;
59
- const pointer = canvas.getScenePoint({
60
- ...e,
58
+ const e = createPointerEvent({
59
+ target: canvas.upperCanvasEl,
61
60
  clientX: 10,
62
61
  clientY: 10,
63
62
  });
63
+ const pointer = canvas.getScenePoint(e);
64
64
  brush.onMouseDown(pointer, { e });
65
65
  // @ts-expect-error -- protected
66
66
  const pathData = brush.convertPointsToSVGPath(brush._points);
@@ -72,12 +72,12 @@ describe('PencilBrush', () => {
72
72
 
73
73
  it('handles multiple coincident points', () => {
74
74
  const brush = new PencilBrush(canvas);
75
- const e = { target: canvas.upperCanvasEl } as unknown as TPointerEvent;
76
- const pointer = canvas.getScenePoint({
77
- ...e,
75
+ const e = createPointerEvent({
76
+ target: canvas.upperCanvasEl,
78
77
  clientX: 10,
79
78
  clientY: 10,
80
79
  });
80
+ const pointer = canvas.getScenePoint(e);
81
81
  brush.onMouseDown(pointer, { e });
82
82
  brush.onMouseMove(pointer, { e });
83
83
  brush.onMouseMove(pointer, { e });
@@ -95,7 +95,7 @@ describe('PencilBrush', () => {
95
95
 
96
96
  it('handles multiple non-coincident points', () => {
97
97
  const brush = new PencilBrush(canvas);
98
- const e = { target: canvas.upperCanvasEl } as unknown as TPointerEvent;
98
+ const e = createPointerEvent({ target: canvas.upperCanvasEl });
99
99
  const pointer = canvas.getScenePoint({
100
100
  ...e,
101
101
  clientX: 10,
@@ -129,7 +129,7 @@ describe('PencilBrush', () => {
129
129
 
130
130
  it('handles points outside canvas', () => {
131
131
  const brush = new PencilBrush(canvas);
132
- const e = { target: canvas.upperCanvasEl } as unknown as TPointerEvent;
132
+ const e = createPointerEvent({ target: canvas.upperCanvasEl });
133
133
  const pointer = canvas.getScenePoint({
134
134
  ...e,
135
135
  clientX: 10,
@@ -177,7 +177,7 @@ describe('PencilBrush', () => {
177
177
  it('limits points to canvas size when limitedToCanvasSize is true', () => {
178
178
  const brush = new PencilBrush(canvas);
179
179
  brush.limitedToCanvasSize = true;
180
- const e = { target: canvas.upperCanvasEl } as unknown as TPointerEvent;
180
+ const e = createPointerEvent({ target: canvas.upperCanvasEl });
181
181
  const pointer = canvas.getScenePoint({
182
182
  ...e,
183
183
  clientX: 10,
@@ -237,7 +237,7 @@ describe('PencilBrush', () => {
237
237
  });
238
238
 
239
239
  const brush = new PencilBrush(canvas);
240
- const e = { target: canvas.upperCanvasEl } as unknown as TPointerEvent;
240
+ const e = createPointerEvent({ target: canvas.upperCanvasEl });
241
241
  const pointer = canvas.getScenePoint({
242
242
  ...e,
243
243
  clientX: 10,
@@ -195,23 +195,24 @@ describe('Canvas dispose', () => {
195
195
 
196
196
  it('dispose edge case: `toCanvasElement` after dispose', async () => {
197
197
  const canvas = new CanvasClass(undefined, { renderOnAddRemove: false });
198
- const testImageData = (colorByteVal: number) => {
198
+ const getAlphaValues = () => {
199
199
  return canvas
200
200
  .toCanvasElement()
201
201
  .getContext('2d')!
202
- .getImageData(0, 0, 20, 20)
203
- .data.filter((_, i) => i % 4 === 0)
204
- .every((x) => x === colorByteVal);
202
+ .getImageData(10, 10, 20, 20)
203
+ .data.filter((_, i) => i % 4 === 3);
205
204
  };
205
+ const hasOpaquePixels = () => getAlphaValues().some((x) => x === 255);
206
+ const isFullyTransparent = () => getAlphaValues().every((x) => x === 0);
206
207
  canvas.add(
207
208
  makeRect({ fill: 'red', width: 20, height: 20, top: 10, left: 10 }),
208
209
  );
209
- expect(testImageData(255), 'control').toBeTruthy();
210
+ expect(hasOpaquePixels(), 'control').toBeTruthy();
210
211
  canvas.disposed = true;
211
- expect(testImageData(255), 'should render canvas').toBeTruthy();
212
+ expect(hasOpaquePixels(), 'should render canvas').toBeTruthy();
212
213
  canvas.destroyed = true;
213
214
  expect(
214
- testImageData(0),
215
+ isFullyTransparent(),
215
216
  'should have disabled canvas rendering',
216
217
  ).toBeTruthy();
217
218
  canvas.destroyed = false;
@@ -10,6 +10,7 @@ import type {
10
10
  MultiSelectionStacking,
11
11
  TPointerEvent,
12
12
  } from '../../fabric';
13
+ import { createPointerEvent } from '../../test/utils';
13
14
  import {
14
15
  ActiveSelection,
15
16
  Circle,
@@ -862,9 +863,7 @@ describe('Canvas', () => {
862
863
  deltaX: 5,
863
864
  deltaY: 5,
864
865
  });
865
- canvas._onMouseUp({
866
- target: canvas.upperCanvasEl,
867
- } as unknown as TPointerEvent);
866
+ canvas._onMouseUp(createPointerEvent({ target: canvas.upperCanvasEl }));
868
867
 
869
868
  expect(fired, 'event fired for each of 3 rects').toBe(3);
870
869
  });
@@ -885,9 +884,7 @@ describe('Canvas', () => {
885
884
  deltaX: 5,
886
885
  deltaY: 5,
887
886
  });
888
- canvas._onMouseUp({
889
- target: canvas.upperCanvasEl,
890
- } as unknown as TPointerEvent);
887
+ canvas._onMouseUp(createPointerEvent({ target: canvas.upperCanvasEl }));
891
888
 
892
889
  expect(isFired, 'selection created fired').toBe(true);
893
890
  expect(
@@ -917,9 +914,7 @@ describe('Canvas', () => {
917
914
  deltaX: 5,
918
915
  deltaY: 5,
919
916
  });
920
- canvas._onMouseUp({
921
- target: canvas.upperCanvasEl,
922
- } as unknown as TPointerEvent);
917
+ canvas._onMouseUp(createPointerEvent({ target: canvas.upperCanvasEl }));
923
918
 
924
919
  expect(isFired, 'selection:created fired').toBe(true);
925
920
  expect(canvas.getActiveObject(), 'rect1 is set as activeObject').toBe(
@@ -1115,18 +1110,22 @@ describe('Canvas', () => {
1115
1110
  const rect = makeRect({ left: 0, top: 0 });
1116
1111
  canvas.add(rect);
1117
1112
 
1118
- const { target } = canvas.findTarget({
1119
- clientX: 5,
1120
- clientY: 5,
1121
- target: canvas.upperCanvasEl,
1122
- } as unknown as TPointerEvent);
1113
+ const { target } = canvas.findTarget(
1114
+ createPointerEvent({
1115
+ clientX: 5,
1116
+ clientY: 5,
1117
+ target: canvas.upperCanvasEl,
1118
+ }),
1119
+ );
1123
1120
  expect(target, 'Should return the rect').toBe(rect);
1124
1121
 
1125
- const { target: target2 } = canvas.findTarget({
1126
- clientX: 30,
1127
- clientY: 30,
1128
- target: canvas.upperCanvasEl,
1129
- } as unknown as TPointerEvent);
1122
+ const { target: target2 } = canvas.findTarget(
1123
+ createPointerEvent({
1124
+ clientX: 30,
1125
+ clientY: 30,
1126
+ target: canvas.upperCanvasEl,
1127
+ }),
1128
+ );
1130
1129
  expect(target2, 'Should not find target').toBeUndefined();
1131
1130
 
1132
1131
  canvas.remove(rect);
@@ -1897,16 +1896,15 @@ describe('Canvas', () => {
1897
1896
  });
1898
1897
 
1899
1898
  it('cleans up transform when discarding active object', () => {
1900
- const e = {
1899
+ const e = createPointerEvent({
1901
1900
  clientX: 5,
1902
1901
  clientY: 5,
1903
- which: 1,
1904
1902
  target: canvas.upperCanvasEl,
1905
- };
1903
+ });
1906
1904
  const target = makeRect();
1907
1905
  canvas.add(target);
1908
1906
  canvas.setActiveObject(target);
1909
- canvas._setupCurrentTransform(e as unknown as TPointerEvent, target, true);
1907
+ canvas._setupCurrentTransform(e, target, true);
1910
1908
  expect(canvas._currentTransform, 'transform should be set').toBeTruthy();
1911
1909
 
1912
1910
  target.isMoving = true;
@@ -2221,11 +2219,11 @@ describe('Canvas', () => {
2221
2219
  const rect = new Rect({ left: 100, top: 100, width: 50, height: 50 });
2222
2220
  canvas.add(rect);
2223
2221
  const canvasOffset = canvas.calcOffset();
2224
- let eventStub = {
2222
+ let eventStub = createPointerEvent({
2225
2223
  clientX: canvasOffset.left + 100,
2226
2224
  clientY: canvasOffset.top + 100,
2227
2225
  target: canvas.upperCanvasEl,
2228
- } as unknown as TPointerEvent;
2226
+ });
2229
2227
  canvas.setActiveObject(rect);
2230
2228
  const targetCorner = rect.findControl(canvas.getViewportPoint(eventStub));
2231
2229
  rect.__corner = targetCorner ? targetCorner.key : undefined;
@@ -2237,11 +2235,11 @@ describe('Canvas', () => {
2237
2235
  expect(t.originX, 'no origin change for drag').toBe(rect.originX);
2238
2236
  expect(t.originY, 'no origin change for drag').toBe(rect.originY);
2239
2237
 
2240
- eventStub = {
2238
+ eventStub = createPointerEvent({
2241
2239
  clientX: canvasOffset.left + rect.oCoords.tl.corner.tl.x + 1,
2242
2240
  clientY: canvasOffset.top + rect.oCoords.tl.corner.tl.y + 1,
2243
2241
  target: canvas.upperCanvasEl,
2244
- } as unknown as TPointerEvent;
2242
+ });
2245
2243
  rect.__corner = rect.findControl(canvas.getViewportPoint(eventStub))!.key;
2246
2244
  canvas._setupCurrentTransform(eventStub, rect, false);
2247
2245
  t = canvas._currentTransform!;
@@ -2264,12 +2262,12 @@ describe('Canvas', () => {
2264
2262
  expect(t.originY, 'origin in opposite direction').toBe('bottom');
2265
2263
  expect(t.shiftKey, 'shift was not pressed').toBe(undefined);
2266
2264
 
2267
- eventStub = {
2265
+ eventStub = createPointerEvent({
2268
2266
  clientX: canvasOffset.left + rect.left - 2 - rect.width / 2,
2269
2267
  clientY: canvasOffset.top + rect.top,
2270
2268
  target: canvas.upperCanvasEl,
2271
2269
  shiftKey: true,
2272
- } as unknown as TPointerEvent;
2270
+ });
2273
2271
  rect.__corner = rect.findControl(canvas.getViewportPoint(eventStub))!.key;
2274
2272
  canvas._setupCurrentTransform(eventStub, rect, alreadySelected);
2275
2273
  t = canvas._currentTransform!;
@@ -227,7 +227,8 @@ export interface CanvasEventsOptions {
227
227
  }
228
228
 
229
229
  export interface CanvasOptions
230
- extends StaticCanvasOptions,
230
+ extends
231
+ StaticCanvasOptions,
231
232
  CanvasTransformOptions,
232
233
  CanvasSelectionOptions,
233
234
  CanvasCursorOptions,
@@ -1,4 +1,3 @@
1
- import { dragHandler } from '../controls/drag';
2
1
  import { getActionFromCorner } from '../controls/util';
3
2
  import { Point } from '../Point';
4
3
  import { FabricObject } from '../shapes/Object/FabricObject';
@@ -51,6 +50,8 @@ import type { CanvasOptions } from './CanvasOptions';
51
50
  import { canvasDefaults } from './CanvasOptions';
52
51
  import { Intersection } from '../Intersection';
53
52
  import { isActiveSelection } from '../util/typeAssertions';
53
+ import { dragHandler } from '../controls';
54
+ import { type FabricImage } from '../shapes/Image';
54
55
 
55
56
  export type TargetsInfo = {
56
57
  target?: FabricObject;
@@ -564,6 +565,8 @@ export class SelectableCanvas<EventSpec extends CanvasEvents = CanvasEvents>
564
565
  * Given the control clicked, determine the origin of the transform.
565
566
  * This is bad because controls can totally have custom names
566
567
  * should disappear before release 4.0
568
+ * Fabric 7.1, jan 2026 we are still using this.
569
+ * Needs to go.
567
570
  * @private
568
571
  * @deprecated
569
572
  */
@@ -571,15 +574,20 @@ export class SelectableCanvas<EventSpec extends CanvasEvents = CanvasEvents>
571
574
  target: FabricObject,
572
575
  controlName: string,
573
576
  ): { x: TOriginX; y: TOriginY } {
574
- const origin = {
575
- x: target.originX,
576
- y: target.originY,
577
- };
577
+ const origin = controlName
578
+ ? target.controls[controlName].getTransformAnchorPoint()
579
+ : {
580
+ x: target.originX,
581
+ y: target.originY,
582
+ };
578
583
 
579
584
  if (!controlName) {
580
585
  return origin;
581
586
  }
582
587
 
588
+ // this part down here is deprecated.
589
+ // It is left to do not change the standard behavior in the middle of a major version
590
+ // but when possible `getTransformAnchorPoint` will be the only source of truth
583
591
  // is a left control ?
584
592
  if (['ml', 'tl', 'bl'].includes(controlName)) {
585
593
  origin.x = RIGHT;
@@ -626,37 +634,52 @@ export class SelectableCanvas<EventSpec extends CanvasEvents = CanvasEvents>
626
634
  origin = this._shouldCenterTransform(target, action, altKey)
627
635
  ? ({ x: CENTER, y: CENTER } as const)
628
636
  : this._getOriginFromCorner(target, corner),
637
+ {
638
+ scaleX,
639
+ scaleY,
640
+ skewX,
641
+ skewY,
642
+ left,
643
+ top,
644
+ angle,
645
+ width,
646
+ height,
647
+ cropX,
648
+ cropY,
649
+ } = target as FabricImage,
629
650
  /**
630
651
  * relative to target's containing coordinate plane
631
652
  * both agree on every point
632
653
  **/
633
654
  transform: Transform = {
634
- target: target,
655
+ target,
635
656
  action,
636
657
  actionHandler,
637
658
  actionPerformed: false,
638
659
  corner,
639
- scaleX: target.scaleX,
640
- scaleY: target.scaleY,
641
- skewX: target.skewX,
642
- skewY: target.skewY,
643
- offsetX: pointer.x - target.left,
644
- offsetY: pointer.y - target.top,
660
+ scaleX,
661
+ scaleY,
662
+ skewX,
663
+ skewY,
664
+ offsetX: pointer.x - left,
665
+ offsetY: pointer.y - top,
645
666
  originX: origin.x,
646
667
  originY: origin.y,
647
668
  ex: pointer.x,
648
669
  ey: pointer.y,
649
670
  lastX: pointer.x,
650
671
  lastY: pointer.y,
651
- theta: degreesToRadians(target.angle),
652
- width: target.width,
653
- height: target.height,
672
+ theta: degreesToRadians(angle),
673
+ width,
674
+ height,
654
675
  shiftKey: e.shiftKey,
655
676
  altKey,
656
677
  original: {
657
678
  ...saveObjectTransform(target),
658
679
  originX: origin.x,
659
680
  originY: origin.y,
681
+ cropX,
682
+ cropY,
660
683
  },
661
684
  };
662
685
 
@@ -2241,6 +2241,26 @@ describe('StaticCanvas', () => {
2241
2241
  // });
2242
2242
  });
2243
2243
 
2244
+ describe('malicious tests', () => {
2245
+ it('from JSON to svg', async () => {
2246
+ const canvas = new StaticCanvas();
2247
+ const maliciousJSON = {
2248
+ objects: [
2249
+ {
2250
+ type: 'rect',
2251
+ id: '"><set onbegin="alert(1)"/>',
2252
+ width: 100,
2253
+ height: 100,
2254
+ fill: 'red',
2255
+ },
2256
+ ],
2257
+ };
2258
+ await canvas.loadFromJSON(maliciousJSON);
2259
+ const svg = canvas.toSVG();
2260
+ expect(svg).not.toContain('onbegin="alert(1)"');
2261
+ });
2262
+ });
2263
+
2244
2264
  function makeRect(options = {}) {
2245
2265
  const defaultOptions = { width: 10, height: 10 };
2246
2266
  return new Rect({ ...defaultOptions, ...options });
@@ -44,6 +44,7 @@ import type { StaticCanvasOptions } from './StaticCanvasOptions';
44
44
  import { staticCanvasDefaults } from './StaticCanvasOptions';
45
45
  import { log, FabricError } from '../util/internals/console';
46
46
  import { getDevicePixelRatio } from '../env';
47
+ import { escapeXml } from '../util/lang_string';
47
48
 
48
49
  /**
49
50
  * Having both options in TCanvasSizeOptions set to true transform the call in a calcOffset
@@ -86,9 +87,9 @@ export type PatternQuality = 'fast' | 'good' | 'best' | 'nearest' | 'bilinear';
86
87
  */
87
88
  // TODO: fix `EventSpec` inheritance https://github.com/microsoft/TypeScript/issues/26154#issuecomment-1366616260
88
89
  export class StaticCanvas<
89
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
90
- EventSpec extends StaticCanvasEvents = StaticCanvasEvents,
91
- >
90
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
91
+ EventSpec extends StaticCanvasEvents = StaticCanvasEvents,
92
+ >
92
93
  extends createCollectionMixin(CommonMethods<CanvasEvents>)
93
94
  implements StaticCanvasOptions
94
95
  {
@@ -950,7 +951,9 @@ export class StaticCanvas<
950
951
  this._setSVGPreamble(markup, options);
951
952
  this._setSVGHeader(markup, options);
952
953
  if (this.clipPath) {
953
- markup.push(`<g clip-path="url(#${this.clipPath.clipPathId})" >\n`);
954
+ markup.push(
955
+ `<g clip-path="url(#${escapeXml(this.clipPath.clipPathId ?? '')})" >\n`,
956
+ );
954
957
  }
955
958
  this._setSVGBgOverlayColor(markup, 'background');
956
959
  this._setSVGBgOverlayImage(markup, 'backgroundImage', reviver);
@@ -113,9 +113,7 @@ export interface CanvasExportOptions {
113
113
  }
114
114
 
115
115
  export interface StaticCanvasOptions
116
- extends CanvasDrawableOptions,
117
- CanvasRenderingOptions,
118
- CanvasExportOptions {
116
+ extends CanvasDrawableOptions, CanvasRenderingOptions, CanvasExportOptions {
119
117
  /**
120
118
  * Width in virtual/logical pixels of the canvas.
121
119
  * The canvas can be larger than width if retina scaling is active
package/src/constants.ts CHANGED
@@ -24,6 +24,7 @@ export const TOP = 'top';
24
24
  export const BOTTOM = 'bottom';
25
25
  export const RIGHT = 'right';
26
26
  export const NONE = 'none';
27
+ export const HEIGHT = 'height';
27
28
 
28
29
  export const reNewline = /\r?\n/;
29
30
 
@@ -67,4 +67,106 @@ describe('Controls', () => {
67
67
  Intersection.isPointInPolygon(new Point(15, 10), Object.values(coords)),
68
68
  ).toBe(true);
69
69
  });
70
+
71
+ describe('commonRenderProps', () => {
72
+ const createMockContext = () =>
73
+ ({
74
+ fillStyle: '',
75
+ strokeStyle: '',
76
+ translate: vi.fn(),
77
+ rotate: vi.fn(),
78
+ }) as unknown as CanvasRenderingContext2D;
79
+
80
+ test('returns default values from fabricObject', () => {
81
+ const control = new Control();
82
+ const ctx = createMockContext();
83
+ const fabricObject = new FabricObject({
84
+ cornerSize: 13,
85
+ cornerColor: 'blue',
86
+ transparentCorners: false,
87
+ cornerStrokeColor: 'red',
88
+ });
89
+
90
+ const result = control.commonRenderProps(ctx, 10, 20, fabricObject);
91
+
92
+ expect(result).toEqual({
93
+ stroke: true,
94
+ xSize: 13,
95
+ ySize: 13,
96
+ transparentCorners: false,
97
+ opName: 'fill',
98
+ });
99
+ expect(ctx.fillStyle).toBe('blue');
100
+ expect(ctx.strokeStyle).toBe('red');
101
+ expect(ctx.translate).toHaveBeenCalledWith(10, 20);
102
+ expect(ctx.rotate).toHaveBeenCalled();
103
+ });
104
+
105
+ test('uses control sizeX and sizeY when set', () => {
106
+ const control = new Control({ sizeX: 20, sizeY: 30 });
107
+ const ctx = createMockContext();
108
+ const fabricObject = new FabricObject({ cornerSize: 13 });
109
+
110
+ const result = control.commonRenderProps(ctx, 0, 0, fabricObject);
111
+
112
+ expect(result.xSize).toBe(20);
113
+ expect(result.ySize).toBe(30);
114
+ });
115
+
116
+ test('styleOverride takes precedence over fabricObject properties', () => {
117
+ const control = new Control();
118
+ const ctx = createMockContext();
119
+ const fabricObject = new FabricObject({
120
+ cornerSize: 13,
121
+ cornerColor: 'blue',
122
+ transparentCorners: false,
123
+ cornerStrokeColor: 'red',
124
+ });
125
+
126
+ const result = control.commonRenderProps(ctx, 0, 0, fabricObject, {
127
+ cornerSize: 25,
128
+ cornerColor: 'green',
129
+ transparentCorners: true,
130
+ cornerStrokeColor: 'yellow',
131
+ });
132
+
133
+ expect(result).toEqual({
134
+ stroke: false,
135
+ xSize: 25,
136
+ ySize: 25,
137
+ transparentCorners: true,
138
+ opName: 'stroke',
139
+ });
140
+ expect(ctx.fillStyle).toBe('green');
141
+ expect(ctx.strokeStyle).toBe('yellow');
142
+ });
143
+
144
+ test('returns stroke false when transparentCorners is true', () => {
145
+ const control = new Control();
146
+ const ctx = createMockContext();
147
+ const fabricObject = new FabricObject({
148
+ transparentCorners: true,
149
+ cornerStrokeColor: 'red',
150
+ });
151
+
152
+ const result = control.commonRenderProps(ctx, 0, 0, fabricObject);
153
+
154
+ expect(result.stroke).toBe(false);
155
+ expect(result.opName).toBe('stroke');
156
+ });
157
+
158
+ test('returns stroke false when no cornerStrokeColor is set', () => {
159
+ const control = new Control();
160
+ const ctx = createMockContext();
161
+ const fabricObject = new FabricObject({
162
+ transparentCorners: false,
163
+ cornerStrokeColor: '',
164
+ });
165
+
166
+ const result = control.commonRenderProps(ctx, 0, 0, fabricObject);
167
+
168
+ expect(result.stroke).toBe(false);
169
+ expect(result.opName).toBe('fill');
170
+ });
171
+ });
70
172
  });