LibreOffice Module svx (master)  1
sdrmeasureprimitive2d.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 
26 #include <rtl/ref.hxx>
30 
31 
32 using namespace com::sun::star;
33 
34 
36 {
37  Primitive2DReference SdrMeasurePrimitive2D::impCreatePart(
38  const attribute::SdrLineAttribute& rLineAttribute,
39  const basegfx::B2DHomMatrix& rObjectMatrix,
40  const basegfx::B2DPoint& rStart,
41  const basegfx::B2DPoint& rEnd,
42  bool bLeftActive,
43  bool bRightActive) const
44  {
45  const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
46  basegfx::B2DPolygon aPolygon;
47 
48  aPolygon.append(rStart);
49  aPolygon.append(rEnd);
50  aPolygon.transform(rObjectMatrix);
51 
52  if(rLineStartEnd.isDefault() || (!bLeftActive && !bRightActive))
53  {
55  aPolygon,
56  rLineAttribute,
58  }
59 
60  if(bLeftActive && bRightActive)
61  {
63  aPolygon,
64  rLineAttribute,
65  rLineStartEnd);
66  }
67 
68  const basegfx::B2DPolyPolygon aEmpty;
69  const attribute::SdrLineStartEndAttribute aLineStartEnd(
70  bLeftActive ? rLineStartEnd.getStartPolyPolygon() : aEmpty, bRightActive ? rLineStartEnd.getEndPolyPolygon() : aEmpty,
71  bLeftActive ? rLineStartEnd.getStartWidth() : 0.0, bRightActive ? rLineStartEnd.getEndWidth() : 0.0,
72  bLeftActive && rLineStartEnd.isStartActive(), bRightActive && rLineStartEnd.isEndActive(),
73  bLeftActive && rLineStartEnd.isStartCentered(), bRightActive && rLineStartEnd.isEndCentered());
74 
76  aPolygon,
77  rLineAttribute,
78  aLineStartEnd);
79  }
80 
81  void SdrMeasurePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const
82  {
83  Primitive2DContainer aRetval;
85  basegfx::B2DRange aTextRange;
86  const basegfx::B2DVector aLine(getEnd() - getStart());
87  const double fDistance(aLine.getLength());
88  const double fAngle(atan2(aLine.getY(), aLine.getX()));
89  bool bAutoUpsideDown(false);
90  const attribute::SdrTextAttribute rTextAttribute = getSdrLSTAttribute().getText();
91  const basegfx::B2DHomMatrix aObjectMatrix(
93 
94  // prepare text, but do not add yet; it needs to be aligned to
95  // the line geometry
96  if(!rTextAttribute.isDefault())
97  {
98  basegfx::B2DHomMatrix aTextMatrix;
99  double fTestAngle(fAngle);
100 
101  if(getTextRotation())
102  {
103  aTextMatrix.rotate(-F_PI2);
104  fTestAngle -= (F_PI2);
105 
106  if(getTextAutoAngle() && fTestAngle < -F_PI)
107  {
108  fTestAngle += F_2PI;
109  }
110  }
111 
112  if(getTextAutoAngle())
113  {
114  if(fTestAngle > (F_PI / 4.0) || fTestAngle < (-F_PI * (3.0 / 4.0)))
115  {
116  bAutoUpsideDown = true;
117  }
118  }
119 
120  // create primitive and get text range
121  xBlockText = new SdrBlockTextPrimitive2D(
122  &rTextAttribute.getSdrText(),
123  rTextAttribute.getOutlinerParaObject(),
124  aTextMatrix,
127  rTextAttribute.isScroll(),
128  false,
129  false,
130  false);
131 
132  aTextRange = xBlockText->getB2DRange(aViewInformation);
133  }
134 
135  // prepare line attribute and result
136  double fTextX;
137  double fTextY;
138  {
139  const attribute::SdrLineAttribute rLineAttribute(getSdrLSTAttribute().getLine());
140  bool bArrowsOutside(false);
141  bool bMainLineSplitted(false);
142  const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
143  double fStartArrowW(0.0);
144  double fStartArrowH(0.0);
145  double fEndArrowW(0.0);
146  double fEndArrowH(0.0);
147 
148  if(!rLineStartEnd.isDefault())
149  {
150  if(rLineStartEnd.isStartActive())
151  {
152  const basegfx::B2DRange aArrowRange(basegfx::utils::getRange(rLineStartEnd.getStartPolyPolygon()));
153  fStartArrowW = rLineStartEnd.getStartWidth();
154  fStartArrowH = aArrowRange.getHeight() * fStartArrowW / aArrowRange.getWidth();
155 
156  if(rLineStartEnd.isStartCentered())
157  {
158  fStartArrowH *= 0.5;
159  }
160  }
161 
162  if(rLineStartEnd.isEndActive())
163  {
164  const basegfx::B2DRange aArrowRange(basegfx::utils::getRange(rLineStartEnd.getEndPolyPolygon()));
165  fEndArrowW = rLineStartEnd.getEndWidth();
166  fEndArrowH = aArrowRange.getHeight() * fEndArrowW / aArrowRange.getWidth();
167 
168  if(rLineStartEnd.isEndCentered())
169  {
170  fEndArrowH *= 0.5;
171  }
172  }
173  }
174 
175  const double fSpaceNeededByArrows(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.5));
176  const double fArrowsOutsideLen((fStartArrowH + fEndArrowH + fStartArrowW + fEndArrowW) * 0.5);
177  const double fHalfLineWidth(rLineAttribute.getWidth() * 0.5);
178 
179  if(fSpaceNeededByArrows > fDistance)
180  {
181  bArrowsOutside = true;
182  }
183 
184  MeasureTextPosition eHorizontal(getHorizontal());
185  MeasureTextPosition eVertical(getVertical());
186 
187  if(MEASURETEXTPOSITION_AUTOMATIC == eVertical)
188  {
189  eVertical = MEASURETEXTPOSITION_NEGATIVE;
190  }
191 
192  if(MEASURETEXTPOSITION_CENTERED == eVertical)
193  {
194  bMainLineSplitted = true;
195  }
196 
197  if(MEASURETEXTPOSITION_AUTOMATIC == eHorizontal)
198  {
199  if(aTextRange.getWidth() > fDistance)
200  {
201  eHorizontal = MEASURETEXTPOSITION_NEGATIVE;
202  }
203  else
204  {
205  eHorizontal = MEASURETEXTPOSITION_CENTERED;
206  }
207 
208  if(bMainLineSplitted)
209  {
210  if(aTextRange.getWidth() + fSpaceNeededByArrows > fDistance)
211  {
212  bArrowsOutside = true;
213  }
214  }
215  else
216  {
217  const double fSmallArrowNeed(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.125));
218 
219  if(aTextRange.getWidth() + fSmallArrowNeed > fDistance)
220  {
221  bArrowsOutside = true;
222  }
223  }
224  }
225 
226  if(MEASURETEXTPOSITION_CENTERED != eHorizontal)
227  {
228  bArrowsOutside = true;
229  }
230 
231  // switch text above/below?
232  if(getBelow() || (bAutoUpsideDown && !getTextRotation()))
233  {
234  if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
235  {
236  eVertical = MEASURETEXTPOSITION_POSITIVE;
237  }
238  else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
239  {
240  eVertical = MEASURETEXTPOSITION_NEGATIVE;
241  }
242  }
243 
244  const double fMainLineOffset(getBelow() ? getDistance() : -getDistance());
245  const basegfx::B2DPoint aMainLeft(0.0, fMainLineOffset);
246  const basegfx::B2DPoint aMainRight(fDistance, fMainLineOffset);
247 
248  // main line
249  if(bArrowsOutside)
250  {
251  double fLenLeft(fArrowsOutsideLen);
252  double fLenRight(fArrowsOutsideLen);
253 
254  if(!bMainLineSplitted)
255  {
256  if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
257  {
258  fLenLeft = fStartArrowH + aTextRange.getWidth();
259  }
260  else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
261  {
262  fLenRight = fEndArrowH + aTextRange.getWidth();
263  }
264  }
265 
266  const basegfx::B2DPoint aMainLeftLeft(aMainLeft.getX() - fLenLeft, aMainLeft.getY());
267  const basegfx::B2DPoint aMainRightRight(aMainRight.getX() + fLenRight, aMainRight.getY());
268 
269  aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeftLeft, aMainLeft, false, true));
270  aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainRight, aMainRightRight, true, false));
271 
272  if(!bMainLineSplitted || MEASURETEXTPOSITION_CENTERED != eHorizontal)
273  {
274  aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, false, false));
275  }
276  }
277  else
278  {
279  if(bMainLineSplitted)
280  {
281  const double fHalfLength((fDistance - (aTextRange.getWidth() + (fStartArrowH + fEndArrowH) * 0.25)) * 0.5);
282  const basegfx::B2DPoint aMainInnerLeft(aMainLeft.getX() + fHalfLength, aMainLeft.getY());
283  const basegfx::B2DPoint aMainInnerRight(aMainRight.getX() - fHalfLength, aMainRight.getY());
284 
285  aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainInnerLeft, true, false));
286  aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainInnerRight, aMainRight, false, true));
287  }
288  else
289  {
290  aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, true, true));
291  }
292  }
293 
294  // left/right help line value preparation
295  const double fTopEdge(getBelow() ? getUpper() + getDistance() : -getUpper() - getDistance());
296  const double fBottomLeft(getBelow() ? getLower() - getLeftDelta() : getLeftDelta() - getLower());
297  const double fBottomRight(getBelow() ? getLower() - getRightDelta() : getRightDelta() - getLower());
298 
299  // left help line
300  const basegfx::B2DPoint aLeftUp(0.0, fTopEdge);
301  const basegfx::B2DPoint aLeftDown(0.0, fBottomLeft);
302 
303  aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aLeftDown, aLeftUp, false, false));
304 
305  // right help line
306  const basegfx::B2DPoint aRightUp(fDistance, fTopEdge);
307  const basegfx::B2DPoint aRightDown(fDistance, fBottomRight);
308 
309  aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aRightDown, aRightUp, false, false));
310 
311  // text horizontal position
312  if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
313  {
314  // left
315  const double fSmall(fArrowsOutsideLen * 0.18);
316  fTextX = aMainLeft.getX() - (fStartArrowH + aTextRange.getWidth() + fSmall + fHalfLineWidth);
317 
318  if(bMainLineSplitted)
319  {
320  fTextX -= (fArrowsOutsideLen - fStartArrowH);
321  }
322 
323  if(!rTextAttribute.isDefault())
324  {
325  fTextX -= rTextAttribute.getTextRightDistance();
326  }
327  }
328  else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
329  {
330  // right
331  const double fSmall(fArrowsOutsideLen * 0.18);
332  fTextX = aMainRight.getX() + (fEndArrowH + fSmall + fHalfLineWidth);
333 
334  if(bMainLineSplitted)
335  {
336  fTextX += (fArrowsOutsideLen - fEndArrowH);
337  }
338 
339  if(!rTextAttribute.isDefault())
340  {
341  fTextX += rTextAttribute.getTextLeftDistance();
342  }
343  }
344  else // MEASURETEXTPOSITION_CENTERED
345  {
346  // centered
347  fTextX = aMainLeft.getX() + ((fDistance - aTextRange.getWidth()) * 0.5);
348 
349  if(!rTextAttribute.isDefault())
350  {
351  fTextX += (rTextAttribute.getTextLeftDistance() - rTextAttribute.getTextRightDistance()) / 2L;
352  }
353  }
354 
355  // text vertical position
356  if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
357  {
358  // top
359  const double fSmall(fArrowsOutsideLen * 0.10);
360  fTextY = aMainLeft.getY() - (aTextRange.getHeight() + fSmall + fHalfLineWidth);
361 
362  if(!rTextAttribute.isDefault())
363  {
364  fTextY -= rTextAttribute.getTextLowerDistance();
365  }
366  }
367  else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
368  {
369  // bottom
370  const double fSmall(fArrowsOutsideLen * 0.10);
371  fTextY = aMainLeft.getY() + (fSmall + fHalfLineWidth);
372 
373  if(!rTextAttribute.isDefault())
374  {
375  fTextY += rTextAttribute.getTextUpperDistance();
376  }
377  }
378  else // MEASURETEXTPOSITION_CENTERED
379  {
380  // centered
381  fTextY = aMainLeft.getY() - (aTextRange.getHeight() * 0.5);
382 
383  if(!rTextAttribute.isDefault())
384  {
385  fTextY += (rTextAttribute.getTextUpperDistance() - rTextAttribute.getTextLowerDistance()) / 2L;
386  }
387  }
388  }
389 
390  if(getSdrLSTAttribute().getLine().isDefault())
391  {
392  // embed line geometry to invisible (100% transparent) line group for HitTest
393  const Primitive2DReference xHiddenLines(new HiddenGeometryPrimitive2D(aRetval));
394 
395  aRetval = Primitive2DContainer { xHiddenLines };
396  }
397 
398  if(xBlockText.is())
399  {
400  // create transformation to text primitive end position
401  basegfx::B2DHomMatrix aChange;
402 
403  // handle auto text rotation
404  if(bAutoUpsideDown)
405  {
406  aChange.rotate(F_PI);
407  }
408 
409  // move from aTextRange.TopLeft to fTextX, fTextY
410  aChange.translate(fTextX - aTextRange.getMinX(), fTextY - aTextRange.getMinY());
411 
412  // apply object matrix
413  aChange *= aObjectMatrix;
414 
415  // apply to existing text primitive
416  std::unique_ptr<SdrTextPrimitive2D> pNewBlockText = xBlockText->createTransformedClone(aChange);
417  OSL_ENSURE(pNewBlockText, "SdrMeasurePrimitive2D::create2DDecomposition: Could not create transformed clone of text primitive (!)");
418  xBlockText.clear();
419 
420  // add to local primitives
421  aRetval.push_back(Primitive2DReference(pNewBlockText.release()));
422  }
423 
424  // add shadow
425  if(!getSdrLSTAttribute().getShadow().isDefault())
426  {
428  aRetval,
429  getSdrLSTAttribute().getShadow());
430  }
431 
432  rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end());
433  }
434 
435  SdrMeasurePrimitive2D::SdrMeasurePrimitive2D(
436  const attribute::SdrLineEffectsTextAttribute& rSdrLSTAttribute,
437  const basegfx::B2DPoint& rStart,
438  const basegfx::B2DPoint& rEnd,
439  MeasureTextPosition eHorizontal,
440  MeasureTextPosition eVertical,
441  double fDistance,
442  double fUpper,
443  double fLower,
444  double fLeftDelta,
445  double fRightDelta,
446  bool bBelow,
447  bool bTextRotation,
448  bool bTextAutoAngle)
450  maSdrLSTAttribute(rSdrLSTAttribute),
451  maStart(rStart),
452  maEnd(rEnd),
453  meHorizontal(eHorizontal),
454  meVertical(eVertical),
455  mfDistance(fDistance),
456  mfUpper(fUpper),
457  mfLower(fLower),
458  mfLeftDelta(fLeftDelta),
459  mfRightDelta(fRightDelta),
460  mbBelow(bBelow),
461  mbTextRotation(bTextRotation),
462  mbTextAutoAngle(bTextAutoAngle)
463  {
464  }
465 
467  {
468  if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
469  {
470  const SdrMeasurePrimitive2D& rCompare = static_cast<const SdrMeasurePrimitive2D&>(rPrimitive);
471 
472  return (getStart() == rCompare.getStart()
473  && getEnd() == rCompare.getEnd()
474  && getHorizontal() == rCompare.getHorizontal()
475  && getVertical() == rCompare.getVertical()
476  && getDistance() == rCompare.getDistance()
477  && getUpper() == rCompare.getUpper()
478  && getLower() == rCompare.getLower()
479  && getLeftDelta() == rCompare.getLeftDelta()
480  && getRightDelta() == rCompare.getRightDelta()
481  && getBelow() == rCompare.getBelow()
482  && getTextRotation() == rCompare.getTextRotation()
483  && getTextAutoAngle() == rCompare.getTextAutoAngle()
484  && getSdrLSTAttribute() == rCompare.getSdrLSTAttribute());
485  }
486 
487  return false;
488  }
489 
490  // provide unique ID
492 
493 } // end of namespace
494 
495 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
double getHeight() const
double getX() const
#define F_PI2
double getY() const
const basegfx::B2DPolyPolygon & getStartPolyPolygon() const
Primitive2DContainer createEmbeddedShadowPrimitive(const Primitive2DContainer &rContent, const attribute::SdrShadowAttribute &rShadow, const basegfx::B2DHomMatrix &rObjectMatrix, const Primitive2DContainer *pContentForShadow)
double getWidth() const
const OutlinerParaObject & getOutlinerParaObject() const
const basegfx::B2DPolyPolygon & getEndPolyPolygon() const
#define F_PI
void rotate(double fRadiant)
#define F_2PI
const attribute::SdrLineEffectsTextAttribute & getSdrLSTAttribute() const
const Point maEnd
ImplPrimitive2DIDBlock(BorderLinePrimitive2D, PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D) Primitive2DReference tryMergeBorderLinePrimitive2D(const BorderLinePrimitive2D *pCandidateA
B2DRange getRange(const B2DPolygon &rCandidate)
void transform(const basegfx::B2DHomMatrix &rMatrix)
css::uno::Reference< css::graphic::XPrimitive2D > Primitive2DReference
double getMinY() const
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
Point maStart
#define PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D
void translate(double fX, double fY)
double getLength() const
double getMinX() const
B2DHomMatrix createShearXRotateTranslateB2DHomMatrix(double fShearX, double fRadiant, double fTranslateX, double fTranslateY)
Primitive2DReference createPolygonLinePrimitive(const basegfx::B2DPolygon &rPolygon, const attribute::SdrLineAttribute &rLine, const attribute::SdrLineStartEndAttribute &rStroke)