RelativeRectangle DrawableComposite::getContentArea() const
{
    jassert (markersX.getNumMarkers() >= 2 && markersX.getMarker (0)->name == contentLeftMarkerName && markersX.getMarker (1)->name == contentRightMarkerName);
    jassert (markersY.getNumMarkers() >= 2 && markersY.getMarker (0)->name == contentTopMarkerName && markersY.getMarker (1)->name == contentBottomMarkerName);

    return RelativeRectangle (markersX.getMarker(0)->position, markersX.getMarker(1)->position,
                              markersY.getMarker(0)->position, markersY.getMarker(1)->position);
}
DrawableComposite::DrawableComposite()
    : bounds (Point<float>(), Point<float> (100.0f, 0.0f), Point<float> (0.0f, 100.0f)),
      updateBoundsReentrant (false)
{
    setContentArea (RelativeRectangle (RelativeCoordinate (0.0),
                                       RelativeCoordinate (100.0),
                                       RelativeCoordinate (0.0),
                                       RelativeCoordinate (100.0)));
}
RelativeRectangle DrawableComposite::ValueTreeWrapper::getContentArea() const
{
    MarkerList::ValueTreeWrapper markersX (getMarkerList (true));
    MarkerList::ValueTreeWrapper markersY (getMarkerList (false));

    return RelativeRectangle (markersX.getMarker (markersX.getMarkerState (0)).position,
                              markersX.getMarker (markersX.getMarkerState (1)).position,
                              markersY.getMarker (markersY.getMarkerState (0)).position,
                              markersY.getMarker (markersY.getMarkerState (1)).position);
}
void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren()
{
    const Rectangle<float> activeArea (getDrawableBounds());

    setContentArea (RelativeRectangle (RelativeCoordinate (activeArea.getX()),
                                       RelativeCoordinate (activeArea.getRight()),
                                       RelativeCoordinate (activeArea.getY()),
                                       RelativeCoordinate (activeArea.getBottom())));
    resetBoundingBoxToContentArea();
}
    //==============================================================================
    Drawable* parseSVGElement (const XmlPath& xml)
    {
        if (! xml->hasTagNameIgnoringNamespace ("svg"))
            return nullptr;

        DrawableComposite* const drawable = new DrawableComposite();

        setDrawableID (*drawable, xml);

        SVGState newState (*this);

        if (xml->hasAttribute ("transform"))
            newState.addTransform (xml);

        newState.elementX = getCoordLength (xml->getStringAttribute ("x",      String (newState.elementX)), viewBoxW);
        newState.elementY = getCoordLength (xml->getStringAttribute ("y",      String (newState.elementY)), viewBoxH);
        newState.width    = getCoordLength (xml->getStringAttribute ("width",  String (newState.width)),    viewBoxW);
        newState.height   = getCoordLength (xml->getStringAttribute ("height", String (newState.height)),   viewBoxH);

        if (newState.width  <= 0) newState.width  = 100;
        if (newState.height <= 0) newState.height = 100;

        Point<float> viewboxXY;

        if (xml->hasAttribute ("viewBox"))
        {
            const String viewBoxAtt (xml->getStringAttribute ("viewBox"));
            String::CharPointerType viewParams (viewBoxAtt.getCharPointer());
            Point<float> vwh;

            if (parseCoords (viewParams, viewboxXY, true)
                 && parseCoords (viewParams, vwh, true)
                 && vwh.x > 0
                 && vwh.y > 0)
            {
                newState.viewBoxW = vwh.x;
                newState.viewBoxH = vwh.y;

                int placementFlags = 0;

                const String aspect (xml->getStringAttribute ("preserveAspectRatio"));

                if (aspect.containsIgnoreCase ("none"))
                {
                    placementFlags = RectanglePlacement::stretchToFit;
                }
                else
                {
                    if (aspect.containsIgnoreCase ("slice"))        placementFlags |= RectanglePlacement::fillDestination;

                    if (aspect.containsIgnoreCase ("xMin"))         placementFlags |= RectanglePlacement::xLeft;
                    else if (aspect.containsIgnoreCase ("xMax"))    placementFlags |= RectanglePlacement::xRight;
                    else                                            placementFlags |= RectanglePlacement::xMid;

                    if (aspect.containsIgnoreCase ("yMin"))         placementFlags |= RectanglePlacement::yTop;
                    else if (aspect.containsIgnoreCase ("yMax"))    placementFlags |= RectanglePlacement::yBottom;
                    else                                            placementFlags |= RectanglePlacement::yMid;
                }

                newState.transform = RectanglePlacement (placementFlags)
                                        .getTransformToFit (Rectangle<float> (viewboxXY.x, viewboxXY.y, vwh.x, vwh.y),
                                                            Rectangle<float> (newState.width, newState.height))
                                        .followedBy (newState.transform);
            }
        }
        else
        {
            if (viewBoxW == 0)  newState.viewBoxW = newState.width;
            if (viewBoxH == 0)  newState.viewBoxH = newState.height;
        }

        newState.parseSubElements (xml, *drawable);

        drawable->setContentArea (RelativeRectangle (RelativeCoordinate (viewboxXY.x),
                                                     RelativeCoordinate (viewboxXY.x + newState.viewBoxW),
                                                     RelativeCoordinate (viewboxXY.y),
                                                     RelativeCoordinate (viewboxXY.y + newState.viewBoxH)));
        drawable->resetBoundingBoxToContentArea();

        return drawable;
    }