fabric 7.3.1 → 7.4.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.
- package/CHANGELOG.md +40 -27
- package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.126.0}/helpers/defineProperty.mjs +1 -1
- package/{dist-extensions/_virtual/_@oxc-project_runtime@0.122.0 → dist/_virtual/_@oxc-project_runtime@0.126.0}/helpers/toPrimitive.mjs +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.126.0}/helpers/toPropertyKey.mjs +1 -1
- package/{dist-extensions/_virtual/_@oxc-project_runtime@0.122.0 → dist/_virtual/_@oxc-project_runtime@0.126.0}/helpers/typeof.mjs +1 -1
- package/dist/index.js +88 -66
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +35 -35
- package/dist/index.min.js.map +1 -1
- package/dist/index.min.mjs +37 -37
- package/dist/index.min.mjs.map +1 -1
- package/dist/index.mjs +88 -66
- package/dist/index.mjs.map +1 -1
- package/dist/index.node.cjs +83 -62
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.mjs +83 -62
- package/dist/index.node.mjs.map +1 -1
- package/dist/package.min.mjs +1 -1
- package/dist/package.mjs +1 -1
- package/dist/src/Collection.min.mjs +1 -1
- package/dist/src/Collection.mjs +1 -1
- package/dist/src/LayoutManager/LayoutManager.min.mjs +1 -1
- package/dist/src/LayoutManager/LayoutManager.mjs +1 -1
- package/dist/src/LayoutManager/LayoutStrategies/ClipPathLayout.min.mjs +1 -1
- package/dist/src/LayoutManager/LayoutStrategies/ClipPathLayout.mjs +1 -1
- package/dist/src/LayoutManager/LayoutStrategies/FitContentLayout.min.mjs +1 -1
- package/dist/src/LayoutManager/LayoutStrategies/FitContentLayout.mjs +1 -1
- package/dist/src/LayoutManager/LayoutStrategies/FixedLayout.min.mjs +1 -1
- package/dist/src/LayoutManager/LayoutStrategies/FixedLayout.mjs +1 -1
- package/dist/src/LayoutManager/LayoutStrategies/LayoutStrategy.min.mjs +1 -1
- package/dist/src/LayoutManager/LayoutStrategies/LayoutStrategy.mjs +1 -1
- package/dist/src/Observable.min.mjs +1 -1
- package/dist/src/Observable.mjs +1 -1
- package/dist/src/Pattern/Pattern.min.mjs +1 -1
- package/dist/src/Pattern/Pattern.mjs +1 -1
- package/dist/src/Shadow.min.mjs +1 -1
- package/dist/src/Shadow.mjs +1 -1
- package/dist/src/brushes/BaseBrush.min.mjs +1 -1
- package/dist/src/brushes/BaseBrush.mjs +1 -1
- package/dist/src/brushes/CircleBrush.min.mjs +1 -1
- package/dist/src/brushes/CircleBrush.mjs +1 -1
- package/dist/src/brushes/PencilBrush.min.mjs +1 -1
- package/dist/src/brushes/PencilBrush.mjs +1 -1
- package/dist/src/brushes/SprayBrush.min.mjs +1 -1
- package/dist/src/brushes/SprayBrush.mjs +1 -1
- package/dist/src/cache.min.mjs +1 -1
- package/dist/src/cache.mjs +1 -1
- package/dist/src/canvas/Canvas.min.mjs +1 -1
- package/dist/src/canvas/Canvas.mjs +1 -1
- package/dist/src/canvas/DOMManagers/CanvasDOMManager.min.mjs +1 -1
- package/dist/src/canvas/DOMManagers/CanvasDOMManager.mjs +1 -1
- package/dist/src/canvas/DOMManagers/StaticCanvasDOMManager.min.mjs +1 -1
- package/dist/src/canvas/DOMManagers/StaticCanvasDOMManager.mjs +1 -1
- package/dist/src/canvas/SelectableCanvas.min.mjs +1 -1
- package/dist/src/canvas/SelectableCanvas.mjs +1 -1
- package/dist/src/canvas/StaticCanvas.d.ts +1 -1
- package/dist/src/canvas/StaticCanvas.d.ts.map +1 -1
- package/dist/src/canvas/StaticCanvas.min.mjs +5 -5
- package/dist/src/canvas/StaticCanvas.min.mjs.map +1 -1
- package/dist/src/canvas/StaticCanvas.mjs +4 -4
- package/dist/src/canvas/StaticCanvas.mjs.map +1 -1
- package/dist/src/canvas/TextEditingManager.min.mjs +1 -1
- package/dist/src/canvas/TextEditingManager.mjs +1 -1
- package/dist/src/color/Color.min.mjs +1 -1
- package/dist/src/color/Color.mjs +1 -1
- package/dist/src/config.min.mjs +1 -1
- package/dist/src/config.mjs +1 -1
- package/dist/src/controls/Control.min.mjs +1 -1
- package/dist/src/controls/Control.mjs +1 -1
- package/dist/src/filters/BaseFilter.min.mjs +1 -1
- package/dist/src/filters/BaseFilter.mjs +1 -1
- package/dist/src/filters/BlendColor.min.mjs +1 -1
- package/dist/src/filters/BlendColor.mjs +1 -1
- package/dist/src/filters/BlendImage.min.mjs +1 -1
- package/dist/src/filters/BlendImage.mjs +1 -1
- package/dist/src/filters/Blur.min.mjs +1 -1
- package/dist/src/filters/Blur.mjs +1 -1
- package/dist/src/filters/Brightness.min.mjs +1 -1
- package/dist/src/filters/Brightness.mjs +1 -1
- package/dist/src/filters/Canvas2dFilterBackend.min.mjs +1 -1
- package/dist/src/filters/Canvas2dFilterBackend.mjs +1 -1
- package/dist/src/filters/ColorMatrix.min.mjs +1 -1
- package/dist/src/filters/ColorMatrix.mjs +1 -1
- package/dist/src/filters/ColorMatrixFilters.min.mjs +1 -1
- package/dist/src/filters/ColorMatrixFilters.mjs +1 -1
- package/dist/src/filters/Composed.min.mjs +1 -1
- package/dist/src/filters/Composed.mjs +1 -1
- package/dist/src/filters/Contrast.min.mjs +1 -1
- package/dist/src/filters/Contrast.mjs +1 -1
- package/dist/src/filters/Convolute.min.mjs +1 -1
- package/dist/src/filters/Convolute.min.mjs.map +1 -1
- package/dist/src/filters/Convolute.mjs +1 -1
- package/dist/src/filters/Gamma.min.mjs +1 -1
- package/dist/src/filters/Gamma.mjs +1 -1
- package/dist/src/filters/Grayscale.min.mjs +1 -1
- package/dist/src/filters/Grayscale.mjs +1 -1
- package/dist/src/filters/HueRotation.min.mjs +1 -1
- package/dist/src/filters/HueRotation.mjs +1 -1
- package/dist/src/filters/Invert.min.mjs +1 -1
- package/dist/src/filters/Invert.mjs +1 -1
- package/dist/src/filters/Noise.min.mjs +1 -1
- package/dist/src/filters/Noise.mjs +1 -1
- package/dist/src/filters/Pixelate.min.mjs +1 -1
- package/dist/src/filters/Pixelate.mjs +1 -1
- package/dist/src/filters/RemoveColor.min.mjs +1 -1
- package/dist/src/filters/RemoveColor.mjs +1 -1
- package/dist/src/filters/Resize.min.mjs +2 -2
- package/dist/src/filters/Resize.min.mjs.map +1 -1
- package/dist/src/filters/Resize.mjs +2 -2
- package/dist/src/filters/Resize.mjs.map +1 -1
- package/dist/src/filters/Saturation.min.mjs +1 -1
- package/dist/src/filters/Saturation.mjs +1 -1
- package/dist/src/filters/Vibrance.min.mjs +1 -1
- package/dist/src/filters/Vibrance.mjs +1 -1
- package/dist/src/filters/WebGLFilterBackend.min.mjs +1 -1
- package/dist/src/filters/WebGLFilterBackend.mjs +1 -1
- package/dist/src/gradient/Gradient.d.ts.map +1 -1
- package/dist/src/gradient/Gradient.min.mjs +4 -4
- package/dist/src/gradient/Gradient.min.mjs.map +1 -1
- package/dist/src/gradient/Gradient.mjs +6 -2
- package/dist/src/gradient/Gradient.mjs.map +1 -1
- package/dist/src/gradient/typedefs.d.ts +7 -7
- package/dist/src/parser/applyViewboxTransform.d.ts.map +1 -1
- package/dist/src/parser/applyViewboxTransform.min.mjs +1 -1
- package/dist/src/parser/applyViewboxTransform.min.mjs.map +1 -1
- package/dist/src/parser/applyViewboxTransform.mjs +2 -4
- package/dist/src/parser/applyViewboxTransform.mjs.map +1 -1
- package/dist/src/shapes/ActiveSelection.min.mjs +1 -1
- package/dist/src/shapes/ActiveSelection.mjs +1 -1
- package/dist/src/shapes/Circle.min.mjs +2 -2
- package/dist/src/shapes/Circle.min.mjs.map +1 -1
- package/dist/src/shapes/Circle.mjs +1 -1
- package/dist/src/shapes/Ellipse.min.mjs +1 -1
- package/dist/src/shapes/Ellipse.mjs +1 -1
- package/dist/src/shapes/Group.min.mjs +1 -1
- package/dist/src/shapes/Group.mjs +1 -1
- package/dist/src/shapes/IText/DraggableTextDelegate.min.mjs +1 -1
- package/dist/src/shapes/IText/DraggableTextDelegate.mjs +1 -1
- package/dist/src/shapes/IText/IText.d.ts.map +1 -1
- package/dist/src/shapes/IText/IText.min.mjs +1 -1
- package/dist/src/shapes/IText/IText.min.mjs.map +1 -1
- package/dist/src/shapes/IText/IText.mjs +3 -3
- package/dist/src/shapes/IText/IText.mjs.map +1 -1
- package/dist/src/shapes/IText/ITextBehavior.min.mjs +1 -1
- package/dist/src/shapes/IText/ITextBehavior.mjs +1 -1
- package/dist/src/shapes/IText/ITextClickBehavior.min.mjs +1 -1
- package/dist/src/shapes/IText/ITextClickBehavior.mjs +1 -1
- package/dist/src/shapes/Image.min.mjs +1 -1
- package/dist/src/shapes/Image.mjs +1 -1
- package/dist/src/shapes/Line.min.mjs +1 -1
- package/dist/src/shapes/Line.mjs +1 -1
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs +2 -2
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs.map +1 -1
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs +11 -26
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs.map +1 -1
- package/dist/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
- package/dist/src/shapes/Object/InteractiveObject.min.mjs +1 -1
- package/dist/src/shapes/Object/InteractiveObject.min.mjs.map +1 -1
- package/dist/src/shapes/Object/InteractiveObject.mjs +7 -6
- package/dist/src/shapes/Object/InteractiveObject.mjs.map +1 -1
- package/dist/src/shapes/Object/Object.min.mjs +1 -1
- package/dist/src/shapes/Object/Object.mjs +1 -1
- package/dist/src/shapes/Object/ObjectGeometry.d.ts.map +1 -1
- package/dist/src/shapes/Object/ObjectGeometry.min.mjs +1 -1
- package/dist/src/shapes/Object/ObjectGeometry.min.mjs.map +1 -1
- package/dist/src/shapes/Object/ObjectGeometry.mjs +6 -2
- package/dist/src/shapes/Object/ObjectGeometry.mjs.map +1 -1
- package/dist/src/shapes/Object/types/FabricObjectProps.d.ts +1 -1
- package/dist/src/shapes/Path.min.mjs +1 -1
- package/dist/src/shapes/Path.mjs +1 -1
- package/dist/src/shapes/Polygon.min.mjs +1 -1
- package/dist/src/shapes/Polygon.mjs +1 -1
- package/dist/src/shapes/Polyline.min.mjs +1 -1
- package/dist/src/shapes/Polyline.mjs +1 -1
- package/dist/src/shapes/Rect.min.mjs +1 -1
- package/dist/src/shapes/Rect.mjs +1 -1
- package/dist/src/shapes/Text/StyledText.min.mjs +1 -1
- package/dist/src/shapes/Text/StyledText.mjs +1 -1
- package/dist/src/shapes/Text/Text.d.ts +4 -4
- package/dist/src/shapes/Text/Text.min.mjs +1 -1
- package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
- package/dist/src/shapes/Text/Text.mjs +3 -3
- package/dist/src/shapes/Text/Text.mjs.map +1 -1
- package/dist/src/shapes/Text/TextSVGExportMixin.d.ts.map +1 -1
- package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs +2 -2
- package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs.map +1 -1
- package/dist/src/shapes/Text/TextSVGExportMixin.mjs +14 -7
- package/dist/src/shapes/Text/TextSVGExportMixin.mjs.map +1 -1
- package/dist/src/shapes/Textbox.d.ts.map +1 -1
- package/dist/src/shapes/Textbox.min.mjs +2 -2
- package/dist/src/shapes/Textbox.min.mjs.map +1 -1
- package/dist/src/shapes/Textbox.mjs +2 -3
- package/dist/src/shapes/Textbox.mjs.map +1 -1
- package/dist/src/shapes/Triangle.min.mjs +1 -1
- package/dist/src/shapes/Triangle.mjs +1 -1
- package/dist/src/util/animation/AnimationBase.min.mjs +1 -1
- package/dist/src/util/animation/AnimationBase.mjs +1 -1
- package/dist/src/util/animation/easing.min.mjs +1 -1
- package/dist/src/util/animation/easing.min.mjs.map +1 -1
- package/dist/src/util/animation/easing.mjs +1 -1
- package/dist/src/util/animation/easing.mjs.map +1 -1
- package/dist/src/util/internals/applyCanvasTransform.d.ts +1 -1
- package/dist/src/util/internals/applyCanvasTransform.min.mjs.map +1 -1
- package/dist/src/util/internals/applyCanvasTransform.mjs +1 -1
- package/dist/src/util/internals/applyCanvasTransform.mjs.map +1 -1
- package/dist/src/util/internals/svgExportCheck.d.ts +4 -0
- package/dist/src/util/internals/svgExportCheck.d.ts.map +1 -0
- package/dist/src/util/internals/svgExportCheck.min.mjs +2 -0
- package/dist/src/util/internals/svgExportCheck.min.mjs.map +1 -0
- package/dist/src/util/internals/svgExportCheck.mjs +12 -0
- package/dist/src/util/internals/svgExportCheck.mjs.map +1 -0
- package/dist/src/util/misc/matrix.d.ts +14 -0
- package/dist/src/util/misc/matrix.d.ts.map +1 -1
- package/dist/src/util/misc/matrix.min.mjs +1 -1
- package/dist/src/util/misc/matrix.min.mjs.map +1 -1
- package/dist/src/util/misc/matrix.mjs +15 -1
- package/dist/src/util/misc/matrix.mjs.map +1 -1
- package/dist/src/util/misc/mergeClipPaths.d.ts +1 -1
- package/dist/src/util/misc/mergeClipPaths.min.mjs.map +1 -1
- package/dist/src/util/misc/mergeClipPaths.mjs +1 -1
- package/dist/src/util/misc/mergeClipPaths.mjs.map +1 -1
- package/dist/src/util/misc/objectEnlive.min.mjs.map +1 -1
- package/dist/src/util/misc/objectEnlive.mjs +1 -1
- package/dist/src/util/misc/objectEnlive.mjs.map +1 -1
- package/dist/src/util/misc/projectStroke/StrokeLineJoinProjections.min.mjs +1 -1
- package/dist/src/util/misc/projectStroke/StrokeLineJoinProjections.min.mjs.map +1 -1
- package/dist/src/util/misc/projectStroke/StrokeLineJoinProjections.mjs +1 -1
- package/dist/src/util/misc/svgParsing.d.ts +2 -2
- package/dist/src/util/misc/svgParsing.d.ts.map +1 -1
- package/dist/src/util/misc/svgParsing.min.mjs +1 -1
- package/dist/src/util/misc/svgParsing.min.mjs.map +1 -1
- package/dist/src/util/misc/svgParsing.mjs +8 -3
- package/dist/src/util/misc/svgParsing.mjs.map +1 -1
- package/dist/src/util/path/index.d.ts.map +1 -1
- package/dist/src/util/path/index.min.mjs +1 -1
- package/dist/src/util/path/index.min.mjs.map +1 -1
- package/dist/src/util/path/index.mjs +2 -1
- package/dist/src/util/path/index.mjs.map +1 -1
- package/dist/src/util/path/regex.min.mjs.map +1 -1
- package/dist/src/util/path/regex.mjs +1 -1
- package/dist/src/util/path/regex.mjs.map +1 -1
- package/dist-extensions/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.126.0}/helpers/defineProperty.mjs +1 -1
- package/{dist/_virtual/_@oxc-project_runtime@0.122.0 → dist-extensions/_virtual/_@oxc-project_runtime@0.126.0}/helpers/toPrimitive.mjs +1 -1
- package/dist-extensions/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.126.0}/helpers/toPropertyKey.mjs +1 -1
- package/{dist/_virtual/_@oxc-project_runtime@0.122.0 → dist-extensions/_virtual/_@oxc-project_runtime@0.126.0}/helpers/typeof.mjs +1 -1
- package/dist-extensions/aligning_guidelines/index.mjs +1 -1
- package/dist-extensions/cropping_controls/croppingHandlers.mjs +1 -7
- package/dist-extensions/cropping_controls/croppingHandlers.mjs.map +1 -1
- package/dist-extensions/extensions/cropping_controls/croppingHandlers.d.ts.map +1 -1
- package/dist-extensions/fabric-extensions.min.js +1 -1
- package/dist-extensions/fabric-extensions.min.js.map +1 -1
- package/dist-extensions/src/canvas/StaticCanvas.d.ts +1 -1
- package/dist-extensions/src/canvas/StaticCanvas.d.ts.map +1 -1
- package/dist-extensions/src/gradient/Gradient.d.ts.map +1 -1
- package/dist-extensions/src/gradient/typedefs.d.ts +7 -7
- package/dist-extensions/src/parser/applyViewboxTransform.d.ts.map +1 -1
- package/dist-extensions/src/shapes/IText/IText.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/ObjectGeometry.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/types/FabricObjectProps.d.ts +1 -1
- package/dist-extensions/src/shapes/Text/Text.d.ts +4 -4
- package/dist-extensions/src/shapes/Text/TextSVGExportMixin.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Textbox.d.ts.map +1 -1
- package/dist-extensions/src/util/internals/applyCanvasTransform.d.ts +1 -1
- package/dist-extensions/src/util/internals/svgExportCheck.d.ts +4 -0
- package/dist-extensions/src/util/internals/svgExportCheck.d.ts.map +1 -0
- package/dist-extensions/src/util/misc/matrix.d.ts +14 -0
- package/dist-extensions/src/util/misc/matrix.d.ts.map +1 -1
- package/dist-extensions/src/util/misc/mergeClipPaths.d.ts +1 -1
- package/dist-extensions/src/util/misc/svgParsing.d.ts +2 -2
- package/dist-extensions/src/util/misc/svgParsing.d.ts.map +1 -1
- package/dist-extensions/src/util/path/index.d.ts.map +1 -1
- package/extensions/cropping_controls/croppingHandlers.ts +13 -19
- package/package.json +10 -10
- package/src/LayoutManager/README.md +3 -3
- package/src/canvas/StaticCanvas.spec.ts +19 -0
- package/src/canvas/StaticCanvas.ts +7 -3
- package/src/filters/Resize.ts +1 -1
- package/src/gradient/Gradient.spec.ts +60 -1
- package/src/gradient/Gradient.ts +9 -2
- package/src/gradient/typedefs.ts +7 -7
- package/src/parser/applyViewboxTransform.ts +2 -4
- package/src/shapes/IText/IText.ts +1 -2
- package/src/shapes/IText/ITextBehavior.test.ts +6 -6
- package/src/shapes/Object/FabricObject.spec.ts +3 -2
- package/src/shapes/Object/FabricObjectSVGExportMixin.ts +47 -37
- package/src/shapes/Object/InteractiveObject.ts +13 -3
- package/src/shapes/Object/Object-interactivity.spec.ts +126 -7
- package/src/shapes/Object/ObjectGeometry.spec.ts +10 -1
- package/src/shapes/Object/ObjectGeometry.ts +10 -3
- package/src/shapes/Object/objectSvgExport.spec.ts +27 -0
- package/src/shapes/Object/types/FabricObjectProps.ts +1 -1
- package/src/shapes/Text/Text.spec.ts +25 -0
- package/src/shapes/Text/Text.ts +5 -5
- package/src/shapes/Text/TextSVGExportMixin.ts +25 -11
- package/src/shapes/Textbox.ts +1 -2
- package/src/util/animation/animations.spec.ts +1 -1
- package/src/util/animation/easing.ts +1 -1
- package/src/util/internals/applyCanvasTransform.ts +1 -1
- package/src/util/internals/svgExportCheck.ts +20 -0
- package/src/util/misc/matrix.spec.ts +52 -0
- package/src/util/misc/matrix.ts +16 -0
- package/src/util/misc/mergeClipPaths.ts +1 -1
- package/src/util/misc/objectEnlive.ts +1 -1
- package/src/util/misc/svgParsing.ts +22 -10
- package/src/util/path/index.ts +3 -2
- package/src/util/path/regex.ts +1 -1
- package/src/util/typeAssertions.spec.ts +1 -1
- /package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.126.0}/helpers/defineProperty.min.mjs +0 -0
- /package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.126.0}/helpers/toPrimitive.min.mjs +0 -0
- /package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.126.0}/helpers/toPropertyKey.min.mjs +0 -0
- /package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.126.0}/helpers/typeof.min.mjs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FabricObject } from './FabricObject';
|
|
2
2
|
|
|
3
|
-
import { describe, expect, it
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
4
|
|
|
5
5
|
describe('FabricObject', () => {
|
|
6
6
|
it('setCoords should calculate control coords only if canvas ref is set', () => {
|
|
@@ -10,7 +10,8 @@ describe('FabricObject', () => {
|
|
|
10
10
|
object.setCoords();
|
|
11
11
|
expect(object.aCoords).toBeDefined();
|
|
12
12
|
expect(object.oCoords).toBeUndefined();
|
|
13
|
-
|
|
13
|
+
// @ts-expect-error -- mock canvas
|
|
14
|
+
object.canvas = { getZoom: () => 1 };
|
|
14
15
|
object.setCoords();
|
|
15
16
|
expect(object.aCoords).toBeDefined();
|
|
16
17
|
expect(object.oCoords).toBeDefined();
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { TSVGReviver } from '../../typedefs';
|
|
2
|
+
import {
|
|
3
|
+
getSafeSvgStyleNumber,
|
|
4
|
+
getSafeSvgStyleToken,
|
|
5
|
+
} from '../../util/internals/svgExportCheck';
|
|
2
6
|
import { uid } from '../../util/internals/uid';
|
|
3
7
|
import { colorPropToSVG } from '../../util/misc/svgParsing';
|
|
4
8
|
import { FILL, NONE, STROKE } from '../../constants';
|
|
@@ -24,16 +28,38 @@ export class FabricObjectSVGExportMixin {
|
|
|
24
28
|
this: FabricObjectSVGExportMixin & FabricObject,
|
|
25
29
|
skipShadow?: boolean,
|
|
26
30
|
) {
|
|
27
|
-
const fillRule =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
const fillRule =
|
|
32
|
+
this.fillRule == null ? 'nonzero' : getSafeSvgStyleToken(this.fillRule),
|
|
33
|
+
strokeWidth =
|
|
34
|
+
this.strokeWidth == null
|
|
35
|
+
? '0'
|
|
36
|
+
: getSafeSvgStyleNumber(this.strokeWidth),
|
|
37
|
+
strokeDashArray =
|
|
38
|
+
this.strokeDashArray == null
|
|
39
|
+
? NONE
|
|
40
|
+
: this.strokeDashArray.every((value) =>
|
|
41
|
+
Number.isFinite(Number(value)),
|
|
42
|
+
)
|
|
43
|
+
? this.strokeDashArray.join(' ')
|
|
44
|
+
: '',
|
|
45
|
+
strokeDashOffset =
|
|
46
|
+
this.strokeDashOffset == null
|
|
47
|
+
? '0'
|
|
48
|
+
: getSafeSvgStyleNumber(this.strokeDashOffset),
|
|
49
|
+
strokeLineCap =
|
|
50
|
+
this.strokeLineCap == null
|
|
51
|
+
? 'butt'
|
|
52
|
+
: getSafeSvgStyleToken(this.strokeLineCap),
|
|
53
|
+
strokeLineJoin =
|
|
54
|
+
this.strokeLineJoin == null
|
|
55
|
+
? 'miter'
|
|
56
|
+
: getSafeSvgStyleToken(this.strokeLineJoin),
|
|
57
|
+
strokeMiterLimit =
|
|
58
|
+
this.strokeMiterLimit == null
|
|
59
|
+
? '4'
|
|
60
|
+
: getSafeSvgStyleNumber(this.strokeMiterLimit),
|
|
61
|
+
opacity =
|
|
62
|
+
this.opacity == null ? '1' : getSafeSvgStyleNumber(this.opacity),
|
|
37
63
|
visibility = this.visible ? '' : ' visibility: hidden;',
|
|
38
64
|
filter = skipShadow ? '' : this.getSvgFilter(),
|
|
39
65
|
fill = colorPropToSVG(FILL, this.fill),
|
|
@@ -41,31 +67,15 @@ export class FabricObjectSVGExportMixin {
|
|
|
41
67
|
|
|
42
68
|
return [
|
|
43
69
|
stroke,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
'stroke-linecap: ',
|
|
51
|
-
strokeLineCap,
|
|
52
|
-
'; ',
|
|
53
|
-
'stroke-dashoffset: ',
|
|
54
|
-
strokeDashOffset,
|
|
55
|
-
'; ',
|
|
56
|
-
'stroke-linejoin: ',
|
|
57
|
-
strokeLineJoin,
|
|
58
|
-
'; ',
|
|
59
|
-
'stroke-miterlimit: ',
|
|
60
|
-
strokeMiterLimit,
|
|
61
|
-
'; ',
|
|
70
|
+
strokeWidth ? `stroke-width: ${strokeWidth}; ` : '',
|
|
71
|
+
strokeDashArray ? `stroke-dasharray: ${strokeDashArray}; ` : '',
|
|
72
|
+
strokeLineCap ? `stroke-linecap: ${strokeLineCap}; ` : '',
|
|
73
|
+
strokeDashOffset ? `stroke-dashoffset: ${strokeDashOffset}; ` : '',
|
|
74
|
+
strokeLineJoin ? `stroke-linejoin: ${strokeLineJoin}; ` : '',
|
|
75
|
+
strokeMiterLimit ? `stroke-miterlimit: ${strokeMiterLimit}; ` : '',
|
|
62
76
|
fill,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
'; ',
|
|
66
|
-
'opacity: ',
|
|
67
|
-
opacity,
|
|
68
|
-
';',
|
|
77
|
+
fillRule ? `fill-rule: ${fillRule}; ` : '',
|
|
78
|
+
opacity ? `opacity: ${opacity};` : '',
|
|
69
79
|
filter,
|
|
70
80
|
visibility,
|
|
71
81
|
]
|
|
@@ -93,10 +103,10 @@ export class FabricObjectSVGExportMixin {
|
|
|
93
103
|
return [
|
|
94
104
|
this.id ? `id="${escapeXml(String(this.id))}" ` : '',
|
|
95
105
|
this.clipPath
|
|
96
|
-
? `clip-path="url(#${
|
|
106
|
+
? `clip-path="url(#${escapeXml(
|
|
97
107
|
(this.clipPath as FabricObjectSVGExportMixin & FabricObject)
|
|
98
|
-
.clipPathId
|
|
99
|
-
})" `
|
|
108
|
+
.clipPathId!,
|
|
109
|
+
)})" `
|
|
100
110
|
: '',
|
|
101
111
|
].join('');
|
|
102
112
|
}
|
|
@@ -5,6 +5,9 @@ import { degreesToRadians } from '../../util/misc/radiansDegreesConversion';
|
|
|
5
5
|
import type { TQrDecomposeOut } from '../../util/misc/matrix';
|
|
6
6
|
import {
|
|
7
7
|
calcDimensionsMatrix,
|
|
8
|
+
calcPlaneRotation,
|
|
9
|
+
calcPlaneZoom,
|
|
10
|
+
calcPlaneScaleY,
|
|
8
11
|
createRotateMatrix,
|
|
9
12
|
createTranslateMatrix,
|
|
10
13
|
multiplyTransformMatrices,
|
|
@@ -254,6 +257,8 @@ export class InteractiveFabricObject<
|
|
|
254
257
|
*/
|
|
255
258
|
calcOCoords(): Record<string, TOCoord> {
|
|
256
259
|
const vpt = this.getViewportTransform(),
|
|
260
|
+
vptScaleX = calcPlaneZoom(vpt),
|
|
261
|
+
vptScaleY = calcPlaneScaleY(vpt),
|
|
257
262
|
center = this.getCenterPoint(),
|
|
258
263
|
tMatrix = createTranslateMatrix(center.x, center.y),
|
|
259
264
|
rMatrix = createRotateMatrix({
|
|
@@ -262,10 +267,10 @@ export class InteractiveFabricObject<
|
|
|
262
267
|
positionMatrix = multiplyTransformMatrices(tMatrix, rMatrix),
|
|
263
268
|
startMatrix = multiplyTransformMatrices(vpt, positionMatrix),
|
|
264
269
|
finalMatrix = multiplyTransformMatrices(startMatrix, [
|
|
265
|
-
1 /
|
|
270
|
+
1 / vptScaleX,
|
|
266
271
|
0,
|
|
267
272
|
0,
|
|
268
|
-
1 /
|
|
273
|
+
1 / vptScaleY,
|
|
269
274
|
0,
|
|
270
275
|
0,
|
|
271
276
|
]),
|
|
@@ -461,7 +466,12 @@ export class InteractiveFabricObject<
|
|
|
461
466
|
if (this.flipX) {
|
|
462
467
|
options.angle -= 180;
|
|
463
468
|
}
|
|
464
|
-
|
|
469
|
+
const vptAngle = calcPlaneRotation(vpt);
|
|
470
|
+
ctx.rotate(
|
|
471
|
+
this.group
|
|
472
|
+
? degreesToRadians(options.angle)
|
|
473
|
+
: degreesToRadians(this.angle) + vptAngle,
|
|
474
|
+
);
|
|
465
475
|
shouldDrawBorders && this.drawBorders(ctx, options, styleOverride);
|
|
466
476
|
shouldDrawControls && this.drawControls(ctx, styleOverride);
|
|
467
477
|
ctx.restore();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
2
|
import { FabricObject } from './FabricObject';
|
|
3
3
|
import { Point } from '../../Point';
|
|
4
4
|
|
|
@@ -211,7 +211,7 @@ describe('ObjectInteractivity', () => {
|
|
|
211
211
|
height: 10,
|
|
212
212
|
strokeWidth: 0,
|
|
213
213
|
// @ts-expect-error -- mock canvas
|
|
214
|
-
canvas: {},
|
|
214
|
+
canvas: { getZoom: () => 1 },
|
|
215
215
|
});
|
|
216
216
|
cObj.setCoords();
|
|
217
217
|
|
|
@@ -337,6 +337,92 @@ describe('ObjectInteractivity', () => {
|
|
|
337
337
|
);
|
|
338
338
|
});
|
|
339
339
|
|
|
340
|
+
it('corner coords with a rotated viewport transform', () => {
|
|
341
|
+
const cObj = new FabricObject({
|
|
342
|
+
left: 0,
|
|
343
|
+
top: 0,
|
|
344
|
+
width: 100,
|
|
345
|
+
height: 100,
|
|
346
|
+
strokeWidth: 0,
|
|
347
|
+
});
|
|
348
|
+
// @ts-expect-error -- mock canvas
|
|
349
|
+
cObj.canvas = {
|
|
350
|
+
viewportTransform: [0, 1, -1, 0, 0, 0],
|
|
351
|
+
getZoom: () => 1,
|
|
352
|
+
};
|
|
353
|
+
cObj.setCoords();
|
|
354
|
+
|
|
355
|
+
expect(cObj.oCoords.tl.x, 'tl.x is rotated 90 degrees').toBeCloseTo(50);
|
|
356
|
+
expect(cObj.oCoords.tl.y, 'tl.y is rotated 90 degrees').toBeCloseTo(-50);
|
|
357
|
+
expect(cObj.oCoords.tr.x, 'tr.x is rotated 90 degrees').toBeCloseTo(50);
|
|
358
|
+
expect(cObj.oCoords.tr.y, 'tr.y is rotated 90 degrees').toBeCloseTo(50);
|
|
359
|
+
expect(cObj.oCoords.br.x, 'br.x is rotated 90 degrees').toBeCloseTo(-50);
|
|
360
|
+
expect(cObj.oCoords.br.y, 'br.y is rotated 90 degrees').toBeCloseTo(50);
|
|
361
|
+
expect(cObj.oCoords.bl.x, 'bl.x is rotated 90 degrees').toBeCloseTo(-50);
|
|
362
|
+
expect(cObj.oCoords.bl.y, 'bl.y is rotated 90 degrees').toBeCloseTo(-50);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('corner coords with a non-uniform viewport transform', () => {
|
|
366
|
+
// originX/Y default to 'center', so left=0,top=0 puts the canvas-space center
|
|
367
|
+
// at (0,0). With vpt=[2,0,0,3,0,0] (no translation) the screen center is also
|
|
368
|
+
// at (0,0); controls should span ±(width*scaleX/2) × ±(height*scaleY/2).
|
|
369
|
+
const cObj = new FabricObject({
|
|
370
|
+
left: 0,
|
|
371
|
+
top: 0,
|
|
372
|
+
width: 100,
|
|
373
|
+
height: 100,
|
|
374
|
+
strokeWidth: 0,
|
|
375
|
+
});
|
|
376
|
+
// @ts-expect-error -- mock canvas
|
|
377
|
+
cObj.canvas = {
|
|
378
|
+
viewportTransform: [2, 0, 0, 3, 0, 0],
|
|
379
|
+
getZoom: () => 2,
|
|
380
|
+
};
|
|
381
|
+
cObj.setCoords();
|
|
382
|
+
|
|
383
|
+
expect(
|
|
384
|
+
cObj.oCoords.tl.x,
|
|
385
|
+
'tl.x: half-width scaled by scaleX=2',
|
|
386
|
+
).toBeCloseTo(-100);
|
|
387
|
+
expect(
|
|
388
|
+
cObj.oCoords.tl.y,
|
|
389
|
+
'tl.y: half-height scaled by scaleY=3',
|
|
390
|
+
).toBeCloseTo(-150);
|
|
391
|
+
expect(
|
|
392
|
+
cObj.oCoords.br.x,
|
|
393
|
+
'br.x: half-width scaled by scaleX=2',
|
|
394
|
+
).toBeCloseTo(100);
|
|
395
|
+
expect(
|
|
396
|
+
cObj.oCoords.br.y,
|
|
397
|
+
'br.y: half-height scaled by scaleY=3',
|
|
398
|
+
).toBeCloseTo(150);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('_renderControls rotates by the combined viewport and object angle', () => {
|
|
402
|
+
const cObj = new FabricObject({ width: 100, height: 100, strokeWidth: 0 });
|
|
403
|
+
const cos = Math.SQRT1_2,
|
|
404
|
+
sin = Math.SQRT1_2;
|
|
405
|
+
// @ts-expect-error -- mock canvas
|
|
406
|
+
cObj.canvas = {
|
|
407
|
+
viewportTransform: [cos, sin, -sin, cos, 0, 0],
|
|
408
|
+
getZoom: () => 1,
|
|
409
|
+
};
|
|
410
|
+
const rotate = vi.fn();
|
|
411
|
+
const ctx = {
|
|
412
|
+
save: vi.fn(),
|
|
413
|
+
restore: vi.fn(),
|
|
414
|
+
translate: vi.fn(),
|
|
415
|
+
rotate,
|
|
416
|
+
lineWidth: 0,
|
|
417
|
+
globalAlpha: 0,
|
|
418
|
+
};
|
|
419
|
+
// @ts-expect-error -- mock context
|
|
420
|
+
cObj._renderControls(ctx, { hasBorders: false, hasControls: false });
|
|
421
|
+
|
|
422
|
+
expect(rotate).toHaveBeenCalledTimes(1);
|
|
423
|
+
expect(rotate.mock.calls[0][0]).toBeCloseTo(Math.PI / 4);
|
|
424
|
+
});
|
|
425
|
+
|
|
340
426
|
// set size for bottom left corner and have different results for bl than normal setCornerCoords test
|
|
341
427
|
it('corner coords: custom control size', () => {
|
|
342
428
|
//set custom corner size
|
|
@@ -352,7 +438,7 @@ describe('ObjectInteractivity', () => {
|
|
|
352
438
|
strokeWidth: 0,
|
|
353
439
|
controls: sharedControls,
|
|
354
440
|
// @ts-expect-error -- mock canvas
|
|
355
|
-
canvas: {},
|
|
441
|
+
canvas: { getZoom: () => 1 },
|
|
356
442
|
});
|
|
357
443
|
cObj.setCoords();
|
|
358
444
|
|
|
@@ -490,7 +576,7 @@ describe('ObjectInteractivity', () => {
|
|
|
490
576
|
height: 30,
|
|
491
577
|
strokeWidth: 0,
|
|
492
578
|
// @ts-expect-error -- mock canvas
|
|
493
|
-
canvas: {},
|
|
579
|
+
canvas: { getZoom: () => 1 },
|
|
494
580
|
});
|
|
495
581
|
|
|
496
582
|
expect(typeof cObj.findControl, 'findControl should exist').toBe(
|
|
@@ -503,6 +589,7 @@ describe('ObjectInteractivity', () => {
|
|
|
503
589
|
getActiveObject() {
|
|
504
590
|
return cObj;
|
|
505
591
|
},
|
|
592
|
+
getZoom: () => 1,
|
|
506
593
|
};
|
|
507
594
|
|
|
508
595
|
expect(cObj.findControl(cObj.oCoords.br), 'br control').toEqual({
|
|
@@ -561,7 +648,7 @@ describe('ObjectInteractivity', () => {
|
|
|
561
648
|
height: 30,
|
|
562
649
|
strokeWidth: 0,
|
|
563
650
|
// @ts-expect-error -- mock canvas
|
|
564
|
-
canvas: {},
|
|
651
|
+
canvas: { getZoom: () => 1 },
|
|
565
652
|
});
|
|
566
653
|
|
|
567
654
|
cObj.setCoords();
|
|
@@ -570,6 +657,7 @@ describe('ObjectInteractivity', () => {
|
|
|
570
657
|
getActiveObject() {
|
|
571
658
|
return cObj;
|
|
572
659
|
},
|
|
660
|
+
getZoom: () => 1,
|
|
573
661
|
};
|
|
574
662
|
|
|
575
663
|
const pointNearBr = new Point({
|
|
@@ -609,7 +697,7 @@ describe('ObjectInteractivity', () => {
|
|
|
609
697
|
height: 30,
|
|
610
698
|
strokeWidth: 0,
|
|
611
699
|
// @ts-expect-error -- mock canvas
|
|
612
|
-
canvas: {},
|
|
700
|
+
canvas: { getZoom: () => 1 },
|
|
613
701
|
});
|
|
614
702
|
|
|
615
703
|
expect(typeof cObj.findControl, 'findControl should exist').toBe(
|
|
@@ -622,6 +710,7 @@ describe('ObjectInteractivity', () => {
|
|
|
622
710
|
getActiveObject() {
|
|
623
711
|
return undefined;
|
|
624
712
|
},
|
|
713
|
+
getZoom: () => 1,
|
|
625
714
|
};
|
|
626
715
|
|
|
627
716
|
expect(
|
|
@@ -638,7 +727,7 @@ describe('ObjectInteractivity', () => {
|
|
|
638
727
|
height: 30,
|
|
639
728
|
strokeWidth: 0,
|
|
640
729
|
// @ts-expect-error -- mock canvas
|
|
641
|
-
canvas: {},
|
|
730
|
+
canvas: { getZoom: () => 1 },
|
|
642
731
|
});
|
|
643
732
|
|
|
644
733
|
expect(typeof cObj.findControl, 'findControl should exist').toBe(
|
|
@@ -651,6 +740,7 @@ describe('ObjectInteractivity', () => {
|
|
|
651
740
|
getActiveObject() {
|
|
652
741
|
return cObj;
|
|
653
742
|
},
|
|
743
|
+
getZoom: () => 1,
|
|
654
744
|
};
|
|
655
745
|
|
|
656
746
|
cObj.isControlVisible = () => false;
|
|
@@ -717,6 +807,35 @@ describe('ObjectInteractivity', () => {
|
|
|
717
807
|
expect(dim.y.toFixed(0), 'height should change with padding').toBe('78');
|
|
718
808
|
});
|
|
719
809
|
|
|
810
|
+
it('_calculateCurrentDimensions with a rotated viewport transform', () => {
|
|
811
|
+
const cObj = new FabricObject({ width: 200, height: 200, strokeWidth: 0 });
|
|
812
|
+
const cos = Math.SQRT1_2,
|
|
813
|
+
sin = Math.SQRT1_2;
|
|
814
|
+
// @ts-expect-error -- mock canvas
|
|
815
|
+
cObj.canvas = {
|
|
816
|
+
viewportTransform: [cos, sin, -sin, cos, 0, 0],
|
|
817
|
+
getZoom: () => 1,
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
const dim = cObj._calculateCurrentDimensions();
|
|
821
|
+
|
|
822
|
+
expect(dim.x, 'width is independent of viewport rotation').toBeCloseTo(200);
|
|
823
|
+
expect(dim.y, 'height is independent of viewport rotation').toBeCloseTo(
|
|
824
|
+
200,
|
|
825
|
+
);
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
it('_calculateCurrentDimensions with a non-uniform viewport transform', () => {
|
|
829
|
+
const cObj = new FabricObject({ width: 200, height: 100, strokeWidth: 0 });
|
|
830
|
+
// @ts-expect-error -- mock canvas
|
|
831
|
+
cObj.canvas = { viewportTransform: [2, 0, 0, 3, 0, 0], getZoom: () => 2 };
|
|
832
|
+
|
|
833
|
+
const dim = cObj._calculateCurrentDimensions();
|
|
834
|
+
|
|
835
|
+
expect(dim.x, 'width scales by vpt scaleX').toBeCloseTo(400);
|
|
836
|
+
expect(dim.y, 'height scales by vpt scaleY').toBeCloseTo(300);
|
|
837
|
+
});
|
|
838
|
+
|
|
720
839
|
it('_getTransformedDimensions', () => {
|
|
721
840
|
const cObj = new FabricObject({ width: 10, height: 15, strokeWidth: 0 });
|
|
722
841
|
|
|
@@ -365,7 +365,7 @@ describe('fabric.ObjectGeometry', () => {
|
|
|
365
365
|
height: 100,
|
|
366
366
|
strokeWidth: 0,
|
|
367
367
|
// @ts-expect-error -- fake canvas
|
|
368
|
-
canvas: {},
|
|
368
|
+
canvas: { getZoom: () => 1 },
|
|
369
369
|
});
|
|
370
370
|
expect(cObj.setCoords).toBeTypeOf('function');
|
|
371
371
|
cObj.setCoords();
|
|
@@ -435,6 +435,7 @@ describe('fabric.ObjectGeometry', () => {
|
|
|
435
435
|
// @ts-expect-error -- partial canvas
|
|
436
436
|
cObj.canvas = {
|
|
437
437
|
viewportTransform: [2, 0, 0, 2, 0, 0],
|
|
438
|
+
getZoom: () => 2,
|
|
438
439
|
};
|
|
439
440
|
cObj.setCoords();
|
|
440
441
|
|
|
@@ -720,6 +721,7 @@ describe('fabric.ObjectGeometry', () => {
|
|
|
720
721
|
// @ts-expect-error -- partial canvas
|
|
721
722
|
cObj.canvas = {
|
|
722
723
|
viewportTransform: [2, 0, 0, 2, 0, 0],
|
|
724
|
+
getZoom: () => 2,
|
|
723
725
|
};
|
|
724
726
|
cObj.scaleToWidth(100);
|
|
725
727
|
expect(cObj.getScaledWidth(), 'is not influenced by zoom - width').toBe(
|
|
@@ -741,6 +743,7 @@ describe('fabric.ObjectGeometry', () => {
|
|
|
741
743
|
// @ts-expect-error -- partial canvas
|
|
742
744
|
cObj.canvas = {
|
|
743
745
|
viewportTransform: [2, 0, 0, 2, 0, 0],
|
|
746
|
+
getZoom: () => 2,
|
|
744
747
|
};
|
|
745
748
|
cObj.scaleToHeight(100);
|
|
746
749
|
expect(cObj.getScaledHeight(), 'is not influenced by zoom - height').toBe(
|
|
@@ -790,6 +793,7 @@ describe('fabric.ObjectGeometry', () => {
|
|
|
790
793
|
// @ts-expect-error -- partial canvas
|
|
791
794
|
cObj.canvas = {
|
|
792
795
|
viewportTransform: [2, 0, 0, 2, 0, 0],
|
|
796
|
+
getZoom: () => 2,
|
|
793
797
|
};
|
|
794
798
|
cObj.setCoords();
|
|
795
799
|
boundingRect = cObj.getBoundingRect();
|
|
@@ -955,6 +959,7 @@ describe('fabric.ObjectGeometry', () => {
|
|
|
955
959
|
// @ts-expect-error -- partial canvas
|
|
956
960
|
cObj.canvas = {
|
|
957
961
|
viewportTransform: [2, 0, 0, 2, 35, 35],
|
|
962
|
+
getZoom: () => 2,
|
|
958
963
|
};
|
|
959
964
|
const coords = cObj.getCoords();
|
|
960
965
|
expect(coords[0]).toEqual(new Point(40, 30));
|
|
@@ -976,6 +981,7 @@ describe('fabric.ObjectGeometry', () => {
|
|
|
976
981
|
// @ts-expect-error -- partial canvas
|
|
977
982
|
cObj.canvas = {
|
|
978
983
|
viewportTransform: [2, 0, 0, 2, 35, 25],
|
|
984
|
+
getZoom: () => 2,
|
|
979
985
|
};
|
|
980
986
|
const coords = cObj.getCoords();
|
|
981
987
|
expect(coords).toMatchSnapshot();
|
|
@@ -994,6 +1000,7 @@ describe('fabric.ObjectGeometry', () => {
|
|
|
994
1000
|
// @ts-expect-error -- partial canvas
|
|
995
1001
|
cObj.canvas = {
|
|
996
1002
|
viewportTransform: [2, 0, 0, 2, 35, 25],
|
|
1003
|
+
getZoom: () => 2,
|
|
997
1004
|
};
|
|
998
1005
|
const coords = cObj.getCoords();
|
|
999
1006
|
expect(coords).toMatchInlineSnapshot(`
|
|
@@ -1031,6 +1038,7 @@ describe('fabric.ObjectGeometry', () => {
|
|
|
1031
1038
|
// @ts-expect-error -- partial canvas
|
|
1032
1039
|
cObj.canvas = {
|
|
1033
1040
|
viewportTransform: [2, 0, 0, 2, 35, 25],
|
|
1041
|
+
getZoom: () => 2,
|
|
1034
1042
|
};
|
|
1035
1043
|
const coords = cObj.getCoords();
|
|
1036
1044
|
expect(coords).toMatchInlineSnapshot(`
|
|
@@ -1070,6 +1078,7 @@ describe('fabric.ObjectGeometry', () => {
|
|
|
1070
1078
|
// @ts-expect-error -- partial canvas
|
|
1071
1079
|
cObj.canvas = {
|
|
1072
1080
|
viewportTransform: [2, 0, 0, 2, 35, 25],
|
|
1081
|
+
getZoom: () => 2,
|
|
1073
1082
|
};
|
|
1074
1083
|
const coords = cObj.getCoords();
|
|
1075
1084
|
expect(coords).toMatchInlineSnapshot(`
|
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
multiplyTransformMatrices,
|
|
19
19
|
transformPoint,
|
|
20
20
|
calcPlaneRotation,
|
|
21
|
+
calcPlaneZoom,
|
|
22
|
+
calcPlaneScaleY,
|
|
21
23
|
} from '../../util/misc/matrix';
|
|
22
24
|
import { radiansToDegrees } from '../../util/misc/radiansDegreesConversion';
|
|
23
25
|
import type { Canvas } from '../../canvas/Canvas';
|
|
@@ -553,9 +555,14 @@ export class ObjectGeometry<EventSpec extends ObjectEvents = ObjectEvents>
|
|
|
553
555
|
* @returns {Point} dimensions
|
|
554
556
|
*/
|
|
555
557
|
_calculateCurrentDimensions(options?: any): Point {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
558
|
+
const vpt = this.canvas?.viewportTransform;
|
|
559
|
+
const dim = this._getTransformedDimensions(options);
|
|
560
|
+
if (vpt) {
|
|
561
|
+
return dim
|
|
562
|
+
.multiply(new Point(calcPlaneZoom(vpt), calcPlaneScaleY(vpt)))
|
|
563
|
+
.scalarAdd(2 * this.padding);
|
|
564
|
+
}
|
|
565
|
+
return dim.scalarAdd(2 * this.padding);
|
|
559
566
|
}
|
|
560
567
|
|
|
561
568
|
// #region Origin
|
|
@@ -110,3 +110,30 @@ describe.each([MALICIOUS, MALICIOUS2])(
|
|
|
110
110
|
});
|
|
111
111
|
},
|
|
112
112
|
);
|
|
113
|
+
|
|
114
|
+
describe('Object SVG style declaration sanitization', () => {
|
|
115
|
+
it('drops unsafe inline style values from object svg styles', () => {
|
|
116
|
+
const rect = new Rect({
|
|
117
|
+
width: 10,
|
|
118
|
+
height: 10,
|
|
119
|
+
fillRule: 'evenodd; filter:url(javascript:alert(1))' as never,
|
|
120
|
+
strokeLineCap: 'round; opacity:0' as never,
|
|
121
|
+
strokeLineJoin: 'bevel; fill:url(#x)' as never,
|
|
122
|
+
strokeDashArray: ['4', '2; fill:url(#x)'] as never,
|
|
123
|
+
strokeDashOffset: '1; opacity:0' as never,
|
|
124
|
+
strokeMiterLimit: '4; opacity:0' as never,
|
|
125
|
+
opacity: '1; visibility:hidden' as never,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const svgStyles = rect.getSvgStyles();
|
|
129
|
+
expect(svgStyles).not.toContain('fill-rule:');
|
|
130
|
+
expect(svgStyles).not.toContain('stroke-linecap:');
|
|
131
|
+
expect(svgStyles).not.toContain('stroke-linejoin:');
|
|
132
|
+
expect(svgStyles).not.toContain('stroke-dasharray:');
|
|
133
|
+
expect(svgStyles).not.toContain('stroke-dashoffset:');
|
|
134
|
+
expect(svgStyles).not.toContain('stroke-miterlimit:');
|
|
135
|
+
expect(svgStyles).not.toContain('opacity:');
|
|
136
|
+
expect(svgStyles).not.toContain('javascript:');
|
|
137
|
+
expect(svgStyles).not.toContain('filter:url');
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -8,7 +8,7 @@ export interface FabricObjectProps
|
|
|
8
8
|
/**
|
|
9
9
|
* When `true`, cache does not get updated during scaling. The picture will get blocky if scaled
|
|
10
10
|
* too much and will be redrawn with correct details at the end of scaling.
|
|
11
|
-
* this setting is performance and application
|
|
11
|
+
* this setting is performance and application dependent.
|
|
12
12
|
* default to true
|
|
13
13
|
* since 1.7.0
|
|
14
14
|
* @type Boolean
|
|
@@ -967,6 +967,31 @@ describe('FabricText', () => {
|
|
|
967
967
|
expect(styleString, 'style is as expected').toBe(expected);
|
|
968
968
|
});
|
|
969
969
|
|
|
970
|
+
it('getSvgSpanStyles drops unsafe style declarations', () => {
|
|
971
|
+
const iText = new IText('test foo bar-baz', {
|
|
972
|
+
textDecorationColor: 'blue; fill:url(#x)' as never,
|
|
973
|
+
});
|
|
974
|
+
// @ts-expect-error -- TODO: this is added by the mixing, can the types be improved here?
|
|
975
|
+
const styleString = iText.getSvgSpanStyles({
|
|
976
|
+
fill: 'red; stroke:url(javascript:alert(1))',
|
|
977
|
+
strokeWidth: '30; opacity:0' as never,
|
|
978
|
+
fontFamily: 'Verdana; font-size:999px',
|
|
979
|
+
fontSize: '25; opacity:0' as never,
|
|
980
|
+
fontStyle: 'italic; fill:url(#x)' as never,
|
|
981
|
+
fontWeight: 'bold; fill:url(#x)' as never,
|
|
982
|
+
underline: true,
|
|
983
|
+
textDecorationColor: 'green; fill:url(#x)' as never,
|
|
984
|
+
});
|
|
985
|
+
expect(styleString).toContain('fill: rgb(0,0,0); ');
|
|
986
|
+
expect(styleString).not.toContain('stroke-width: 30;');
|
|
987
|
+
expect(styleString).not.toContain('font-family:');
|
|
988
|
+
expect(styleString).not.toContain('font-size: 25px;');
|
|
989
|
+
expect(styleString).not.toContain('font-style:');
|
|
990
|
+
expect(styleString).not.toContain('font-weight:');
|
|
991
|
+
expect(styleString).not.toContain('text-decoration-color:');
|
|
992
|
+
expect(styleString).not.toContain('javascript:');
|
|
993
|
+
});
|
|
994
|
+
|
|
970
995
|
it('getSvgTextDecoration with overline true produces correct output', () => {
|
|
971
996
|
const iText = new IText('test foo bar-baz');
|
|
972
997
|
const styleObject = {
|
package/src/shapes/Text/Text.ts
CHANGED
|
@@ -301,11 +301,11 @@ export class FabricText<
|
|
|
301
301
|
declare path?: Path;
|
|
302
302
|
|
|
303
303
|
/**
|
|
304
|
-
* The text decoration
|
|
305
|
-
* The
|
|
304
|
+
* The text decoration thickness for underline, overline and strikethrough
|
|
305
|
+
* The thickness is expressed in thousandths of fontSize ( em ).
|
|
306
306
|
* The original value was 1/15 that translates to 66.6667 thousandths.
|
|
307
307
|
* The choice of unit of measure is to align with charSpacing.
|
|
308
|
-
* You can slim the
|
|
308
|
+
* You can slim the thickness without issues, while large underline or overline may end up
|
|
309
309
|
* outside the bounding box of the text. In order to fix that a bigger refactor of the code
|
|
310
310
|
* is needed and is out of scope for now. If you need such large overline on the first line
|
|
311
311
|
* of text or large underline on the last line of text, consider disabling caching as a
|
|
@@ -1147,7 +1147,7 @@ export class FabricText<
|
|
|
1147
1147
|
* @private
|
|
1148
1148
|
* @param {String} method fillText or strokeText.
|
|
1149
1149
|
* @param {CanvasRenderingContext2D} ctx Context to render on
|
|
1150
|
-
* @param {Array} line Content of the line,
|
|
1150
|
+
* @param {Array} line Content of the line, split in an array by grapheme
|
|
1151
1151
|
* @param {Number} left
|
|
1152
1152
|
* @param {Number} top
|
|
1153
1153
|
* @param {Number} lineIndex
|
|
@@ -1595,7 +1595,7 @@ export class FabricText<
|
|
|
1595
1595
|
TEXT_DECORATION_THICKNESS,
|
|
1596
1596
|
);
|
|
1597
1597
|
let currentDecoration = lastDecoration;
|
|
1598
|
-
let currentFill
|
|
1598
|
+
let currentFill: typeof lastFill;
|
|
1599
1599
|
let currentDecorationColor = lastDecorationColor;
|
|
1600
1600
|
let currentTickness = lastTickness;
|
|
1601
1601
|
const top = topOffset + maxHeight * (1 - this._fontSizeFraction);
|
|
@@ -16,6 +16,11 @@ import { STROKE, FILL } from '../../constants';
|
|
|
16
16
|
import { createRotateMatrix } from '../../util/misc/matrix';
|
|
17
17
|
import { radiansToDegrees } from '../../util/misc/radiansDegreesConversion';
|
|
18
18
|
import { Point } from '../../Point';
|
|
19
|
+
import {
|
|
20
|
+
getSafeSvgStyleNumber,
|
|
21
|
+
getSafeSvgStyleToken,
|
|
22
|
+
isSafeSvgStyleValue,
|
|
23
|
+
} from '../../util/internals/svgExportCheck';
|
|
19
24
|
import { matrixToSVG } from '../../util/misc/svgExport';
|
|
20
25
|
|
|
21
26
|
const multipleSpacesRegex = / +/g;
|
|
@@ -307,7 +312,9 @@ export class TextSVGExportMixin extends FabricObjectSVGExportMixin {
|
|
|
307
312
|
* @return {String}
|
|
308
313
|
*/
|
|
309
314
|
getSvgStyles(this: TextSVGExportMixin & FabricText, skipShadow?: boolean) {
|
|
310
|
-
const objectLevelTextDecorationColor =
|
|
315
|
+
const objectLevelTextDecorationColor = isSafeSvgStyleValue(
|
|
316
|
+
this[TEXT_DECORATION_COLOR],
|
|
317
|
+
)
|
|
311
318
|
? ` text-decoration-color: ${escapeXml(this[TEXT_DECORATION_COLOR])};`
|
|
312
319
|
: '';
|
|
313
320
|
return `${super.getSvgStyles(skipShadow)} text-decoration-thickness: ${toFixed((this.textDecorationThickness * this.getObjectScaling().y) / 10, config.NUM_FRACTION_DIGITS)}%;${objectLevelTextDecorationColor} white-space: pre;`;
|
|
@@ -347,23 +354,30 @@ export class TextSVGExportMixin extends FabricObjectSVGExportMixin {
|
|
|
347
354
|
const thickness =
|
|
348
355
|
textDecorationThickness || this[TEXT_DECORATION_THICKNESS];
|
|
349
356
|
const decorationColor = textDecorationColor || this[TEXT_DECORATION_COLOR];
|
|
357
|
+
const safeStrokeWidth = getSafeSvgStyleNumber(strokeWidth);
|
|
358
|
+
const safeFontFamily = getSafeSvgStyleToken(fontFamily);
|
|
359
|
+
const safeFontSize = getSafeSvgStyleNumber(fontSize);
|
|
360
|
+
const safeFontStyle = getSafeSvgStyleToken(fontStyle);
|
|
361
|
+
const safeFontWeight =
|
|
362
|
+
getSafeSvgStyleNumber(fontWeight) || getSafeSvgStyleToken(fontWeight);
|
|
363
|
+
const safeDecorationColor = getSafeSvgStyleToken(decorationColor);
|
|
350
364
|
return [
|
|
351
365
|
stroke ? colorPropToSVG(STROKE, stroke) : '',
|
|
352
|
-
|
|
353
|
-
|
|
366
|
+
safeStrokeWidth ? `stroke-width: ${escapeXml(safeStrokeWidth)}; ` : '',
|
|
367
|
+
safeFontFamily
|
|
354
368
|
? `font-family: ${
|
|
355
|
-
!
|
|
356
|
-
? `'${escapeXml(
|
|
357
|
-
: escapeXml(
|
|
369
|
+
!safeFontFamily.includes("'") && !safeFontFamily.includes('"')
|
|
370
|
+
? `'${escapeXml(safeFontFamily)}'`
|
|
371
|
+
: escapeXml(safeFontFamily)
|
|
358
372
|
}; `
|
|
359
373
|
: '',
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
374
|
+
safeFontSize ? `font-size: ${escapeXml(safeFontSize)}px; ` : '',
|
|
375
|
+
safeFontStyle ? `font-style: ${escapeXml(safeFontStyle)}; ` : '',
|
|
376
|
+
safeFontWeight ? `font-weight: ${escapeXml(safeFontWeight)}; ` : '',
|
|
363
377
|
textDecoration
|
|
364
378
|
? `text-decoration: ${textDecoration}; text-decoration-thickness: ${toFixed((thickness * this.getObjectScaling().y) / 10, config.NUM_FRACTION_DIGITS)}%;${
|
|
365
|
-
|
|
366
|
-
? ` text-decoration-color: ${escapeXml(
|
|
379
|
+
safeDecorationColor
|
|
380
|
+
? ` text-decoration-color: ${escapeXml(safeDecorationColor)};`
|
|
367
381
|
: ''
|
|
368
382
|
} `
|
|
369
383
|
: '',
|