Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/customshapes/EnhancedCustomShapeEngine.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 <com/sun/star/uno/Reference.h>
21
#include <com/sun/star/uno/XComponentContext.hpp>
22
#include <com/sun/star/awt/Rectangle.hpp>
23
#include <com/sun/star/beans/PropertyValue.hpp>
24
#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
25
#include <com/sun/star/lang/XInitialization.hpp>
26
#include <com/sun/star/lang/XServiceInfo.hpp>
27
#include <com/sun/star/drawing/XCustomShapeEngine.hpp>
28
#include <svx/EnhancedCustomShape2d.hxx>
29
#include <EnhancedCustomShapeEngine.hxx>
30
#include "EnhancedCustomShape3d.hxx"
31
#include "EnhancedCustomShapeFontWork.hxx"
32
#include "EnhancedCustomShapeHandle.hxx"
33
#include <svx/unoshape.hxx>
34
#include <svx/unopage.hxx>
35
#include <svx/svdobj.hxx>
36
#include <svx/svdoashp.hxx>
37
#include <svx/svdogrp.hxx>
38
#include <editeng/outlobj.hxx>
39
#include <svl/itemset.hxx>
40
#include <svx/svdopath.hxx>
41
#include <svx/svdpage.hxx>
42
#include <svx/svditer.hxx>
43
#include <svx/xfillit0.hxx>
44
#include <svx/xlineit0.hxx>
45
#include <basegfx/polygon/b2dpolypolygontools.hxx>
46
#include <com/sun/star/document/XActionLockable.hpp>
47
#include <cppuhelper/implbase.hxx>
48
#include <cppuhelper/supportsservice.hxx>
49
50
using namespace css;
51
using namespace css::uno;
52
53
class SdrObject;
54
class SdrObjCustomShape;
55
56
EnhancedCustomShapeEngine::EnhancedCustomShapeEngine(const css::uno::Sequence< css::uno::Any >& aArguments)
57
0
    : mpCustomShape(nullptr)
58
0
    , mbForceGroupWithText(false)
59
0
{
60
0
    for (const css::uno::Any& rArg : aArguments)
61
0
    {
62
0
        beans::PropertyValue aProp;
63
0
        if (rArg >>= aProp)
64
0
        {
65
0
            if ( aProp.Name == "CustomShape" )
66
0
            {
67
0
                css::uno::Reference<css::drawing::XShape> xShape;
68
0
                aProp.Value >>= xShape;
69
                // the only two subclasses of SdrObject we see here are SdrObjCustomShape and SwDrawVirtObj
70
                // and we only return useful data for SdrObjCustomShape
71
0
                mpCustomShape = dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape));
72
0
            }
73
0
            else if ( aProp.Name == "ForceGroupWithText" )
74
0
                aProp.Value >>= mbForceGroupWithText;
75
0
            else
76
0
                assert(false);
77
0
        }
78
0
        else
79
0
            assert(false);
80
0
    }
81
0
}
82
83
EnhancedCustomShapeEngine::EnhancedCustomShapeEngine(SdrObjCustomShape& rShape)
84
346k
    : mpCustomShape(&rShape)
85
346k
    , mbForceGroupWithText(false)
86
346k
{
87
346k
}
88
89
// XInterface
90
void SAL_CALL EnhancedCustomShapeEngine::acquire() noexcept
91
804k
{
92
804k
    OWeakObject::acquire();
93
804k
}
94
void SAL_CALL EnhancedCustomShapeEngine::release() noexcept
95
804k
{
96
804k
    OWeakObject::release();
97
804k
}
98
99
// XServiceInfo
100
OUString SAL_CALL EnhancedCustomShapeEngine::getImplementationName()
101
0
{
102
0
    return u"com.sun.star.drawing.EnhancedCustomShapeEngine"_ustr;
103
0
}
104
sal_Bool SAL_CALL EnhancedCustomShapeEngine::supportsService( const OUString& rServiceName )
105
0
{
106
0
    return cppu::supportsService(this, rServiceName);
107
0
}
108
Sequence< OUString > SAL_CALL EnhancedCustomShapeEngine::getSupportedServiceNames()
109
0
{
110
0
    return { u"com.sun.star.drawing.CustomShapeEngine"_ustr };
111
0
}
112
113
// XCustomShapeEngine
114
rtl::Reference<SdrObject> EnhancedCustomShapeEngine::ImplForceGroupWithText(
115
    SdrObjCustomShape& rSdrObjCustomShape,
116
    SdrObject* pRenderedShape1)
117
0
{
118
0
    rtl::Reference<SdrObject> pRenderedShape = pRenderedShape1;
119
0
    const bool bHasText(rSdrObjCustomShape.HasText());
120
121
0
    if ( pRenderedShape || bHasText )
122
0
    {
123
        // applying shadow
124
0
        const SdrObject* pShadowGeometry(rSdrObjCustomShape.GetSdrObjectShadowFromCustomShape());
125
126
0
        if ( pShadowGeometry )
127
0
        {
128
0
            if ( pRenderedShape )
129
0
            {
130
0
                if ( dynamic_cast<const SdrObjGroup*>( pRenderedShape.get() ) ==  nullptr )
131
0
                {
132
0
                    auto pTmp = std::move(pRenderedShape);
133
0
                    pRenderedShape = new SdrObjGroup(rSdrObjCustomShape.getSdrModelFromSdrObject());
134
0
                    static_cast<SdrObjGroup*>(pRenderedShape.get())->GetSubList()->NbcInsertObject( pTmp.get() );
135
0
                }
136
137
0
                static_cast<SdrObjGroup*>(pRenderedShape.get())->GetSubList()->NbcInsertObject(
138
0
                    pShadowGeometry->CloneSdrObject(pShadowGeometry->getSdrModelFromSdrObject()).get(),
139
0
                    0);
140
0
            }
141
0
            else
142
0
            {
143
0
                pRenderedShape = pShadowGeometry->CloneSdrObject(pShadowGeometry->getSdrModelFromSdrObject());
144
0
            }
145
0
        }
146
147
        // apply text
148
0
        if ( bHasText )
149
0
        {
150
            // #i37011# also create a text object and add at rPos + 1
151
0
            rtl::Reference<SdrObject> pTextObj( SdrObjFactory::MakeNewObject(
152
0
                rSdrObjCustomShape.getSdrModelFromSdrObject(),
153
0
                rSdrObjCustomShape.GetObjInventor(),
154
0
                SdrObjKind::Text) );
155
156
            // Copy text content
157
0
            OutlinerParaObject* pParaObj(rSdrObjCustomShape.GetOutlinerParaObject());
158
159
0
            if( pParaObj )
160
0
                pTextObj->NbcSetOutlinerParaObject( *pParaObj );
161
162
            // copy all attributes
163
0
            SfxItemSet aTargetItemSet(rSdrObjCustomShape.GetMergedItemSet());
164
165
            // clear fill and line style
166
0
            aTargetItemSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
167
0
            aTargetItemSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
168
169
            // get the text bounds and set at text object
170
0
            tools::Rectangle aTextBounds(rSdrObjCustomShape.GetSnapRect());
171
0
            EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
172
0
            aTextBounds = aCustomShape2d.GetTextRect();
173
174
0
            pTextObj->SetSnapRect( aTextBounds );
175
176
            // if rotated, copy GeoStat, too.
177
0
            const GeoStat& rSourceGeo(rSdrObjCustomShape.GetGeoStat());
178
0
            if ( rSourceGeo.m_nRotationAngle )
179
0
            {
180
0
                pTextObj->NbcRotate(
181
0
                    rSdrObjCustomShape.GetSnapRect().Center(),
182
0
                    rSourceGeo.m_nRotationAngle,
183
0
                    rSourceGeo.mfSinRotationAngle,
184
0
                    rSourceGeo.mfCosRotationAngle);
185
0
            }
186
187
            // set modified ItemSet at text object
188
0
            pTextObj->SetMergedItemSet(aTargetItemSet);
189
190
0
            if ( pRenderedShape )
191
0
            {
192
0
                if ( dynamic_cast<const SdrObjGroup*>( pRenderedShape.get() ) == nullptr )
193
0
                {
194
0
                    auto pTmp = std::move(pRenderedShape);
195
0
                    pRenderedShape = new SdrObjGroup(rSdrObjCustomShape.getSdrModelFromSdrObject());
196
0
                    static_cast<SdrObjGroup*>(pRenderedShape.get())->GetSubList()->NbcInsertObject( pTmp.get() );
197
0
                }
198
0
                static_cast<SdrObjGroup*>(pRenderedShape.get())->GetSubList()->NbcInsertObject( pTextObj.get() );
199
0
            }
200
0
            else
201
0
                pRenderedShape = std::move(pTextObj);
202
0
        }
203
204
        // force group
205
0
        if ( pRenderedShape )
206
0
        {
207
0
            if ( dynamic_cast<const SdrObjGroup*>( pRenderedShape.get() ) ==  nullptr )
208
0
            {
209
0
                auto pTmp = std::move(pRenderedShape);
210
0
                pRenderedShape = new SdrObjGroup(rSdrObjCustomShape.getSdrModelFromSdrObject());
211
0
                static_cast<SdrObjGroup*>(pRenderedShape.get())->GetSubList()->NbcInsertObject( pTmp.get() );
212
0
            }
213
0
        }
214
0
    }
215
216
0
    return pRenderedShape;
217
0
}
218
219
Reference< drawing::XShape > SAL_CALL EnhancedCustomShapeEngine::render()
220
0
{
221
0
    rtl::Reference<SdrObject> pSdrObj = render2();
222
0
    if (!pSdrObj)
223
0
        return {};
224
0
    return SvxDrawPage::CreateShapeByTypeAndInventor( pSdrObj->GetObjIdentifier(),
225
0
        pSdrObj->GetObjInventor(), pSdrObj.get() );
226
0
}
227
228
rtl::Reference<SdrObject> EnhancedCustomShapeEngine::render2() const
229
75.1k
{
230
75.1k
    if (!mpCustomShape)
231
0
        return {};
232
233
    // retrieving the TextPath property to check if feature is enabled
234
75.1k
    const SdrCustomShapeGeometryItem& rGeometryItem(mpCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
235
75.1k
    bool bTextPathOn = false;
236
75.1k
    const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( u"TextPath"_ustr, u"TextPath"_ustr );
237
75.1k
    if ( pAny )
238
4.11k
        *pAny >>= bTextPathOn;
239
240
75.1k
    EnhancedCustomShape2d aCustomShape2d(*mpCustomShape);
241
75.1k
    Degree100 nRotateAngle = aCustomShape2d.GetRotateAngle();
242
243
75.1k
    bool bFlipV = aCustomShape2d.IsFlipVert();
244
75.1k
    bool bFlipH = aCustomShape2d.IsFlipHorz();
245
75.1k
    bool bLineGeometryNeededOnly = bTextPathOn;
246
247
75.1k
    rtl::Reference<SdrObject> xRenderedShape(aCustomShape2d.CreateObject(bLineGeometryNeededOnly, mpCustomShape->GetStyleSheet()));
248
75.1k
    if (xRenderedShape)
249
34.4k
    {
250
34.4k
        if ( bTextPathOn )
251
3.46k
        {
252
3.46k
            rtl::Reference<SdrObject> xRenderedFontWork(
253
3.46k
                EnhancedCustomShapeFontWork::CreateFontWork(
254
3.46k
                    xRenderedShape.get(),
255
3.46k
                    *mpCustomShape));
256
257
3.46k
            if (xRenderedFontWork)
258
0
            {
259
0
                xRenderedShape = std::move(xRenderedFontWork);
260
0
            }
261
3.46k
        }
262
34.4k
        rtl::Reference<SdrObject> xRenderedShape3d(EnhancedCustomShape3d::Create3DObject(xRenderedShape.get(), *mpCustomShape));
263
34.4k
        if (xRenderedShape3d)
264
3.42k
        {
265
3.42k
            bFlipV = bFlipH = false;
266
3.42k
            nRotateAngle = 0_deg100;
267
3.42k
            xRenderedShape = std::move(xRenderedShape3d);
268
3.42k
        }
269
270
34.4k
        tools::Rectangle aRect(mpCustomShape->GetSnapRect());
271
34.4k
        const GeoStat& rGeoStat(mpCustomShape->GetGeoStat());
272
273
34.4k
        if ( rGeoStat.m_nShearAngle )
274
0
        {
275
0
            Degree100 nShearAngle = rGeoStat.m_nShearAngle;
276
0
            double nTan = rGeoStat.mfTanShearAngle;
277
0
            if (bFlipV != bFlipH)
278
0
            {
279
0
                nShearAngle = -nShearAngle;
280
0
                nTan = -nTan;
281
0
            }
282
283
0
            xRenderedShape->Shear(mpCustomShape->GetSnapRect().Center(), nShearAngle, nTan, false);
284
0
        }
285
34.4k
        if(nRotateAngle )
286
4.50k
            xRenderedShape->NbcRotate(mpCustomShape->GetSnapRect().Center(), nRotateAngle);
287
34.4k
        if ( bFlipV )
288
10.5k
        {
289
10.5k
            Point aLeft( aRect.Left(), ( aRect.Top() + aRect.Bottom() ) >> 1 );
290
10.5k
            Point aRight( aLeft.X() + 1000, aLeft.Y() );
291
10.5k
            xRenderedShape->NbcMirror( aLeft, aRight );
292
10.5k
        }
293
34.4k
        if ( bFlipH )
294
10.3k
        {
295
10.3k
            Point aTop( ( aRect.Left() + aRect.Right() ) >> 1, aRect.Top() );
296
10.3k
            Point aBottom( aTop.X(), aTop.Y() + 1000 );
297
10.3k
            xRenderedShape->NbcMirror( aTop, aBottom );
298
10.3k
        }
299
300
34.4k
        xRenderedShape->RecalcSnapRect();
301
34.4k
    }
302
303
75.1k
    if ( mbForceGroupWithText )
304
0
    {
305
0
        xRenderedShape = ImplForceGroupWithText(
306
0
            *mpCustomShape,
307
0
            xRenderedShape.get());
308
0
    }
309
310
75.1k
    if (!xRenderedShape)
311
40.7k
        return nullptr;
312
313
34.4k
    aCustomShape2d.ApplyGluePoints(xRenderedShape.get());
314
315
34.4k
    return xRenderedShape;
316
75.1k
}
317
318
tools::Rectangle EnhancedCustomShapeEngine::getTextBounds() const
319
372k
{
320
372k
    if (!mpCustomShape)
321
0
        return tools::Rectangle();
322
323
372k
    uno::Reference< document::XActionLockable > xLockable( mpCustomShape->getUnoShape(), uno::UNO_QUERY );
324
372k
    if(!xLockable.is() || xLockable->isActionLocked())
325
39.3k
        return tools::Rectangle();
326
327
332k
    EnhancedCustomShape2d aCustomShape2d(*mpCustomShape);
328
332k
    return aCustomShape2d.GetTextRect();
329
372k
}
330
331
basegfx::B2DPolyPolygon EnhancedCustomShapeEngine::getB2DLineGeometry() const
332
9.07k
{
333
9.07k
    if (!mpCustomShape)
334
0
        return basegfx::B2DPolyPolygon();
335
336
9.07k
    EnhancedCustomShape2d aCustomShape2d(*mpCustomShape);
337
9.07k
    rtl::Reference<SdrObject> pObj = aCustomShape2d.CreateLineGeometry();
338
339
9.07k
    if ( !pObj )
340
115
        return basegfx::B2DPolyPolygon();
341
342
8.96k
    tools::Rectangle aRect(mpCustomShape->GetSnapRect());
343
8.96k
    bool bFlipV = aCustomShape2d.IsFlipVert();
344
8.96k
    bool bFlipH = aCustomShape2d.IsFlipHorz();
345
8.96k
    const GeoStat& rGeoStat(mpCustomShape->GetGeoStat());
346
347
8.96k
    if ( rGeoStat.m_nShearAngle )
348
0
    {
349
0
        Degree100 nShearAngle = rGeoStat.m_nShearAngle;
350
0
        double nTan = rGeoStat.mfTanShearAngle;
351
0
        if (bFlipV != bFlipH)
352
0
        {
353
0
            nShearAngle = -nShearAngle;
354
0
            nTan = -nTan;
355
0
        }
356
0
        pObj->Shear( aRect.Center(), nShearAngle, nTan, false);
357
0
    }
358
8.96k
    Degree100 nRotateAngle = aCustomShape2d.GetRotateAngle();
359
8.96k
    if( nRotateAngle )
360
5.05k
        pObj->NbcRotate( aRect.Center(), nRotateAngle );
361
8.96k
    if ( bFlipH )
362
2.17k
    {
363
2.17k
        Point aTop( ( aRect.Left() + aRect.Right() ) >> 1, aRect.Top() );
364
2.17k
        Point aBottom( aTop.X(), aTop.Y() + 1000 );
365
2.17k
        pObj->NbcMirror( aTop, aBottom );
366
2.17k
    }
367
8.96k
    if ( bFlipV )
368
3.01k
    {
369
3.01k
        Point aLeft( aRect.Left(), ( aRect.Top() + aRect.Bottom() ) >> 1 );
370
3.01k
        Point aRight( aLeft.X() + 1000, aLeft.Y() );
371
3.01k
        pObj->NbcMirror( aLeft, aRight );
372
3.01k
    }
373
374
8.96k
    basegfx::B2DPolyPolygon aPolyPolygon;
375
8.96k
    SdrObjListIter aIter( *pObj, SdrIterMode::DeepWithGroups );
376
377
19.4k
    while ( aIter.IsMore() )
378
10.5k
    {
379
10.5k
        basegfx::B2DPolyPolygon aPP;
380
10.5k
        const SdrObject* pNext = aIter.Next();
381
382
10.5k
        if ( auto pPathObj = dynamic_cast<const SdrPathObj*>(pNext) )
383
10.1k
        {
384
10.1k
            aPP = pPathObj->GetPathPoly();
385
10.1k
        }
386
359
        else
387
359
        {
388
359
            rtl::Reference<SdrObject> pNewObj = pNext->ConvertToPolyObj( false, false );
389
359
            SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pNewObj.get() );
390
359
            if ( pPath )
391
0
                aPP = pPath->GetPathPoly();
392
359
        }
393
394
10.5k
        if ( aPP.count() )
395
10.1k
            aPolyPolygon.append(aPP);
396
10.5k
    }
397
8.96k
    pObj.clear();
398
399
8.96k
    return aPolyPolygon;
400
9.07k
}
401
402
std::vector< Reference< drawing::XCustomShapeHandle > > EnhancedCustomShapeEngine::getInteraction()
403
1.83k
{
404
1.83k
    if (!mpCustomShape)
405
0
        return {};
406
407
1.83k
    EnhancedCustomShape2d aCustomShape2d(*mpCustomShape);
408
1.83k
    sal_uInt32 nHdlCount = aCustomShape2d.GetHdlCount();
409
410
1.83k
    std::vector< Reference< drawing::XCustomShapeHandle > > aVec;
411
1.83k
    aVec.reserve( nHdlCount );
412
413
1.84k
    for ( sal_uInt32 i = 0; i < nHdlCount; i++ )
414
9
        aVec.push_back(new EnhancedCustomShapeHandle( mpCustomShape, i ));
415
1.83k
    return aVec;
416
1.83k
}
417
418
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
419
com_sun_star_drawing_EnhancedCustomShapeEngine_get_implementation(
420
    css::uno::XComponentContext *,
421
    css::uno::Sequence<css::uno::Any> const & args)
422
0
{
423
0
    return cppu::acquire(new EnhancedCustomShapeEngine(args));
424
0
}
425
426
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */