1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sdr/primitive2d/sdrmeasureprimitive2d.hxx>
21 #include <sdr/primitive2d/sdrdecompositiontools.hxx>
22 #include <basegfx/matrix/b2dhommatrix.hxx>
23 #include <sdr/primitive2d/sdrtextprimitive2d.hxx>
24 #include <sdr/attribute/sdrtextattribute.hxx>
25 #include <basegfx/polygon/b2dpolypolygontools.hxx>
26 #include <rtl/ref.hxx>
27 #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
28 #include <basegfx/matrix/b2dhommatrixtools.hxx>
29 #include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
30 #include <osl/diagnose.h>
33 using namespace com::sun::star
;
36 namespace drawinglayer::primitive2d
38 Primitive2DReference
SdrMeasurePrimitive2D::impCreatePart(
39 const attribute::SdrLineAttribute
& rLineAttribute
,
40 const basegfx::B2DHomMatrix
& rObjectMatrix
,
41 const basegfx::B2DPoint
& rStart
,
42 const basegfx::B2DPoint
& rEnd
,
44 bool bRightActive
) const
46 const attribute::SdrLineStartEndAttribute
& rLineStartEnd
= getSdrLSTAttribute().getLineStartEnd();
47 basegfx::B2DPolygon aPolygon
;
49 aPolygon
.append(rStart
);
50 aPolygon
.append(rEnd
);
51 aPolygon
.transform(rObjectMatrix
);
53 if(rLineStartEnd
.isDefault() || (!bLeftActive
&& !bRightActive
))
55 return createPolygonLinePrimitive(
58 attribute::SdrLineStartEndAttribute());
61 if(bLeftActive
&& bRightActive
)
63 return createPolygonLinePrimitive(
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());
76 return createPolygonLinePrimitive(
82 Primitive2DReference
SdrMeasurePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& aViewInformation
) const
84 Primitive2DContainer aRetval
;
85 rtl::Reference
<SdrBlockTextPrimitive2D
> xBlockText
;
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(
93 basegfx::utils::createShearXRotateTranslateB2DHomMatrix(0.0, fAngle
, getStart()));
95 // prepare text, but do not add yet; it needs to be aligned to
97 if(!rTextAttribute
.isDefault())
99 basegfx::B2DHomMatrix aTextMatrix
;
100 double fTestAngle(fAngle
);
102 if(getTextRotation())
104 aTextMatrix
.rotate(-M_PI_2
);
105 fTestAngle
-= (M_PI_2
);
107 if(getTextAutoAngle() && fTestAngle
< -M_PI
)
109 fTestAngle
+= 2 * M_PI
;
113 if(getTextAutoAngle())
115 if(fTestAngle
> (M_PI
/ 4.0) || fTestAngle
< (-M_PI
* (3.0 / 4.0)))
117 bAutoUpsideDown
= true;
121 // create primitive and get text range
122 xBlockText
= new SdrBlockTextPrimitive2D(
123 &rTextAttribute
.getSdrText(),
124 rTextAttribute
.getOutlinerParaObject(),
126 SDRTEXTHORZADJUST_CENTER
,
127 SDRTEXTVERTADJUST_CENTER
,
128 rTextAttribute
.isScroll(),
133 aTextRange
= xBlockText
->getB2DRange(aViewInformation
);
136 // prepare line attribute and result
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);
149 if(!rLineStartEnd
.isDefault())
151 if(rLineStartEnd
.isStartActive())
153 const basegfx::B2DRange
aArrowRange(basegfx::utils::getRange(rLineStartEnd
.getStartPolyPolygon()));
154 fStartArrowW
= rLineStartEnd
.getStartWidth();
155 fStartArrowH
= aArrowRange
.getHeight() * fStartArrowW
/ aArrowRange
.getWidth();
157 if(rLineStartEnd
.isStartCentered())
163 if(rLineStartEnd
.isEndActive())
165 const basegfx::B2DRange
aArrowRange(basegfx::utils::getRange(rLineStartEnd
.getEndPolyPolygon()));
166 fEndArrowW
= rLineStartEnd
.getEndWidth();
167 fEndArrowH
= aArrowRange
.getHeight() * fEndArrowW
/ aArrowRange
.getWidth();
169 if(rLineStartEnd
.isEndCentered())
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);
180 if(fSpaceNeededByArrows
> fDistance
)
182 bArrowsOutside
= true;
185 MeasureTextPosition
eHorizontal(getHorizontal());
186 MeasureTextPosition
eVertical(getVertical());
188 if(MEASURETEXTPOSITION_AUTOMATIC
== eVertical
)
190 eVertical
= MEASURETEXTPOSITION_NEGATIVE
;
193 if(MEASURETEXTPOSITION_CENTERED
== eVertical
)
195 bMainLineSplitted
= true;
198 if(MEASURETEXTPOSITION_AUTOMATIC
== eHorizontal
)
200 if(aTextRange
.getWidth() > fDistance
)
202 eHorizontal
= MEASURETEXTPOSITION_NEGATIVE
;
206 eHorizontal
= MEASURETEXTPOSITION_CENTERED
;
209 if(bMainLineSplitted
)
211 if(aTextRange
.getWidth() + fSpaceNeededByArrows
> fDistance
)
213 bArrowsOutside
= true;
218 const double fSmallArrowNeed(fStartArrowH
+ fEndArrowH
+ ((fStartArrowW
+ fEndArrowW
) * 0.125));
220 if(aTextRange
.getWidth() + fSmallArrowNeed
> fDistance
)
222 bArrowsOutside
= true;
227 if(MEASURETEXTPOSITION_CENTERED
!= eHorizontal
)
229 bArrowsOutside
= true;
232 // switch text above/below?
233 if(getBelow() || (bAutoUpsideDown
&& !getTextRotation()))
235 if(MEASURETEXTPOSITION_NEGATIVE
== eVertical
)
237 eVertical
= MEASURETEXTPOSITION_POSITIVE
;
239 else if(MEASURETEXTPOSITION_POSITIVE
== eVertical
)
241 eVertical
= MEASURETEXTPOSITION_NEGATIVE
;
245 const double fMainLineOffset(getBelow() ? getDistance() : -getDistance());
246 const basegfx::B2DPoint
aMainLeft(0.0, fMainLineOffset
);
247 const basegfx::B2DPoint
aMainRight(fDistance
, fMainLineOffset
);
252 double fLenLeft(fArrowsOutsideLen
);
253 double fLenRight(fArrowsOutsideLen
);
255 if(!bMainLineSplitted
)
257 if(MEASURETEXTPOSITION_NEGATIVE
== eHorizontal
)
259 fLenLeft
= fStartArrowH
+ aTextRange
.getWidth();
261 else if(MEASURETEXTPOSITION_POSITIVE
== eHorizontal
)
263 fLenRight
= fEndArrowH
+ aTextRange
.getWidth();
267 const basegfx::B2DPoint
aMainLeftLeft(aMainLeft
.getX() - fLenLeft
, aMainLeft
.getY());
268 const basegfx::B2DPoint
aMainRightRight(aMainRight
.getX() + fLenRight
, aMainRight
.getY());
270 aRetval
.push_back(impCreatePart(rLineAttribute
, aObjectMatrix
, aMainLeftLeft
, aMainLeft
, false, true));
271 aRetval
.push_back(impCreatePart(rLineAttribute
, aObjectMatrix
, aMainRight
, aMainRightRight
, true, false));
273 if(!bMainLineSplitted
|| MEASURETEXTPOSITION_CENTERED
!= eHorizontal
)
275 aRetval
.push_back(impCreatePart(rLineAttribute
, aObjectMatrix
, aMainLeft
, aMainRight
, false, false));
280 if(bMainLineSplitted
)
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());
286 aRetval
.push_back(impCreatePart(rLineAttribute
, aObjectMatrix
, aMainLeft
, aMainInnerLeft
, true, false));
287 aRetval
.push_back(impCreatePart(rLineAttribute
, aObjectMatrix
, aMainInnerRight
, aMainRight
, false, true));
291 aRetval
.push_back(impCreatePart(rLineAttribute
, aObjectMatrix
, aMainLeft
, aMainRight
, true, true));
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());
301 const basegfx::B2DPoint
aLeftUp(0.0, fTopEdge
);
302 const basegfx::B2DPoint
aLeftDown(0.0, fBottomLeft
);
304 aRetval
.push_back(impCreatePart(rLineAttribute
, aObjectMatrix
, aLeftDown
, aLeftUp
, false, false));
307 const basegfx::B2DPoint
aRightUp(fDistance
, fTopEdge
);
308 const basegfx::B2DPoint
aRightDown(fDistance
, fBottomRight
);
310 aRetval
.push_back(impCreatePart(rLineAttribute
, aObjectMatrix
, aRightDown
, aRightUp
, false, false));
312 // text horizontal position
313 if(MEASURETEXTPOSITION_NEGATIVE
== eHorizontal
)
316 const double fSmall(fArrowsOutsideLen
* 0.18);
317 fTextX
= aMainLeft
.getX() - (fStartArrowH
+ aTextRange
.getWidth() + fSmall
+ fHalfLineWidth
);
319 if(bMainLineSplitted
)
321 fTextX
-= (fArrowsOutsideLen
- fStartArrowH
);
324 if(!rTextAttribute
.isDefault())
326 fTextX
-= rTextAttribute
.getTextRightDistance();
329 else if(MEASURETEXTPOSITION_POSITIVE
== eHorizontal
)
332 const double fSmall(fArrowsOutsideLen
* 0.18);
333 fTextX
= aMainRight
.getX() + (fEndArrowH
+ fSmall
+ fHalfLineWidth
);
335 if(bMainLineSplitted
)
337 fTextX
+= (fArrowsOutsideLen
- fEndArrowH
);
340 if(!rTextAttribute
.isDefault())
342 fTextX
+= rTextAttribute
.getTextLeftDistance();
345 else // MEASURETEXTPOSITION_CENTERED
348 fTextX
= aMainLeft
.getX() + ((fDistance
- aTextRange
.getWidth()) * 0.5);
350 if(!rTextAttribute
.isDefault())
352 fTextX
+= (rTextAttribute
.getTextLeftDistance() - rTextAttribute
.getTextRightDistance()) / 2L;
356 // text vertical position
357 if(MEASURETEXTPOSITION_NEGATIVE
== eVertical
)
360 const double fSmall(fArrowsOutsideLen
* 0.10);
361 fTextY
= aMainLeft
.getY() - (aTextRange
.getHeight() + fSmall
+ fHalfLineWidth
);
363 if(!rTextAttribute
.isDefault())
365 fTextY
-= rTextAttribute
.getTextLowerDistance();
368 else if(MEASURETEXTPOSITION_POSITIVE
== eVertical
)
371 const double fSmall(fArrowsOutsideLen
* 0.10);
372 fTextY
= aMainLeft
.getY() + (fSmall
+ fHalfLineWidth
);
374 if(!rTextAttribute
.isDefault())
376 fTextY
+= rTextAttribute
.getTextUpperDistance();
379 else // MEASURETEXTPOSITION_CENTERED
382 fTextY
= aMainLeft
.getY() - (aTextRange
.getHeight() * 0.5);
384 if(!rTextAttribute
.isDefault())
386 fTextY
+= (rTextAttribute
.getTextUpperDistance() - rTextAttribute
.getTextLowerDistance()) / 2L;
391 if(getSdrLSTAttribute().getLine().isDefault())
393 // embed line geometry to invisible (100% transparent) line group for HitTest
394 aRetval
= Primitive2DContainer
{
395 new HiddenGeometryPrimitive2D(std::move(aRetval
))
401 // create transformation to text primitive end position
402 basegfx::B2DHomMatrix aChange
;
404 // handle auto text rotation
407 aChange
.rotate(M_PI
);
410 // move from aTextRange.TopLeft to fTextX, fTextY
411 aChange
.translate(fTextX
- aTextRange
.getMinX(), fTextY
- aTextRange
.getMinY());
413 // apply object matrix
414 aChange
*= aObjectMatrix
;
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 (!)");
421 // add to local primitives
422 aRetval
.push_back(pNewBlockText
);
426 if(!getSdrLSTAttribute().getShadow().isDefault())
428 aRetval
= createEmbeddedShadowPrimitive(
430 getSdrLSTAttribute().getShadow());
433 return new GroupPrimitive2D(std::move(aRetval
));
436 SdrMeasurePrimitive2D::SdrMeasurePrimitive2D(
437 const attribute::SdrLineEffectsTextAttribute
& rSdrLSTAttribute
,
438 const basegfx::B2DPoint
& rStart
,
439 const basegfx::B2DPoint
& rEnd
,
440 MeasureTextPosition eHorizontal
,
441 MeasureTextPosition eVertical
,
450 : maSdrLSTAttribute(rSdrLSTAttribute
),
453 meHorizontal(eHorizontal
),
454 meVertical(eVertical
),
455 mfDistance(fDistance
),
458 mfLeftDelta(fLeftDelta
),
459 mfRightDelta(fRightDelta
),
461 mbTextRotation(bTextRotation
),
462 mbTextAutoAngle(bTextAutoAngle
)
466 bool SdrMeasurePrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
468 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
470 const SdrMeasurePrimitive2D
& rCompare
= static_cast<const SdrMeasurePrimitive2D
&>(rPrimitive
);
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());
491 sal_uInt32
SdrMeasurePrimitive2D::getPrimitive2DID() const
493 return PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D
;
496 } // end of namespace
498 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */