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