LibreOffice Module svx (master)  1
viewcontactofsdrobjcustomshape.cxx
Go to the documentation of this file.
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 
21 #include <svx/svdoashp.hxx>
22 #include <svx/sdooitm.hxx>
25 #include <svx/svditer.hxx>
30 #include <svx/obj3d.hxx>
32 #include <vcl/canvastools.hxx>
33 #include <sal/log.hxx>
34 
35 
36 namespace sdr::contact
37 {
39  : ViewContactOfTextObj(rCustomShape)
40  {
41  }
42 
44  {
45  }
46 
48  {
49  const tools::Rectangle aObjectBound(GetCustomShapeObj().GetGeoRect());
50  tools::Rectangle aTextBound(aObjectBound);
51  GetCustomShapeObj().GetTextBounds(aTextBound);
53  const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(aObjectBound);
54 
55  // no need to correct if no extra text range
56  if(aTextRange != aObjectRange)
57  {
58  const GeoStat& rGeoStat(GetCustomShapeObj().GetGeoStat());
59 
60  // only correct when rotation and/or shear is used
61  if(rGeoStat.nShearAngle || rGeoStat.nRotationAngle )
62  {
63  // text range needs to be corrected by
64  // aObjectRange.getCenter() - aRotObjectRange.getCenter() since it's
65  // defined differently by using rotation around object center. Start
66  // with positive part
67  basegfx::B2DVector aTranslation(aObjectRange.getCenter());
68 
69  // get rotated and sheared object's range
70  basegfx::B2DRange aRotObjectRange(aObjectRange);
71  basegfx::B2DHomMatrix aRotMatrix;
72 
73  aRotMatrix.translate(-aObjectRange.getMinimum().getX(), -aObjectRange.getMinimum().getY());
74 
75  if(rGeoStat.nShearAngle)
76  {
77  aRotMatrix.shearX(tan((36000 - rGeoStat.nShearAngle) * F_PI18000));
78  }
79 
80  if(rGeoStat.nRotationAngle)
81  {
82  aRotMatrix.rotate((36000 - rGeoStat.nRotationAngle) * F_PI18000);
83  }
84 
85  aRotMatrix.translate(aObjectRange.getMinimum().getX(), aObjectRange.getMinimum().getY());
86  aRotObjectRange.transform(aRotMatrix);
87 
88  // add negative translation part
89  aTranslation -= aRotObjectRange.getCenter();
90 
91  // create new range
92  aTextRange = basegfx::B2DRange(
93  aTextRange.getMinX() + aTranslation.getX(), aTextRange.getMinY() + aTranslation.getY(),
94  aTextRange.getMaxX() + aTranslation.getX(), aTextRange.getMaxY() + aTranslation.getY());
95  }
96 
97  // NbcMirror() of SdrTextObj (from which SdrObjCustomShape is derived), adds a
98  // 180deg rotation around the shape center to GeoStat.nRotationAngle. So remove here the
99  // 180° rotation, which was added by GetTextBounds().
101  {
103  aObjectRange.getCenterX(), aObjectRange.getCenterY(), F_PI));
104  aTextRange.transform(aRotMatrix);
105  }
106  }
107 
108  return aTextRange;
109  }
110 
112  {
114  const SfxItemSet& rItemSet = GetCustomShapeObj().GetMergedItemSet();
115 
116  // #i98072# Get shadow and text; eventually suppress the text if it's
117  // a TextPath FontworkGallery object
120  rItemSet,
121  GetCustomShapeObj().getText(0),
122  GetCustomShapeObj().IsTextPath()));
124  bool bHasText(!aAttribute.getText().isDefault());
125 
126  // create Primitive2DContainer from sub-geometry
127  const SdrObject* pSdrObjRepresentation = GetCustomShapeObj().GetSdrObjectFromCustomShape();
128  bool b3DShape(false);
129 
130  if(pSdrObjRepresentation)
131  {
132  // tdf#118498 The processing of SdrObjListIter for SdrIterMode::DeepNoGroups
133  // did change for 3D-Objects, it now correctly enters and iterates the
134  // SdrObjects in the E3dScene (same as for SdrObjGroup). This is more correct
135  // as the old version which just checked for dynamic_cast<const SdrObjGroup*>
136  // and *only* entered these, ignoring E3dScene as grouping-object.
137  // But how to fix that? Taking back the SdrObjListIter change would be easy, but
138  // not correct. After checking ViewContactOfE3dScene and ViewContactOfGroup
139  // I see that both traverse their children by themselves (on VC-Level,
140  // see createViewIndependentPrimitive2DSequence implementations and usage of
141  // GetObjectCount()). Thus in principle iterating here (esp. 'deep') seems to
142  // be wrong anyways, it might have even created wrong and double geometries
143  // (only with complex CustomShapes with multiple representation SdrObects and
144  // only visible when transparency involved, but runtime-expensive).
145  // Thus: Just do not iterate, will check behaviour deeply.
146  b3DShape = (nullptr != dynamic_cast< const E3dObject* >(pSdrObjRepresentation));
148  pSdrObjRepresentation->GetViewContact().getViewIndependentPrimitive2DContainer());
149  xGroup.insert(xGroup.end(), xNew.begin(), xNew.end());
150  }
151 
152  if(bHasText || !xGroup.empty())
153  {
154  // prepare text box geometry
155  basegfx::B2DHomMatrix aTextBoxMatrix;
156  bool bWordWrap(false);
157 
158  if(bHasText)
159  {
160  // take unrotated snap rect as default, then get the
161  // unrotated text box. Rotation needs to be done centered
162  const tools::Rectangle aObjectBound(GetCustomShapeObj().GetGeoRect());
163  const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(aObjectBound);
164 
165  // #i101684# get the text range unrotated and absolute to the object range
166  const basegfx::B2DRange aTextRange(getCorrectedTextBoundRect());
167 
168  // Get the text range before unrotated and independent from object range
169  const tools::Rectangle aIndTextRect(Point(aTextRange.getMinX(), aTextRange.getMinY()), GetCustomShapeObj().GetTextSize());
170  const basegfx::B2DRange aIndTextRange = vcl::unotools::b2DRectangleFromRectangle(aIndTextRect);
171 
172  // Rotation before scaling
173  if(!basegfx::fTools::equalZero(GetCustomShapeObj().GetExtraTextRotation(true)) ||
174  !basegfx::fTools::equalZero(GetCustomShapeObj().GetCameraRotation()))
175  {
176  basegfx::B2DVector aTranslation(0.5, 0.5);
177  aTextBoxMatrix.translate( -aTranslation.getX(), -aTranslation.getY() );
178  aTextBoxMatrix.rotate(basegfx::deg2rad(
179  360.0 - GetCustomShapeObj().GetExtraTextRotation(true) - GetCustomShapeObj().GetCameraRotation()));
180  aTextBoxMatrix.translate( aTranslation.getX(), aTranslation.getY() );
181  }
182  // give text object a size
183  aTextBoxMatrix.scale(aTextRange.getWidth(), aTextRange.getHeight());
184 
185  // check if we have a rotation/shear at all to take care of
186  const double fExtraTextRotation(GetCustomShapeObj().GetExtraTextRotation());
187  const double fTextCameraZRotation(GetCustomShapeObj().GetCameraRotation());
188  const GeoStat& rGeoStat(GetCustomShapeObj().GetGeoStat());
189 
190  if(rGeoStat.nShearAngle || rGeoStat.nRotationAngle || !basegfx::fTools::equalZero(fExtraTextRotation))
191  {
192  if(aObjectRange != aTextRange)
193  {
194  // move relative to unrotated object range
195  aTextBoxMatrix.translate(
196  aTextRange.getMinX() - aObjectRange.getMinimum().getX(),
197  aTextRange.getMinY() - aObjectRange.getMinimum().getY());
198  }
199 
200  if(!basegfx::fTools::equalZero(fExtraTextRotation) || !basegfx::fTools::equalZero(fTextCameraZRotation))
201  {
202  basegfx::B2DVector aTranslation(
203  ( aTextRange.getWidth() / 2 ) + ( aTextRange.getMinX() - aObjectRange.getMinimum().getX() ),
204  ( aTextRange.getHeight() / 2 ) + ( aTextRange.getMinY() - aObjectRange.getMinimum().getY() ) );
205  aTextBoxMatrix.translate( -aTranslation.getX(), -aTranslation.getY() );
206  aTextBoxMatrix.rotate(basegfx::deg2rad(360.0 - fExtraTextRotation + fTextCameraZRotation));
207  aTextBoxMatrix.translate( aTranslation.getX(), aTranslation.getY() );
208  }
209 
210  if(rGeoStat.nShearAngle)
211  {
212  aTextBoxMatrix.shearX(tan((36000 - rGeoStat.nShearAngle) * F_PI18000));
213  }
214 
215  if(rGeoStat.nRotationAngle)
216  {
217  aTextBoxMatrix.rotate((36000 - rGeoStat.nRotationAngle) * F_PI18000);
218  }
219 
220  // give text it's target position
221  aTextBoxMatrix.translate(aObjectRange.getMinimum().getX(), aObjectRange.getMinimum().getY());
222  }
223  // If text overflows from textbox we should use text info instead of textbox to relocation.
224  else if((aTextRange.getWidth() < aIndTextRange.getWidth() ||
225  aTextRange.getHeight() < aIndTextRange.getHeight()) &&
226  !basegfx::fTools::equalZero(fTextCameraZRotation))
227  {
228  aTextBoxMatrix.translate(aIndTextRange.getCenterX(), aIndTextRange.getCenterY());
229  }
230  else
231  {
232  aTextBoxMatrix.translate(aTextRange.getMinX(), aTextRange.getMinY());
233  }
234 
235  // check if SdrTextWordWrapItem is set
236  bWordWrap = GetCustomShapeObj().GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue();
237  }
238 
239  // create primitive
242  aAttribute,
243  xGroup,
244  aTextBoxMatrix,
245  bWordWrap,
246  b3DShape));
247  xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference };
248  }
249 
250  return xRetval;
251  }
252 
253 } // end of namespace
254 
255 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double getCenterY() const
B2DPoint getCenter() const
#define SDRATTR_TEXT_WORDWRAP
Definition: svddef.hxx:218
double getHeight() const
virtual const Size & GetTextSize() const
Definition: svdotext.cxx:253
long nRotationAngle
Definition: svdtrans.hxx:216
double getX() const
double getY() const
double getMaxX() const
double getWidth() const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
The transformation of a rectangle into a polygon, by using angle parameters from GeoStat.
Definition: svdtrans.hxx:214
double getMaxY() const
void shearX(double fSx)
#define F_PI
void rotate(double fRadiant)
static bool equalZero(const double &rfVal)
void scale(double fX, double fY)
constexpr double deg2rad(double v)
const SfxPoolItem & GetMergedItem(const sal_uInt16 nWhich) const
Definition: svdobj.cxx:1943
Abstract DrawObject.
Definition: svdobj.hxx:312
attribute::SdrShadowTextAttribute createNewSdrShadowTextAttribute(const SfxItemSet &rSet, const SdrText *pText, bool bSuppressText)
long nShearAngle
Definition: svdtrans.hxx:217
double getCenterX() const
basegfx::B2DRange b2DRectangleFromRectangle(const ::tools::Rectangle &rRect)
const SdrObject * GetSdrObjectFromCustomShape() const
Definition: svdoashp.cxx:415
css::uno::Reference< css::graphic::XPrimitive2D > Primitive2DReference
double getMinY() const
B2DHomMatrix createRotateAroundPoint(double fPointX, double fPointY, double fRadiant)
const SfxItemSet & GetMergedItemSet() const
Definition: svdobj.cxx:1908
#define F_PI18000
B2DPoint getMinimum() const
bool GetTextBounds(tools::Rectangle &rTextBound) const
Definition: svdoashp.cxx:549
void translate(double fX, double fY)
virtual drawinglayer::primitive2d::Primitive2DContainer createViewIndependentPrimitive2DSequence() const override
double getMinX() const
bool IsMirroredY() const
Definition: svdoashp.cxx:488