/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkCanvas.h" #include "SkDOM.h" #include "SkParse.h" #include "SkParsePath.h" #include "SkString.h" #include "SkSVGAttributeParser.h" #include "SkSVGDOM.h" #include "SkSVGG.h" #include "SkSVGNode.h" #include "SkSVGPath.h" #include "SkSVGRect.h" #include "SkSVGRenderContext.h" #include "SkSVGSVG.h" #include "SkSVGTypes.h" #include "SkSVGValue.h" #include "SkTSearch.h" namespace { const char* ParseScalarPair(const char* str, SkScalar v[2]) { str = SkParse::FindScalar(str, v); if (str) { const char* second = SkParse::FindScalar(str, v + 1); if (!second) { v[1] = v[0]; } else { str = second; } } return str; } SkMatrix ParseTransform(const char* str) { SkMatrix m = SkMatrix::I(); // FIXME: real parser if (!strncmp(str, "matrix(", 7)) { SkScalar values[6]; str = SkParse::FindScalars(str + 7, values, 6); if (str) { m.setAffine(values); } } else if (!strncmp(str, "scale(", 6)) { SkScalar values[2]; str = ParseScalarPair(str + 6, values); if (str) { m.setScale(values[0], values[1]); } } else if (!strncmp(str, "translate(", 10)) { SkScalar values[2]; str = ParseScalarPair(str + 10, values); if (str) { m.setTranslate(values[0], values[1]); } } else if (!strncmp(str, "rotate(", 7)) { SkScalar value; str = SkParse::FindScalar(str + 7, &value); if (str) { m.setRotate(value); } } return m; } bool SetPaintAttribute(const sk_sp& node, SkSVGAttribute attr, const char* stringValue) { SkSVGColor color; SkSVGAttributeParser parser(stringValue); if (!parser.parseColor(&color)) { return false; } node->setAttribute(attr, SkSVGColorValue(color)); return true; } bool SetPathDataAttribute(const sk_sp& node, SkSVGAttribute attr, const char* stringValue) { SkPath path; if (!SkParsePath::FromSVGString(stringValue, &path)) { return false; } node->setAttribute(attr, SkSVGPathValue(path)); return true; } bool SetTransformAttribute(const sk_sp& node, SkSVGAttribute attr, const char* stringValue) { node->setAttribute(attr, SkSVGTransformValue(ParseTransform(stringValue))); return true; } bool SetLengthAttribute(const sk_sp& node, SkSVGAttribute attr, const char* stringValue) { SkSVGLength length; SkSVGAttributeParser parser(stringValue); if (!parser.parseLength(&length)) { return false; } node->setAttribute(attr, SkSVGLengthValue(length)); return true; } // Breaks a "foo: bar; baz: ..." string into key:value pairs. class StyleIterator { public: StyleIterator(const char* str) : fPos(str) { } std::tuple next() { SkString name, value; if (fPos) { const char* sep = this->nextSeparator(); SkASSERT(*sep == ';' || *sep == '\0'); const char* valueSep = strchr(fPos, ':'); if (valueSep && valueSep < sep) { name.set(fPos, valueSep - fPos); value.set(valueSep + 1, sep - valueSep - 1); } fPos = *sep ? sep + 1 : nullptr; } return std::make_tuple(name, value); } private: const char* nextSeparator() const { const char* sep = fPos; while (*sep != ';' && *sep != '\0') { sep++; } return sep; } const char* fPos; }; void set_string_attribute(const sk_sp& node, const char* name, const char* value); bool SetStyleAttributes(const sk_sp& node, SkSVGAttribute, const char* stringValue) { SkString name, value; StyleIterator iter(stringValue); for (;;) { std::tie(name, value) = iter.next(); if (name.isEmpty()) { break; } set_string_attribute(node, name.c_str(), value.c_str()); } return true; } template struct SortedDictionaryEntry { const char* fKey; const T fValue; }; struct AttrParseInfo { SkSVGAttribute fAttr; bool (*fSetter)(const sk_sp& node, SkSVGAttribute attr, const char* stringValue); }; SortedDictionaryEntry gAttributeParseInfo[] = { { "d" , { SkSVGAttribute::kD , SetPathDataAttribute }}, { "fill" , { SkSVGAttribute::kFill , SetPaintAttribute }}, { "height" , { SkSVGAttribute::kHeight , SetLengthAttribute }}, { "stroke" , { SkSVGAttribute::kStroke , SetPaintAttribute }}, { "style" , { SkSVGAttribute::kUnknown , SetStyleAttributes }}, { "transform", { SkSVGAttribute::kTransform, SetTransformAttribute }}, { "width" , { SkSVGAttribute::kWidth , SetLengthAttribute }}, { "x" , { SkSVGAttribute::kX , SetLengthAttribute }}, { "y" , { SkSVGAttribute::kY , SetLengthAttribute }}, }; SortedDictionaryEntry(*)()> gTagFactories[] = { { "g" , []() -> sk_sp { return SkSVGG::Make(); }}, { "path", []() -> sk_sp { return SkSVGPath::Make(); }}, { "rect", []() -> sk_sp { return SkSVGRect::Make(); }}, { "svg" , []() -> sk_sp { return SkSVGSVG::Make(); }}, }; struct ConstructionContext { ConstructionContext() : fParent(nullptr) { } ConstructionContext(const ConstructionContext& other, const sk_sp& newParent) : fParent(newParent.get()) { } const SkSVGNode* fParent; }; void set_string_attribute(const sk_sp& node, const char* name, const char* value) { const int attrIndex = SkStrSearch(&gAttributeParseInfo[0].fKey, SkTo(SK_ARRAY_COUNT(gAttributeParseInfo)), name, sizeof(gAttributeParseInfo[0])); if (attrIndex < 0) { SkDebugf("unhandled attribute: %s\n", name); return; } SkASSERT(SkTo(attrIndex) < SK_ARRAY_COUNT(gAttributeParseInfo)); const auto& attrInfo = gAttributeParseInfo[attrIndex].fValue; if (!attrInfo.fSetter(node, attrInfo.fAttr, value)) { SkDebugf("could not parse attribute: '%s=\"%s\"'\n", name, value); } } void parse_node_attributes(const SkDOM& xmlDom, const SkDOM::Node* xmlNode, const sk_sp& svgNode) { const char* name, *value; SkDOM::AttrIter attrIter(xmlDom, xmlNode); while ((name = attrIter.next(&value))) { set_string_attribute(svgNode, name, value); } } sk_sp construct_svg_node(const SkDOM& dom, const ConstructionContext& ctx, const SkDOM::Node* xmlNode) { const char* elem = dom.getName(xmlNode); const SkDOM::Type elemType = dom.getType(xmlNode); if (elemType == SkDOM::kText_Type) { SkASSERT(dom.countChildren(xmlNode) == 0); // TODO: text handling return nullptr; } SkASSERT(elemType == SkDOM::kElement_Type); const int tagIndex = SkStrSearch(&gTagFactories[0].fKey, SkTo(SK_ARRAY_COUNT(gTagFactories)), elem, sizeof(gTagFactories[0])); if (tagIndex < 0) { SkDebugf("unhandled element: <%s>\n", elem); return nullptr; } SkASSERT(SkTo(tagIndex) < SK_ARRAY_COUNT(gTagFactories)); sk_sp node = gTagFactories[tagIndex].fValue(); parse_node_attributes(dom, xmlNode, node); ConstructionContext localCtx(ctx, node); for (auto* child = dom.getFirstChild(xmlNode, nullptr); child; child = dom.getNextSibling(child)) { sk_sp childNode = construct_svg_node(dom, localCtx, child); if (childNode) { node->appendChild(std::move(childNode)); } } return node; } } // anonymous namespace SkSVGDOM::SkSVGDOM(const SkSize& containerSize) : fContainerSize(containerSize) { } sk_sp SkSVGDOM::MakeFromDOM(const SkDOM& xmlDom, const SkSize& containerSize) { sk_sp dom = sk_make_sp(containerSize); ConstructionContext ctx; dom->fRoot = construct_svg_node(xmlDom, ctx, xmlDom.getRootNode()); return dom; } sk_sp SkSVGDOM::MakeFromStream(SkStream& svgStream, const SkSize& containerSize) { SkDOM xmlDom; if (!xmlDom.build(svgStream)) { return nullptr; } return MakeFromDOM(xmlDom, containerSize); } void SkSVGDOM::render(SkCanvas* canvas) const { if (fRoot) { SkSVGRenderContext ctx(fContainerSize); fRoot->render(canvas, ctx); } } void SkSVGDOM::setContainerSize(const SkSize& containerSize) { // TODO: inval fContainerSize = containerSize; }