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