/* libs/graphics/svg/SkSVGParser.cpp ** ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #include "SkSVGParser.h" #include "SkSVGCircle.h" #include "SkSVGClipPath.h" #include "SkSVGDefs.h" #include "SkSVGEllipse.h" #include "SkSVGFeColorMatrix.h" #include "SkSVGFilter.h" #include "SkSVGG.h" #include "SkSVGImage.h" #include "SkSVGLine.h" #include "SkSVGLinearGradient.h" #include "SkSVGMask.h" #include "SkSVGMetadata.h" #include "SkSVGPath.h" #include "SkSVGPolygon.h" #include "SkSVGPolyline.h" #include "SkSVGRadialGradient.h" #include "SkSVGRect.h" #include "SkSVGSVG.h" #include "SkSVGStop.h" #include "SkSVGSymbol.h" #include "SkSVGText.h" #include "SkSVGUse.h" #include "SkTSearch.h" #include <stdio.h> static int gGeneratedMatrixID = 0; SkSVGParser::SkSVGParser() : fHead(&fEmptyPaint), fIDs(256), fXMLWriter(&fStream), fCurrElement(NULL), fInSVG(false), fSuppressPaint(false) { fLastTransform.reset(); fEmptyPaint.f_fill.set("black"); fEmptyPaint.f_stroke.set("none"); fEmptyPaint.f_strokeMiterlimit.set("4"); fEmptyPaint.f_fillRule.set("winding"); fEmptyPaint.f_opacity.set("1"); fEmptyPaint.fNext = NULL; for (int index = SkSVGPaint::kInitial + 1; index < SkSVGPaint::kTerminal; index++) { SkString* initial = fEmptyPaint[index]; if (initial->size() == 0) continue; fLastFlush[index]->set(*initial); } } SkSVGParser::~SkSVGParser() { } void SkSVGParser::Delete(SkTDArray<SkSVGElement*>& fChildren) { SkSVGElement** ptr; for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { Delete((*ptr)->fChildren); delete *ptr; } } int SkSVGParser::findAttribute(SkSVGBase* element, const char* attrValue, size_t len, bool isPaint) { const SkSVGAttribute* attributes; int count = element->getAttributes(&attributes); int result = 0; while (result < count) { if (strncmp(attributes->fName, attrValue, len) == 0 && strlen(attributes->fName) == len) { SkASSERT(result == (attributes->fOffset - (isPaint ? sizeof(SkString) : sizeof(SkSVGElement))) / sizeof(SkString)); return result; } attributes++; result++; } return -1; } const char* SkSVGParser::getFinal() { _startElement("screenplay"); // generate defs SkSVGElement** ptr; for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { SkSVGElement* element = *ptr; translate(element, true); } // generate onLoad _startElement("event"); _addAttribute("kind", "onLoad"); _startElement("paint"); _addAttribute("antiAlias", "true"); _endElement(); for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) { SkSVGElement* element = *ptr; translate(element, false); } _endElement(); // event _endElement(); // screenplay Delete(fChildren); fStream.write("", 1); return fStream.getStream(); } SkString& SkSVGParser::getPaintLast(SkSVGPaint::Field field) { SkSVGPaint* state = fHead; do { SkString* attr = (*state)[field]; SkASSERT(attr); if (attr->size() > 0) return *attr; state = state->fNext; } while (state); SkASSERT(0); SkASSERT(fEmptyPaint[field]); return *fEmptyPaint[field]; } bool SkSVGParser::isStrokeAndFill( SkSVGPaint** strokeState, SkSVGPaint** fillState) { SkSVGPaint* walking = fHead; bool stroke = false; bool fill = false; bool strokeSet = false; bool fillSet = false; while (walking != NULL) { if (strokeSet == false && walking->f_stroke.size() > 0) { stroke = walking->f_stroke.equals("none") == false; *strokeState = walking; strokeSet = true; } if (fillSet == false && walking->f_fill.size() > 0) { fill = walking->f_fill.equals("none") == false; *fillState = walking; fillSet = true; } walking = walking->fNext; } return stroke && fill; } bool SkSVGParser::onAddAttribute(const char name[], const char value[]) { return onAddAttributeLen(name, value, strlen(value)); } bool SkSVGParser::onAddAttributeLen(const char name[], const char value[], size_t len) { if (fCurrElement == NULL) // this signals we should ignore attributes for this element return true; if (fCurrElement->fIsDef == false && fCurrElement->fIsNotDef == false) return true; // also an ignored element size_t nameLen = strlen(name); int attrIndex = findAttribute(fCurrElement, name, nameLen, false); if (attrIndex == -1) { attrIndex = findAttribute(&fCurrElement->fPaintState, name, nameLen, true); if (attrIndex >= 0) { fCurrElement->fPaintState.addAttribute(*this, attrIndex, value, len); return false; } if (nameLen == 2 && strncmp("id", name, nameLen) == 0) { fCurrElement->f_id.set(value, len); return false; } if (strchr(name, ':') != 0) // part of a different namespace return false; } SkASSERT(attrIndex >= 0); fCurrElement->addAttribute(*this, attrIndex, value, len); return false; } bool SkSVGParser::onEndElement(const char elem[]) { int parentIndex = fParents.count() - 1; if (parentIndex >= 0) { SkSVGElement* element = fParents[parentIndex]; element->onEndElement(*this); fParents.remove(parentIndex); } return false; } bool SkSVGParser::onStartElement(const char name[]) { return onStartElementLen(name, strlen(name)); } bool SkSVGParser::onStartElementLen(const char name[], size_t len) { if (strncmp(name, "svg", len) == 0) { fInSVG = true; } else if (fInSVG == false) return false; const char* nextColon = strchr(name, ':'); if (nextColon && nextColon - name < len) return false; SkSVGTypes type = GetType(name, len); SkASSERT(type >= 0); if (type < 0) return true; SkSVGElement* parent = fParents.count() > 0 ? fParents.top() : NULL; SkSVGElement* element = CreateElement(type, parent); bool result = false; if (parent) { element->fParent = parent; result = fParents.top()->onStartElement(element); } else *fChildren.append() = element; if (strncmp(name, "svg", len) != 0) *fParents.append() = element; fCurrElement = element; return result; } bool SkSVGParser::onText(const char text[], int len) { if (fInSVG == false) return false; SkSVGTypes type = fCurrElement->getType(); if (type != SkSVGType_Text && type != SkSVGType_Tspan) return false; SkSVGText* textElement = (SkSVGText*) fCurrElement; textElement->f_text.set(text, len); return false; } static int32_t strokeFillID = 0; void SkSVGParser::translate(SkSVGElement* element, bool isDef) { SkSVGPaint::Push(&fHead, &element->fPaintState); bool isFlushable = element->isFlushable(); if ((element->fIsDef == false && element->fIsNotDef == false) || (element->fIsDef && isDef == false && element->fIsNotDef == false) || (element->fIsDef == false && isDef && element->fIsNotDef)) { isFlushable = false; } SkSVGPaint* strokeState = NULL, * fillState = NULL; if (isFlushable) element->fPaintState.setSave(*this); if (isFlushable && isStrokeAndFill(&strokeState, &fillState)) { SkString& elementID = element->f_id; if (elementID.size() == 0) { elementID.set("sf"); elementID.appendS32(++strokeFillID); } SkString saveStroke(strokeState->f_stroke); SkString saveFill(fillState->f_fill); strokeState->f_stroke.set("none"); element->fPaintState.flush(*this, isFlushable, isDef); element->translate(*this, isDef); strokeState->f_stroke.set(saveStroke); fillState->f_fill.set("none"); if (element->fPaintState.flush(*this, isFlushable, isDef)) { _startElement("add"); _addAttributeLen("use", elementID.c_str(), elementID.size()); _endElement(); // add } fillState->f_fill.set(saveFill); } else { element->fPaintState.flush(*this, isFlushable, isDef); if (isFlushable || element->isGroup()) element->translate(*this, isDef); } SkSVGPaint::Pop(&fHead); } void SkSVGParser::translateMatrix(SkString& string, SkString* stringID) { if (string.size() == 0) return; if (stringID->size() > 0) { _startElement("add"); _addAttribute("use", stringID->c_str()); _endElement(); // add return; } SkASSERT(strncmp(string.c_str(), "matrix", 6) == 0); ++gGeneratedMatrixID; _startElement("matrix"); char idStr[24]; strcpy(idStr, "sk_matrix"); sprintf(idStr + strlen(idStr), "%d", gGeneratedMatrixID); _addAttribute("id", idStr); stringID->set(idStr); const char* str = string.c_str(); SkASSERT(strncmp(str, "matrix(", 7) == 0); str += 6; const char* strEnd = strrchr(str, ')'); SkASSERT(strEnd != NULL); SkString mat(str, strEnd - str); ConvertToArray(mat); const char* elems[6]; static const int order[] = {0, 3, 1, 4, 2, 5}; const int* orderPtr = order; str = mat.c_str(); strEnd = str + mat.size(); while (str < strEnd) { elems[*orderPtr++] = str; while (str < strEnd && *str != ',' ) str++; str++; } string.reset(); for (int index = 0; index < 6; index++) { const char* end = strchr(elems[index], ','); if (end == NULL) end= strchr(elems[index], ']'); string.append(elems[index], end - elems[index] + 1); } string.remove(string.size() - 1, 1); string.append(",0,0,1]"); _addAttribute("matrix", string); _endElement(); // matrix } static bool is_whitespace(char ch) { return ch > 0 && ch <= ' '; } void SkSVGParser::ConvertToArray(SkString& vals) { vals.appendUnichar(']'); char* valCh = (char*) vals.c_str(); valCh[0] = '['; int index = 1; while (valCh[index] != ']') { while (is_whitespace(valCh[index])) index++; bool foundComma = false; char next; do { next = valCh[index++]; if (next == ',') { foundComma = true; continue; } if (next == ']') { index--; goto undoLastComma; } if (next == ' ') break; foundComma = false; } while (is_whitespace(next) == false); if (foundComma == false) valCh[index - 1] = ','; } undoLastComma: while (is_whitespace(valCh[--index])) ; if (valCh[index] == ',') valCh[index] = ' '; } #define CASE_NEW(type) case SkSVGType_##type : created = new SkSVG##type(); break SkSVGElement* SkSVGParser::CreateElement(SkSVGTypes type, SkSVGElement* parent) { SkSVGElement* created = NULL; switch (type) { CASE_NEW(Circle); CASE_NEW(ClipPath); CASE_NEW(Defs); CASE_NEW(Ellipse); CASE_NEW(FeColorMatrix); CASE_NEW(Filter); CASE_NEW(G); CASE_NEW(Image); CASE_NEW(Line); CASE_NEW(LinearGradient); CASE_NEW(Mask); CASE_NEW(Metadata); CASE_NEW(Path); CASE_NEW(Polygon); CASE_NEW(Polyline); CASE_NEW(RadialGradient); CASE_NEW(Rect); CASE_NEW(Stop); CASE_NEW(SVG); CASE_NEW(Symbol); CASE_NEW(Text); CASE_NEW(Tspan); CASE_NEW(Use); default: SkASSERT(0); return NULL; } created->fParent = parent; bool isDef = created->fIsDef = created->isDef(); bool isNotDef = created->fIsNotDef = created->isNotDef(); if (isDef) { SkSVGElement* up = parent; while (up && up->fIsDef == false) { up->fIsDef = true; up = up->fParent; } } if (isNotDef) { SkSVGElement* up = parent; while (up && up->fIsNotDef == false) { up->fIsNotDef = true; up = up->fParent; } } return created; } const SkSVGTypeName gSVGTypeNames[] = { {"circle", SkSVGType_Circle}, {"clipPath", SkSVGType_ClipPath}, {"defs", SkSVGType_Defs}, {"ellipse", SkSVGType_Ellipse}, {"feColorMatrix", SkSVGType_FeColorMatrix}, {"filter", SkSVGType_Filter}, {"g", SkSVGType_G}, {"image", SkSVGType_Image}, {"line", SkSVGType_Line}, {"linearGradient", SkSVGType_LinearGradient}, {"mask", SkSVGType_Mask}, {"metadata", SkSVGType_Metadata}, {"path", SkSVGType_Path}, {"polygon", SkSVGType_Polygon}, {"polyline", SkSVGType_Polyline}, {"radialGradient", SkSVGType_RadialGradient}, {"rect", SkSVGType_Rect}, {"stop", SkSVGType_Stop}, {"svg", SkSVGType_SVG}, {"symbol", SkSVGType_Symbol}, {"text", SkSVGType_Text}, {"tspan", SkSVGType_Tspan}, {"use", SkSVGType_Use} }; const int kSVGTypeNamesSize = SK_ARRAY_COUNT(gSVGTypeNames); SkSVGTypes SkSVGParser::GetType(const char match[], size_t len ) { int index = SkStrSearch(&gSVGTypeNames[0].fName, kSVGTypeNamesSize, match, len, sizeof(gSVGTypeNames[0])); return index >= 0 && index < kSVGTypeNamesSize ? gSVGTypeNames[index].fType : (SkSVGTypes) -1; }