1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla SVG Project code.
16 : *
17 : * The Initial Developer of the Original Code is the Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "mozilla/Util.h"
38 :
39 : #include "SVGLength.h"
40 : #include "nsSVGElement.h"
41 : #include "nsSVGSVGElement.h"
42 : #include "nsString.h"
43 : #include "nsSVGUtils.h"
44 : #include "nsTextFormatter.h"
45 : #include "prdtoa.h"
46 : #include "nsMathUtils.h"
47 : #include <limits>
48 :
49 : namespace mozilla {
50 :
51 : // Declare some helpers defined below:
52 : static void GetUnitString(nsAString& unit, PRUint16 unitType);
53 : static PRUint16 GetUnitTypeForString(const char* unitStr);
54 :
55 : void
56 0 : SVGLength::GetValueAsString(nsAString &aValue) const
57 : {
58 : PRUnichar buf[24];
59 : nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
60 0 : NS_LITERAL_STRING("%g").get(),
61 0 : (double)mValue);
62 0 : aValue.Assign(buf);
63 :
64 0 : nsAutoString unitString;
65 0 : GetUnitString(unitString, mUnit);
66 0 : aValue.Append(unitString);
67 0 : }
68 :
69 : bool
70 0 : SVGLength::SetValueFromString(const nsAString &aValue)
71 : {
72 : float tmpValue;
73 : PRUint16 tmpUnit;
74 :
75 0 : NS_ConvertUTF16toUTF8 value(aValue);
76 0 : const char *str = value.get();
77 :
78 0 : while (*str != '\0' && IsSVGWhitespace(*str)) {
79 0 : ++str;
80 : }
81 : char *unit;
82 0 : tmpValue = float(PR_strtod(str, &unit));
83 0 : if (unit != str && NS_finite(tmpValue)) {
84 0 : char *theRest = unit;
85 0 : if (*unit != '\0' && !IsSVGWhitespace(*unit)) {
86 0 : while (*theRest != '\0' && !IsSVGWhitespace(*theRest)) {
87 0 : ++theRest;
88 : }
89 0 : nsCAutoString unitStr(unit, theRest - unit);
90 0 : tmpUnit = GetUnitTypeForString(unitStr.get());
91 0 : if (tmpUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN) {
92 : // nsSVGUtils::ReportToConsole
93 0 : return false;
94 : }
95 : } else {
96 0 : tmpUnit = nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER;
97 : }
98 0 : while (*theRest && IsSVGWhitespace(*theRest)) {
99 0 : ++theRest;
100 : }
101 0 : if (!*theRest) {
102 0 : mValue = tmpValue;
103 0 : mUnit = tmpUnit;
104 0 : return true;
105 : }
106 : }
107 0 : return false;
108 : }
109 :
110 : inline static bool
111 0 : IsAbsoluteUnit(PRUint8 aUnit)
112 : {
113 : return aUnit >= nsIDOMSVGLength::SVG_LENGTHTYPE_CM &&
114 0 : aUnit <= nsIDOMSVGLength::SVG_LENGTHTYPE_PC;
115 : }
116 :
117 : /**
118 : * Helper to convert between different CSS absolute units without the need for
119 : * an element, which provides more flexibility at the DOM level (and without
120 : * the need for an intermediary conversion to user units, which avoids
121 : * unnecessary overhead and rounding error).
122 : *
123 : * Example usage: to find out how many centimeters there are per inch:
124 : *
125 : * GetAbsUnitsPerAbsUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_CM,
126 : * nsIDOMSVGLength::SVG_LENGTHTYPE_IN)
127 : */
128 0 : inline static float GetAbsUnitsPerAbsUnit(PRUint8 aUnits, PRUint8 aPerUnit)
129 : {
130 0 : NS_ABORT_IF_FALSE(IsAbsoluteUnit(aUnits), "Not a CSS absolute unit");
131 0 : NS_ABORT_IF_FALSE(IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit");
132 :
133 : float CSSAbsoluteUnitConversionFactors[5][5] = { // columns: cm, mm, in, pt, pc
134 : // cm per...:
135 : { 1.0, 0.1, 2.54, 0.035277777777777778, 0.42333333333333333 },
136 : // mm per...:
137 : { 10.0, 1.0, 25.4, 0.35277777777777778, 4.2333333333333333 },
138 : // in per...:
139 : { 0.39370078740157481, 0.039370078740157481, 1.0, 0.013888888888888889, 0.16666666666666667 },
140 : // pt per...:
141 : { 28.346456692913386, 2.8346456692913386, 72.0, 1.0, 12.0 },
142 : // pc per...:
143 : { 2.3622047244094489, 0.23622047244094489, 6.0, 0.083333333333333333, 1.0 }
144 0 : };
145 :
146 : // First absolute unit is SVG_LENGTHTYPE_CM = 6
147 0 : return CSSAbsoluteUnitConversionFactors[aUnits - 6][aPerUnit - 6];
148 : }
149 :
150 : float
151 0 : SVGLength::GetValueInSpecifiedUnit(PRUint8 aUnit,
152 : const nsSVGElement *aElement,
153 : PRUint8 aAxis) const
154 : {
155 0 : if (aUnit == mUnit) {
156 0 : return mValue;
157 : }
158 0 : if ((aUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER &&
159 : mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) ||
160 : (aUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX &&
161 : mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER)) {
162 0 : return mValue;
163 : }
164 0 : if (IsAbsoluteUnit(aUnit) && IsAbsoluteUnit(mUnit)) {
165 0 : return mValue * GetAbsUnitsPerAbsUnit(aUnit, mUnit);
166 : }
167 :
168 : // Otherwise we do a two step convertion via user units. This can only
169 : // succeed if aElement is non-null (although that's not sufficent to
170 : // guarantee success).
171 :
172 0 : float userUnitsPerCurrentUnit = GetUserUnitsPerUnit(aElement, aAxis);
173 : float userUnitsPerNewUnit =
174 0 : SVGLength(0.0f, aUnit).GetUserUnitsPerUnit(aElement, aAxis);
175 :
176 0 : NS_ASSERTION(userUnitsPerCurrentUnit >= 0 ||
177 : !NS_finite(userUnitsPerCurrentUnit),
178 : "bad userUnitsPerCurrentUnit");
179 0 : NS_ASSERTION(userUnitsPerNewUnit >= 0 ||
180 : !NS_finite(userUnitsPerNewUnit),
181 : "bad userUnitsPerNewUnit");
182 :
183 0 : float value = mValue * userUnitsPerCurrentUnit / userUnitsPerNewUnit;
184 :
185 : // userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could
186 : // be zero.
187 0 : if (NS_finite(value)) {
188 0 : return value;
189 : }
190 0 : return std::numeric_limits<float>::quiet_NaN();
191 : }
192 :
193 : #define INCHES_PER_MM_FLOAT float(0.0393700787)
194 : #define INCHES_PER_CM_FLOAT float(0.393700787)
195 :
196 : float
197 0 : SVGLength::GetUserUnitsPerUnit(const nsSVGElement *aElement, PRUint8 aAxis) const
198 : {
199 0 : switch (mUnit) {
200 : case nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER:
201 : case nsIDOMSVGLength::SVG_LENGTHTYPE_PX:
202 0 : return 1.0f;
203 : case nsIDOMSVGLength::SVG_LENGTHTYPE_MM:
204 0 : return INCHES_PER_MM_FLOAT * GetUserUnitsPerInch();
205 : case nsIDOMSVGLength::SVG_LENGTHTYPE_CM:
206 0 : return INCHES_PER_CM_FLOAT * GetUserUnitsPerInch();
207 : case nsIDOMSVGLength::SVG_LENGTHTYPE_IN:
208 0 : return GetUserUnitsPerInch();
209 : case nsIDOMSVGLength::SVG_LENGTHTYPE_PT:
210 0 : return (1.0f/POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch();
211 : case nsIDOMSVGLength::SVG_LENGTHTYPE_PC:
212 0 : return (12.0f/POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch();
213 : case nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE:
214 0 : return GetUserUnitsPerPercent(aElement, aAxis);
215 : case nsIDOMSVGLength::SVG_LENGTHTYPE_EMS:
216 0 : return nsSVGUtils::GetFontSize(const_cast<nsSVGElement*>(aElement));
217 : case nsIDOMSVGLength::SVG_LENGTHTYPE_EXS:
218 0 : return nsSVGUtils::GetFontXHeight(const_cast<nsSVGElement*>(aElement));
219 : default:
220 0 : NS_NOTREACHED("Unknown unit type");
221 0 : return std::numeric_limits<float>::quiet_NaN();
222 : }
223 : }
224 :
225 : /* static */ float
226 0 : SVGLength::GetUserUnitsPerPercent(const nsSVGElement *aElement, PRUint8 aAxis)
227 : {
228 0 : if (aElement) {
229 0 : nsSVGSVGElement *viewportElement = const_cast<nsSVGElement*>(aElement)->GetCtx();
230 0 : if (viewportElement) {
231 0 : return NS_MAX(viewportElement->GetLength(aAxis) / 100.0f, 0.0f);
232 : }
233 : }
234 0 : return std::numeric_limits<float>::quiet_NaN();
235 : }
236 :
237 : // Helpers:
238 :
239 : // These items must be at the same index as the nsIDOMSVGLength constants!
240 : static nsIAtom** const unitMap[] =
241 : {
242 : nsnull, /* SVG_LENGTHTYPE_UNKNOWN */
243 : nsnull, /* SVG_LENGTHTYPE_NUMBER */
244 : &nsGkAtoms::percentage,
245 : &nsGkAtoms::em,
246 : &nsGkAtoms::ex,
247 : &nsGkAtoms::px,
248 : &nsGkAtoms::cm,
249 : &nsGkAtoms::mm,
250 : &nsGkAtoms::in,
251 : &nsGkAtoms::pt,
252 : &nsGkAtoms::pc
253 : };
254 :
255 : static void
256 0 : GetUnitString(nsAString& unit, PRUint16 unitType)
257 : {
258 0 : if (SVGLength::IsValidUnitType(unitType)) {
259 0 : if (unitMap[unitType]) {
260 0 : (*unitMap[unitType])->ToString(unit);
261 : }
262 0 : return;
263 : }
264 0 : NS_NOTREACHED("Unknown unit type"); // Someone's using an SVGLength with an invalid unit?
265 0 : return;
266 : }
267 :
268 : static PRUint16
269 0 : GetUnitTypeForString(const char* unitStr)
270 : {
271 0 : if (!unitStr || *unitStr == '\0')
272 0 : return nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER;
273 :
274 0 : nsCOMPtr<nsIAtom> unitAtom = do_GetAtom(unitStr);
275 :
276 0 : for (PRUint32 i = 1 ; i < ArrayLength(unitMap) ; i++) {
277 0 : if (unitMap[i] && *unitMap[i] == unitAtom) {
278 0 : return i;
279 : }
280 : }
281 0 : return nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN;
282 : }
283 :
284 : } // namespace mozilla
|