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/ArrayUtils.h"
10 #include "mozilla/dom/SVGElement.h"
11 #include "mozilla/dom/SVGSVGElement.h"
12 #include "nsCSSValue.h"
13 #include "nsTextFormatter.h"
14 #include "SVGContentUtils.h"
18 using namespace mozilla::dom
;
19 using namespace mozilla::dom::SVGLength_Binding
;
23 const unsigned short SVG_LENGTHTYPE_Q
= 11;
25 void SVGLength::GetValueAsString(nsAString
& aValue
) const {
26 nsTextFormatter::ssprintf(aValue
, u
"%g", (double)mValue
);
28 nsAutoString unitString
;
29 GetUnitString(unitString
, mUnit
);
30 aValue
.Append(unitString
);
33 bool SVGLength::SetValueFromString(const nsAString
& aString
) {
35 auto token
= SVGContentUtils::GetAndEnsureOneToken(aString
, success
);
41 RangedPtr
<const char16_t
> iter
= SVGContentUtils::GetStartRangedPtr(token
);
42 const RangedPtr
<const char16_t
> end
= SVGContentUtils::GetEndRangedPtr(token
);
46 if (!SVGContentUtils::ParseNumber(iter
, end
, value
)) {
50 const nsAString
& units
= Substring(iter
.get(), end
.get());
51 uint16_t unitType
= GetUnitTypeForString(units
);
52 if (unitType
== SVG_LENGTHTYPE_UNKNOWN
) {
56 mUnit
= uint8_t(unitType
);
61 bool SVGLength::IsAbsoluteUnit(uint8_t aUnit
) {
62 return aUnit
== SVG_LENGTHTYPE_NUMBER
||
63 (aUnit
>= SVG_LENGTHTYPE_PX
&& aUnit
<= SVG_LENGTHTYPE_Q
);
67 bool SVGLength::IsFontRelativeUnit(uint8_t aUnit
) {
68 return aUnit
== SVG_LENGTHTYPE_EMS
|| aUnit
== SVG_LENGTHTYPE_EXS
;
72 * Helper to convert between different CSS absolute units without the need for
73 * an element, which provides more flexibility at the DOM level (and without
74 * the need for an intermediary conversion to user units, which avoids
75 * unnecessary overhead and rounding error).
77 * Example usage: to find out how many centimeters there are per inch:
79 * GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_CM, SVG_LENGTHTYPE_IN)
82 float SVGLength::GetAbsUnitsPerAbsUnit(uint8_t aUnits
, uint8_t aPerUnit
) {
83 MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aUnits
), "Not a CSS absolute unit");
84 MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aPerUnit
), "Not a CSS absolute unit");
86 static const float CSSAbsoluteUnitConversionFactors
[7][7] = {
87 // columns: px, cm, mm, in, pt, pc, q
89 {1.0f
, 37.7952755906f
, 3.779528f
, 96.0f
, 1.33333333333333333f
, 16.0f
,
92 {0.02645833333f
, 1.0f
, 0.1f
, 2.54f
, 0.035277777777777778f
,
93 0.42333333333333333f
, 0.025f
},
95 {0.26458333333f
, 10.0f
, 1.0f
, 25.4f
, 0.35277777777777778f
,
96 4.2333333333333333f
, 0.25f
},
98 {0.01041666666f
, 0.39370078740157481f
, 0.039370078740157481f
, 1.0f
,
99 0.013888888888888889f
, 0.16666666666666667f
, 0.02204860853f
},
101 {0.75f
, 28.346456692913386f
, 2.8346456692913386f
, 72.0f
, 1.0f
, 12.0f
,
104 {0.0625f
, 2.3622047244094489f
, 0.23622047244094489f
, 6.0f
,
105 0.083333333333333333f
, 1.0f
, 16.9333333333f
},
107 {1.0583333332f
, 40.0f
, 4.0f
, 45.354336f
, 1.41111111111f
, 16.9333333333f
,
110 auto ToIndex
= [](uint8_t aUnit
) {
111 return aUnit
== SVG_LENGTHTYPE_NUMBER
? 0 : aUnit
- 5;
114 return CSSAbsoluteUnitConversionFactors
[ToIndex(aUnits
)][ToIndex(aPerUnit
)];
117 float SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit
,
118 const SVGElement
* aElement
,
119 uint8_t aAxis
) const {
120 if (aUnit
== mUnit
) {
123 if ((aUnit
== SVG_LENGTHTYPE_NUMBER
&& mUnit
== SVG_LENGTHTYPE_PX
) ||
124 (aUnit
== SVG_LENGTHTYPE_PX
&& mUnit
== SVG_LENGTHTYPE_NUMBER
)) {
127 if (IsAbsoluteUnit(aUnit
) && IsAbsoluteUnit(mUnit
)) {
128 return mValue
* GetAbsUnitsPerAbsUnit(aUnit
, mUnit
);
131 // Otherwise we do a two step conversion via user units. This can only
132 // succeed if aElement is non-null (although that's not sufficient to
133 // guarantee success).
135 SVGElementMetrics
userSpaceMetrics(aElement
);
137 float userUnitsPerCurrentUnit
= GetPixelsPerUnit(userSpaceMetrics
, aAxis
);
138 float userUnitsPerNewUnit
=
139 SVGLength(0.0f
, aUnit
).GetPixelsPerUnit(userSpaceMetrics
, aAxis
);
142 userUnitsPerCurrentUnit
>= 0 || !std::isfinite(userUnitsPerCurrentUnit
),
143 "bad userUnitsPerCurrentUnit");
144 NS_ASSERTION(userUnitsPerNewUnit
>= 0 || !std::isfinite(userUnitsPerNewUnit
),
145 "bad userUnitsPerNewUnit");
147 float value
= mValue
* userUnitsPerCurrentUnit
/ userUnitsPerNewUnit
;
149 // userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could
151 if (std::isfinite(value
)) {
154 return std::numeric_limits
<float>::quiet_NaN();
160 float SVGLength::GetPixelsPerUnit(const UserSpaceMetrics
& aMetrics
,
161 uint8_t aUnitType
, uint8_t aAxis
) {
163 case SVG_LENGTHTYPE_NUMBER
:
164 case SVG_LENGTHTYPE_PX
:
166 case SVG_LENGTHTYPE_PERCENTAGE
:
167 return aMetrics
.GetAxisLength(aAxis
) / 100.0f
;
168 case SVG_LENGTHTYPE_EMS
:
169 return aMetrics
.GetEmLength();
170 case SVG_LENGTHTYPE_EXS
:
171 return aMetrics
.GetExLength();
173 MOZ_ASSERT(IsAbsoluteUnit(aUnitType
));
174 return GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_PX
, aUnitType
);
179 nsCSSUnit
SVGLength::SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit
) {
180 switch (aSpecifiedUnit
) {
181 case SVG_LENGTHTYPE_NUMBER
:
182 case SVG_LENGTHTYPE_PX
:
183 return nsCSSUnit::eCSSUnit_Pixel
;
185 case SVG_LENGTHTYPE_MM
:
186 return nsCSSUnit::eCSSUnit_Millimeter
;
188 case SVG_LENGTHTYPE_CM
:
189 return nsCSSUnit::eCSSUnit_Centimeter
;
191 case SVG_LENGTHTYPE_IN
:
192 return nsCSSUnit::eCSSUnit_Inch
;
194 case SVG_LENGTHTYPE_PT
:
195 return nsCSSUnit::eCSSUnit_Point
;
197 case SVG_LENGTHTYPE_PC
:
198 return nsCSSUnit::eCSSUnit_Pica
;
200 case SVG_LENGTHTYPE_PERCENTAGE
:
201 return nsCSSUnit::eCSSUnit_Percent
;
203 case SVG_LENGTHTYPE_EMS
:
204 return nsCSSUnit::eCSSUnit_EM
;
206 case SVG_LENGTHTYPE_EXS
:
207 return nsCSSUnit::eCSSUnit_XHeight
;
209 case SVG_LENGTHTYPE_Q
:
210 return nsCSSUnit::eCSSUnit_Quarter
;
213 MOZ_ASSERT_UNREACHABLE("Unknown unit type");
214 return nsCSSUnit::eCSSUnit_Pixel
;
219 void SVGLength::GetUnitString(nsAString
& aUnit
, uint16_t aUnitType
) {
221 case SVG_LENGTHTYPE_NUMBER
:
224 case SVG_LENGTHTYPE_PERCENTAGE
:
225 aUnit
.AssignLiteral("%");
227 case SVG_LENGTHTYPE_EMS
:
228 aUnit
.AssignLiteral("em");
230 case SVG_LENGTHTYPE_EXS
:
231 aUnit
.AssignLiteral("ex");
233 case SVG_LENGTHTYPE_PX
:
234 aUnit
.AssignLiteral("px");
236 case SVG_LENGTHTYPE_CM
:
237 aUnit
.AssignLiteral("cm");
239 case SVG_LENGTHTYPE_MM
:
240 aUnit
.AssignLiteral("mm");
242 case SVG_LENGTHTYPE_IN
:
243 aUnit
.AssignLiteral("in");
245 case SVG_LENGTHTYPE_PT
:
246 aUnit
.AssignLiteral("pt");
248 case SVG_LENGTHTYPE_PC
:
249 aUnit
.AssignLiteral("pc");
251 case SVG_LENGTHTYPE_Q
:
252 aUnit
.AssignLiteral("q");
255 MOZ_ASSERT_UNREACHABLE(
256 "Unknown unit type! Someone's using an SVGLength "
257 "with an invalid unit?");
261 uint16_t SVGLength::GetUnitTypeForString(const nsAString
& aUnit
) {
262 if (aUnit
.IsEmpty()) {
263 return SVG_LENGTHTYPE_NUMBER
;
265 if (aUnit
.EqualsLiteral("%")) {
266 return SVG_LENGTHTYPE_PERCENTAGE
;
268 if (aUnit
.LowerCaseEqualsLiteral("em")) {
269 return SVG_LENGTHTYPE_EMS
;
271 if (aUnit
.LowerCaseEqualsLiteral("ex")) {
272 return SVG_LENGTHTYPE_EXS
;
274 if (aUnit
.LowerCaseEqualsLiteral("px")) {
275 return SVG_LENGTHTYPE_PX
;
277 if (aUnit
.LowerCaseEqualsLiteral("cm")) {
278 return SVG_LENGTHTYPE_CM
;
280 if (aUnit
.LowerCaseEqualsLiteral("mm")) {
281 return SVG_LENGTHTYPE_MM
;
283 if (aUnit
.LowerCaseEqualsLiteral("in")) {
284 return SVG_LENGTHTYPE_IN
;
286 if (aUnit
.LowerCaseEqualsLiteral("pt")) {
287 return SVG_LENGTHTYPE_PT
;
289 if (aUnit
.LowerCaseEqualsLiteral("pc")) {
290 return SVG_LENGTHTYPE_PC
;
292 if (aUnit
.LowerCaseEqualsLiteral("q")) {
293 return SVG_LENGTHTYPE_Q
;
295 return SVG_LENGTHTYPE_UNKNOWN
;
298 } // namespace mozilla