1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is the Mozilla SVG project.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * IBM Corporation
20 : * Portions created by the Initial Developer are Copyright (C) 2006
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "mozilla/Util.h"
40 :
41 : #include "SVGTransformListParser.h"
42 : #include "SVGTransform.h"
43 : #include "prdtoa.h"
44 : #include "nsDOMError.h"
45 : #include "nsGkAtoms.h"
46 : #include "nsReadableUtils.h"
47 : #include "nsCRT.h"
48 : #include "nsContentUtils.h"
49 : #include "nsDOMClassInfoID.h"
50 : #include "nsIAtom.h"
51 :
52 : using namespace mozilla;
53 :
54 : //----------------------------------------------------------------------
55 : // private methods
56 :
57 : nsresult
58 0 : SVGTransformListParser::Match()
59 : {
60 0 : mTransforms.Clear();
61 0 : return MatchTransformList();
62 : }
63 :
64 :
65 : nsresult
66 0 : SVGTransformListParser::MatchTransformList()
67 : {
68 0 : MatchWsp();
69 :
70 0 : if (IsTokenTransformStarter()) {
71 0 : ENSURE_MATCHED(MatchTransforms());
72 : }
73 :
74 0 : MatchWsp();
75 :
76 0 : return NS_OK;
77 : }
78 :
79 :
80 : nsresult
81 0 : SVGTransformListParser::MatchTransforms()
82 : {
83 0 : ENSURE_MATCHED(MatchTransform());
84 :
85 0 : while (mTokenType != END) {
86 0 : const char* pos = mTokenPos;
87 :
88 : /* Curiously the SVG BNF allows multiple comma-wsp between transforms */
89 0 : while (IsTokenCommaWspStarter()) {
90 0 : ENSURE_MATCHED(MatchCommaWsp());
91 : }
92 :
93 0 : if (IsTokenTransformStarter()) {
94 0 : ENSURE_MATCHED(MatchTransform());
95 : } else {
96 0 : if (pos != mTokenPos) RewindTo(pos);
97 0 : break;
98 : }
99 : }
100 :
101 0 : return NS_OK;
102 : }
103 :
104 :
105 : nsresult
106 0 : SVGTransformListParser::GetTransformToken(nsIAtom** aKeyAtom,
107 : bool aAdvancePos)
108 : {
109 0 : if (mTokenType != OTHER || *mTokenPos == '\0') {
110 0 : return NS_ERROR_FAILURE;
111 : }
112 :
113 0 : nsresult rv = NS_OK;
114 :
115 0 : const char* delimiters = "\x20\x9\xD\xA,(";
116 0 : char* delimiterStart = PL_strnpbrk(mTokenPos, delimiters, 11);
117 0 : if (delimiterStart != 0) {
118 : /* save this character and null it out */
119 0 : char holdingChar = *delimiterStart;
120 0 : *delimiterStart = '\0';
121 :
122 : PRUint32 len;
123 0 : if ((len = nsCRT::strlen(mTokenPos)) > 0) {
124 0 : *aKeyAtom = NS_NewAtom(Substring(mTokenPos, mTokenPos + len));
125 :
126 0 : if (aAdvancePos) {
127 0 : mInputPos = mTokenPos + len;
128 0 : mTokenPos = mInputPos;
129 : }
130 : } else {
131 0 : rv = NS_ERROR_FAILURE;
132 : }
133 : /* reset character back to original */
134 0 : *delimiterStart = holdingChar;
135 : } else {
136 0 : rv = NS_ERROR_FAILURE;
137 : }
138 :
139 0 : return rv;
140 : }
141 :
142 :
143 : nsresult
144 0 : SVGTransformListParser::MatchTransform()
145 : {
146 0 : nsCOMPtr<nsIAtom> keyatom;
147 :
148 0 : nsresult rv = GetTransformToken(getter_AddRefs(keyatom), true);
149 0 : if (NS_FAILED(rv)) {
150 0 : return rv;
151 : }
152 :
153 0 : if (keyatom == nsGkAtoms::translate) {
154 0 : ENSURE_MATCHED(MatchTranslate());
155 0 : } else if (keyatom == nsGkAtoms::scale) {
156 0 : ENSURE_MATCHED(MatchScale());
157 0 : } else if (keyatom == nsGkAtoms::rotate) {
158 0 : ENSURE_MATCHED(MatchRotate());
159 0 : } else if (keyatom == nsGkAtoms::skewX) {
160 0 : ENSURE_MATCHED(MatchSkewX());
161 0 : } else if (keyatom == nsGkAtoms::skewY) {
162 0 : ENSURE_MATCHED(MatchSkewY());
163 0 : } else if (keyatom == nsGkAtoms::matrix) {
164 0 : ENSURE_MATCHED(MatchMatrix());
165 : } else {
166 0 : return NS_ERROR_FAILURE;
167 : }
168 :
169 0 : return NS_OK;
170 : }
171 :
172 :
173 : bool
174 0 : SVGTransformListParser::IsTokenTransformStarter()
175 : {
176 0 : nsCOMPtr<nsIAtom> keyatom;
177 :
178 0 : nsresult rv = GetTransformToken(getter_AddRefs(keyatom), false);
179 0 : if (NS_FAILED(rv)) {
180 0 : return false;
181 : }
182 :
183 0 : if (keyatom == nsGkAtoms::translate ||
184 0 : keyatom == nsGkAtoms::scale ||
185 0 : keyatom == nsGkAtoms::rotate ||
186 0 : keyatom == nsGkAtoms::skewX ||
187 0 : keyatom == nsGkAtoms::skewY ||
188 0 : keyatom == nsGkAtoms::matrix) {
189 0 : return true;
190 : }
191 :
192 0 : return false;
193 : }
194 :
195 : nsresult
196 0 : SVGTransformListParser::MatchNumberArguments(float *aResult,
197 : PRUint32 aMaxNum,
198 : PRUint32 *aParsedNum)
199 : {
200 0 : *aParsedNum = 0;
201 :
202 0 : MatchWsp();
203 :
204 0 : ENSURE_MATCHED(MatchLeftParen());
205 :
206 0 : MatchWsp();
207 :
208 0 : ENSURE_MATCHED(MatchNumber(&aResult[0]));
209 0 : *aParsedNum = 1;
210 :
211 0 : while (IsTokenCommaWspStarter()) {
212 0 : MatchWsp();
213 0 : if (mTokenType == RIGHT_PAREN) {
214 0 : break;
215 : }
216 0 : if (*aParsedNum == aMaxNum) {
217 0 : return NS_ERROR_FAILURE;
218 : }
219 0 : if (IsTokenCommaWspStarter()) {
220 0 : MatchCommaWsp();
221 : }
222 0 : ENSURE_MATCHED(MatchNumber(&aResult[(*aParsedNum)++]));
223 : }
224 :
225 0 : MatchWsp();
226 :
227 0 : ENSURE_MATCHED(MatchRightParen());
228 :
229 0 : return NS_OK;
230 : }
231 :
232 : nsresult
233 0 : SVGTransformListParser::MatchTranslate()
234 : {
235 0 : GetNextToken();
236 :
237 : float t[2];
238 : PRUint32 count;
239 :
240 0 : ENSURE_MATCHED(MatchNumberArguments(t, ArrayLength(t), &count));
241 :
242 0 : switch (count) {
243 : case 1:
244 0 : t[1] = 0.f;
245 : // fall-through
246 : case 2:
247 : {
248 0 : SVGTransform* transform = mTransforms.AppendElement();
249 0 : NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY);
250 0 : transform->SetTranslate(t[0], t[1]);
251 : break;
252 : }
253 : default:
254 0 : return NS_ERROR_FAILURE;
255 : }
256 :
257 0 : return NS_OK;
258 : }
259 :
260 :
261 : nsresult
262 0 : SVGTransformListParser::MatchScale()
263 : {
264 0 : GetNextToken();
265 :
266 : float s[2];
267 : PRUint32 count;
268 :
269 0 : ENSURE_MATCHED(MatchNumberArguments(s, ArrayLength(s), &count));
270 :
271 0 : switch (count) {
272 : case 1:
273 0 : s[1] = s[0];
274 : // fall-through
275 : case 2:
276 : {
277 0 : SVGTransform* transform = mTransforms.AppendElement();
278 0 : NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY);
279 0 : transform->SetScale(s[0], s[1]);
280 : break;
281 : }
282 : default:
283 0 : return NS_ERROR_FAILURE;
284 : }
285 :
286 0 : return NS_OK;
287 : }
288 :
289 :
290 : nsresult
291 0 : SVGTransformListParser::MatchRotate()
292 : {
293 0 : GetNextToken();
294 :
295 : float r[3];
296 : PRUint32 count;
297 :
298 0 : ENSURE_MATCHED(MatchNumberArguments(r, ArrayLength(r), &count));
299 :
300 0 : switch (count) {
301 : case 1:
302 0 : r[1] = r[2] = 0.f;
303 : // fall-through
304 : case 3:
305 : {
306 0 : SVGTransform* transform = mTransforms.AppendElement();
307 0 : NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY);
308 0 : transform->SetRotate(r[0], r[1], r[2]);
309 : break;
310 : }
311 : default:
312 0 : return NS_ERROR_FAILURE;
313 : }
314 :
315 0 : return NS_OK;
316 : }
317 :
318 :
319 : nsresult
320 0 : SVGTransformListParser::MatchSkewX()
321 : {
322 0 : GetNextToken();
323 :
324 : float skew;
325 : PRUint32 count;
326 :
327 0 : ENSURE_MATCHED(MatchNumberArguments(&skew, 1, &count));
328 :
329 0 : if (count != 1) {
330 0 : return NS_ERROR_FAILURE;
331 : }
332 :
333 0 : SVGTransform* transform = mTransforms.AppendElement();
334 0 : NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY);
335 0 : transform->SetSkewX(skew);
336 :
337 0 : return NS_OK;
338 : }
339 :
340 :
341 : nsresult
342 0 : SVGTransformListParser::MatchSkewY()
343 : {
344 0 : GetNextToken();
345 :
346 : float skew;
347 : PRUint32 count;
348 :
349 0 : ENSURE_MATCHED(MatchNumberArguments(&skew, 1, &count));
350 :
351 0 : if (count != 1) {
352 0 : return NS_ERROR_FAILURE;
353 : }
354 :
355 0 : SVGTransform* transform = mTransforms.AppendElement();
356 0 : NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY);
357 0 : transform->SetSkewY(skew);
358 :
359 0 : return NS_OK;
360 : }
361 :
362 :
363 : nsresult
364 0 : SVGTransformListParser::MatchMatrix()
365 : {
366 0 : GetNextToken();
367 :
368 : float m[6];
369 : PRUint32 count;
370 :
371 0 : ENSURE_MATCHED(MatchNumberArguments(m, ArrayLength(m), &count));
372 :
373 0 : if (count != 6) {
374 0 : return NS_ERROR_FAILURE;
375 : }
376 :
377 0 : SVGTransform* transform = mTransforms.AppendElement();
378 0 : NS_ENSURE_TRUE(transform, NS_ERROR_OUT_OF_MEMORY);
379 0 : transform->SetMatrix(gfxMatrix(m[0], m[1], m[2], m[3], m[4], m[5]));
380 :
381 0 : return NS_OK;
382 : }
|