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
@@ -1,16 +1,16 @@
1
1
  import { getFabricDocument } from '../env';
2
2
  import { FabricObject } from '../shapes/Object/FabricObject';
3
3
  import { Gradient } from './Gradient';
4
- import type { GradientUnits, SVGOptions } from './typedefs';
4
+ import type {
5
+ GradientUnits,
6
+ RadialGradientCoords,
7
+ SVGOptions,
8
+ } from './typedefs';
5
9
  import { classRegistry } from '../ClassRegistry';
6
10
 
7
- import { describe, expect, it, test, vi } from 'vitest';
11
+ import { describe, expect, it, test } from 'vitest';
8
12
  import { StaticCanvas } from '../canvas/StaticCanvas';
9
13
 
10
- vi.mock('../util/internals/uid', () => ({
11
- uid: () => 0,
12
- }));
13
-
14
14
  function createLinearGradient(units: GradientUnits = 'pixels', id?: string) {
15
15
  return new Gradient({
16
16
  type: 'linear',
@@ -85,17 +85,17 @@ function createRadialGradientSwapped() {
85
85
  }
86
86
 
87
87
  const SVG_LINEAR =
88
- '<linearGradient id="SVGID_0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 -50 -50)" x1="0" y1="10" x2="100" y2="200">\n<stop offset="0%" style="stop-color:rgba(255,0,0,0);"/>\n<stop offset="100%" style="stop-color:green;"/>\n</linearGradient>\n';
88
+ '<linearGradient id="SVGID" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 -50 -50)" x1="0" y1="10" x2="100" y2="200">\n<stop offset="0%" style="stop-color:rgba(255,0,0,0);"/>\n<stop offset="100%" style="stop-color:green;"/>\n</linearGradient>\n';
89
89
  const SVG_RADIAL =
90
- '<radialGradient id="SVGID_0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 -50 -50)" cx="100" cy="200" r="50" fx="0" fy="10">\n<stop offset="0%" style="stop-color:red;"/>\n<stop offset="100%" style="stop-color:rgba(0,255,0,0);"/>\n</radialGradient>\n';
90
+ '<radialGradient id="SVGID" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 -50 -50)" cx="100" cy="200" r="50" fx="0" fy="10">\n<stop offset="0%" style="stop-color:red;"/>\n<stop offset="100%" style="stop-color:rgba(0,255,0,0);"/>\n</radialGradient>\n';
91
91
  const SVG_INTERNALRADIUS =
92
- '<radialGradient id="SVGID_0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 -50 -50)" cx="100" cy="200" r="50" fx="0" fy="10">\n<stop offset="20%" style="stop-color:red;"/>\n<stop offset="100%" style="stop-color:rgba(0,255,0,0);"/>\n</radialGradient>\n';
92
+ '<radialGradient id="SVGID" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 -50 -50)" cx="100" cy="200" r="50" fx="0" fy="10">\n<stop offset="20%" style="stop-color:red;"/>\n<stop offset="100%" style="stop-color:rgba(0,255,0,0);"/>\n</radialGradient>\n';
93
93
  const SVG_SWAPPED =
94
- '<radialGradient id="SVGID_0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 -50 -50)" cx="0" cy="10" r="50" fx="100" fy="200">\n<stop offset="20%" style="stop-color:rgba(0,255,0,0);"/>\n<stop offset="100%" style="stop-color:red;"/>\n</radialGradient>\n';
94
+ '<radialGradient id="SVGID" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 1 -50 -50)" cx="0" cy="10" r="50" fx="100" fy="200">\n<stop offset="20%" style="stop-color:rgba(0,255,0,0);"/>\n<stop offset="100%" style="stop-color:red;"/>\n</radialGradient>\n';
95
95
  const SVG_LINEAR_PERCENTAGE =
96
- '<linearGradient id="SVGID_0" gradientUnits="objectBoundingBox" gradientTransform="matrix(1 0 0 1 0 0)" x1="0" y1="10" x2="100" y2="200">\n<stop offset="0%" style="stop-color:rgba(255,0,0,0);"/>\n<stop offset="100%" style="stop-color:green;"/>\n</linearGradient>\n';
96
+ '<linearGradient id="SVGID" gradientUnits="objectBoundingBox" gradientTransform="matrix(1 0 0 1 0 0)" x1="0" y1="10" x2="100" y2="200">\n<stop offset="0%" style="stop-color:rgba(255,0,0,0);"/>\n<stop offset="100%" style="stop-color:green;"/>\n</linearGradient>\n';
97
97
  const SVG_RADIAL_PERCENTAGE =
98
- '<radialGradient id="SVGID_0" gradientUnits="objectBoundingBox" gradientTransform="matrix(1 0 0 1 0 0)" cx="100" cy="200" r="50" fx="0" fy="10">\n<stop offset="0%" style="stop-color:red;"/>\n<stop offset="100%" style="stop-color:rgba(0,255,0,0);"/>\n</radialGradient>\n';
98
+ '<radialGradient id="SVGID" gradientUnits="objectBoundingBox" gradientTransform="matrix(1 0 0 1 0 0)" cx="100" cy="200" r="50" fx="0" fy="10">\n<stop offset="0%" style="stop-color:red;"/>\n<stop offset="100%" style="stop-color:rgba(0,255,0,0);"/>\n</radialGradient>\n';
99
99
 
100
100
  describe('Gradient', () => {
101
101
  function fromElement(
@@ -149,30 +149,73 @@ describe('Gradient', () => {
149
149
  test('toSVG linear', () => {
150
150
  const gradient = createLinearGradient();
151
151
  const baseObj = new FabricObject({ width: 100, height: 100 });
152
- expect(gradient.toSVG(baseObj)).toEqual(SVG_LINEAR);
152
+ expect(gradient.toSVG(baseObj)).toEqualSVG(SVG_LINEAR);
153
153
  });
154
154
 
155
155
  test('toSVG radial', () => {
156
156
  const gradient = createRadialGradient();
157
157
  const baseObj = new FabricObject({ width: 100, height: 100 });
158
- expect(gradient.toSVG(baseObj)).toEqual(SVG_RADIAL);
158
+ expect(gradient.toSVG(baseObj)).toEqualSVG(SVG_RADIAL);
159
+ });
160
+
161
+ test('toSVG linear sanitizes coord injection', () => {
162
+ const gradient = new Gradient({
163
+ type: 'linear',
164
+ coords: {
165
+ x1: '0" /><script>alert(1)</script>' as unknown as number,
166
+ y1: '0" /><script>alert(1)</script>' as unknown as number,
167
+ x2: '0" /><script>alert(1)</script>' as unknown as number,
168
+ y2: '0" /><script>alert(1)</script>' as unknown as number,
169
+ },
170
+ colorStops: [
171
+ { offset: 0, color: 'rgba(255,0,0,0)' },
172
+ { offset: 1, color: 'green' },
173
+ ],
174
+ });
175
+ const baseObj = new FabricObject({ width: 100, height: 100 });
176
+ const svg = gradient.toSVG(baseObj);
177
+ expect(svg).not.toContain('<script>');
178
+ });
179
+
180
+ test('toSVG radial sanitizes coord injection', () => {
181
+ const gradient = new Gradient({
182
+ type: 'radial',
183
+ coords: {
184
+ x1: '0" /><script>alert(1)</script>' as unknown as number,
185
+ y1: '0" /><script>alert(1)</script>' as unknown as number,
186
+ x2: '0" /><script>alert(1)</script>' as unknown as number,
187
+ y2: '0" /><script>alert(1)</script>' as unknown as number,
188
+ r1: '0" /><script>alert(1)</script>' as unknown as number,
189
+ r2: '50" /><script>alert(1)</script>' as unknown as number,
190
+ },
191
+ colorStops: [
192
+ { offset: 0, color: 'red' },
193
+ { offset: 1, color: 'rgba(0,255,0,0)' },
194
+ ],
195
+ });
196
+ const baseObj = new FabricObject({ width: 100, height: 100 });
197
+ const svg = gradient.toSVG(baseObj);
198
+ expect(svg).not.toContain('<script>');
159
199
  });
160
200
 
161
201
  test('toSVG radial with r1 > 0', () => {
162
202
  const gradient = createRadialGradientWithInternalRadius();
163
203
  const obj = new FabricObject({ width: 100, height: 100 });
164
- expect(gradient.toSVG(obj)).toEqual(SVG_INTERNALRADIUS);
204
+ expect(gradient.toSVG(obj)).toEqualSVG(SVG_INTERNALRADIUS);
165
205
  });
166
206
 
167
207
  test('toSVG radial with r1 > 0 swapped', () => {
168
208
  const gradient = createRadialGradientSwapped();
169
209
  const obj = new FabricObject({ width: 100, height: 100 });
170
210
  const gradientColorStops = JSON.stringify(gradient.colorStops);
171
- expect(gradient.toSVG(obj), 'it exports as expected').toBe(SVG_SWAPPED);
172
- const gradientColorStopsAfterExport = JSON.stringify(gradient.colorStops);
173
- expect(gradient.toSVG(obj), 'it exports as expected a second time').toBe(
211
+ expect(gradient.toSVG(obj), 'it exports as expected').toEqualSVG(
174
212
  SVG_SWAPPED,
175
213
  );
214
+ const gradientColorStopsAfterExport = JSON.stringify(gradient.colorStops);
215
+ expect(
216
+ gradient.toSVG(obj),
217
+ 'it exports as expected a second time',
218
+ ).toEqualSVG(SVG_SWAPPED);
176
219
  expect(gradientColorStops, 'colorstops do not change').toBe(
177
220
  gradientColorStopsAfterExport,
178
221
  );
@@ -181,13 +224,13 @@ describe('Gradient', () => {
181
224
  test('toSVG linear objectBoundingBox', () => {
182
225
  const gradient = createLinearGradient('percentage');
183
226
  const obj = new FabricObject({ width: 100, height: 100 });
184
- expect(gradient.toSVG(obj)).toBe(SVG_LINEAR_PERCENTAGE);
227
+ expect(gradient.toSVG(obj)).toEqualSVG(SVG_LINEAR_PERCENTAGE);
185
228
  });
186
229
 
187
230
  test('toSVG radial objectBoundingBox', () => {
188
231
  const gradient = createRadialGradient('percentage');
189
232
  const obj = new FabricObject({ width: 100, height: 100 });
190
- expect(gradient.toSVG(obj)).toBe(SVG_RADIAL_PERCENTAGE);
233
+ expect(gradient.toSVG(obj)).toEqualSVG(SVG_RADIAL_PERCENTAGE);
191
234
  });
192
235
  });
193
236
 
@@ -235,8 +278,8 @@ describe('Gradient', () => {
235
278
 
236
279
  test('toObject with custom props', () => {
237
280
  const gradient = createLinearGradient('pixels', 'myId');
238
- const object = gradient.toObject(['id']);
239
- expect(object.id).toBe('myId_0');
281
+ const object = gradient.toObject(['id']) as { id: string };
282
+ expect(object.id).toMatch(/^myId_\d+$/);
240
283
  });
241
284
 
242
285
  test('toObject radialGradient', () => {
@@ -535,23 +578,25 @@ describe('Gradient', () => {
535
578
  let object = new FabricObject({ width: 200, height: 200 });
536
579
  let gradient = fromElement(element, object, { opacity: '' });
537
580
  it('should not change with width height', () => {
538
- expect(gradient.coords.x1).toEqual(0.3);
539
- expect(gradient.coords.y1).toEqual(0.2);
540
- expect(gradient.coords.x2).toEqual(0.1);
541
- expect(gradient.coords.y2).toEqual(1);
542
- expect(gradient.coords.r1).toEqual(0);
543
- expect(gradient.coords.r2).toEqual(1);
581
+ const coords = gradient.coords as RadialGradientCoords<number>;
582
+ expect(coords.x1).toEqual(0.3);
583
+ expect(coords.y1).toEqual(0.2);
584
+ expect(coords.x2).toEqual(0.1);
585
+ expect(coords.y2).toEqual(1);
586
+ expect(coords.r1).toEqual(0);
587
+ expect(coords.r2).toEqual(1);
544
588
  });
545
589
 
546
590
  it('should not change with top left', () => {
547
591
  object = new FabricObject({ width: 200, height: 200, top: 10, left: 10 });
548
592
  gradient = fromElement(element, object, { opacity: '' });
549
- expect(gradient.coords.x1).toEqual(0.3);
550
- expect(gradient.coords.y1).toEqual(0.2);
551
- expect(gradient.coords.x2).toEqual(0.1);
552
- expect(gradient.coords.y2).toEqual(1);
553
- expect(gradient.coords.r1).toEqual(0);
554
- expect(gradient.coords.r2).toEqual(1);
593
+ const coords = gradient.coords as RadialGradientCoords<number>;
594
+ expect(coords.x1).toEqual(0.3);
595
+ expect(coords.y1).toEqual(0.2);
596
+ expect(coords.x2).toEqual(0.1);
597
+ expect(coords.y2).toEqual(1);
598
+ expect(coords.r1).toEqual(0);
599
+ expect(coords.r2).toEqual(1);
555
600
  });
556
601
  });
557
602
 
@@ -572,12 +617,13 @@ describe('Gradient', () => {
572
617
  it('should not change with width height', () => {
573
618
  const object = new FabricObject({ width: 200, height: 200 });
574
619
  const gradient = fromElement(element, object, { opacity: '' });
575
- expect(gradient.coords.x1).toEqual(30);
576
- expect(gradient.coords.y1).toEqual(20);
577
- expect(gradient.coords.x2).toEqual(15);
578
- expect(gradient.coords.y2).toEqual(18);
579
- expect(gradient.coords.r1).toEqual(0);
580
- expect(gradient.coords.r2).toEqual(100);
620
+ const coords = gradient.coords as RadialGradientCoords<number>;
621
+ expect(coords.x1).toEqual(30);
622
+ expect(coords.y1).toEqual(20);
623
+ expect(coords.x2).toEqual(15);
624
+ expect(coords.y2).toEqual(18);
625
+ expect(coords.r1).toEqual(0);
626
+ expect(coords.r2).toEqual(100);
581
627
  });
582
628
 
583
629
  it('should not change with top left', () => {
@@ -588,12 +634,13 @@ describe('Gradient', () => {
588
634
  left: 60,
589
635
  });
590
636
  const gradient = fromElement(element, object, { opacity: '' });
591
- expect(gradient.coords.x1).toEqual(30);
592
- expect(gradient.coords.y1).toEqual(20);
593
- expect(gradient.coords.x2).toEqual(15);
594
- expect(gradient.coords.y2).toEqual(18);
595
- expect(gradient.coords.r1).toEqual(0);
596
- expect(gradient.coords.r2).toEqual(100);
637
+ const coords = gradient.coords as RadialGradientCoords<number>;
638
+ expect(coords.x1).toEqual(30);
639
+ expect(coords.y1).toEqual(20);
640
+ expect(coords.x2).toEqual(15);
641
+ expect(coords.y2).toEqual(18);
642
+ expect(coords.r1).toEqual(0);
643
+ expect(coords.r2).toEqual(100);
597
644
  });
598
645
  });
599
646
 
@@ -818,4 +865,12 @@ describe('Gradient', () => {
818
865
  expect(gradient.colorStops[2].color).toEqual('rgba(0,0,0,1)');
819
866
  expect(gradient.colorStops[3].color).toEqual('rgba(0,0,0,1)');
820
867
  });
868
+
869
+ describe('Attrivbute injection', () => {
870
+ it('id injection', () => {
871
+ const gradient = new Gradient({ id: 'malicious"><script>' });
872
+ const svg = gradient.toSVG({} as any);
873
+ expect(svg).toContain('id="SVGID_malicious&quot;&gt;&lt;script&gt;');
874
+ });
875
+ });
821
876
  });
@@ -20,6 +20,7 @@ import type {
20
20
  } from './typedefs';
21
21
  import { classRegistry } from '../ClassRegistry';
22
22
  import { isPath } from '../util/typeAssertions';
23
+ import { escapeXml } from '../util/lang_string';
23
24
 
24
25
  /**
25
26
  * Gradient class
@@ -209,7 +210,7 @@ export class Gradient<
209
210
  transform[5] -= offsetY;
210
211
 
211
212
  const commonAttributes = [
212
- `id="SVGID_${this.id}"`,
213
+ `id="SVGID_${escapeXml(String(this.id))}"`,
213
214
  `gradientUnits="${gradientUnits}"`,
214
215
  `gradientTransform="${
215
216
  preTransform ? preTransform + ' ' : ''
@@ -217,39 +218,51 @@ export class Gradient<
217
218
  '',
218
219
  ].join(' ');
219
220
 
221
+ const sanitizeCoord = (value: unknown) => parseFloat(String(value));
222
+
220
223
  if (this.type === 'linear') {
221
224
  const { x1, y1, x2, y2 } = this.coords;
225
+ const sx1 = sanitizeCoord(x1);
226
+ const sy1 = sanitizeCoord(y1);
227
+ const sx2 = sanitizeCoord(x2);
228
+ const sy2 = sanitizeCoord(y2);
222
229
  markup.push(
223
230
  '<linearGradient ',
224
231
  commonAttributes,
225
232
  ' x1="',
226
- x1,
233
+ sx1,
227
234
  '" y1="',
228
- y1,
235
+ sy1,
229
236
  '" x2="',
230
- x2,
237
+ sx2,
231
238
  '" y2="',
232
- y2,
239
+ sy2,
233
240
  '">\n',
234
241
  );
235
242
  } else if (this.type === 'radial') {
236
243
  const { x1, y1, x2, y2, r1, r2 } = this
237
244
  .coords as GradientCoords<'radial'>;
238
- const needsSwap = r1 > r2;
245
+ const sx1 = sanitizeCoord(x1);
246
+ const sy1 = sanitizeCoord(y1);
247
+ const sx2 = sanitizeCoord(x2);
248
+ const sy2 = sanitizeCoord(y2);
249
+ const sr1 = sanitizeCoord(r1);
250
+ const sr2 = sanitizeCoord(r2);
251
+ const needsSwap = sr1 > sr2;
239
252
  // svg radial gradient has just 1 radius. the biggest.
240
253
  markup.push(
241
254
  '<radialGradient ',
242
255
  commonAttributes,
243
256
  ' cx="',
244
- needsSwap ? x1 : x2,
257
+ needsSwap ? sx1 : sx2,
245
258
  '" cy="',
246
- needsSwap ? y1 : y2,
259
+ needsSwap ? sy1 : sy2,
247
260
  '" r="',
248
- needsSwap ? r1 : r2,
261
+ needsSwap ? sr1 : sr2,
249
262
  '" fx="',
250
- needsSwap ? x2 : x1,
263
+ needsSwap ? sx2 : sx1,
251
264
  '" fy="',
252
- needsSwap ? y2 : y1,
265
+ needsSwap ? sy2 : sy1,
253
266
  '">\n',
254
267
  );
255
268
  if (needsSwap) {
@@ -259,17 +272,17 @@ export class Gradient<
259
272
  colorStop.offset = 1 - colorStop.offset;
260
273
  });
261
274
  }
262
- const minRadius = Math.min(r1, r2);
275
+ const minRadius = Math.min(sr1, sr2);
263
276
  if (minRadius > 0) {
264
277
  // i have to shift all colorStops and add new one in 0.
265
- const maxRadius = Math.max(r1, r2),
278
+ const maxRadius = Math.max(sr1, sr2),
266
279
  percentageShift = minRadius / maxRadius;
267
280
  colorStops.forEach((colorStop) => {
268
281
  colorStop.offset += percentageShift * (1 - colorStop.offset);
269
282
  });
270
283
  }
271
284
  }
272
-
285
+ // todo make a malicious script tag injection test with color and also apply a fix with escapeXml
273
286
  colorStops.forEach(({ color, offset }) => {
274
287
  markup.push(
275
288
  `<stop offset="${offset * 100}%" style="stop-color:${color};"/>\n`,
@@ -2,6 +2,14 @@ import { describe, expect, it } from 'vitest';
2
2
  import { Circle } from './Circle';
3
3
  import { FabricObject } from './Object/FabricObject';
4
4
  import { getFabricDocument, version } from '../../fabric';
5
+ import { createReferenceObject } from '../../test/utils';
6
+
7
+ const REFERENCE_CIRCLE = createReferenceObject('Circle', {
8
+ radius: 0,
9
+ startAngle: 0,
10
+ endAngle: 360,
11
+ counterClockwise: false,
12
+ });
5
13
 
6
14
  describe('Circle', () => {
7
15
  it('constructor', () => {
@@ -86,52 +94,15 @@ describe('Circle', () => {
86
94
 
87
95
  it('toObject', () => {
88
96
  const circle = new Circle();
89
- const defaultProperties = {
90
- version: version,
91
- type: 'Circle',
92
- originX: 'center',
93
- originY: 'center',
94
- left: 0,
95
- top: 0,
96
- width: 0,
97
- height: 0,
98
- fill: 'rgb(0,0,0)',
99
- stroke: null,
100
- strokeWidth: 1,
101
- strokeDashArray: null,
102
- strokeLineCap: 'butt',
103
- strokeDashOffset: 0,
104
- strokeLineJoin: 'miter',
105
- strokeMiterLimit: 4,
106
- scaleX: 1,
107
- scaleY: 1,
108
- angle: 0,
109
- flipX: false,
110
- flipY: false,
111
- opacity: 1,
112
- shadow: null,
113
- visible: true,
114
- backgroundColor: '',
115
- fillRule: 'nonzero',
116
- paintFirst: 'fill',
117
- globalCompositeOperation: 'source-over',
118
- radius: 0,
119
- startAngle: 0,
120
- endAngle: 360,
121
- counterClockwise: false,
122
- skewX: 0,
123
- skewY: 0,
124
- strokeUniform: false,
125
- };
126
97
  expect(circle.toObject).toBeTypeOf('function');
127
- expect(circle.toObject()).toStrictEqual(defaultProperties);
98
+ expect(circle.toObject()).toStrictEqual(REFERENCE_CIRCLE);
128
99
 
129
100
  circle.set('left', 100);
130
101
  circle.set('top', 200);
131
102
  circle.set('radius', 15);
132
103
 
133
104
  expect(circle.toObject()).toStrictEqual({
134
- ...defaultProperties,
105
+ ...REFERENCE_CIRCLE,
135
106
  left: 100,
136
107
  top: 200,
137
108
  width: 30,
@@ -10,6 +10,7 @@ import type { Abortable, TClassProperties, TOptions } from '../typedefs';
10
10
  import type { FabricObjectProps, SerializedObjectProps } from './Object/types';
11
11
  import type { CSSRules } from '../parser/typedefs';
12
12
  import { SCALE_X, SCALE_Y } from '../constants';
13
+ import { escapeXml } from '../util/lang_string';
13
14
 
14
15
  interface UniqueCircleProps {
15
16
  /**
@@ -43,8 +44,7 @@ interface UniqueCircleProps {
43
44
  }
44
45
 
45
46
  export interface SerializedCircleProps
46
- extends SerializedObjectProps,
47
- UniqueCircleProps {}
47
+ extends SerializedObjectProps, UniqueCircleProps {}
48
48
 
49
49
  export interface CircleProps extends FabricObjectProps, UniqueCircleProps {}
50
50
 
@@ -63,10 +63,10 @@ export const circleDefaultValues: Partial<TClassProperties<Circle>> = {
63
63
  };
64
64
 
65
65
  export class Circle<
66
- Props extends TOptions<CircleProps> = Partial<CircleProps>,
67
- SProps extends SerializedCircleProps = SerializedCircleProps,
68
- EventSpec extends ObjectEvents = ObjectEvents,
69
- >
66
+ Props extends TOptions<CircleProps> = Partial<CircleProps>,
67
+ SProps extends SerializedCircleProps = SerializedCircleProps,
68
+ EventSpec extends ObjectEvents = ObjectEvents,
69
+ >
70
70
  extends FabricObject<Props, SProps, EventSpec>
71
71
  implements UniqueCircleProps
72
72
  {
@@ -174,7 +174,8 @@ export class Circle<
174
174
  * of the instance
175
175
  */
176
176
  _toSVG(): string[] {
177
- const angle = (this.endAngle - this.startAngle) % 360;
177
+ const { radius, startAngle, endAngle } = this;
178
+ const angle = (endAngle - startAngle) % 360;
178
179
 
179
180
  if (angle === 0) {
180
181
  return [
@@ -182,13 +183,12 @@ export class Circle<
182
183
  'COMMON_PARTS',
183
184
  'cx="0" cy="0" ',
184
185
  'r="',
185
- `${this.radius}`,
186
+ `${escapeXml(radius)}`,
186
187
  '" />\n',
187
188
  ];
188
189
  } else {
189
- const { radius } = this;
190
- const start = degreesToRadians(this.startAngle),
191
- end = degreesToRadians(this.endAngle),
190
+ const start = degreesToRadians(startAngle),
191
+ end = degreesToRadians(endAngle),
192
192
  startX = cos(start) * radius,
193
193
  startY = sin(start) * radius,
194
194
  endX = cos(end) * radius,
@@ -3,6 +3,12 @@ import { Ellipse } from './Ellipse';
3
3
  import { FabricObject } from './Object/Object';
4
4
  import { getFabricDocument, version } from '../../fabric';
5
5
  import { sanitizeSVG } from '../../vitest.extend';
6
+ import { createReferenceObject } from '../../test/utils';
7
+
8
+ const REFERENCE_ELLIPSE = createReferenceObject('Ellipse', {
9
+ rx: 0,
10
+ ry: 0,
11
+ });
6
12
 
7
13
  describe('Ellipse', () => {
8
14
  it('initializes constructor correctly', () => {
@@ -24,43 +30,8 @@ describe('Ellipse', () => {
24
30
 
25
31
  it('converts to object with correct properties', () => {
26
32
  const ellipse = new Ellipse();
27
- const defaultProperties = {
28
- version: version,
29
- type: 'Ellipse',
30
- originX: 'center',
31
- originY: 'center',
32
- left: 0,
33
- top: 0,
34
- width: 0,
35
- height: 0,
36
- fill: 'rgb(0,0,0)',
37
- stroke: null,
38
- strokeWidth: 1,
39
- strokeDashArray: null,
40
- strokeLineCap: 'butt',
41
- strokeDashOffset: 0,
42
- strokeLineJoin: 'miter',
43
- strokeMiterLimit: 4,
44
- scaleX: 1,
45
- scaleY: 1,
46
- angle: 0,
47
- flipX: false,
48
- flipY: false,
49
- opacity: 1,
50
- skewX: 0,
51
- skewY: 0,
52
- rx: 0,
53
- ry: 0,
54
- shadow: null,
55
- visible: true,
56
- backgroundColor: '',
57
- fillRule: 'nonzero',
58
- paintFirst: 'fill',
59
- globalCompositeOperation: 'source-over',
60
- strokeUniform: false,
61
- };
62
33
  expect(ellipse.toObject).toBeTypeOf('function');
63
- expect(ellipse.toObject()).toEqual(defaultProperties);
34
+ expect(ellipse.toObject()).toEqual(REFERENCE_ELLIPSE);
64
35
 
65
36
  ellipse.set('left', 100);
66
37
  ellipse.set('top', 200);
@@ -68,7 +39,7 @@ describe('Ellipse', () => {
68
39
  ellipse.set('ry', 25);
69
40
 
70
41
  expect(ellipse.toObject()).toEqual({
71
- ...defaultProperties,
42
+ ...REFERENCE_ELLIPSE,
72
43
  left: 100,
73
44
  top: 200,
74
45
  rx: 15,
@@ -7,6 +7,7 @@ import { FabricObject, cacheProperties } from './Object/FabricObject';
7
7
  import type { FabricObjectProps, SerializedObjectProps } from './Object/types';
8
8
  import type { ObjectEvents } from '../EventTypeDefs';
9
9
  import type { CSSRules } from '../parser/typedefs';
10
+ import { escapeXml } from '../util/lang_string';
10
11
 
11
12
  export const ellipseDefaultValues: Partial<TClassProperties<Ellipse>> = {
12
13
  rx: 0,
@@ -19,18 +20,17 @@ interface UniqueEllipseProps {
19
20
  }
20
21
 
21
22
  export interface SerializedEllipseProps
22
- extends SerializedObjectProps,
23
- UniqueEllipseProps {}
23
+ extends SerializedObjectProps, UniqueEllipseProps {}
24
24
 
25
25
  export interface EllipseProps extends FabricObjectProps, UniqueEllipseProps {}
26
26
 
27
27
  const ELLIPSE_PROPS = ['rx', 'ry'] as const;
28
28
 
29
29
  export class Ellipse<
30
- Props extends TOptions<EllipseProps> = Partial<EllipseProps>,
31
- SProps extends SerializedEllipseProps = SerializedEllipseProps,
32
- EventSpec extends ObjectEvents = ObjectEvents,
33
- >
30
+ Props extends TOptions<EllipseProps> = Partial<EllipseProps>,
31
+ SProps extends SerializedEllipseProps = SerializedEllipseProps,
32
+ EventSpec extends ObjectEvents = ObjectEvents,
33
+ >
34
34
  extends FabricObject<Props, SProps, EventSpec>
35
35
  implements EllipseProps
36
36
  {
@@ -128,7 +128,7 @@ export class Ellipse<
128
128
  return [
129
129
  '<ellipse ',
130
130
  'COMMON_PARTS',
131
- `cx="0" cy="0" rx="${this.rx}" ry="${this.ry}" />\n`,
131
+ `cx="0" cy="0" rx="${escapeXml(this.rx)}" ry="${escapeXml(this.ry)}" />\n`,
132
132
  ];
133
133
  }
134
134
 
@@ -35,6 +35,7 @@ import {
35
35
  import type { SerializedLayoutManager } from '../LayoutManager/LayoutManager';
36
36
  import type { FitContentLayout } from '../LayoutManager';
37
37
  import type { DrawContext } from './Object/Object';
38
+ import { escapeXml } from '../util/lang_string';
38
39
 
39
40
  /**
40
41
  * This class handles the specific case of creating a group using {@link Group#fromObject} and is not meant to be used in any other case.
@@ -57,8 +58,7 @@ export interface GroupOwnProps {
57
58
  }
58
59
 
59
60
  export interface SerializedGroupProps
60
- extends SerializedObjectProps,
61
- GroupOwnProps {
61
+ extends SerializedObjectProps, GroupOwnProps {
62
62
  objects: SerializedObjectProps[];
63
63
  layoutManager: SerializedLayoutManager;
64
64
  }
@@ -650,7 +650,7 @@ export class Group
650
650
  getSvgStyles(): string {
651
651
  const opacity =
652
652
  typeof this.opacity !== 'undefined' && this.opacity !== 1
653
- ? `opacity: ${this.opacity};`
653
+ ? `opacity: ${escapeXml(this.opacity)};`
654
654
  : '',
655
655
  visibility = this.visible ? '' : ' visibility: hidden;';
656
656
  return [opacity, this.getSvgFilter(), visibility].join('');