commit 7eaee8e028d96a9fd2b3e29c2e50421a03e04597
parent 8b39e2f000103df4f00897d660291d019993b423
Author: longsonr <longsonr@gmail.com>
Date: Fri, 21 Nov 2025 08:28:25 +0000
Bug 2000531 - Fix getBBox when content has padding and scale r=dholbert,firefox-svg-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D273465
Diffstat:
2 files changed, 49 insertions(+), 11 deletions(-)
diff --git a/dom/svg/SVGContentUtils.cpp b/dom/svg/SVGContentUtils.cpp
@@ -438,9 +438,8 @@ static gfx::Matrix GetCTMInternal(SVGElement* aElement, CTMType aCTMType,
auto postTranslateFrameOffset = [](nsIFrame* aFrame, nsIFrame* aAncestorFrame,
gfx::Matrix& aMatrix) {
auto point = aFrame->GetOffsetTo(aAncestorFrame);
- aMatrix =
- aMatrix.PostTranslate(nsPresContext::AppUnitsToFloatCSSPixels(point.x),
- nsPresContext::AppUnitsToFloatCSSPixels(point.y));
+ aMatrix.PostTranslate(nsPresContext::AppUnitsToFloatCSSPixels(point.x),
+ nsPresContext::AppUnitsToFloatCSSPixels(point.y));
};
gfxMatrix matrix = getLocalTransformHelper(aElement, aHaveRecursed);
@@ -502,8 +501,7 @@ static gfx::Matrix GetCTMInternal(SVGElement* aElement, CTMType aCTMType,
if (frame->IsSVGOuterSVGFrame()) {
nsMargin bp = frame->GetUsedBorderAndPadding();
int32_t appUnitsPerCSSPixel = AppUnitsPerCSSPixel();
- float xOffset = NSAppUnitsToFloatPixels(bp.left, appUnitsPerCSSPixel);
- float yOffset = NSAppUnitsToFloatPixels(bp.top, appUnitsPerCSSPixel);
+ nscoord xOffset, yOffset;
// See
// https://drafts.csswg.org/css-transforms/#valdef-transform-box-fill-box
// For elements with associated CSS layout box, the used value for fill-box
@@ -511,17 +509,24 @@ static gfx::Matrix GetCTMInternal(SVGElement* aElement, CTMType aCTMType,
switch (frame->StyleDisplay()->mTransformBox) {
case StyleTransformBox::FillBox:
case StyleTransformBox::ContentBox:
- // Apply border/padding separate from the rest of the transform.
- // i.e. after it's been transformed
- tm.PostTranslate(xOffset, yOffset);
+ xOffset = bp.left;
+ yOffset = bp.top;
break;
case StyleTransformBox::StrokeBox:
case StyleTransformBox::ViewBox:
- case StyleTransformBox::BorderBox:
- // Apply border/padding before we transform the surface.
- tm.PreTranslate(xOffset, yOffset);
+ case StyleTransformBox::BorderBox: {
+ // Extract the rotation component of the matrix.
+ float angle = std::atan2(tm._12, tm._11);
+ float cosAngle = std::cos(angle);
+ float sinAngle = std::sin(angle);
+ // Apply that rotation to bp.left and bp.top.
+ xOffset = bp.left * cosAngle - bp.top * sinAngle;
+ yOffset = bp.top * cosAngle + bp.left * sinAngle;
break;
+ }
}
+ tm.PostTranslate(NSAppUnitsToFloatPixels(xOffset, appUnitsPerCSSPixel),
+ NSAppUnitsToFloatPixels(yOffset, appUnitsPerCSSPixel));
}
if (!ancestor || !ancestor->IsElement()) {
diff --git a/testing/web-platform/tests/svg/types/scripted/SVGGraphicsElement-padding.svg b/testing/web-platform/tests/svg/types/scripted/SVGGraphicsElement-padding.svg
@@ -51,6 +51,17 @@
test(function() {
let svg = document.getElementById("svg");
+ svg.setAttribute("style", "padding-left: 12px; padding-bottom: 10px; padding-top: 12px; padding-right: 24px; transform: rotate(180deg);");
+ let ctm = svg.getScreenCTM();
+ let pt = DOMPoint.fromPoint({x: 50, y: 50});
+ let transformedPoint = pt.matrixTransform(ctm.inverse());
+ svg.removeAttribute("style");
+ assert_approx_equals(transformedPoint.x, pt.x + 24, 0.1);
+ assert_approx_equals(transformedPoint.y, pt.y + 10, 0.1);
+ }, 'getScreenCTM with padding and rotation');
+
+ test(function() {
+ let svg = document.getElementById("svg");
svg.setAttribute("style", "padding-left: 12px; transform: rotate(180deg); transform-box: content-box");
let ctm = svg.getScreenCTM();
let pt = DOMPoint.fromPoint({x: 50, y: 50});
@@ -60,5 +71,27 @@
assert_equals(transformedPoint.y, pt.y);
}, 'getScreenCTM with padding-left, rotation and content-box');
+ test(function() {
+ let svg = document.getElementById("svg");
+ svg.setAttribute("style", "padding-left: 12px; transform: scale(2)");
+ let ctm = svg.getScreenCTM();
+ let pt = DOMPoint.fromPoint({x: 50, y: 50});
+ let transformedPoint = pt.matrixTransform(ctm.inverse());
+ svg.removeAttribute("style");
+ assert_equals(transformedPoint.x, pt.x - 3);
+ assert_equals(transformedPoint.y, pt.y);
+ }, 'getScreenCTM with padding-left, scale');
+
+ test(function() {
+ let svg = document.getElementById("svg");
+ svg.setAttribute("style", "border-width: 12px; transform: rotate(180deg);");
+ let ctm = svg.getScreenCTM();
+ let pt = DOMPoint.fromPoint({x: 50, y: 50});
+ let transformedPoint = pt.matrixTransform(ctm.inverse());
+ svg.removeAttribute("style");
+ assert_equals(transformedPoint.x, pt.x);
+ assert_equals(transformedPoint.y, pt.y);
+ }, 'getScreenCTM with border-width and rotation');
+
]]></script>
</svg>