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