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