1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 #include "precompiled_svx.hxx" 29 #include <svx/sdr/primitive2d/sdrdecompositiontools.hxx> 30 #include <drawinglayer/primitive2d/baseprimitive2d.hxx> 31 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> 32 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> 33 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> 34 #include <basegfx/polygon/b2dpolypolygontools.hxx> 35 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> 36 #include <drawinglayer/attribute/strokeattribute.hxx> 37 #include <drawinglayer/attribute/linestartendattribute.hxx> 38 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx> 39 #include <drawinglayer/attribute/sdrfillbitmapattribute.hxx> 40 #include <basegfx/matrix/b2dhommatrix.hxx> 41 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx> 42 #include <svx/sdr/attribute/sdrtextattribute.hxx> 43 #include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx> 44 #include <svx/svdotext.hxx> 45 #include <basegfx/polygon/b2dpolygontools.hxx> 46 #include <drawinglayer/primitive2d/animatedprimitive2d.hxx> 47 #include <drawinglayer/animation/animationtiming.hxx> 48 #include <drawinglayer/primitive2d/maskprimitive2d.hxx> 49 #include <basegfx/tools/canvastools.hxx> 50 #include <drawinglayer/geometry/viewinformation2d.hxx> 51 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> 52 #include <drawinglayer/attribute/sdrfillattribute.hxx> 53 #include <drawinglayer/attribute/sdrlineattribute.hxx> 54 #include <drawinglayer/attribute/sdrlinestartendattribute.hxx> 55 #include <drawinglayer/attribute/sdrshadowattribute.hxx> 56 57 ////////////////////////////////////////////////////////////////////////////// 58 59 using namespace com::sun::star; 60 61 ////////////////////////////////////////////////////////////////////////////// 62 63 namespace drawinglayer 64 { 65 namespace primitive2d 66 { 67 Primitive2DReference createPolyPolygonFillPrimitive( 68 const basegfx::B2DPolyPolygon& rUnitPolyPolygon, 69 const basegfx::B2DHomMatrix& rObjectTransform, 70 const attribute::SdrFillAttribute& rFill, 71 const attribute::FillGradientAttribute& rFillGradient) 72 { 73 // prepare fully scaled polygon 74 basegfx::B2DPolyPolygon aScaledPolyPolygon(rUnitPolyPolygon); 75 aScaledPolyPolygon.transform(rObjectTransform); 76 BasePrimitive2D* pNewFillPrimitive = 0; 77 78 if(!rFill.getGradient().isDefault()) 79 { 80 pNewFillPrimitive = new PolyPolygonGradientPrimitive2D(aScaledPolyPolygon, rFill.getGradient()); 81 } 82 else if(!rFill.getHatch().isDefault()) 83 { 84 pNewFillPrimitive = new PolyPolygonHatchPrimitive2D(aScaledPolyPolygon, rFill.getColor(), rFill.getHatch()); 85 } 86 else if(!rFill.getBitmap().isDefault()) 87 { 88 const basegfx::B2DRange aRange(basegfx::tools::getRange(aScaledPolyPolygon)); 89 pNewFillPrimitive = new PolyPolygonBitmapPrimitive2D(aScaledPolyPolygon, rFill.getBitmap().getFillBitmapAttribute(aRange)); 90 } 91 else 92 { 93 pNewFillPrimitive = new PolyPolygonColorPrimitive2D(aScaledPolyPolygon, rFill.getColor()); 94 } 95 96 if(0.0 != rFill.getTransparence()) 97 { 98 // create simpleTransparencePrimitive, add created fill primitive 99 const Primitive2DReference xRefA(pNewFillPrimitive); 100 const Primitive2DSequence aContent(&xRefA, 1L); 101 return Primitive2DReference(new UnifiedTransparencePrimitive2D(aContent, rFill.getTransparence())); 102 } 103 else if(!rFillGradient.isDefault()) 104 { 105 // create sequence with created fill primitive 106 const Primitive2DReference xRefA(pNewFillPrimitive); 107 const Primitive2DSequence aContent(&xRefA, 1L); 108 109 // create FillGradientPrimitive2D for transparence and add to new sequence 110 // fillGradientPrimitive is enough here (compared to PolyPolygonGradientPrimitive2D) since float transparence will be masked anyways 111 const basegfx::B2DRange aRange(basegfx::tools::getRange(aScaledPolyPolygon)); 112 const Primitive2DReference xRefB(new FillGradientPrimitive2D(aRange, rFillGradient)); 113 const Primitive2DSequence aAlpha(&xRefB, 1L); 114 115 // create TransparencePrimitive2D using alpha and content 116 return Primitive2DReference(new TransparencePrimitive2D(aContent, aAlpha)); 117 } 118 else 119 { 120 // add to decomposition 121 return Primitive2DReference(pNewFillPrimitive); 122 } 123 } 124 125 Primitive2DReference createPolygonLinePrimitive( 126 const basegfx::B2DPolygon& rUnitPolygon, 127 const basegfx::B2DHomMatrix& rObjectTransform, 128 const attribute::SdrLineAttribute& rLine, 129 const attribute::SdrLineStartEndAttribute& rStroke) 130 { 131 // prepare fully scaled polygon 132 basegfx::B2DPolygon aScaledPolygon(rUnitPolygon); 133 aScaledPolygon.transform(rObjectTransform); 134 135 // create line and stroke attribute 136 const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin()); 137 const attribute::StrokeAttribute aStrokeAttribute(rLine.getDotDashArray(), rLine.getFullDotDashLen()); 138 BasePrimitive2D* pNewLinePrimitive = 0L; 139 140 if(!rUnitPolygon.isClosed() && !rStroke.isDefault()) 141 { 142 attribute::LineStartEndAttribute aStart(rStroke.getStartWidth(), rStroke.getStartPolyPolygon(), rStroke.isStartCentered()); 143 attribute::LineStartEndAttribute aEnd(rStroke.getEndWidth(), rStroke.getEndPolyPolygon(), rStroke.isEndCentered()); 144 145 // create data 146 pNewLinePrimitive = new PolygonStrokeArrowPrimitive2D(aScaledPolygon, aLineAttribute, aStrokeAttribute, aStart, aEnd); 147 } 148 else 149 { 150 // create data 151 pNewLinePrimitive = new PolygonStrokePrimitive2D(aScaledPolygon, aLineAttribute, aStrokeAttribute); 152 } 153 154 if(0.0 != rLine.getTransparence()) 155 { 156 // create simpleTransparencePrimitive, add created fill primitive 157 const Primitive2DReference xRefA(pNewLinePrimitive); 158 const Primitive2DSequence aContent(&xRefA, 1L); 159 return Primitive2DReference(new UnifiedTransparencePrimitive2D(aContent, rLine.getTransparence())); 160 } 161 else 162 { 163 // add to decomposition 164 return Primitive2DReference(pNewLinePrimitive); 165 } 166 } 167 168 Primitive2DReference createTextPrimitive( 169 const basegfx::B2DPolyPolygon& rUnitPolyPolygon, 170 const basegfx::B2DHomMatrix& rObjectTransform, 171 const attribute::SdrTextAttribute& rText, 172 const attribute::SdrLineAttribute& rStroke, 173 bool bCellText, 174 bool bWordWrap, 175 bool bClipOnBounds) 176 { 177 basegfx::B2DHomMatrix aAnchorTransform(rObjectTransform); 178 SdrTextPrimitive2D* pNew = 0; 179 180 if(rText.isContour()) 181 { 182 // contour text 183 if(!rStroke.isDefault() && 0.0 != rStroke.getWidth()) 184 { 185 // take line width into account and shrink contour polygon accordingly 186 // decompose to get scale 187 basegfx::B2DVector aScale, aTranslate; 188 double fRotate, fShearX; 189 rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX); 190 191 // scale outline to object's size to allow growing with value relative to that size 192 // and also to keep aspect ratio 193 basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon); 194 aScaledUnitPolyPolygon.transform(basegfx::tools::createScaleB2DHomMatrix( 195 fabs(aScale.getX()), fabs(aScale.getY()))); 196 197 // grow the polygon. To shrink, use negative value (half width) 198 aScaledUnitPolyPolygon = basegfx::tools::growInNormalDirection(aScaledUnitPolyPolygon, -(rStroke.getWidth() * 0.5)); 199 200 // scale back to unit polygon 201 aScaledUnitPolyPolygon.transform(basegfx::tools::createScaleB2DHomMatrix( 202 0.0 != aScale.getX() ? 1.0 / aScale.getX() : 1.0, 203 0.0 != aScale.getY() ? 1.0 / aScale.getY() : 1.0)); 204 205 // create with unit polygon 206 pNew = new SdrContourTextPrimitive2D( 207 &rText.getSdrText(), 208 rText.getOutlinerParaObject(), 209 aScaledUnitPolyPolygon, 210 rObjectTransform); 211 } 212 else 213 { 214 // create with unit polygon 215 pNew = new SdrContourTextPrimitive2D( 216 &rText.getSdrText(), 217 rText.getOutlinerParaObject(), 218 rUnitPolyPolygon, 219 rObjectTransform); 220 } 221 } 222 else if(!rText.getSdrFormTextAttribute().isDefault()) 223 { 224 // text on path, use scaled polygon 225 basegfx::B2DPolyPolygon aScaledPolyPolygon(rUnitPolyPolygon); 226 aScaledPolyPolygon.transform(rObjectTransform); 227 pNew = new SdrPathTextPrimitive2D( 228 &rText.getSdrText(), 229 rText.getOutlinerParaObject(), 230 aScaledPolyPolygon, 231 rText.getSdrFormTextAttribute()); 232 } 233 else 234 { 235 // rObjectTransform is the whole SdrObject transformation from unit rectangle 236 // to it's size and position. Decompose to allow working with single values. 237 basegfx::B2DVector aScale, aTranslate; 238 double fRotate, fShearX; 239 rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX); 240 241 // extract mirroring 242 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0)); 243 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0)); 244 aScale = basegfx::absolute(aScale); 245 246 // Get the real size, since polygon ountline and scale 247 // from the object transformation may vary (e.g. ellipse segments) 248 basegfx::B2DHomMatrix aJustScaleTransform; 249 aJustScaleTransform.set(0, 0, aScale.getX()); 250 aJustScaleTransform.set(1, 1, aScale.getY()); 251 basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon); 252 aScaledUnitPolyPolygon.transform(aJustScaleTransform); 253 const basegfx::B2DRange aSnapRange(basegfx::tools::getRange(aScaledUnitPolyPolygon)); 254 255 // create a range describing the wanted text position and size (aTextAnchorRange). This 256 // means to use the text distance values here 257 const basegfx::B2DPoint aTopLeft(aSnapRange.getMinX() + rText.getTextLeftDistance(), aSnapRange.getMinY() + rText.getTextUpperDistance()); 258 const basegfx::B2DPoint aBottomRight(aSnapRange.getMaxX() - rText.getTextRightDistance(), aSnapRange.getMaxY() - rText.getTextLowerDistance()); 259 basegfx::B2DRange aTextAnchorRange; 260 aTextAnchorRange.expand(aTopLeft); 261 aTextAnchorRange.expand(aBottomRight); 262 263 // now create a transformation from this basic range (aTextAnchorRange) 264 aAnchorTransform = basegfx::tools::createScaleTranslateB2DHomMatrix( 265 aTextAnchorRange.getWidth(), aTextAnchorRange.getHeight(), 266 aTextAnchorRange.getMinX(), aTextAnchorRange.getMinY()); 267 268 // apply mirroring 269 aAnchorTransform.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0); 270 271 // apply object's other transforms 272 aAnchorTransform = basegfx::tools::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate) 273 * aAnchorTransform; 274 275 if(rText.isFitToSize()) 276 { 277 // streched text in range 278 pNew = new SdrStretchTextPrimitive2D( 279 &rText.getSdrText(), 280 rText.getOutlinerParaObject(), 281 aAnchorTransform, 282 rText.isFixedCellHeight()); 283 } 284 else // text in range 285 { 286 // build new primitive 287 pNew = new SdrBlockTextPrimitive2D( 288 &rText.getSdrText(), 289 rText.getOutlinerParaObject(), 290 aAnchorTransform, 291 rText.getSdrTextHorzAdjust(), 292 rText.getSdrTextVertAdjust(), 293 rText.isFixedCellHeight(), 294 rText.isScroll(), 295 bCellText, 296 bWordWrap, 297 bClipOnBounds); 298 } 299 } 300 301 OSL_ENSURE(pNew != 0, "createTextPrimitive: no text primitive created (!)"); 302 303 if(rText.isBlink()) 304 { 305 // prepare animation and primitive list 306 drawinglayer::animation::AnimationEntryList aAnimationList; 307 rText.getBlinkTextTiming(aAnimationList); 308 309 if(0.0 != aAnimationList.getDuration()) 310 { 311 // create content sequence 312 const Primitive2DReference xRefA(pNew); 313 const Primitive2DSequence aContent(&xRefA, 1L); 314 315 // create and add animated switch primitive 316 return Primitive2DReference(new AnimatedBlinkPrimitive2D(aAnimationList, aContent, true)); 317 } 318 else 319 { 320 // add to decomposition 321 return Primitive2DReference(pNew); 322 } 323 } 324 325 if(rText.isScroll()) 326 { 327 // suppress scroll when FontWork 328 if(rText.getSdrFormTextAttribute().isDefault()) 329 { 330 // get scroll direction 331 const SdrTextAniDirection eDirection(rText.getSdrText().GetObject().GetTextAniDirection()); 332 const bool bHorizontal(SDRTEXTANI_LEFT == eDirection || SDRTEXTANI_RIGHT == eDirection); 333 334 // decompose to get separated values for the scroll box 335 basegfx::B2DVector aScale, aTranslate; 336 double fRotate, fShearX; 337 aAnchorTransform.decompose(aScale, aTranslate, fRotate, fShearX); 338 339 // build transform from scaled only to full AnchorTransform and inverse 340 const basegfx::B2DHomMatrix aSRT(basegfx::tools::createShearXRotateTranslateB2DHomMatrix( 341 fShearX, fRotate, aTranslate)); 342 basegfx::B2DHomMatrix aISRT(aSRT); 343 aISRT.invert(); 344 345 // bring the primitive back to scaled only and get scaled range, create new clone for this 346 SdrTextPrimitive2D* pNew2 = pNew->createTransformedClone(aISRT); 347 OSL_ENSURE(pNew2, "createTextPrimitive: Could not create transformed clone of text primitive (!)"); 348 delete pNew; 349 pNew = pNew2; 350 351 // create neutral geometry::ViewInformation2D for local range and decompose calls. This is okay 352 // since the decompose is view-independent 353 const uno::Sequence< beans::PropertyValue > xViewParameters; 354 geometry::ViewInformation2D aViewInformation2D(xViewParameters); 355 356 // get range 357 const basegfx::B2DRange aScaledRange(pNew->getB2DRange(aViewInformation2D)); 358 359 // create left outside and right outside transformations. Also take care 360 // of the clip rectangle 361 basegfx::B2DHomMatrix aLeft, aRight; 362 basegfx::B2DPoint aClipTopLeft(0.0, 0.0); 363 basegfx::B2DPoint aClipBottomRight(aScale.getX(), aScale.getY()); 364 365 if(bHorizontal) 366 { 367 aClipTopLeft.setY(aScaledRange.getMinY()); 368 aClipBottomRight.setY(aScaledRange.getMaxY()); 369 aLeft.translate(-aScaledRange.getMaxX(), 0.0); 370 aRight.translate(aScale.getX() - aScaledRange.getMinX(), 0.0); 371 } 372 else 373 { 374 aClipTopLeft.setX(aScaledRange.getMinX()); 375 aClipBottomRight.setX(aScaledRange.getMaxX()); 376 aLeft.translate(0.0, -aScaledRange.getMaxY()); 377 aRight.translate(0.0, aScale.getY() - aScaledRange.getMinY()); 378 } 379 380 aLeft *= aSRT; 381 aRight *= aSRT; 382 383 // prepare animation list 384 drawinglayer::animation::AnimationEntryList aAnimationList; 385 386 if(bHorizontal) 387 { 388 rText.getScrollTextTiming(aAnimationList, aScale.getX(), aScaledRange.getWidth()); 389 } 390 else 391 { 392 rText.getScrollTextTiming(aAnimationList, aScale.getY(), aScaledRange.getHeight()); 393 } 394 395 if(0.0 != aAnimationList.getDuration()) 396 { 397 // create a new Primitive2DSequence containing the animated text in it's scaled only state. 398 // use the decomposition to force to simple text primitives, those will no longer 399 // need the outliner for formatting (alternatively it is also possible to just add 400 // pNew to aNewPrimitiveSequence) 401 Primitive2DSequence aAnimSequence(pNew->get2DDecomposition(aViewInformation2D)); 402 delete pNew; 403 404 // create a new animatedInterpolatePrimitive and add it 405 std::vector< basegfx::B2DHomMatrix > aMatrixStack; 406 aMatrixStack.push_back(aLeft); 407 aMatrixStack.push_back(aRight); 408 const Primitive2DReference xRefA(new AnimatedInterpolatePrimitive2D(aMatrixStack, aAnimationList, aAnimSequence, true)); 409 const Primitive2DSequence aContent(&xRefA, 1L); 410 411 // scrolling needs an encapsulating clipping primitive 412 const basegfx::B2DRange aClipRange(aClipTopLeft, aClipBottomRight); 413 basegfx::B2DPolygon aClipPolygon(basegfx::tools::createPolygonFromRect(aClipRange)); 414 aClipPolygon.transform(aSRT); 415 return Primitive2DReference(new MaskPrimitive2D(basegfx::B2DPolyPolygon(aClipPolygon), aContent)); 416 } 417 else 418 { 419 // add to decomposition 420 return Primitive2DReference(pNew); 421 } 422 } 423 } 424 425 if(rText.isInEditMode()) 426 { 427 // #i97628# 428 // encapsulate with TextHierarchyEditPrimitive2D to allow renderers 429 // to suppress actively edited content if needed 430 const Primitive2DReference xRefA(pNew); 431 const Primitive2DSequence aContent(&xRefA, 1L); 432 433 // create and add TextHierarchyEditPrimitive2D primitive 434 return Primitive2DReference(new TextHierarchyEditPrimitive2D(aContent)); 435 } 436 else 437 { 438 // add to decomposition 439 return Primitive2DReference(pNew); 440 } 441 } 442 443 Primitive2DSequence createEmbeddedShadowPrimitive( 444 const Primitive2DSequence& rContent, 445 const attribute::SdrShadowAttribute& rShadow) 446 { 447 if(rContent.hasElements()) 448 { 449 Primitive2DSequence aRetval(2); 450 basegfx::B2DHomMatrix aShadowOffset; 451 452 // prepare shadow offset 453 aShadowOffset.set(0, 2, rShadow.getOffset().getX()); 454 aShadowOffset.set(1, 2, rShadow.getOffset().getY()); 455 456 // create shadow primitive and add content 457 aRetval[0] = Primitive2DReference( 458 new ShadowPrimitive2D( 459 aShadowOffset, 460 rShadow.getColor(), 461 rContent)); 462 463 if(0.0 != rShadow.getTransparence()) 464 { 465 // create SimpleTransparencePrimitive2D 466 const Primitive2DSequence aTempContent(&aRetval[0], 1); 467 468 aRetval[0] = Primitive2DReference( 469 new UnifiedTransparencePrimitive2D( 470 aTempContent, 471 rShadow.getTransparence())); 472 } 473 474 aRetval[1] = Primitive2DReference(new GroupPrimitive2D(rContent)); 475 return aRetval; 476 } 477 else 478 { 479 return rContent; 480 } 481 } 482 } // end of namespace primitive2d 483 } // end of namespace drawinglayer 484 485 ////////////////////////////////////////////////////////////////////////////// 486 // eof 487