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