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