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;
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_VW
= 16;
29 const unsigned short SVG_LENGTHTYPE_VH
= 17;
30 const unsigned short SVG_LENGTHTYPE_VMIN
= 18;
31 const unsigned short SVG_LENGTHTYPE_VMAX
= 19;
33 void SVGLength::GetValueAsString(nsAString
& aValue
) const {
34 nsTextFormatter::ssprintf(aValue
, u
"%g", (double)mValue
);
36 nsAutoString unitString
;
37 GetUnitString(unitString
, mUnit
);
38 aValue
.Append(unitString
);
41 bool SVGLength::SetValueFromString(const nsAString
& aString
) {
43 auto token
= SVGContentUtils::GetAndEnsureOneToken(aString
, success
);
49 RangedPtr
<const char16_t
> iter
= SVGContentUtils::GetStartRangedPtr(token
);
50 const RangedPtr
<const char16_t
> end
= SVGContentUtils::GetEndRangedPtr(token
);
54 if (!SVGContentUtils::ParseNumber(iter
, end
, value
)) {
58 const nsAString
& units
= Substring(iter
.get(), end
.get());
59 uint16_t unitType
= GetUnitTypeForString(units
);
60 if (unitType
== SVG_LENGTHTYPE_UNKNOWN
) {
64 mUnit
= uint8_t(unitType
);
69 bool SVGLength::IsAbsoluteUnit(uint8_t aUnit
) {
70 return aUnit
== SVG_LENGTHTYPE_NUMBER
||
71 (aUnit
>= SVG_LENGTHTYPE_PX
&& aUnit
<= SVG_LENGTHTYPE_Q
);
75 bool SVGLength::IsFontRelativeUnit(uint8_t aUnit
) {
76 return aUnit
== SVG_LENGTHTYPE_EMS
|| aUnit
== SVG_LENGTHTYPE_EXS
||
77 (aUnit
>= SVG_LENGTHTYPE_CH
&& aUnit
<= SVG_LENGTHTYPE_CAP
);
81 * Helper to convert between different CSS absolute units without the need for
82 * an element, which provides more flexibility at the DOM level (and without
83 * the need for an intermediary conversion to user units, which avoids
84 * unnecessary overhead and rounding error).
86 * Example usage: to find out how many centimeters there are per inch:
88 * GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_CM, SVG_LENGTHTYPE_IN)
91 float SVGLength::GetAbsUnitsPerAbsUnit(uint8_t aUnits
, uint8_t aPerUnit
) {
92 MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aUnits
), "Not a CSS absolute unit");
93 MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aPerUnit
), "Not a CSS absolute unit");
95 static const float CSSAbsoluteUnitConversionFactors
[7][7] = {
96 // columns: px, cm, mm, in, pt, pc, q
98 {1.0f
, 37.7952755906f
, 3.779528f
, 96.0f
, 1.33333333333333333f
, 16.0f
,
101 {0.02645833333f
, 1.0f
, 0.1f
, 2.54f
, 0.035277777777777778f
,
102 0.42333333333333333f
, 0.025f
},
104 {0.26458333333f
, 10.0f
, 1.0f
, 25.4f
, 0.35277777777777778f
,
105 4.2333333333333333f
, 0.25f
},
107 {0.01041666666f
, 0.39370078740157481f
, 0.039370078740157481f
, 1.0f
,
108 0.013888888888888889f
, 0.16666666666666667f
, 0.02204860853f
},
110 {0.75f
, 28.346456692913386f
, 2.8346456692913386f
, 72.0f
, 1.0f
, 12.0f
,
113 {0.0625f
, 2.3622047244094489f
, 0.23622047244094489f
, 6.0f
,
114 0.083333333333333333f
, 1.0f
, 16.9333333333f
},
116 {1.0583333332f
, 40.0f
, 4.0f
, 45.354336f
, 1.41111111111f
, 16.9333333333f
,
119 auto ToIndex
= [](uint8_t aUnit
) {
120 return aUnit
== SVG_LENGTHTYPE_NUMBER
? 0 : aUnit
- 5;
123 return CSSAbsoluteUnitConversionFactors
[ToIndex(aUnits
)][ToIndex(aPerUnit
)];
126 float SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit
,
127 const SVGElement
* aElement
,
128 uint8_t aAxis
) const {
129 if (aUnit
== mUnit
) {
132 if ((aUnit
== SVG_LENGTHTYPE_NUMBER
&& mUnit
== SVG_LENGTHTYPE_PX
) ||
133 (aUnit
== SVG_LENGTHTYPE_PX
&& mUnit
== SVG_LENGTHTYPE_NUMBER
)) {
136 if (IsAbsoluteUnit(aUnit
) && IsAbsoluteUnit(mUnit
)) {
137 return mValue
* GetAbsUnitsPerAbsUnit(aUnit
, mUnit
);
140 // Otherwise we do a two step conversion via user units. This can only
141 // succeed if aElement is non-null (although that's not sufficient to
142 // guarantee success).
144 SVGElementMetrics
userSpaceMetrics(aElement
);
146 float userUnitsPerCurrentUnit
= GetPixelsPerUnit(userSpaceMetrics
, aAxis
);
147 float userUnitsPerNewUnit
=
148 SVGLength(0.0f
, aUnit
).GetPixelsPerUnit(userSpaceMetrics
, aAxis
);
150 float value
= mValue
* userUnitsPerCurrentUnit
/ userUnitsPerNewUnit
;
152 // userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could
154 if (std::isfinite(value
)) {
157 return std::numeric_limits
<float>::quiet_NaN();
163 float SVGLength::GetPixelsPerUnit(const UserSpaceMetrics
& aMetrics
,
164 uint8_t aUnitType
, uint8_t aAxis
) {
166 case SVG_LENGTHTYPE_NUMBER
:
167 case SVG_LENGTHTYPE_PX
:
169 case SVG_LENGTHTYPE_PERCENTAGE
:
170 return aMetrics
.GetAxisLength(aAxis
) / 100.0f
;
171 case SVG_LENGTHTYPE_EMS
:
172 return aMetrics
.GetEmLength(UserSpaceMetrics::Type::This
);
173 case SVG_LENGTHTYPE_EXS
:
174 return aMetrics
.GetExLength(UserSpaceMetrics::Type::This
);
175 case SVG_LENGTHTYPE_CH
:
176 return aMetrics
.GetChSize(UserSpaceMetrics::Type::This
);
177 case SVG_LENGTHTYPE_REM
:
178 return aMetrics
.GetEmLength(UserSpaceMetrics::Type::Root
);
179 case SVG_LENGTHTYPE_IC
:
180 return aMetrics
.GetIcWidth(UserSpaceMetrics::Type::This
);
181 case SVG_LENGTHTYPE_CAP
:
182 return aMetrics
.GetCapHeight(UserSpaceMetrics::Type::This
);
183 case SVG_LENGTHTYPE_VW
:
184 return aMetrics
.GetCSSViewportSize().width
/ 100.f
;
185 case SVG_LENGTHTYPE_VH
:
186 return aMetrics
.GetCSSViewportSize().height
/ 100.f
;
187 case SVG_LENGTHTYPE_VMIN
: {
188 auto sz
= aMetrics
.GetCSSViewportSize();
189 return std::min(sz
.width
, sz
.height
) / 100.f
;
191 case SVG_LENGTHTYPE_VMAX
: {
192 auto sz
= aMetrics
.GetCSSViewportSize();
193 return std::max(sz
.width
, sz
.height
) / 100.f
;
196 MOZ_ASSERT(IsAbsoluteUnit(aUnitType
));
197 return GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_PX
, aUnitType
);
202 nsCSSUnit
SVGLength::SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit
) {
203 switch (aSpecifiedUnit
) {
204 case SVG_LENGTHTYPE_NUMBER
:
205 case SVG_LENGTHTYPE_PX
:
206 return nsCSSUnit::eCSSUnit_Pixel
;
208 case SVG_LENGTHTYPE_MM
:
209 return nsCSSUnit::eCSSUnit_Millimeter
;
211 case SVG_LENGTHTYPE_CM
:
212 return nsCSSUnit::eCSSUnit_Centimeter
;
214 case SVG_LENGTHTYPE_IN
:
215 return nsCSSUnit::eCSSUnit_Inch
;
217 case SVG_LENGTHTYPE_PT
:
218 return nsCSSUnit::eCSSUnit_Point
;
220 case SVG_LENGTHTYPE_PC
:
221 return nsCSSUnit::eCSSUnit_Pica
;
223 case SVG_LENGTHTYPE_PERCENTAGE
:
224 return nsCSSUnit::eCSSUnit_Percent
;
226 case SVG_LENGTHTYPE_EMS
:
227 return nsCSSUnit::eCSSUnit_EM
;
229 case SVG_LENGTHTYPE_EXS
:
230 return nsCSSUnit::eCSSUnit_XHeight
;
232 case SVG_LENGTHTYPE_Q
:
233 return nsCSSUnit::eCSSUnit_Quarter
;
235 case SVG_LENGTHTYPE_CH
:
236 return nsCSSUnit::eCSSUnit_Char
;
238 case SVG_LENGTHTYPE_REM
:
239 return nsCSSUnit::eCSSUnit_RootEM
;
241 case SVG_LENGTHTYPE_IC
:
242 return nsCSSUnit::eCSSUnit_Ideographic
;
244 case SVG_LENGTHTYPE_CAP
:
245 return nsCSSUnit::eCSSUnit_CapHeight
;
247 case SVG_LENGTHTYPE_VW
:
248 return nsCSSUnit::eCSSUnit_VW
;
250 case SVG_LENGTHTYPE_VH
:
251 return nsCSSUnit::eCSSUnit_VH
;
253 case SVG_LENGTHTYPE_VMIN
:
254 return nsCSSUnit::eCSSUnit_VMin
;
256 case SVG_LENGTHTYPE_VMAX
:
257 return nsCSSUnit::eCSSUnit_VMax
;
260 MOZ_ASSERT_UNREACHABLE("Unknown unit type");
261 return nsCSSUnit::eCSSUnit_Pixel
;
266 void SVGLength::GetUnitString(nsAString
& aUnit
, uint16_t aUnitType
) {
268 case SVG_LENGTHTYPE_NUMBER
:
271 case SVG_LENGTHTYPE_PERCENTAGE
:
272 aUnit
.AssignLiteral("%");
274 case SVG_LENGTHTYPE_EMS
:
275 aUnit
.AssignLiteral("em");
277 case SVG_LENGTHTYPE_EXS
:
278 aUnit
.AssignLiteral("ex");
280 case SVG_LENGTHTYPE_PX
:
281 aUnit
.AssignLiteral("px");
283 case SVG_LENGTHTYPE_CM
:
284 aUnit
.AssignLiteral("cm");
286 case SVG_LENGTHTYPE_MM
:
287 aUnit
.AssignLiteral("mm");
289 case SVG_LENGTHTYPE_IN
:
290 aUnit
.AssignLiteral("in");
292 case SVG_LENGTHTYPE_PT
:
293 aUnit
.AssignLiteral("pt");
295 case SVG_LENGTHTYPE_PC
:
296 aUnit
.AssignLiteral("pc");
298 case SVG_LENGTHTYPE_Q
:
299 aUnit
.AssignLiteral("q");
301 case SVG_LENGTHTYPE_CH
:
302 aUnit
.AssignLiteral("ch");
304 case SVG_LENGTHTYPE_REM
:
305 aUnit
.AssignLiteral("rem");
307 case SVG_LENGTHTYPE_IC
:
308 aUnit
.AssignLiteral("ic");
310 case SVG_LENGTHTYPE_CAP
:
311 aUnit
.AssignLiteral("cap");
313 case SVG_LENGTHTYPE_VW
:
314 aUnit
.AssignLiteral("vw");
316 case SVG_LENGTHTYPE_VH
:
317 aUnit
.AssignLiteral("vh");
319 case SVG_LENGTHTYPE_VMIN
:
320 aUnit
.AssignLiteral("vmin");
322 case SVG_LENGTHTYPE_VMAX
:
323 aUnit
.AssignLiteral("vmax");
326 MOZ_ASSERT_UNREACHABLE(
327 "Unknown unit type! Someone's using an SVGLength "
328 "with an invalid unit?");
332 uint16_t SVGLength::GetUnitTypeForString(const nsAString
& aUnit
) {
333 if (aUnit
.IsEmpty()) {
334 return SVG_LENGTHTYPE_NUMBER
;
336 if (aUnit
.EqualsLiteral("%")) {
337 return SVG_LENGTHTYPE_PERCENTAGE
;
339 if (aUnit
.LowerCaseEqualsLiteral("em")) {
340 return SVG_LENGTHTYPE_EMS
;
342 if (aUnit
.LowerCaseEqualsLiteral("ex")) {
343 return SVG_LENGTHTYPE_EXS
;
345 if (aUnit
.LowerCaseEqualsLiteral("px")) {
346 return SVG_LENGTHTYPE_PX
;
348 if (aUnit
.LowerCaseEqualsLiteral("cm")) {
349 return SVG_LENGTHTYPE_CM
;
351 if (aUnit
.LowerCaseEqualsLiteral("mm")) {
352 return SVG_LENGTHTYPE_MM
;
354 if (aUnit
.LowerCaseEqualsLiteral("in")) {
355 return SVG_LENGTHTYPE_IN
;
357 if (aUnit
.LowerCaseEqualsLiteral("pt")) {
358 return SVG_LENGTHTYPE_PT
;
360 if (aUnit
.LowerCaseEqualsLiteral("pc")) {
361 return SVG_LENGTHTYPE_PC
;
363 if (aUnit
.LowerCaseEqualsLiteral("q")) {
364 return SVG_LENGTHTYPE_Q
;
366 if (aUnit
.LowerCaseEqualsLiteral("ch")) {
367 return SVG_LENGTHTYPE_CH
;
369 if (aUnit
.LowerCaseEqualsLiteral("rem")) {
370 return SVG_LENGTHTYPE_REM
;
372 if (aUnit
.LowerCaseEqualsLiteral("ic")) {
373 return SVG_LENGTHTYPE_IC
;
375 if (aUnit
.LowerCaseEqualsLiteral("cap")) {
376 return SVG_LENGTHTYPE_CAP
;
378 if (aUnit
.LowerCaseEqualsLiteral("vw")) {
379 return SVG_LENGTHTYPE_VW
;
381 if (aUnit
.LowerCaseEqualsLiteral("vh")) {
382 return SVG_LENGTHTYPE_VH
;
384 if (aUnit
.LowerCaseEqualsLiteral("vmin")) {
385 return SVG_LENGTHTYPE_VMIN
;
387 if (aUnit
.LowerCaseEqualsLiteral("vmax")) {
388 return SVG_LENGTHTYPE_VMAX
;
390 return SVG_LENGTHTYPE_UNKNOWN
;
393 } // namespace mozilla