xref: /core/oox/source/drawingml/fillproperties.cxx (revision 078aa3e98baaead463d267bb84fe49e633401956)
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 <comphelper/propertyvalue.hxx>
25 #include <drawingml/graphicproperties.hxx>
26 #include <vcl/graph.hxx>
27 #include <vcl/bitmap/BitmapMonochromeFilter.hxx>
28 #include <docmodel/uno/UnoComplexColor.hxx>
29 #include <docmodel/uno/UnoGradientTools.hxx>
30 #include <basegfx/utils/gradienttools.hxx>
31 
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/awt/Gradient2.hpp>
34 #include <com/sun/star/text/GraphicCrop.hpp>
35 #include <com/sun/star/awt/Size.hpp>
36 #include <com/sun/star/drawing/BitmapMode.hpp>
37 #include <com/sun/star/drawing/ColorMode.hpp>
38 #include <com/sun/star/drawing/FillStyle.hpp>
39 #include <com/sun/star/drawing/RectanglePoint.hpp>
40 #include <com/sun/star/graphic/XGraphicTransformer.hpp>
41 #include <oox/helper/graphichelper.hxx>
42 #include <oox/drawingml/drawingmltypes.hxx>
43 #include <oox/drawingml/shapepropertymap.hxx>
44 #include <drawingml/hatchmap.hxx>
45 #include <oox/token/namespaces.hxx>
46 #include <oox/token/properties.hxx>
47 #include <oox/token/tokens.hxx>
48 #include <osl/diagnose.h>
49 #include <sal/log.hxx>
50 
51 #include <frozen/bits/defines.h>
52 #include <frozen/bits/elsa_std.h>
53 #include <frozen/unordered_map.h>
54 
55 
56 using namespace ::com::sun::star;
57 using namespace ::com::sun::star::drawing;
58 using namespace ::com::sun::star::graphic;
59 
60 using ::com::sun::star::uno::Reference;
61 using ::com::sun::star::uno::Exception;
62 using ::com::sun::star::uno::UNO_QUERY_THROW;
63 using ::com::sun::star::geometry::IntegerRectangle2D;
64 
65 namespace oox::drawingml {
66 
67 namespace {
68 
lclCheckAndApplyDuotoneTransform(const BlipFillProperties & aBlipProps,uno::Reference<graphic::XGraphic> const & xGraphic,const GraphicHelper & rGraphicHelper,const::Color nPhClr)69 Reference< XGraphic > lclCheckAndApplyDuotoneTransform(const BlipFillProperties& aBlipProps, uno::Reference<graphic::XGraphic> const & xGraphic,
70                                                        const GraphicHelper& rGraphicHelper, const ::Color nPhClr)
71 {
72     if (aBlipProps.maDuotoneColors[0].isUsed() && aBlipProps.maDuotoneColors[1].isUsed())
73     {
74         ::Color nColor1 = aBlipProps.maDuotoneColors[0].getColor( rGraphicHelper, nPhClr );
75         ::Color nColor2 = aBlipProps.maDuotoneColors[1].getColor( rGraphicHelper, nPhClr );
76 
77         uno::Reference<graphic::XGraphicTransformer> xTransformer(aBlipProps.mxFillGraphic, uno::UNO_QUERY);
78         if (xTransformer.is())
79             return xTransformer->applyDuotone(xGraphic, sal_Int32(nColor1), sal_Int32(nColor2));
80     }
81     return xGraphic;
82 }
83 
lclRotateGraphic(uno::Reference<graphic::XGraphic> const & xGraphic,Degree10 nRotation)84 Reference< XGraphic > lclRotateGraphic(uno::Reference<graphic::XGraphic> const & xGraphic, Degree10 nRotation)
85 {
86     ::Graphic aGraphic(xGraphic);
87     ::Graphic aReturnGraphic;
88 
89     assert (aGraphic.GetType() == GraphicType::Bitmap);
90 
91     Bitmap aBitmap(aGraphic.GetBitmap());
92     const ::Color aColor(0x00);
93     aBitmap.Rotate(nRotation, aColor);
94     aReturnGraphic = ::Graphic(aBitmap);
95     aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
96 
97     return aReturnGraphic.GetXGraphic();
98 }
99 
100 using Quotients = std::tuple<double, double, double, double>;
getQuotients(geometry::IntegerRectangle2D aRelRect,double hDiv,double vDiv)101 Quotients getQuotients(geometry::IntegerRectangle2D aRelRect, double hDiv, double vDiv)
102 {
103     return { aRelRect.X1 / hDiv, aRelRect.Y1 / vDiv, aRelRect.X2 / hDiv, aRelRect.Y2 / vDiv };
104 }
105 
106 // ECMA-376 Part 1 20.1.8.55 srcRect (Source Rectangle)
CropQuotientsFromSrcRect(geometry::IntegerRectangle2D aSrcRect)107 std::optional<Quotients> CropQuotientsFromSrcRect(geometry::IntegerRectangle2D aSrcRect)
108 {
109     aSrcRect.X1 = std::max(aSrcRect.X1, sal_Int32(0));
110     aSrcRect.X2 = std::max(aSrcRect.X2, sal_Int32(0));
111     aSrcRect.Y1 = std::max(aSrcRect.Y1, sal_Int32(0));
112     aSrcRect.Y2 = std::max(aSrcRect.Y2, sal_Int32(0));
113     if (aSrcRect.X1 + aSrcRect.X2 >= MAX_PERCENT || aSrcRect.Y1 + aSrcRect.Y2 >= MAX_PERCENT)
114         return {}; // Cropped everything
115     return getQuotients(aSrcRect, MAX_PERCENT, MAX_PERCENT);
116 }
117 
118 // ECMA-376 Part 1 20.1.8.30 fillRect (Fill Rectangle)
CropQuotientsFromFillRect(geometry::IntegerRectangle2D aFillRect)119 std::optional<Quotients> CropQuotientsFromFillRect(geometry::IntegerRectangle2D aFillRect)
120 {
121     aFillRect.X1 = std::min(aFillRect.X1, sal_Int32(0));
122     aFillRect.X2 = std::min(aFillRect.X2, sal_Int32(0));
123     aFillRect.Y1 = std::min(aFillRect.Y1, sal_Int32(0));
124     aFillRect.Y2 = std::min(aFillRect.Y2, sal_Int32(0));
125     // Negative divisor and negative relative offset give positive value wanted in lclCropGraphic
126     return getQuotients(aFillRect, -MAX_PERCENT + aFillRect.X1 + aFillRect.X2,
127                         -MAX_PERCENT + aFillRect.Y1 + aFillRect.Y2);
128 }
129 
130 // Crops a piece of the bitmap. lclCropGraphic doesn't handle growing.
lclCropGraphic(uno::Reference<graphic::XGraphic> const & xGraphic,std::optional<Quotients> quotients)131 Reference<XGraphic> lclCropGraphic(uno::Reference<graphic::XGraphic> const& xGraphic,
132                                    std::optional<Quotients> quotients)
133 {
134     ::Graphic aGraphic(xGraphic);
135     assert (aGraphic.GetType() == GraphicType::Bitmap);
136 
137     Bitmap aBitmap;
138     if (quotients)
139     {
140         aBitmap = aGraphic.GetBitmap();
141 
142         const Size bmpSize = aBitmap.GetSizePixel();
143         const auto& [qx1, qy1, qx2, qy2] = *quotients;
144         const tools::Long l = std::round(bmpSize.Width() * qx1);
145         const tools::Long t = std::round(bmpSize.Height() * qy1);
146         const tools::Long r = std::round(bmpSize.Width() * qx2);
147         const tools::Long b = std::round(bmpSize.Height() * qy2);
148 
149         aBitmap.Crop({ l, t, bmpSize.Width() - r - 1, bmpSize.Height() - b - 1 });
150     }
151 
152     ::Graphic aReturnGraphic(aBitmap);
153     aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
154 
155     return aReturnGraphic.GetXGraphic();
156 }
157 
lclMirrorGraphic(uno::Reference<graphic::XGraphic> const & xGraphic,bool bFlipH,bool bFlipV)158 Reference< XGraphic > lclMirrorGraphic(uno::Reference<graphic::XGraphic> const & xGraphic, bool bFlipH, bool bFlipV)
159 {
160     ::Graphic aGraphic(xGraphic);
161     ::Graphic aReturnGraphic;
162 
163     assert (aGraphic.GetType() == GraphicType::Bitmap);
164 
165     Bitmap aBitmap(aGraphic.GetBitmap());
166     BmpMirrorFlags nMirrorFlags = BmpMirrorFlags::NONE;
167 
168     if(bFlipH)
169         nMirrorFlags |= BmpMirrorFlags::Horizontal;
170     if(bFlipV)
171         nMirrorFlags |= BmpMirrorFlags::Vertical;
172 
173     aBitmap.Mirror(nMirrorFlags);
174 
175     aReturnGraphic = ::Graphic(aBitmap);
176     aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
177 
178     return aReturnGraphic.GetXGraphic();
179 }
180 
lclGreysScaleGraphic(uno::Reference<graphic::XGraphic> const & xGraphic)181 Reference< XGraphic > lclGreysScaleGraphic(uno::Reference<graphic::XGraphic> const & xGraphic)
182 {
183     ::Graphic aGraphic(xGraphic);
184     ::Graphic aReturnGraphic;
185 
186     assert (aGraphic.GetType() == GraphicType::Bitmap);
187 
188     Bitmap aBitmap(aGraphic.GetBitmap());
189     aBitmap.Convert(BmpConversion::N8BitGreys);
190 
191     aReturnGraphic = ::Graphic(aBitmap);
192     aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
193 
194     return aReturnGraphic.GetXGraphic();
195 }
196 
197 /// Applies the graphic Black&White (Monochrome) effect with the imported threshold
lclApplyBlackWhiteEffect(const BlipFillProperties & aBlipProps,const uno::Reference<graphic::XGraphic> & xGraphic)198 Reference<XGraphic> lclApplyBlackWhiteEffect(const BlipFillProperties& aBlipProps,
199                                              const uno::Reference<graphic::XGraphic>& xGraphic)
200 {
201     const auto& oBiLevelThreshold = aBlipProps.moBiLevelThreshold;
202     if (oBiLevelThreshold.has_value())
203     {
204         sal_uInt8 nThreshold
205             = static_cast<sal_uInt8>(oBiLevelThreshold.value() * 255 / MAX_PERCENT);
206 
207         ::Graphic aGraphic(xGraphic);
208         ::Graphic aReturnGraphic;
209 
210         Bitmap aBitmap(aGraphic.GetBitmap());
211         if (aBitmap.HasAlpha())
212         {
213             const AlphaMask aMask(aBitmap.CreateAlphaMask());
214 
215             Bitmap aTmpBmp(aBitmap.CreateColorBitmap());
216             BitmapFilter::Filter(aTmpBmp, BitmapMonochromeFilter{ nThreshold });
217 
218             aReturnGraphic = ::Graphic(BitmapEx(aTmpBmp, aMask));
219         }
220         else
221         {
222             BitmapFilter::Filter(aBitmap, BitmapMonochromeFilter{ nThreshold });
223             aReturnGraphic = ::Graphic(aBitmap);
224         }
225         aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
226         return aReturnGraphic.GetXGraphic();
227     }
228     return xGraphic;
229 }
230 
lclCheckAndApplyChangeColorTransform(const BlipFillProperties & aBlipProps,uno::Reference<graphic::XGraphic> const & xGraphic,const GraphicHelper & rGraphicHelper,const::Color nPhClr)231 Reference< XGraphic > lclCheckAndApplyChangeColorTransform(const BlipFillProperties &aBlipProps, uno::Reference<graphic::XGraphic> const & xGraphic,
232                                                            const GraphicHelper& rGraphicHelper, const ::Color nPhClr)
233 {
234     if( aBlipProps.maColorChangeFrom.isUsed() && aBlipProps.maColorChangeTo.isUsed() )
235     {
236         ::Color nFromColor = aBlipProps.maColorChangeFrom.getColor( rGraphicHelper, nPhClr );
237         ::Color nToColor = aBlipProps.maColorChangeTo.getColor( rGraphicHelper, nPhClr );
238         if ( (nFromColor != nToColor) || aBlipProps.maColorChangeTo.hasTransparency() )
239         {
240             sal_Int16 nToTransparence = aBlipProps.maColorChangeTo.getTransparency();
241             sal_Int8 nToAlpha = static_cast< sal_Int8 >( (100 - nToTransparence) * 2.55 );
242 
243             sal_uInt8 nTolerance = 9;
244             Graphic aGraphic{ xGraphic };
245             if( aGraphic.IsGfxLink() )
246             {
247                 // tdf#149670: Try to guess tolerance depending on image format
248                 switch (aGraphic.GetGfxLink().GetType())
249                 {
250                     case GfxLinkType::NativeJpg:
251                         nTolerance = 15;
252                         break;
253                     case GfxLinkType::NativePng:
254                     case GfxLinkType::NativeTif:
255                         nTolerance = 1;
256                         break;
257                     case GfxLinkType::NativeBmp:
258                         nTolerance = 0;
259                         break;
260                     default:
261                         break;
262                 }
263             }
264 
265             uno::Reference<graphic::XGraphicTransformer> xTransformer(aBlipProps.mxFillGraphic, uno::UNO_QUERY);
266             if (xTransformer.is())
267                 return xTransformer->colorChange(xGraphic, sal_Int32(nFromColor), nTolerance, sal_Int32(nToColor), nToAlpha);
268         }
269     }
270     return xGraphic;
271 }
272 
applyBrightnessContrast(uno::Reference<graphic::XGraphic> const & xGraphic,sal_Int32 brightness,sal_Int32 contrast)273 uno::Reference<graphic::XGraphic> applyBrightnessContrast(uno::Reference<graphic::XGraphic> const & xGraphic, sal_Int32 brightness, sal_Int32 contrast)
274 {
275     uno::Reference<graphic::XGraphicTransformer> xTransformer(xGraphic, uno::UNO_QUERY);
276     if (xTransformer.is())
277         return xTransformer->applyBrightnessContrast(xGraphic, brightness, contrast, true);
278     return xGraphic;
279 }
280 
lclGetBitmapMode(sal_Int32 nToken)281 BitmapMode lclGetBitmapMode( sal_Int32 nToken )
282 {
283     OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
284     switch( nToken )
285     {
286         case XML_tile:      return BitmapMode_REPEAT;
287         case XML_stretch:   return BitmapMode_STRETCH;
288     }
289 
290     // tdf#128596 Default value is XML_tile for MSO.
291     return BitmapMode_REPEAT;
292 }
293 
lclGetRectanglePoint(sal_Int32 nToken)294 RectanglePoint lclGetRectanglePoint( sal_Int32 nToken )
295 {
296     OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
297     switch( nToken )
298     {
299         case XML_tl:    return RectanglePoint_LEFT_TOP;
300         case XML_t:     return RectanglePoint_MIDDLE_TOP;
301         case XML_tr:    return RectanglePoint_RIGHT_TOP;
302         case XML_l:     return RectanglePoint_LEFT_MIDDLE;
303         case XML_ctr:   return RectanglePoint_MIDDLE_MIDDLE;
304         case XML_r:     return RectanglePoint_RIGHT_MIDDLE;
305         case XML_bl:    return RectanglePoint_LEFT_BOTTOM;
306         case XML_b:     return RectanglePoint_MIDDLE_BOTTOM;
307         case XML_br:    return RectanglePoint_RIGHT_BOTTOM;
308     }
309     return RectanglePoint_LEFT_TOP;
310 }
311 
lclGetOriginalSize(const GraphicHelper & rGraphicHelper,const Reference<XGraphic> & rxGraphic)312 awt::Size lclGetOriginalSize( const GraphicHelper& rGraphicHelper, const Reference< XGraphic >& rxGraphic )
313 {
314     awt::Size aSizeHmm( 0, 0 );
315     try
316     {
317         Reference< beans::XPropertySet > xGraphicPropertySet( rxGraphic, UNO_QUERY_THROW );
318         if( xGraphicPropertySet->getPropertyValue( u"Size100thMM"_ustr ) >>= aSizeHmm )
319         {
320             if( !aSizeHmm.Width && !aSizeHmm.Height )
321             {   // MAPMODE_PIXEL USED :-(
322                 awt::Size aSourceSizePixel( 0, 0 );
323                 if( xGraphicPropertySet->getPropertyValue( u"SizePixel"_ustr ) >>= aSourceSizePixel )
324                     aSizeHmm = rGraphicHelper.convertScreenPixelToHmm( aSourceSizePixel );
325             }
326         }
327     }
328     catch( Exception& )
329     {
330     }
331     return aSizeHmm;
332 }
333 
334 } // namespace
335 
assignUsed(const GradientFillProperties & rSourceProps)336 void GradientFillProperties::assignUsed( const GradientFillProperties& rSourceProps )
337 {
338     if( !rSourceProps.maGradientStops.empty() )
339         maGradientStops = rSourceProps.maGradientStops;
340     assignIfUsed( moFillToRect, rSourceProps.moFillToRect );
341     assignIfUsed( moTileRect, rSourceProps.moTileRect );
342     assignIfUsed( moGradientPath, rSourceProps.moGradientPath );
343     assignIfUsed( moShadeAngle, rSourceProps.moShadeAngle );
344     assignIfUsed( moShadeFlip, rSourceProps.moShadeFlip );
345     assignIfUsed( moShadeScaled, rSourceProps.moShadeScaled );
346     assignIfUsed( moRotateWithShape, rSourceProps.moRotateWithShape );
347 }
348 
assignUsed(const PatternFillProperties & rSourceProps)349 void PatternFillProperties::assignUsed( const PatternFillProperties& rSourceProps )
350 {
351     maPattFgColor.assignIfUsed( rSourceProps.maPattFgColor );
352     maPattBgColor.assignIfUsed( rSourceProps.maPattBgColor );
353     assignIfUsed( moPattPreset, rSourceProps.moPattPreset );
354 }
355 
assignUsed(const BlipFillProperties & rSourceProps)356 void BlipFillProperties::assignUsed( const BlipFillProperties& rSourceProps )
357 {
358     if(rSourceProps.mxFillGraphic.is())
359         mxFillGraphic = rSourceProps.mxFillGraphic;
360     assignIfUsed( moBitmapMode, rSourceProps.moBitmapMode );
361     assignIfUsed( moFillRect, rSourceProps.moFillRect );
362     assignIfUsed( moTileOffsetX, rSourceProps.moTileOffsetX );
363     assignIfUsed( moTileOffsetY, rSourceProps.moTileOffsetY );
364     assignIfUsed( moTileScaleX, rSourceProps.moTileScaleX );
365     assignIfUsed( moTileScaleY, rSourceProps.moTileScaleY );
366     assignIfUsed( moTileAlign, rSourceProps.moTileAlign );
367     assignIfUsed( moTileFlip, rSourceProps.moTileFlip );
368     assignIfUsed( moRotateWithShape, rSourceProps.moRotateWithShape );
369     assignIfUsed( moColorEffect, rSourceProps.moColorEffect );
370     assignIfUsed( moBrightness, rSourceProps.moBrightness );
371     assignIfUsed( moContrast, rSourceProps.moContrast );
372     assignIfUsed( moBiLevelThreshold, rSourceProps.moBiLevelThreshold );
373     maColorChangeFrom.assignIfUsed( rSourceProps.maColorChangeFrom );
374     maColorChangeTo.assignIfUsed( rSourceProps.maColorChangeTo );
375     maDuotoneColors[0].assignIfUsed( rSourceProps.maDuotoneColors[0] );
376     maDuotoneColors[1].assignIfUsed( rSourceProps.maDuotoneColors[1] );
377     maEffect.assignUsed( rSourceProps.maEffect );
378     assignIfUsed(moAlphaModFix, rSourceProps.moAlphaModFix);
379 }
380 
assignUsed(const FillProperties & rSourceProps)381 void FillProperties::assignUsed( const FillProperties& rSourceProps )
382 {
383     assignIfUsed( moFillType, rSourceProps.moFillType );
384     maFillColor.assignIfUsed( rSourceProps.maFillColor );
385     assignIfUsed( moUseBgFill, rSourceProps.moUseBgFill );
386     maGradientProps.assignUsed( rSourceProps.maGradientProps );
387     maPatternProps.assignUsed( rSourceProps.maPatternProps );
388     maBlipProps.assignUsed( rSourceProps.maBlipProps );
389 }
390 
getBestSolidColor() const391 Color FillProperties::getBestSolidColor() const
392 {
393     Color aSolidColor;
394     if( moFillType.has_value() ) switch( moFillType.value() )
395     {
396         case XML_solidFill:
397             aSolidColor = maFillColor;
398         break;
399         case XML_gradFill:
400             if( !maGradientProps.maGradientStops.empty() )
401             {
402                 GradientFillProperties::GradientStopMap::const_iterator aGradientStop =
403                     maGradientProps.maGradientStops.begin();
404                 if (maGradientProps.maGradientStops.size() > 2)
405                     ++aGradientStop;
406                 aSolidColor = aGradientStop->second;
407             }
408         break;
409         case XML_pattFill:
410             aSolidColor = maPatternProps.maPattBgColor.isUsed() ? maPatternProps.maPattBgColor : maPatternProps.maPattFgColor;
411         break;
412     }
413     return aSolidColor;
414 }
415 
pushToPropMap(ShapePropertyMap & rPropMap,const GraphicHelper & rGraphicHelper,sal_Int32 nShapeRotation,::Color nPhClr,const css::awt::Size & rSize,sal_Int16 nPhClrTheme,bool bFlipH,bool bFlipV,bool bIsCustomShape) const416 void FillProperties::pushToPropMap(ShapePropertyMap& rPropMap, const GraphicHelper& rGraphicHelper,
417                                    sal_Int32 nShapeRotation, ::Color nPhClr,
418                                    const css::awt::Size& rSize, sal_Int16 nPhClrTheme, bool bFlipH,
419                                    bool bFlipV, bool bIsCustomShape) const
420 {
421     if( !moFillType.has_value() )
422         return;
423 
424     FillStyle eFillStyle = FillStyle_NONE;
425     OSL_ASSERT((moFillType.value() & sal_Int32(0xFFFF0000))==0);
426     switch( moFillType.value() )
427     {
428         case XML_noFill:
429         {
430             eFillStyle = FillStyle_NONE;
431             rPropMap.setProperty(ShapeProperty::FillUseSlideBackground, moUseBgFill.value_or(false));
432         }
433         break;
434 
435         case XML_solidFill:
436             if( maFillColor.isUsed() )
437             {
438                 ::Color aFillColor = maFillColor.getColor(rGraphicHelper, nPhClr);
439                 rPropMap.setProperty(ShapeProperty::FillColor, aFillColor);
440                 if( maFillColor.hasTransparency() )
441                     rPropMap.setProperty( ShapeProperty::FillTransparency, maFillColor.getTransparency() );
442 
443                 model::ComplexColor aComplexColor;
444                 if (aFillColor == nPhClr)
445                 {
446                     aComplexColor.setThemeColor(model::convertToThemeColorType(nPhClrTheme));
447                 }
448                 else
449                 {
450                     aComplexColor = maFillColor.getComplexColor();
451                     OUString sColorName = getBestSolidColor().getSchemeColorName();
452                     sal_Int32 nToken = Color::getColorMapToken(sColorName);
453                     if (nToken != -1)
454                     {
455                         rGraphicHelper.getSchemeColorToken(nToken);
456                         model::ThemeColorType eThemeColorType = schemeTokenToThemeColorType(nToken);
457                         aComplexColor.setThemeColor(eThemeColorType);
458                     }
459                 }
460                 rPropMap.setProperty(PROP_FillComplexColor, model::color::createXComplexColor(aComplexColor));
461 
462                 eFillStyle = FillStyle_SOLID;
463             }
464         break;
465 
466         case XML_gradFill:
467             // do not create gradient struct if property is not supported...
468             if( rPropMap.supportsProperty( ShapeProperty::FillGradient ) )
469             {
470                 // prepare ColorStops
471                 basegfx::BColorStops aColorStops;
472                 basegfx::BColorStops aTransparencyStops;
473                 bool bContainsTransparency(false);
474 
475                 // convert to BColorStops, check for contained transparency
476                 for (const auto& rCandidate : maGradientProps.maGradientStops)
477                 {
478                     const ::Color aColor(rCandidate.second.getColor(rGraphicHelper, nPhClr));
479                     aColorStops.emplace_back(rCandidate.first, aColor.getBColor());
480                     bContainsTransparency = bContainsTransparency || rCandidate.second.hasTransparency();
481                 }
482 
483                 // if we have transparency, convert to BColorStops
484                 if (bContainsTransparency)
485                 {
486                     for (const auto& rCandidate : maGradientProps.maGradientStops)
487                     {
488                         const double fTrans(rCandidate.second.getTransparency() * (1.0/100.0));
489                         aTransparencyStops.emplace_back(rCandidate.first, basegfx::BColor(fTrans, fTrans, fTrans));
490                     }
491                 }
492 
493                 // prepare BGradient with some defaults
494                 // CAUTION: This used awt::Gradient2 before who's empty constructor
495                 //          (see workdir/UnoApiHeadersTarget/offapi/normal/com/sun/
496                 //          star/awt/Gradient.hpp) initializes all to zeros, so reflect
497                 //          this here. OTOH set all that were set, e.g. Start/EndIntens
498                 //          were set to 100, so just use default of BGradient constructor
499                 basegfx::BGradient aGradient(
500                     aColorStops,
501                     awt::GradientStyle_LINEAR,
502                     Degree10(900),
503                     0,  // border
504                     0,  // OfsX -> 0, not 50 (!)
505                     0); // OfsY -> 0, not 50 (!)
506 
507                 // "rotate with shape" set to false -> do not rotate
508                 if (!maGradientProps.moRotateWithShape.value_or(true))
509                 {
510                     nShapeRotation = 0;
511                 }
512 
513                 if (maGradientProps.moGradientPath.has_value())
514                 {
515                     IntegerRectangle2D aFillToRect = maGradientProps.moFillToRect.value_or( IntegerRectangle2D( 0, 0, MAX_PERCENT, MAX_PERCENT ) );
516                     sal_Int32 nCenterX = (MAX_PERCENT + aFillToRect.X1 - aFillToRect.X2) / 2;
517                     aGradient.SetXOffset(getLimitedValue<sal_Int16, sal_Int32>(
518                         nCenterX / PER_PERCENT, 0, 100));
519                     sal_Int32 nCenterY = (MAX_PERCENT + aFillToRect.Y1 - aFillToRect.Y2) / 2;
520                     aGradient.SetYOffset(getLimitedValue<sal_Int16, sal_Int32>(
521                         nCenterY / PER_PERCENT, 0, 100));
522 
523                     // FIXME tdf#166140: Size of gradient is smaller than in MSO
524                     if( maGradientProps.moGradientPath.value() == XML_circle )
525                     {
526                         aGradient.SetGradientStyle(awt::GradientStyle_RADIAL);
527                     }
528                     else
529                     {
530                         // XML_rect or XML_shape, but the latter is not implemented.
531                         aGradient.SetGradientStyle(awt::GradientStyle_RECT);
532                     }
533 
534                     aColorStops.reverseColorStops();
535                     aGradient.SetColorStops(aColorStops);
536                     aTransparencyStops.reverseColorStops();
537                 }
538                 else if (!maGradientProps.maGradientStops.empty())
539                 {
540                     // aGradient.SetGradientStyle(awt::GradientStyle_LINEAR);
541                     sal_Int32 nShadeAngle(maGradientProps.moShadeAngle.value_or( 0 ));
542                     // Adjust for flips
543                     if ( bFlipH )
544                         nShadeAngle = 180*60000 - nShadeAngle;
545                     if ( bFlipV )
546                         nShadeAngle = -nShadeAngle;
547                     const sal_Int32 nDmlAngle = nShadeAngle + nShapeRotation;
548 
549                     // convert DrawingML angle (in 1/60000 degrees) to API angle (in 1/10 degrees)
550                     aGradient.SetAngle(Degree10(static_cast< sal_Int16 >( (8100 - (nDmlAngle / (PER_DEGREE / 10))) % 3600 )));
551 
552                     // If this is symmetrical, set it as an axial gradient for better UI/export.
553                     // There were chart2 unit test failures when doing this to transparent gradients
554                     // so just avoid that case.
555                     if (!bContainsTransparency)
556                         aGradient.tryToConvertToAxial();
557                 }
558 
559                 if (awt::GradientStyle_RECT == aGradient.GetGradientStyle())
560                 {
561                     // MCGR: tdf#155362: better support border
562                     // CAUTION: Need to handle TransparencyStops if used
563                     aGradient.tryToRecreateBorder(aTransparencyStops.empty() ? nullptr : &aTransparencyStops);
564                 }
565 
566                 // push gradient or named gradient to property map
567                 if (rPropMap.setProperty(ShapeProperty::FillGradient, model::gradient::createUnoGradient2(aGradient)))
568                 {
569                     eFillStyle = FillStyle_GRADIENT;
570                 }
571 
572                 // push gradient transparency to property map if it exists
573                 if (!aTransparencyStops.empty())
574                 {
575                     aGradient.SetColorStops(aTransparencyStops);
576                     rPropMap.setProperty(ShapeProperty::GradientTransparency, model::gradient::createUnoGradient2(aGradient));
577                 }
578             }
579         break;
580 
581         case XML_blipFill:
582             // do not start complex graphic transformation if property is not supported...
583             if (maBlipProps.mxFillGraphic.is() && rPropMap.supportsProperty(ShapeProperty::FillBitmap))
584             {
585                 uno::Reference<graphic::XGraphic> xGraphic = lclCheckAndApplyDuotoneTransform(maBlipProps, maBlipProps.mxFillGraphic, rGraphicHelper, nPhClr);
586                 // TODO: "rotate with shape" is not possible with our current core
587 
588                 if (xGraphic.is())
589                 {
590                     if (maBlipProps.moColorEffect.value_or(XML_TOKEN_INVALID) == XML_grayscl)
591                         xGraphic = lclGreysScaleGraphic(xGraphic);
592 
593                     if (rPropMap.supportsProperty(ShapeProperty::FillBitmapName) &&
594                         rPropMap.setProperty(ShapeProperty::FillBitmapName, xGraphic))
595                     {
596                         eFillStyle = FillStyle_BITMAP;
597                     }
598                     else if (rPropMap.setProperty(ShapeProperty::FillBitmap, xGraphic))
599                     {
600                         eFillStyle = FillStyle_BITMAP;
601                     }
602                 }
603 
604                 // set other bitmap properties, if bitmap has been inserted into the map
605                 if( eFillStyle == FillStyle_BITMAP )
606                 {
607                     // bitmap mode (single, repeat, stretch)
608                     BitmapMode eBitmapMode = lclGetBitmapMode( maBlipProps.moBitmapMode.value_or( XML_TOKEN_INVALID ) );
609 
610                     // additional settings for repeated bitmap
611                     if( eBitmapMode == BitmapMode_REPEAT )
612                     {
613                         // anchor position inside bitmap
614                         RectanglePoint eRectPoint = lclGetRectanglePoint( maBlipProps.moTileAlign.value_or( XML_tl ) );
615                         rPropMap.setProperty( ShapeProperty::FillBitmapRectanglePoint, eRectPoint );
616 
617                         awt::Size aOriginalSize = lclGetOriginalSize(rGraphicHelper, maBlipProps.mxFillGraphic);
618                         if( (aOriginalSize.Width > 0) && (aOriginalSize.Height > 0) )
619                         {
620                             // size of one bitmap tile (given as 1/1000 percent of bitmap size), convert to 1/100 mm
621                             double fScaleX = maBlipProps.moTileScaleX.value_or( MAX_PERCENT ) / static_cast< double >( MAX_PERCENT );
622                             sal_Int32 nFillBmpSizeX = getLimitedValue< sal_Int32, double >( aOriginalSize.Width * fScaleX, 1, SAL_MAX_INT32 );
623                             rPropMap.setProperty( ShapeProperty::FillBitmapSizeX, nFillBmpSizeX );
624                             double fScaleY = maBlipProps.moTileScaleY.value_or( MAX_PERCENT ) / static_cast< double >( MAX_PERCENT );
625                             sal_Int32 nFillBmpSizeY = getLimitedValue< sal_Int32, double >( aOriginalSize.Height * fScaleY, 1, SAL_MAX_INT32 );
626                             rPropMap.setProperty( ShapeProperty::FillBitmapSizeY, nFillBmpSizeY );
627 
628                             awt::Size aBmpSize(nFillBmpSizeX, nFillBmpSizeY);
629                             // offset of the first bitmap tile (given as EMUs), convert to percent
630                             sal_Int16 nTileOffsetX = getDoubleIntervalValue< sal_Int16 >(std::round(maBlipProps.moTileOffsetX.value_or( 0 ) / 3.6 / aBmpSize.Width), 0, 100 );
631                             rPropMap.setProperty( ShapeProperty::FillBitmapOffsetX, nTileOffsetX );
632                             sal_Int16 nTileOffsetY = getDoubleIntervalValue< sal_Int16 >(std::round(maBlipProps.moTileOffsetY.value_or( 0 ) / 3.6 / aBmpSize.Height), 0, 100 );
633                             rPropMap.setProperty( ShapeProperty::FillBitmapOffsetY, nTileOffsetY );
634                         }
635                     }
636                     else if ( eBitmapMode == BitmapMode_STRETCH && maBlipProps.moFillRect.has_value() )
637                     {
638                         geometry::IntegerRectangle2D aFillRect( maBlipProps.moFillRect.value() );
639                         awt::Size aOriginalSize( rGraphicHelper.getOriginalSize( xGraphic ) );
640                         if ( aOriginalSize.Width && aOriginalSize.Height )
641                         {
642                             text::GraphicCrop aGraphCrop( 0, 0, 0, 0 );
643                             if ( aFillRect.X1 )
644                                 aGraphCrop.Left = o3tl::convert(aFillRect.X1, aOriginalSize.Width, MAX_PERCENT);
645                             if ( aFillRect.Y1 )
646                                 aGraphCrop.Top = o3tl::convert(aFillRect.Y1, aOriginalSize.Height, MAX_PERCENT);
647                             if ( aFillRect.X2 )
648                                 aGraphCrop.Right = o3tl::convert(aFillRect.X2, aOriginalSize.Width, MAX_PERCENT);
649                             if ( aFillRect.Y2 )
650                                 aGraphCrop.Bottom = o3tl::convert(aFillRect.Y2, aOriginalSize.Height, MAX_PERCENT);
651 
652                             bool bHasCropValues = aGraphCrop.Left != 0 || aGraphCrop.Right !=0 || aGraphCrop.Top != 0 || aGraphCrop.Bottom != 0;
653                             // Negative GraphicCrop values means "crop" here.
654                             bool bNeedCrop = aGraphCrop.Left <= 0 && aGraphCrop.Right <= 0 && aGraphCrop.Top <= 0 && aGraphCrop.Bottom <= 0;
655 
656                             if (bHasCropValues)
657                             {
658                                 if (bIsCustomShape && bNeedCrop)
659                                 {
660                                     // Physically crop the image
661                                     // In this case, don't set the PROP_GraphicCrop because that
662                                     // would lead to applying the crop twice after roundtrip
663                                     xGraphic = lclCropGraphic(xGraphic, CropQuotientsFromFillRect(aFillRect));
664                                     if (rPropMap.supportsProperty(ShapeProperty::FillBitmapName))
665                                         rPropMap.setProperty(ShapeProperty::FillBitmapName, xGraphic);
666                                     else
667                                         rPropMap.setProperty(ShapeProperty::FillBitmap, xGraphic);
668                                 }
669                                 else if ((aFillRect.X1 != 0 && aFillRect.X2 != 0
670                                           && aFillRect.X1 != aFillRect.X2)
671                                          || (aFillRect.Y1 != 0 && aFillRect.Y2 != 0
672                                              && aFillRect.Y1 != aFillRect.Y2))
673                                 {
674                                     rPropMap.setProperty(PROP_GraphicCrop, aGraphCrop);
675                                 }
676                                 else
677                                 {
678                                     double nL = aFillRect.X1 / static_cast<double>(MAX_PERCENT);
679                                     double nT = aFillRect.Y1 / static_cast<double>(MAX_PERCENT);
680                                     double nR = aFillRect.X2 / static_cast<double>(MAX_PERCENT);
681                                     double nB = aFillRect.Y2 / static_cast<double>(MAX_PERCENT);
682 
683                                     sal_Int32 nSizeX;
684                                     if (nL || nR)
685                                         nSizeX = rSize.Width * (1 - (nL + nR));
686                                     else
687                                         nSizeX = rSize.Width;
688                                     rPropMap.setProperty(ShapeProperty::FillBitmapSizeX, nSizeX);
689 
690                                     sal_Int32 nSizeY;
691                                     if (nT || nB)
692                                         nSizeY = rSize.Height * (1 - (nT + nB));
693                                     else
694                                         nSizeY = rSize.Height;
695                                     rPropMap.setProperty(ShapeProperty::FillBitmapSizeY, nSizeY);
696 
697                                     RectanglePoint eRectPoint;
698                                     if (!aFillRect.X1 && aFillRect.X2)
699                                     {
700                                         if (!aFillRect.Y1 && aFillRect.Y2)
701                                             eRectPoint = lclGetRectanglePoint(XML_tl);
702                                         else if (aFillRect.Y1 && !aFillRect.Y2)
703                                             eRectPoint = lclGetRectanglePoint(XML_bl);
704                                         else
705                                             eRectPoint = lclGetRectanglePoint(XML_l);
706                                     }
707                                     else if (aFillRect.X1 && !aFillRect.X2)
708                                     {
709                                         if (!aFillRect.Y1 && aFillRect.Y2)
710                                             eRectPoint = lclGetRectanglePoint(XML_tr);
711                                         else if (aFillRect.Y1 && !aFillRect.Y2)
712                                             eRectPoint = lclGetRectanglePoint(XML_br);
713                                         else
714                                             eRectPoint = lclGetRectanglePoint(XML_r);
715                                     }
716                                     else
717                                     {
718                                         if (!aFillRect.Y1 && aFillRect.Y2)
719                                             eRectPoint = lclGetRectanglePoint(XML_t);
720                                         else if (aFillRect.Y1 && !aFillRect.Y2)
721                                             eRectPoint = lclGetRectanglePoint(XML_b);
722                                         else
723                                             eRectPoint = lclGetRectanglePoint(XML_ctr);
724                                     }
725                                     rPropMap.setProperty(ShapeProperty::FillBitmapRectanglePoint, eRectPoint);
726                                     eBitmapMode = BitmapMode_NO_REPEAT;
727                                 }
728                             }
729                         }
730                     }
731                     rPropMap.setProperty(ShapeProperty::FillBitmapMode, eBitmapMode);
732                 }
733 
734                 if (maBlipProps.moAlphaModFix.has_value())
735                     rPropMap.setProperty(ShapeProperty::FillTransparency, static_cast<sal_Int16>(100 - (maBlipProps.moAlphaModFix.value() / PER_PERCENT)));
736             }
737         break;
738 
739         case XML_pattFill:
740         {
741             if( rPropMap.supportsProperty( ShapeProperty::FillHatch ) )
742             {
743                 Color aColor( maPatternProps.maPattFgColor );
744                 if( aColor.isUsed() && maPatternProps.moPattPreset.has_value() )
745                 {
746                     eFillStyle = FillStyle_HATCH;
747                     rPropMap.setProperty( ShapeProperty::FillHatch, createHatch( maPatternProps.moPattPreset.value(), aColor.getColor( rGraphicHelper, nPhClr ) ) );
748                     if( aColor.hasTransparency() )
749                         rPropMap.setProperty( ShapeProperty::FillTransparency, aColor.getTransparency() );
750 
751                     // Set background color for hatch
752                     if(maPatternProps.maPattBgColor.isUsed())
753                     {
754                         aColor = maPatternProps.maPattBgColor;
755                         rPropMap.setProperty( ShapeProperty::FillBackground, aColor.getTransparency() != 100 );
756                         rPropMap.setProperty( ShapeProperty::FillColor, aColor.getColor( rGraphicHelper, nPhClr ) );
757                     }
758                 }
759                 else if ( maPatternProps.maPattBgColor.isUsed() )
760                 {
761                     aColor = maPatternProps.maPattBgColor;
762                     rPropMap.setProperty( ShapeProperty::FillColor, aColor.getColor( rGraphicHelper, nPhClr ) );
763                     if( aColor.hasTransparency() )
764                         rPropMap.setProperty( ShapeProperty::FillTransparency, aColor.getTransparency() );
765                     eFillStyle = FillStyle_SOLID;
766                 }
767             }
768         }
769         break;
770 
771         case XML_grpFill:
772             // todo
773             eFillStyle = FillStyle_NONE;
774         break;
775     }
776 
777     // set final fill style property
778     rPropMap.setProperty( ShapeProperty::FillStyle, eFillStyle );
779 }
780 
pushToPropMap(PropertyMap & rPropMap,const GraphicHelper & rGraphicHelper,bool bFlipH,bool bFlipV) const781 void GraphicProperties::pushToPropMap( PropertyMap& rPropMap, const GraphicHelper& rGraphicHelper, bool bFlipH, bool bFlipV) const
782 {
783     sal_Int16 nBrightness = getLimitedValue< sal_Int16, sal_Int32 >( maBlipProps.moBrightness.value_or( 0 ) / PER_PERCENT, -100, 100 );
784     sal_Int16 nContrast = getLimitedValue< sal_Int16, sal_Int32 >( maBlipProps.moContrast.value_or( 0 ) / PER_PERCENT, -100, 100 );
785     ColorMode eColorMode = ColorMode_STANDARD;
786 
787     switch( maBlipProps.moColorEffect.value_or( XML_TOKEN_INVALID ) )
788     {
789         case XML_biLevel:   eColorMode = ColorMode_MONO;    break;
790         case XML_grayscl:   eColorMode = ColorMode_GREYS;   break;
791     }
792 
793     if (maBlipProps.mxFillGraphic.is())
794     {
795         // created transformed graphic
796         uno::Reference<graphic::XGraphic> xGraphic = lclCheckAndApplyChangeColorTransform(maBlipProps, maBlipProps.mxFillGraphic, rGraphicHelper, API_RGB_TRANSPARENT);
797         xGraphic = lclCheckAndApplyDuotoneTransform(maBlipProps, xGraphic, rGraphicHelper, API_RGB_TRANSPARENT);
798 
799         if( eColorMode == ColorMode_MONO )
800         {
801             // ColorMode_MONO is the same with MSO's biLevel with 50000 (50%) threshold,
802             // when threshold isn't 50000 bake the effect instead.
803             if( maBlipProps.moBiLevelThreshold != 50000 )
804             {
805                 xGraphic = lclApplyBlackWhiteEffect(maBlipProps, xGraphic);
806                 eColorMode = ColorMode_STANDARD;
807             }
808         }
809 
810         if (eColorMode == ColorMode_STANDARD && nBrightness == 70 && nContrast == -70)
811         {
812             // map MSO 'washout' to our Watermark colormode
813             eColorMode = ColorMode_WATERMARK;
814             nBrightness = 0;
815             nContrast = 0;
816         }
817         else if( nBrightness != 0 && nContrast != 0 )
818         {
819             // MSO uses a different algorithm for contrast+brightness, LO applies contrast before brightness,
820             // while MSO apparently applies half of brightness before contrast and half after. So if only
821             // contrast or brightness need to be altered, the result is the same, but if both are involved,
822             // there's no way to map that, so just force a conversion of the image.
823             xGraphic = applyBrightnessContrast( xGraphic, nBrightness, nContrast );
824             nBrightness = 0;
825             nContrast = 0;
826         }
827 
828         // cropping
829         if ( maBlipProps.moClipRect.has_value() )
830         {
831             geometry::IntegerRectangle2D oClipRect( maBlipProps.moClipRect.value() );
832             awt::Size aOriginalSize( rGraphicHelper.getOriginalSize( xGraphic ) );
833 
834             if (aOriginalSize.Width <= 0 || aOriginalSize.Height <= 0)
835             {
836                 // VectorGraphic Objects need the correct object size for cropping
837                 Graphic aGraphic(xGraphic);
838                 if (aGraphic.getVectorGraphicData())
839                 {
840                     Size aPrefSize = aGraphic.GetPrefSize();
841                     aOriginalSize.Height = static_cast<sal_Int32>(aPrefSize.getHeight());
842                     aOriginalSize.Width = static_cast<sal_Int32>(aPrefSize.getWidth());
843                 }
844             }
845 
846             if (aOriginalSize.Width > 0 && aOriginalSize.Height > 0)
847             {
848                 text::GraphicCrop aGraphCrop( 0, 0, 0, 0 );
849                 if ( oClipRect.X1 )
850                     aGraphCrop.Left = o3tl::convert(oClipRect.X1, aOriginalSize.Width, MAX_PERCENT);
851                 if ( oClipRect.Y1 )
852                     aGraphCrop.Top = o3tl::convert(oClipRect.Y1, aOriginalSize.Height, MAX_PERCENT);
853                 if ( oClipRect.X2 )
854                     aGraphCrop.Right = o3tl::convert(oClipRect.X2, aOriginalSize.Width, MAX_PERCENT);
855                 if ( oClipRect.Y2 )
856                     aGraphCrop.Bottom = o3tl::convert(oClipRect.Y2, aOriginalSize.Height, MAX_PERCENT);
857                 rPropMap.setProperty(PROP_GraphicCrop, aGraphCrop);
858 
859                 if(mbIsCustomShape)
860                 {
861                     // Positive GraphicCrop values means "crop" here.
862                     if (aGraphCrop.Left > 0 || aGraphCrop.Right > 0 || aGraphCrop.Top > 0 || aGraphCrop.Bottom > 0)
863                         xGraphic = lclCropGraphic(xGraphic, CropQuotientsFromSrcRect(oClipRect));
864                 }
865             }
866         }
867 
868         if(mbIsCustomShape)
869         {
870             // it is a cropped graphic.
871             rPropMap.setProperty(PROP_FillStyle, FillStyle_BITMAP);
872             rPropMap.setProperty(PROP_FillBitmapMode, BitmapMode_STRETCH);
873 
874             // It is a bitmap filled and rotated graphic.
875             // When custom shape is rotated, bitmap have to be rotated too.
876             // Only in extruded mode the bitmap is transformed together with the shape
877             if(rPropMap.hasProperty(PROP_RotateAngle) && !mbIsExtruded)
878             {
879                 tools::Long nAngle = rPropMap.getProperty(PROP_RotateAngle).get<tools::Long>();
880                 xGraphic = lclRotateGraphic(xGraphic, Degree10(nAngle/10) );
881             }
882 
883             // We have not core feature that flips graphic in the shape.
884             // Here we are applying flip property to bitmap directly.
885             if((bFlipH || bFlipV) && !mbIsExtruded)
886                 xGraphic = lclMirrorGraphic(xGraphic, bFlipH, bFlipV );
887 
888             if(eColorMode == ColorMode_GREYS)
889                 xGraphic = lclGreysScaleGraphic( xGraphic );
890 
891             rPropMap.setProperty(PROP_FillBitmap, xGraphic);
892         }
893         else
894             rPropMap.setProperty(PROP_Graphic, xGraphic);
895 
896 
897         if ( maBlipProps.moAlphaModFix.has_value() )
898         {
899             rPropMap.setProperty(
900                 mbIsCustomShape ? PROP_FillTransparence : PROP_Transparency,
901                 static_cast<sal_Int16>(100 - (maBlipProps.moAlphaModFix.value() / PER_PERCENT)));
902         }
903     }
904     rPropMap.setProperty(PROP_GraphicColorMode, eColorMode);
905 
906     // brightness and contrast
907     if( nBrightness != 0 )
908         rPropMap.setProperty(PROP_AdjustLuminance, nBrightness);
909     if( nContrast != 0 )
910         rPropMap.setProperty(PROP_AdjustContrast, nContrast);
911 
912     // Media content
913     if (!m_sMediaPackageURL.isEmpty())
914     {
915         rPropMap.setProperty(PROP_MediaURL, m_sMediaPackageURL);
916         if (m_xMediaStream.is())
917             rPropMap.setProperty(PROP_PrivateStream, m_xMediaStream);
918     }
919 }
920 
isEmpty() const921 bool ArtisticEffectProperties::isEmpty() const
922 {
923     return msName.isEmpty();
924 }
925 
getEffect()926 css::beans::PropertyValue ArtisticEffectProperties::getEffect()
927 {
928     css::beans::PropertyValue aRet;
929     if( msName.isEmpty() )
930         return aRet;
931 
932     css::uno::Sequence< css::beans::PropertyValue > aSeq( maAttribs.size() + 1 );
933     auto pSeq = aSeq.getArray();
934     sal_uInt32 i = 0;
935     for (auto const& attrib : maAttribs)
936     {
937         pSeq[i].Name = attrib.first;
938         pSeq[i].Value = attrib.second;
939         i++;
940     }
941 
942     if( mrOleObjectInfo.maEmbeddedData.hasElements() )
943     {
944         css::uno::Sequence< css::beans::PropertyValue > aGraphicSeq{
945             comphelper::makePropertyValue(u"Id"_ustr, mrOleObjectInfo.maProgId),
946             comphelper::makePropertyValue(u"Data"_ustr, mrOleObjectInfo.maEmbeddedData)
947         };
948 
949         pSeq[i].Name = "OriginalGraphic";
950         pSeq[i].Value <<= aGraphicSeq;
951     }
952 
953     aRet.Name = msName;
954     aRet.Value <<= aSeq;
955 
956     return aRet;
957 }
958 
assignUsed(const ArtisticEffectProperties & rSourceProps)959 void ArtisticEffectProperties::assignUsed( const ArtisticEffectProperties& rSourceProps )
960 {
961     if( !rSourceProps.isEmpty() )
962     {
963         msName = rSourceProps.msName;
964         maAttribs = rSourceProps.maAttribs;
965     }
966 }
967 
getEffectString(sal_Int32 nToken)968 OUString ArtisticEffectProperties::getEffectString( sal_Int32 nToken )
969 {
970     switch( nToken )
971     {
972         // effects
973         case OOX_TOKEN( a14, artisticBlur ):                return u"artisticBlur"_ustr;
974         case OOX_TOKEN( a14, artisticCement ):              return u"artisticCement"_ustr;
975         case OOX_TOKEN( a14, artisticChalkSketch ):         return u"artisticChalkSketch"_ustr;
976         case OOX_TOKEN( a14, artisticCrisscrossEtching ):   return u"artisticCrisscrossEtching"_ustr;
977         case OOX_TOKEN( a14, artisticCutout ):              return u"artisticCutout"_ustr;
978         case OOX_TOKEN( a14, artisticFilmGrain ):           return u"artisticFilmGrain"_ustr;
979         case OOX_TOKEN( a14, artisticGlass ):               return u"artisticGlass"_ustr;
980         case OOX_TOKEN( a14, artisticGlowDiffused ):        return u"artisticGlowDiffused"_ustr;
981         case OOX_TOKEN( a14, artisticGlowEdges ):           return u"artisticGlowEdges"_ustr;
982         case OOX_TOKEN( a14, artisticLightScreen ):         return u"artisticLightScreen"_ustr;
983         case OOX_TOKEN( a14, artisticLineDrawing ):         return u"artisticLineDrawing"_ustr;
984         case OOX_TOKEN( a14, artisticMarker ):              return u"artisticMarker"_ustr;
985         case OOX_TOKEN( a14, artisticMosiaicBubbles ):      return u"artisticMosiaicBubbles"_ustr;
986         case OOX_TOKEN( a14, artisticPaintStrokes ):        return u"artisticPaintStrokes"_ustr;
987         case OOX_TOKEN( a14, artisticPaintBrush ):          return u"artisticPaintBrush"_ustr;
988         case OOX_TOKEN( a14, artisticPastelsSmooth ):       return u"artisticPastelsSmooth"_ustr;
989         case OOX_TOKEN( a14, artisticPencilGrayscale ):     return u"artisticPencilGrayscale"_ustr;
990         case OOX_TOKEN( a14, artisticPencilSketch ):        return u"artisticPencilSketch"_ustr;
991         case OOX_TOKEN( a14, artisticPhotocopy ):           return u"artisticPhotocopy"_ustr;
992         case OOX_TOKEN( a14, artisticPlasticWrap ):         return u"artisticPlasticWrap"_ustr;
993         case OOX_TOKEN( a14, artisticTexturizer ):          return u"artisticTexturizer"_ustr;
994         case OOX_TOKEN( a14, artisticWatercolorSponge ):    return u"artisticWatercolorSponge"_ustr;
995         case OOX_TOKEN( a14, brightnessContrast ):          return u"brightnessContrast"_ustr;
996         case OOX_TOKEN( a14, colorTemperature ):            return u"colorTemperature"_ustr;
997         case OOX_TOKEN( a14, saturation ):                  return u"saturation"_ustr;
998         case OOX_TOKEN( a14, sharpenSoften ):               return u"sharpenSoften"_ustr;
999 
1000         // attributes
1001         case XML_visible:           return u"visible"_ustr;
1002         case XML_trans:             return u"trans"_ustr;
1003         case XML_crackSpacing:      return u"crackSpacing"_ustr;
1004         case XML_pressure:          return u"pressure"_ustr;
1005         case XML_numberOfShades:    return u"numberOfShades"_ustr;
1006         case XML_grainSize:         return u"grainSize"_ustr;
1007         case XML_intensity:         return u"intensity"_ustr;
1008         case XML_smoothness:        return u"smoothness"_ustr;
1009         case XML_gridSize:          return u"gridSize"_ustr;
1010         case XML_pencilSize:        return u"pencilSize"_ustr;
1011         case XML_size:              return u"size"_ustr;
1012         case XML_brushSize:         return u"brushSize"_ustr;
1013         case XML_scaling:           return u"scaling"_ustr;
1014         case XML_detail:            return u"detail"_ustr;
1015         case XML_bright:            return u"bright"_ustr;
1016         case XML_contrast:          return u"contrast"_ustr;
1017         case XML_colorTemp:         return u"colorTemp"_ustr;
1018         case XML_sat:               return u"sat"_ustr;
1019         case XML_amount:            return u"amount"_ustr;
1020     }
1021     SAL_WARN( "oox.drawingml", "ArtisticEffectProperties::getEffectString: unexpected token " << nToken );
1022     return OUString();
1023 }
1024 
1025 constexpr auto constEffectTokenForEffectNameMap = frozen::make_unordered_map<std::u16string_view, sal_Int32>(
1026 {
1027     // effects
1028     { u"artisticBlur", XML_artisticBlur },
1029     { u"artisticCement", XML_artisticCement },
1030     { u"artisticChalkSketch", XML_artisticChalkSketch },
1031     { u"artisticCrisscrossEtching", XML_artisticCrisscrossEtching },
1032     { u"artisticCutout", XML_artisticCutout },
1033     { u"artisticFilmGrain", XML_artisticFilmGrain },
1034     { u"artisticGlass", XML_artisticGlass },
1035     { u"artisticGlowDiffused", XML_artisticGlowDiffused },
1036     { u"artisticGlowEdges", XML_artisticGlowEdges },
1037     { u"artisticLightScreen", XML_artisticLightScreen },
1038     { u"artisticLineDrawing", XML_artisticLineDrawing },
1039     { u"artisticMarker", XML_artisticMarker },
1040     { u"artisticMosiaicBubbles", XML_artisticMosiaicBubbles },
1041     { u"artisticPaintStrokes", XML_artisticPaintStrokes },
1042     { u"artisticPaintBrush", XML_artisticPaintBrush },
1043     { u"artisticPastelsSmooth", XML_artisticPastelsSmooth },
1044     { u"artisticPencilGrayscale", XML_artisticPencilGrayscale },
1045     { u"artisticPencilSketch", XML_artisticPencilSketch },
1046     { u"artisticPhotocopy", XML_artisticPhotocopy },
1047     { u"artisticPlasticWrap", XML_artisticPlasticWrap },
1048     { u"artisticTexturizer", XML_artisticTexturizer },
1049     { u"artisticWatercolorSponge", XML_artisticWatercolorSponge },
1050     { u"brightnessContrast", XML_brightnessContrast },
1051     { u"colorTemperature", XML_colorTemperature },
1052     { u"saturation", XML_saturation },
1053     { u"sharpenSoften", XML_sharpenSoften },
1054 
1055     // attributes
1056     { u"visible", XML_visible },
1057     { u"trans", XML_trans },
1058     { u"crackSpacing", XML_crackSpacing },
1059     { u"pressure", XML_pressure },
1060     { u"numberOfShades", XML_numberOfShades },
1061     { u"grainSize", XML_grainSize },
1062     { u"intensity", XML_intensity },
1063     { u"smoothness", XML_smoothness },
1064     { u"gridSize", XML_gridSize },
1065     { u"pencilSize", XML_pencilSize },
1066     { u"size", XML_size },
1067     { u"brushSize", XML_brushSize },
1068     { u"scaling", XML_scaling },
1069     { u"detail", XML_detail },
1070     { u"bright", XML_bright },
1071     { u"contrast", XML_contrast },
1072     { u"colorTemp", XML_colorTemp },
1073     { u"sat", XML_sat },
1074     { u"amount", XML_amount }
1075 });
1076 
getEffectToken(const OUString & sName)1077 sal_Int32 ArtisticEffectProperties::getEffectToken(const OUString& sName)
1078 {
1079     auto const aIterator = constEffectTokenForEffectNameMap.find(sName);
1080 
1081     if (aIterator != constEffectTokenForEffectNameMap.end())
1082         return aIterator->second;
1083 
1084     SAL_WARN( "oox.drawingml", "ArtisticEffectProperties::getEffectToken - unexpected token name: " << sName );
1085     return XML_none;
1086 }
1087 
1088 } // namespace oox
1089 
1090 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1091