/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: */ |