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 SdrObjects 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  // take unrotated snap rect as default, then get the
153  // unrotated text box. Rotation needs to be done centered
154  const tools::Rectangle aObjectBound(GetCustomShapeObj().GetGeoRect());
155  const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(aObjectBound);
156 
157  if(bHasText)
158  {
159  // #i101684# get the text range unrotated and absolute to the object range
160  const basegfx::B2DRange aTextRange(getCorrectedTextBoundRect());
161 
162  // Rotation before scaling
163  if(!basegfx::fTools::equalZero(GetCustomShapeObj().GetExtraTextRotation(true)))
164  {
165  basegfx::B2DVector aTranslation(0.5, 0.5);
166  aTextBoxMatrix.translate( -aTranslation.getX(), -aTranslation.getY() );
167  aTextBoxMatrix.rotate(basegfx::deg2rad(
168  360.0 - GetCustomShapeObj().GetExtraTextRotation(true)));
169  aTextBoxMatrix.translate( aTranslation.getX(), aTranslation.getY() );
170  }
171  // give text object a size
172  aTextBoxMatrix.scale(aTextRange.getWidth(), aTextRange.getHeight());
173 
174  // check if we have a rotation/shear at all to take care of
175  const double fExtraTextRotation(GetCustomShapeObj().GetExtraTextRotation());
176  const GeoStat& rGeoStat(GetCustomShapeObj().GetGeoStat());
177 
178  if(rGeoStat.nShearAngle || rGeoStat.nRotationAngle || !basegfx::fTools::equalZero(fExtraTextRotation))
179  {
180  if(aObjectRange != aTextRange)
181  {
182  // move relative to unrotated object range
183  aTextBoxMatrix.translate(
184  aTextRange.getMinX() - aObjectRange.getMinimum().getX(),
185  aTextRange.getMinY() - aObjectRange.getMinimum().getY());
186  }
187 
188  if(!basegfx::fTools::equalZero(fExtraTextRotation))
189  {
190  basegfx::B2DVector aTranslation(
191  ( aTextRange.getWidth() / 2 ) + ( aTextRange.getMinX() - aObjectRange.getMinimum().getX() ),
192  ( aTextRange.getHeight() / 2 ) + ( aTextRange.getMinY() - aObjectRange.getMinimum().getY() ) );
193  aTextBoxMatrix.translate( -aTranslation.getX(), -aTranslation.getY() );
194  aTextBoxMatrix.rotate(basegfx::deg2rad(360.0 - fExtraTextRotation));
195  aTextBoxMatrix.translate( aTranslation.getX(), aTranslation.getY() );
196  }
197 
198  if(rGeoStat.nShearAngle)
199  {
200  aTextBoxMatrix.shearX(tan((36000 - rGeoStat.nShearAngle) * F_PI18000));
201  }
202 
203  if(rGeoStat.nRotationAngle)
204  {
205  aTextBoxMatrix.rotate((36000 - rGeoStat.nRotationAngle) * F_PI18000);
206  }
207 
208  // give text it's target position
209  aTextBoxMatrix.translate(aObjectRange.getMinimum().getX(), aObjectRange.getMinimum().getY());
210  }
211  else
212  {
213  aTextBoxMatrix.translate(aTextRange.getMinX(), aTextRange.getMinY());
214  }
215 
216  // check if SdrTextWordWrapItem is set
217  bWordWrap = GetCustomShapeObj().GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue();
218  }
219 
220  // fill object matrix
222  aObjectRange.getWidth(), aObjectRange.getHeight(),
223  /*fShearX=*/0, /*fRotate=*/0,
224  aObjectRange.getMinX(), aObjectRange.getMinY()));
225 
226  // create primitive
229  aAttribute,
230  xGroup,
231  aTextBoxMatrix,
232  bWordWrap,
233  b3DShape,
234  aObjectMatrix));
235  xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference };
236  }
237 
238  return xRetval;
239  }
240 
241 } // end of namespace
242 
243 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double getCenterY() const
B2DPoint getCenter() const
double getHeight() const
tools::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)
B2DHomMatrix createScaleShearXRotateTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fShearX, double fRadiant, double fTranslateX, double fTranslateY)
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:1925
Abstract DrawObject.
Definition: svdobj.hxx:260
attribute::SdrEffectsTextAttribute createNewSdrEffectsTextAttribute(const SfxItemSet &rSet, const SdrText *pText, bool bSuppressText)
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:1890
#define F_PI18000
B2DPoint getMinimum() const
bool GetTextBounds(tools::Rectangle &rTextBound) const
Definition: svdoashp.cxx:514
void translate(double fX, double fY)
virtual drawinglayer::primitive2d::Primitive2DContainer createViewIndependentPrimitive2DSequence() const override
double getMinX() const
tools::Long nShearAngle
Definition: svdtrans.hxx:217
bool IsMirroredY() const
Definition: svdoashp.cxx:475