diff --git a/modules/juce_core/xml/juce_XmlElement.cpp b/modules/juce_core/xml/juce_XmlElement.cpp index 27585047a8..5beea41c41 100644 --- a/modules/juce_core/xml/juce_XmlElement.cpp +++ b/modules/juce_core/xml/juce_XmlElement.cpp @@ -29,9 +29,8 @@ XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) n { } -XmlElement::XmlAttributeNode::XmlAttributeNode (const String& name_, const String& value_) noexcept - : name (name_), - value (value_) +XmlElement::XmlAttributeNode::XmlAttributeNode (const String& n, const String& v) noexcept + : name (n), value (v) { #if JUCE_DEBUG // this checks whether the attribute name string contains any illegal characters.. @@ -46,14 +45,14 @@ inline bool XmlElement::XmlAttributeNode::hasName (const String& nameToMatch) co } //============================================================================== -XmlElement::XmlElement (const String& tagName_) noexcept - : tagName (tagName_) +XmlElement::XmlElement (const String& tag) noexcept + : tagName (tag) { // the tag name mustn't be empty, or it'll look like a text element! - jassert (tagName_.containsNonWhitespaceChars()) + jassert (tag.containsNonWhitespaceChars()) // The tag can't contain spaces or other characters that would create invalid XML! - jassert (! tagName_.containsAnyOf (" <>/&")); + jassert (! tag.containsAnyOf (" <>/&")); } XmlElement::XmlElement (int /*dummy*/) noexcept @@ -362,24 +361,30 @@ bool XmlElement::writeToFile (const File& file, } //============================================================================== -bool XmlElement::hasTagName (const String& tagNameWanted) const noexcept +bool XmlElement::hasTagName (const String& possibleTagName) const noexcept { - #if JUCE_DEBUG - // if debugging, check that the case is actually the same, because - // valid xml is case-sensitive, and although this lets it pass, it's - // better not to.. - if (tagName.equalsIgnoreCase (tagNameWanted)) - { - jassert (tagName == tagNameWanted); - return true; - } - else - { - return false; - } - #else - return tagName.equalsIgnoreCase (tagNameWanted); - #endif + const bool matches = tagName.equalsIgnoreCase (possibleTagName); + + // XML tags should be case-sensitive, so although this method allows a + // case-insensitive match to pass, you should try to avoid this. + jassert ((! matches) || tagName == possibleTagName); + + return matches; +} + +String XmlElement::getNamespace() const +{ + return tagName.upToFirstOccurrenceOf (":", false, false); +} + +String XmlElement::getTagNameWithoutNamespace() const +{ + return tagName.fromLastOccurrenceOf (":", false, false); +} + +bool XmlElement::hasTagNameIgnoringNamespace (const String& possibleTagName) const +{ + return hasTagName (possibleTagName) || getTagNameWithoutNamespace() == possibleTagName; } XmlElement* XmlElement::getNextElementWithTagName (const String& requiredTagName) const diff --git a/modules/juce_core/xml/juce_XmlElement.h b/modules/juce_core/xml/juce_XmlElement.h index cb496b9b11..196f66d018 100644 --- a/modules/juce_core/xml/juce_XmlElement.h +++ b/modules/juce_core/xml/juce_XmlElement.h @@ -249,22 +249,29 @@ public: //============================================================================== /** Returns this element's tag type name. - - E.g. for an element such as \, this would return - "MOOSE". - + E.g. for an element such as \, this would return "MOOSE". @see hasTagName */ inline const String& getTagName() const noexcept { return tagName; } + /** Returns the namespace portion of the tag-name, or an empty string if none is specified. */ + String getNamespace() const; + + /** Returns the part of the tag-name that follows any namespace declaration. */ + String getTagNameWithoutNamespace() const; + /** Tests whether this element has a particular tag name. - @param possibleTagName the tag name you're comparing it with - @see getTagName */ bool hasTagName (const String& possibleTagName) const noexcept; + /** Tests whether this element has a particular tag name, ignoring any XML namespace prefix. + So a test for e.g. "xyz" will return true for "xyz" and also "foo:xyz", "bar::xyz", etc. + @see getTagName + */ + bool hasTagNameIgnoringNamespace (const String& possibleTagName) const; + //============================================================================== /** Returns the number of XML attributes this element contains. diff --git a/modules/juce_gui_basics/drawables/juce_SVGParser.cpp b/modules/juce_gui_basics/drawables/juce_SVGParser.cpp index ada043539b..b041753f79 100644 --- a/modules/juce_gui_basics/drawables/juce_SVGParser.cpp +++ b/modules/juce_gui_basics/drawables/juce_SVGParser.cpp @@ -38,7 +38,7 @@ public: //============================================================================== Drawable* parseSVGElement (const XmlElement& xml) { - if (! xml.hasTagName ("svg")) + if (! xml.hasTagNameIgnoringNamespace ("svg")) return nullptr; DrawableComposite* const drawable = new DrawableComposite(); @@ -105,7 +105,7 @@ public: if (viewBoxH == 0) newState.viewBoxH = newState.height; } - newState.parseSubElements (xml, drawable); + newState.parseSubElements (xml, *drawable); drawable->setContentArea (RelativeRectangle (Rectangle (newState.viewBoxW, newState.viewBoxH))); drawable->resetBoundingBoxToContentArea(); @@ -121,27 +121,30 @@ private: String cssStyleText; //============================================================================== - void parseSubElements (const XmlElement& xml, DrawableComposite* const parentDrawable) + void parseSubElements (const XmlElement& xml, DrawableComposite& parentDrawable) { forEachXmlChildElement (xml, e) - { - Drawable* d = nullptr; + parentDrawable.addAndMakeVisible (parseSubElement (*e)); + } - if (e->hasTagName ("g")) d = parseGroupElement (*e); - else if (e->hasTagName ("svg")) d = parseSVGElement (*e); - else if (e->hasTagName ("path")) d = parsePath (*e); - else if (e->hasTagName ("rect")) d = parseRect (*e); - else if (e->hasTagName ("circle")) d = parseCircle (*e); - else if (e->hasTagName ("ellipse")) d = parseEllipse (*e); - else if (e->hasTagName ("line")) d = parseLine (*e); - else if (e->hasTagName ("polyline")) d = parsePolygon (*e, true); - else if (e->hasTagName ("polygon")) d = parsePolygon (*e, false); - else if (e->hasTagName ("text")) d = parseText (*e); - else if (e->hasTagName ("switch")) d = parseSwitch (*e); - else if (e->hasTagName ("style")) parseCSSStyle (*e); + Drawable* parseSubElement (const XmlElement& xml) + { + const String tag (xml.getTagNameWithoutNamespace()); - parentDrawable->addAndMakeVisible (d); - } + 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) @@ -163,11 +166,11 @@ private: SVGState newState (*this); newState.addTransform (xml); - newState.parseSubElements (xml, drawable); + newState.parseSubElements (xml, *drawable); } else { - parseSubElements (xml, drawable); + parseSubElements (xml, *drawable); } drawable->resetContentAreaAndBoundingBoxToFitChildren(); @@ -412,26 +415,26 @@ private: if (hasRX || hasRY) { - float rx = getCoordLength (xml.getStringAttribute ("rx"), viewBoxW); - float ry = getCoordLength (xml.getStringAttribute ("ry"), viewBoxH); + float rx = getCoordLength (xml, "rx", viewBoxW); + float ry = getCoordLength (xml, "ry", viewBoxH); if (! hasRX) rx = ry; else if (! hasRY) ry = rx; - rect.addRoundedRectangle (getCoordLength (xml.getStringAttribute ("x"), viewBoxW), - getCoordLength (xml.getStringAttribute ("y"), viewBoxH), - getCoordLength (xml.getStringAttribute ("width"), viewBoxW), - getCoordLength (xml.getStringAttribute ("height"), viewBoxH), + rect.addRoundedRectangle (getCoordLength (xml, "x", viewBoxW), + getCoordLength (xml, "y", viewBoxH), + getCoordLength (xml, "width", viewBoxW), + getCoordLength (xml, "height", viewBoxH), rx, ry); } else { - rect.addRectangle (getCoordLength (xml.getStringAttribute ("x"), viewBoxW), - getCoordLength (xml.getStringAttribute ("y"), viewBoxH), - getCoordLength (xml.getStringAttribute ("width"), viewBoxW), - getCoordLength (xml.getStringAttribute ("height"), viewBoxH)); + rect.addRectangle (getCoordLength (xml, "x", viewBoxW), + getCoordLength (xml, "y", viewBoxH), + getCoordLength (xml, "width", viewBoxW), + getCoordLength (xml, "height", viewBoxH)); } return parseShape (xml, rect); @@ -441,9 +444,9 @@ private: { Path circle; - const float cx = getCoordLength (xml.getStringAttribute ("cx"), viewBoxW); - const float cy = getCoordLength (xml.getStringAttribute ("cy"), viewBoxH); - const float radius = getCoordLength (xml.getStringAttribute ("r"), viewBoxW); + const float cx = getCoordLength (xml, "cx", viewBoxW); + const float cy = getCoordLength (xml, "cy", viewBoxH); + const float radius = getCoordLength (xml, "r", viewBoxW); circle.addEllipse (cx - radius, cy - radius, radius * 2.0f, radius * 2.0f); @@ -454,10 +457,10 @@ private: { Path ellipse; - const float cx = getCoordLength (xml.getStringAttribute ("cx"), viewBoxW); - const float cy = getCoordLength (xml.getStringAttribute ("cy"), viewBoxH); - const float radiusX = getCoordLength (xml.getStringAttribute ("rx"), viewBoxW); - const float radiusY = getCoordLength (xml.getStringAttribute ("ry"), viewBoxH); + const float cx = getCoordLength (xml, "cx", viewBoxW); + const float cy = getCoordLength (xml, "cy", viewBoxH); + const float radiusX = getCoordLength (xml, "rx", viewBoxW); + const float radiusY = getCoordLength (xml, "ry", viewBoxH); ellipse.addEllipse (cx - radiusX, cy - radiusY, radiusX * 2.0f, radiusY * 2.0f); @@ -468,10 +471,10 @@ private: { Path line; - const float x1 = getCoordLength (xml.getStringAttribute ("x1"), viewBoxW); - const float y1 = getCoordLength (xml.getStringAttribute ("y1"), viewBoxH); - const float x2 = getCoordLength (xml.getStringAttribute ("x2"), viewBoxW); - const float y2 = getCoordLength (xml.getStringAttribute ("y2"), viewBoxH); + const float x1 = getCoordLength (xml, "x1", viewBoxW); + const float y1 = getCoordLength (xml, "y1", viewBoxH); + const float x2 = getCoordLength (xml, "x2", viewBoxW); + const float y2 = getCoordLength (xml, "y2", viewBoxH); line.startNewSubPath (x1, y1); line.lineTo (x2, y2); @@ -615,7 +618,7 @@ private: jassert (gradient.getNumColours() > 0); - gradient.isRadial = fillXml->hasTagName ("radialGradient"); + gradient.isRadial = fillXml->hasTagNameIgnoringNamespace ("radialGradient"); float gradientWidth = viewBoxW; float gradientHeight = viewBoxH; @@ -722,7 +725,8 @@ private: .upToLastOccurrenceOf (")", false, false).trim()); if (const XmlElement* const fillXml = findElementForId (topLevelXml, id)) - if (fillXml->hasTagName ("linearGradient") || fillXml->hasTagName ("radialGradient")) + if (fillXml->hasTagNameIgnoringNamespace ("linearGradient") + || fillXml->hasTagNameIgnoringNamespace ("radialGradient")) return getGradientFillType (fillXml, path, opacity); } @@ -788,7 +792,7 @@ private: Drawable* s = parseShape (*e, path); delete s; // xxx not finished! } - else if (e->hasTagName ("tspan")) + else if (e->hasTagNameIgnoringNamespace ("tspan")) { Drawable* s = parseText (*e); delete s; // xxx not finished! @@ -857,6 +861,11 @@ private: return n; } + float getCoordLength (const XmlElement& xml, const char* attName, const float sizeForProportions) const + { + return getCoordLength (xml.getStringAttribute (attName), sizeForProportions); + } + void getCoordList (Array & coords, const String& list, const bool allowUnits, const bool isX) const {