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