Coverage Report

Created: 2026-06-30 11:14

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.28k
{
74
2.28k
    if (aBlipProps.maDuotoneColors[0].isUsed() && aBlipProps.maDuotoneColors[1].isUsed())
75
11
    {
76
11
        ::Color nColor1 = aBlipProps.maDuotoneColors[0].getColor( rGraphicHelper, nPhClr );
77
11
        ::Color nColor2 = aBlipProps.maDuotoneColors[1].getColor( rGraphicHelper, nPhClr );
78
79
11
        uno::Reference<graphic::XGraphicTransformer> xTransformer(aBlipProps.mxFillGraphic, uno::UNO_QUERY);
80
11
        if (xTransformer.is())
81
11
            return xTransformer->applyDuotone(xGraphic, sal_Int32(nColor1), sal_Int32(nColor2));
82
11
    }
83
2.27k
    return xGraphic;
84
2.28k
}
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
2.25k
{
236
2.25k
    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
2.25k
    return xGraphic;
282
2.25k
}
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
30
{
294
30
    OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
295
30
    switch( nToken )
296
30
    {
297
30
        case XML_tile:      return BitmapMode_REPEAT;
298
0
        case XML_stretch:   return BitmapMode_STRETCH;
299
30
    }
300
301
    // tdf#128596 Default value is XML_tile for MSO.
302
0
    return BitmapMode_REPEAT;
303
30
}
304
305
RectanglePoint lclGetRectanglePoint( sal_Int32 nToken )
306
30
{
307
30
    OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
308
30
    switch( nToken )
309
30
    {
310
30
        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
30
    }
320
0
    return RectanglePoint_LEFT_TOP;
321
30
}
322
323
awt::Size lclGetOriginalSize( const GraphicHelper& rGraphicHelper, const Reference< XGraphic >& rxGraphic )
324
30
{
325
30
    awt::Size aSizeHmm( 0, 0 );
326
30
    try
327
30
    {
328
30
        Reference< beans::XPropertySet > xGraphicPropertySet( rxGraphic, UNO_QUERY_THROW );
329
30
        if( xGraphicPropertySet->getPropertyValue( u"Size100thMM"_ustr ) >>= aSizeHmm )
330
30
        {
331
30
            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
30
        }
338
30
    }
339
30
    catch( Exception& )
340
30
    {
341
0
    }
342
30
    return aSizeHmm;
343
30
}
344
345
} // namespace
346
347
void GradientFillProperties::assignUsed( const GradientFillProperties& rSourceProps )
348
10.1M
{
349
10.1M
    if( !rSourceProps.maGradientStops.empty() )
350
2.21k
        maGradientStops = rSourceProps.maGradientStops;
351
10.1M
    assignIfUsed( moFillToRect, rSourceProps.moFillToRect );
352
10.1M
    assignIfUsed( moTileRect, rSourceProps.moTileRect );
353
10.1M
    assignIfUsed( moGradientPath, rSourceProps.moGradientPath );
354
10.1M
    assignIfUsed( moShadeAngle, rSourceProps.moShadeAngle );
355
10.1M
    assignIfUsed( moShadeFlip, rSourceProps.moShadeFlip );
356
10.1M
    assignIfUsed( moShadeScaled, rSourceProps.moShadeScaled );
357
10.1M
    assignIfUsed( moRotateWithShape, rSourceProps.moRotateWithShape );
358
10.1M
}
359
360
void PatternFillProperties::assignUsed( const PatternFillProperties& rSourceProps )
361
10.1M
{
362
10.1M
    maPattFgColor.assignIfUsed( rSourceProps.maPattFgColor );
363
10.1M
    maPattBgColor.assignIfUsed( rSourceProps.maPattBgColor );
364
10.1M
    assignIfUsed( moPattPreset, rSourceProps.moPattPreset );
365
10.1M
}
366
367
void BlipFillProperties::assignUsed( const BlipFillProperties& rSourceProps )
368
10.1M
{
369
10.1M
    if(rSourceProps.mxFillGraphic.is())
370
16
        mxFillGraphic = rSourceProps.mxFillGraphic;
371
10.1M
    assignIfUsed( moBitmapMode, rSourceProps.moBitmapMode );
372
10.1M
    assignIfUsed( moFillRect, rSourceProps.moFillRect );
373
10.1M
    assignIfUsed( moTileOffsetX, rSourceProps.moTileOffsetX );
374
10.1M
    assignIfUsed( moTileOffsetY, rSourceProps.moTileOffsetY );
375
10.1M
    assignIfUsed( moTileScaleX, rSourceProps.moTileScaleX );
376
10.1M
    assignIfUsed( moTileScaleY, rSourceProps.moTileScaleY );
377
10.1M
    assignIfUsed( moTileAlign, rSourceProps.moTileAlign );
378
10.1M
    assignIfUsed( moTileFlip, rSourceProps.moTileFlip );
379
10.1M
    assignIfUsed( moRotateWithShape, rSourceProps.moRotateWithShape );
380
10.1M
    assignIfUsed( moColorEffect, rSourceProps.moColorEffect );
381
10.1M
    assignIfUsed( moBrightness, rSourceProps.moBrightness );
382
10.1M
    assignIfUsed( moContrast, rSourceProps.moContrast );
383
10.1M
    assignIfUsed( moBiLevelThreshold, rSourceProps.moBiLevelThreshold );
384
10.1M
    maColorChangeFrom.assignIfUsed( rSourceProps.maColorChangeFrom );
385
10.1M
    maColorChangeTo.assignIfUsed( rSourceProps.maColorChangeTo );
386
10.1M
    maDuotoneColors[0].assignIfUsed( rSourceProps.maDuotoneColors[0] );
387
10.1M
    maDuotoneColors[1].assignIfUsed( rSourceProps.maDuotoneColors[1] );
388
10.1M
    maEffect.assignUsed( rSourceProps.maEffect );
389
10.1M
    assignIfUsed(moAlphaModFix, rSourceProps.moAlphaModFix);
390
10.1M
}
391
392
void FillProperties::assignUsed( const FillProperties& rSourceProps )
393
10.1M
{
394
10.1M
    assignIfUsed( moFillType, rSourceProps.moFillType );
395
10.1M
    maFillColor.assignIfUsed( rSourceProps.maFillColor );
396
10.1M
    assignIfUsed( moUseBgFill, rSourceProps.moUseBgFill );
397
10.1M
    maGradientProps.assignUsed( rSourceProps.maGradientProps );
398
10.1M
    maPatternProps.assignUsed( rSourceProps.maPatternProps );
399
10.1M
    maBlipProps.assignUsed( rSourceProps.maBlipProps );
400
10.1M
}
401
402
Color FillProperties::getBestSolidColor() const
403
392k
{
404
392k
    Color aSolidColor;
405
392k
    if( moFillType.has_value() ) switch( moFillType.value() )
406
388k
    {
407
324k
        case XML_solidFill:
408
324k
            aSolidColor = maFillColor;
409
324k
        break;
410
235
        case XML_gradFill:
411
235
            if( !maGradientProps.maGradientStops.empty() )
412
235
            {
413
235
                GradientFillProperties::GradientStopMap::const_iterator aGradientStop =
414
235
                    maGradientProps.maGradientStops.begin();
415
235
                if (maGradientProps.maGradientStops.size() > 2)
416
235
                    ++aGradientStop;
417
235
                aSolidColor = aGradientStop->second;
418
235
            }
419
235
        break;
420
0
        case XML_pattFill:
421
0
            aSolidColor = maPatternProps.maPattBgColor.isUsed() ? maPatternProps.maPattBgColor : maPatternProps.maPattFgColor;
422
0
        break;
423
388k
    }
424
392k
    return aSolidColor;
425
392k
}
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
80.5k
{
432
80.5k
    if( !moFillType.has_value() )
433
0
        return;
434
435
80.5k
    FillStyle eFillStyle = FillStyle_NONE;
436
80.5k
    OSL_ASSERT((moFillType.value() & sal_Int32(0xFFFF0000))==0);
437
80.5k
    switch( moFillType.value() )
438
80.5k
    {
439
61.3k
        case XML_noFill:
440
61.3k
        {
441
61.3k
            eFillStyle = FillStyle_NONE;
442
61.3k
            rPropMap.setProperty(ShapeProperty::FillUseSlideBackground, moUseBgFill.value_or(false));
443
61.3k
        }
444
61.3k
        break;
445
446
17.1k
        case XML_solidFill:
447
17.1k
            if( maFillColor.isUsed() )
448
17.1k
            {
449
17.1k
                ::Color aFillColor = maFillColor.getColor(rGraphicHelper, nPhClr);
450
17.1k
                rPropMap.setProperty(ShapeProperty::FillColor, aFillColor);
451
17.1k
                if( maFillColor.hasTransparency() )
452
362
                    rPropMap.setProperty( ShapeProperty::FillTransparency, maFillColor.getTransparency() );
453
454
17.1k
                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.1k
                if (aFillColor == nPhClr && !moFillType.has_value())
459
0
                {
460
0
                    aComplexColor.setThemeColor(model::convertToThemeColorType(nPhClrTheme));
461
0
                }
462
17.1k
                else
463
17.1k
                {
464
17.1k
                    aComplexColor = maFillColor.createComplexColor(rGraphicHelper, nPhClrTheme);
465
17.1k
                    OUString sColorName = getBestSolidColor().getSchemeColorName();
466
17.1k
                    sal_Int32 nToken = Color::getColorMapToken(sColorName);
467
17.1k
                    if (nToken != -1)
468
2.94k
                    {
469
2.94k
                        rGraphicHelper.getSchemeColorToken(nToken);
470
2.94k
                        model::ThemeColorType eThemeColorType = schemeTokenToThemeColorType(nToken);
471
2.94k
                        aComplexColor.setThemeColor(eThemeColorType);
472
2.94k
                    }
473
17.1k
                }
474
17.1k
                rPropMap.setProperty(PROP_FillComplexColor, model::color::createXComplexColor(aComplexColor));
475
476
17.1k
                eFillStyle = FillStyle_SOLID;
477
17.1k
            }
478
17.1k
        break;
479
480
2.02k
        case XML_gradFill:
481
            // do not create gradient struct if property is not supported...
482
2.02k
            if( rPropMap.supportsProperty( ShapeProperty::FillGradient ) )
483
2.02k
            {
484
                // prepare ColorStops
485
2.02k
                basegfx::BColorStops aColorStops;
486
2.02k
                basegfx::BColorStops aTransparencyStops;
487
2.02k
                bool bContainsTransparency(false);
488
489
                // convert to BColorStops, check for contained transparency
490
2.02k
                for (const auto& rCandidate : maGradientProps.maGradientStops)
491
4.32k
                {
492
                    // TODO: oox::drawingml::Color does not support transforms
493
                    // (lumMod, etc). So those get lost at this point.
494
4.32k
                    const ::Color aColor(rCandidate.second.getColor(rGraphicHelper, nPhClr));
495
4.32k
                    aColorStops.addStop(rCandidate.first, aColor.getBColor());
496
4.32k
                    bContainsTransparency = bContainsTransparency || rCandidate.second.hasTransparency();
497
4.32k
                }
498
499
                // if we have transparency, convert to BColorStops
500
2.02k
                if (bContainsTransparency)
501
6
                {
502
6
                    for (const auto& rCandidate : maGradientProps.maGradientStops)
503
12
                    {
504
12
                        const double fTrans(rCandidate.second.getTransparency() * (1.0/100.0));
505
12
                        aTransparencyStops.addStop(rCandidate.first, basegfx::BColor(fTrans, fTrans, fTrans));
506
12
                    }
507
6
                }
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.02k
                basegfx::BGradient aGradient(
516
2.02k
                    aColorStops,
517
2.02k
                    awt::GradientStyle_LINEAR,
518
2.02k
                    Degree10(900),
519
2.02k
                    0,  // border
520
2.02k
                    0,  // OfsX -> 0, not 50 (!)
521
2.02k
                    0); // OfsY -> 0, not 50 (!)
522
523
                // "rotate with shape" set to false -> do not rotate
524
2.02k
                if (!maGradientProps.moRotateWithShape.value_or(true))
525
21
                {
526
21
                    nShapeRotation = 0;
527
21
                }
528
529
2.02k
                if (maGradientProps.moGradientPath.has_value())
530
38
                {
531
38
                    IntegerRectangle2D aFillToRect = maGradientProps.moFillToRect.value_or( IntegerRectangle2D( 0, 0, MAX_PERCENT, MAX_PERCENT ) );
532
38
                    sal_Int32 nCenterX = (MAX_PERCENT + aFillToRect.X1 - aFillToRect.X2) / 2;
533
38
                    aGradient.SetXOffset(getLimitedValue<sal_Int16, sal_Int32>(
534
38
                        nCenterX / PER_PERCENT, 0, 100));
535
38
                    sal_Int32 nCenterY = (MAX_PERCENT + aFillToRect.Y1 - aFillToRect.Y2) / 2;
536
38
                    aGradient.SetYOffset(getLimitedValue<sal_Int16, sal_Int32>(
537
38
                        nCenterY / PER_PERCENT, 0, 100));
538
539
                    // FIXME tdf#166140: Size of gradient is smaller than in MSO
540
38
                    if( maGradientProps.moGradientPath.value() == XML_circle )
541
17
                    {
542
17
                        aGradient.SetGradientStyle(awt::GradientStyle_RADIAL);
543
17
                    }
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
38
                    aColorStops.reverseColorStops();
551
38
                    aGradient.SetColorStops(aColorStops);
552
38
                    aTransparencyStops.reverseColorStops();
553
38
                }
554
1.99k
                else if (!maGradientProps.maGradientStops.empty())
555
1.99k
                {
556
                    // aGradient.SetGradientStyle(awt::GradientStyle_LINEAR);
557
1.99k
                    sal_Int32 nShadeAngle(maGradientProps.moShadeAngle.value_or( 0 ));
558
                    // Adjust for flips
559
1.99k
                    if ( bFlipH )
560
4
                        nShadeAngle = 180*60000 - nShadeAngle;
561
1.99k
                    if ( bFlipV )
562
4
                        nShadeAngle = -nShadeAngle;
563
1.99k
                    const sal_Int32 nDmlAngle = nShadeAngle + nShapeRotation;
564
565
                    // convert DrawingML angle (in 1/60000 degrees) to API angle (in 1/10 degrees)
566
1.99k
                    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.99k
                    if (!bContainsTransparency)
572
1.98k
                        aGradient.tryToConvertToAxial();
573
1.99k
                }
574
575
2.02k
                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.02k
                if (rPropMap.setProperty(ShapeProperty::FillGradient, model::gradient::createUnoGradient2(aGradient)))
584
2.02k
                {
585
2.02k
                    eFillStyle = FillStyle_GRADIENT;
586
2.02k
                }
587
588
                // push gradient transparency to property map if it exists
589
2.02k
                if (!aTransparencyStops.empty())
590
6
                {
591
6
                    aGradient.SetColorStops(aTransparencyStops);
592
6
                    rPropMap.setProperty(ShapeProperty::GradientTransparency, model::gradient::createUnoGradient2(aGradient));
593
6
                }
594
2.02k
            }
595
2.02k
        break;
596
597
33
        case XML_blipFill:
598
            // do not start complex graphic transformation if property is not supported...
599
33
            if (maBlipProps.mxFillGraphic.is() && rPropMap.supportsProperty(ShapeProperty::FillBitmap))
600
30
            {
601
30
                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
30
                if (xGraphic.is())
605
30
                {
606
30
                    if (maBlipProps.moColorEffect.value_or(XML_TOKEN_INVALID) == XML_grayscl)
607
0
                        xGraphic = lclGreysScaleGraphic(xGraphic);
608
609
30
                    if (rPropMap.supportsProperty(ShapeProperty::FillBitmapName) &&
610
30
                        rPropMap.setProperty(ShapeProperty::FillBitmapName, xGraphic))
611
30
                    {
612
30
                        eFillStyle = FillStyle_BITMAP;
613
30
                    }
614
0
                    else if (rPropMap.setProperty(ShapeProperty::FillBitmap, xGraphic))
615
0
                    {
616
0
                        eFillStyle = FillStyle_BITMAP;
617
0
                    }
618
30
                }
619
620
                // set other bitmap properties, if bitmap has been inserted into the map
621
30
                if( eFillStyle == FillStyle_BITMAP )
622
30
                {
623
                    // bitmap mode (single, repeat, stretch)
624
30
                    BitmapMode eBitmapMode = lclGetBitmapMode( maBlipProps.moBitmapMode.value_or( XML_TOKEN_INVALID ) );
625
626
                    // additional settings for repeated bitmap
627
30
                    if( eBitmapMode == BitmapMode_REPEAT )
628
30
                    {
629
                        // anchor position inside bitmap
630
30
                        RectanglePoint eRectPoint = lclGetRectanglePoint( maBlipProps.moTileAlign.value_or( XML_tl ) );
631
30
                        rPropMap.setProperty( ShapeProperty::FillBitmapRectanglePoint, eRectPoint );
632
633
30
                        awt::Size aOriginalSize = lclGetOriginalSize(rGraphicHelper, maBlipProps.mxFillGraphic);
634
30
                        if( (aOriginalSize.Width > 0) && (aOriginalSize.Height > 0) )
635
30
                        {
636
                            // size of one bitmap tile (given as 1/1000 percent of bitmap size), convert to 1/100 mm
637
30
                            double fScaleX = maBlipProps.moTileScaleX.value_or( MAX_PERCENT ) / static_cast< double >( MAX_PERCENT );
638
30
                            sal_Int32 nFillBmpSizeX = getLimitedValue< sal_Int32, double >( aOriginalSize.Width * fScaleX, 1, SAL_MAX_INT32 );
639
30
                            rPropMap.setProperty( ShapeProperty::FillBitmapSizeX, nFillBmpSizeX );
640
30
                            double fScaleY = maBlipProps.moTileScaleY.value_or( MAX_PERCENT ) / static_cast< double >( MAX_PERCENT );
641
30
                            sal_Int32 nFillBmpSizeY = getLimitedValue< sal_Int32, double >( aOriginalSize.Height * fScaleY, 1, SAL_MAX_INT32 );
642
30
                            rPropMap.setProperty( ShapeProperty::FillBitmapSizeY, nFillBmpSizeY );
643
644
30
                            awt::Size aBmpSize(nFillBmpSizeX, nFillBmpSizeY);
645
                            // offset of the first bitmap tile (given as EMUs), convert to percent
646
30
                            sal_Int16 nTileOffsetX = getDoubleIntervalValue< sal_Int16 >(std::round(maBlipProps.moTileOffsetX.value_or( 0 ) / 3.6 / aBmpSize.Width), 0, 100 );
647
30
                            rPropMap.setProperty( ShapeProperty::FillBitmapOffsetX, nTileOffsetX );
648
30
                            sal_Int16 nTileOffsetY = getDoubleIntervalValue< sal_Int16 >(std::round(maBlipProps.moTileOffsetY.value_or( 0 ) / 3.6 / aBmpSize.Height), 0, 100 );
649
30
                            rPropMap.setProperty( ShapeProperty::FillBitmapOffsetY, nTileOffsetY );
650
30
                        }
651
30
                    }
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
30
                    rPropMap.setProperty(ShapeProperty::FillBitmapMode, eBitmapMode);
748
30
                }
749
750
30
                if (maBlipProps.moAlphaModFix.has_value())
751
0
                    rPropMap.setProperty(ShapeProperty::FillTransparency, static_cast<sal_Int16>(100 - (maBlipProps.moAlphaModFix.value() / PER_PERCENT)));
752
30
            }
753
33
        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
80.5k
    }
792
793
    // set final fill style property
794
80.5k
    rPropMap.setProperty( ShapeProperty::FillStyle, eFillStyle );
795
80.5k
}
796
797
void GraphicProperties::pushToPropMap( PropertyMap& rPropMap, const GraphicHelper& rGraphicHelper, bool bFlipH, bool bFlipV) const
798
23.6k
{
799
23.6k
    sal_Int16 nBrightness = getLimitedValue< sal_Int16, sal_Int32 >( maBlipProps.moBrightness.value_or( 0 ) / PER_PERCENT, -100, 100 );
800
23.6k
    sal_Int16 nContrast = getLimitedValue< sal_Int16, sal_Int32 >( maBlipProps.moContrast.value_or( 0 ) / PER_PERCENT, -100, 100 );
801
23.6k
    ColorMode eColorMode = ColorMode_STANDARD;
802
803
23.6k
    switch( maBlipProps.moColorEffect.value_or( XML_TOKEN_INVALID ) )
804
23.6k
    {
805
39
        case XML_biLevel:   eColorMode = ColorMode_MONO;    break;
806
3
        case XML_grayscl:   eColorMode = ColorMode_GREYS;   break;
807
23.6k
    }
808
809
23.6k
    if (maBlipProps.mxFillGraphic.is())
810
2.25k
    {
811
        // created transformed graphic
812
2.25k
        uno::Reference<graphic::XGraphic> xGraphic = lclCheckAndApplyChangeColorTransform(maBlipProps, maBlipProps.mxFillGraphic, rGraphicHelper, API_RGB_TRANSPARENT);
813
2.25k
        xGraphic = lclCheckAndApplyDuotoneTransform(maBlipProps, xGraphic, rGraphicHelper, API_RGB_TRANSPARENT);
814
815
2.25k
        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
2.25k
        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
2.25k
        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
2.25k
        if ( maBlipProps.moClipRect.has_value() )
846
1.15k
        {
847
1.15k
            geometry::IntegerRectangle2D oClipRect( maBlipProps.moClipRect.value() );
848
1.15k
            awt::Size aOriginalSize( rGraphicHelper.getOriginalSize( xGraphic ) );
849
850
1.15k
            if (aOriginalSize.Width <= 0 || aOriginalSize.Height <= 0)
851
217
            {
852
                // VectorGraphic Objects need the correct object size for cropping
853
217
                Graphic aGraphic(xGraphic);
854
217
                if (aGraphic.getVectorGraphicData())
855
13
                {
856
13
                    Size aPrefSize = aGraphic.GetPrefSize();
857
13
                    aOriginalSize.Height = static_cast<sal_Int32>(aPrefSize.getHeight());
858
13
                    aOriginalSize.Width = static_cast<sal_Int32>(aPrefSize.getWidth());
859
13
                }
860
217
            }
861
862
1.15k
            if (aOriginalSize.Width > 0 && aOriginalSize.Height > 0)
863
951
            {
864
951
                text::GraphicCrop aGraphCrop( 0, 0, 0, 0 );
865
951
                if ( oClipRect.X1 )
866
0
                    aGraphCrop.Left = o3tl::convert(oClipRect.X1, aOriginalSize.Width, MAX_PERCENT);
867
951
                if ( oClipRect.Y1 )
868
0
                    aGraphCrop.Top = o3tl::convert(oClipRect.Y1, aOriginalSize.Height, MAX_PERCENT);
869
951
                if ( oClipRect.X2 )
870
1
                    aGraphCrop.Right = o3tl::convert(oClipRect.X2, aOriginalSize.Width, MAX_PERCENT);
871
951
                if ( oClipRect.Y2 )
872
2
                    aGraphCrop.Bottom = o3tl::convert(oClipRect.Y2, aOriginalSize.Height, MAX_PERCENT);
873
951
                rPropMap.setProperty(PROP_GraphicCrop, aGraphCrop);
874
875
951
                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
951
            }
882
1.15k
        }
883
884
2.25k
        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
2.25k
        else
910
2.25k
            rPropMap.setProperty(PROP_Graphic, xGraphic);
911
912
913
2.25k
        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
2.25k
    }
920
23.6k
    rPropMap.setProperty(PROP_GraphicColorMode, eColorMode);
921
922
    // brightness and contrast
923
23.6k
    if( nBrightness != 0 )
924
1
        rPropMap.setProperty(PROP_AdjustLuminance, nBrightness);
925
23.6k
    if( nContrast != 0 )
926
1
        rPropMap.setProperty(PROP_AdjustContrast, nContrast);
927
928
    // Media content
929
23.6k
    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
23.6k
    if (!m_sMediaMimeType.isEmpty())
937
0
    {
938
0
        rPropMap.setProperty(PROP_MediaMimeType, m_sMediaMimeType);
939
0
    }
940
23.6k
}
941
942
bool ArtisticEffectProperties::isEmpty() const
943
10.1M
{
944
10.1M
    return msName.isEmpty();
945
10.1M
}
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
10.1M
{
982
10.1M
    if( !rSourceProps.isEmpty() )
983
0
    {
984
0
        msName = rSourceProps.msName;
985
0
        maAttribs = rSourceProps.maAttribs;
986
0
    }
987
10.1M
}
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: */