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