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