1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Added XmlElement methods to help parse namespaces on tagnames. Updated SVG parser to handle files which use namespaces.

This commit is contained in:
jules 2013-04-01 11:19:38 +01:00
parent 30f02c4cfd
commit 93dd9757e5
3 changed files with 96 additions and 75 deletions

View file

@ -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

View file

@ -249,22 +249,29 @@ public:
//==============================================================================
/** Returns this element's tag type name.
E.g. for an element such as \<MOOSE legs="4" antlers="2">, this would return
"MOOSE".
E.g. for an element such as \<MOOSE legs="4" antlers="2">, 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.

View file

@ -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<float> (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 <float>& coords, const String& list,
const bool allowUnits, const bool isX) const
{