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