1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "precompiled_svx.hxx"
25 #include <svx/sdr/primitive2d/sdrmeasureprimitive2d.hxx>
26 #include <svx/sdr/primitive2d/sdrdecompositiontools.hxx>
27 #include <basegfx/matrix/b2dhommatrix.hxx>
28 #include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx>
29 #include <svx/sdr/attribute/sdrtextattribute.hxx>
30 #include <basegfx/polygon/b2dpolypolygontools.hxx>
31 #include <basegfx/tools/canvastools.hxx>
32 #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
33 #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
34 #include <basegfx/matrix/b2dhommatrixtools.hxx>
35 #include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
36 
37 //////////////////////////////////////////////////////////////////////////////
38 
39 using namespace com::sun::star;
40 
41 //////////////////////////////////////////////////////////////////////////////
42 
43 namespace drawinglayer
44 {
45     namespace primitive2d
46     {
impCreatePart(const attribute::SdrLineAttribute & rLineAttribute,const basegfx::B2DHomMatrix & rObjectMatrix,const basegfx::B2DPoint & rStart,const basegfx::B2DPoint & rEnd,bool bLeftActive,bool bRightActive) const47         Primitive2DReference SdrMeasurePrimitive2D::impCreatePart(
48             const attribute::SdrLineAttribute& rLineAttribute,
49             const basegfx::B2DHomMatrix& rObjectMatrix,
50             const basegfx::B2DPoint& rStart,
51             const basegfx::B2DPoint& rEnd,
52             bool bLeftActive,
53             bool bRightActive) const
54         {
55             const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
56             basegfx::B2DPolygon aPolygon;
57 
58             aPolygon.append(rStart);
59             aPolygon.append(rEnd);
60             aPolygon.transform(rObjectMatrix);
61 
62             if(rLineStartEnd.isDefault() || (!bLeftActive && !bRightActive))
63             {
64                 return createPolygonLinePrimitive(
65                     aPolygon,
66                     rLineAttribute,
67                     attribute::SdrLineStartEndAttribute());
68             }
69 
70             if(bLeftActive && bRightActive)
71             {
72                 return createPolygonLinePrimitive(
73                     aPolygon,
74                     rLineAttribute,
75                     rLineStartEnd);
76             }
77 
78             const basegfx::B2DPolyPolygon aEmpty;
79             const attribute::SdrLineStartEndAttribute aLineStartEnd(
80                 bLeftActive ? rLineStartEnd.getStartPolyPolygon() : aEmpty, bRightActive ? rLineStartEnd.getEndPolyPolygon() : aEmpty,
81                 bLeftActive ? rLineStartEnd.getStartWidth() : 0.0, bRightActive ? rLineStartEnd.getEndWidth() : 0.0,
82                 bLeftActive ? rLineStartEnd.isStartActive() : false, bRightActive ? rLineStartEnd.isEndActive() : false,
83                 bLeftActive ? rLineStartEnd.isStartCentered() : false, bRightActive? rLineStartEnd.isEndCentered() : false);
84 
85             return createPolygonLinePrimitive(
86                 aPolygon,
87                 rLineAttribute,
88                 aLineStartEnd);
89         }
90 
create2DDecomposition(const geometry::ViewInformation2D & aViewInformation) const91 		Primitive2DSequence SdrMeasurePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& aViewInformation) const
92 		{
93 			Primitive2DSequence aRetval;
94 			SdrBlockTextPrimitive2D* pBlockText = 0;
95 			basegfx::B2DRange aTextRange;
96 			double fTextX((getStart().getX() + getEnd().getX()) * 0.5);
97 			double fTextY((getStart().getX() + getEnd().getX()) * 0.5);
98 			const basegfx::B2DVector aLine(getEnd() - getStart());
99 			const double fDistance(aLine.getLength());
100 			const double fAngle(atan2(aLine.getY(), aLine.getX()));
101 			bool bAutoUpsideDown(false);
102 			const attribute::SdrTextAttribute rTextAttribute = getSdrLSTAttribute().getText();
103             const basegfx::B2DHomMatrix aObjectMatrix(
104                 basegfx::tools::createShearXRotateTranslateB2DHomMatrix(0.0, fAngle, getStart()));
105 
106             // preapare text, but do not add yet; it needs to be aligned to
107             // the line geometry
108 			if(!rTextAttribute.isDefault())
109 			{
110 				basegfx::B2DHomMatrix aTextMatrix;
111 				double fTestAngle(fAngle);
112 
113 				if(getTextRotation())
114 				{
115 					aTextMatrix.rotate(-90.0 * F_PI180);
116 					fTestAngle -= (90.0 * F_PI180);
117 
118 					if(getTextAutoAngle() && fTestAngle < -F_PI)
119 					{
120 						fTestAngle += F_2PI;
121 					}
122 				}
123 
124 				if(getTextAutoAngle())
125 				{
126 					if(fTestAngle > (F_PI / 4.0) || fTestAngle < (-F_PI * (3.0 / 4.0)))
127 					{
128 						bAutoUpsideDown = true;
129 					}
130 				}
131 
132 				// create primitive and get text range
133 				pBlockText = new SdrBlockTextPrimitive2D(
134                     &rTextAttribute.getSdrText(),
135                     rTextAttribute.getOutlinerParaObject(),
136                     aTextMatrix,
137                     SDRTEXTHORZADJUST_CENTER,
138                     SDRTEXTVERTADJUST_CENTER,
139                     rTextAttribute.isScroll(),
140                     false,
141                     false,
142                     false,
143 					false);
144 
145                 aTextRange = pBlockText->getB2DRange(aViewInformation);
146 			}
147 
148             // prepare line attribute and result
149 			{
150 	            const attribute::SdrLineAttribute rLineAttribute(getSdrLSTAttribute().getLine());
151 				bool bArrowsOutside(false);
152 				bool bMainLineSplitted(false);
153 				const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
154 				double fStartArrowW(0.0);
155 				double fStartArrowH(0.0);
156 				double fEndArrowW(0.0);
157 				double fEndArrowH(0.0);
158 
159 				if(!rLineStartEnd.isDefault())
160 				{
161 					if(rLineStartEnd.isStartActive())
162 					{
163 						const basegfx::B2DRange aArrowRange(basegfx::tools::getRange(rLineStartEnd.getStartPolyPolygon()));
164 						fStartArrowW = rLineStartEnd.getStartWidth();
165 						fStartArrowH = aArrowRange.getHeight() * fStartArrowW / aArrowRange.getWidth();
166 
167 						if(rLineStartEnd.isStartCentered())
168 						{
169 							fStartArrowH *= 0.5;
170 						}
171 					}
172 
173 					if(rLineStartEnd.isEndActive())
174 					{
175 						const basegfx::B2DRange aArrowRange(basegfx::tools::getRange(rLineStartEnd.getEndPolyPolygon()));
176 						fEndArrowW = rLineStartEnd.getEndWidth();
177 						fEndArrowH = aArrowRange.getHeight() * fEndArrowW / aArrowRange.getWidth();
178 
179 						if(rLineStartEnd.isEndCentered())
180 						{
181 							fEndArrowH *= 0.5;
182 						}
183 					}
184 				}
185 
186 				const double fSpaceNeededByArrows(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.5));
187 				const double fArrowsOutsideLen((fStartArrowH + fEndArrowH + fStartArrowW + fEndArrowW) * 0.5);
188 				const double fHalfLineWidth(rLineAttribute.getWidth() * 0.5);
189 
190 				if(fSpaceNeededByArrows > fDistance)
191 				{
192 					bArrowsOutside = true;
193 				}
194 
195 				MeasureTextPosition eHorizontal(getHorizontal());
196 				MeasureTextPosition eVertical(getVertical());
197 
198 				if(MEASURETEXTPOSITION_AUTOMATIC == eVertical)
199 				{
200 					eVertical = MEASURETEXTPOSITION_NEGATIVE;
201 				}
202 
203 				if(MEASURETEXTPOSITION_CENTERED == eVertical)
204 				{
205 					bMainLineSplitted = true;
206 				}
207 
208 				if(MEASURETEXTPOSITION_AUTOMATIC == eHorizontal)
209 				{
210 					if(aTextRange.getWidth() > fDistance)
211 					{
212 						eHorizontal = MEASURETEXTPOSITION_NEGATIVE;
213 					}
214 					else
215 					{
216 						eHorizontal = MEASURETEXTPOSITION_CENTERED;
217 					}
218 
219 					if(bMainLineSplitted)
220 					{
221 						if(aTextRange.getWidth() + fSpaceNeededByArrows > fDistance)
222 						{
223 							bArrowsOutside = true;
224 						}
225 					}
226 					else
227 					{
228 						const double fSmallArrowNeed(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.125));
229 
230 						if(aTextRange.getWidth() + fSmallArrowNeed > fDistance)
231 						{
232 							bArrowsOutside = true;
233 						}
234 					}
235 				}
236 
237 				if(MEASURETEXTPOSITION_CENTERED != eHorizontal)
238 				{
239 					bArrowsOutside = true;
240 				}
241 
242 				// switch text above/below?
243 				if(getBelow() || (bAutoUpsideDown && !getTextRotation()))
244 				{
245 					if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
246 					{
247 						eVertical = MEASURETEXTPOSITION_POSITIVE;
248 					}
249 					else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
250 					{
251 						eVertical = MEASURETEXTPOSITION_NEGATIVE;
252 					}
253 				}
254 
255 				const double fMainLineOffset(getBelow() ? getDistance() : -getDistance());
256 				const basegfx::B2DPoint aMainLeft(0.0, fMainLineOffset);
257 				const basegfx::B2DPoint aMainRight(fDistance, fMainLineOffset);
258 
259 				// main line
260 				if(bArrowsOutside)
261 				{
262 					double fLenLeft(fArrowsOutsideLen);
263 					double fLenRight(fArrowsOutsideLen);
264 
265 					if(!bMainLineSplitted)
266 					{
267 						if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
268 						{
269 							fLenLeft = fStartArrowH + aTextRange.getWidth();
270 						}
271 						else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
272 						{
273 							fLenRight = fEndArrowH + aTextRange.getWidth();
274 						}
275 					}
276 
277 					const basegfx::B2DPoint aMainLeftLeft(aMainLeft.getX() - fLenLeft, aMainLeft.getY());
278 					const basegfx::B2DPoint aMainRightRight(aMainRight.getX() + fLenRight, aMainRight.getY());
279 
280 					appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeftLeft, aMainLeft, false, true));
281 					appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainRight, aMainRightRight, true, false));
282 
283 					if(!bMainLineSplitted || MEASURETEXTPOSITION_CENTERED != eHorizontal)
284 					{
285 						appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, false, false));
286 					}
287 				}
288 				else
289 				{
290 					if(bMainLineSplitted)
291 					{
292 						const double fHalfLength((fDistance - (aTextRange.getWidth() + (fStartArrowH + fEndArrowH) * 0.25)) * 0.5);
293 						const basegfx::B2DPoint aMainInnerLeft(aMainLeft.getX() + fHalfLength, aMainLeft.getY());
294 						const basegfx::B2DPoint aMainInnerRight(aMainRight.getX() - fHalfLength, aMainRight.getY());
295 
296 						appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainInnerLeft, true, false));
297 						appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainInnerRight, aMainRight, false, true));
298 					}
299 					else
300 					{
301 						appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, true, true));
302 					}
303 				}
304 
305 				// left/right help line value preparation
306 				const double fTopEdge(getBelow() ? getUpper() + getDistance() : -getUpper() - getDistance());
307 				const double fBottomLeft(getBelow() ? getLower() - getLeftDelta() : getLeftDelta() - getLower());
308 				const double fBottomRight(getBelow() ? getLower() - getRightDelta() : getRightDelta() - getLower());
309 
310 				// left help line
311 				const basegfx::B2DPoint aLeftUp(0.0, fTopEdge);
312 				const basegfx::B2DPoint aLeftDown(0.0, fBottomLeft);
313 
314 				appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aLeftDown, aLeftUp, false, false));
315 
316 				// right help line
317 				const basegfx::B2DPoint aRightUp(fDistance, fTopEdge);
318 				const basegfx::B2DPoint aRightDown(fDistance, fBottomRight);
319 
320 				appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aRightDown, aRightUp, false, false));
321 
322 				// text horizontal position
323 				if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
324 				{
325 					// left
326 					const double fSmall(fArrowsOutsideLen * 0.18);
327 					fTextX = aMainLeft.getX() - (fStartArrowH + aTextRange.getWidth() + fSmall + fHalfLineWidth);
328 
329 					if(bMainLineSplitted)
330 					{
331 						fTextX -= (fArrowsOutsideLen - fStartArrowH);
332 					}
333 
334 					if(!rTextAttribute.isDefault())
335 					{
336 						fTextX -= rTextAttribute.getTextRightDistance();
337 					}
338 				}
339 				else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
340 				{
341 					// right
342 					const double fSmall(fArrowsOutsideLen * 0.18);
343 					fTextX = aMainRight.getX() + (fEndArrowH + fSmall + fHalfLineWidth);
344 
345 					if(bMainLineSplitted)
346 					{
347 						fTextX += (fArrowsOutsideLen - fEndArrowH);
348 					}
349 
350 					if(!rTextAttribute.isDefault())
351 					{
352 						fTextX += rTextAttribute.getTextLeftDistance();
353 					}
354 				}
355 				else // MEASURETEXTPOSITION_CENTERED
356 				{
357 					// centered
358 					fTextX = aMainLeft.getX() + ((fDistance - aTextRange.getWidth()) * 0.5);
359 
360 					if(!rTextAttribute.isDefault())
361 					{
362 						fTextX += (rTextAttribute.getTextLeftDistance() - rTextAttribute.getTextRightDistance()) / 2L;
363 					}
364 				}
365 
366 				// text vertical position
367 				if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
368 				{
369 					// top
370 					const double fSmall(fArrowsOutsideLen * 0.10);
371 					fTextY = aMainLeft.getY() - (aTextRange.getHeight() + fSmall + fHalfLineWidth);
372 
373 					if(!rTextAttribute.isDefault())
374 					{
375 						fTextY -= rTextAttribute.getTextLowerDistance();
376 					}
377 				}
378 				else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
379 				{
380 					// bottom
381 					const double fSmall(fArrowsOutsideLen * 0.10);
382 					fTextY = aMainLeft.getY() + (fSmall + fHalfLineWidth);
383 
384 					if(!rTextAttribute.isDefault())
385 					{
386 						fTextY += rTextAttribute.getTextUpperDistance();
387 					}
388 				}
389 				else // MEASURETEXTPOSITION_CENTERED
390 				{
391 					// centered
392 					fTextY = aMainLeft.getY() - (aTextRange.getHeight() * 0.5);
393 
394 					if(!rTextAttribute.isDefault())
395 					{
396 						fTextY += (rTextAttribute.getTextUpperDistance() - rTextAttribute.getTextLowerDistance()) / 2L;
397 					}
398 				}
399 			}
400 
401 			if(getSdrLSTAttribute().getLine().isDefault())
402             {
403                 // embed line geometry to invisible (100% transparent) line group for HitTest
404                 const Primitive2DReference xHiddenLines(new HiddenGeometryPrimitive2D(aRetval));
405 
406 				aRetval = Primitive2DSequence(&xHiddenLines, 1);
407             }
408 
409             if(pBlockText)
410 			{
411 				// create transformation to text primitive end position
412 				basegfx::B2DHomMatrix aChange;
413 
414 				// handle auto text rotation
415 				if(bAutoUpsideDown)
416 				{
417 					aChange.rotate(F_PI);
418 				}
419 
420 				// move from aTextRange.TopLeft to fTextX, fTextY
421 				aChange.translate(fTextX - aTextRange.getMinX(), fTextY - aTextRange.getMinY());
422 
423 				// apply object matrix
424 				aChange *= aObjectMatrix;
425 
426 				// apply to existing text primitive
427 				SdrTextPrimitive2D* pNewBlockText = pBlockText->createTransformedClone(aChange);
428 				OSL_ENSURE(pNewBlockText, "SdrMeasurePrimitive2D::create2DDecomposition: Could not create transformed clone of text primitive (!)");
429 				delete pBlockText;
430 
431 				// add to local primitives
432 				appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, Primitive2DReference(pNewBlockText));
433 			}
434 
435 			// add shadow
436 			if(!getSdrLSTAttribute().getShadow().isDefault())
437 			{
438                 aRetval = createEmbeddedShadowPrimitive(
439 					aRetval,
440 					getSdrLSTAttribute().getShadow());
441 			}
442 
443 			return aRetval;
444 		}
445 
SdrMeasurePrimitive2D(const attribute::SdrLineShadowTextAttribute & rSdrLSTAttribute,const basegfx::B2DPoint & rStart,const basegfx::B2DPoint & rEnd,MeasureTextPosition eHorizontal,MeasureTextPosition eVertical,double fDistance,double fUpper,double fLower,double fLeftDelta,double fRightDelta,bool bBelow,bool bTextRotation,bool bTextAutoAngle)446 		SdrMeasurePrimitive2D::SdrMeasurePrimitive2D(
447 			const attribute::SdrLineShadowTextAttribute& rSdrLSTAttribute,
448 			const basegfx::B2DPoint& rStart,
449 			const basegfx::B2DPoint& rEnd,
450 			MeasureTextPosition eHorizontal,
451 			MeasureTextPosition eVertical,
452 			double fDistance,
453 			double fUpper,
454 			double fLower,
455 			double fLeftDelta,
456 			double fRightDelta,
457 			bool bBelow,
458 			bool bTextRotation,
459 			bool bTextAutoAngle)
460 		:	BufferedDecompositionPrimitive2D(),
461 			maSdrLSTAttribute(rSdrLSTAttribute),
462 			maStart(rStart),
463 			maEnd(rEnd),
464 			meHorizontal(eHorizontal),
465 			meVertical(eVertical),
466 			mfDistance(fDistance),
467 			mfUpper(fUpper),
468 			mfLower(fLower),
469 			mfLeftDelta(fLeftDelta),
470 			mfRightDelta(fRightDelta),
471 			mbBelow(bBelow),
472 			mbTextRotation(bTextRotation),
473 			mbTextAutoAngle(bTextAutoAngle)
474 		{
475 		}
476 
operator ==(const BasePrimitive2D & rPrimitive) const477 		bool SdrMeasurePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
478 		{
479 			if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
480 			{
481 				const SdrMeasurePrimitive2D& rCompare = (SdrMeasurePrimitive2D&)rPrimitive;
482 
483 				return (getStart() == rCompare.getStart()
484 					&& getEnd() == rCompare.getEnd()
485 					&& getHorizontal() == rCompare.getHorizontal()
486 					&& getVertical() == rCompare.getVertical()
487 					&& getDistance() == rCompare.getDistance()
488 					&& getUpper() == rCompare.getUpper()
489 					&& getLower() == rCompare.getLower()
490 					&& getLeftDelta() == rCompare.getLeftDelta()
491 					&& getRightDelta() == rCompare.getRightDelta()
492 					&& getBelow() == rCompare.getBelow()
493 					&& getTextRotation() == rCompare.getTextRotation()
494 					&& getTextAutoAngle() == rCompare.getTextAutoAngle()
495 					&& getSdrLSTAttribute() == rCompare.getSdrLSTAttribute());
496 			}
497 
498 			return false;
499 		}
500 
501 		// provide unique ID
502 		ImplPrimitrive2DIDBlock(SdrMeasurePrimitive2D, PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D)
503 
504 	} // end of namespace primitive2d
505 } // end of namespace drawinglayer
506 
507 //////////////////////////////////////////////////////////////////////////////
508 // eof
509