diff --git a/modules/juce_gui_basics/drawables/juce_Drawable.h b/modules/juce_gui_basics/drawables/juce_Drawable.h index f7ad85cc79..e6de08775a 100644 --- a/modules/juce_gui_basics/drawables/juce_Drawable.h +++ b/modules/juce_gui_basics/drawables/juce_Drawable.h @@ -156,6 +156,9 @@ public: */ static Drawable* createFromSVG (const XmlElement& svgDocument); + /** Parses an SVG path string and returns it. */ + static Path parseSVGPath (const String& svgPath); + //============================================================================== /** Tries to create a Drawable from a previously-saved ValueTree. The ValueTree must have been created by the createValueTree() method. diff --git a/modules/juce_gui_basics/drawables/juce_SVGParser.cpp b/modules/juce_gui_basics/drawables/juce_SVGParser.cpp index d1c748bc63..3d6ffbfa5d 100644 --- a/modules/juce_gui_basics/drawables/juce_SVGParser.cpp +++ b/modules/juce_gui_basics/drawables/juce_SVGParser.cpp @@ -27,7 +27,7 @@ class SVGState { public: //============================================================================== - SVGState (const XmlElement* const topLevel) + explicit SVGState (const XmlElement* const topLevel) : topLevelXml (topLevel), elementX (0), elementY (0), width (512), height (512), @@ -113,82 +113,7 @@ public: return drawable; } -private: - //============================================================================== - const XmlElement* const topLevelXml; - float elementX, elementY, width, height, viewBoxW, viewBoxH; - AffineTransform transform; - String cssStyleText; - //============================================================================== - void parseSubElements (const XmlElement& xml, DrawableComposite& parentDrawable) - { - forEachXmlChildElement (xml, e) - parentDrawable.addAndMakeVisible (parseSubElement (*e)); - } - - Drawable* parseSubElement (const XmlElement& xml) - { - const String tag (xml.getTagNameWithoutNamespace()); - - if (tag == "g") return parseGroupElement (xml); - if (tag == "svg") return parseSVGElement (xml); - if (tag == "path") return parsePath (xml); - if (tag == "rect") return parseRect (xml); - if (tag == "circle") return parseCircle (xml); - if (tag == "ellipse") return parseEllipse (xml); - if (tag == "line") return parseLine (xml); - if (tag == "polyline") return parsePolygon (xml, true); - if (tag == "polygon") return parsePolygon (xml, false); - if (tag == "text") return parseText (xml); - if (tag == "switch") return parseSwitch (xml); - if (tag == "style") parseCSSStyle (xml); - - return nullptr; - } - - DrawableComposite* parseSwitch (const XmlElement& xml) - { - if (const XmlElement* const group = xml.getChildByName ("g")) - return parseGroupElement (*group); - - return nullptr; - } - - DrawableComposite* parseGroupElement (const XmlElement& xml) - { - DrawableComposite* const drawable = new DrawableComposite(); - - drawable->setName (xml.getStringAttribute ("id")); - - if (xml.hasAttribute ("transform")) - { - SVGState newState (*this); - newState.addTransform (xml); - - newState.parseSubElements (xml, *drawable); - } - else - { - parseSubElements (xml, *drawable); - } - - drawable->resetContentAreaAndBoundingBoxToFitChildren(); - return drawable; - } - - //============================================================================== - Drawable* parsePath (const XmlElement& xml) const - { - Path path; - parsePathString (path, xml.getStringAttribute ("d")); - - if (getStyleAttribute (&xml, "fill-rule").trim().equalsIgnoreCase ("evenodd")) - path.setUsingNonZeroWinding (false); - - return parseShape (xml, path); - } - void parsePathString (Path& path, const String& pathString) const { String::CharPointerType d (pathString.getCharPointer().findEndOfWhitespace()); @@ -410,6 +335,82 @@ private: } } +private: + //============================================================================== + const XmlElement* const topLevelXml; + float elementX, elementY, width, height, viewBoxW, viewBoxH; + AffineTransform transform; + String cssStyleText; + + //============================================================================== + void parseSubElements (const XmlElement& xml, DrawableComposite& parentDrawable) + { + forEachXmlChildElement (xml, e) + parentDrawable.addAndMakeVisible (parseSubElement (*e)); + } + + Drawable* parseSubElement (const XmlElement& xml) + { + const String tag (xml.getTagNameWithoutNamespace()); + + if (tag == "g") return parseGroupElement (xml); + if (tag == "svg") return parseSVGElement (xml); + if (tag == "path") return parsePath (xml); + if (tag == "rect") return parseRect (xml); + if (tag == "circle") return parseCircle (xml); + if (tag == "ellipse") return parseEllipse (xml); + if (tag == "line") return parseLine (xml); + if (tag == "polyline") return parsePolygon (xml, true); + if (tag == "polygon") return parsePolygon (xml, false); + if (tag == "text") return parseText (xml); + if (tag == "switch") return parseSwitch (xml); + if (tag == "style") parseCSSStyle (xml); + + return nullptr; + } + + DrawableComposite* parseSwitch (const XmlElement& xml) + { + if (const XmlElement* const group = xml.getChildByName ("g")) + return parseGroupElement (*group); + + return nullptr; + } + + DrawableComposite* parseGroupElement (const XmlElement& xml) + { + DrawableComposite* const drawable = new DrawableComposite(); + + drawable->setName (xml.getStringAttribute ("id")); + + if (xml.hasAttribute ("transform")) + { + SVGState newState (*this); + newState.addTransform (xml); + + newState.parseSubElements (xml, *drawable); + } + else + { + parseSubElements (xml, *drawable); + } + + drawable->resetContentAreaAndBoundingBoxToFitChildren(); + return drawable; + } + + //============================================================================== + Drawable* parsePath (const XmlElement& xml) const + { + Path path; + parsePathString (path, xml.getStringAttribute ("d")); + + if (getStyleAttribute (&xml, "fill-rule").trim().equalsIgnoreCase ("evenodd")) + path.setUsingNonZeroWinding (false); + + return parseShape (xml, path); + } + Drawable* parseRect (const XmlElement& xml) const { Path rect; @@ -1267,3 +1268,11 @@ Drawable* Drawable::createFromSVG (const XmlElement& svgDocument) SVGState state (&svgDocument); return state.parseSVGElement (svgDocument); } + +Path Drawable::parseSVGPath (const String& svgPath) +{ + SVGState state (nullptr); + Path p; + state.parsePathString (p, svgPath); + return p; +}