Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/svgio/source/svgreader/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 128 kB image not shown  

SSL svgstyleattributes.cxx

  Interaktion und
PortierbarkeitC
 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/config.h>

#include <algorithm>

#include <svgstyleattributes.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
#include <svgnode.hxx>
#include <svgdocument.hxx>
#include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
#include <svggradientnode.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx>
#include <basegfx/vector/b2enums.hxx>
#include <drawinglayer/processor2d/linegeometryextractor2d.hxx>
#include <drawinglayer/processor2d/textaspolygonextractor2d.hxx>
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
#include <svgclippathnode.hxx>
#include <svgfilternode.hxx>
#include <svgmasknode.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <svgmarkernode.hxx>
#include <svgpatternnode.hxx>
#include <drawinglayer/primitive2d/patternfillprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/pagehierarchyprimitive2d.hxx>
#include <o3tl/string_view.hxx>
#include <o3tl/unit_conversion.hxx>

const int nStyleDepthLimit = 1024;

namespace svgio::svgreader
{
        static basegfx::B2DLineJoin StrokeLinejoinToB2DLineJoin(StrokeLinejoin aStrokeLinejoin)
        {
            if(StrokeLinejoin::round == aStrokeLinejoin)
            {
                return basegfx::B2DLineJoin::Round;
            }
            else if(StrokeLinejoin::bevel == aStrokeLinejoin)
            {
                return basegfx::B2DLineJoin::Bevel;
            }

            return basegfx::B2DLineJoin::Miter;
        }

        static css::drawing::LineCap StrokeLinecapToDrawingLineCap(StrokeLinecap aStrokeLinecap)
        {
            switch(aStrokeLinecap)
            {
                default/* StrokeLinecap::notset, StrokeLinecap::butt */
                {
                    return css::drawing::LineCap_BUTT;
                }
                case StrokeLinecap::round:
                {
                    return css::drawing::LineCap_ROUND;
                }
                case StrokeLinecap::square:
                {
                    return css::drawing::LineCap_SQUARE;
                }
            }
        }

        FontStretch getWider(FontStretch aSource)
        {
            switch(aSource)
            {
                case FontStretch::ultra_condensed: aSource = FontStretch::extra_condensed; break;
                case FontStretch::extra_condensed: aSource = FontStretch::condensed; break;
                case FontStretch::condensed: aSource = FontStretch::semi_condensed; break;
                case FontStretch::semi_condensed: aSource = FontStretch::normal; break;
                case FontStretch::normal: aSource = FontStretch::semi_expanded; break;
                case FontStretch::semi_expanded: aSource = FontStretch::expanded; break;
                case FontStretch::expanded: aSource = FontStretch::extra_expanded; break;
                case FontStretch::extra_expanded: aSource = FontStretch::ultra_expanded; break;
                defaultbreak;
            }

            return aSource;
        }

        FontStretch getNarrower(FontStretch aSource)
        {
            switch(aSource)
            {
                case FontStretch::extra_condensed: aSource = FontStretch::ultra_condensed; break;
                case FontStretch::condensed: aSource = FontStretch::extra_condensed; break;
                case FontStretch::semi_condensed: aSource = FontStretch::condensed; break;
                case FontStretch::normal: aSource = FontStretch::semi_condensed; break;
                case FontStretch::semi_expanded: aSource = FontStretch::normal; break;
                case FontStretch::expanded: aSource = FontStretch::semi_expanded; break;
                case FontStretch::extra_expanded: aSource = FontStretch::expanded; break;
                case FontStretch::ultra_expanded: aSource = FontStretch::extra_expanded; break;
                defaultbreak;
            }

            return aSource;
        }

        FontWeight getBolder(FontWeight aSource)
        {
            switch(aSource)
            {
                case FontWeight::N100: aSource = FontWeight::N200; break;
                case FontWeight::N200: aSource = FontWeight::N300; break;
                case FontWeight::N300: aSource = FontWeight::N400; break;
                case FontWeight::N400: aSource = FontWeight::N500; break;
                case FontWeight::N500: aSource = FontWeight::N600; break;
                case FontWeight::N600: aSource = FontWeight::N700; break;
                case FontWeight::N700: aSource = FontWeight::N800; break;
                case FontWeight::N800: aSource = FontWeight::N900; break;
                defaultbreak;
            }

            return aSource;
        }

        FontWeight getLighter(FontWeight aSource)
        {
            switch(aSource)
            {
                case FontWeight::N200: aSource = FontWeight::N100; break;
                case FontWeight::N300: aSource = FontWeight::N200; break;
                case FontWeight::N400: aSource = FontWeight::N300; break;
                case FontWeight::N500: aSource = FontWeight::N400; break;
                case FontWeight::N600: aSource = FontWeight::N500; break;
                case FontWeight::N700: aSource = FontWeight::N600; break;
                case FontWeight::N800: aSource = FontWeight::N700; break;
                case FontWeight::N900: aSource = FontWeight::N800; break;
                defaultbreak;
            }

            return aSource;
        }

        ::FontWeight getVclFontWeight(FontWeight aSource)
        {
            ::FontWeight nRetval(WEIGHT_NORMAL);

            switch(aSource)
            {
                case FontWeight::N100: nRetval = WEIGHT_ULTRALIGHT; break;
                case FontWeight::N200: nRetval = WEIGHT_LIGHT; break;
                case FontWeight::N300: nRetval = WEIGHT_SEMILIGHT; break;
                case FontWeight::N400: nRetval = WEIGHT_NORMAL; break;
                case FontWeight::N500: nRetval = WEIGHT_MEDIUM; break;
                case FontWeight::N600: nRetval = WEIGHT_SEMIBOLD; break;
                case FontWeight::N700: nRetval = WEIGHT_BOLD; break;
                case FontWeight::N800: nRetval = WEIGHT_ULTRABOLD; break;
                case FontWeight::N900: nRetval = WEIGHT_BLACK; break;
                defaultbreak;
            }

            return nRetval;
        }

        void SvgStyleAttributes::readCssStyle(std::u16string_view rCandidate)
        {
            const sal_Int32 nLen(rCandidate.size());
            sal_Int32 nPos(0);

            while(nPos < nLen)
            {
                // get TokenName
                OUStringBuffer aTokenName;
                skip_char(rCandidate, u' ', nPos, nLen);
                copyString(rCandidate, nPos, aTokenName, nLen);

                if (aTokenName.isEmpty())
                {
                    // if no TokenName advance one by force to avoid death loop, continue
                    OSL_ENSURE(false"Could not interpret on current position, advancing one byte (!)");
                    nPos++;
                    continue;
                }

                // get TokenValue
                OUStringBuffer aTokenValue;
                skip_char(rCandidate, u' ', u':', nPos, nLen);
                copyToLimiter(rCandidate, u';', nPos, aTokenValue, nLen);
                skip_char(rCandidate, u' ', u';', nPos, nLen);

                if (aTokenValue.isEmpty())
                {
                    // no value - continue
                    continue;
                }

                // generate OUStrings
                const OUString aOUTokenName(aTokenName.makeStringAndClear());
                OUString aOUTokenValue(aTokenValue.makeStringAndClear());

                // check for '!important' CssStyle mark, currently not supported
                // but needs to be extracted for correct parsing
                OUString aTokenImportant(u"!important"_ustr);
                const sal_Int32 nIndexTokenImportant(aOUTokenValue.indexOf(aTokenImportant));

                if(-1 != nIndexTokenImportant)
                {
                    // if there currently just remove it and remove spaces to have the value only
                    OUString aNewOUTokenValue;

                    if(nIndexTokenImportant > 0)
                    {
                        // copy content before token
                        aNewOUTokenValue += aOUTokenValue.subView(0, nIndexTokenImportant);
                    }

                    if(aOUTokenValue.getLength() > nIndexTokenImportant + aTokenImportant.getLength())
                    {
                        // copy content after token
                        aNewOUTokenValue += aOUTokenValue.subView(nIndexTokenImportant + aTokenImportant.getLength());
                    }

                    // remove spaces
                    aOUTokenValue = aNewOUTokenValue.trim();
                }

                // valid token-value pair, parse it
                parseStyleAttribute(StrToSVGToken(aOUTokenName, true), aOUTokenValue);
            }
        }

        const SvgStyleAttributes* SvgStyleAttributes::getCssStyleOrParentStyle() const
        {
            if(const SvgStyleAttributes* pCssStyleParent = getCssStyle())
            {
                return pCssStyleParent;
            }

            if(mrOwner.supportsParentStyle() && mrOwner.getParent())
            {
                return mrOwner.getParent()->getSvgStyleAttributes();
            }

            return nullptr;
        }

        const SvgMarkerNode* SvgStyleAttributes::getMarkerParentNode() const
        {
            if (SVGToken::Marker == mrOwner.getType())
                return static_cast<const SvgMarkerNode *>(&mrOwner);

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();
            if (pSvgStyleAttributes && maResolvingParent[32] < nStyleDepthLimit)
            {
                ++maResolvingParent[32];
                const SvgMarkerNode* ret = pSvgStyleAttributes->getMarkerParentNode();
                --maResolvingParent[32];
                return ret;
            }

            return nullptr;
        }

        void SvgStyleAttributes::add_text(
            drawinglayer::primitive2d::Primitive2DContainer& rTarget,
            drawinglayer::primitive2d::Primitive2DContainer&& rSource) const
        {
            if(rSource.empty())
                return;

            // at this point the primitives in rSource are of type TextSimplePortionPrimitive2D
            // or TextDecoratedPortionPrimitive2D and have the Fill Color (pAttributes->getFill())
            // set. When another fill is used and also evtl. stroke is set it gets necessary to
            // dismantle to geometry and add needed primitives
            const basegfx::BColor* pFill = getFill();
            const SvgGradientNode* pFillGradient = getSvgGradientNodeFill();
            const SvgPatternNode* pFillPattern = getSvgPatternNodeFill();
            const basegfx::BColor* pStroke = getStroke();
            const SvgGradientNode* pStrokeGradient = getSvgGradientNodeStroke();
            const SvgPatternNode* pStrokePattern = getSvgPatternNodeStroke();
            basegfx::B2DPolyPolygon aMergedArea;

            if(pFillGradient || pFillPattern || pStroke || pStrokeGradient || pStrokePattern)
            {
                // text geometry is needed, create
                // use neutral ViewInformation and create LineGeometryExtractor2D
                const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
                drawinglayer::processor2d::TextAsPolygonExtractor2D aExtractor(aViewInformation2D);

                // process
                aExtractor.process(rSource);

                // get results
                const drawinglayer::processor2d::TextAsPolygonDataNodeVector& rResult = aExtractor.getTarget();
                const sal_uInt32 nResultCount(rResult.size());
                basegfx::B2DPolyPolygonVector aTextFillVector;
                aTextFillVector.reserve(nResultCount);

                for(sal_uInt32 a(0); a < nResultCount; a++)
                {
                    const drawinglayer::processor2d::TextAsPolygonDataNode& rCandidate = rResult[a];

                    if(rCandidate.getIsFilled())
                    {
                        aTextFillVector.push_back(rCandidate.getB2DPolyPolygon());
                    }
                }

                if(!aTextFillVector.empty())
                {
                    aMergedArea = basegfx::utils::mergeToSinglePolyPolygon(aTextFillVector);
                }
            }

            const bool bStrokeUsed(pStroke || pStrokeGradient || pStrokePattern);

            // add fill. Use geometry even for simple color fill when stroke
            // is used, else text rendering and the geometry-based stroke will
            // normally not really match optically due to diverse system text
            // renderers
            if(aMergedArea.count() && (pFillGradient || pFillPattern || bStrokeUsed))
            {
                // create text fill content based on geometry
                add_fill(aMergedArea, rTarget, aMergedArea.getB2DRange());
            }
            else if(pFill)
            {
                // add the already prepared primitives for single color fill
                rTarget.append(std::move(rSource));
            }

            // add stroke
            if(aMergedArea.count() && bStrokeUsed)
            {
                // create text stroke content
                add_stroke(aMergedArea, rTarget, aMergedArea.getB2DRange());
            }
        }

        void SvgStyleAttributes::add_fillGradient(
            const basegfx::B2DPolyPolygon& rPath,
            drawinglayer::primitive2d::Primitive2DContainer& rTarget,
            const SvgGradientNode& rFillGradient,
            const basegfx::B2DRange& rGeoRange) const
        {
            // create fill content
            drawinglayer::primitive2d::SvgGradientEntryVector aSvgGradientEntryVector;

            // get the color stops
            rFillGradient.collectGradientEntries(aSvgGradientEntryVector);

            if(aSvgGradientEntryVector.empty())
                return;

            basegfx::B2DHomMatrix aGeoToUnit;
            basegfx::B2DHomMatrix aGradientTransform;

            if(rFillGradient.getGradientTransform())
            {
                aGradientTransform = *rFillGradient.getGradientTransform();
            }

            if (SvgUnits::userSpaceOnUse == rFillGradient.getGradientUnits())
            {
                aGeoToUnit.translate(-rGeoRange.getMinX(), -rGeoRange.getMinY());
                aGeoToUnit.scale(1.0 / rGeoRange.getWidth(), 1.0 / rGeoRange.getHeight());
            }

            if(SVGToken::LinearGradient == rFillGradient.getType())
            {
                basegfx::B2DPoint aStart(0.0, 0.0);
                basegfx::B2DPoint aEnd(1.0, 0.0);

                if (SvgUnits::userSpaceOnUse == rFillGradient.getGradientUnits())
                {
                    // all possible units
                    aStart.setX(rFillGradient.getX1().solve(mrOwner, NumberType::xcoordinate));
                    aStart.setY(rFillGradient.getY1().solve(mrOwner, NumberType::ycoordinate));
                    aEnd.setX(rFillGradient.getX2().solve(mrOwner, NumberType::xcoordinate));
                    aEnd.setY(rFillGradient.getY2().solve(mrOwner, NumberType::ycoordinate));
                }
                else
                {
                    // fractions or percent relative to object bounds
                    const SvgNumber X1(rFillGradient.getX1());
                    const SvgNumber Y1(rFillGradient.getY1());
                    const SvgNumber X2(rFillGradient.getX2());
                    const SvgNumber Y2(rFillGradient.getY2());

                    aStart.setX(SvgUnit::percent == X1.getUnit() ? X1.getNumber() * 0.01 : X1.getNumber());
                    aStart.setY(SvgUnit::percent == Y1.getUnit() ? Y1.getNumber() * 0.01 : Y1.getNumber());
                    aEnd.setX(SvgUnit::percent == X2.getUnit() ? X2.getNumber() * 0.01 : X2.getNumber());
                    aEnd.setY(SvgUnit::percent == Y2.getUnit() ? Y2.getNumber() * 0.01 : Y2.getNumber());
                }

                if(!aGeoToUnit.isIdentity())
                {
                    aStart *= aGeoToUnit;
                    aEnd *= aGeoToUnit;
                }

                rTarget.push_back(
                    new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
                        aGradientTransform,
                        rPath,
                        std::move(aSvgGradientEntryVector),
                        aStart,
                        aEnd,
                        SvgUnits::userSpaceOnUse != rFillGradient.getGradientUnits(),
                        rFillGradient.getSpreadMethod()));
            }
            else
            {
                basegfx::B2DPoint aStart(0.5, 0.5);
                basegfx::B2DPoint aFocal;
                double fRadius(0.5);
                const SvgNumber* pFx = rFillGradient.getFx();
                const SvgNumber* pFy = rFillGradient.getFy();
                const bool bFocal(pFx || pFy);

                if (SvgUnits::userSpaceOnUse == rFillGradient.getGradientUnits())
                {
                    // all possible units
                    aStart.setX(rFillGradient.getCx().solve(mrOwner, NumberType::xcoordinate));
                    aStart.setY(rFillGradient.getCy().solve(mrOwner, NumberType::ycoordinate));
                    fRadius = rFillGradient.getR().solve(mrOwner);

                    if(bFocal)
                    {
                        aFocal.setX(pFx ? pFx->solve(mrOwner, NumberType::xcoordinate) : aStart.getX());
                        aFocal.setY(pFy ? pFy->solve(mrOwner, NumberType::ycoordinate) : aStart.getY());
                    }
                }
                else
                {
                    // fractions or percent relative to object bounds
                    const SvgNumber Cx(rFillGradient.getCx());
                    const SvgNumber Cy(rFillGradient.getCy());
                    const SvgNumber R(rFillGradient.getR());

                    aStart.setX(SvgUnit::percent == Cx.getUnit() ? Cx.getNumber() * 0.01 : Cx.getNumber());
                    aStart.setY(SvgUnit::percent == Cy.getUnit() ? Cy.getNumber() * 0.01 : Cy.getNumber());
                    fRadius = (SvgUnit::percent == R.getUnit()) ? R.getNumber() * 0.01 : R.getNumber();

                    if(bFocal)
                    {
                        aFocal.setX(pFx ? (SvgUnit::percent == pFx->getUnit() ? pFx->getNumber() * 0.01 : pFx->getNumber()) : aStart.getX());
                        aFocal.setY(pFy ? (SvgUnit::percent == pFy->getUnit() ? pFy->getNumber() * 0.01 : pFy->getNumber()) : aStart.getY());
                    }
                }

                if(!aGeoToUnit.isIdentity())
                {
                    aStart *= aGeoToUnit;
                    fRadius = (aGeoToUnit * basegfx::B2DVector(fRadius, 0.0)).getLength();

                    if(bFocal)
                    {
                        aFocal *= aGeoToUnit;
                    }
                }

                rTarget.push_back(
                    new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D(
                        aGradientTransform,
                        rPath,
                        std::move(aSvgGradientEntryVector),
                        aStart,
                        fRadius,
                        SvgUnits::userSpaceOnUse != rFillGradient.getGradientUnits(),
                        rFillGradient.getSpreadMethod(),
                        bFocal ? &aFocal : nullptr));
            }
        }

        void SvgStyleAttributes::add_fillPatternTransform(
            const basegfx::B2DPolyPolygon& rPath,
            drawinglayer::primitive2d::Primitive2DContainer& rTarget,
            const SvgPatternNode& rFillPattern,
            const basegfx::B2DRange& rGeoRange) const
        {
            // prepare fill polyPolygon with given pattern, check for patternTransform
            if(rFillPattern.getPatternTransform() && !rFillPattern.getPatternTransform()->isIdentity())
            {
                // PatternTransform is active; Handle by filling the inverse transformed
                // path and back-transforming the result
                basegfx::B2DPolyPolygon aPath(rPath);
                basegfx::B2DHomMatrix aInv(*rFillPattern.getPatternTransform());
                drawinglayer::primitive2d::Primitive2DContainer aNewTarget;

                aInv.invert();
                aPath.transform(aInv);
                add_fillPattern(aPath, aNewTarget, rFillPattern, aPath.getB2DRange());

                if(!aNewTarget.empty())
                {
                    rTarget.push_back(
                        new drawinglayer::primitive2d::TransformPrimitive2D(
                            *rFillPattern.getPatternTransform(),
                            std::move(aNewTarget)));
                }
            }
            else
            {
                // no patternTransform, create fillPattern directly
                add_fillPattern(rPath, rTarget, rFillPattern, rGeoRange);
            }
        }

        void SvgStyleAttributes::add_fillPattern(
            const basegfx::B2DPolyPolygon& rPath,
            drawinglayer::primitive2d::Primitive2DContainer& rTarget,
            const SvgPatternNode& rFillPattern,
            const basegfx::B2DRange& rGeoRange) const
        {
            // fill polyPolygon with given pattern
            const drawinglayer::primitive2d::Primitive2DContainer& rPrimitives = rFillPattern.getPatternPrimitives();

            if(rPrimitives.empty())
                return;

            double fTargetWidth(rGeoRange.getWidth());
            double fTargetHeight(rGeoRange.getHeight());

            if(fTargetWidth <= 0.0 || fTargetHeight <= 0.0)
                return;

            // get relative values from pattern
            double fX(0.0);
            double fY(0.0);
            double fW(0.0);
            double fH(0.0);

            rFillPattern.getValuesRelative(fX, fY, fW, fH, rGeoRange, mrOwner);

            if(fW <= 0.0 || fH <= 0.0)
                return;

            // build the reference range relative to the rGeoRange
            const basegfx::B2DRange aReferenceRange(fX, fY, fX + fW, fY + fH);

            // find out how the content is mapped to the reference range
            basegfx::B2DHomMatrix aMapPrimitivesToUnitRange;
            const basegfx::B2DRange* pViewBox = rFillPattern.getViewBox();

            if(pViewBox)
            {
                // use viewBox/preserveAspectRatio
                const SvgAspectRatio& rRatio = rFillPattern.getSvgAspectRatio();
                const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);

                if(rRatio.isSet())
                {
                    // let mapping be created from SvgAspectRatio
                    aMapPrimitivesToUnitRange = rRatio.createMapping(aUnitRange, *pViewBox);
                }
                else
                {
                    // choose default mapping
                    aMapPrimitivesToUnitRange = SvgAspectRatio::createLinearMapping(aUnitRange, *pViewBox);
                }
            }
            else
            {
                // use patternContentUnits
                const SvgUnits aPatternContentUnits(rFillPattern.getPatternContentUnits() ? *rFillPattern.getPatternContentUnits() : SvgUnits::userSpaceOnUse);

                if (SvgUnits::userSpaceOnUse == aPatternContentUnits)
                {
                    // create relative mapping to unit coordinates
                    aMapPrimitivesToUnitRange.scale(1.0 / (fW * fTargetWidth), 1.0 / (fH * fTargetHeight));
                }
                else
                {
                    aMapPrimitivesToUnitRange.scale(1.0 / fW, 1.0 / fH);
                }
            }

            // apply aMapPrimitivesToUnitRange to content when used
            drawinglayer::primitive2d::Primitive2DContainer aPrimitives(rPrimitives);

            if(!aMapPrimitivesToUnitRange.isIdentity())
            {
                const drawinglayer::primitive2d::Primitive2DReference xRef(
                    new drawinglayer::primitive2d::TransformPrimitive2D(
                        aMapPrimitivesToUnitRange,
                        std::move(aPrimitives)));

                aPrimitives = drawinglayer::primitive2d::Primitive2DContainer { xRef };
            }

            // embed in PatternFillPrimitive2D
            rTarget.push_back(
                new drawinglayer::primitive2d::PatternFillPrimitive2D(
                    rPath,
                    std::move(aPrimitives),
                    aReferenceRange));
        }

        void SvgStyleAttributes::add_fill(
            const basegfx::B2DPolyPolygon& rPath,
            drawinglayer::primitive2d::Primitive2DContainer& rTarget,
            const basegfx::B2DRange& rGeoRange) const
        {
            const basegfx::BColor* pFill = nullptr;
            const SvgGradientNode* pFillGradient = nullptr;
            const SvgPatternNode* pFillPattern = nullptr;

            if (mbUseFillFromContextFill)
            {
                if (const SvgMarkerNode* pMarker = getMarkerParentNode())
                {
                    const SvgStyleAttributes* pStyle = pMarker->getContextStyleAttributes();
                    pFill = pStyle->getFill();
                    pFillGradient = pStyle->getSvgGradientNodeFill();
                    pFillPattern = pStyle->getSvgPatternNodeFill();
                }
            }
            else if (mbUseFillFromContextStroke)
            {
                if (const SvgMarkerNode* pMarker = getMarkerParentNode())
                {
                    const SvgStyleAttributes* pStyle = pMarker->getContextStyleAttributes();
                    pFill = pStyle->getStroke();
                    pFillGradient = pStyle->getSvgGradientNodeStroke();
                    pFillPattern = pStyle->getSvgPatternNodeStroke();
                }
            }
            else
            {
                pFill = getFill();
                pFillGradient = getSvgGradientNodeFill();
                pFillPattern = getSvgPatternNodeFill();
            }

            if(!(pFill || pFillGradient || pFillPattern))
                return;

            double fFillOpacity(getFillOpacity().solve(mrOwner));

            if (fFillOpacity <= 0.0 || basegfx::fTools::equalZero(fFillOpacity))
                return;

            drawinglayer::primitive2d::Primitive2DContainer aNewFill;

            if(pFillGradient)
            {
                // create fill content with SVG gradient primitive
                add_fillGradient(rPath, aNewFill, *pFillGradient, rGeoRange);
            }
            else if(pFillPattern)
            {
                // create fill content with SVG pattern primitive
                add_fillPatternTransform(rPath, aNewFill, *pFillPattern, rGeoRange);
            }
            else // if(pFill)
            {
                // create fill content
                if(basegfx::fTools::moreOrEqual(fFillOpacity, 1.0))
                {
                    // no transparence
                    aNewFill.push_back(
                        new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
                            rPath, *pFill));
                }
                else
                {
                    // transparence
                    aNewFill.push_back(
                        new drawinglayer::primitive2d::PolyPolygonRGBAPrimitive2D(
                            rPath, *pFill, 1.0 - fFillOpacity));

                    // do not embed  again below
                    fFillOpacity = 1.0;
                }
            }

            if(aNewFill.empty())
                return;

            if(basegfx::fTools::less(fFillOpacity, 1.0))
            {
                // embed in UnifiedTransparencePrimitive2D
                rTarget.push_back(
                    new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
                        std::move(aNewFill),
                        1.0 - fFillOpacity));
            }
            else
            {
                // append
                rTarget.append(aNewFill);
            }
        }

        void SvgStyleAttributes::add_stroke(
            const basegfx::B2DPolyPolygon& rPath,
            drawinglayer::primitive2d::Primitive2DContainer& rTarget,
            const basegfx::B2DRange& rGeoRange) const
        {
            const basegfx::BColor* pStroke = nullptr;
            const SvgGradientNode* pStrokeGradient = nullptr;
            const SvgPatternNode* pStrokePattern = nullptr;

            if(mbUseStrokeFromContextFill)
            {
                if (const SvgMarkerNode* pMarker = getMarkerParentNode())
                {
                    const SvgStyleAttributes* pStyle = pMarker->getContextStyleAttributes();
                    pStroke = pStyle->getFill();
                    pStrokeGradient = pStyle->getSvgGradientNodeFill();
                    pStrokePattern = pStyle->getSvgPatternNodeFill();
                }
            }
            else if(mbUseStrokeFromContextStroke)
            {
                if (const SvgMarkerNode* pMarker = getMarkerParentNode())
                {
                    const SvgStyleAttributes* pStyle = pMarker->getContextStyleAttributes();
                    pStroke = pStyle->getStroke();
                    pStrokeGradient = pStyle->getSvgGradientNodeStroke();
                    pStrokePattern = pStyle->getSvgPatternNodeStroke();
                }
            }
            else
            {
                pStroke = getStroke();
                pStrokeGradient = getSvgGradientNodeStroke();
                pStrokePattern = getSvgPatternNodeStroke();
            }

            if(!(pStroke || pStrokeGradient || pStrokePattern))
                return;

            drawinglayer::primitive2d::Primitive2DContainer aNewStroke;
            const double fStrokeOpacity(getStrokeOpacity().solve(mrOwner));

            if (fStrokeOpacity <= 0.0 || basegfx::fTools::equalZero(fStrokeOpacity))
                return;

            // get stroke width; SVG does not use 0.0 == hairline, so 0.0 is no line at all
            const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner) : 1.0);

            if (fStrokeWidth <= 0.0 || basegfx::fTools::equalZero(fStrokeWidth))
                return;

            if (fStrokeWidth > std::numeric_limits<sal_Int32>::max())
            {
                SAL_WARN("svgio""ignoring ludicrous stroke width: " << fStrokeWidth);
                return;
            }

            drawinglayer::primitive2d::Primitive2DReference aNewLinePrimitive;

            // if we have a line with two identical points it is not really a line,
            // but used by SVG sometimes to paint a single dot.In that case, create
            // the geometry for a single dot
            if(1 == rPath.count())
            {
                const basegfx::B2DPolygon& aSingle(rPath.getB2DPolygon(0));

                if(2 == aSingle.count() && aSingle.getB2DPoint(0).equal(aSingle.getB2DPoint(1)))
                {
                    aNewLinePrimitive = new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
                        basegfx::B2DPolyPolygon(
                            basegfx::utils::createPolygonFromCircle(
                                aSingle.getB2DPoint(0),
                                fStrokeWidth * (1.44 * 0.5))),
                        pStroke ? *pStroke : basegfx::BColor(0.0, 0.0, 0.0));
                }
            }

            if(!aNewLinePrimitive.is())
            {
                // get LineJoin, LineCap and stroke array
                const basegfx::B2DLineJoin aB2DLineJoin(StrokeLinejoinToB2DLineJoin(getStrokeLinejoin()));
                const css::drawing::LineCap aLineCap(StrokeLinecapToDrawingLineCap(getStrokeLinecap()));
                ::std::vector< double > aDashArray;

                if(!getStrokeDasharray().empty())
                {
                    aDashArray = solveSvgNumberVector(getStrokeDasharray(), mrOwner);
                }

                // convert svg:stroke-miterlimit to LineAttrute:mfMiterMinimumAngle
                // The default needs to be set explicitly, because svg default <> Draw default
                double fMiterMinimumAngle;
                if (getStrokeMiterLimit().isSet())
                {
                    fMiterMinimumAngle = 2.0 * asin(1.0/getStrokeMiterLimit().getNumber());
                }
                else
                {
                    fMiterMinimumAngle = 2.0 * asin(0.25); // 1.0/default 4.0
                }

                // todo: Handle getStrokeDashOffset()

                // prepare line attribute
                const drawinglayer::attribute::LineAttribute aLineAttribute(
                    pStroke ? *pStroke : basegfx::BColor(0.0, 0.0, 0.0),
                    fStrokeWidth,
                    aB2DLineJoin,
                    aLineCap,
                    fMiterMinimumAngle);

                if(aDashArray.empty())
                {
                    aNewLinePrimitive = new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
                        rPath,
                        aLineAttribute);
                }
                else
                {
                    drawinglayer::attribute::StrokeAttribute aStrokeAttribute(std::move(aDashArray));

                    aNewLinePrimitive = new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
                        rPath,
                        aLineAttribute,
                        std::move(aStrokeAttribute));
                }
            }

            if(pStrokeGradient || pStrokePattern)
            {
                // put primitive into Primitive2DReference and Primitive2DSequence
                const drawinglayer::primitive2d::Primitive2DContainer aSeq { aNewLinePrimitive };

                // use neutral ViewInformation and create LineGeometryExtractor2D
                const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
                drawinglayer::processor2d::LineGeometryExtractor2D aExtractor(aViewInformation2D);

                // process
                aExtractor.process(aSeq);

                // check for fill rsults
                const basegfx::B2DPolyPolygonVector& rLineFillVector(aExtractor.getExtractedLineFills());

                if(!rLineFillVector.empty())
                {
                    const basegfx::B2DPolyPolygon aMergedArea(
                        basegfx::utils::mergeToSinglePolyPolygon(
                            rLineFillVector));

                    if(aMergedArea.count())
                    {
                        if(pStrokeGradient)
                        {
                            // create fill content with SVG gradient primitive. Use original GeoRange,
                            // e.g. from circle without LineWidth
                            add_fillGradient(aMergedArea, aNewStroke, *pStrokeGradient, rGeoRange);
                        }
                        else // if(pStrokePattern)
                        {
                            // create fill content with SVG pattern primitive. Use GeoRange
                            // from the expanded data, e.g. circle with extended geo by half linewidth
                            add_fillPatternTransform(aMergedArea, aNewStroke, *pStrokePattern, aMergedArea.getB2DRange());
                        }
                    }
                }
            }
            else // if(pStroke)
            {
                aNewStroke.push_back(aNewLinePrimitive);
            }

            if(aNewStroke.empty())
                return;

            if(basegfx::fTools::less(fStrokeOpacity, 1.0))
            {
                // embed in UnifiedTransparencePrimitive2D
                rTarget.push_back(
                    new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
                        std::move(aNewStroke),
                        1.0 - fStrokeOpacity));
            }
            else
            {
                // append
                rTarget.append(aNewStroke);
            }
        }

        bool SvgStyleAttributes::prepare_singleMarker(
            drawinglayer::primitive2d::Primitive2DContainer& rMarkerPrimitives,
            basegfx::B2DHomMatrix& rMarkerTransform,
            basegfx::B2DRange& rClipRange,
            const SvgMarkerNode& rMarker) const
        {
            // reset return values
            rMarkerTransform.identity();
            rClipRange.reset();

            // Set the current style attributes to the marker before calling getMarkerPrimitives,
            // which calls decomposeSvgNode to decompose the children of the marker.
            // If any children uses 'context-fill' or 'context-stroke',
            // then these style attributes will be used in add_fill or add_stroke
            const_cast<SvgMarkerNode&>(rMarker).setContextStyleAttributes(this);

            // get marker primitive representation
            rMarkerPrimitives = rMarker.getMarkerPrimitives();

            if(!rMarkerPrimitives.empty())
            {
                basegfx::B2DRange aPrimitiveRange(0.0, 0.0, 1.0, 1.0);
                const basegfx::B2DRange* pViewBox = rMarker.getViewBox();

                if(pViewBox)
                {
                    aPrimitiveRange = *pViewBox;
                }

                if(aPrimitiveRange.getWidth() > 0.0 && aPrimitiveRange.getHeight() > 0.0)
                {
                    double fTargetWidth(rMarker.getMarkerWidth().isSet() ? rMarker.getMarkerWidth().solve(mrOwner, NumberType::xcoordinate) : 3.0);
                    double fTargetHeight(rMarker.getMarkerHeight().isSet() ? rMarker.getMarkerHeight().solve(mrOwner, NumberType::xcoordinate) : 3.0);
                    const bool bStrokeWidth(SvgMarkerNode::MarkerUnits::strokeWidth == rMarker.getMarkerUnits());
                    const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner) : 1.0);

                    if(bStrokeWidth)
                    {
                        // relative to strokeWidth
                        fTargetWidth *= fStrokeWidth;
                        fTargetHeight *= fStrokeWidth;
                    }

                    if(fTargetWidth > 0.0 && fTargetHeight > 0.0)
                    {
                        // create mapping
                        const basegfx::B2DRange aTargetRange(0.0, 0.0, fTargetWidth, fTargetHeight);
                        const SvgAspectRatio& rRatio = rMarker.getSvgAspectRatio();


                        if(rRatio.isSet() && Overflow::visible != rMarker.getSvgStyleAttributes()->getOverflow())
                        {
                            // let mapping be created from SvgAspectRatio
                            rMarkerTransform = rRatio.createMapping(aTargetRange, aPrimitiveRange);

                            if(rRatio.isMeetOrSlice())
                            {
                                // need to clip
                                rClipRange = aPrimitiveRange;
                            }
                        }
                        else
                        {
                            if(!pViewBox)
                            {
                                if(bStrokeWidth)
                                {
                                    // adapt to strokewidth if needed
                                    rMarkerTransform.scale(fStrokeWidth, fStrokeWidth);
                                }
                            }
                            else
                            {
                                // choose default mapping
                                rMarkerTransform = SvgAspectRatio::createLinearMapping(aTargetRange, aPrimitiveRange);
                            }
                        }

                        // get and apply reference point. Initially it's in marker local coordinate system
                        basegfx::B2DPoint aRefPoint(
                            rMarker.getRefX().isSet() ? rMarker.getRefX().solve(mrOwner, NumberType::xcoordinate) : 0.0,
                            rMarker.getRefY().isSet() ? rMarker.getRefY().solve(mrOwner, NumberType::ycoordinate) : 0.0);

                        // apply MarkerTransform to have it in mapped coordinates
                        aRefPoint *= rMarkerTransform;

                        // apply by moving RepPoint to (0.0)
                        rMarkerTransform.translate(-aRefPoint.getX(), -aRefPoint.getY());

                        return true;
                    }
                }
            }

            return false;
        }

        void SvgStyleAttributes::add_markers(
            const basegfx::B2DPolyPolygon& rPath,
            drawinglayer::primitive2d::Primitive2DContainer& rTarget,
            const basegfx::utils::PointIndexSet* pHelpPointIndices) const
        {
            // try to access linked markers
            const SvgMarkerNode* pStart = accessMarkerStartXLink();
            const SvgMarkerNode* pMid = accessMarkerMidXLink();
            const SvgMarkerNode* pEnd = accessMarkerEndXLink();

            if(!(pStart || pMid || pEnd))
                return;

            const sal_uInt32 nSubPathCount(rPath.count());

            if(!nSubPathCount)
                return;

            // remember prepared marker; pStart, pMid and pEnd may all be equal when
            // only 'marker' was used instead of 'marker-start', 'marker-mid' or 'marker-end',
            // see 'case SVGToken::Marker' in this file; thus in this case only one common
            // marker in primitive form will be prepared
            const SvgMarkerNode* pPrepared = nullptr;

            // values for the prepared marker, results of prepare_singleMarker
            drawinglayer::primitive2d::Primitive2DContainer aPreparedMarkerPrimitives;
            basegfx::B2DHomMatrix aPreparedMarkerTransform;
            basegfx::B2DRange aPreparedMarkerClipRange;

            for (sal_uInt32 a(0); a < nSubPathCount; a++)
            {
                // iterate over sub-paths
                const basegfx::B2DPolygon& aSubPolygonPath(rPath.getB2DPolygon(a));
                const sal_uInt32 nSubPolygonPointCount(aSubPolygonPath.count());
                const bool bSubPolygonPathIsClosed(aSubPolygonPath.isClosed());

                if(nSubPolygonPointCount)
                {
                    // for each sub-path, create one marker per point (when closed, two markers
                    // need to pe created for the 1st point)
                    const sal_uInt32 nTargetMarkerCount(bSubPolygonPathIsClosed ? nSubPolygonPointCount + 1 : nSubPolygonPointCount);

                    for (sal_uInt32 b(0); b < nTargetMarkerCount; b++)
                    {
                        const bool bIsFirstMarker(!a && !b);
                        const bool bIsLastMarker(nSubPathCount - 1 == a && nTargetMarkerCount - 1 == b);
                        const SvgMarkerNode* pNeeded = nullptr;

                        if(bIsFirstMarker)
                        {
                            // 1st point in 1st sub-polygon, use pStart
                            pNeeded = pStart;
                        }
                        else if(bIsLastMarker)
                        {
                            // last point in last sub-polygon, use pEnd
                            pNeeded = pEnd;
                        }
                        else
                        {
                            // anything in-between, use pMid
                            pNeeded = pMid;
                        }

                        if(pHelpPointIndices && !pHelpPointIndices->empty())
                        {
                            const basegfx::utils::PointIndexSet::const_iterator aFound(
                                pHelpPointIndices->find(basegfx::utils::PointIndex(a, b)));

                            if(aFound != pHelpPointIndices->end())
                            {
                                // this point is a pure helper point; do not create a marker for it
                                continue;
                            }
                        }

                        if(!pNeeded)
                        {
                            // no marker needs to be created for this point
                            continue;
                        }

                        if(pPrepared != pNeeded)
                        {
                            // if needed marker is not yet prepared, do it now
                            if(prepare_singleMarker(aPreparedMarkerPrimitives, aPreparedMarkerTransform, aPreparedMarkerClipRange, *pNeeded))
                            {
                                pPrepared = pNeeded;
                            }
                            else
                            {
                                // error: could not prepare given marker
                                OSL_ENSURE(false"OOps, could not prepare given marker as primitives (!)");
                                pPrepared = nullptr;
                                continue;
                            }
                        }

                        // prepare complete transform
                        basegfx::B2DHomMatrix aCombinedTransform(aPreparedMarkerTransform);

                        // get rotation
                        if(pPrepared->getMarkerOrient() == SvgMarkerNode::MarkerOrient::auto_start ||
                            pPrepared->getMarkerOrient() == SvgMarkerNode::MarkerOrient::auto_start_reverse)
                        {
                            const sal_uInt32 nPointIndex(b % nSubPolygonPointCount);

                            // get entering and leaving tangents; this will search backward/forward
                            // in the polygon to find tangents unequal to zero, skipping empty edges
                            // see basegfx descriptions)
                            // Hint: Mozilla, Inkscape and others use only leaving tangent for start marker
                            // and entering tangent for end marker. To achieve this (if wanted) it is possible
                            // to make the fetch of aEntering/aLeaving dependent on bIsFirstMarker/bIsLastMarker.
                            // This is not done here, see comment 14 in task #1232379#
                            // or http://www.w3.org/TR/SVG/painting.html#OrientAttribute
                            basegfx::B2DVector aEntering(
                                basegfx::utils::getTangentEnteringPoint(
                                    aSubPolygonPath,
                                    nPointIndex));
                            basegfx::B2DVector aLeaving(
                                basegfx::utils::getTangentLeavingPoint(
                                    aSubPolygonPath,
                                    nPointIndex));
                            const bool bEntering(!aEntering.equalZero());
                            const bool bLeaving(!aLeaving.equalZero());

                            if(bEntering || bLeaving)
                            {
                                basegfx::B2DVector aSum(0.0, 0.0);

                                if(bEntering)
                                {
                                    if(bIsFirstMarker && pPrepared->getMarkerOrient() == SvgMarkerNode::MarkerOrient::auto_start_reverse)
                                        aSum -= aEntering.normalize();
                                    else
                                        aSum += aEntering.normalize();
                                }

                                if(bLeaving)
                                {
                                    if(bIsFirstMarker && pPrepared->getMarkerOrient() == SvgMarkerNode::MarkerOrient::auto_start_reverse)
                                        aSum -= aLeaving.normalize();
                                    else
                                        aSum += aLeaving.normalize();
                                }

                                if(!aSum.equalZero())
                                {
                                    const double fAngle(atan2(aSum.getY(), aSum.getX()));

                                    // apply rotation
                                    aCombinedTransform.rotate(fAngle);
                                }
                            }
                        }
                        else
                        {
                            // apply rotation
                            aCombinedTransform.rotate(pPrepared->getAngle());
                        }

                        // get and apply target position
                        const basegfx::B2DPoint aPoint(aSubPolygonPath.getB2DPoint(b % nSubPolygonPointCount));

                        aCombinedTransform.translate(aPoint.getX(), aPoint.getY());

                        // prepare marker
                        drawinglayer::primitive2d::Primitive2DReference xMarker(
                            new drawinglayer::primitive2d::TransformPrimitive2D(
                                aCombinedTransform,
                                drawinglayer::primitive2d::Primitive2DContainer(aPreparedMarkerPrimitives)));

                        if(!aPreparedMarkerClipRange.isEmpty())
                        {
                            // marker needs to be clipped, it's bigger as the mapping
                            basegfx::B2DPolyPolygon aClipPolygon(basegfx::utils::createPolygonFromRect(aPreparedMarkerClipRange));

                            aClipPolygon.transform(aCombinedTransform);
                            xMarker = new drawinglayer::primitive2d::MaskPrimitive2D(
                                std::move(aClipPolygon),
                                drawinglayer::primitive2d::Primitive2DContainer { xMarker });
                        }

                        // add marker
                        rTarget.push_back(xMarker);
                    }
                }
            }
        }

        void SvgStyleAttributes::add_path(
            const basegfx::B2DPolyPolygon& rPath,
            drawinglayer::primitive2d::Primitive2DContainer& rTarget,
            const basegfx::utils::PointIndexSet* pHelpPointIndices) const
        {
            if(!rPath.count())
            {
                // no geometry at all
                return;
            }

            const basegfx::B2DRange aGeoRange(rPath.getB2DRange());

            if(aGeoRange.isEmpty())
            {
                // no geometry range
                return;
            }

            const double fOpacity(getOpacity().solve(mrOwner));

            if(basegfx::fTools::equalZero(fOpacity))
            {
                // not visible
                return;
            }

            // check if it's a line
            const bool bNoWidth(basegfx::fTools::equalZero(aGeoRange.getWidth()));
            const bool bNoHeight(basegfx::fTools::equalZero(aGeoRange.getHeight()));
            const bool bIsTwoPointLine(1 == rPath.count()
                && !rPath.areControlPointsUsed()
                && 2 == rPath.getB2DPolygon(0).count());
            const bool bIsLine(bIsTwoPointLine || bNoWidth || bNoHeight);

            if(!bIsLine)
            {
                // create fill
                basegfx::B2DPolyPolygon aPath(rPath);

                if(SVGToken::Path == mrOwner.getType() || SVGToken::Polygon == mrOwner.getType())
                {
                    if(FillRule::evenodd != getClipRule() && FillRule::evenodd != getFillRule())
                    {
                        if(getFill() || getSvgGradientNodeFill() || getSvgPatternNodeFill())
                        {
                            // nonzero is wanted, solve geometrically (see description on basegfx)
                            // basegfx::utils::createNonzeroConform() is expensive for huge paths
                            // and is only needed if path will be filled later on
                            aPath = basegfx::utils::createNonzeroConform(aPath);
                        }
                    }
                }

                add_fill(aPath, rTarget, aGeoRange);
            }

            // create stroke
            add_stroke(rPath, rTarget, aGeoRange);

            // Svg supports markers for path, polygon, polyline and line
            if(SVGToken::Path == mrOwner.getType() ||         // path
                SVGToken::Polygon == mrOwner.getType() ||     // polygon
                SVGToken::Polyline == mrOwner.getType() ||     // polyline
                SVGToken::Line == mrOwner.getType() ||        // line
                SVGToken::Style == mrOwner.getType())        // tdf#150323
            {
                // try to add markers
                add_markers(rPath, rTarget, pHelpPointIndices);
            }
        }

        void SvgStyleAttributes::add_postProcess(
            drawinglayer::primitive2d::Primitive2DContainer& rTarget,
            drawinglayer::primitive2d::Primitive2DContainer&& rSource,
            const std::optional<basegfx::B2DHomMatrix>& pTransform) const
        {
            // default is 1
            double fOpacity(1.0);

            if(maOpacity.isSet())
            {
                fOpacity = maOpacity.solve(mrOwner);
            }
            else
            {
                // if opacity is not set, check the css style
                if (const SvgStyleAttributes* pSvgStyleAttributes = getCssStyle())
                {
                    if (pSvgStyleAttributes->maOpacity.isSet())
                    {
                        fOpacity =  pSvgStyleAttributes->maOpacity.solve(mrOwner);
                    }
                }
            }

            if(basegfx::fTools::equalZero(fOpacity))
            {
                return;
            }

            drawinglayer::primitive2d::Primitive2DContainer aSource(std::move(rSource));

            if(basegfx::fTools::less(fOpacity, 1.0))
            {
                // embed in UnifiedTransparencePrimitive2D
                const drawinglayer::primitive2d::Primitive2DReference xRef(
                    new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
                        std::move(aSource),
                        1.0 - fOpacity));

                aSource = drawinglayer::primitive2d::Primitive2DContainer { xRef };
            }

            if(pTransform)
            {
                // create embedding group element with transformation. This applies the given
                // transformation to the graphical content, but *not* to mask and/or clip (as needed)
                const drawinglayer::primitive2d::Primitive2DReference xRef(
                    new drawinglayer::primitive2d::TransformPrimitive2D(
                        *pTransform,
                        std::move(aSource)));

                aSource = drawinglayer::primitive2d::Primitive2DContainer { xRef };
            }

            const SvgClipPathNode* pClip = accessClipPathXLink();
            while(pClip)
            {
                // #i124852# transform may be needed when SvgUnits::userSpaceOnUse
                pClip->apply(aSource, pTransform);
                pClip = pClip->getSvgStyleAttributes()->accessClipPathXLink();
            }

            if(!aSource.empty()) // test again, applied clipPath may have lead to empty geometry
            {
                const SvgFilterNode* pFilter = accessFilterXLink();
                if(pFilter)
                {
                    pFilter->apply(aSource, nullptr);
                }
            }

            if(!aSource.empty()) // test again, applied filter may have lead to empty geometry
            {

                const SvgMaskNode* pMask = accessMaskXLink();
                if(pMask)
                {
                    // #i124852# transform may be needed when SvgUnits::userSpaceOnUse
                    pMask->apply(aSource, pTransform);
                }
            }

            // This is part of the SVG import of self-written SVGs from
            // Draw/Impress containing multiple Slides/Pages. To be able
            // to later 'break' these to multiple Pages if wanted, embed
            // each Page-Content in an identifiable Primitive Grouping
            // Object.
            // This is the case when the current Node is a GroupNode, has
            // class="Page" set, has a parent that also is a GroupNode
            // at which class="Slide" is set.
            // Multiple Slides/Pages are possible for Draw and Impress.
            if(SVGToken::G == mrOwner.getType() && mrOwner.getClass())
            {
                const OUString aOwnerClass(*mrOwner.getClass());

                if("Page" == aOwnerClass)
                {
                    const SvgNode* pParent(mrOwner.getParent());

                    if(nullptr != pParent && SVGToken::G == pParent->getType() && pParent->getClass())
                    {
                        const OUString aParentClass(*pParent->getClass());

                        if("Slide" == aParentClass)
                        {
                            // embed to grouping primitive to identify the
                            // Slide/Page information
                            const drawinglayer::primitive2d::Primitive2DReference xRef(
                                new drawinglayer::primitive2d::PageHierarchyPrimitive2D(
                                    std::move(aSource)));

                            aSource = drawinglayer::primitive2d::Primitive2DContainer { xRef };
                        }
                    }
                }
            }

            if(!aSource.empty()) // test again, applied mask may have lead to empty geometry
            {
                // append to current target
                rTarget.append(aSource);
            }
        }

        SvgStyleAttributes::SvgStyleAttributes(SvgNode& rOwner)
        :   mrOwner(rOwner),
            mpCssStyle(nullptr),
            maStopColor(basegfx::BColor(0.0, 0.0, 0.0), true),
            maStrokeLinecap(StrokeLinecap::notset),
            maStrokeLinejoin(StrokeLinejoin::notset),
            maFontSize(),
            maFontStretch(FontStretch::notset),
            maFontStyle(FontStyle::notset),
            maFontWeight(FontWeight::notset),
            maFontDirection(FontDirection::notset),
            maUnicodeBidi(UnicodeBidi::notset),
            maTextAlign(TextAlign::notset),
            maTextDecoration(TextDecoration::notset),
            maTextAnchor(TextAnchor::notset),
            maOverflow(Overflow::notset),
            maVisibility(Visibility::notset),
            maFillRule(FillRule::notset),
            maClipRule(FillRule::notset),
            maBaselineShift(BaselineShift::Baseline),
            maBaselineShiftNumber(0),
            maDominantBaseline(DominantBaseline::Auto),
            maResolvingParent(36, 0),
            mbStrokeDasharraySet(false),
            mbUseFillFromContextFill(false),
            mbUseFillFromContextStroke(false),
            mbUseStrokeFromContextFill(false),
            mbUseStrokeFromContextStroke(false)
        {
        }

        SvgStyleAttributes::~SvgStyleAttributes()
        {
        }

        void SvgStyleAttributes::parseStyleAttribute(
            SVGToken aSVGToken,
            const OUString& aContent)
        {
            switch(aSVGToken)
            {
                case SVGToken::Fill:
                {
                    SvgPaint aSvgPaint;
                    OUString aURL;
                    SvgNumber aOpacity;

                    if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"context-fill"))
                    {
                        mbUseFillFromContextFill = true;
                    }
                    else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"context-stroke"))
                    {
                        mbUseFillFromContextStroke = true;
                    }
                    else if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity))
                    {
                        setFill(aSvgPaint);
                        if(aOpacity.isSet())
                        {
                            setOpacity(SvgNumber(std::clamp(aOpacity.getNumber(), 0.0, 1.0)));
                        }
                    }
                    else if(!aURL.isEmpty())
                    {
                       maNodeFillURL = aURL;
                    }
                    break;
                }
                case SVGToken::FillOpacity:
                {
                    SvgNumber aNum;

                    if(readSingleNumber(aContent, aNum))
                    {
                        maFillOpacity = SvgNumber(std::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet());
                    }
                    break;
                }
                case SVGToken::FillRule:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), commonStrings::aStrNonzero))
                        {
                            maFillRule = FillRule::nonzero;
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), commonStrings::aStrEvenOdd))
                        {
                            maFillRule = FillRule::evenodd;
                        }
                    }
                    break;
                }
                case SVGToken::Stroke:
                {
                    SvgPaint aSvgPaint;
                    OUString aURL;
                    SvgNumber aOpacity;

                    if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"context-stroke"))
                    {
                        mbUseStrokeFromContextStroke = true;
                    }
                    else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"context-fill"))
                    {
                        mbUseStrokeFromContextFill = true;
                    }
                    else if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity))
                    {
                        maStroke = aSvgPaint;
                        if(aOpacity.isSet())
                        {
                            setOpacity(SvgNumber(std::clamp(aOpacity.getNumber(), 0.0, 1.0)));
                        }
                    }
                    else if(!aURL.isEmpty())
                    {
                        maNodeStrokeURL = aURL;
                    }
                    break;
                }
                case SVGToken::StrokeDasharray:
                {
                    if(!aContent.isEmpty())
                    {
                        SvgNumberVector aVector;

                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"none"))
                        {
                            // #121221# The special value 'none' needs to be handled
                            // in the sense that *when* it is set, the parent shall not
                            // be used. Before this was only dependent on the array being
                            // empty
                            mbStrokeDasharraySet = true;
                        }
                        else if(readSvgNumberVector(aContent, aVector))
                        {
                            maStrokeDasharray = std::move(aVector);
                        }
                    }
                    break;
                }
                case SVGToken::StrokeDashoffset:
                {
                    SvgNumber aNum;

                    if(readSingleNumber(aContent, aNum))
                    {
                        if(aNum.isPositive())
                        {
                            maStrokeDashOffset = aNum;
                        }
                    }
                    break;
                }
                case SVGToken::StrokeLinecap:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"butt"))
                        {
                            setStrokeLinecap(StrokeLinecap::butt);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"round"))
                        {
                            setStrokeLinecap(StrokeLinecap::round);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"square"))
                        {
                            setStrokeLinecap(StrokeLinecap::square);
                        }
                    }
                    break;
                }
                case SVGToken::StrokeLinejoin:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"miter"))
                        {
                            setStrokeLinejoin(StrokeLinejoin::miter);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"round"))
                        {
                            setStrokeLinejoin(StrokeLinejoin::round);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"bevel"))
                        {
                            setStrokeLinejoin(StrokeLinejoin::bevel);
                        }
                    }
                    break;
                }
                case SVGToken::StrokeMiterlimit:
                {
                    SvgNumber aNum;

                    if(readSingleNumber(aContent, aNum))
                    {
                        if(basegfx::fTools::moreOrEqual(aNum.getNumber(), 1.0))
                        { //readSingleNumber sets SvgUnit::px as default, if unit is missing. Correct it here.
                            maStrokeMiterLimit = SvgNumber(aNum.getNumber(), SvgUnit::none);
                        }
                    }
                    break;
                }
                case SVGToken::StrokeOpacity:
                {

                    SvgNumber aNum;

                    if(readSingleNumber(aContent, aNum))
                    {
                        maStrokeOpacity = SvgNumber(std::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet());
                    }
                    break;
                }
                case SVGToken::StrokeWidth:
                {
                    SvgNumber aNum;

                    if(readSingleNumber(aContent, aNum))
                    {
                        if(aNum.isPositive())
                        {
                            maStrokeWidth = aNum;
                        }
                    }
                    break;
                }
                case SVGToken::StopColor:
                {
                    SvgPaint aSvgPaint;
                    OUString aURL;
                    SvgNumber aOpacity;

                    if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity))
                    {
                        maStopColor = aSvgPaint;
                        if(aOpacity.isSet())
                        {
                            setOpacity(SvgNumber(std::clamp(aOpacity.getNumber(), 0.0, 1.0)));
                        }
                    }
                    break;
                }
                case SVGToken::StopOpacity:
                {
                    SvgNumber aNum;

                    if(readSingleNumber(aContent, aNum))
                    {
                        if(aNum.isPositive())
                        {
                            maStopOpacity = aNum;
                        }
                    }
                    break;
                }
                case SVGToken::Font:
                {
                    break;
                }
                case SVGToken::FontFamily:
                {
                    SvgStringVector aSvgStringVector;

                    if(readSvgStringVector(aContent, aSvgStringVector, ','))
                    {
                        maFontFamily = std::move(aSvgStringVector);
                    }
                    break;
                }
                case SVGToken::FontSize:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"xx-small"))
                        {
                            setFontSize(FontSize::xx_small);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"x-small"))
                        {
                            setFontSize(FontSize::x_small);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"small"))
                        {
                            setFontSize(FontSize::small);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"smaller"))
                        {
                            setFontSize(FontSize::smaller);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"medium"))
                        {
                            setFontSize(FontSize::medium);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"larger"))
                        {
                            setFontSize(FontSize::larger);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"large"))
                        {
                            setFontSize(FontSize::large);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"x-large"))
                        {
                            setFontSize(FontSize::x_large);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"xx-large"))
                        {
                            setFontSize(FontSize::xx_large);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"initial"))
                        {
                            setFontSize(FontSize::initial);
                        }
                        else
                        {
                            SvgNumber aNum;

                            if(readSingleNumber(aContent, aNum))
                            {
                                maFontSizeNumber = aNum;
                            }
                        }
                    }
                    break;
                }
                case SVGToken::FontSizeAdjust:
                {
                    break;
                }
                case SVGToken::FontStretch:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"normal"))
                        {
                            setFontStretch(FontStretch::normal);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"wider"))
                        {
                            setFontStretch(FontStretch::wider);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"narrower"))
                        {
                            setFontStretch(FontStretch::narrower);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"ultra-condensed"))
                        {
                            setFontStretch(FontStretch::ultra_condensed);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"extra-condensed"))
                        {
                            setFontStretch(FontStretch::extra_condensed);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"condensed"))
                        {
                            setFontStretch(FontStretch::condensed);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"semi-condensed"))
                        {
                            setFontStretch(FontStretch::semi_condensed);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"semi-expanded"))
                        {
                            setFontStretch(FontStretch::semi_expanded);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"expanded"))
                        {
                            setFontStretch(FontStretch::expanded);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"extra-expanded"))
                        {
                            setFontStretch(FontStretch::extra_expanded);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"ultra-expanded"))
                        {
                            setFontStretch(FontStretch::ultra_expanded);
                        }
                    }
                    break;
                }
                case SVGToken::FontStyle:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"normal"))
                        {
                            setFontStyle(FontStyle::normal);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"italic"))
                        {
                            setFontStyle(FontStyle::italic);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"oblique"))
                        {
                            setFontStyle(FontStyle::oblique);
                        }
                    }
                    break;
                }
                case SVGToken::FontVariant:
                {
                    break;
                }
                case SVGToken::FontWeight:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"100"))
                        {
                            setFontWeight(FontWeight::N100);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"200"))
                        {
                            setFontWeight(FontWeight::N200);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"300"))
                        {
                            setFontWeight(FontWeight::N300);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"400") || o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"normal"))
                        {
                            setFontWeight(FontWeight::N400);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"500"))
                        {
                            setFontWeight(FontWeight::N500);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"600"))
                        {
                            setFontWeight(FontWeight::N600);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"700") || o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"bold"))
                        {
                            setFontWeight(FontWeight::N700);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"800"))
                        {
                            setFontWeight(FontWeight::N800);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"900"))
                        {
                            setFontWeight(FontWeight::N900);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"bolder"))
                        {
                            setFontWeight(FontWeight::bolder);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"lighter"))
                        {
                            setFontWeight(FontWeight::lighter);
                        }
                    }
                    break;
                }
                case SVGToken::Direction:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"ltr"))
                        {
                            setFontDirection(FontDirection::LTR);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"rtl"))
                        {
                            setFontDirection(FontDirection::RTL);
                        }
                    }
                    break;
                }
                case SVGToken::LetterSpacing:
                {
                    break;
                }
                case SVGToken::TextDecoration:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"none"))
                        {
                            setTextDecoration(TextDecoration::none);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"underline"))
                        {
                            setTextDecoration(TextDecoration::underline);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"overline"))
                        {
                            setTextDecoration(TextDecoration::overline);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"line-through"))
                        {
                            setTextDecoration(TextDecoration::line_through);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"blink"))
                        {
                            setTextDecoration(TextDecoration::blink);
                        }
                    }
                    break;
                }
                case SVGToken::UnicodeBidi:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"normal"))
                        {
                            setUnicodeBidi(UnicodeBidi::normal);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"bidi-override"))
                        {
                            setUnicodeBidi(UnicodeBidi::bidi_override);
                        }
                    }
                    break;
                }
                case SVGToken::WordSpacing:
                {
                    break;
                }
                case SVGToken::TextAnchor:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"start"))
                        {
                            setTextAnchor(TextAnchor::start);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"middle"))
                        {
                            setTextAnchor(TextAnchor::middle);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"end"))
                        {
                            setTextAnchor(TextAnchor::end);
                        }
                    }
                    break;
                }
                case SVGToken::TextAlign:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"left"))
                        {
                            setTextAlign(TextAlign::left);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"right"))
                        {
                            setTextAlign(TextAlign::right);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"center"))
                        {
                            setTextAlign(TextAlign::center);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"justify"))
                        {
                            setTextAlign(TextAlign::justify);
                        }
                    }
                    break;
                }
                case SVGToken::Color:
                {
                    SvgPaint aSvgPaint;
                    OUString aURL;
                    SvgNumber aOpacity;

                    if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity))
                    {
                        maColor = aSvgPaint;
                        if(aOpacity.isSet())
                        {
                            setOpacity(SvgNumber(std::clamp(aOpacity.getNumber(), 0.0, 1.0)));
                        }
                    }
                    break;
                }
                case SVGToken::Opacity:
                {
                    SvgNumber aNum;

                    if(readSingleNumber(aContent, aNum))
                    {
                        setOpacity(SvgNumber(std::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet()));
                    }
                    break;
                }
                case SVGToken::Overflow:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"visible"))
                        {
                            setOverflow(Overflow::visible);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"hidden"))
                        {
                            setOverflow(Overflow::hidden);
                        }
                    }
                    break;
                }
                case SVGToken::Visibility:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"visible"))
                        {
                            setVisibility(Visibility::visible);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"hidden"))
                        {
                            setVisibility(Visibility::hidden);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"collapse"))
                        {
                            setVisibility(Visibility::collapse);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"inherit"))
                        {
                            setVisibility(Visibility::inherit);
                        }
                    }
                    break;
                }
                case SVGToken::Title:
                {
                    maTitle = aContent;
                    break;
                }
                case SVGToken::Desc:
                {
                    maDesc = aContent;
                    break;
                }
                case SVGToken::ClipPathProperty:
                {
                    readLocalUrl(aContent, maClipPathXLink);
                    break;
                }
                case SVGToken::Filter:
                {
                    readLocalUrl(aContent, maFilterXLink);
                    break;
                }
                case SVGToken::Mask:
                {
                    readLocalUrl(aContent, maMaskXLink);
                    break;
                }
                case SVGToken::ClipRule:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), commonStrings::aStrNonzero))
                        {
                            maClipRule = FillRule::nonzero;
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), commonStrings::aStrEvenOdd))
                        {
                            maClipRule = FillRule::evenodd;
                        }
                    }
                    break;
                }
                case SVGToken::Marker:
                {
                    // tdf#155819: Using the marker property from a style sheet is equivalent to using all three (start, mid, end).
                    if(mrOwner.getType() == SVGToken::Style)
                    {
                        readLocalUrl(aContent, maMarkerEndXLink);
                        maMarkerStartXLink = maMarkerMidXLink = maMarkerEndXLink;
                    }
                    break;
                }
                case SVGToken::MarkerStart:
                {
                    readLocalUrl(aContent, maMarkerStartXLink);
                    break;
                }
                case SVGToken::MarkerMid:
                {
                    readLocalUrl(aContent, maMarkerMidXLink);
                    break;
                }
                case SVGToken::MarkerEnd:
                {
                    readLocalUrl(aContent, maMarkerEndXLink);
                    break;
                }
                case SVGToken::Display:
                {
                    // There may be display:none statements inside of style defines, e.g. the following line:
                    // style="display:none"
                    // taken from a svg example; this needs to be parsed and set at the owning node. Do not call
                    // mrOwner.parseAttribute(...) here, this would lead to a recursion
                    if(!aContent.isEmpty())
                    {
                        mrOwner.setDisplay(getDisplayFromContent(aContent));
                    }
                    break;
                }
                case SVGToken::BaselineShift:
                {
                    if(!aContent.isEmpty())
                    {
                        SvgNumber aNum;

                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"sub"))
                        {
                            setBaselineShift(BaselineShift::Sub);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"super"))
                        {
                            setBaselineShift(BaselineShift::Super);
                        }
                        else if(readSingleNumber(aContent, aNum))
                        {
                            maBaselineShiftNumber = aNum;

                            if(SvgUnit::percent == aNum.getUnit())
                            {
                                setBaselineShift(BaselineShift::Percentage);
                            }
                            else
                            {
                                setBaselineShift(BaselineShift::Length);
                            }
                        }
                        else
                        {
                            // no BaselineShift or inherit (which is automatically)
                            setBaselineShift(BaselineShift::Baseline);
                        }
                    }
                    break;
                }
                case SVGToken::DominantBaseline:
                {
                    if(!aContent.isEmpty())
                    {
                        if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"middle"))
                        {
                            setDominantBaseline(DominantBaseline::Middle);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"hanging"))
                        {
                            setDominantBaseline(DominantBaseline::Hanging);
                        }
                        else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"central"))
                        {
                            setDominantBaseline(DominantBaseline::Central);
                        }
                        else
                        {
                            // no DominantBaseline
                            setDominantBaseline(DominantBaseline::Auto);
                        }
                    }
                    break;
                }
                default:
                {
                    break;
                }
            }
        }

        bool SvgStyleAttributes::isClipPathContent() const
        {
            if (SVGToken::ClipPathNode == mrOwner.getType())
                return true;

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();
            if (pSvgStyleAttributes && maResolvingParent[31] < nStyleDepthLimit)
            {
                ++maResolvingParent[31];
                bool ret = pSvgStyleAttributes->isClipPathContent();
                --maResolvingParent[31];
                return ret;
            }

            return false;
        }

        // #i125258# ask if fill is a direct hard attribute (no hierarchy)
        bool SvgStyleAttributes::isFillSet() const
        {
            if(isClipPathContent())
            {
                return false;
            }
            else if(maFill.isSet())
            {
                return true;
            }

            return false;
        }

        const basegfx::BColor* SvgStyleAttributes::getCurrentColor() const
        {
            static basegfx::BColor aBlack(0.0, 0.0, 0.0);
            const basegfx::BColor *aColor = getColor();
            if( aColor )
                return aColor;
            else
                return &aBlack;
        }

        const basegfx::BColor* SvgStyleAttributes::getFill() const
        {
            if(maFill.isSet())
            {
                if(maFill.isCurrent())
                {
                    return getCurrentColor();
                }
                else if(maFill.isOn())
                {
                    return &maFill.getBColor();
                }
                else if(isClipPathContent())
                {
                    const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

                    if (pSvgStyleAttributes && maResolvingParent[0] < nStyleDepthLimit)
                    {
                        ++maResolvingParent[0];
                        const basegfx::BColor* pFill = pSvgStyleAttributes->getFill();
                        --maResolvingParent[0];

                        return pFill;
                    }
                }
            }
            else if (maNodeFillURL.isEmpty())
            {
                const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

                if (pSvgStyleAttributes && maResolvingParent[0] < nStyleDepthLimit)
                {
                    ++maResolvingParent[0];
                    const basegfx::BColor* pFill = pSvgStyleAttributes->getFill();
                    --maResolvingParent[0];

                    if(isClipPathContent())
                    {
                        if (pFill)
                        {
                            return pFill;
                        }
                        else
                        {
                            static basegfx::BColor aBlack(0.0, 0.0, 0.0);
                            return &aBlack;
                        }
                    }
                    else
                    {
                        return pFill;
                    }
                }
            }

            return nullptr;
        }

        const basegfx::BColor* SvgStyleAttributes::getStroke() const
        {
            if(maStroke.isSet())
            {
                if(maStroke.isCurrent())
                {
                    return getCurrentColor();
                }
                else if(maStroke.isOn())
                {
                    return &maStroke.getBColor();
                }
            }
            else if (maNodeStrokeURL.isEmpty())
            {
                const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

                if (pSvgStyleAttributes && maResolvingParent[1] < nStyleDepthLimit)
                {
                    ++maResolvingParent[1];
                    auto ret = pSvgStyleAttributes->getStroke();
                    --maResolvingParent[1];
                    return ret;
                }
            }

            return nullptr;
        }

        const basegfx::BColor& SvgStyleAttributes::getStopColor() const
        {
            if(maStopColor.isCurrent())
            {
                return *getCurrentColor();
            }
            else
            {
                return maStopColor.getBColor();
            }
        }

        const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeFill() const
        {
            if (!maFill.isSet())
            {
                if (!maNodeFillURL.isEmpty())
                {
                    const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(maNodeFillURL);

                    if(pNode)
                    {
                        if(SVGToken::LinearGradient == pNode->getType() || SVGToken::RadialGradient == pNode->getType())
                        {
                            return static_castconst SvgGradientNode* >(pNode);
                        }
                    }
                }
                const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

                if (pSvgStyleAttributes && maResolvingParent[2] < nStyleDepthLimit)
                {
                    ++maResolvingParent[2];
                    auto ret = pSvgStyleAttributes->getSvgGradientNodeFill();
                    --maResolvingParent[2];
                    return ret;
                }
            }

            return nullptr;
        }

        const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeStroke() const
        {
            if (!maStroke.isSet())
            {
                if(!maNodeStrokeURL.isEmpty())
                {
                    const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(maNodeStrokeURL);

                    if(pNode)
                    {
                        if(SVGToken::LinearGradient == pNode->getType() || SVGToken::RadialGradient  == pNode->getType())
                        {
                            return static_castconst SvgGradientNode* >(pNode);
                        }
                    }
                }

                const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

                if (pSvgStyleAttributes && maResolvingParent[3] < nStyleDepthLimit)
                {
                    ++maResolvingParent[3];
                    auto ret = pSvgStyleAttributes->getSvgGradientNodeStroke();
                    --maResolvingParent[3];
                    return ret;
                }
            }

            return nullptr;
        }

        const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeFill() const
        {
            if (!maFill.isSet())
            {
                if (!maNodeFillURL.isEmpty())
                {
                    const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(maNodeFillURL);

                    if(pNode)
                    {
                        if(SVGToken::Pattern == pNode->getType())
                        {
                            return static_castconst SvgPatternNode* >(pNode);
                        }
                    }
                }

                const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

                if (pSvgStyleAttributes && maResolvingParent[4] < nStyleDepthLimit)
                {
                    ++maResolvingParent[4];
                    auto ret = pSvgStyleAttributes->getSvgPatternNodeFill();
                    --maResolvingParent[4];
                    return ret;
                }
            }

            return nullptr;
        }

        const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeStroke() const
        {
            if (!maStroke.isSet())
            {
                if(!maNodeStrokeURL.isEmpty())
                {
                    const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(maNodeStrokeURL);

                    if(pNode)
                    {
                        if(SVGToken::Pattern == pNode->getType())
                        {
                            return static_castconst SvgPatternNode* >(pNode);
                        }
                    }
                }

                const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

                if (pSvgStyleAttributes && maResolvingParent[5] < nStyleDepthLimit)
                {
                    ++maResolvingParent[5];
                    auto ret = pSvgStyleAttributes->getSvgPatternNodeStroke();
                    --maResolvingParent[5];
                    return ret;
                }
            }

            return nullptr;
        }

        SvgNumber SvgStyleAttributes::getStrokeWidth() const
        {
            if(maStrokeWidth.isSet())
            {
                return maStrokeWidth;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[6] < nStyleDepthLimit)
            {
                ++maResolvingParent[6];
                auto ret = pSvgStyleAttributes->getStrokeWidth();
                --maResolvingParent[6];
                return ret;
            }

            if(isClipPathContent())
            {
                return SvgNumber(0.0);
            }

            // default is 1
            return SvgNumber(1.0);
        }

        SvgNumber SvgStyleAttributes::getStopOpacity() const
        {
            if(maStopOpacity.isSet())
            {
                return maStopOpacity;
            }

            // default is 1
            return SvgNumber(1.0);
        }

        SvgNumber SvgStyleAttributes::getFillOpacity() const
        {
            if(maFillOpacity.isSet())
            {
                return maFillOpacity;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[7] < nStyleDepthLimit)
            {
                ++maResolvingParent[7];
                auto ret = pSvgStyleAttributes->getFillOpacity();
                --maResolvingParent[7];
                return ret;
            }

            // default is 1
            return SvgNumber(1.0);
        }

        SvgNumber SvgStyleAttributes::getOpacity() const
        {
            if(maOpacity.isSet())
            {
                return maOpacity;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[33] < nStyleDepthLimit)
            {
                ++maResolvingParent[33];
                auto ret = pSvgStyleAttributes->getOpacity();
                --maResolvingParent[33];
                return ret;
            }

            // default is 1
            return SvgNumber(1.0);
        }

        Overflow SvgStyleAttributes::getOverflow() const
        {
            if(Overflow::notset != maOverflow)
            {
                return maOverflow;
            }

            if (const SvgStyleAttributes* pSvgStyleAttributes = getCssStyle())
            {
                return pSvgStyleAttributes->getOverflow();
            }

            return Overflow::hidden;
        }

        Visibility SvgStyleAttributes::getVisibility() const
        {
            if(Visibility::notset == maVisibility || Visibility::inherit == maVisibility)
            {
                const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

                if (pSvgStyleAttributes && maResolvingParent[9] < nStyleDepthLimit)
                {
                    ++maResolvingParent[9];
                    auto ret = pSvgStyleAttributes->getVisibility();
                    --maResolvingParent[9];
                    return ret;
                }
                //default is Visible
                return Visibility::visible;
            }

            // Visibility correction/exception for self-exported SVGs:
            // When Impress exports single or multi-page SVGs, it puts the
            // single slides into <g visibility="hidden">. Not sure why
            // this happens, but this leads (correctly) to empty imported
            // Graphics.
            // Thus, if Visibility::hidden is active and owner is a SVGToken::G
            // and it's parent is also a SVGToken::G and it has a Class 'SlideGroup'
            // set, check if we are an Impress export.
            // We are an Impress export if an SVG-Node titled 'ooo:meta_slides'
            // exists.
            // All together gives:
            if(Visibility::hidden == maVisibility
                && SVGToken::G == mrOwner.getType()
                && nullptr != mrOwner.getDocument().findSvgNodeById(u"ooo:meta_slides"_ustr))
            {
                const SvgNode* pParent(mrOwner.getParent());

                if(nullptr != pParent && SVGToken::G == pParent->getType() && pParent->getClass())
                {
                    const OUString aClass(*pParent->getClass());

                    if("SlideGroup" == aClass)
                    {
                        // if we detect this exception,
                        // override Visibility::hidden -> Visibility::visible
                        return Visibility::visible;
                    }
                }
            }

            return maVisibility;
        }

        FillRule SvgStyleAttributes::getFillRule() const
        {
            if(FillRule::notset != maFillRule)
            {
                return maFillRule;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[10] < nStyleDepthLimit)
            {
                ++maResolvingParent[10];
                auto ret = pSvgStyleAttributes->getFillRule();
                --maResolvingParent[10];
                return ret;
            }

            // default is NonZero
            return FillRule::nonzero;
        }

        FillRule SvgStyleAttributes::getClipRule() const
        {
            if(FillRule::notset != maClipRule)
            {
                return maClipRule;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[25] < nStyleDepthLimit)
            {
                ++maResolvingParent[25];
                auto ret = pSvgStyleAttributes->getClipRule();
                --maResolvingParent[25];
                return ret;
            }

            // default is NonZero
            return FillRule::nonzero;
        }

        const SvgNumberVector& SvgStyleAttributes::getStrokeDasharray() const
        {
            if(!maStrokeDasharray.empty())
            {
                return maStrokeDasharray;
            }
            else if(mbStrokeDasharraySet)
            {
                // #121221# is set to empty *by purpose*, do not visit parent styles
                return maStrokeDasharray;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[11] < nStyleDepthLimit)
            {
                ++maResolvingParent[11];
                const SvgNumberVector& ret = pSvgStyleAttributes->getStrokeDasharray();
                --maResolvingParent[11];
                return ret;
            }

            // default empty
            return maStrokeDasharray;
        }

        SvgNumber SvgStyleAttributes::getStrokeDashOffset() const
        {
            if(maStrokeDashOffset.isSet())
            {
                return maStrokeDashOffset;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[12] < nStyleDepthLimit)
            {
                ++maResolvingParent[12];
                auto ret = pSvgStyleAttributes->getStrokeDashOffset();
                --maResolvingParent[12];
                return ret;
            }

            // default is 0
            return SvgNumber(0.0);
        }

        StrokeLinecap SvgStyleAttributes::getStrokeLinecap() const
        {
            if(maStrokeLinecap != StrokeLinecap::notset)
            {
                return maStrokeLinecap;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[13] < nStyleDepthLimit)
            {
                ++maResolvingParent[13];
                auto ret = pSvgStyleAttributes->getStrokeLinecap();
                --maResolvingParent[13];
                return ret;
            }

            // default is StrokeLinecap::butt
            return StrokeLinecap::butt;
        }

        StrokeLinejoin SvgStyleAttributes::getStrokeLinejoin() const
        {
            if(maStrokeLinejoin != StrokeLinejoin::notset)
            {
                return maStrokeLinejoin;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[14] < nStyleDepthLimit)
            {
                ++maResolvingParent[14];
                auto ret = pSvgStyleAttributes->getStrokeLinejoin();
                --maResolvingParent[14];
                return ret;
            }

            // default is StrokeLinejoin::butt
            return StrokeLinejoin::miter;
        }

        SvgNumber SvgStyleAttributes::getStrokeMiterLimit() const
        {
            if(maStrokeMiterLimit.isSet())
            {
                return maStrokeMiterLimit;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[15] < nStyleDepthLimit)
            {
                ++maResolvingParent[15];
                auto ret = pSvgStyleAttributes->getStrokeMiterLimit();
                --maResolvingParent[15];
                return ret;
            }

            // default is 4
            return SvgNumber(4.0, SvgUnit::none);
        }

        SvgNumber SvgStyleAttributes::getStrokeOpacity() const
        {
            if(maStrokeOpacity.isSet())
            {
                return maStrokeOpacity;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[16] < nStyleDepthLimit)
            {
                ++maResolvingParent[16];
                auto ret = pSvgStyleAttributes->getStrokeOpacity();
                --maResolvingParent[16];
                return ret;
            }

            // default is 1
            return SvgNumber(1.0);
        }

        const SvgStringVector& SvgStyleAttributes::getFontFamily() const
        {
            if(!maFontFamily.empty() && !o3tl::equalsIgnoreAsciiCase(o3tl::trim(maFontFamily[0]), u"inherit"))
            {
                return maFontFamily;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[17] < nStyleDepthLimit)
            {
                ++maResolvingParent[17];
                const SvgStringVector& ret = pSvgStyleAttributes->getFontFamily();
                --maResolvingParent[17];
                return ret;
            }

            // default is empty
            return maFontFamily;
        }

        SvgNumber SvgStyleAttributes::getFontSizeNumber() const
        {
            // default size is 'medium', i.e. 12 pt, or 16px
            constexpr double aDefaultSize = o3tl::convert(12.0, o3tl::Length::pt, o3tl::Length::px);

            if(maFontSizeNumber.isSet())
            {
                if(!maFontSizeNumber.isPositive())
                    return aDefaultSize;

                // #122524# Handle SvgUnit::percent relative to parent FontSize (see SVG1.1
                // spec 10.10 Font selection properties \91font-size\92, lastline (click 'normative
                // definition of the property')
                if(SvgUnit::percent == maFontSizeNumber.getUnit())
                {
                    const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

                    if(pSvgStyleAttributes)
                    {
                        const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber();

                        return SvgNumber(
                            aParentNumber.getNumber() * maFontSizeNumber.getNumber() * 0.01,
                            aParentNumber.getUnit(),
                            true);
                    }
                    // if there's no parent style, set the font size based on the default size
                    // 100% = 16px
                    return SvgNumber(
                        maFontSizeNumber.getNumber() * aDefaultSize / 100.0, SvgUnit::px, true);
                }
                else if((SvgUnit::em == maFontSizeNumber.getUnit()) || (SvgUnit::ex == maFontSizeNumber.getUnit()))
                {
                    const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

                    if(pSvgStyleAttributes)
                    {
                        const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber();
                        double n = aParentNumber.getNumber() * maFontSizeNumber.getNumber();
                        if (SvgUnit::ex == maFontSizeNumber.getUnit())
                            n *= 0.5; // FIXME: use "x-height of the first available font"

                        return SvgNumber(n, aParentNumber.getUnit());
                    }
                }

                return maFontSizeNumber;
            }

            //In CSS2, the suggested scaling factor between adjacent indexes is 1.2
            switch(maFontSize)
            {
                case FontSize::notset:
                    break;
                case FontSize::xx_small:
                {
                    return SvgNumber(aDefaultSize / 1.728);
                }
                case FontSize::x_small:
                {
                    return SvgNumber(aDefaultSize / 1.44);
                }
                case FontSize::small:
                {
                    return SvgNumber(aDefaultSize / 1.2);
                }
                case FontSize::smaller:
                {
                    const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();
                    if(pSvgStyleAttributes)
                    {
                        const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber();
                        return SvgNumber(aParentNumber.getNumber() / 1.2, aParentNumber.getUnit());
                    }
                    [[fallthrough]];
                }
                case FontSize::medium:
                case FontSize::initial:
                {
                    return SvgNumber(aDefaultSize);
                }
                case FontSize::large:
                {
                    return SvgNumber(aDefaultSize * 1.2);
                }
                case FontSize::larger:
                {
                    const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();
                    if(pSvgStyleAttributes)
                    {
                        const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber();
                        return SvgNumber(aParentNumber.getNumber() * 1.2, aParentNumber.getUnit());
                    }
                    [[fallthrough]];
                }
                case FontSize::x_large:
                {
                    return SvgNumber(aDefaultSize * 1.44);
                }
                case FontSize::xx_large:
                {
                    return SvgNumber(aDefaultSize * 1.728);
                }
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if(pSvgStyleAttributes)
            {
                return pSvgStyleAttributes->getFontSizeNumber();
            }

            return SvgNumber(aDefaultSize);
        }

        FontStretch SvgStyleAttributes::getFontStretch() const
        {
            if(maFontStretch != FontStretch::notset)
            {
                if(FontStretch::wider != maFontStretch && FontStretch::narrower != maFontStretch)
                {
                    return maFontStretch;
                }
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[18] < nStyleDepthLimit)
            {
                ++maResolvingParent[18];
                FontStretch aInherited = pSvgStyleAttributes->getFontStretch();
                --maResolvingParent[18];

                if(FontStretch::wider == maFontStretch)
                {
                    aInherited = getWider(aInherited);
                }
                else if(FontStretch::narrower == maFontStretch)
                {
                    aInherited = getNarrower(aInherited);
                }

                return aInherited;
            }

            // default is FontStretch::normal
            return FontStretch::normal;
        }

        FontStyle SvgStyleAttributes::getFontStyle() const
        {
            if(maFontStyle != FontStyle::notset)
            {
                return maFontStyle;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[19] < nStyleDepthLimit)
            {
                ++maResolvingParent[19];
                auto ret = pSvgStyleAttributes->getFontStyle();
                --maResolvingParent[19];
                return ret;
            }

            // default is FontStyle::normal
            return FontStyle::normal;
        }

        FontWeight SvgStyleAttributes::getFontWeight() const
        {
            if(maFontWeight != FontWeight::notset)
            {
                if(FontWeight::bolder != maFontWeight && FontWeight::lighter != maFontWeight)
                {
                    return maFontWeight;
                }
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[20] < nStyleDepthLimit)
            {
                ++maResolvingParent[20];
                FontWeight aInherited = pSvgStyleAttributes->getFontWeight();
                --maResolvingParent[20];

                if(FontWeight::bolder == maFontWeight)
                {
                    aInherited = getBolder(aInherited);
                }
                else if(FontWeight::lighter == maFontWeight)
                {
                    aInherited = getLighter(aInherited);
                }

                return aInherited;
            }

            // default is FontWeight::N400 (FontWeight::normal)
            return FontWeight::N400;
        }

        FontDirection SvgStyleAttributes::getFontDirection() const
        {
            if(maFontDirection != FontDirection::notset)
            {
                return maFontDirection;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();
            if (pSvgStyleAttributes && maResolvingParent[34] < nStyleDepthLimit)
            {
                ++maResolvingParent[34];
                auto ret = pSvgStyleAttributes->getFontDirection();
                --maResolvingParent[34];

                return ret;
            }

            // default is LTR
            return FontDirection::LTR;
        }

        UnicodeBidi SvgStyleAttributes::getUnicodeBidi() const
        {
            if(maUnicodeBidi != UnicodeBidi::notset)
            {
                return maUnicodeBidi;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();
            if (pSvgStyleAttributes && maResolvingParent[35] < nStyleDepthLimit)
            {
                ++maResolvingParent[35];
                auto ret = pSvgStyleAttributes->getUnicodeBidi();
                --maResolvingParent[35];

                return ret;
            }

            // default is normal
            return UnicodeBidi::normal;
        }

        TextAlign SvgStyleAttributes::getTextAlign() const
        {
            if(maTextAlign != TextAlign::notset)
            {
                return maTextAlign;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[21] < nStyleDepthLimit)
            {
                ++maResolvingParent[21];
                auto ret = pSvgStyleAttributes->getTextAlign();
                --maResolvingParent[21];
                return ret;
            }

            // default is TextAlign::left
            return TextAlign::left;
        }

        const SvgStyleAttributes* SvgStyleAttributes::getTextDecorationDefiningSvgStyleAttributes() const
        {
            if(maTextDecoration != TextDecoration::notset)
            {
                return this;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[22] < nStyleDepthLimit)
            {
                ++maResolvingParent[22];
                auto ret = pSvgStyleAttributes->getTextDecorationDefiningSvgStyleAttributes();
                --maResolvingParent[22];
                return ret;
            }

            // default is 0
            return nullptr;
        }

        TextDecoration SvgStyleAttributes::getTextDecoration() const
        {
            const SvgStyleAttributes* pDefining = getTextDecorationDefiningSvgStyleAttributes();

            if(pDefining)
            {
                return pDefining->maTextDecoration;
            }
            else
            {
                // default is TextDecoration::none
                return TextDecoration::none;
            }
        }

        TextAnchor SvgStyleAttributes::getTextAnchor() const
        {
            if(maTextAnchor != TextAnchor::notset)
            {
                return maTextAnchor;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[23] < nStyleDepthLimit)
            {
                ++maResolvingParent[23];
                auto ret = pSvgStyleAttributes->getTextAnchor();
                --maResolvingParent[23];
                return ret;
            }

            // default is TextAnchor::start
            return TextAnchor::start;
        }

        const basegfx::BColor* SvgStyleAttributes::getColor() const
        {
            if(maColor.isSet())
            {
                if(maColor.isCurrent())
                {
                    OSL_ENSURE(false"Svg error: current color uses current color (!)");
                    return nullptr;
                }
                else if(maColor.isOn())
                {
                    return &maColor.getBColor();
                }
            }
            else
            {
                const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

                if (pSvgStyleAttributes && maResolvingParent[24] < nStyleDepthLimit)
                {
                    ++maResolvingParent[24];
                    auto ret = pSvgStyleAttributes->getColor();
                    --maResolvingParent[24];
                    return ret;
                }
            }

            return nullptr;
        }

        OUString SvgStyleAttributes::getClipPathXLink() const
        {
            if(!maClipPathXLink.isEmpty())
            {
                return maClipPathXLink;
            }

            // This is called from add_postProcess so only check if it has a css style
            if (const SvgStyleAttributes* pSvgStyleAttributes = getCssStyle())
            {
                return pSvgStyleAttributes->maClipPathXLink;
            }

            return OUString();
        }

        const SvgClipPathNode* SvgStyleAttributes::accessClipPathXLink() const
        {
            const OUString aClipPath(getClipPathXLink());

            if(!aClipPath.isEmpty())
            {
                return dynamic_castconst SvgClipPathNode* >(mrOwner.getDocument().findSvgNodeById(aClipPath));
            }
            return nullptr;
        }

        OUString SvgStyleAttributes::getFilterXLink() const
        {
            if(!maFilterXLink.isEmpty())
            {
                return maFilterXLink;
            }

            // This is called from add_postProcess so only check if it has a css style
            if (const SvgStyleAttributes* pSvgStyleAttributes = getCssStyle())
            {
                return pSvgStyleAttributes->maFilterXLink;
            }

            return OUString();
        }

        const SvgFilterNode* SvgStyleAttributes::accessFilterXLink() const
        {
            const OUString aFilter(getFilterXLink());

            if(!aFilter.isEmpty())
            {
                return dynamic_castconst SvgFilterNode* >(mrOwner.getDocument().findSvgNodeById(aFilter));
            }
            return nullptr;
        }

        OUString SvgStyleAttributes::getMaskXLink() const
        {
            if(!maMaskXLink.isEmpty())
            {
                return maMaskXLink;
            }

            // This is called from add_postProcess so only check if it has a css style
            if (const SvgStyleAttributes* pSvgStyleAttributes = getCssStyle())
            {
                return pSvgStyleAttributes->maMaskXLink;
            }

            return OUString();
        }

        const SvgMaskNode* SvgStyleAttributes::accessMaskXLink() const
        {
            const OUString aMask(getMaskXLink());

            if(!aMask.isEmpty())
            {
                return dynamic_castconst SvgMaskNode* >(mrOwner.getDocument().findSvgNodeById(aMask));
            }
            return nullptr;
        }

        OUString SvgStyleAttributes::getMarkerStartXLink() const
        {
            if(!maMarkerStartXLink.isEmpty())
            {
                return maMarkerStartXLink;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[26] < nStyleDepthLimit)
            {
                ++maResolvingParent[26];
                auto ret = pSvgStyleAttributes->getMarkerStartXLink();
                --maResolvingParent[26];
                return ret;
            }

            return OUString();
        }

        const SvgMarkerNode* SvgStyleAttributes::accessMarkerStartXLink() const
        {
            const OUString aMarker(getMarkerStartXLink());

            if(!aMarker.isEmpty())
            {
                return dynamic_castconst SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerStartXLink()));
            }
            return nullptr;
        }

        OUString SvgStyleAttributes::getMarkerMidXLink() const
        {
            if(!maMarkerMidXLink.isEmpty())
            {
                return maMarkerMidXLink;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[27] < nStyleDepthLimit)
            {
                ++maResolvingParent[27];
                auto ret = pSvgStyleAttributes->getMarkerMidXLink();
                --maResolvingParent[27];
                return ret;
            }

            return OUString();
        }

        const SvgMarkerNode* SvgStyleAttributes::accessMarkerMidXLink() const
        {
            const OUString aMarker(getMarkerMidXLink());

            if(!aMarker.isEmpty())
            {
                return dynamic_castconst SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerMidXLink()));
            }
            return nullptr;
        }

        OUString SvgStyleAttributes::getMarkerEndXLink() const
        {
            if(!maMarkerEndXLink.isEmpty())
            {
                return maMarkerEndXLink;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[28] < nStyleDepthLimit)
            {
                ++maResolvingParent[28];
                auto ret = pSvgStyleAttributes->getMarkerEndXLink();
                --maResolvingParent[28];
                return ret;
            }

            return OUString();
        }

        const SvgMarkerNode* SvgStyleAttributes::accessMarkerEndXLink() const
        {
            const OUString aMarker(getMarkerEndXLink());

            if(!aMarker.isEmpty())
            {
                return dynamic_castconst SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerEndXLink()));
            }
            return nullptr;
        }

        SvgNumber SvgStyleAttributes::getBaselineShiftNumber() const
        {
            // #122524# Handle SvgUnit::percent relative to parent BaselineShift
            if(SvgUnit::percent == maBaselineShiftNumber.getUnit())
            {
                const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

                if (pSvgStyleAttributes && maResolvingParent[8] < nStyleDepthLimit)
                {
                    ++maResolvingParent[8];
                    const SvgNumber aParentNumber = pSvgStyleAttributes->getBaselineShiftNumber();
                    --maResolvingParent[8];

                    return SvgNumber(
                        aParentNumber.getNumber() * maBaselineShiftNumber.getNumber() * 0.01,
                        aParentNumber.getUnit(),
                        true);
                }
            }

            return maBaselineShiftNumber;
        }

        BaselineShift SvgStyleAttributes::getBaselineShift() const
        {
            if(maBaselineShift != BaselineShift::Baseline)
            {
                return maBaselineShift;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[29] < nStyleDepthLimit)
            {
                ++maResolvingParent[29];
                auto ret = pSvgStyleAttributes->getBaselineShift();
                --maResolvingParent[29];
                return ret;
            }

            return BaselineShift::Baseline;
        }

        DominantBaseline SvgStyleAttributes::getDominantBaseline() const
        {
            if(maDominantBaseline != DominantBaseline::Auto)
            {
                return maDominantBaseline;
            }

            const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle();

            if (pSvgStyleAttributes && maResolvingParent[30] < nStyleDepthLimit)
            {
                ++maResolvingParent[30];
                auto ret = pSvgStyleAttributes->getDominantBaseline();
                --maResolvingParent[30];
                return ret;
            }

            return DominantBaseline::Auto;
        }
// end of namespace svgio

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */


Messung V0.5 in Prozent
C=94 H=100 G=96

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.70Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-05-04) ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.