1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "mozilla/dom/SVGElement.h"
10 #include "nsCSSValue.h"
11 #include "nsTextFormatter.h"
12 #include "SVGContentUtils.h"
16 using namespace mozilla::dom
;
17 using namespace mozilla::dom::SVGLength_Binding
;
21 // These types are numbered so that different length categories are in
22 // contiguous ranges - See `SVGLength::Is[..]Unit()`.
23 const unsigned short SVG_LENGTHTYPE_Q
= 11;
24 const unsigned short SVG_LENGTHTYPE_CH
= 12;
25 const unsigned short SVG_LENGTHTYPE_REM
= 13;
26 const unsigned short SVG_LENGTHTYPE_IC
= 14;
27 const unsigned short SVG_LENGTHTYPE_CAP
= 15;
28 const unsigned short SVG_LENGTHTYPE_LH
= 16;
29 const unsigned short SVG_LENGTHTYPE_RLH
= 17;
30 const unsigned short SVG_LENGTHTYPE_VW
= 18;
31 const unsigned short SVG_LENGTHTYPE_VH
= 19;
32 const unsigned short SVG_LENGTHTYPE_VMIN
= 20;
33 const unsigned short SVG_LENGTHTYPE_VMAX
= 21;
35 void SVGLength::GetValueAsString(nsAString
& aValue
) const {
36 nsTextFormatter::ssprintf(aValue
, u
"%g", (double)mValue
);
38 nsAutoString unitString
;
39 GetUnitString(unitString
, mUnit
);
40 aValue
.Append(unitString
);
43 bool SVGLength::SetValueFromString(const nsAString
& aString
) {
45 auto token
= SVGContentUtils::GetAndEnsureOneToken(aString
, success
);
51 nsAString::const_iterator iter
, end
;
52 aString
.BeginReading(iter
);
53 aString
.EndReading(end
);
57 if (!SVGContentUtils::ParseNumber(iter
, end
, value
)) {
61 const nsAString
& units
= Substring(iter
, end
);
62 uint16_t unitType
= GetUnitTypeForString(units
);
63 if (unitType
== SVG_LENGTHTYPE_UNKNOWN
) {
67 mUnit
= uint8_t(unitType
);
72 bool SVGLength::IsAbsoluteUnit(uint8_t aUnit
) {
73 return aUnit
== SVG_LENGTHTYPE_NUMBER
||
74 (aUnit
>= SVG_LENGTHTYPE_PX
&& aUnit
<= SVG_LENGTHTYPE_Q
);
78 bool SVGLength::IsFontRelativeUnit(uint8_t aUnit
) {
79 return aUnit
== SVG_LENGTHTYPE_EMS
|| aUnit
== SVG_LENGTHTYPE_EXS
||
80 (aUnit
>= SVG_LENGTHTYPE_CH
&& aUnit
<= SVG_LENGTHTYPE_RLH
);
84 * Helper to convert between different CSS absolute units without the need for
85 * an element, which provides more flexibility at the DOM level (and without
86 * the need for an intermediary conversion to user units, which avoids
87 * unnecessary overhead and rounding error).
89 * Example usage: to find out how many centimeters there are per inch:
91 * GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_CM, SVG_LENGTHTYPE_IN)
94 float SVGLength::GetAbsUnitsPerAbsUnit(uint8_t aUnits
, uint8_t aPerUnit
) {
95 MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aUnits
), "Not a CSS absolute unit");
96 MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aPerUnit
), "Not a CSS absolute unit");
98 static const float CSSAbsoluteUnitConversionFactors
[7][7] = {
99 // columns: px, cm, mm, in, pt, pc, q
101 {1.0f
, 37.7952755906f
, 3.779528f
, 96.0f
, 1.33333333333333333f
, 16.0f
,
104 {0.02645833333f
, 1.0f
, 0.1f
, 2.54f
, 0.035277777777777778f
,
105 0.42333333333333333f
, 0.025f
},
107 {0.26458333333f
, 10.0f
, 1.0f
, 25.4f
, 0.35277777777777778f
,
108 4.2333333333333333f
, 0.25f
},
110 {0.01041666666f
, 0.39370078740157481f
, 0.039370078740157481f
, 1.0f
,
111 0.013888888888888889f
, 0.16666666666666667f
, 0.02204860853f
},
113 {0.75f
, 28.346456692913386f
, 2.8346456692913386f
, 72.0f
, 1.0f
, 12.0f
,
116 {0.0625f
, 2.3622047244094489f
, 0.23622047244094489f
, 6.0f
,
117 0.083333333333333333f
, 1.0f
, 16.9333333333f
},
119 {1.0583333332f
, 40.0f
, 4.0f
, 45.354336f
, 1.41111111111f
, 16.9333333333f
,
122 auto ToIndex
= [](uint8_t aUnit
) {
123 return aUnit
== SVG_LENGTHTYPE_NUMBER
? 0 : aUnit
- 5;
126 return CSSAbsoluteUnitConversionFactors
[ToIndex(aUnits
)][ToIndex(aPerUnit
)];
129 float SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit
,
130 const SVGElement
* aElement
,
131 uint8_t aAxis
) const {
132 if (aUnit
== mUnit
) {
135 if ((aUnit
== SVG_LENGTHTYPE_NUMBER
&& mUnit
== SVG_LENGTHTYPE_PX
) ||
136 (aUnit
== SVG_LENGTHTYPE_PX
&& mUnit
== SVG_LENGTHTYPE_NUMBER
)) {
139 if (IsAbsoluteUnit(aUnit
) && IsAbsoluteUnit(mUnit
)) {
140 return mValue
* GetAbsUnitsPerAbsUnit(aUnit
, mUnit
);
143 // Otherwise we do a two step conversion via user units. This can only
144 // succeed if aElement is non-null (although that's not sufficient to
145 // guarantee success).
147 SVGElementMetrics
userSpaceMetrics(aElement
);
149 float userUnitsPerCurrentUnit
= GetPixelsPerUnit(userSpaceMetrics
, aAxis
);
150 float userUnitsPerNewUnit
=
151 SVGLength(0.0f
, aUnit
).GetPixelsPerUnit(userSpaceMetrics
, aAxis
);
153 float value
= mValue
* userUnitsPerCurrentUnit
/ userUnitsPerNewUnit
;
155 // userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could
157 if (std::isfinite(value
)) {
160 return std::numeric_limits
<float>::quiet_NaN();
165 enum class ZoomType
{ Self
, SelfFromRoot
, None
};
168 float SVGLength::GetPixelsPerUnit(const UserSpaceMetrics
& aMetrics
,
169 uint8_t aUnitType
, uint8_t aAxis
,
171 auto zoomType
= ZoomType::Self
;
172 float value
= [&]() -> float {
174 case SVG_LENGTHTYPE_NUMBER
:
175 case SVG_LENGTHTYPE_PX
:
177 case SVG_LENGTHTYPE_PERCENTAGE
:
178 zoomType
= ZoomType::None
;
179 return aMetrics
.GetAxisLength(aAxis
) / 100.0f
;
180 case SVG_LENGTHTYPE_EMS
:
181 zoomType
= ZoomType::None
;
182 return aMetrics
.GetEmLength(UserSpaceMetrics::Type::This
);
183 case SVG_LENGTHTYPE_EXS
:
184 zoomType
= ZoomType::None
;
185 return aMetrics
.GetExLength(UserSpaceMetrics::Type::This
);
186 case SVG_LENGTHTYPE_CH
:
187 zoomType
= ZoomType::None
;
188 return aMetrics
.GetChSize(UserSpaceMetrics::Type::This
);
189 case SVG_LENGTHTYPE_REM
:
190 zoomType
= ZoomType::SelfFromRoot
;
191 return aMetrics
.GetEmLength(UserSpaceMetrics::Type::Root
);
192 case SVG_LENGTHTYPE_IC
:
193 zoomType
= ZoomType::None
;
194 return aMetrics
.GetIcWidth(UserSpaceMetrics::Type::This
);
195 case SVG_LENGTHTYPE_CAP
:
196 zoomType
= ZoomType::None
;
197 return aMetrics
.GetCapHeight(UserSpaceMetrics::Type::This
);
198 case SVG_LENGTHTYPE_VW
:
199 return aMetrics
.GetCSSViewportSize().width
/ 100.f
;
200 case SVG_LENGTHTYPE_VH
:
201 return aMetrics
.GetCSSViewportSize().height
/ 100.f
;
202 case SVG_LENGTHTYPE_VMIN
: {
203 auto sz
= aMetrics
.GetCSSViewportSize();
204 return std::min(sz
.width
, sz
.height
) / 100.f
;
206 case SVG_LENGTHTYPE_VMAX
: {
207 auto sz
= aMetrics
.GetCSSViewportSize();
208 return std::max(sz
.width
, sz
.height
) / 100.f
;
210 case SVG_LENGTHTYPE_LH
:
211 zoomType
= ZoomType::None
;
212 return aMetrics
.GetLineHeight(UserSpaceMetrics::Type::This
);
213 case SVG_LENGTHTYPE_RLH
:
214 zoomType
= ZoomType::SelfFromRoot
;
215 return aMetrics
.GetLineHeight(UserSpaceMetrics::Type::Root
);
217 MOZ_ASSERT(IsAbsoluteUnit(aUnitType
));
218 return GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_PX
, aUnitType
);
226 value
*= aMetrics
.GetZoom();
228 case ZoomType::SelfFromRoot
:
229 value
*= aMetrics
.GetZoom() / aMetrics
.GetRootZoom();
237 nsCSSUnit
SVGLength::SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit
) {
238 switch (aSpecifiedUnit
) {
239 #define SVG_LENGTH_EMPTY_UNIT(id, cssValue) \
242 #define SVG_LENGTH_UNIT(id, name, cssValue) SVG_LENGTH_EMPTY_UNIT(id, cssValue)
243 #include "mozilla/dom/SVGLengthUnits.h"
244 #undef SVG_LENGTH_UNIT
245 #undef SVG_LENGTH_EMPTY_UNIT
247 MOZ_ASSERT_UNREACHABLE("Unknown unit type");
248 return nsCSSUnit::eCSSUnit_Pixel
;
253 void SVGLength::GetUnitString(nsAString
& aUnit
, uint16_t aUnitType
) {
255 #define SVG_LENGTH_EMPTY_UNIT(id, cssValue) \
259 #define SVG_LENGTH_UNIT(id, name, cssValue) \
261 aUnit.AssignLiteral(name); \
263 #include "mozilla/dom/SVGLengthUnits.h"
264 #undef SVG_LENGTH_UNIT
265 #undef SVG_LENGTH_EMPTY_UNIT
267 MOZ_ASSERT_UNREACHABLE(
268 "Unknown unit type! Someone's using an SVGLength "
269 "with an invalid unit?");
273 uint16_t SVGLength::GetUnitTypeForString(const nsAString
& aUnit
) {
274 #define SVG_LENGTH_EMPTY_UNIT(id, cssValue) \
275 if (aUnit.IsEmpty()) { \
278 #define SVG_LENGTH_UNIT(id, name, cssValue) \
279 if (aUnit.LowerCaseEqualsLiteral(name)) { \
282 #include "mozilla/dom/SVGLengthUnits.h"
283 #undef SVG_LENGTH_UNIT
284 #undef SVG_LENGTH_EMPTY_UNIT
286 return SVG_LENGTHTYPE_UNKNOWN
;
289 } // namespace mozilla