Line data Source code
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/. */
6 :
7 : #include "mozilla/ArrayUtils.h"
8 :
9 : #include "SVGLength.h"
10 : #include "nsSVGElement.h"
11 : #include "mozilla/dom/SVGSVGElement.h"
12 : #include "nsTextFormatter.h"
13 : #include "SVGContentUtils.h"
14 : #include <limits>
15 : #include <algorithm>
16 :
17 : namespace mozilla {
18 :
19 : // Declare some helpers defined below:
20 : static void GetUnitString(nsAString& unit, uint16_t unitType);
21 : static uint16_t GetUnitTypeForString(const nsAString& unitStr);
22 :
23 : void
24 0 : SVGLength::GetValueAsString(nsAString &aValue) const
25 : {
26 : char16_t buf[24];
27 0 : nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
28 : u"%g",
29 0 : (double)mValue);
30 0 : aValue.Assign(buf);
31 :
32 0 : nsAutoString unitString;
33 0 : GetUnitString(unitString, mUnit);
34 0 : aValue.Append(unitString);
35 0 : }
36 :
37 : bool
38 0 : SVGLength::SetValueFromString(const nsAString &aString)
39 : {
40 : RangedPtr<const char16_t> iter =
41 0 : SVGContentUtils::GetStartRangedPtr(aString);
42 : const RangedPtr<const char16_t> end =
43 0 : SVGContentUtils::GetEndRangedPtr(aString);
44 :
45 : float value;
46 :
47 0 : if (!SVGContentUtils::ParseNumber(iter, end, value)) {
48 0 : return false;
49 : }
50 :
51 0 : const nsAString& units = Substring(iter.get(), end.get());
52 0 : uint16_t unitType = GetUnitTypeForString(units);
53 0 : if (!IsValidUnitType(unitType)) {
54 0 : return false;
55 : }
56 0 : mValue = value;
57 0 : mUnit = uint8_t(unitType);
58 0 : return true;
59 : }
60 :
61 : inline static bool
62 0 : IsAbsoluteUnit(uint8_t aUnit)
63 : {
64 0 : return aUnit >= nsIDOMSVGLength::SVG_LENGTHTYPE_CM &&
65 0 : aUnit <= nsIDOMSVGLength::SVG_LENGTHTYPE_PC;
66 : }
67 :
68 : /**
69 : * Helper to convert between different CSS absolute units without the need for
70 : * an element, which provides more flexibility at the DOM level (and without
71 : * the need for an intermediary conversion to user units, which avoids
72 : * unnecessary overhead and rounding error).
73 : *
74 : * Example usage: to find out how many centimeters there are per inch:
75 : *
76 : * GetAbsUnitsPerAbsUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_CM,
77 : * nsIDOMSVGLength::SVG_LENGTHTYPE_IN)
78 : */
79 0 : inline static float GetAbsUnitsPerAbsUnit(uint8_t aUnits, uint8_t aPerUnit)
80 : {
81 0 : MOZ_ASSERT(IsAbsoluteUnit(aUnits), "Not a CSS absolute unit");
82 0 : MOZ_ASSERT(IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit");
83 :
84 : float CSSAbsoluteUnitConversionFactors[5][5] = { // columns: cm, mm, in, pt, pc
85 : // cm per...:
86 : { 1.0f, 0.1f, 2.54f, 0.035277777777777778f, 0.42333333333333333f },
87 : // mm per...:
88 : { 10.0f, 1.0f, 25.4f, 0.35277777777777778f, 4.2333333333333333f },
89 : // in per...:
90 : { 0.39370078740157481f, 0.039370078740157481f, 1.0f, 0.013888888888888889f, 0.16666666666666667f },
91 : // pt per...:
92 : { 28.346456692913386f, 2.8346456692913386f, 72.0f, 1.0f, 12.0f },
93 : // pc per...:
94 : { 2.3622047244094489f, 0.23622047244094489f, 6.0f, 0.083333333333333333f, 1.0f }
95 0 : };
96 :
97 : // First absolute unit is SVG_LENGTHTYPE_CM = 6
98 0 : return CSSAbsoluteUnitConversionFactors[aUnits - 6][aPerUnit - 6];
99 : }
100 :
101 : float
102 0 : SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit,
103 : const nsSVGElement *aElement,
104 : uint8_t aAxis) const
105 : {
106 0 : if (aUnit == mUnit) {
107 0 : return mValue;
108 : }
109 0 : if ((aUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER &&
110 0 : mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) ||
111 0 : (aUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX &&
112 0 : mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER)) {
113 0 : return mValue;
114 : }
115 0 : if (IsAbsoluteUnit(aUnit) && IsAbsoluteUnit(mUnit)) {
116 0 : return mValue * GetAbsUnitsPerAbsUnit(aUnit, mUnit);
117 : }
118 :
119 : // Otherwise we do a two step convertion via user units. This can only
120 : // succeed if aElement is non-null (although that's not sufficent to
121 : // guarantee success).
122 :
123 0 : float userUnitsPerCurrentUnit = GetUserUnitsPerUnit(aElement, aAxis);
124 : float userUnitsPerNewUnit =
125 0 : SVGLength(0.0f, aUnit).GetUserUnitsPerUnit(aElement, aAxis);
126 :
127 0 : NS_ASSERTION(userUnitsPerCurrentUnit >= 0 ||
128 : !IsFinite(userUnitsPerCurrentUnit),
129 : "bad userUnitsPerCurrentUnit");
130 0 : NS_ASSERTION(userUnitsPerNewUnit >= 0 ||
131 : !IsFinite(userUnitsPerNewUnit),
132 : "bad userUnitsPerNewUnit");
133 :
134 0 : float value = mValue * userUnitsPerCurrentUnit / userUnitsPerNewUnit;
135 :
136 : // userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could
137 : // be zero.
138 0 : if (IsFinite(value)) {
139 0 : return value;
140 : }
141 0 : return std::numeric_limits<float>::quiet_NaN();
142 : }
143 :
144 : #define INCHES_PER_MM_FLOAT float(0.0393700787)
145 : #define INCHES_PER_CM_FLOAT float(0.393700787)
146 :
147 : float
148 0 : SVGLength::GetUserUnitsPerUnit(const nsSVGElement *aElement, uint8_t aAxis) const
149 : {
150 0 : switch (mUnit) {
151 : case nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER:
152 : case nsIDOMSVGLength::SVG_LENGTHTYPE_PX:
153 0 : return 1.0f;
154 : case nsIDOMSVGLength::SVG_LENGTHTYPE_MM:
155 0 : return INCHES_PER_MM_FLOAT * GetUserUnitsPerInch();
156 : case nsIDOMSVGLength::SVG_LENGTHTYPE_CM:
157 0 : return INCHES_PER_CM_FLOAT * GetUserUnitsPerInch();
158 : case nsIDOMSVGLength::SVG_LENGTHTYPE_IN:
159 0 : return GetUserUnitsPerInch();
160 : case nsIDOMSVGLength::SVG_LENGTHTYPE_PT:
161 0 : return (1.0f/POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch();
162 : case nsIDOMSVGLength::SVG_LENGTHTYPE_PC:
163 0 : return (12.0f/POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch();
164 : case nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE:
165 0 : return GetUserUnitsPerPercent(aElement, aAxis);
166 : case nsIDOMSVGLength::SVG_LENGTHTYPE_EMS:
167 0 : return SVGContentUtils::GetFontSize(const_cast<nsSVGElement*>(aElement));
168 : case nsIDOMSVGLength::SVG_LENGTHTYPE_EXS:
169 0 : return SVGContentUtils::GetFontXHeight(const_cast<nsSVGElement*>(aElement));
170 : default:
171 0 : NS_NOTREACHED("Unknown unit type");
172 0 : return std::numeric_limits<float>::quiet_NaN();
173 : }
174 : }
175 :
176 : /* static */ float
177 0 : SVGLength::GetUserUnitsPerPercent(const nsSVGElement *aElement, uint8_t aAxis)
178 : {
179 0 : if (aElement) {
180 0 : dom::SVGSVGElement *viewportElement = aElement->GetCtx();
181 0 : if (viewportElement) {
182 0 : return std::max(viewportElement->GetLength(aAxis) / 100.0f, 0.0f);
183 : }
184 : }
185 0 : return std::numeric_limits<float>::quiet_NaN();
186 : }
187 :
188 : // Helpers:
189 :
190 : // These items must be at the same index as the nsIDOMSVGLength constants!
191 : static nsIAtom** const unitMap[] =
192 : {
193 : nullptr, /* SVG_LENGTHTYPE_UNKNOWN */
194 : nullptr, /* SVG_LENGTHTYPE_NUMBER */
195 : &nsGkAtoms::percentage,
196 : &nsGkAtoms::em,
197 : &nsGkAtoms::ex,
198 : &nsGkAtoms::px,
199 : &nsGkAtoms::cm,
200 : &nsGkAtoms::mm,
201 : &nsGkAtoms::in,
202 : &nsGkAtoms::pt,
203 : &nsGkAtoms::pc
204 : };
205 :
206 : static void
207 0 : GetUnitString(nsAString& unit, uint16_t unitType)
208 : {
209 0 : if (SVGLength::IsValidUnitType(unitType)) {
210 0 : if (unitMap[unitType]) {
211 0 : (*unitMap[unitType])->ToString(unit);
212 : }
213 0 : return;
214 : }
215 0 : NS_NOTREACHED("Unknown unit type"); // Someone's using an SVGLength with an invalid unit?
216 0 : return;
217 : }
218 :
219 : static uint16_t
220 0 : GetUnitTypeForString(const nsAString& unitStr)
221 : {
222 0 : if (unitStr.IsEmpty())
223 0 : return nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER;
224 :
225 0 : nsIAtom* unitAtom = NS_GetStaticAtom(unitStr);
226 :
227 0 : if (unitAtom) {
228 0 : for (uint32_t i = 1 ; i < ArrayLength(unitMap) ; i++) {
229 0 : if (unitMap[i] && *unitMap[i] == unitAtom) {
230 0 : return i;
231 : }
232 : }
233 : }
234 0 : return nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN;
235 : }
236 :
237 : } // namespace mozilla
|