Coverage Report

Created: 2026-05-16 09:25

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