blob: f4ad1987f9a6c96b3a32bbc0840cbd46c41e09ed [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/svg/SkSVGParser.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkSVGParser.h"
19#include "SkSVGCircle.h"
20#include "SkSVGClipPath.h"
21#include "SkSVGDefs.h"
22#include "SkSVGEllipse.h"
23#include "SkSVGFeColorMatrix.h"
24#include "SkSVGFilter.h"
25#include "SkSVGG.h"
26#include "SkSVGImage.h"
27#include "SkSVGLine.h"
28#include "SkSVGLinearGradient.h"
29#include "SkSVGMask.h"
30#include "SkSVGMetadata.h"
31#include "SkSVGPath.h"
32#include "SkSVGPolygon.h"
33#include "SkSVGPolyline.h"
34#include "SkSVGRadialGradient.h"
35#include "SkSVGRect.h"
36#include "SkSVGSVG.h"
37#include "SkSVGStop.h"
38#include "SkSVGSymbol.h"
39#include "SkSVGText.h"
40#include "SkSVGUse.h"
41#include "SkTSearch.h"
42#include <stdio.h>
43
44static int gGeneratedMatrixID = 0;
45
reed@android.com5ee64ad2010-05-17 14:34:13 +000046SkSVGParser::SkSVGParser(SkXMLParserError* errHandler) :
47 SkXMLParser(errHandler),
48 fHead(&fEmptyPaint), fIDs(256),
reed@android.com8a1c16f2008-12-17 15:59:43 +000049 fXMLWriter(&fStream), fCurrElement(NULL), fInSVG(false), fSuppressPaint(false) {
50 fLastTransform.reset();
51 fEmptyPaint.f_fill.set("black");
52 fEmptyPaint.f_stroke.set("none");
53 fEmptyPaint.f_strokeMiterlimit.set("4");
54 fEmptyPaint.f_fillRule.set("winding");
55 fEmptyPaint.f_opacity.set("1");
56 fEmptyPaint.fNext = NULL;
57 for (int index = SkSVGPaint::kInitial + 1; index < SkSVGPaint::kTerminal; index++) {
58 SkString* initial = fEmptyPaint[index];
59 if (initial->size() == 0)
60 continue;
61 fLastFlush[index]->set(*initial);
62 }
63}
64
65SkSVGParser::~SkSVGParser() {
66}
67
68void SkSVGParser::Delete(SkTDArray<SkSVGElement*>& fChildren) {
69 SkSVGElement** ptr;
70 for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
71 Delete((*ptr)->fChildren);
72 delete *ptr;
73 }
74}
75
76int SkSVGParser::findAttribute(SkSVGBase* element, const char* attrValue,
77 size_t len, bool isPaint) {
78 const SkSVGAttribute* attributes;
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +000079 size_t count = element->getAttributes(&attributes);
80 size_t result = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000081 while (result < count) {
82 if (strncmp(attributes->fName, attrValue, len) == 0 && strlen(attributes->fName) == len) {
83 SkASSERT(result == (attributes->fOffset -
84 (isPaint ? sizeof(SkString) : sizeof(SkSVGElement))) / sizeof(SkString));
85 return result;
86 }
87 attributes++;
88 result++;
89 }
90 return -1;
91}
92
93const char* SkSVGParser::getFinal() {
94 _startElement("screenplay");
95 // generate defs
96 SkSVGElement** ptr;
97 for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
98 SkSVGElement* element = *ptr;
99 translate(element, true);
100 }
101 // generate onLoad
102 _startElement("event");
103 _addAttribute("kind", "onLoad");
104 _startElement("paint");
105 _addAttribute("antiAlias", "true");
106 _endElement();
107 for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
108 SkSVGElement* element = *ptr;
109 translate(element, false);
110 }
111 _endElement(); // event
112 _endElement(); // screenplay
113 Delete(fChildren);
114 fStream.write("", 1);
115 return fStream.getStream();
116}
117
118SkString& SkSVGParser::getPaintLast(SkSVGPaint::Field field) {
119 SkSVGPaint* state = fHead;
120 do {
121 SkString* attr = (*state)[field];
122 SkASSERT(attr);
123 if (attr->size() > 0)
124 return *attr;
125 state = state->fNext;
126 } while (state);
127 SkASSERT(0);
128 SkASSERT(fEmptyPaint[field]);
129 return *fEmptyPaint[field];
130}
131
132bool SkSVGParser::isStrokeAndFill( SkSVGPaint** strokeState, SkSVGPaint** fillState) {
133 SkSVGPaint* walking = fHead;
134 bool stroke = false;
135 bool fill = false;
136 bool strokeSet = false;
137 bool fillSet = false;
138 while (walking != NULL) {
139 if (strokeSet == false && walking->f_stroke.size() > 0) {
140 stroke = walking->f_stroke.equals("none") == false;
141 *strokeState = walking;
142 strokeSet = true;
143 }
144 if (fillSet == false && walking->f_fill.size() > 0) {
145 fill = walking->f_fill.equals("none") == false;
146 *fillState = walking;
147 fillSet = true;
148 }
149 walking = walking->fNext;
150 }
151 return stroke && fill;
152}
153
154bool SkSVGParser::onAddAttribute(const char name[], const char value[]) {
155 return onAddAttributeLen(name, value, strlen(value));
156}
157
158bool SkSVGParser::onAddAttributeLen(const char name[], const char value[], size_t len) {
159 if (fCurrElement == NULL) // this signals we should ignore attributes for this element
160 return true;
161 if (fCurrElement->fIsDef == false && fCurrElement->fIsNotDef == false)
reed@android.com5ee64ad2010-05-17 14:34:13 +0000162 return false; // also an ignored element
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 size_t nameLen = strlen(name);
164 int attrIndex = findAttribute(fCurrElement, name, nameLen, false);
165 if (attrIndex == -1) {
166 attrIndex = findAttribute(&fCurrElement->fPaintState, name, nameLen, true);
167 if (attrIndex >= 0) {
168 fCurrElement->fPaintState.addAttribute(*this, attrIndex, value, len);
169 return false;
170 }
171 if (nameLen == 2 && strncmp("id", name, nameLen) == 0) {
172 fCurrElement->f_id.set(value, len);
173 return false;
174 }
175 if (strchr(name, ':') != 0) // part of a different namespace
176 return false;
177 }
178 SkASSERT(attrIndex >= 0);
179 fCurrElement->addAttribute(*this, attrIndex, value, len);
180 return false;
181}
182
183bool SkSVGParser::onEndElement(const char elem[]) {
184 int parentIndex = fParents.count() - 1;
185 if (parentIndex >= 0) {
186 SkSVGElement* element = fParents[parentIndex];
187 element->onEndElement(*this);
188 fParents.remove(parentIndex);
189 }
190 return false;
191}
192
193bool SkSVGParser::onStartElement(const char name[]) {
194 return onStartElementLen(name, strlen(name));
195}
196
197bool SkSVGParser::onStartElementLen(const char name[], size_t len) {
198 if (strncmp(name, "svg", len) == 0) {
199 fInSVG = true;
200 } else if (fInSVG == false)
201 return false;
202 const char* nextColon = strchr(name, ':');
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000203 if (nextColon && (size_t)(nextColon - name) < len)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 return false;
205 SkSVGTypes type = GetType(name, len);
reed@android.com5ee64ad2010-05-17 14:34:13 +0000206// SkASSERT(type >= 0);
207 if (type < 0) {
208 type = SkSVGType_G;
209// return true;
210 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 SkSVGElement* parent = fParents.count() > 0 ? fParents.top() : NULL;
212 SkSVGElement* element = CreateElement(type, parent);
213 bool result = false;
214 if (parent) {
215 element->fParent = parent;
216 result = fParents.top()->onStartElement(element);
217 } else
218 *fChildren.append() = element;
219 if (strncmp(name, "svg", len) != 0)
220 *fParents.append() = element;
221 fCurrElement = element;
222 return result;
223}
224
225bool SkSVGParser::onText(const char text[], int len) {
226 if (fInSVG == false)
227 return false;
228 SkSVGTypes type = fCurrElement->getType();
229 if (type != SkSVGType_Text && type != SkSVGType_Tspan)
230 return false;
231 SkSVGText* textElement = (SkSVGText*) fCurrElement;
232 textElement->f_text.set(text, len);
233 return false;
234}
235
236static int32_t strokeFillID = 0;
237
238void SkSVGParser::translate(SkSVGElement* element, bool isDef) {
239 SkSVGPaint::Push(&fHead, &element->fPaintState);
240 bool isFlushable = element->isFlushable();
241 if ((element->fIsDef == false && element->fIsNotDef == false) ||
242 (element->fIsDef && isDef == false && element->fIsNotDef == false) ||
243 (element->fIsDef == false && isDef && element->fIsNotDef)) {
244 isFlushable = false;
245 }
246 SkSVGPaint* strokeState = NULL, * fillState = NULL;
247 if (isFlushable)
248 element->fPaintState.setSave(*this);
249 if (isFlushable && isStrokeAndFill(&strokeState, &fillState)) {
250 SkString& elementID = element->f_id;
251 if (elementID.size() == 0) {
252 elementID.set("sf");
253 elementID.appendS32(++strokeFillID);
254 }
255 SkString saveStroke(strokeState->f_stroke);
256 SkString saveFill(fillState->f_fill);
257 strokeState->f_stroke.set("none");
258 element->fPaintState.flush(*this, isFlushable, isDef);
259 element->translate(*this, isDef);
260 strokeState->f_stroke.set(saveStroke);
261 fillState->f_fill.set("none");
262 if (element->fPaintState.flush(*this, isFlushable, isDef)) {
263 _startElement("add");
264 _addAttributeLen("use", elementID.c_str(), elementID.size());
265 _endElement(); // add
266 }
267 fillState->f_fill.set(saveFill);
268 } else {
269 element->fPaintState.flush(*this, isFlushable, isDef);
270 if (isFlushable || element->isGroup())
271 element->translate(*this, isDef);
272 }
273 SkSVGPaint::Pop(&fHead);
274}
275
276void SkSVGParser::translateMatrix(SkString& string, SkString* stringID) {
277 if (string.size() == 0)
278 return;
279 if (stringID->size() > 0) {
280 _startElement("add");
281 _addAttribute("use", stringID->c_str());
282 _endElement(); // add
283 return;
284 }
285 SkASSERT(strncmp(string.c_str(), "matrix", 6) == 0);
286 ++gGeneratedMatrixID;
287 _startElement("matrix");
288 char idStr[24];
289 strcpy(idStr, "sk_matrix");
290 sprintf(idStr + strlen(idStr), "%d", gGeneratedMatrixID);
291 _addAttribute("id", idStr);
292 stringID->set(idStr);
293 const char* str = string.c_str();
294 SkASSERT(strncmp(str, "matrix(", 7) == 0);
295 str += 6;
296 const char* strEnd = strrchr(str, ')');
297 SkASSERT(strEnd != NULL);
298 SkString mat(str, strEnd - str);
299 ConvertToArray(mat);
300 const char* elems[6];
301 static const int order[] = {0, 3, 1, 4, 2, 5};
302 const int* orderPtr = order;
303 str = mat.c_str();
304 strEnd = str + mat.size();
305 while (str < strEnd) {
306 elems[*orderPtr++] = str;
307 while (str < strEnd && *str != ',' )
308 str++;
309 str++;
310 }
311 string.reset();
312 for (int index = 0; index < 6; index++) {
313 const char* end = strchr(elems[index], ',');
314 if (end == NULL)
315 end= strchr(elems[index], ']');
316 string.append(elems[index], end - elems[index] + 1);
317 }
318 string.remove(string.size() - 1, 1);
319 string.append(",0,0,1]");
320 _addAttribute("matrix", string);
321 _endElement(); // matrix
322}
323
324static bool is_whitespace(char ch) {
325 return ch > 0 && ch <= ' ';
326}
327
328void SkSVGParser::ConvertToArray(SkString& vals) {
329 vals.appendUnichar(']');
330 char* valCh = (char*) vals.c_str();
331 valCh[0] = '[';
332 int index = 1;
333 while (valCh[index] != ']') {
334 while (is_whitespace(valCh[index]))
335 index++;
336 bool foundComma = false;
337 char next;
338 do {
339 next = valCh[index++];
340 if (next == ',') {
341 foundComma = true;
342 continue;
343 }
344 if (next == ']') {
345 index--;
346 goto undoLastComma;
347 }
348 if (next == ' ')
349 break;
350 foundComma = false;
351 } while (is_whitespace(next) == false);
352 if (foundComma == false)
353 valCh[index - 1] = ',';
354 }
355undoLastComma:
356 while (is_whitespace(valCh[--index]))
357 ;
358 if (valCh[index] == ',')
359 valCh[index] = ' ';
360}
361
362#define CASE_NEW(type) case SkSVGType_##type : created = new SkSVG##type(); break
363
364SkSVGElement* SkSVGParser::CreateElement(SkSVGTypes type, SkSVGElement* parent) {
365 SkSVGElement* created = NULL;
366 switch (type) {
367 CASE_NEW(Circle);
368 CASE_NEW(ClipPath);
369 CASE_NEW(Defs);
370 CASE_NEW(Ellipse);
371 CASE_NEW(FeColorMatrix);
372 CASE_NEW(Filter);
373 CASE_NEW(G);
374 CASE_NEW(Image);
375 CASE_NEW(Line);
376 CASE_NEW(LinearGradient);
377 CASE_NEW(Mask);
378 CASE_NEW(Metadata);
379 CASE_NEW(Path);
380 CASE_NEW(Polygon);
381 CASE_NEW(Polyline);
382 CASE_NEW(RadialGradient);
383 CASE_NEW(Rect);
384 CASE_NEW(Stop);
385 CASE_NEW(SVG);
386 CASE_NEW(Symbol);
387 CASE_NEW(Text);
388 CASE_NEW(Tspan);
389 CASE_NEW(Use);
390 default:
391 SkASSERT(0);
392 return NULL;
393 }
394 created->fParent = parent;
395 bool isDef = created->fIsDef = created->isDef();
396 bool isNotDef = created->fIsNotDef = created->isNotDef();
397 if (isDef) {
398 SkSVGElement* up = parent;
399 while (up && up->fIsDef == false) {
400 up->fIsDef = true;
401 up = up->fParent;
402 }
403 }
404 if (isNotDef) {
405 SkSVGElement* up = parent;
406 while (up && up->fIsNotDef == false) {
407 up->fIsNotDef = true;
408 up = up->fParent;
409 }
410 }
411 return created;
412}
413
414const SkSVGTypeName gSVGTypeNames[] = {
415 {"circle", SkSVGType_Circle},
416 {"clipPath", SkSVGType_ClipPath},
417 {"defs", SkSVGType_Defs},
418 {"ellipse", SkSVGType_Ellipse},
419 {"feColorMatrix", SkSVGType_FeColorMatrix},
420 {"filter", SkSVGType_Filter},
421 {"g", SkSVGType_G},
422 {"image", SkSVGType_Image},
423 {"line", SkSVGType_Line},
424 {"linearGradient", SkSVGType_LinearGradient},
425 {"mask", SkSVGType_Mask},
426 {"metadata", SkSVGType_Metadata},
427 {"path", SkSVGType_Path},
428 {"polygon", SkSVGType_Polygon},
429 {"polyline", SkSVGType_Polyline},
430 {"radialGradient", SkSVGType_RadialGradient},
431 {"rect", SkSVGType_Rect},
432 {"stop", SkSVGType_Stop},
433 {"svg", SkSVGType_SVG},
434 {"symbol", SkSVGType_Symbol},
435 {"text", SkSVGType_Text},
436 {"tspan", SkSVGType_Tspan},
437 {"use", SkSVGType_Use}
438};
439
440const int kSVGTypeNamesSize = SK_ARRAY_COUNT(gSVGTypeNames);
441
442SkSVGTypes SkSVGParser::GetType(const char match[], size_t len ) {
443 int index = SkStrSearch(&gSVGTypeNames[0].fName, kSVGTypeNamesSize, match,
444 len, sizeof(gSVGTypeNames[0]));
445 return index >= 0 && index < kSVGTypeNamesSize ? gSVGTypeNames[index].fType :
446 (SkSVGTypes) -1;
447}