1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <drawingml/fillproperties.hxx>
21 
22 #include <iterator>
23 
24 #include <drawingml/graphicproperties.hxx>
25 
26 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
27 #include <com/sun/star/beans/XPropertySet.hpp>
28 #include <com/sun/star/awt/Gradient.hpp>
29 #include <com/sun/star/text/GraphicCrop.hpp>
30 #include <com/sun/star/awt/Size.hpp>
31 #include <com/sun/star/awt/XBitmap.hpp>
32 #include <com/sun/star/drawing/BitmapMode.hpp>
33 #include <com/sun/star/drawing/ColorMode.hpp>
34 #include <com/sun/star/drawing/FillStyle.hpp>
35 #include <com/sun/star/drawing/Hatch.hpp>
36 #include <com/sun/star/drawing/RectanglePoint.hpp>
37 #include <com/sun/star/graphic/XGraphicTransformer.hpp>
38 #include <oox/core/fragmenthandler.hxx>
39 #include <oox/helper/graphichelper.hxx>
40 #include <oox/drawingml/drawingmltypes.hxx>
41 #include <oox/drawingml/shapepropertymap.hxx>
42 #include <oox/token/namespaces.hxx>
43 #include <oox/token/properties.hxx>
44 #include <oox/token/tokens.hxx>
45 #include <rtl/math.hxx>
46 #include <osl/diagnose.h>
47 #include <sal/log.hxx>
48 
49 using namespace ::com::sun::star;
50 using namespace ::com::sun::star::drawing;
51 using namespace ::com::sun::star::graphic;
52 
53 using ::com::sun::star::uno::Reference;
54 using ::com::sun::star::uno::Exception;
55 using ::com::sun::star::uno::UNO_QUERY_THROW;
56 using ::com::sun::star::geometry::IntegerRectangle2D;
57 
58 namespace oox {
59 namespace drawingml {
60 
61 namespace {
62 
63 Reference< XGraphic > lclCheckAndApplyDuotoneTransform(const BlipFillProperties& aBlipProps, uno::Reference<graphic::XGraphic> const & xGraphic,
64                                                        const GraphicHelper& rGraphicHelper, const ::Color nPhClr)
65 {
66     if (aBlipProps.maDuotoneColors[0].isUsed() && aBlipProps.maDuotoneColors[1].isUsed())
67     {
68         ::Color nColor1 = aBlipProps.maDuotoneColors[0].getColor( rGraphicHelper, nPhClr );
69         ::Color nColor2 = aBlipProps.maDuotoneColors[1].getColor( rGraphicHelper, nPhClr );
70 
71         uno::Reference<graphic::XGraphicTransformer> xTransformer(aBlipProps.mxFillGraphic, uno::UNO_QUERY);
72         if (xTransformer.is())
73             return xTransformer->applyDuotone(xGraphic, sal_Int32(nColor1), sal_Int32(nColor2));
74     }
75     return xGraphic;
76 }
77 
78 Reference< XGraphic > lclCheckAndApplyChangeColorTransform(const BlipFillProperties &aBlipProps, uno::Reference<graphic::XGraphic> const & xGraphic,
79                                                            const GraphicHelper& rGraphicHelper, const ::Color nPhClr)
80 {
81     if( aBlipProps.maColorChangeFrom.isUsed() && aBlipProps.maColorChangeTo.isUsed() )
82     {
83         ::Color nFromColor = aBlipProps.maColorChangeFrom.getColor( rGraphicHelper, nPhClr );
84         ::Color nToColor = aBlipProps.maColorChangeTo.getColor( rGraphicHelper, nPhClr );
85         if ( (nFromColor != nToColor) || aBlipProps.maColorChangeTo.hasTransparency() )
86         {
87             sal_Int16 nToTransparence = aBlipProps.maColorChangeTo.getTransparency();
88             sal_Int8 nToAlpha = static_cast< sal_Int8 >( (100 - nToTransparence) * 2.55 );
89 
90             uno::Reference<graphic::XGraphicTransformer> xTransformer(aBlipProps.mxFillGraphic, uno::UNO_QUERY);
91             if (xTransformer.is())
92                 return xTransformer->colorChange(xGraphic, sal_Int32(nFromColor), 9, sal_Int32(nToColor), nToAlpha);
93         }
94     }
95     return xGraphic;
96 }
97 
98 uno::Reference<graphic::XGraphic> applyBrightnessContrast(uno::Reference<graphic::XGraphic> const & xGraphic, sal_Int32 brightness, sal_Int32 contrast)
99 {
100     uno::Reference<graphic::XGraphicTransformer> xTransformer(xGraphic, uno::UNO_QUERY);
101     if (xTransformer.is())
102         return xTransformer->applyBrightnessContrast(xGraphic, brightness, contrast, true);
103     return xGraphic;
104 }
105 
106 BitmapMode lclGetBitmapMode( sal_Int32 nToken )
107 {
108     OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
109     switch( nToken )
110     {
111         case XML_tile:      return BitmapMode_REPEAT;
112         case XML_stretch:   return BitmapMode_STRETCH;
113     }
114     return BitmapMode_NO_REPEAT;
115 }
116 
117 RectanglePoint lclGetRectanglePoint( sal_Int32 nToken )
118 {
119     OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
120     switch( nToken )
121     {
122         case XML_tl:    return RectanglePoint_LEFT_TOP;
123         case XML_t:     return RectanglePoint_MIDDLE_TOP;
124         case XML_tr:    return RectanglePoint_RIGHT_TOP;
125         case XML_l:     return RectanglePoint_LEFT_MIDDLE;
126         case XML_ctr:   return RectanglePoint_MIDDLE_MIDDLE;
127         case XML_r:     return RectanglePoint_RIGHT_MIDDLE;
128         case XML_bl:    return RectanglePoint_LEFT_BOTTOM;
129         case XML_b:     return RectanglePoint_MIDDLE_BOTTOM;
130         case XML_br:    return RectanglePoint_RIGHT_BOTTOM;
131     }
132     return RectanglePoint_LEFT_TOP;
133 }
134 
135 awt::Size lclGetOriginalSize( const GraphicHelper& rGraphicHelper, const Reference< XGraphic >& rxGraphic )
136 {
137     awt::Size aSizeHmm( 0, 0 );
138     try
139     {
140         Reference< beans::XPropertySet > xGraphicPropertySet( rxGraphic, UNO_QUERY_THROW );
141         if( xGraphicPropertySet->getPropertyValue( "Size100thMM" ) >>= aSizeHmm )
142         {
143             if( !aSizeHmm.Width && !aSizeHmm.Height )
144             {   // MAPMODE_PIXEL USED :-(
145                 awt::Size aSourceSizePixel( 0, 0 );
146                 if( xGraphicPropertySet->getPropertyValue( "SizePixel" ) >>= aSourceSizePixel )
147                     aSizeHmm = rGraphicHelper.convertScreenPixelToHmm( aSourceSizePixel );
148             }
149         }
150     }
151     catch( Exception& )
152     {
153     }
154     return aSizeHmm;
155 }
156 
157 /**
158  * Looks for a last gradient transition and possibly sets a gradient border
159  * based on that.
160  */
161 void extractGradientBorderFromStops(const GradientFillProperties& rGradientProps,
162                                     const GraphicHelper& rGraphicHelper, ::Color nPhClr,
163                                     awt::Gradient& rGradient)
164 {
165     if (rGradientProps.maGradientStops.size() <= 1)
166         return;
167 
168     auto it = rGradientProps.maGradientStops.rbegin();
169     double fLastPos = it->first;
170     Color aLastColor = it->second;
171     ++it;
172     double fLastButOnePos = it->first;
173     Color aLastButOneColor = it->second;
174     if (!aLastColor.equals(aLastButOneColor, rGraphicHelper, nPhClr))
175         return;
176 
177     // Last transition has the same color, we can map that to a border.
178     rGradient.Border = rtl::math::round((fLastPos - fLastButOnePos) * 100);
179 }
180 
181 } // namespace
182 
183 void GradientFillProperties::assignUsed( const GradientFillProperties& rSourceProps )
184 {
185     if( !rSourceProps.maGradientStops.empty() )
186         maGradientStops = rSourceProps.maGradientStops;
187     moFillToRect.assignIfUsed( rSourceProps.moFillToRect );
188     moTileRect.assignIfUsed( rSourceProps.moTileRect );
189     moGradientPath.assignIfUsed( rSourceProps.moGradientPath );
190     moShadeAngle.assignIfUsed( rSourceProps.moShadeAngle );
191     moShadeFlip.assignIfUsed( rSourceProps.moShadeFlip );
192     moShadeScaled.assignIfUsed( rSourceProps.moShadeScaled );
193     moRotateWithShape.assignIfUsed( rSourceProps.moRotateWithShape );
194 }
195 
196 void PatternFillProperties::assignUsed( const PatternFillProperties& rSourceProps )
197 {
198     maPattFgColor.assignIfUsed( rSourceProps.maPattFgColor );
199     maPattBgColor.assignIfUsed( rSourceProps.maPattBgColor );
200     moPattPreset.assignIfUsed( rSourceProps.moPattPreset );
201 }
202 
203 void BlipFillProperties::assignUsed( const BlipFillProperties& rSourceProps )
204 {
205     if(rSourceProps.mxFillGraphic.is())
206         mxFillGraphic = rSourceProps.mxFillGraphic;
207     moBitmapMode.assignIfUsed( rSourceProps.moBitmapMode );
208     moFillRect.assignIfUsed( rSourceProps.moFillRect );
209     moTileOffsetX.assignIfUsed( rSourceProps.moTileOffsetX );
210     moTileOffsetY.assignIfUsed( rSourceProps.moTileOffsetY );
211     moTileScaleX.assignIfUsed( rSourceProps.moTileScaleX );
212     moTileScaleY.assignIfUsed( rSourceProps.moTileScaleY );
213     moTileAlign.assignIfUsed( rSourceProps.moTileAlign );
214     moTileFlip.assignIfUsed( rSourceProps.moTileFlip );
215     moRotateWithShape.assignIfUsed( rSourceProps.moRotateWithShape );
216     moColorEffect.assignIfUsed( rSourceProps.moColorEffect );
217     moBrightness.assignIfUsed( rSourceProps.moBrightness );
218     moContrast.assignIfUsed( rSourceProps.moContrast );
219     maColorChangeFrom.assignIfUsed( rSourceProps.maColorChangeFrom );
220     maColorChangeTo.assignIfUsed( rSourceProps.maColorChangeTo );
221     maDuotoneColors[0].assignIfUsed( rSourceProps.maDuotoneColors[0] );
222     maDuotoneColors[1].assignIfUsed( rSourceProps.maDuotoneColors[1] );
223     maEffect.assignUsed( rSourceProps.maEffect );
224     moAlphaModFix.assignIfUsed(rSourceProps.moAlphaModFix);
225 }
226 
227 void FillProperties::assignUsed( const FillProperties& rSourceProps )
228 {
229     moFillType.assignIfUsed( rSourceProps.moFillType );
230     maFillColor.assignIfUsed( rSourceProps.maFillColor );
231     maGradientProps.assignUsed( rSourceProps.maGradientProps );
232     maPatternProps.assignUsed( rSourceProps.maPatternProps );
233     maBlipProps.assignUsed( rSourceProps.maBlipProps );
234 }
235 
236 Color FillProperties::getBestSolidColor() const
237 {
238     Color aSolidColor;
239     if( moFillType.has() ) switch( moFillType.get() )
240     {
241         case XML_solidFill:
242             aSolidColor = maFillColor;
243         break;
244         case XML_gradFill:
245             if( !maGradientProps.maGradientStops.empty() )
246             {
247                 GradientFillProperties::GradientStopMap::const_iterator aGradientStop =
248                     maGradientProps.maGradientStops.begin();
249                 if (maGradientProps.maGradientStops.size() > 2)
250                     ++aGradientStop;
251                 aSolidColor = aGradientStop->second;
252             }
253         break;
254         case XML_pattFill:
255             aSolidColor = maPatternProps.maPattBgColor.isUsed() ? maPatternProps.maPattBgColor : maPatternProps.maPattFgColor;
256         break;
257     }
258     return aSolidColor;
259 }
260 
261 /// Maps the hatch token to drawing::Hatch.
262 static drawing::Hatch createHatch( sal_Int32 nHatchToken, ::Color nColor )
263 {
264     drawing::Hatch aHatch;
265     aHatch.Color = sal_Int32(nColor);
266 
267     // best-effort mapping; we do not support all the styles in core
268     switch ( nHatchToken )
269     {
270         case XML_pct5:       aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 250; aHatch.Angle = 450;  break;
271         case XML_pct10:      aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 200; aHatch.Angle = 450;  break;
272         case XML_pct20:      aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 150; aHatch.Angle = 450;  break;
273         case XML_pct25:      aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 200; aHatch.Angle = 450;  break;
274         case XML_pct30:      aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 175; aHatch.Angle = 450;  break;
275         case XML_pct40:      aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 150; aHatch.Angle = 450;  break;
276         case XML_pct50:      aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 125; aHatch.Angle = 450;  break;
277         case XML_pct60:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 150; aHatch.Angle = 450;  break;
278         case XML_pct70:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 125; aHatch.Angle = 450;  break;
279         case XML_pct75:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
280         case XML_pct80:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 75;  aHatch.Angle = 450;  break;
281         case XML_pct90:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 50;  aHatch.Angle = 450;  break;
282         case XML_horz:       aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 0;    break;
283         case XML_vert:       aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 900;  break;
284         case XML_ltHorz:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 0;    break;
285         case XML_ltVert:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 900;  break;
286         case XML_dkHorz:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 25;  aHatch.Angle = 0;    break;
287         case XML_dkVert:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 25;  aHatch.Angle = 900;  break;
288         case XML_narHorz:    aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 0;    break;
289         case XML_narVert:    aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 900;  break;
290         case XML_dashHorz:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 150; aHatch.Angle = 0;    break;
291         case XML_dashVert:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 150; aHatch.Angle = 900;  break;
292         case XML_cross:      aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 100; aHatch.Angle = 0;    break;
293         case XML_dnDiag:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 1350; break;
294         case XML_upDiag:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
295         case XML_ltDnDiag:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 1350; break;
296         case XML_ltUpDiag:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 450;  break;
297         case XML_dkDnDiag:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 1350; break;
298         case XML_dkUpDiag:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 50;  aHatch.Angle = 450;  break;
299         case XML_wdDnDiag:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 1350; break;
300         case XML_wdUpDiag:   aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
301         case XML_dashDnDiag: aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 150; aHatch.Angle = 1350; break;
302         case XML_dashUpDiag: aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 150; aHatch.Angle = 450;  break;
303         case XML_diagCross:  aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
304         case XML_smCheck:    aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 50;  aHatch.Angle = 450;  break;
305         case XML_lgCheck:    aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
306         case XML_smGrid:     aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 50;  aHatch.Angle = 0;    break;
307         case XML_lgGrid:     aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 100; aHatch.Angle = 0;    break;
308         case XML_dotGrid:    aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 400; aHatch.Angle = 0;    break;
309         case XML_smConfetti: aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 200; aHatch.Angle = 600;  break;
310         case XML_lgConfetti: aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 600;  break;
311         case XML_horzBrick:  aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 300; aHatch.Angle = 0;    break;
312         case XML_diagBrick:  aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 300; aHatch.Angle = 450;  break;
313         case XML_solidDmnd:  aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
314         case XML_openDmnd:   aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 100; aHatch.Angle = 450;  break;
315         case XML_dotDmnd:    aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 300; aHatch.Angle = 450;  break;
316         case XML_plaid:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 200; aHatch.Angle = 900;  break;
317         case XML_sphere:     aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 100; aHatch.Angle = 0;    break;
318         case XML_weave:      aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 150; aHatch.Angle = 450;  break;
319         case XML_divot:      aHatch.Style = drawing::HatchStyle_TRIPLE; aHatch.Distance = 400; aHatch.Angle = 450;  break;
320         case XML_shingle:    aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 200; aHatch.Angle = 1350; break;
321         case XML_wave:       aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 100; aHatch.Angle = 0;    break;
322         case XML_trellis:    aHatch.Style = drawing::HatchStyle_DOUBLE; aHatch.Distance = 75;  aHatch.Angle = 450;  break;
323         case XML_zigZag:     aHatch.Style = drawing::HatchStyle_SINGLE; aHatch.Distance = 75;  aHatch.Angle = 0;    break;
324     }
325 
326     return aHatch;
327 }
328 
329 void FillProperties::pushToPropMap( ShapePropertyMap& rPropMap,
330         const GraphicHelper& rGraphicHelper, sal_Int32 nShapeRotation, ::Color nPhClr,
331         bool bFlipH, bool bFlipV ) const
332 {
333     if( moFillType.has() )
334     {
335         FillStyle eFillStyle = FillStyle_NONE;
336         OSL_ASSERT((moFillType.get() & sal_Int32(0xFFFF0000))==0);
337         switch( moFillType.get() )
338         {
339             case XML_noFill:
340                 eFillStyle = FillStyle_NONE;
341             break;
342 
343             case XML_solidFill:
344                 if( maFillColor.isUsed() )
345                 {
346                     rPropMap.setProperty( ShapeProperty::FillColor, maFillColor.getColor( rGraphicHelper, nPhClr ) );
347                     if( maFillColor.hasTransparency() )
348                         rPropMap.setProperty( ShapeProperty::FillTransparency, maFillColor.getTransparency() );
349                     eFillStyle = FillStyle_SOLID;
350                 }
351             break;
352 
353             case XML_gradFill:
354                 // do not create gradient struct if property is not supported...
355                 if( rPropMap.supportsProperty( ShapeProperty::FillGradient ) )
356                 {
357                     sal_Int32 nEndTrans     = 0;
358                     sal_Int32 nStartTrans   = 0;
359                     awt::Gradient aGradient;
360                     aGradient.Angle = 900;
361                     aGradient.StartIntensity = 100;
362                     aGradient.EndIntensity = 100;
363 
364                     // Old code, values in aGradient overwritten in many cases by newer code below
365                     if( maGradientProps.maGradientStops.size() > 1 )
366                     {
367                         aGradient.StartColor = sal_Int32(maGradientProps.maGradientStops.begin()->second.getColor( rGraphicHelper, nPhClr ));
368                         aGradient.EndColor = sal_Int32(maGradientProps.maGradientStops.rbegin()->second.getColor( rGraphicHelper, nPhClr ));
369                         if( maGradientProps.maGradientStops.rbegin()->second.hasTransparency() )
370                             nEndTrans = maGradientProps.maGradientStops.rbegin()->second.getTransparency()*255/100;
371                         if( maGradientProps.maGradientStops.begin()->second.hasTransparency() )
372                             nStartTrans = maGradientProps.maGradientStops.begin()->second.getTransparency()*255/100;
373                     }
374 
375                     // "rotate with shape" set to false -> do not rotate
376                     if ( !maGradientProps.moRotateWithShape.get( true ) )
377                         nShapeRotation = 0;
378 
379                     if( maGradientProps.moGradientPath.has() )
380                     {
381                         // position of gradient center (limited to [30%;100%], otherwise gradient is too hidden)
382                         IntegerRectangle2D aFillToRect = maGradientProps.moFillToRect.get( IntegerRectangle2D( 0, 0, MAX_PERCENT, MAX_PERCENT ) );
383                         sal_Int32 nCenterX = (MAX_PERCENT + aFillToRect.X1 - aFillToRect.X2) / 2;
384                         aGradient.XOffset = getLimitedValue<sal_Int16, sal_Int32>(
385                             nCenterX / PER_PERCENT, 30, 100);
386 
387                         // Style should be radial at least when the horizontal center is at 50%.
388                         awt::GradientStyle eCircle = aGradient.XOffset == 50
389                                                          ? awt::GradientStyle_RADIAL
390                                                          : awt::GradientStyle_ELLIPTICAL;
391                         aGradient.Style = (maGradientProps.moGradientPath.get() == XML_circle)
392                                               ? eCircle
393                                               : awt::GradientStyle_RECT;
394 
395                         sal_Int32 nCenterY = (MAX_PERCENT + aFillToRect.Y1 - aFillToRect.Y2) / 2;
396                         aGradient.YOffset = getLimitedValue<sal_Int16, sal_Int32>(
397                             nCenterY / PER_PERCENT, 30, 100);
398                         ::std::swap( aGradient.StartColor, aGradient.EndColor );
399                         ::std::swap( nStartTrans, nEndTrans );
400 
401                         extractGradientBorderFromStops(maGradientProps, rGraphicHelper, nPhClr,
402                                                        aGradient);
403                     }
404                     else if (!maGradientProps.maGradientStops.empty())
405                     {
406                         // A copy of the gradient stops for local modification
407                         GradientFillProperties::GradientStopMap aGradientStops(maGradientProps.maGradientStops);
408 
409                         // Add a fake gradient stop at 0% and 100% if necessary, so that the gradient always starts
410                         // at 0% and ends at 100%, to make following logic clearer (?).
411                         auto a0 = aGradientStops.find( 0.0 );
412                         if( a0 == aGradientStops.end() )
413                         {
414                             // temp variable required
415                             Color aFirstColor(aGradientStops.begin()->second);
416                             aGradientStops.emplace( 0.0, aFirstColor );
417                         }
418 
419                         auto a1 = aGradientStops.find( 1.0 );
420                         if( a1 == aGradientStops.end() )
421                         {
422                             // ditto
423                             Color aLastColor(aGradientStops.rbegin()->second);
424                             aGradientStops.emplace( 1.0, aLastColor );
425                         }
426 
427                         // Check if the gradient is symmetric, which we will emulate with an "axial" gradient.
428                         bool bSymmetric(true);
429                         {
430                             GradientFillProperties::GradientStopMap::const_iterator aItA( aGradientStops.begin() );
431                             GradientFillProperties::GradientStopMap::const_iterator aItZ(std::prev(aGradientStops.end()));
432                             while( bSymmetric && aItA->first < aItZ->first )
433                             {
434                                 if (!aItA->second.equals(aItZ->second, rGraphicHelper, nPhClr))
435                                     bSymmetric = false;
436                                 else
437                                 {
438                                     ++aItA;
439                                     aItZ = std::prev(aItZ);
440                                 }
441                             }
442                             // Don't be fooled if the middlemost stop isn't at 0.5.
443                             if( bSymmetric && aItA == aItZ && aItA->first != 0.5 )
444                                 bSymmetric = false;
445 
446                             // If symmetric, do the rest of the logic for just a half.
447                             if( bSymmetric )
448                             {
449                                 // aItZ already points to the colour for the middle, but insert a fake stop at the
450                                 // exact middle if necessary.
451                                 if( aItA->first != aItZ->first )
452                                 {
453                                     Color aMiddleColor = aItZ->second;
454                                     auto a05 = aGradientStops.find( 0.5 );
455 
456                                     if( a05 != aGradientStops.end() )
457                                         a05->second = aMiddleColor;
458                                     else
459                                         aGradientStops.emplace( 0.5, aMiddleColor );
460                                 }
461                                 // Drop the rest of the stops
462                                 while( aGradientStops.rbegin()->first > 0.5 )
463                                     aGradientStops.erase( aGradientStops.rbegin()->first );
464                             }
465                         }
466 
467                         SAL_INFO("oox.drawingml.gradient", "symmetric: " << (bSymmetric ? "YES" : "NO") <<
468                                  ", number of stops: " << aGradientStops.size());
469                         size_t nIndex = 0;
470                         for (auto const& gradientStop : aGradientStops)
471                             SAL_INFO("oox.drawingml.gradient", "  " << nIndex++ << ": " <<
472                                      gradientStop.first << ": " <<
473                                      std::hex << sal_Int32(gradientStop.second.getColor( rGraphicHelper, nPhClr )) << std::dec <<
474                                      "@" << (100 - gradientStop.second.getTransparency()) << "%");
475 
476                         // Now estimate the simple LO style gradient (only two stops, at n% and 100%, where n ==
477                         // the "border") that best emulates the gradient between begin() and prior(end()).
478 
479                         // First look for the largest segment in the gradient.
480                         GradientFillProperties::GradientStopMap::iterator aIt(aGradientStops.begin());
481                         double nWidestWidth = -1;
482                         GradientFillProperties::GradientStopMap::iterator aWidestSegmentStart;
483                         ++aIt;
484                         while( aIt != aGradientStops.end() )
485                         {
486                             if (aIt->first - std::prev(aIt)->first > nWidestWidth)
487                             {
488                                 nWidestWidth = aIt->first - std::prev(aIt)->first;
489                                 aWidestSegmentStart = std::prev(aIt);
490                             }
491                             ++aIt;
492                         }
493                         assert( nWidestWidth > 0 );
494 
495                         double nBorder = 0;
496                         bool bSwap(false);
497 
498                         // Do we have just two segments, and either one is of uniform colour, or three or more
499                         // segments, and the widest one is the first or last one, and is it of uniform colour? If
500                         // so, deduce the border from it, and drop that segment.
501                         if( aGradientStops.size() == 3 &&
502                             aGradientStops.begin()->second.getColor(rGraphicHelper, nPhClr) == std::next(aGradientStops.begin())->second.getColor(rGraphicHelper, nPhClr) &&
503                             aGradientStops.begin()->second.getTransparency() == std::next(aGradientStops.begin())->second.getTransparency())
504                         {
505                             // Two segments, first is uniformly coloured
506                             SAL_INFO("oox.drawingml.gradient", "two segments, first is uniformly coloured");
507                             nBorder = std::next(aGradientStops.begin())->first - aGradientStops.begin()->first;
508                             aGradientStops.erase(aGradientStops.begin());
509                             aWidestSegmentStart = aGradientStops.begin();
510                         }
511                         else if( !bSymmetric &&
512                                  aGradientStops.size() == 3 &&
513                                  std::next(aGradientStops.begin())->second.getColor(rGraphicHelper, nPhClr) == std::prev(aGradientStops.end())->second.getColor(rGraphicHelper, nPhClr) &&
514                                  std::next(aGradientStops.begin())->second.getTransparency() == std::prev(aGradientStops.end())->second.getTransparency())
515                         {
516                             // Two segments, second is uniformly coloured
517                             SAL_INFO("oox.drawingml.gradient", "two segments, second is uniformly coloured");
518                             nBorder = std::prev(aGradientStops.end())->first - std::next(aGradientStops.begin())->first;
519                             aGradientStops.erase(std::next(aGradientStops.begin()));
520                             aWidestSegmentStart = aGradientStops.begin();
521                             bSwap = true;
522                             nShapeRotation = 180*60000 - nShapeRotation;
523                         }
524                         else if( !bSymmetric &&
525                                  aGradientStops.size() >= 4 &&
526                                  aWidestSegmentStart->second.getColor( rGraphicHelper, nPhClr ) == std::next(aWidestSegmentStart)->second.getColor(rGraphicHelper, nPhClr) &&
527                                  aWidestSegmentStart->second.getTransparency() == std::next(aWidestSegmentStart)->second.getTransparency() &&
528                                  ( aWidestSegmentStart == aGradientStops.begin() ||
529                                    std::next(aWidestSegmentStart) == std::prev(aGradientStops.end())))
530                         {
531                             // Not symmetric, three or more segments, the widest is first or last and is uniformly coloured
532                             SAL_INFO("oox.drawingml.gradient", "first or last segment is widest and is uniformly coloured");
533                             nBorder = std::next(aWidestSegmentStart)->first - aWidestSegmentStart->first;
534 
535                             // If it's the last segment that is uniformly coloured, rotate the gradient 180
536                             // degrees and swap start and end colours
537                             if (std::next(aWidestSegmentStart) == std::prev(aGradientStops.end()))
538                             {
539                                 bSwap = true;
540                                 nShapeRotation = 180*60000 - nShapeRotation;
541                             }
542 
543                             aGradientStops.erase( aWidestSegmentStart++ );
544 
545                             // Look for which is widest now
546                             aIt = std::next(aGradientStops.begin());
547                             nWidestWidth = -1;
548                             while( aIt != aGradientStops.end() )
549                             {
550                                 if (aIt->first - std::prev(aIt)->first > nWidestWidth)
551                                 {
552                                     nWidestWidth = aIt->first - std::prev(aIt)->first;
553                                     aWidestSegmentStart = std::prev(aIt);
554                                 }
555                                 ++aIt;
556                             }
557                         }
558                         SAL_INFO("oox.drawingml.gradient", "widest segment start: " << aWidestSegmentStart->first << ", border: " << nBorder);
559                         assert( (!bSymmetric && !bSwap) || !(bSymmetric && bSwap) );
560 
561                         // Now we have a potential border and a largest segment. Use those.
562 
563                         aGradient.Style = bSymmetric ? awt::GradientStyle_AXIAL : awt::GradientStyle_LINEAR;
564                         sal_Int32 nShadeAngle = maGradientProps.moShadeAngle.get( 0 );
565                         // Adjust for flips
566                         if ( bFlipH )
567                             nShadeAngle = 180*60000 - nShadeAngle;
568                         if ( bFlipV )
569                             nShadeAngle = -nShadeAngle;
570                         sal_Int32 nDmlAngle = nShadeAngle + nShapeRotation;
571                         // convert DrawingML angle (in 1/60000 degrees) to API angle (in 1/10 degrees)
572                         aGradient.Angle = static_cast< sal_Int16 >( (8100 - (nDmlAngle / (PER_DEGREE / 10))) % 3600 );
573                         Color aStartColor, aEndColor;
574                         if( bSymmetric )
575                         {
576                             aStartColor = std::next(aWidestSegmentStart)->second;
577                             aEndColor = aWidestSegmentStart->second;
578                             nBorder *= 2;
579                         }
580                         else if( bSwap )
581                         {
582                             aStartColor = std::next(aWidestSegmentStart)->second;
583                             aEndColor = aWidestSegmentStart->second;
584                         }
585                         else
586                         {
587                             aStartColor = aWidestSegmentStart->second;
588                             aEndColor = std::next(aWidestSegmentStart)->second;
589                         }
590 
591                         SAL_INFO("oox.drawingml.gradient", "start color: " << std::hex << sal_Int32(aStartColor.getColor( rGraphicHelper, nPhClr )) << std::dec <<
592                                  "@" << (100-aStartColor.getTransparency()) << "%"
593                                  ", end color: " << std::hex << sal_Int32(aEndColor.getColor( rGraphicHelper, nPhClr )) << std::dec <<
594                                  "@" << (100-aEndColor.getTransparency()) << "%");
595 
596                         aGradient.StartColor = sal_Int32(aStartColor.getColor( rGraphicHelper, nPhClr ));
597                         aGradient.EndColor = sal_Int32(aEndColor.getColor( rGraphicHelper, nPhClr ));
598 
599                         if( aStartColor.hasTransparency() )
600                             nStartTrans = aStartColor.getTransparency()*255/100;
601                         if( aEndColor.hasTransparency() )
602                             nEndTrans = aEndColor.getTransparency()*255/100;
603 
604                         aGradient.Border = rtl::math::round(100*nBorder);
605                     }
606 
607                     // push gradient or named gradient to property map
608                     if( rPropMap.setProperty( ShapeProperty::FillGradient, aGradient ) )
609                         eFillStyle = FillStyle_GRADIENT;
610 
611                     // push gradient transparency to property map
612                     if( nStartTrans != 0 || nEndTrans != 0 )
613                     {
614                         awt::Gradient aGrad(aGradient);
615                         uno::Any aVal;
616                         aGrad.EndColor = static_cast<sal_Int32>( nEndTrans | nEndTrans << 8 | nEndTrans << 16 );
617                         aGrad.StartColor = static_cast<sal_Int32>( nStartTrans | nStartTrans << 8 | nStartTrans << 16 );
618                         aVal <<= aGrad;
619                         rPropMap.setProperty( ShapeProperty::GradientTransparency, aGrad );
620                     }
621 
622                 }
623             break;
624 
625             case XML_blipFill:
626                 // do not start complex graphic transformation if property is not supported...
627                 if (maBlipProps.mxFillGraphic.is() && rPropMap.supportsProperty(ShapeProperty::FillBitmap))
628                 {
629                     uno::Reference<graphic::XGraphic> xGraphic = lclCheckAndApplyDuotoneTransform(maBlipProps, maBlipProps.mxFillGraphic, rGraphicHelper, nPhClr);
630                     // TODO: "rotate with shape" is not possible with our current core
631 
632                     if (xGraphic.is())
633                     {
634                         if (rPropMap.supportsProperty(ShapeProperty::FillBitmapName) &&
635                             rPropMap.setProperty(ShapeProperty::FillBitmapName, xGraphic))
636                         {
637                             eFillStyle = FillStyle_BITMAP;
638                         }
639                         else if (rPropMap.setProperty(ShapeProperty::FillBitmap, xGraphic))
640                         {
641                             eFillStyle = FillStyle_BITMAP;
642                         }
643                     }
644 
645                     // set other bitmap properties, if bitmap has been inserted into the map
646                     if( eFillStyle == FillStyle_BITMAP )
647                     {
648                         // bitmap mode (single, repeat, stretch)
649                         BitmapMode eBitmapMode = lclGetBitmapMode( maBlipProps.moBitmapMode.get( XML_TOKEN_INVALID ) );
650                         rPropMap.setProperty( ShapeProperty::FillBitmapMode, eBitmapMode );
651 
652                         // additional settings for repeated bitmap
653                         if( eBitmapMode == BitmapMode_REPEAT )
654                         {
655                             // anchor position inside bitmap
656                             RectanglePoint eRectPoint = lclGetRectanglePoint( maBlipProps.moTileAlign.get( XML_tl ) );
657                             rPropMap.setProperty( ShapeProperty::FillBitmapRectanglePoint, eRectPoint );
658 
659                             awt::Size aOriginalSize = lclGetOriginalSize(rGraphicHelper, maBlipProps.mxFillGraphic);
660                             if( (aOriginalSize.Width > 0) && (aOriginalSize.Height > 0) )
661                             {
662                                 // size of one bitmap tile (given as 1/1000 percent of bitmap size), convert to 1/100 mm
663                                 double fScaleX = maBlipProps.moTileScaleX.get( MAX_PERCENT ) / static_cast< double >( MAX_PERCENT );
664                                 sal_Int32 nFillBmpSizeX = getLimitedValue< sal_Int32, double >( aOriginalSize.Width * fScaleX, 1, SAL_MAX_INT32 );
665                                 rPropMap.setProperty( ShapeProperty::FillBitmapSizeX, nFillBmpSizeX );
666                                 double fScaleY = maBlipProps.moTileScaleY.get( MAX_PERCENT ) / static_cast< double >( MAX_PERCENT );
667                                 sal_Int32 nFillBmpSizeY = getLimitedValue< sal_Int32, double >( aOriginalSize.Height * fScaleY, 1, SAL_MAX_INT32 );
668                                 rPropMap.setProperty( ShapeProperty::FillBitmapSizeY, nFillBmpSizeY );
669 
670                                 // offset of the first bitmap tile (given as EMUs), convert to percent
671                                 sal_Int16 nTileOffsetX = getDoubleIntervalValue< sal_Int16 >( maBlipProps.moTileOffsetX.get( 0 ) / 3.6 / aOriginalSize.Width, 0, 100 );
672                                 rPropMap.setProperty( ShapeProperty::FillBitmapOffsetX, nTileOffsetX );
673                                 sal_Int16 nTileOffsetY = getDoubleIntervalValue< sal_Int16 >( maBlipProps.moTileOffsetY.get( 0 ) / 3.6 / aOriginalSize.Height, 0, 100 );
674                                 rPropMap.setProperty( ShapeProperty::FillBitmapOffsetY, nTileOffsetY );
675                             }
676                         }
677                         else if ( eBitmapMode == BitmapMode_STRETCH && maBlipProps.moFillRect.has() )
678                         {
679                             geometry::IntegerRectangle2D aFillRect( maBlipProps.moFillRect.get() );
680                             awt::Size aOriginalSize( rGraphicHelper.getOriginalSize( xGraphic ) );
681                             if ( aOriginalSize.Width && aOriginalSize.Height )
682                             {
683                                 text::GraphicCrop aGraphCrop( 0, 0, 0, 0 );
684                                 if ( aFillRect.X1 )
685                                     aGraphCrop.Left = static_cast< sal_Int32 >( ( static_cast< double >( aOriginalSize.Width ) * aFillRect.X1 ) / 100000 );
686                                 if ( aFillRect.Y1 )
687                                     aGraphCrop.Top = static_cast< sal_Int32 >( ( static_cast< double >( aOriginalSize.Height ) * aFillRect.Y1 ) / 100000 );
688                                 if ( aFillRect.X2 )
689                                     aGraphCrop.Right = static_cast< sal_Int32 >( ( static_cast< double >( aOriginalSize.Width ) * aFillRect.X2 ) / 100000 );
690                                 if ( aFillRect.Y2 )
691                                     aGraphCrop.Bottom = static_cast< sal_Int32 >( ( static_cast< double >( aOriginalSize.Height ) * aFillRect.Y2 ) / 100000 );
692                                 rPropMap.setProperty(PROP_GraphicCrop, aGraphCrop);
693                             }
694                         }
695                     }
696 
697                     if (maBlipProps.moAlphaModFix.has())
698                         rPropMap.setProperty(ShapeProperty::FillTransparency, static_cast<sal_Int16>(100 - (maBlipProps.moAlphaModFix.get() / PER_PERCENT)));
699                 }
700             break;
701 
702             case XML_pattFill:
703             {
704                 if( rPropMap.supportsProperty( ShapeProperty::FillHatch ) )
705                 {
706                     Color aColor( maPatternProps.maPattFgColor );
707                     if( aColor.isUsed() && maPatternProps.moPattPreset.has() )
708                     {
709                         eFillStyle = FillStyle_HATCH;
710                         rPropMap.setProperty( ShapeProperty::FillHatch, createHatch( maPatternProps.moPattPreset.get(), aColor.getColor( rGraphicHelper, nPhClr ) ) );
711 
712                         // Set background color for hatch
713                         if(maPatternProps.maPattBgColor.isUsed())
714                         {
715                             rPropMap.setProperty( ShapeProperty::FillBackground, true );
716                             rPropMap.setProperty( ShapeProperty::FillColor, maPatternProps.maPattBgColor.getColor( rGraphicHelper, nPhClr ) );
717                         }
718                     }
719                     else if ( maPatternProps.maPattBgColor.isUsed() )
720                     {
721                         aColor = maPatternProps.maPattBgColor;
722                         rPropMap.setProperty( ShapeProperty::FillColor, aColor.getColor( rGraphicHelper, nPhClr ) );
723                         if( aColor.hasTransparency() )
724                             rPropMap.setProperty( ShapeProperty::FillTransparency, aColor.getTransparency() );
725                         eFillStyle = FillStyle_SOLID;
726                     }
727                 }
728             }
729             break;
730 
731             case XML_grpFill:
732                 // todo
733                 eFillStyle = FillStyle_NONE;
734             break;
735         }
736 
737         // set final fill style property
738         rPropMap.setProperty( ShapeProperty::FillStyle, eFillStyle );
739     }
740 }
741 
742 void GraphicProperties::pushToPropMap( PropertyMap& rPropMap, const GraphicHelper& rGraphicHelper) const
743 {
744     sal_Int16 nBrightness = getLimitedValue< sal_Int16, sal_Int32 >( maBlipProps.moBrightness.get( 0 ) / PER_PERCENT, -100, 100 );
745     sal_Int16 nContrast = getLimitedValue< sal_Int16, sal_Int32 >( maBlipProps.moContrast.get( 0 ) / PER_PERCENT, -100, 100 );
746     ColorMode eColorMode = ColorMode_STANDARD;
747 
748     switch( maBlipProps.moColorEffect.get( XML_TOKEN_INVALID ) )
749     {
750         case XML_biLevel:   eColorMode = ColorMode_MONO;    break;
751         case XML_grayscl:   eColorMode = ColorMode_GREYS;   break;
752     }
753 
754     if (maBlipProps.mxFillGraphic.is())
755     {
756         // created transformed graphic
757         uno::Reference<graphic::XGraphic> xGraphic = lclCheckAndApplyChangeColorTransform(maBlipProps, maBlipProps.mxFillGraphic, rGraphicHelper, API_RGB_TRANSPARENT);
758         xGraphic = lclCheckAndApplyDuotoneTransform(maBlipProps, xGraphic, rGraphicHelper, API_RGB_TRANSPARENT);
759 
760         if (eColorMode == ColorMode_STANDARD && nBrightness == 70 && nContrast == -70)
761             // map MSO 'washout' to our Watermark colormode
762             eColorMode = ColorMode_WATERMARK;
763         else if( nBrightness != 0 && nContrast != 0 )
764         {
765             // MSO uses a different algorithm for contrast+brightness, LO applies contrast before brightness,
766             // while MSO apparently applies half of brightness before contrast and half after. So if only
767             // contrast or brightness need to be altered, the result is the same, but if both are involved,
768             // there's no way to map that, so just force a conversion of the image.
769             xGraphic = applyBrightnessContrast( xGraphic, nBrightness, nContrast );
770             nBrightness = 0;
771             nContrast = 0;
772         }
773         if(mbIsCustomShape)
774         {
775             // it is a cropped graphic.
776             rPropMap.setProperty(PROP_FillStyle, FillStyle_BITMAP);
777             rPropMap.setProperty(PROP_FillBitmapMode, BitmapMode_STRETCH);
778             rPropMap.setProperty(PROP_FillBitmap, xGraphic);
779         }
780         else
781             rPropMap.setProperty(PROP_Graphic, xGraphic);
782 
783         // cropping
784         if ( maBlipProps.moClipRect.has() )
785         {
786             geometry::IntegerRectangle2D oClipRect( maBlipProps.moClipRect.get() );
787             awt::Size aOriginalSize( rGraphicHelper.getOriginalSize( xGraphic ) );
788             if ( aOriginalSize.Width && aOriginalSize.Height )
789             {
790                 text::GraphicCrop aGraphCrop( 0, 0, 0, 0 );
791                 if ( oClipRect.X1 )
792                     aGraphCrop.Left = rtl::math::round( ( static_cast< double >( aOriginalSize.Width ) * oClipRect.X1 ) / 100000 );
793                 if ( oClipRect.Y1 )
794                     aGraphCrop.Top = rtl::math::round( ( static_cast< double >( aOriginalSize.Height ) * oClipRect.Y1 ) / 100000 );
795                 if ( oClipRect.X2 )
796                     aGraphCrop.Right = rtl::math::round( ( static_cast< double >( aOriginalSize.Width ) * oClipRect.X2 ) / 100000 );
797                 if ( oClipRect.Y2 )
798                     aGraphCrop.Bottom = rtl::math::round( ( static_cast< double >( aOriginalSize.Height ) * oClipRect.Y2 ) / 100000 );
799                 rPropMap.setProperty(PROP_GraphicCrop, aGraphCrop);
800             }
801         }
802 
803         if ( maBlipProps.moAlphaModFix.has() )
804         {
805             rPropMap.setProperty(PROP_Transparency, static_cast<sal_Int16>(100 - (maBlipProps.moAlphaModFix.get() / PER_PERCENT)));
806         }
807     }
808     rPropMap.setProperty(PROP_GraphicColorMode, eColorMode);
809 
810     // brightness and contrast
811     if( nBrightness != 0 )
812         rPropMap.setProperty(PROP_AdjustLuminance, nBrightness);
813     if( nContrast != 0 )
814         rPropMap.setProperty(PROP_AdjustContrast, nContrast);
815 
816     // Media content
817     assert(m_xMediaStream.is() != m_sMediaPackageURL.isEmpty());
818     if (m_xMediaStream.is() && !m_sMediaPackageURL.isEmpty())
819     {
820         rPropMap.setProperty(PROP_PrivateStream, m_xMediaStream);
821         rPropMap.setProperty(PROP_MediaURL, m_sMediaPackageURL);
822     }
823 }
824 
825 bool ArtisticEffectProperties::isEmpty() const
826 {
827     return msName.isEmpty();
828 }
829 
830 css::beans::PropertyValue ArtisticEffectProperties::getEffect()
831 {
832     css::beans::PropertyValue aRet;
833     if( msName.isEmpty() )
834         return aRet;
835 
836     css::uno::Sequence< css::beans::PropertyValue > aSeq( maAttribs.size() + 1 );
837     sal_uInt32 i = 0;
838     for (auto const& attrib : maAttribs)
839     {
840         aSeq[i].Name = attrib.first;
841         aSeq[i].Value = attrib.second;
842         i++;
843     }
844 
845     if( mrOleObjectInfo.maEmbeddedData.hasElements() )
846     {
847         css::uno::Sequence< css::beans::PropertyValue > aGraphicSeq( 2 );
848         aGraphicSeq[0].Name = "Id";
849         aGraphicSeq[0].Value <<= mrOleObjectInfo.maProgId;
850         aGraphicSeq[1].Name = "Data";
851         aGraphicSeq[1].Value <<= mrOleObjectInfo.maEmbeddedData;
852 
853         aSeq[i].Name = "OriginalGraphic";
854         aSeq[i].Value <<= aGraphicSeq;
855     }
856 
857     aRet.Name = msName;
858     aRet.Value <<= aSeq;
859 
860     return aRet;
861 }
862 
863 void ArtisticEffectProperties::assignUsed( const ArtisticEffectProperties& rSourceProps )
864 {
865     if( !rSourceProps.isEmpty() )
866     {
867         msName = rSourceProps.msName;
868         maAttribs = rSourceProps.maAttribs;
869     }
870 }
871 
872 OUString ArtisticEffectProperties::getEffectString( sal_Int32 nToken )
873 {
874     switch( nToken )
875     {
876         // effects
877         case OOX_TOKEN( a14, artisticBlur ):                return "artisticBlur";
878         case OOX_TOKEN( a14, artisticCement ):              return "artisticCement";
879         case OOX_TOKEN( a14, artisticChalkSketch ):         return "artisticChalkSketch";
880         case OOX_TOKEN( a14, artisticCrisscrossEtching ):   return "artisticCrisscrossEtching";
881         case OOX_TOKEN( a14, artisticCutout ):              return "artisticCutout";
882         case OOX_TOKEN( a14, artisticFilmGrain ):           return "artisticFilmGrain";
883         case OOX_TOKEN( a14, artisticGlass ):               return "artisticGlass";
884         case OOX_TOKEN( a14, artisticGlowDiffused ):        return "artisticGlowDiffused";
885         case OOX_TOKEN( a14, artisticGlowEdges ):           return "artisticGlowEdges";
886         case OOX_TOKEN( a14, artisticLightScreen ):         return "artisticLightScreen";
887         case OOX_TOKEN( a14, artisticLineDrawing ):         return "artisticLineDrawing";
888         case OOX_TOKEN( a14, artisticMarker ):              return "artisticMarker";
889         case OOX_TOKEN( a14, artisticMosiaicBubbles ):      return "artisticMosiaicBubbles";
890         case OOX_TOKEN( a14, artisticPaintStrokes ):        return "artisticPaintStrokes";
891         case OOX_TOKEN( a14, artisticPaintBrush ):          return "artisticPaintBrush";
892         case OOX_TOKEN( a14, artisticPastelsSmooth ):       return "artisticPastelsSmooth";
893         case OOX_TOKEN( a14, artisticPencilGrayscale ):     return "artisticPencilGrayscale";
894         case OOX_TOKEN( a14, artisticPencilSketch ):        return "artisticPencilSketch";
895         case OOX_TOKEN( a14, artisticPhotocopy ):           return "artisticPhotocopy";
896         case OOX_TOKEN( a14, artisticPlasticWrap ):         return "artisticPlasticWrap";
897         case OOX_TOKEN( a14, artisticTexturizer ):          return "artisticTexturizer";
898         case OOX_TOKEN( a14, artisticWatercolorSponge ):    return "artisticWatercolorSponge";
899         case OOX_TOKEN( a14, brightnessContrast ):          return "brightnessContrast";
900         case OOX_TOKEN( a14, colorTemperature ):            return "colorTemperature";
901         case OOX_TOKEN( a14, saturation ):                  return "saturation";
902         case OOX_TOKEN( a14, sharpenSoften ):               return "sharpenSoften";
903 
904         // attributes
905         case XML_visible:           return "visible";
906         case XML_trans:             return "trans";
907         case XML_crackSpacing:      return "crackSpacing";
908         case XML_pressure:          return "pressure";
909         case XML_numberOfShades:    return "numberOfShades";
910         case XML_grainSize:         return "grainSize";
911         case XML_intensity:         return "intensity";
912         case XML_smoothness:        return "smoothness";
913         case XML_gridSize:          return "gridSize";
914         case XML_pencilSize:        return "pencilSize";
915         case XML_size:              return "size";
916         case XML_brushSize:         return "brushSize";
917         case XML_scaling:           return "scaling";
918         case XML_detail:            return "detail";
919         case XML_bright:            return "bright";
920         case XML_contrast:          return "contrast";
921         case XML_colorTemp:         return "colorTemp";
922         case XML_sat:               return "sat";
923         case XML_amount:            return "amount";
924     }
925     SAL_WARN( "oox.drawingml", "ArtisticEffectProperties::getEffectString: unexpected token " << nToken );
926     return OUString();
927 }
928 
929 sal_Int32 ArtisticEffectProperties::getEffectToken( const OUString& sName )
930 {
931     // effects
932     if( sName == "artisticBlur" )
933         return XML_artisticBlur;
934     else if( sName == "artisticCement" )
935         return XML_artisticCement;
936     else if( sName == "artisticChalkSketch" )
937         return XML_artisticChalkSketch;
938     else if( sName == "artisticCrisscrossEtching" )
939         return XML_artisticCrisscrossEtching;
940     else if( sName == "artisticCutout" )
941         return XML_artisticCutout;
942     else if( sName == "artisticFilmGrain" )
943         return XML_artisticFilmGrain;
944     else if( sName == "artisticGlass" )
945         return XML_artisticGlass;
946     else if( sName == "artisticGlowDiffused" )
947         return XML_artisticGlowDiffused;
948     else if( sName == "artisticGlowEdges" )
949         return XML_artisticGlowEdges;
950     else if( sName == "artisticLightScreen" )
951         return XML_artisticLightScreen;
952     else if( sName == "artisticLineDrawing" )
953         return XML_artisticLineDrawing;
954     else if( sName == "artisticMarker" )
955         return XML_artisticMarker;
956     else if( sName == "artisticMosiaicBubbles" )
957         return XML_artisticMosiaicBubbles;
958     else if( sName == "artisticPaintStrokes" )
959         return XML_artisticPaintStrokes;
960     else if( sName == "artisticPaintBrush" )
961         return XML_artisticPaintBrush;
962     else if( sName == "artisticPastelsSmooth" )
963         return XML_artisticPastelsSmooth;
964     else if( sName == "artisticPencilGrayscale" )
965         return XML_artisticPencilGrayscale;
966     else if( sName == "artisticPencilSketch" )
967         return XML_artisticPencilSketch;
968     else if( sName == "artisticPhotocopy" )
969         return XML_artisticPhotocopy;
970     else if( sName == "artisticPlasticWrap" )
971         return XML_artisticPlasticWrap;
972     else if( sName == "artisticTexturizer" )
973         return XML_artisticTexturizer;
974     else if( sName == "artisticWatercolorSponge" )
975         return XML_artisticWatercolorSponge;
976     else if( sName == "brightnessContrast" )
977         return XML_brightnessContrast;
978     else if( sName == "colorTemperature" )
979         return XML_colorTemperature;
980     else if( sName == "saturation" )
981         return XML_saturation;
982     else if( sName == "sharpenSoften" )
983         return XML_sharpenSoften;
984 
985     // attributes
986     else if( sName == "visible" )
987         return XML_visible;
988     else if( sName == "trans" )
989         return XML_trans;
990     else if( sName == "crackSpacing" )
991         return XML_crackSpacing;
992     else if( sName == "pressure" )
993         return XML_pressure;
994     else if( sName == "numberOfShades" )
995         return XML_numberOfShades;
996     else if( sName == "grainSize" )
997         return XML_grainSize;
998     else if( sName == "intensity" )
999         return XML_intensity;
1000     else if( sName == "smoothness" )
1001         return XML_smoothness;
1002     else if( sName == "gridSize" )
1003         return XML_gridSize;
1004     else if( sName == "pencilSize" )
1005         return XML_pencilSize;
1006     else if( sName == "size" )
1007         return XML_size;
1008     else if( sName == "brushSize" )
1009         return XML_brushSize;
1010     else if( sName == "scaling" )
1011         return XML_scaling;
1012     else if( sName == "detail" )
1013         return XML_detail;
1014     else if( sName == "bright" )
1015         return XML_bright;
1016     else if( sName == "contrast" )
1017         return XML_contrast;
1018     else if( sName == "colorTemp" )
1019         return XML_colorTemp;
1020     else if( sName == "sat" )
1021         return XML_sat;
1022     else if( sName == "amount" )
1023         return XML_amount;
1024 
1025     SAL_WARN( "oox.drawingml", "ArtisticEffectProperties::getEffectToken - unexpected token name" );
1026     return XML_none;
1027 }
1028 
1029 } // namespace drawingml
1030 } // namespace oox
1031 
1032 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1033