    /* -*- 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 <drawingml/fillproperties.hxx>

    #include <iterator>

    #include <drawingml/graphicproperties.hxx>

    #include <com/sun/star/lang/XMultiServiceFactory.hpp>
    #include <com/sun/star/beans/XPropertySet.hpp>
    #include <com/sun/star/awt/Gradient.hpp>
    #include <com/sun/star/text/GraphicCrop.hpp>
    #include <com/sun/star/awt/Size.hpp>
    #include <com/sun/star/awt/XBitmap.hpp>
    #include <com/sun/star/drawing/BitmapMode.hpp>
    #include <com/sun/star/drawing/ColorMode.hpp>
    #include <com/sun/star/drawing/FillStyle.hpp>
    #include <com/sun/star/drawing/Hatch.hpp>
    #include <com/sun/star/drawing/RectanglePoint.hpp>
    #include <com/sun/star/graphic/XGraphicTransformer.hpp>
    #include <oox/core/fragmenthandler.hxx>
    #include <oox/helper/graphichelper.hxx>
    #include <oox/drawingml/drawingmltypes.hxx>
    #include <oox/drawingml/shapepropertymap.hxx>
    #include <oox/token/namespaces.hxx>
    #include <oox/token/properties.hxx>
    #include <oox/token/tokens.hxx>
    #include <rtl/math.hxx>
    #include <osl/diagnose.h>
    #include <sal/log.hxx>

    using namespace ::com::sun::star;
    using namespace ::com::sun::star::drawing;
    using namespace ::com::sun::star::graphic;

    using ::com::sun::star::uno::Reference;
    using ::com::sun::star::uno::Exception;
    using ::com::sun::star::uno::UNO_QUERY_THROW;
    using ::com::sun::star::geometry::IntegerRectangle2D;

    namespace oox {
    namespace drawingml {

    namespace {

    Reference< XGraphic > lclCheckAndApplyDuotoneTransform(const BlipFillProperties& aBlipProps, uno::Reference<graphic::XGraphic> const & xGraphic,
                                                           const GraphicHelper& rGraphicHelper, const ::Color nPhClr)
    {
        if (aBlipProps.maDuotoneColors[0].isUsed() && aBlipProps.maDuotoneColors[1].isUsed())
        {
            ::Color nColor1 = aBlipProps.maDuotoneColors[0].getColor( rGraphicHelper, nPhClr );
            ::Color nColor2 = aBlipProps.maDuotoneColors[1].getColor( rGraphicHelper, nPhClr );

            uno::Reference<graphic::XGraphicTransformer> xTransformer(aBlipProps.mxFillGraphic, uno::UNO_QUERY);
            if (xTransformer.is())
                return xTransformer->applyDuotone(xGraphic, sal_Int32(nColor1), sal_Int32(nColor2));
        }
        return xGraphic;
    }

    Reference< XGraphic > lclCheckAndApplyChangeColorTransform(const BlipFillProperties &aBlipProps, uno::Reference<graphic::XGraphic> const & xGraphic,
                                                               const GraphicHelper& rGraphicHelper, const ::Color nPhClr)
    {
        if( aBlipProps.maColorChangeFrom.isUsed() && aBlipProps.maColorChangeTo.isUsed() )
        {
            ::Color nFromColor = aBlipProps.maColorChangeFrom.getColor( rGraphicHelper, nPhClr );
            ::Color nToColor = aBlipProps.maColorChangeTo.getColor( rGraphicHelper, nPhClr );
            if ( (nFromColor != nToColor) || aBlipProps.maColorChangeTo.hasTransparency() )
            {
                sal_Int16 nToTransparence = aBlipProps.maColorChangeTo.getTransparency();
                sal_Int8 nToAlpha = static_cast< sal_Int8 >( (100 - nToTransparence) * 2.55 );

                uno::Reference<graphic::XGraphicTransformer> xTransformer(aBlipProps.mxFillGraphic, uno::UNO_QUERY);
                if (xTransformer.is())
                    return xTransformer->colorChange(xGraphic, sal_Int32(nFromColor), 9, sal_Int32(nToColor), nToAlpha);
            }
        }
        return xGraphic;
    }

    uno::Reference<graphic::XGraphic> applyBrightnessContrast(uno::Reference<graphic::XGraphic> const & xGraphic, sal_Int32 brightness, sal_Int32 contrast)
    {
        uno::Reference<graphic::XGraphicTransformer> xTransformer(xGraphic, uno::UNO_QUERY);
        if (xTransformer.is())
            return xTransformer->applyBrightnessContrast(xGraphic, brightness, contrast, true);
        return xGraphic;
    }

    BitmapMode lclGetBitmapMode( sal_Int32 nToken )
    {
        OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
        switch( nToken )
        {
            case XML_tile:      return BitmapMode_REPEAT;
            case XML_stretch:   return BitmapMode_STRETCH;
        }
        return BitmapMode_NO_REPEAT;
    }

    RectanglePoint lclGetRectanglePoint( sal_Int32 nToken )
    {
        OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
        switch( nToken )
        {
            case XML_tl:    return RectanglePoint_LEFT_TOP;
            case XML_t:     return RectanglePoint_MIDDLE_TOP;
            case XML_tr:    return RectanglePoint_RIGHT_TOP;
            case XML_l:     return RectanglePoint_LEFT_MIDDLE;
            case XML_ctr:   return RectanglePoint_MIDDLE_MIDDLE;
            case XML_r:     return RectanglePoint_RIGHT_MIDDLE;
            case XML_bl:    return RectanglePoint_LEFT_BOTTOM;
            case XML_b:     return RectanglePoint_MIDDLE_BOTTOM;
            case XML_br:    return RectanglePoint_RIGHT_BOTTOM;
        }
        return RectanglePoint_LEFT_TOP;
    }

    const awt::Size lclGetOriginalSize( const GraphicHelper& rGraphicHelper, const Reference< XGraphic >& rxGraphic )
    {
        awt::Size aSizeHmm( 0, 0 );
        try
        {
            Reference< beans::XPropertySet > xGraphicPropertySet( rxGraphic, UNO_QUERY_THROW );
            if( xGraphicPropertySet->getPropertyValue( "Size100thMM" ) >>= aSizeHmm )
            {
                if( !aSizeHmm.Width && !aSizeHmm.Height )
                {   // MAPMODE_PIXEL USED :-(
                    awt::Size aSourceSizePixel( 0, 0 );
                    if( xGraphicPropertySet->getPropertyValue( "SizePixel" ) >>= aSourceSizePixel )
                        aSizeHmm = rGraphicHelper.convertScreenPixelToHmm( aSourceSizePixel );
                }
            }
        }
        catch( Exception& )
        {
        }
        return aSizeHmm;
    }

    } // namespace

    void GradientFillProperties::assignUsed( const GradientFillProperties& rSourceProps )
    {
        if( !rSourceProps.maGradientStops.empty() )
            maGradientStops = rSourceProps.maGradientStops;
        moFillToRect.assignIfUsed( rSourceProps.moFillToRect );
        moTileRect.assignIfUsed( rSourceProps.moTileRect );
        moGradientPath.assignIfUsed( rSourceProps.moGradientPath );
        moShadeAngle.assignIfUsed( rSourceProps.moShadeAngle );
        moShadeFlip.assignIfUsed( rSourceProps.moShadeFlip );
        moShadeScaled.assignIfUsed( rSourceProps.moShadeScaled );
        moRotateWithShape.assignIfUsed( rSourceProps.moRotateWithShape );
    }

    void PatternFillProperties::assignUsed( const PatternFillProperties& rSourceProps )
    {
        maPattFgColor.assignIfUsed( rSourceProps.maPattFgColor );
        maPattBgColor.assignIfUsed( rSourceProps.maPattBgColor );
        moPattPreset.assignIfUsed( rSourceProps.moPattPreset );
    }

    void BlipFillProperties::assignUsed( const BlipFillProperties& rSourceProps )
    {
        if(rSourceProps.mxFillGraphic.is())
            mxFillGraphic = rSourceProps.mxFillGraphic;
        moBitmapMode.assignIfUsed( rSourceProps.moBitmapMode );
        moFillRect.assignIfUsed( rSourceProps.moFillRect );
        moTileOffsetX.assignIfUsed( rSourceProps.moTileOffsetX );
        moTileOffsetY.assignIfUsed( rSourceProps.moTileOffsetY );
        moTileScaleX.assignIfUsed( rSourceProps.moTileScaleX );
        moTileScaleY.assignIfUsed( rSourceProps.moTileScaleY );
        moTileAlign.assignIfUsed( rSourceProps.moTileAlign );
        moTileFlip.assignIfUsed( rSourceProps.moTileFlip );
        moRotateWithShape.assignIfUsed( rSourceProps.moRotateWithShape );
        moColorEffect.assignIfUsed( rSourceProps.moColorEffect );
        moBrightness.assignIfUsed( rSourceProps.moBrightness );
        moContrast.assignIfUsed( rSourceProps.moContrast );
        maColorChangeFrom.assignIfUsed( rSourceProps.maColorChangeFrom );
        maColorChangeTo.assignIfUsed( rSourceProps.maColorChangeTo );
        maDuotoneColors[0].assignIfUsed( rSourceProps.maDuotoneColors[0] );
        maDuotoneColors[1].assignIfUsed( rSourceProps.maDuotoneColors[1] );
        maEffect.assignUsed( rSourceProps.maEffect );
        moAlphaModFix.assignIfUsed(rSourceProps.moAlphaModFix);
    }

    void FillProperties::assignUsed( const FillProperties& rSourceProps )
    {
        moFillType.assignIfUsed( rSourceProps.moFillType );
        maFillColor.assignIfUsed( rSourceProps.maFillColor );
        maGradientProps.assignUsed( rSourceProps.maGradientProps );
        maPatternProps.assignUsed( rSourceProps.maPatternProps );
        maBlipProps.assignUsed( rSourceProps.maBlipProps );
    }

    Color FillProperties::getBestSolidColor() const
    {
        Color aSolidColor;
        if( moFillType.has() ) switch( moFillType.get() )
        {
            case XML_solidFill:
                aSolidColor = maFillColor;
            break;
            case XML_gradFill:
                if( !maGradientProps.maGradientStops.empty() )
                {
                    GradientFillProperties::GradientStopMap::const_iterator aGradientStop =
                        maGradientProps.maGradientStops.begin();
                    if (maGradientProps.maGradientStops.size() > 2)
                        ++aGradientStop;
                    aSolidColor = aGradientStop->second;
                }
            break;
            case XML_pattFill:
                aSolidColor = maPatternProps.maPattBgColor.isUsed() ? maPatternProps.maPattBgColor : maPatternProps.maPattFgColor;
            break;
        }
        return aSolidColor;
    }

    /// Maps the hatch token to drawing::Hatch.
    static drawing::Hatch createHatch( sal_Int32 nHatchToken, ::Color nColor )
    {
        drawing::Hatch aHatch;
        aHatch.Color = sal_Int32(nColor);

        // best-effort mapping; we do not support all the styles in core
        switch ( nHatchToken )
        {
            case XML_pct5:       aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 250; aHatch.Angle = 450;  break;
            case XML_pct10:      aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 200; aHatch.Angle = 450;  break;
            case XML_pct20:      aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 150; aHatch.Angle = 450;  break;
            case XML_pct25:      aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 200; aHatch.Angle = 450;  break;
            case XML_pct30:      aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 175; aHatch.Angle = 450;  break;
            case XML_pct40:      aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 150; aHatch.Angle = 450;  break;
            case XML_pct50:      aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 125; aHatch.Angle = 450;  break;
            case XML_pct60:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 150; aHatch.Angle = 450;  break;
            case XML_pct70:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 125; aHatch.Angle = 450;  break;
            case XML_pct75:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
            case XML_pct80:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 75;  aHatch.Angle = 450;  break;
            case XML_pct90:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 50;  aHatch.Angle = 450;  break;
            case XML_horz:       aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 0;    break;
            case XML_vert:       aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 900;  break;
            case XML_ltHorz:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 0;    break;
            case XML_ltVert:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 900;  break;
            case XML_dkHorz:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 25;  aHatch.Angle = 0;    break;
            case XML_dkVert:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 25;  aHatch.Angle = 900;  break;
            case XML_narHorz:    aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 0;    break;
            case XML_narVert:    aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 900;  break;
            case XML_dashHorz:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 150; aHatch.Angle = 0;    break;
            case XML_dashVert:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 150; aHatch.Angle = 900;  break;
            case XML_cross:      aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 100; aHatch.Angle = 0;    break;
            case XML_dnDiag:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 1350; break;
            case XML_upDiag:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
            case XML_ltDnDiag:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 1350; break;
            case XML_ltUpDiag:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 450;  break;
            case XML_dkDnDiag:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 1350; break;
            case XML_dkUpDiag:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 450;  break;
            case XML_wdDnDiag:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 1350; break;
            case XML_wdUpDiag:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
            case XML_dashDnDiag: aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 150; aHatch.Angle = 1350; break;
            case XML_dashUpDiag: aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 150; aHatch.Angle = 450;  break;
            case XML_diagCross:  aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
            case XML_smCheck:    aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 50;  aHatch.Angle = 450;  break;
            case XML_lgCheck:    aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
            case XML_smGrid:     aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 50;  aHatch.Angle = 0;    break;
            case XML_lgGrid:     aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 100; aHatch.Angle = 0;    break;
            case XML_dotGrid:    aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 400; aHatch.Angle = 0;    break;
            case XML_smConfetti: aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 200; aHatch.Angle = 600;  break;
            case XML_lgConfetti: aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 600;  break;
            case XML_horzBrick:  aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 300; aHatch.Angle = 0;    break;
            case XML_diagBrick:  aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 300; aHatch.Angle = 450;  break;
            case XML_solidDmnd:  aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
            case XML_openDmnd:   aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
            case XML_dotDmnd:    aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 300; aHatch.Angle = 450;  break;
            case XML_plaid:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 200; aHatch.Angle = 900;  break;
            case XML_sphere:     aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 100; aHatch.Angle = 0;    break;
            case XML_weave:      aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 150; aHatch.Angle = 450;  break;
            case XML_divot:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 400; aHatch.Angle = 450;  break;
            case XML_shingle:    aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 200; aHatch.Angle = 1350; break;
            case XML_wave:       aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 0;    break;
            case XML_trellis:    aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 75;  aHatch.Angle = 450;  break;
            case XML_zigZag:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 75;  aHatch.Angle = 0;    break;
        }

        return aHatch;
    }

    void FillProperties::pushToPropMap( ShapePropertyMap& rPropMap,
            const GraphicHelper& rGraphicHelper, sal_Int32 nShapeRotation, ::Color nPhClr,
            bool bFlipH, bool bFlipV ) const
    {
        if( moFillType.has() )
        {
            FillStyle eFillStyle = FillStyle_NONE;
            OSL_ASSERT((moFillType.get() & sal_Int32(0xFFFF0000))==0);
            switch( moFillType.get() )
            {
                case XML_noFill:
                    eFillStyle = FillStyle_NONE;
                break;

                case XML_solidFill:
                    if( maFillColor.isUsed() )
                    {
                        rPropMap.setProperty( ShapeProperty::FillColor, maFillColor.getColor( rGraphicHelper, nPhClr ) );
                        if( maFillColor.hasTransparency() )
                            rPropMap.setProperty( ShapeProperty::FillTransparency, maFillColor.getTransparency() );
                        eFillStyle = FillStyle_SOLID;
                    }
                break;

                case XML_gradFill:
                    // do not create gradient struct if property is not supported...
                    if( rPropMap.supportsProperty( ShapeProperty::FillGradient ) )
                    {
                        sal_Int32 nEndTrans     = 0;
                        sal_Int32 nStartTrans   = 0;
                        awt::Gradient aGradient;
                        aGradient.Angle = 900;
                        aGradient.StartIntensity = 100;
                        aGradient.EndIntensity = 100;

                        // Old code, values in aGradient overwritten in many cases by newer code below
                        if( maGradientProps.maGradientStops.size() > 1 )
                        {
                            aGradient.StartColor = sal_Int32(maGradientProps.maGradientStops.begin()->second.getColor( rGraphicHelper, nPhClr ));
                            aGradient.EndColor = sal_Int32(maGradientProps.maGradientStops.rbegin()->second.getColor( rGraphicHelper, nPhClr ));
                            if( maGradientProps.maGradientStops.rbegin()->second.hasTransparency() )
                                nEndTrans = maGradientProps.maGradientStops.rbegin()->second.getTransparency()*255/100;
                            if( maGradientProps.maGradientStops.begin()->second.hasTransparency() )
                                nStartTrans = maGradientProps.maGradientStops.begin()->second.getTransparency()*255/100;
                        }

                        // "rotate with shape" set to false -> do not rotate
                        if ( !maGradientProps.moRotateWithShape.get( true ) )
                            nShapeRotation = 0;

                        if( maGradientProps.moGradientPath.has() )
                        {
                            aGradient.Style = (maGradientProps.moGradientPath.get() == XML_circle) ? awt::GradientStyle_ELLIPTICAL : awt::GradientStyle_RECT;
                            // position of gradient center (limited to [30%;70%], otherwise gradient is too hidden)
                            IntegerRectangle2D aFillToRect = maGradientProps.moFillToRect.get( IntegerRectangle2D( 0, 0, MAX_PERCENT, MAX_PERCENT ) );
                            sal_Int32 nCenterX = (MAX_PERCENT + aFillToRect.X1 - aFillToRect.X2) / 2;
                            aGradient.XOffset = getLimitedValue< sal_Int16, sal_Int32 >( nCenterX / PER_PERCENT, 30, 70 );
                            sal_Int32 nCenterY = (MAX_PERCENT + aFillToRect.Y1 - aFillToRect.Y2) / 2;
                            aGradient.YOffset = getLimitedValue< sal_Int16, sal_Int32 >( nCenterY / PER_PERCENT, 30, 70 );
                            ::std::swap( aGradient.StartColor, aGradient.EndColor );
                            ::std::swap( nStartTrans, nEndTrans );
                        }
                        else if (!maGradientProps.maGradientStops.empty())
                        {
                            // A copy of the gradient stops for local modification
                            GradientFillProperties::GradientStopMap aGradientStops(maGradientProps.maGradientStops);

                            // Add a fake gradient stop at 0% and 100% if necessary, so that the gradient always starts
                            // at 0% and ends at 100%, to make following logic clearer (?).
                            auto a0 = aGradientStops.find( 0.0 );
                            if( a0 == aGradientStops.end() )
                            {
                                // temp variable required
                                Color aFirstColor(aGradientStops.begin()->second);
                                aGradientStops.emplace( 0.0, aFirstColor );
                            }

                            auto a1 = aGradientStops.find( 1.0 );
                            if( a1 == aGradientStops.end() )
                            {
                                // ditto
                                Color aLastColor(aGradientStops.rbegin()->second);
                                aGradientStops.emplace( 1.0, aLastColor );
                            }

                            // Check if the gradient is symmetric, which we will emulate with an "axial" gradient.
                            bool bSymmetric(true);
                            {
                                GradientFillProperties::GradientStopMap::const_iterator aItA( aGradientStops.begin() );
                                GradientFillProperties::GradientStopMap::const_iterator aItZ(std::prev(aGradientStops.end()));
                                while( bSymmetric && aItA->first < aItZ->first )
                                {
                                    if( aItA->second.getColor( rGraphicHelper, nPhClr ) != aItZ->second.getColor( rGraphicHelper, nPhClr ) ||
                                        aItA->second.getTransparency() != aItZ->second.getTransparency() )
                                        bSymmetric = false;
                                    else
                                    {
                                        ++aItA;
                                        aItZ = std::prev(aItZ);
                                    }
                                }
                                // Don't be fooled if the middlemost stop isn't at 0.5.
                                if( bSymmetric && aItA == aItZ && aItA->first != 0.5 )
                                    bSymmetric = false;

                                // If symmetric, do the rest of the logic for just a half.
                                if( bSymmetric )
                                {
                                    // aItZ already points to the colour for the middle, but insert a fake stop at the
                                    // exact middle if necessary.
                                    if( aItA->first != aItZ->first )
                                    {
                                        Color aMiddleColor = aItZ->second;
                                        auto a05 = aGradientStops.find( 0.5 );

                                        if( a05 != aGradientStops.end() )
                                            a05->second = aMiddleColor;
                                        else
                                            aGradientStops.emplace( 0.5, aMiddleColor );
                                    }
                                    // Drop the rest of the stops
                                    while( aGradientStops.rbegin()->first > 0.5 )
                                        aGradientStops.erase( aGradientStops.rbegin()->first );
                                }
                            }

                            SAL_INFO("oox.drawingml.gradient", "symmetric: " << (bSymmetric ? "YES" : "NO") <<
                                     ", number of stops: " << aGradientStops.size());
                            size_t nIndex = 0;
                            for (auto const& gradientStop : aGradientStops)
                                SAL_INFO("oox.drawingml.gradient", "  " << nIndex++ << ": " <<
                                         gradientStop.first << ": " <<
                                         std::hex << sal_Int32(gradientStop.second.getColor( rGraphicHelper, nPhClr )) << std::dec <<
                                         "@" << (100 - gradientStop.second.getTransparency()) << "%");

                            // Now estimate the simple LO style gradient (only two stops, at n% and 100%, where n ==
                            // the "border") that best emulates the gradient between begin() and prior(end()).

                            // First look for the largest segment in the gradient.
                            GradientFillProperties::GradientStopMap::iterator aIt(aGradientStops.begin());
                            double nWidestWidth = -1;
                            GradientFillProperties::GradientStopMap::iterator aWidestSegmentStart;
                            ++aIt;
                            while( aIt != aGradientStops.end() )
                            {
                                if (aIt->first - std::prev(aIt)->first > nWidestWidth)
                                {
                                    nWidestWidth = aIt->first - std::prev(aIt)->first;
                                    aWidestSegmentStart = std::prev(aIt);
                                }
                                ++aIt;
                            }
                            assert( nWidestWidth > 0 );

                            double nBorder = 0;
                            bool bSwap(false);

                            // Do we have just two segments, and either one is of uniform colour, or three or more
                            // segments, and the widest one is the first or last one, and is it of uniform colour? If
                            // so, deduce the border from it, and drop that segment.
                            if( aGradientStops.size() == 3 &&
                                aGradientStops.begin()->second.getColor(rGraphicHelper, nPhClr) == std::next(aGradientStops.begin())->second.getColor(rGraphicHelper, nPhClr) &&
                                aGradientStops.begin()->second.getTransparency() == std::next(aGradientStops.begin())->second.getTransparency())
                            {
                                // Two segments, first is uniformly coloured
                                SAL_INFO("oox.drawingml.gradient", "two segments, first is uniformly coloured");
                                nBorder = std::next(aGradientStops.begin())->first - aGradientStops.begin()->first;
                                aGradientStops.erase(aGradientStops.begin());
                                aWidestSegmentStart = aGradientStops.begin();
                            }
                            else if( !bSymmetric &&
                                     aGradientStops.size() == 3 &&
                                     std::next(aGradientStops.begin())->second.getColor(rGraphicHelper, nPhClr) == std::prev(aGradientStops.end())->second.getColor(rGraphicHelper, nPhClr) &&
                                     std::next(aGradientStops.begin())->second.getTransparency() == std::prev(aGradientStops.end())->second.getTransparency())
                            {
                                // Two segments, second is uniformly coloured
                                SAL_INFO("oox.drawingml.gradient", "two segments, second is uniformly coloured");
                                nBorder = std::prev(aGradientStops.end())->first - std::next(aGradientStops.begin())->first;
                                aGradientStops.erase(std::next(aGradientStops.begin()));
                                aWidestSegmentStart = aGradientStops.begin();
                                bSwap = true;
                                nShapeRotation = 180*60000 - nShapeRotation;
                            }
                            else if( !bSymmetric &&
                                     aGradientStops.size() >= 4 &&
                                     aWidestSegmentStart->second.getColor( rGraphicHelper, nPhClr ) == std::next(aWidestSegmentStart)->second.getColor(rGraphicHelper, nPhClr) &&
                                     aWidestSegmentStart->second.getTransparency() == std::next(aWidestSegmentStart)->second.getTransparency() &&
                                     ( aWidestSegmentStart == aGradientStops.begin() ||
                                       std::next(aWidestSegmentStart) == std::prev(aGradientStops.end())))
                            {
                                // Not symmetric, three or more segments, the widest is first or last and is uniformly coloured
                                SAL_INFO("oox.drawingml.gradient", "first or last segment is widest and is uniformly coloured");
                                nBorder = std::next(aWidestSegmentStart)->first - aWidestSegmentStart->first;

                                // If it's the last segment that is uniformly coloured, rotate the gradient 180
                                // degrees and swap start and end colours
                                if (std::next(aWidestSegmentStart) == std::prev(aGradientStops.end()))
                                {
                                    bSwap = true;
                                    nShapeRotation = 180*60000 - nShapeRotation;
                                }

                                aGradientStops.erase( aWidestSegmentStart++ );

                                // Look for which is widest now
                                aIt = std::next(aGradientStops.begin());
                                nWidestWidth = -1;
                                while( aIt != aGradientStops.end() )
                                {
                                    if (aIt->first - std::prev(aIt)->first > nWidestWidth)
                                    {
                                        nWidestWidth = aIt->first - std::prev(aIt)->first;
                                        aWidestSegmentStart = std::prev(aIt);
                                    }
                                    ++aIt;
                                }
                            }
                            SAL_INFO("oox.drawingml.gradient", "widest segment start: " << aWidestSegmentStart->first << ", border: " << nBorder);
                            assert( (!bSymmetric && !bSwap) || !(bSymmetric && bSwap) );

                            // Now we have a potential border and a largest segment. Use those.

                            aGradient.Style = bSymmetric ? awt::GradientStyle_AXIAL : awt::GradientStyle_LINEAR;
                            sal_Int32 nShadeAngle = maGradientProps.moShadeAngle.get( 0 );
                            // Adjust for flips
                            if ( bFlipH )
                                nShadeAngle = 180*60000 - nShadeAngle;
                            if ( bFlipV )
                                nShadeAngle = -nShadeAngle;
                            sal_Int32 nDmlAngle = nShadeAngle + nShapeRotation;
                            // convert DrawingML angle (in 1/60000 degrees) to API angle (in 1/10 degrees)
                            aGradient.Angle = static_cast< sal_Int16 >( (8100 - (nDmlAngle / (PER_DEGREE / 10))) % 3600 );
                            Color aStartColor, aEndColor;
                            if( bSymmetric )
                            {
                                aStartColor = std::next(aWidestSegmentStart)->second;
                                aEndColor = aWidestSegmentStart->second;
                                nBorder *= 2;
                            }
                            else if( bSwap )
                            {
                                aStartColor = std::next(aWidestSegmentStart)->second;
                                aEndColor = aWidestSegmentStart->second;
                            }
                            else
                            {
                                aStartColor = aWidestSegmentStart->second;
                                aEndColor = std::next(aWidestSegmentStart)->second;
                            }

                            SAL_INFO("oox.drawingml.gradient", "start color: " << std::hex << sal_Int32(aStartColor.getColor( rGraphicHelper, nPhClr )) << std::dec <<
                                     "@" << (100-aStartColor.getTransparency()) << "%"
                                     ", end color: " << std::hex << sal_Int32(aEndColor.getColor( rGraphicHelper, nPhClr )) << std::dec <<
                                     "@" << (100-aEndColor.getTransparency()) << "%");

                            aGradient.StartColor = sal_Int32(aStartColor.getColor( rGraphicHelper, nPhClr ));
                            aGradient.EndColor = sal_Int32(aEndColor.getColor( rGraphicHelper, nPhClr ));

                            if( aStartColor.hasTransparency() )
                                nStartTrans = aStartColor.getTransparency()*255/100;
                            if( aEndColor.hasTransparency() )
                                nEndTrans = aEndColor.getTransparency()*255/100;

                            aGradient.Border = rtl::math::round(100*nBorder);
                        }

                        // push gradient or named gradient to property map
                        if( rPropMap.setProperty( ShapeProperty::FillGradient, aGradient ) )
                            eFillStyle = FillStyle_GRADIENT;

                        // push gradient transparency to property map
                        if( nStartTrans != 0 || nEndTrans != 0 )
                        {
                            awt::Gradient aGrad(aGradient);
                            uno::Any aVal;
                            aGrad.EndColor = static_cast<sal_Int32>( nEndTrans | nEndTrans << 8 | nEndTrans << 16 );
                            aGrad.StartColor = static_cast<sal_Int32>( nStartTrans | nStartTrans << 8 | nStartTrans << 16 );
                            aVal <<= aGrad;
                            rPropMap.setProperty( ShapeProperty::GradientTransparency, aGrad );
                        }

                    }
                break;

                case XML_blipFill:
                    // do not start complex graphic transformation if property is not supported...
                    if (maBlipProps.mxFillGraphic.is() && rPropMap.supportsProperty(ShapeProperty::FillBitmap))
                    {
                        uno::Reference<graphic::XGraphic> xGraphic = lclCheckAndApplyDuotoneTransform(maBlipProps, maBlipProps.mxFillGraphic, rGraphicHelper, nPhClr);
                        // TODO: "rotate with shape" is not possible with our current core

                        if (xGraphic.is())
                        {
                            if (rPropMap.supportsProperty(ShapeProperty::FillBitmapName) &&
                                rPropMap.setProperty(ShapeProperty::FillBitmapName, xGraphic))
                            {
                                eFillStyle = FillStyle_BITMAP;
                            }
                            else if (rPropMap.setProperty(ShapeProperty::FillBitmap, xGraphic))
                            {
                                eFillStyle = FillStyle_BITMAP;
                            }
                        }

                        // set other bitmap properties, if bitmap has been inserted into the map
                        if( eFillStyle == FillStyle_BITMAP )
                        {
                            // bitmap mode (single, repeat, stretch)
                            BitmapMode eBitmapMode = lclGetBitmapMode( maBlipProps.moBitmapMode.get( XML_TOKEN_INVALID ) );
                            rPropMap.setProperty( ShapeProperty::FillBitmapMode, eBitmapMode );

                            // additional settings for repeated bitmap
                            if( eBitmapMode == BitmapMode_REPEAT )
                            {
                                // anchor position inside bitmap
                                RectanglePoint eRectPoint = lclGetRectanglePoint( maBlipProps.moTileAlign.get( XML_tl ) );
                                rPropMap.setProperty( ShapeProperty::FillBitmapRectanglePoint, eRectPoint );

                                awt::Size aOriginalSize = lclGetOriginalSize(rGraphicHelper, maBlipProps.mxFillGraphic);
                                if( (aOriginalSize.Width > 0) && (aOriginalSize.Height > 0) )
                                {
                                    // size of one bitmap tile (given as 1/1000 percent of bitmap size), convert to 1/100 mm
                                    double fScaleX = maBlipProps.moTileScaleX.get( MAX_PERCENT ) / static_cast< double >( MAX_PERCENT );
                                    sal_Int32 nFillBmpSizeX = getLimitedValue< sal_Int32, double >( aOriginalSize.Width * fScaleX, 1, SAL_MAX_INT32 );
                                    rPropMap.setProperty( ShapeProperty::FillBitmapSizeX, nFillBmpSizeX );
                                    double fScaleY = maBlipProps.moTileScaleY.get( MAX_PERCENT ) / static_cast< double >( MAX_PERCENT );
                                    sal_Int32 nFillBmpSizeY = getLimitedValue< sal_Int32, double >( aOriginalSize.Height * fScaleY, 1, SAL_MAX_INT32 );
                                    rPropMap.setProperty( ShapeProperty::FillBitmapSizeY, nFillBmpSizeY );

                                    // offset of the first bitmap tile (given as EMUs), convert to percent
                                    sal_Int16 nTileOffsetX = getDoubleIntervalValue< sal_Int16 >( maBlipProps.moTileOffsetX.get( 0 ) / 3.6 / aOriginalSize.Width, 0, 100 );
                                    rPropMap.setProperty( ShapeProperty::FillBitmapOffsetX, nTileOffsetX );
                                    sal_Int16 nTileOffsetY = getDoubleIntervalValue< sal_Int16 >( maBlipProps.moTileOffsetY.get( 0 ) / 3.6 / aOriginalSize.Height, 0, 100 );
                                    rPropMap.setProperty( ShapeProperty::FillBitmapOffsetY, nTileOffsetY );
                                }
                            }
                            else if ( eBitmapMode == BitmapMode_STRETCH && maBlipProps.moFillRect.has() )
                            {
                                geometry::IntegerRectangle2D aFillRect( maBlipProps.moFillRect.get() );
                                awt::Size aOriginalSize( rGraphicHelper.getOriginalSize( xGraphic ) );
                                if ( aOriginalSize.Width && aOriginalSize.Height )
                                {
                                    text::GraphicCrop aGraphCrop( 0, 0, 0, 0 );
                                    if ( aFillRect.X1 )
                                        aGraphCrop.Left = static_cast< sal_Int32 >( ( static_cast< double >( aOriginalSize.Width ) * aFillRect.X1 ) / 100000 );
                                    if ( aFillRect.Y1 )
                                        aGraphCrop.Top = static_cast< sal_Int32 >( ( static_cast< double >( aOriginalSize.Height ) * aFillRect.Y1 ) / 100000 );
                                    if ( aFillRect.X2 )
                                        aGraphCrop.Right = static_cast< sal_Int32 >( ( static_cast< double >( aOriginalSize.Width ) * aFillRect.X2 ) / 100000 );
                                    if ( aFillRect.Y2 )
                                        aGraphCrop.Bottom = static_cast< sal_Int32 >( ( static_cast< double >( aOriginalSize.Height ) * aFillRect.Y2 ) / 100000 );
                                    rPropMap.setProperty(PROP_GraphicCrop, aGraphCrop);
                                }
                            }
                        }

                        if (maBlipProps.moAlphaModFix.has())
                            rPropMap.setProperty(ShapeProperty::FillTransparency, static_cast<sal_Int16>(100 - (maBlipProps.moAlphaModFix.get() / PER_PERCENT)));
                    }
                break;

                case XML_pattFill:
                {
                    if( rPropMap.supportsProperty( ShapeProperty::FillHatch ) )
                    {
                        Color aColor( maPatternProps.maPattFgColor );
                        if( aColor.isUsed() && maPatternProps.moPattPreset.has() )
                        {
                            eFillStyle = FillStyle_HATCH;
                            rPropMap.setProperty( ShapeProperty::FillHatch, createHatch( maPatternProps.moPattPreset.get(), aColor.getColor( rGraphicHelper, nPhClr ) ) );

                            // Set background color for hatch
                            if(maPatternProps.maPattBgColor.isUsed())
                            {
                                rPropMap.setProperty( ShapeProperty::FillBackground, true );
                                rPropMap.setProperty( ShapeProperty::FillColor, maPatternProps.maPattBgColor.getColor( rGraphicHelper, nPhClr ) );
                            }
                        }
                        else if ( maPatternProps.maPattBgColor.isUsed() )
                        {
                            aColor = maPatternProps.maPattBgColor;
                            rPropMap.setProperty( ShapeProperty::FillColor, aColor.getColor( rGraphicHelper, nPhClr ) );
                            if( aColor.hasTransparency() )
                                rPropMap.setProperty( ShapeProperty::FillTransparency, aColor.getTransparency() );
                            eFillStyle = FillStyle_SOLID;
                        }
                    }
                }
                break;

                case XML_grpFill:
                    // todo
                    eFillStyle = FillStyle_NONE;
                break;
            }

            // set final fill style property
            rPropMap.setProperty( ShapeProperty::FillStyle, eFillStyle );
        }
    }

    void GraphicProperties::pushToPropMap( PropertyMap& rPropMap, const GraphicHelper& rGraphicHelper ) const
    {
        sal_Int16 nBrightness = getLimitedValue< sal_Int16, sal_Int32 >( maBlipProps.moBrightness.get( 0 ) / PER_PERCENT, -100, 100 );
        sal_Int16 nContrast = getLimitedValue< sal_Int16, sal_Int32 >( maBlipProps.moContrast.get( 0 ) / PER_PERCENT, -100, 100 );
        ColorMode eColorMode = ColorMode_STANDARD;

        switch( maBlipProps.moColorEffect.get( XML_TOKEN_INVALID ) )
        {
            case XML_biLevel:   eColorMode = ColorMode_MONO;    break;
            case XML_grayscl:   eColorMode = ColorMode_GREYS;   break;
        }

        if (maBlipProps.mxFillGraphic.is())
        {
            // created transformed graphic
            uno::Reference<graphic::XGraphic> xGraphic = lclCheckAndApplyChangeColorTransform(maBlipProps, maBlipProps.mxFillGraphic, rGraphicHelper, API_RGB_TRANSPARENT);
            xGraphic = lclCheckAndApplyDuotoneTransform(maBlipProps, xGraphic, rGraphicHelper, API_RGB_TRANSPARENT);

            if (eColorMode == ColorMode_STANDARD && nBrightness == 70 && nContrast == -70)
                // map MSO 'washout' to our Watermark colormode
                eColorMode = ColorMode_WATERMARK;
            else if( nBrightness != 0 && nContrast != 0 )
            {
                // MSO uses a different algorithm for contrast+brightness, LO applies contrast before brightness,
                // while MSO apparently applies half of brightness before contrast and half after. So if only
                // contrast or brightness need to be altered, the result is the same, but if both are involved,
                // there's no way to map that, so just force a conversion of the image.
                xGraphic = applyBrightnessContrast( xGraphic, nBrightness, nContrast );
                nBrightness = 0;
                nContrast = 0;
            }
            rPropMap.setProperty(PROP_Graphic, xGraphic);

            // cropping
            if ( maBlipProps.moClipRect.has() )
            {
                geometry::IntegerRectangle2D oClipRect( maBlipProps.moClipRect.get() );
                awt::Size aOriginalSize( rGraphicHelper.getOriginalSize( xGraphic ) );
                if ( aOriginalSize.Width && aOriginalSize.Height )
                {
                    text::GraphicCrop aGraphCrop( 0, 0, 0, 0 );
                    if ( oClipRect.X1 )
                        aGraphCrop.Left = rtl::math::round( ( static_cast< double >( aOriginalSize.Width ) * oClipRect.X1 ) / 100000 );
                    if ( oClipRect.Y1 )
                        aGraphCrop.Top = rtl::math::round( ( static_cast< double >( aOriginalSize.Height ) * oClipRect.Y1 ) / 100000 );
                    if ( oClipRect.X2 )
                        aGraphCrop.Right = rtl::math::round( ( static_cast< double >( aOriginalSize.Width ) * oClipRect.X2 ) / 100000 );
                    if ( oClipRect.Y2 )
                        aGraphCrop.Bottom = rtl::math::round( ( static_cast< double >( aOriginalSize.Height ) * oClipRect.Y2 ) / 100000 );
                    rPropMap.setProperty(PROP_GraphicCrop, aGraphCrop);
                }
            }
        }
        rPropMap.setProperty(PROP_GraphicColorMode, eColorMode);

        // brightness and contrast
        if( nBrightness != 0 )
            rPropMap.setProperty(PROP_AdjustLuminance, nBrightness);
        if( nContrast != 0 )
            rPropMap.setProperty(PROP_AdjustContrast, nContrast);

        // Media content
        assert(m_xMediaStream.is() != m_sMediaPackageURL.isEmpty());
        if (m_xMediaStream.is() && !m_sMediaPackageURL.isEmpty())
        {
            rPropMap.setProperty(PROP_PrivateStream, m_xMediaStream);
            rPropMap.setProperty(PROP_MediaURL, m_sMediaPackageURL);
        }
    }

    bool ArtisticEffectProperties::isEmpty() const
    {
        return msName.isEmpty();
    }

    css::beans::PropertyValue ArtisticEffectProperties::getEffect()
    {
        css::beans::PropertyValue aRet;
        if( msName.isEmpty() )
            return aRet;

        css::uno::Sequence< css::beans::PropertyValue > aSeq( maAttribs.size() + 1 );
        sal_uInt32 i = 0;
        for (auto const& attrib : maAttribs)
        {
            aSeq[i].Name = attrib.first;
            aSeq[i].Value = attrib.second;
            i++;
        }

        if( mrOleObjectInfo.maEmbeddedData.hasElements() )
        {
            css::uno::Sequence< css::beans::PropertyValue > aGraphicSeq( 2 );
            aGraphicSeq[0].Name = "Id";
            aGraphicSeq[0].Value <<= mrOleObjectInfo.maProgId;
            aGraphicSeq[1].Name = "Data";
            aGraphicSeq[1].Value <<= mrOleObjectInfo.maEmbeddedData;

            aSeq[i].Name = "OriginalGraphic";
            aSeq[i].Value <<= aGraphicSeq;
        }

        aRet.Name = msName;
        aRet.Value <<= aSeq;

        return aRet;
    }

    void ArtisticEffectProperties::assignUsed( const ArtisticEffectProperties& rSourceProps )
    {
        if( !rSourceProps.isEmpty() )
        {
            msName = rSourceProps.msName;
            maAttribs = rSourceProps.maAttribs;
        }
    }

    OUString ArtisticEffectProperties::getEffectString( sal_Int32 nToken )
    {
        switch( nToken )
        {
            // effects
            case OOX_TOKEN( a14, artisticBlur ):                return OUString( "artisticBlur" );
            case OOX_TOKEN( a14, artisticCement ):              return OUString( "artisticCement" );
            case OOX_TOKEN( a14, artisticChalkSketch ):         return OUString( "artisticChalkSketch" );
            case OOX_TOKEN( a14, artisticCrisscrossEtching ):   return OUString( "artisticCrisscrossEtching" );
            case OOX_TOKEN( a14, artisticCutout ):              return OUString( "artisticCutout" );
            case OOX_TOKEN( a14, artisticFilmGrain ):           return OUString( "artisticFilmGrain" );
            case OOX_TOKEN( a14, artisticGlass ):               return OUString( "artisticGlass" );
            case OOX_TOKEN( a14, artisticGlowDiffused ):        return OUString( "artisticGlowDiffused" );
            case OOX_TOKEN( a14, artisticGlowEdges ):           return OUString( "artisticGlowEdges" );
            case OOX_TOKEN( a14, artisticLightScreen ):         return OUString( "artisticLightScreen" );
            case OOX_TOKEN( a14, artisticLineDrawing ):         return OUString( "artisticLineDrawing" );
            case OOX_TOKEN( a14, artisticMarker ):              return OUString( "artisticMarker" );
            case OOX_TOKEN( a14, artisticMosiaicBubbles ):      return OUString( "artisticMosiaicBubbles" );
            case OOX_TOKEN( a14, artisticPaintStrokes ):        return OUString( "artisticPaintStrokes" );
            case OOX_TOKEN( a14, artisticPaintBrush ):          return OUString( "artisticPaintBrush" );
            case OOX_TOKEN( a14, artisticPastelsSmooth ):       return OUString( "artisticPastelsSmooth" );
            case OOX_TOKEN( a14, artisticPencilGrayscale ):     return OUString( "artisticPencilGrayscale" );
            case OOX_TOKEN( a14, artisticPencilSketch ):        return OUString( "artisticPencilSketch" );
            case OOX_TOKEN( a14, artisticPhotocopy ):           return OUString( "artisticPhotocopy" );
            case OOX_TOKEN( a14, artisticPlasticWrap ):         return OUString( "artisticPlasticWrap" );
            case OOX_TOKEN( a14, artisticTexturizer ):          return OUString( "artisticTexturizer" );
            case OOX_TOKEN( a14, artisticWatercolorSponge ):    return OUString( "artisticWatercolorSponge" );
            case OOX_TOKEN( a14, brightnessContrast ):          return OUString( "brightnessContrast" );
            case OOX_TOKEN( a14, colorTemperature ):            return OUString( "colorTemperature" );
            case OOX_TOKEN( a14, saturation ):                  return OUString( "saturation" );
            case OOX_TOKEN( a14, sharpenSoften ):               return OUString( "sharpenSoften" );

            // attributes
            case XML_visible:           return OUString( "visible" );
            case XML_trans:             return OUString( "trans" );
            case XML_crackSpacing:      return OUString( "crackSpacing" );
            case XML_pressure:          return OUString( "pressure" );
            case XML_numberOfShades:    return OUString( "numberOfShades" );
            case XML_grainSize:         return OUString( "grainSize" );
            case XML_intensity:         return OUString( "intensity" );
            case XML_smoothness:        return OUString( "smoothness" );
            case XML_gridSize:          return OUString( "gridSize" );
            case XML_pencilSize:        return OUString( "pencilSize" );
            case XML_size:              return OUString( "size" );
            case XML_brushSize:         return OUString( "brushSize" );
            case XML_scaling:           return OUString( "scaling" );
            case XML_detail:            return OUString( "detail" );
            case XML_bright:            return OUString( "bright" );
            case XML_contrast:          return OUString( "contrast" );
            case XML_colorTemp:         return OUString( "colorTemp" );
            case XML_sat:               return OUString( "sat" );
            case XML_amount:            return OUString( "amount" );
        }
        SAL_WARN( "oox.drawingml", "ArtisticEffectProperties::getEffectString: unexpected token " << nToken );
        return OUString();
    }

    sal_Int32 ArtisticEffectProperties::getEffectToken( const OUString& sName )
    {
        // effects
        if( sName == "artisticBlur" )
            return XML_artisticBlur;
        else if( sName == "artisticCement" )
            return XML_artisticCement;
        else if( sName == "artisticChalkSketch" )
            return XML_artisticChalkSketch;
        else if( sName == "artisticCrisscrossEtching" )
            return XML_artisticCrisscrossEtching;
        else if( sName == "artisticCutout" )
            return XML_artisticCutout;
        else if( sName == "artisticFilmGrain" )
            return XML_artisticFilmGrain;
        else if( sName == "artisticGlass" )
            return XML_artisticGlass;
        else if( sName == "artisticGlowDiffused" )
            return XML_artisticGlowDiffused;
        else if( sName == "artisticGlowEdges" )
            return XML_artisticGlowEdges;
        else if( sName == "artisticLightScreen" )
            return XML_artisticLightScreen;
        else if( sName == "artisticLineDrawing" )
            return XML_artisticLineDrawing;
        else if( sName == "artisticMarker" )
            return XML_artisticMarker;
        else if( sName == "artisticMosiaicBubbles" )
            return XML_artisticMosiaicBubbles;
        else if( sName == "artisticPaintStrokes" )
            return XML_artisticPaintStrokes;
        else if( sName == "artisticPaintBrush" )
            return XML_artisticPaintBrush;
        else if( sName == "artisticPastelsSmooth" )
            return XML_artisticPastelsSmooth;
        else if( sName == "artisticPencilGrayscale" )
            return XML_artisticPencilGrayscale;
        else if( sName == "artisticPencilSketch" )
            return XML_artisticPencilSketch;
        else if( sName == "artisticPhotocopy" )
            return XML_artisticPhotocopy;
        else if( sName == "artisticPlasticWrap" )
            return XML_artisticPlasticWrap;
        else if( sName == "artisticTexturizer" )
            return XML_artisticTexturizer;
        else if( sName == "artisticWatercolorSponge" )
            return XML_artisticWatercolorSponge;
        else if( sName == "brightnessContrast" )
            return XML_brightnessContrast;
        else if( sName == "colorTemperature" )
            return XML_colorTemperature;
        else if( sName == "saturation" )
            return XML_saturation;
        else if( sName == "sharpenSoften" )
            return XML_sharpenSoften;

        // attributes
        else if( sName == "visible" )
            return XML_visible;
        else if( sName == "trans" )
            return XML_trans;
        else if( sName == "crackSpacing" )
            return XML_crackSpacing;
        else if( sName == "pressure" )
            return XML_pressure;
        else if( sName == "numberOfShades" )
            return XML_numberOfShades;
        else if( sName == "grainSize" )
            return XML_grainSize;
        else if( sName == "intensity" )
            return XML_intensity;
        else if( sName == "smoothness" )
            return XML_smoothness;
        else if( sName == "gridSize" )
            return XML_gridSize;
        else if( sName == "pencilSize" )
            return XML_pencilSize;
        else if( sName == "size" )
            return XML_size;
        else if( sName == "brushSize" )
            return XML_brushSize;
        else if( sName == "scaling" )
            return XML_scaling;
        else if( sName == "detail" )
            return XML_detail;
        else if( sName == "bright" )
            return XML_bright;
        else if( sName == "contrast" )
            return XML_contrast;
        else if( sName == "colorTemp" )
            return XML_colorTemp;
        else if( sName == "sat" )
            return XML_sat;
        else if( sName == "amount" )
            return XML_amount;

        SAL_WARN( "oox.drawingml", "ArtisticEffectProperties::getEffectToken - unexpected token name" );
        return XML_none;
    }

    } // namespace drawingml
    } // namespace oox

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

↑ V568 It's odd that the argument of sizeof() operator is the expression.