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 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_drawinglayer.hxx" 26 27 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx> 28 #include <basegfx/tools/canvastools.hxx> 29 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> 30 #include <basegfx/color/bcolor.hxx> 31 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> 32 #include <vcl/lineinfo.hxx> 33 #include <drawinglayer/attribute/lineattribute.hxx> 34 #include <drawinglayer/attribute/strokeattribute.hxx> 35 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx> 36 #include <vcl/metaact.hxx> 37 #include <drawinglayer/primitive2d/transformprimitive2d.hxx> 38 #include <basegfx/matrix/b2dhommatrixtools.hxx> 39 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> 40 #include <basegfx/polygon/b2dpolygontools.hxx> 41 #include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx> 42 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> 43 #include <vcl/salbtype.hxx> 44 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> 45 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> 46 #include <vcl/svapp.hxx> 47 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> 48 #include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx> 49 #include <drawinglayer/primitive2d/maskprimitive2d.hxx> 50 #include <basegfx/polygon/b2dpolygonclipper.hxx> 51 #include <drawinglayer/primitive2d/invertprimitive2d.hxx> 52 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> 53 #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx> 54 #include <drawinglayer/primitive2d/wallpaperprimitive2d.hxx> 55 #include <drawinglayer/primitive2d/textprimitive2d.hxx> 56 #include <drawinglayer/primitive2d/textlayoutdevice.hxx> 57 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> 58 #include <i18npool/mslangid.hxx> 59 #include <drawinglayer/primitive2d/textlineprimitive2d.hxx> 60 #include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx> 61 #include <drawinglayer/primitive2d/epsprimitive2d.hxx> 62 #include <numeric> 63 64 ////////////////////////////////////////////////////////////////////////////// 65 66 using namespace com::sun::star; 67 68 ////////////////////////////////////////////////////////////////////////////// 69 70 namespace 71 { 72 /** helper class for graphic context 73 74 This class allows to hold a complete status of classic 75 VCL OutputDevice stati. This data is needed for correct 76 interpretation of the MetaFile action flow. 77 */ 78 class PropertyHolder 79 { 80 private: 81 /// current transformation (aka MapMode) 82 basegfx::B2DHomMatrix maTransformation; 83 MapUnit maMapUnit; 84 85 /// current colors 86 basegfx::BColor maLineColor; 87 basegfx::BColor maFillColor; 88 basegfx::BColor maTextColor; 89 basegfx::BColor maTextFillColor; 90 basegfx::BColor maTextLineColor; 91 basegfx::BColor maOverlineColor; 92 93 /// clipping 94 basegfx::B2DPolyPolygon maClipPolyPoygon; 95 96 /// font, etc. 97 Font maFont; 98 RasterOp maRasterOp; 99 sal_uInt32 mnLayoutMode; 100 LanguageType maLanguageType; 101 sal_uInt16 mnPushFlags; 102 103 /// bitfield 104 /// contains all active markers 105 bool mbLineColor : 1; 106 bool mbFillColor : 1; 107 bool mbTextColor : 1; 108 bool mbTextFillColor : 1; 109 bool mbTextLineColor : 1; 110 bool mbOverlineColor : 1; 111 bool mbClipPolyPolygonActive : 1; 112 113 public: PropertyHolder()114 PropertyHolder() 115 : maTransformation(), 116 maMapUnit(MAP_100TH_MM), 117 maLineColor(), 118 maFillColor(), 119 maTextColor(COL_BLACK), 120 maTextFillColor(), 121 maTextLineColor(), 122 maOverlineColor(), 123 maClipPolyPoygon(), 124 maFont(), 125 maRasterOp(ROP_OVERPAINT), 126 mnLayoutMode(0), 127 maLanguageType(0), 128 mnPushFlags(0), 129 mbLineColor(false), 130 mbFillColor(false), 131 mbTextColor(true), 132 mbTextFillColor(false), 133 mbTextLineColor(false), 134 mbOverlineColor(false), 135 mbClipPolyPolygonActive(false) 136 { 137 } 138 ~PropertyHolder()139 ~PropertyHolder() 140 { 141 } 142 143 /// read/write accesses getTransformation() const144 const basegfx::B2DHomMatrix& getTransformation() const { return maTransformation; } setTransformation(const basegfx::B2DHomMatrix & rNew)145 void setTransformation(const basegfx::B2DHomMatrix& rNew) { if(rNew != maTransformation) maTransformation = rNew; } 146 getMapUnit() const147 MapUnit getMapUnit() const { return maMapUnit; } setMapUnit(MapUnit eNew)148 void setMapUnit(MapUnit eNew) { if(eNew != maMapUnit) maMapUnit = eNew; } 149 getLineColor() const150 const basegfx::BColor& getLineColor() const { return maLineColor; } setLineColor(const basegfx::BColor & rNew)151 void setLineColor(const basegfx::BColor& rNew) { if(rNew != maLineColor) maLineColor = rNew; } getLineColorActive() const152 bool getLineColorActive() const { return mbLineColor; } setLineColorActive(bool bNew)153 void setLineColorActive(bool bNew) { if(bNew != mbLineColor) mbLineColor = bNew; } 154 getFillColor() const155 const basegfx::BColor& getFillColor() const { return maFillColor; } setFillColor(const basegfx::BColor & rNew)156 void setFillColor(const basegfx::BColor& rNew) { if(rNew != maFillColor) maFillColor = rNew; } getFillColorActive() const157 bool getFillColorActive() const { return mbFillColor; } setFillColorActive(bool bNew)158 void setFillColorActive(bool bNew) { if(bNew != mbFillColor) mbFillColor = bNew; } 159 getTextColor() const160 const basegfx::BColor& getTextColor() const { return maTextColor; } setTextColor(const basegfx::BColor & rNew)161 void setTextColor(const basegfx::BColor& rNew) { if(rNew != maTextColor) maTextColor = rNew; } getTextColorActive() const162 bool getTextColorActive() const { return mbTextColor; } setTextColorActive(bool bNew)163 void setTextColorActive(bool bNew) { if(bNew != mbTextColor) mbTextColor = bNew; } 164 getTextFillColor() const165 const basegfx::BColor& getTextFillColor() const { return maTextFillColor; } setTextFillColor(const basegfx::BColor & rNew)166 void setTextFillColor(const basegfx::BColor& rNew) { if(rNew != maTextFillColor) maTextFillColor = rNew; } getTextFillColorActive() const167 bool getTextFillColorActive() const { return mbTextFillColor; } setTextFillColorActive(bool bNew)168 void setTextFillColorActive(bool bNew) { if(bNew != mbTextFillColor) mbTextFillColor = bNew; } 169 getTextLineColor() const170 const basegfx::BColor& getTextLineColor() const { return maTextLineColor; } setTextLineColor(const basegfx::BColor & rNew)171 void setTextLineColor(const basegfx::BColor& rNew) { if(rNew != maTextLineColor) maTextLineColor = rNew; } getTextLineColorActive() const172 bool getTextLineColorActive() const { return mbTextLineColor; } setTextLineColorActive(bool bNew)173 void setTextLineColorActive(bool bNew) { if(bNew != mbTextLineColor) mbTextLineColor = bNew; } 174 getOverlineColor() const175 const basegfx::BColor& getOverlineColor() const { return maOverlineColor; } setOverlineColor(const basegfx::BColor & rNew)176 void setOverlineColor(const basegfx::BColor& rNew) { if(rNew != maOverlineColor) maOverlineColor = rNew; } getOverlineColorActive() const177 bool getOverlineColorActive() const { return mbOverlineColor; } setOverlineColorActive(bool bNew)178 void setOverlineColorActive(bool bNew) { if(bNew != mbOverlineColor) mbOverlineColor = bNew; } 179 getClipPolyPolygon() const180 const basegfx::B2DPolyPolygon& getClipPolyPolygon() const { return maClipPolyPoygon; } setClipPolyPolygon(const basegfx::B2DPolyPolygon & rNew)181 void setClipPolyPolygon(const basegfx::B2DPolyPolygon& rNew) { if(rNew != maClipPolyPoygon) maClipPolyPoygon = rNew; } getClipPolyPolygonActive() const182 bool getClipPolyPolygonActive() const { return mbClipPolyPolygonActive; } setClipPolyPolygonActive(bool bNew)183 void setClipPolyPolygonActive(bool bNew) { if(bNew != mbClipPolyPolygonActive) mbClipPolyPolygonActive = bNew; } 184 getFont() const185 const Font& getFont() const { return maFont; } setFont(const Font & rFont)186 void setFont(const Font& rFont) { if(rFont != maFont) maFont = rFont; } 187 getRasterOp() const188 const RasterOp& getRasterOp() const { return maRasterOp; } setRasterOp(const RasterOp & rRasterOp)189 void setRasterOp(const RasterOp& rRasterOp) { if(rRasterOp != maRasterOp) maRasterOp = rRasterOp; } isRasterOpInvert() const190 bool isRasterOpInvert() const { return (ROP_XOR == maRasterOp || ROP_INVERT == maRasterOp); } isRasterOpForceBlack() const191 bool isRasterOpForceBlack() const { return ROP_0 == maRasterOp; } isRasterOpActive() const192 bool isRasterOpActive() const { return isRasterOpInvert() || isRasterOpForceBlack(); } 193 getLayoutMode() const194 sal_uInt32 getLayoutMode() const { return mnLayoutMode; } setLayoutMode(sal_uInt32 nNew)195 void setLayoutMode(sal_uInt32 nNew) { if(nNew != mnLayoutMode) mnLayoutMode = nNew; } 196 getLanguageType() const197 LanguageType getLanguageType() const { return maLanguageType; } setLanguageType(LanguageType aNew)198 void setLanguageType(LanguageType aNew) { if(aNew != maLanguageType) maLanguageType = aNew; } 199 getPushFlags() const200 sal_uInt16 getPushFlags() const { return mnPushFlags; } setPushFlags(sal_uInt16 nNew)201 void setPushFlags(sal_uInt16 nNew) { if(nNew != mnPushFlags) mnPushFlags = nNew; } 202 getLineOrFillActive() const203 bool getLineOrFillActive() const { return (mbLineColor || mbFillColor); } 204 }; 205 } // end of anonymous namespace 206 207 ////////////////////////////////////////////////////////////////////////////// 208 209 namespace 210 { 211 /** stack for properites 212 213 This class builds a stack based on the PropertyHolder 214 class. It encapsulates the pointer/new/delete usage to 215 make it safe and implements the push/pop as needed by a 216 VCL Metafile interpreter. The critical part here are the 217 flag values VCL OutputDevice uses here; not all stuff is 218 pushed and thus needs to be copied at pop. 219 */ 220 class PropertyHolders 221 { 222 private: 223 std::vector< PropertyHolder* > maPropertyHolders; 224 225 public: PropertyHolders()226 PropertyHolders() 227 { 228 maPropertyHolders.push_back(new PropertyHolder()); 229 } 230 size()231 sal_uInt32 size() 232 { 233 return maPropertyHolders.size(); 234 } 235 PushDefault()236 void PushDefault() 237 { 238 PropertyHolder* pNew = new PropertyHolder(); 239 maPropertyHolders.push_back(pNew); 240 } 241 Push(sal_uInt16 nPushFlags)242 void Push(sal_uInt16 nPushFlags) 243 { 244 if(nPushFlags) 245 { 246 OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: PUSH with no property holders (!)"); 247 if ( !maPropertyHolders.empty() ) 248 { 249 PropertyHolder* pNew = new PropertyHolder(*maPropertyHolders.back()); 250 pNew->setPushFlags(nPushFlags); 251 maPropertyHolders.push_back(pNew); 252 } 253 } 254 } 255 Pop()256 void Pop() 257 { 258 OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: POP with no property holders (!)"); 259 const sal_uInt32 nSize(maPropertyHolders.size()); 260 261 if(nSize) 262 { 263 const PropertyHolder* pTip = maPropertyHolders.back(); 264 const sal_uInt16 nPushFlags(pTip->getPushFlags()); 265 266 if(nPushFlags) 267 { 268 if(nSize > 1) 269 { 270 // copy back content for all non-set flags 271 PropertyHolder* pLast = maPropertyHolders[nSize - 2]; 272 273 if(PUSH_ALL != nPushFlags) 274 { 275 if(!(nPushFlags & PUSH_LINECOLOR )) 276 { 277 pLast->setLineColor(pTip->getLineColor()); 278 pLast->setLineColorActive(pTip->getLineColorActive()); 279 } 280 if(!(nPushFlags & PUSH_FILLCOLOR )) 281 { 282 pLast->setFillColor(pTip->getFillColor()); 283 pLast->setFillColorActive(pTip->getFillColorActive()); 284 } 285 if(!(nPushFlags & PUSH_FONT )) 286 { 287 pLast->setFont(pTip->getFont()); 288 } 289 if(!(nPushFlags & PUSH_TEXTCOLOR )) 290 { 291 pLast->setTextColor(pTip->getTextColor()); 292 pLast->setTextColorActive(pTip->getTextColorActive()); 293 } 294 if(!(nPushFlags & PUSH_MAPMODE )) 295 { 296 pLast->setTransformation(pTip->getTransformation()); 297 pLast->setMapUnit(pTip->getMapUnit()); 298 } 299 if(!(nPushFlags & PUSH_CLIPREGION )) 300 { 301 pLast->setClipPolyPolygon(pTip->getClipPolyPolygon()); 302 pLast->setClipPolyPolygonActive(pTip->getClipPolyPolygonActive()); 303 } 304 if(!(nPushFlags & PUSH_RASTEROP )) 305 { 306 pLast->setRasterOp(pTip->getRasterOp()); 307 } 308 if(!(nPushFlags & PUSH_TEXTFILLCOLOR )) 309 { 310 pLast->setTextFillColor(pTip->getTextFillColor()); 311 pLast->setTextFillColorActive(pTip->getTextFillColorActive()); 312 } 313 if(!(nPushFlags & PUSH_TEXTALIGN )) 314 { 315 if(pLast->getFont().GetAlign() != pTip->getFont().GetAlign()) 316 { 317 Font aFont(pLast->getFont()); 318 aFont.SetAlign(pTip->getFont().GetAlign()); 319 pLast->setFont(aFont); 320 } 321 } 322 if(!(nPushFlags & PUSH_REFPOINT )) 323 { 324 // not supported 325 } 326 if(!(nPushFlags & PUSH_TEXTLINECOLOR )) 327 { 328 pLast->setTextLineColor(pTip->getTextLineColor()); 329 pLast->setTextLineColorActive(pTip->getTextLineColorActive()); 330 } 331 if(!(nPushFlags & PUSH_TEXTLAYOUTMODE )) 332 { 333 pLast->setLayoutMode(pTip->getLayoutMode()); 334 } 335 if(!(nPushFlags & PUSH_TEXTLANGUAGE )) 336 { 337 pLast->setLanguageType(pTip->getLanguageType()); 338 } 339 if(!(nPushFlags & PUSH_OVERLINECOLOR )) 340 { 341 pLast->setOverlineColor(pTip->getOverlineColor()); 342 pLast->setOverlineColorActive(pTip->getOverlineColorActive()); 343 } 344 } 345 } 346 } 347 348 // execute the pop 349 delete maPropertyHolders.back(); 350 maPropertyHolders.pop_back(); 351 } 352 } 353 Current()354 PropertyHolder& Current() 355 { 356 static PropertyHolder aDummy; 357 OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: CURRENT with no property holders (!)"); 358 return maPropertyHolders.empty() ? aDummy : *maPropertyHolders.back(); 359 } 360 ~PropertyHolders()361 ~PropertyHolders() 362 { 363 while(maPropertyHolders.size()) 364 { 365 delete maPropertyHolders.back(); 366 maPropertyHolders.pop_back(); 367 } 368 } 369 }; 370 } // end of anonymous namespace 371 372 ////////////////////////////////////////////////////////////////////////////// 373 374 namespace 375 { 376 /** helper to convert a Region to a B2DPolyPolygon 377 when it does not yet contain one. In the future 378 this may be expanded to merge the polygons created 379 from rectangles or use a special algo to directly turn 380 the spans of regions to a single, already merged 381 PolyPolygon. 382 */ getB2DPolyPolygonFromRegion(const Region & rRegion)383 basegfx::B2DPolyPolygon getB2DPolyPolygonFromRegion(const Region& rRegion) 384 { 385 basegfx::B2DPolyPolygon aRetval; 386 387 if(!rRegion.IsEmpty()) 388 { 389 Region aRegion(rRegion); 390 391 aRetval = aRegion.GetAsB2DPolyPolygon(); 392 } 393 394 return aRetval; 395 } 396 } // end of anonymous namespace 397 398 ////////////////////////////////////////////////////////////////////////////// 399 400 namespace 401 { 402 /** Helper class to buffer and hold a Primive target vector. It 403 encapsulates the new/delete functionality and aloows to work 404 on pointers of the implementation classes. All data will 405 be converted to uno sequences of uno references when accessing the 406 data. 407 */ 408 class TargetHolder 409 { 410 private: 411 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aTargets; 412 413 public: TargetHolder()414 TargetHolder() 415 : aTargets() 416 { 417 } 418 ~TargetHolder()419 ~TargetHolder() 420 { 421 const sal_uInt32 nCount(aTargets.size()); 422 423 for(sal_uInt32 a(0); a < nCount; a++) 424 { 425 delete aTargets[a]; 426 } 427 } 428 size()429 sal_uInt32 size() 430 { 431 return aTargets.size(); 432 } 433 append(drawinglayer::primitive2d::BasePrimitive2D * pCandidate)434 void append(drawinglayer::primitive2d::BasePrimitive2D* pCandidate) 435 { 436 if(pCandidate) 437 { 438 aTargets.push_back(pCandidate); 439 } 440 } 441 getPrimitive2DSequence(const PropertyHolder & rPropertyHolder)442 drawinglayer::primitive2d::Primitive2DSequence getPrimitive2DSequence(const PropertyHolder& rPropertyHolder) 443 { 444 const sal_uInt32 nCount(aTargets.size()); 445 drawinglayer::primitive2d::Primitive2DSequence xRetval(nCount); 446 447 for(sal_uInt32 a(0); a < nCount; a++) 448 { 449 xRetval[a] = aTargets[a]; 450 } 451 452 // All Targets were pointers, but do not need to be deleted since they 453 // were converted to UNO API references now, so they stay as long as 454 // referenced. Do NOT delete the C++ implementation classes here, but clear 455 // the buffer to not delete them in the destructor. 456 aTargets.clear(); 457 458 if(xRetval.hasElements() && rPropertyHolder.getClipPolyPolygonActive()) 459 { 460 const basegfx::B2DPolyPolygon& rClipPolyPolygon = rPropertyHolder.getClipPolyPolygon(); 461 462 if(rClipPolyPolygon.count()) 463 { 464 const drawinglayer::primitive2d::Primitive2DReference xMask( 465 new drawinglayer::primitive2d::MaskPrimitive2D( 466 rClipPolyPolygon, 467 xRetval)); 468 469 xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1); 470 } 471 } 472 473 return xRetval; 474 } 475 }; 476 } // end of anonymous namespace 477 478 ////////////////////////////////////////////////////////////////////////////// 479 480 namespace 481 { 482 /** Helper class which builds a stack on the TargetHolder class */ 483 class TargetHolders 484 { 485 private: 486 std::vector< TargetHolder* > maTargetHolders; 487 488 public: TargetHolders()489 TargetHolders() 490 { 491 maTargetHolders.push_back(new TargetHolder()); 492 } 493 size()494 sal_uInt32 size() 495 { 496 return maTargetHolders.size(); 497 } 498 Push()499 void Push() 500 { 501 maTargetHolders.push_back(new TargetHolder()); 502 } 503 Pop()504 void Pop() 505 { 506 OSL_ENSURE(maTargetHolders.size(), "TargetHolders: POP with no property holders (!)"); 507 if(maTargetHolders.size()) 508 { 509 delete maTargetHolders.back(); 510 maTargetHolders.pop_back(); 511 } 512 } 513 Current()514 TargetHolder& Current() 515 { 516 OSL_ENSURE(maTargetHolders.size(), "TargetHolders: CURRENT with no property holders (!)"); 517 return *maTargetHolders.back(); 518 } 519 ~TargetHolders()520 ~TargetHolders() 521 { 522 while(maTargetHolders.size()) 523 { 524 delete maTargetHolders.back(); 525 maTargetHolders.pop_back(); 526 } 527 } 528 }; 529 } // end of anonymous namespace 530 531 ////////////////////////////////////////////////////////////////////////////// 532 533 namespace drawinglayer 534 { 535 namespace primitive2d 536 { 537 /** NonOverlappingFillGradientPrimitive2D class 538 539 This is a special version of the FillGradientPrimitive2D which decomposes 540 to a non-overlapping geometry version of the gradient. This needs to be 541 used to support the old XOR paint-'trick'. 542 543 It does not need an own identifier since a renderer who wants to interpret 544 it itself may do so. It just overloads the decomposition of the C++ 545 implementation class to do an alternative decomposition. 546 */ 547 class NonOverlappingFillGradientPrimitive2D : public FillGradientPrimitive2D 548 { 549 protected: 550 /// local decomposition. 551 virtual Primitive2DSequence create2DDecomposition( 552 const geometry::ViewInformation2D& rViewInformation) const; 553 554 public: 555 /// constructor NonOverlappingFillGradientPrimitive2D(const basegfx::B2DRange & rObjectRange,const attribute::FillGradientAttribute & rFillGradient)556 NonOverlappingFillGradientPrimitive2D( 557 const basegfx::B2DRange& rObjectRange, 558 const attribute::FillGradientAttribute& rFillGradient) 559 : FillGradientPrimitive2D(rObjectRange, rFillGradient) 560 { 561 } 562 }; 563 create2DDecomposition(const geometry::ViewInformation2D &) const564 Primitive2DSequence NonOverlappingFillGradientPrimitive2D::create2DDecomposition( 565 const geometry::ViewInformation2D& /*rViewInformation*/) const 566 { 567 if(!getFillGradient().isDefault()) 568 { 569 return createFill(false); 570 } 571 else 572 { 573 return Primitive2DSequence(); 574 } 575 } 576 } // end of namespace primitive2d 577 } // end of namespace drawinglayer 578 579 ////////////////////////////////////////////////////////////////////////////// 580 581 namespace 582 { 583 /** helper to convert a MapMode to a transformation */ getTransformFromMapMode(const MapMode & rMapMode)584 basegfx::B2DHomMatrix getTransformFromMapMode(const MapMode& rMapMode) 585 { 586 basegfx::B2DHomMatrix aMapping; 587 const Fraction aNoScale(1, 1); 588 const Point& rOrigin(rMapMode.GetOrigin()); 589 590 if(0 != rOrigin.X() || 0 != rOrigin.Y()) 591 { 592 aMapping.translate(rOrigin.X(), rOrigin.Y()); 593 } 594 595 if(rMapMode.GetScaleX() != aNoScale || rMapMode.GetScaleY() != aNoScale) 596 { 597 aMapping.scale( 598 double(rMapMode.GetScaleX()), 599 double(rMapMode.GetScaleY())); 600 } 601 602 return aMapping; 603 } 604 605 /** helper to create a PointArrayPrimitive2D based on current context */ createPointArrayPrimitive(const std::vector<basegfx::B2DPoint> & rPositions,TargetHolder & rTarget,PropertyHolder & rProperties,basegfx::BColor aBColor)606 void createPointArrayPrimitive( 607 const std::vector< basegfx::B2DPoint >& rPositions, 608 TargetHolder& rTarget, 609 PropertyHolder& rProperties, 610 basegfx::BColor aBColor) 611 { 612 if(rPositions.size()) 613 { 614 if(rProperties.getTransformation().isIdentity()) 615 { 616 rTarget.append( 617 new drawinglayer::primitive2d::PointArrayPrimitive2D( 618 rPositions, 619 aBColor)); 620 } 621 else 622 { 623 std::vector< basegfx::B2DPoint > aPositions(rPositions); 624 625 for(sal_uInt32 a(0); a < aPositions.size(); a++) 626 { 627 aPositions[a] = rProperties.getTransformation() * aPositions[a]; 628 } 629 630 rTarget.append( 631 new drawinglayer::primitive2d::PointArrayPrimitive2D( 632 aPositions, 633 aBColor)); 634 } 635 } 636 } 637 638 /** helper to create a PolygonHairlinePrimitive2D based on current context */ createHairlinePrimitive(const basegfx::B2DPolygon & rLinePolygon,TargetHolder & rTarget,PropertyHolder & rProperties)639 void createHairlinePrimitive( 640 const basegfx::B2DPolygon& rLinePolygon, 641 TargetHolder& rTarget, 642 PropertyHolder& rProperties) 643 { 644 if(rLinePolygon.count()) 645 { 646 basegfx::B2DPolygon aLinePolygon(rLinePolygon); 647 aLinePolygon.transform(rProperties.getTransformation()); 648 rTarget.append( 649 new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( 650 aLinePolygon, 651 rProperties.getLineColor())); 652 } 653 } 654 655 /** helper to create a PolyPolygonColorPrimitive2D based on current context */ createFillPrimitive(const basegfx::B2DPolyPolygon & rFillPolyPolygon,TargetHolder & rTarget,PropertyHolder & rProperties)656 void createFillPrimitive( 657 const basegfx::B2DPolyPolygon& rFillPolyPolygon, 658 TargetHolder& rTarget, 659 PropertyHolder& rProperties) 660 { 661 if(rFillPolyPolygon.count()) 662 { 663 basegfx::B2DPolyPolygon aFillPolyPolygon(rFillPolyPolygon); 664 aFillPolyPolygon.transform(rProperties.getTransformation()); 665 rTarget.append( 666 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( 667 aFillPolyPolygon, 668 rProperties.getFillColor())); 669 } 670 } 671 672 /** helper to create a PolygonStrokePrimitive2D based on current context */ createLinePrimitive(const basegfx::B2DPolygon & rLinePolygon,const LineInfo & rLineInfo,TargetHolder & rTarget,PropertyHolder & rProperties)673 void createLinePrimitive( 674 const basegfx::B2DPolygon& rLinePolygon, 675 const LineInfo& rLineInfo, 676 TargetHolder& rTarget, 677 PropertyHolder& rProperties) 678 { 679 if(rLinePolygon.count()) 680 { 681 const bool bDashDotUsed(LINE_DASH == rLineInfo.GetStyle()); 682 const bool bWidthUsed(rLineInfo.GetWidth() > 1); 683 684 if(bDashDotUsed || bWidthUsed) 685 { 686 basegfx::B2DPolygon aLinePolygon(rLinePolygon); 687 aLinePolygon.transform(rProperties.getTransformation()); 688 const drawinglayer::attribute::LineAttribute aLineAttribute( 689 rProperties.getLineColor(), 690 bWidthUsed ? rLineInfo.GetWidth() : 0.0, 691 rLineInfo.GetLineJoin(), 692 rLineInfo.GetLineCap()); 693 694 if(bDashDotUsed) 695 { 696 ::std::vector< double > fDotDashArray; 697 const double fDashLen(rLineInfo.GetDashLen()); 698 const double fDotLen(rLineInfo.GetDotLen()); 699 const double fDistance(rLineInfo.GetDistance()); 700 701 for(sal_uInt16 a(0); a < rLineInfo.GetDashCount(); a++) 702 { 703 fDotDashArray.push_back(fDashLen); 704 fDotDashArray.push_back(fDistance); 705 } 706 707 for(sal_uInt16 b(0); b < rLineInfo.GetDotCount(); b++) 708 { 709 fDotDashArray.push_back(fDotLen); 710 fDotDashArray.push_back(fDistance); 711 } 712 713 const double fAccumulated(::std::accumulate(fDotDashArray.begin(), fDotDashArray.end(), 0.0)); 714 const drawinglayer::attribute::StrokeAttribute aStrokeAttribute( 715 fDotDashArray, 716 fAccumulated); 717 718 rTarget.append( 719 new drawinglayer::primitive2d::PolygonStrokePrimitive2D( 720 aLinePolygon, 721 aLineAttribute, 722 aStrokeAttribute)); 723 } 724 else 725 { 726 rTarget.append( 727 new drawinglayer::primitive2d::PolygonStrokePrimitive2D( 728 aLinePolygon, 729 aLineAttribute)); 730 } 731 } 732 else 733 { 734 createHairlinePrimitive(rLinePolygon, rTarget, rProperties); 735 } 736 } 737 } 738 739 /** helper to create needed line and fill primitives based on current context */ createHairlineAndFillPrimitive(const basegfx::B2DPolygon & rPolygon,TargetHolder & rTarget,PropertyHolder & rProperties)740 void createHairlineAndFillPrimitive( 741 const basegfx::B2DPolygon& rPolygon, 742 TargetHolder& rTarget, 743 PropertyHolder& rProperties) 744 { 745 if(rProperties.getFillColorActive()) 746 { 747 createFillPrimitive(basegfx::B2DPolyPolygon(rPolygon), rTarget, rProperties); 748 } 749 750 if(rProperties.getLineColorActive()) 751 { 752 createHairlinePrimitive(rPolygon, rTarget, rProperties); 753 } 754 } 755 756 /** helper to create needed line and fill primitives based on current context */ createHairlineAndFillPrimitive(const basegfx::B2DPolyPolygon & rPolyPolygon,TargetHolder & rTarget,PropertyHolder & rProperties)757 void createHairlineAndFillPrimitive( 758 const basegfx::B2DPolyPolygon& rPolyPolygon, 759 TargetHolder& rTarget, 760 PropertyHolder& rProperties) 761 { 762 if(rProperties.getFillColorActive()) 763 { 764 createFillPrimitive(rPolyPolygon, rTarget, rProperties); 765 } 766 767 if(rProperties.getLineColorActive()) 768 { 769 for(sal_uInt32 a(0); a < rPolyPolygon.count(); a++) 770 { 771 createHairlinePrimitive(rPolyPolygon.getB2DPolygon(a), rTarget, rProperties); 772 } 773 } 774 } 775 776 /** helper to create DiscreteBitmapPrimitive2D based on current context. 777 The DiscreteBitmapPrimitive2D is especially created for this usage 778 since no other usage defines a bitmap visualisation based on top-left 779 position and size in pixels. At the end it will create a view-dependent 780 transformed embedding of a BitmapPrimitive2D. 781 */ createBitmapExPrimitive(const BitmapEx & rBitmapEx,const Point & rPoint,TargetHolder & rTarget,PropertyHolder & rProperties)782 void createBitmapExPrimitive( 783 const BitmapEx& rBitmapEx, 784 const Point& rPoint, 785 TargetHolder& rTarget, 786 PropertyHolder& rProperties) 787 { 788 if(!rBitmapEx.IsEmpty()) 789 { 790 basegfx::B2DPoint aPoint(rPoint.X(), rPoint.Y()); 791 aPoint = rProperties.getTransformation() * aPoint; 792 793 rTarget.append( 794 new drawinglayer::primitive2d::DiscreteBitmapPrimitive2D( 795 rBitmapEx, 796 aPoint)); 797 } 798 } 799 800 /** helper to create BitmapPrimitive2D based on current context */ createBitmapExPrimitive(const BitmapEx & rBitmapEx,const Point & rPoint,const Size & rSize,TargetHolder & rTarget,PropertyHolder & rProperties)801 void createBitmapExPrimitive( 802 const BitmapEx& rBitmapEx, 803 const Point& rPoint, 804 const Size& rSize, 805 TargetHolder& rTarget, 806 PropertyHolder& rProperties) 807 { 808 if(!rBitmapEx.IsEmpty()) 809 { 810 basegfx::B2DHomMatrix aObjectTransform; 811 812 aObjectTransform.set(0, 0, rSize.Width()); 813 aObjectTransform.set(1, 1, rSize.Height()); 814 aObjectTransform.set(0, 2, rPoint.X()); 815 aObjectTransform.set(1, 2, rPoint.Y()); 816 817 aObjectTransform = rProperties.getTransformation() * aObjectTransform; 818 819 rTarget.append( 820 new drawinglayer::primitive2d::BitmapPrimitive2D( 821 rBitmapEx, 822 aObjectTransform)); 823 } 824 } 825 826 /** helper to create a regular BotmapEx from a MaskAction (definitions 827 which use a bitmap without transparence but define one of the colors as 828 transparent) 829 */ createMaskBmpEx(const Bitmap & rBitmap,const Color & rMaskColor)830 BitmapEx createMaskBmpEx(const Bitmap& rBitmap, const Color& rMaskColor) 831 { 832 const Color aWhite(COL_WHITE); 833 BitmapPalette aBiLevelPalette(2); 834 835 aBiLevelPalette[0] = aWhite; 836 aBiLevelPalette[1] = rMaskColor; 837 838 Bitmap aMask(rBitmap.CreateMask(aWhite)); 839 Bitmap aSolid(rBitmap.GetSizePixel(), 1, &aBiLevelPalette); 840 841 aSolid.Erase(rMaskColor); 842 843 return BitmapEx(aSolid, aMask); 844 } 845 846 /** helper to convert from a VCL Gradient definition to the corresponding 847 data for primitive representation 848 */ createFillGradientAttribute(const Gradient & rGradient)849 drawinglayer::attribute::FillGradientAttribute createFillGradientAttribute(const Gradient& rGradient) 850 { 851 const Color aStartColor(rGradient.GetStartColor()); 852 const sal_uInt16 nStartIntens(rGradient.GetStartIntensity()); 853 basegfx::BColor aStart(aStartColor.getBColor()); 854 855 if(nStartIntens != 100) 856 { 857 const basegfx::BColor aBlack; 858 aStart = interpolate(aBlack, aStart, (double)nStartIntens * 0.01); 859 } 860 861 const Color aEndColor(rGradient.GetEndColor()); 862 const sal_uInt16 nEndIntens(rGradient.GetEndIntensity()); 863 basegfx::BColor aEnd(aEndColor.getBColor()); 864 865 if(nEndIntens != 100) 866 { 867 const basegfx::BColor aBlack; 868 aEnd = interpolate(aBlack, aEnd, (double)nEndIntens * 0.01); 869 } 870 871 drawinglayer::attribute::GradientStyle aGradientStyle(drawinglayer::attribute::GRADIENTSTYLE_RECT); 872 873 switch(rGradient.GetStyle()) 874 { 875 case GRADIENT_LINEAR : 876 { 877 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_LINEAR; 878 break; 879 } 880 case GRADIENT_AXIAL : 881 { 882 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_AXIAL; 883 break; 884 } 885 case GRADIENT_RADIAL : 886 { 887 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_RADIAL; 888 break; 889 } 890 case GRADIENT_ELLIPTICAL : 891 { 892 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_ELLIPTICAL; 893 break; 894 } 895 case GRADIENT_SQUARE : 896 { 897 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_SQUARE; 898 break; 899 } 900 default : // GRADIENT_RECT 901 { 902 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_RECT; 903 break; 904 } 905 } 906 907 return drawinglayer::attribute::FillGradientAttribute( 908 aGradientStyle, 909 (double)rGradient.GetBorder() * 0.01, 910 (double)rGradient.GetOfsX() * 0.01, 911 (double)rGradient.GetOfsY() * 0.01, 912 (double)rGradient.GetAngle() * F_PI1800, 913 aStart, 914 aEnd, 915 rGradient.GetSteps()); 916 } 917 918 /** helper to convert from a VCL Hatch definition to the corresponding 919 data for primitive representation 920 */ createFillHatchAttribute(const Hatch & rHatch)921 drawinglayer::attribute::FillHatchAttribute createFillHatchAttribute(const Hatch& rHatch) 922 { 923 drawinglayer::attribute::HatchStyle aHatchStyle(drawinglayer::attribute::HATCHSTYLE_SINGLE); 924 925 switch(rHatch.GetStyle()) 926 { 927 default : // case HATCH_SINGLE : 928 { 929 aHatchStyle = drawinglayer::attribute::HATCHSTYLE_SINGLE; 930 break; 931 } 932 case HATCH_DOUBLE : 933 { 934 aHatchStyle = drawinglayer::attribute::HATCHSTYLE_DOUBLE; 935 break; 936 } 937 case HATCH_TRIPLE : 938 { 939 aHatchStyle = drawinglayer::attribute::HATCHSTYLE_TRIPLE; 940 break; 941 } 942 } 943 944 return drawinglayer::attribute::FillHatchAttribute( 945 aHatchStyle, 946 (double)rHatch.GetDistance(), 947 (double)rHatch.GetAngle() * F_PI1800, 948 rHatch.GetColor().getBColor(), 949 3, // same default as VCL, a minimum of three discrete units (pixels) offset 950 false); 951 } 952 953 /** helper to take needed action on ClipRegion change. This method needs to be called 954 on any Region change, e.g. at the obvious actions doing this, but also at pop-calls 955 whcih change the Region of the current context. It takes care of creating the 956 current embeddec context, set the new Region at the context and eventually prepare 957 a new target for embracing new geometry to the current region 958 */ HandleNewClipRegion(const basegfx::B2DPolyPolygon & rClipPolyPolygon,TargetHolders & rTargetHolders,PropertyHolders & rPropertyHolders)959 void HandleNewClipRegion( 960 const basegfx::B2DPolyPolygon& rClipPolyPolygon, 961 TargetHolders& rTargetHolders, 962 PropertyHolders& rPropertyHolders) 963 { 964 const bool bNewActive(rClipPolyPolygon.count()); 965 966 // #i108636# The handlig of new ClipPolyPolygons was not done as good as possible 967 // in the first version of this interpreter; e.g. when a ClipPolyPolygon was set 968 // initially and then using a lot of push/pop actions, the pop always leads 969 // to setting a 'new' ClipPolyPolygon which indeed is the return to the ClipPolyPolygon 970 // of the properties next on the stack. 971 // 972 // This ClipPolyPolygon is identical to the current one, so there is no need to 973 // create a MaskPrimitive2D containing the up-to-now created primitives, but 974 // this was done before. While this does not lead to wrong primitive 975 // representations of the metafile data, it creates unneccesarily expensive 976 // representations. Just detecting when no really 'new' ClipPolyPolygon gets set 977 // solves the problem. 978 979 if(!rPropertyHolders.Current().getClipPolyPolygonActive() && !bNewActive) 980 { 981 // no active ClipPolyPolygon exchanged by no new one, done 982 return; 983 } 984 985 if(rPropertyHolders.Current().getClipPolyPolygonActive() && bNewActive) 986 { 987 // active ClipPolyPolygon and new active ClipPolyPolygon 988 if(rPropertyHolders.Current().getClipPolyPolygon() == rClipPolyPolygon) 989 { 990 // new is the same as old, done 991 return; 992 } 993 } 994 995 // Here the old and the new are definitively different, maybe 996 // old one and/or new one is not active. 997 998 // Handle deletion of old ClipPolyPolygon. The process evtl. created primitives which 999 // belong to this active ClipPolyPolygon. These need to be embedded to a 1000 // MaskPrimitive2D accordingly. 1001 if(rPropertyHolders.Current().getClipPolyPolygonActive() && rTargetHolders.size() > 1) 1002 { 1003 drawinglayer::primitive2d::Primitive2DSequence aSubContent; 1004 1005 if(rPropertyHolders.Current().getClipPolyPolygon().count() 1006 && rTargetHolders.Current().size()) 1007 { 1008 aSubContent = rTargetHolders.Current().getPrimitive2DSequence( 1009 rPropertyHolders.Current()); 1010 } 1011 1012 rTargetHolders.Pop(); 1013 1014 if(aSubContent.hasElements()) 1015 { 1016 rTargetHolders.Current().append( 1017 new drawinglayer::primitive2d::GroupPrimitive2D( 1018 aSubContent)); 1019 } 1020 } 1021 1022 // apply new settings to current properties by setting 1023 // the new region now 1024 rPropertyHolders.Current().setClipPolyPolygonActive(bNewActive); 1025 1026 if(bNewActive) 1027 { 1028 rPropertyHolders.Current().setClipPolyPolygon(rClipPolyPolygon); 1029 1030 // prepare new content holder for new active region 1031 rTargetHolders.Push(); 1032 } 1033 } 1034 1035 /** helper to handle the change of RasterOp. It takes care of encapsulating all current 1036 geometry to the current RasterOp (if changed) and needs to be called on any RasterOp 1037 change. It will also start a new geometry target to embrace to the new RasterOp if 1038 a changuing RasterOp is used. Currently, ROP_XOR and ROP_INVERT are supported using 1039 InvertPrimitive2D, and ROP_0 by using a ModifiedColorPrimitive2D to force to black paint 1040 */ HandleNewRasterOp(RasterOp aRasterOp,TargetHolders & rTargetHolders,PropertyHolders & rPropertyHolders)1041 void HandleNewRasterOp( 1042 RasterOp aRasterOp, 1043 TargetHolders& rTargetHolders, 1044 PropertyHolders& rPropertyHolders) 1045 { 1046 // check if currently active 1047 if(rPropertyHolders.Current().isRasterOpActive() && rTargetHolders.size() > 1) 1048 { 1049 drawinglayer::primitive2d::Primitive2DSequence aSubContent; 1050 1051 if(rTargetHolders.Current().size()) 1052 { 1053 aSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current()); 1054 } 1055 1056 rTargetHolders.Pop(); 1057 1058 if(aSubContent.hasElements()) 1059 { 1060 if(rPropertyHolders.Current().isRasterOpForceBlack()) 1061 { 1062 // force content to black 1063 rTargetHolders.Current().append( 1064 new drawinglayer::primitive2d::ModifiedColorPrimitive2D( 1065 aSubContent, 1066 basegfx::BColorModifierSharedPtr( 1067 new basegfx::BColorModifier_replace( 1068 basegfx::BColor(0.0, 0.0, 0.0))))); 1069 } 1070 else // if(rPropertyHolders.Current().isRasterOpInvert()) 1071 { 1072 // invert content 1073 rTargetHolders.Current().append( 1074 new drawinglayer::primitive2d::InvertPrimitive2D( 1075 aSubContent)); 1076 } 1077 } 1078 } 1079 1080 // apply new settings 1081 rPropertyHolders.Current().setRasterOp(aRasterOp); 1082 1083 // check if now active 1084 if(rPropertyHolders.Current().isRasterOpActive()) 1085 { 1086 // prepare new content holder for new invert 1087 rTargetHolders.Push(); 1088 } 1089 } 1090 1091 /** helper to create needed data to emulate the VCL Wallpaper Metafile action. 1092 It is a quite mighty action. This helper is for simple color filled background. 1093 */ CreateColorWallpaper(const basegfx::B2DRange & rRange,const basegfx::BColor & rColor,PropertyHolder & rPropertyHolder)1094 drawinglayer::primitive2d::BasePrimitive2D* CreateColorWallpaper( 1095 const basegfx::B2DRange& rRange, 1096 const basegfx::BColor& rColor, 1097 PropertyHolder& rPropertyHolder) 1098 { 1099 basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(rRange)); 1100 aOutline.transform(rPropertyHolder.getTransformation()); 1101 1102 return new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( 1103 basegfx::B2DPolyPolygon(aOutline), 1104 rColor); 1105 } 1106 1107 /** helper to create needed data to emulate the VCL Wallpaper Metafile action. 1108 It is a quite mighty action. This helper is for gradient filled background. 1109 */ CreateGradientWallpaper(const basegfx::B2DRange & rRange,const Gradient & rGradient,PropertyHolder & rPropertyHolder)1110 drawinglayer::primitive2d::BasePrimitive2D* CreateGradientWallpaper( 1111 const basegfx::B2DRange& rRange, 1112 const Gradient& rGradient, 1113 PropertyHolder& rPropertyHolder) 1114 { 1115 const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); 1116 1117 if(aAttribute.getStartColor() == aAttribute.getEndColor()) 1118 { 1119 // not really a gradient. Create filled rectangle 1120 return CreateColorWallpaper(rRange, aAttribute.getStartColor(), rPropertyHolder); 1121 } 1122 else 1123 { 1124 // really a gradient 1125 drawinglayer::primitive2d::BasePrimitive2D* pRetval = 1126 new drawinglayer::primitive2d::FillGradientPrimitive2D( 1127 rRange, 1128 aAttribute); 1129 1130 if(!rPropertyHolder.getTransformation().isIdentity()) 1131 { 1132 const drawinglayer::primitive2d::Primitive2DReference xPrim(pRetval); 1133 const drawinglayer::primitive2d::Primitive2DSequence xSeq(&xPrim, 1); 1134 1135 pRetval = new drawinglayer::primitive2d::TransformPrimitive2D( 1136 rPropertyHolder.getTransformation(), 1137 xSeq); 1138 } 1139 1140 return pRetval; 1141 } 1142 } 1143 1144 /** helper to create needed data to emulate the VCL Wallpaper Metafile action. 1145 It is a quite mighty action. This helper decides if color and/or gradient 1146 background is needed for the wnated bitmap fill and then creates the needed 1147 WallpaperBitmapPrimitive2D. This primitive was created for this purpose and 1148 takes over all needed logic of orientations and tiling. 1149 */ CreateAndAppendBitmapWallpaper(basegfx::B2DRange aWallpaperRange,const Wallpaper & rWallpaper,TargetHolder & rTarget,PropertyHolder & rProperty)1150 void CreateAndAppendBitmapWallpaper( 1151 basegfx::B2DRange aWallpaperRange, 1152 const Wallpaper& rWallpaper, 1153 TargetHolder& rTarget, 1154 PropertyHolder& rProperty) 1155 { 1156 const BitmapEx aBitmapEx(rWallpaper.GetBitmap()); 1157 const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle()); 1158 1159 // if bitmap visualisation is transparent, maybe background 1160 // needs to be filled. Create background 1161 if(aBitmapEx.IsTransparent() 1162 || (WALLPAPER_TILE != eWallpaperStyle && WALLPAPER_SCALE != eWallpaperStyle)) 1163 { 1164 if(rWallpaper.IsGradient()) 1165 { 1166 rTarget.append( 1167 CreateGradientWallpaper( 1168 aWallpaperRange, 1169 rWallpaper.GetGradient(), 1170 rProperty)); 1171 } 1172 else if(!rWallpaper.GetColor().GetTransparency()) 1173 { 1174 rTarget.append( 1175 CreateColorWallpaper( 1176 aWallpaperRange, 1177 rWallpaper.GetColor().getBColor(), 1178 rProperty)); 1179 } 1180 } 1181 1182 // use wallpaper rect if set 1183 if(rWallpaper.IsRect() && !rWallpaper.GetRect().IsEmpty()) 1184 { 1185 aWallpaperRange = basegfx::B2DRange( 1186 rWallpaper.GetRect().Left(), rWallpaper.GetRect().Top(), 1187 rWallpaper.GetRect().Right(), rWallpaper.GetRect().Bottom()); 1188 } 1189 1190 drawinglayer::primitive2d::BasePrimitive2D* pBitmapWallpaperFill = 1191 new drawinglayer::primitive2d::WallpaperBitmapPrimitive2D( 1192 aWallpaperRange, 1193 aBitmapEx, 1194 eWallpaperStyle); 1195 1196 if(rProperty.getTransformation().isIdentity()) 1197 { 1198 // add directly 1199 rTarget.append(pBitmapWallpaperFill); 1200 } 1201 else 1202 { 1203 // when a transformation is set, embed to it 1204 const drawinglayer::primitive2d::Primitive2DReference xPrim(pBitmapWallpaperFill); 1205 1206 rTarget.append( 1207 new drawinglayer::primitive2d::TransformPrimitive2D( 1208 rProperty.getTransformation(), 1209 drawinglayer::primitive2d::Primitive2DSequence(&xPrim, 1))); 1210 } 1211 } 1212 1213 /** helper to decide UnderlineAbove for text primitives */ isUnderlineAbove(const Font & rFont)1214 bool isUnderlineAbove(const Font& rFont) 1215 { 1216 if(!rFont.IsVertical()) 1217 { 1218 return false; 1219 } 1220 1221 if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage())) 1222 { 1223 // the underline is right for Japanese only 1224 return true; 1225 } 1226 1227 return false; 1228 } 1229 createFontAttributeTransformAndAlignment(drawinglayer::attribute::FontAttribute & rFontAttribute,basegfx::B2DHomMatrix & rTextTransform,basegfx::B2DVector & rAlignmentOffset,PropertyHolder & rProperty)1230 void createFontAttributeTransformAndAlignment( 1231 drawinglayer::attribute::FontAttribute& rFontAttribute, 1232 basegfx::B2DHomMatrix& rTextTransform, 1233 basegfx::B2DVector& rAlignmentOffset, 1234 PropertyHolder& rProperty) 1235 { 1236 const Font& rFont = rProperty.getFont(); 1237 basegfx::B2DVector aFontScaling; 1238 1239 rFontAttribute = drawinglayer::attribute::FontAttribute( 1240 drawinglayer::primitive2d::getFontAttributeFromVclFont( 1241 aFontScaling, 1242 rFont, 1243 0 != (rProperty.getLayoutMode() & TEXT_LAYOUT_BIDI_RTL), 1244 0 != (rProperty.getLayoutMode() & TEXT_LAYOUT_BIDI_STRONG))); 1245 1246 // add FontScaling 1247 rTextTransform.scale(aFontScaling.getX(), aFontScaling.getY()); 1248 1249 // take text align into account 1250 if(ALIGN_BASELINE != rFont.GetAlign()) 1251 { 1252 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice; 1253 aTextLayouterDevice.setFont(rFont); 1254 1255 if(ALIGN_TOP == rFont.GetAlign()) 1256 { 1257 rAlignmentOffset.setY(aTextLayouterDevice.getFontAscent()); 1258 } 1259 else // ALIGN_BOTTOM 1260 { 1261 rAlignmentOffset.setY(-aTextLayouterDevice.getFontDescent()); 1262 } 1263 1264 rTextTransform.translate(rAlignmentOffset.getX(), rAlignmentOffset.getY()); 1265 } 1266 1267 // add FontRotation (if used) 1268 if(rFont.GetOrientation()) 1269 { 1270 rTextTransform.rotate(-rFont.GetOrientation() * F_PI1800); 1271 } 1272 } 1273 1274 /** helper which takes complete care for creating the needed text primitives. It 1275 takes care of decorated stuff and all the geometry adaptions needed 1276 */ proccessMetaTextAction(const Point & rTextStartPosition,const XubString & rText,sal_uInt16 nTextStart,sal_uInt16 nTextLength,const::std::vector<double> & rDXArray,TargetHolder & rTarget,PropertyHolder & rProperty)1277 void proccessMetaTextAction( 1278 const Point& rTextStartPosition, 1279 const XubString& rText, 1280 sal_uInt16 nTextStart, 1281 sal_uInt16 nTextLength, 1282 const ::std::vector< double >& rDXArray, 1283 TargetHolder& rTarget, 1284 PropertyHolder& rProperty) 1285 { 1286 drawinglayer::primitive2d::BasePrimitive2D* pResult = 0; 1287 const Font& rFont = rProperty.getFont(); 1288 basegfx::B2DVector aAlignmentOffset(0.0, 0.0); 1289 1290 if(nTextLength) 1291 { 1292 drawinglayer::attribute::FontAttribute aFontAttribute; 1293 basegfx::B2DHomMatrix aTextTransform; 1294 1295 // fill parameters derived from current font 1296 createFontAttributeTransformAndAlignment( 1297 aFontAttribute, 1298 aTextTransform, 1299 aAlignmentOffset, 1300 rProperty); 1301 1302 // add TextStartPosition 1303 aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y()); 1304 1305 // prepare FontColor and Locale 1306 const basegfx::BColor aFontColor(rProperty.getTextColor()); 1307 const com::sun::star::lang::Locale aLocale(MsLangId::convertLanguageToLocale(rProperty.getLanguageType())); 1308 const bool bWordLineMode(rFont.IsWordLineMode()); 1309 1310 const bool bDecoratedIsNeeded( 1311 UNDERLINE_NONE != rFont.GetOverline() 1312 || UNDERLINE_NONE != rFont.GetUnderline() 1313 || STRIKEOUT_NONE != rFont.GetStrikeout() 1314 || EMPHASISMARK_NONE != (rFont.GetEmphasisMark() & EMPHASISMARK_STYLE) 1315 || RELIEF_NONE != rFont.GetRelief() 1316 || rFont.IsShadow() 1317 || bWordLineMode); 1318 1319 if(bDecoratedIsNeeded) 1320 { 1321 // prepare overline, underline and srikeout data 1322 const drawinglayer::primitive2d::TextLine eFontOverline(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rFont.GetOverline())); 1323 const drawinglayer::primitive2d::TextLine eFontUnderline(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rFont.GetUnderline())); 1324 const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rFont.GetStrikeout())); 1325 1326 // check UndelineAbove 1327 const bool bUnderlineAbove(drawinglayer::primitive2d::TEXT_LINE_NONE != eFontUnderline && isUnderlineAbove(rFont)); 1328 1329 // prepare emphasis mark data 1330 drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE); 1331 1332 switch(rFont.GetEmphasisMark() & EMPHASISMARK_STYLE) 1333 { 1334 case EMPHASISMARK_DOT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DOT; break; 1335 case EMPHASISMARK_CIRCLE : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_CIRCLE; break; 1336 case EMPHASISMARK_DISC : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DISC; break; 1337 case EMPHASISMARK_ACCENT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_ACCENT; break; 1338 } 1339 1340 const bool bEmphasisMarkAbove(rFont.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE); 1341 const bool bEmphasisMarkBelow(rFont.GetEmphasisMark() & EMPHASISMARK_POS_BELOW); 1342 1343 // prepare font relief data 1344 drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE); 1345 1346 switch(rFont.GetRelief()) 1347 { 1348 case RELIEF_EMBOSSED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break; 1349 case RELIEF_ENGRAVED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break; 1350 default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE 1351 } 1352 1353 // prepare shadow/outline data 1354 const bool bShadow(rFont.IsShadow()); 1355 1356 // TextDecoratedPortionPrimitive2D is needed, create one 1357 pResult = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( 1358 1359 // attributes for TextSimplePortionPrimitive2D 1360 aTextTransform, 1361 rText, 1362 nTextStart, 1363 nTextLength, 1364 rDXArray, 1365 aFontAttribute, 1366 aLocale, 1367 aFontColor, 1368 1369 // attributes for TextDecoratedPortionPrimitive2D 1370 rProperty.getOverlineColorActive() ? rProperty.getOverlineColor() : aFontColor, 1371 rProperty.getTextLineColorActive() ? rProperty.getTextLineColor() : aFontColor, 1372 eFontOverline, 1373 eFontUnderline, 1374 bUnderlineAbove, 1375 eTextStrikeout, 1376 bWordLineMode, 1377 eTextEmphasisMark, 1378 bEmphasisMarkAbove, 1379 bEmphasisMarkBelow, 1380 eTextRelief, 1381 bShadow); 1382 } 1383 else 1384 { 1385 // TextSimplePortionPrimitive2D is enough 1386 pResult = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( 1387 aTextTransform, 1388 rText, 1389 nTextStart, 1390 nTextLength, 1391 rDXArray, 1392 aFontAttribute, 1393 aLocale, 1394 aFontColor); 1395 } 1396 } 1397 1398 if(pResult && rProperty.getTextFillColorActive()) 1399 { 1400 // text background is requested, add and encapsulate both to new primitive 1401 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice; 1402 aTextLayouterDevice.setFont(rFont); 1403 1404 // get text width 1405 double fTextWidth(0.0); 1406 1407 if(rDXArray.empty()) 1408 { 1409 fTextWidth = aTextLayouterDevice.getTextWidth(rText, nTextStart, nTextLength); 1410 } 1411 else 1412 { 1413 fTextWidth = rDXArray.back(); 1414 } 1415 1416 if(basegfx::fTools::more(fTextWidth, 0.0)) 1417 { 1418 // build text range 1419 const basegfx::B2DRange aTextRange( 1420 0.0, -aTextLayouterDevice.getFontAscent(), 1421 fTextWidth, aTextLayouterDevice.getFontDescent()); 1422 1423 // create Transform 1424 basegfx::B2DHomMatrix aTextTransform; 1425 1426 aTextTransform.translate(aAlignmentOffset.getX(), aAlignmentOffset.getY()); 1427 1428 if(rFont.GetOrientation()) 1429 { 1430 aTextTransform.rotate(-rFont.GetOrientation() * F_PI1800); 1431 } 1432 1433 aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y()); 1434 1435 // prepare Primitive2DSequence, put text in foreground 1436 drawinglayer::primitive2d::Primitive2DSequence aSequence(2); 1437 aSequence[1] = drawinglayer::primitive2d::Primitive2DReference(pResult); 1438 1439 // prepare filled polygon 1440 basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aTextRange)); 1441 aOutline.transform(aTextTransform); 1442 1443 aSequence[0] = drawinglayer::primitive2d::Primitive2DReference( 1444 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( 1445 basegfx::B2DPolyPolygon(aOutline), 1446 rProperty.getTextFillColor())); 1447 1448 // set as group at pResult 1449 pResult = new drawinglayer::primitive2d::GroupPrimitive2D(aSequence); 1450 } 1451 } 1452 1453 if(pResult) 1454 { 1455 // add created text primitive to target 1456 if(rProperty.getTransformation().isIdentity()) 1457 { 1458 rTarget.append(pResult); 1459 } 1460 else 1461 { 1462 // when a transformation is set, embed to it 1463 const drawinglayer::primitive2d::Primitive2DReference aReference(pResult); 1464 1465 rTarget.append( 1466 new drawinglayer::primitive2d::TransformPrimitive2D( 1467 rProperty.getTransformation(), 1468 drawinglayer::primitive2d::Primitive2DSequence(&aReference, 1))); 1469 } 1470 } 1471 } 1472 1473 /** helper which takes complete care for creating the needed textLine primitives */ proccessMetaTextLineAction(const MetaTextLineAction & rAction,TargetHolder & rTarget,PropertyHolder & rProperty)1474 void proccessMetaTextLineAction( 1475 const MetaTextLineAction& rAction, 1476 TargetHolder& rTarget, 1477 PropertyHolder& rProperty) 1478 { 1479 const double fLineWidth(fabs((double)rAction.GetWidth())); 1480 1481 if(fLineWidth > 0.0) 1482 { 1483 const drawinglayer::primitive2d::TextLine aOverlineMode(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rAction.GetOverline())); 1484 const drawinglayer::primitive2d::TextLine aUnderlineMode(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rAction.GetUnderline())); 1485 const drawinglayer::primitive2d::TextStrikeout aTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rAction.GetStrikeout())); 1486 1487 const bool bOverlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aOverlineMode); 1488 const bool bUnderlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aUnderlineMode); 1489 const bool bStrikeoutUsed(drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE != aTextStrikeout); 1490 1491 if(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed) 1492 { 1493 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aTargetVector; 1494 basegfx::B2DVector aAlignmentOffset(0.0, 0.0); 1495 drawinglayer::attribute::FontAttribute aFontAttribute; 1496 basegfx::B2DHomMatrix aTextTransform; 1497 1498 // fill parameters derived from current font 1499 createFontAttributeTransformAndAlignment( 1500 aFontAttribute, 1501 aTextTransform, 1502 aAlignmentOffset, 1503 rProperty); 1504 1505 // add TextStartPosition 1506 aTextTransform.translate(rAction.GetStartPoint().X(), rAction.GetStartPoint().Y()); 1507 1508 // prepare TextLayouter (used in most cases) 1509 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter; 1510 aTextLayouter.setFont(rProperty.getFont()); 1511 1512 if(bOverlineUsed) 1513 { 1514 // create primitive geometry for overline 1515 aTargetVector.push_back( 1516 new drawinglayer::primitive2d::TextLinePrimitive2D( 1517 aTextTransform, 1518 fLineWidth, 1519 aTextLayouter.getOverlineOffset(), 1520 aTextLayouter.getOverlineHeight(), 1521 aOverlineMode, 1522 rProperty.getOverlineColor())); 1523 } 1524 1525 if(bUnderlineUsed) 1526 { 1527 // create primitive geometry for underline 1528 aTargetVector.push_back( 1529 new drawinglayer::primitive2d::TextLinePrimitive2D( 1530 aTextTransform, 1531 fLineWidth, 1532 aTextLayouter.getUnderlineOffset(), 1533 aTextLayouter.getUnderlineHeight(), 1534 aUnderlineMode, 1535 rProperty.getTextLineColor())); 1536 } 1537 1538 if(bStrikeoutUsed) 1539 { 1540 // create primitive geometry for strikeout 1541 if(drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout 1542 || drawinglayer::primitive2d::TEXT_STRIKEOUT_X == aTextStrikeout) 1543 { 1544 // strikeout with character 1545 const sal_Unicode aStrikeoutChar( 1546 drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout ? '/' : 'X'); 1547 const com::sun::star::lang::Locale aLocale(MsLangId::convertLanguageToLocale( 1548 rProperty.getLanguageType())); 1549 1550 aTargetVector.push_back( 1551 new drawinglayer::primitive2d::TextCharacterStrikeoutPrimitive2D( 1552 aTextTransform, 1553 fLineWidth, 1554 rProperty.getTextColor(), 1555 aStrikeoutChar, 1556 aFontAttribute, 1557 aLocale)); 1558 } 1559 else 1560 { 1561 // strikeout with geometry 1562 aTargetVector.push_back( 1563 new drawinglayer::primitive2d::TextGeometryStrikeoutPrimitive2D( 1564 aTextTransform, 1565 fLineWidth, 1566 rProperty.getTextColor(), 1567 aTextLayouter.getUnderlineHeight(), 1568 aTextLayouter.getStrikeoutOffset(), 1569 aTextStrikeout)); 1570 } 1571 } 1572 1573 if(aTargetVector.size()) 1574 { 1575 // add created text primitive to target 1576 if(rProperty.getTransformation().isIdentity()) 1577 { 1578 for(sal_uInt32 a(0); a < aTargetVector.size(); a++) 1579 { 1580 rTarget.append(aTargetVector[a]); 1581 } 1582 } 1583 else 1584 { 1585 // when a transformation is set, embed to it 1586 drawinglayer::primitive2d::Primitive2DSequence xTargets(aTargetVector.size()); 1587 1588 for(sal_uInt32 a(0); a < aTargetVector.size(); a++) 1589 { 1590 xTargets[a] = drawinglayer::primitive2d::Primitive2DReference(aTargetVector[a]); 1591 } 1592 1593 rTarget.append( 1594 new drawinglayer::primitive2d::TransformPrimitive2D( 1595 rProperty.getTransformation(), 1596 xTargets)); 1597 } 1598 } 1599 } 1600 } 1601 1602 } 1603 1604 /** This is the main interpreter method. It is designed to handle the given Metafile 1605 completely inside the given context and target. It may use and modify the context and 1606 target. This design allows to call itself recursively wich adapted contexts and 1607 targets as e.g. needed for the META_FLOATTRANSPARENT_ACTION where the content is expressed 1608 as a metafile as sub-content. 1609 1610 This interpreter is as free of VCL functionality as possible. It uses VCL data classes 1611 (else reading the data would not be possible), but e.g. does NOT use a local OutputDevice 1612 as most other MetaFile interpreters/exporters do to hold and work with the current context. 1613 This is necessary to be able to get away from the strong internal VCL-binding. 1614 1615 It tries to combine e.g. pixel and/or point actions and to stitch together single line primitives 1616 where possible (which is not trivial with the possible line geometry definitions). 1617 1618 It tries to handle clipping no longer as Regions and spans of Rectangles, but as PolyPolygon 1619 ClipRegions with (where possible) high precision by using the best possible data quality 1620 from the Region. The Region is unavoidable as data container, but nowadays allows the transport 1621 of Polygon-based clip regions. Where this is not used, a Polygon is constructed from the 1622 Region ranges. All primitive clipping uses the MaskPrimitive2D with Polygon-based clipping. 1623 1624 I have marked the single MetaActions with: 1625 1626 SIMPLE, DONE: 1627 Simple, e.g nothing to do or value setting in the context 1628 1629 CHECKED, WORKS WELL: 1630 Thoroughly tested with extra written test code which created a replacement 1631 Metafile just to test this action in various combinations 1632 1633 NEEDS IMPLEMENTATION: 1634 Not implemented and asserted, but also no usage found, neither in own Metafile 1635 creations, nor in EMF/WMF imports (checked with a whole bunch of critical EMF/WMF 1636 bugdocs) 1637 1638 For more commens, see the single action implementations. 1639 */ interpretMetafile(const GDIMetaFile & rMetaFile,TargetHolders & rTargetHolders,PropertyHolders & rPropertyHolders,const drawinglayer::geometry::ViewInformation2D & rViewInformation)1640 void interpretMetafile( 1641 const GDIMetaFile& rMetaFile, 1642 TargetHolders& rTargetHolders, 1643 PropertyHolders& rPropertyHolders, 1644 const drawinglayer::geometry::ViewInformation2D& rViewInformation) 1645 { 1646 const sal_uInt32 nCount(rMetaFile.GetActionCount()); 1647 1648 for(sal_uInt32 nAction(0); nAction < nCount; nAction++) 1649 { 1650 MetaAction* pAction = rMetaFile.GetAction(nAction); 1651 1652 switch(pAction->GetType()) 1653 { 1654 case META_NULL_ACTION : 1655 { 1656 /** SIMPLE, DONE */ 1657 break; 1658 } 1659 case META_PIXEL_ACTION : 1660 { 1661 /** CHECKED, WORKS WELL */ 1662 std::vector< basegfx::B2DPoint > aPositions; 1663 Color aLastColor(COL_BLACK); 1664 1665 while(META_PIXEL_ACTION == pAction->GetType() && nAction < nCount) 1666 { 1667 const MetaPixelAction* pA = (const MetaPixelAction*)pAction; 1668 1669 if(pA->GetColor() != aLastColor) 1670 { 1671 if(aPositions.size()) 1672 { 1673 createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor()); 1674 aPositions.clear(); 1675 } 1676 1677 aLastColor = pA->GetColor(); 1678 } 1679 1680 const Point& rPoint = pA->GetPoint(); 1681 aPositions.push_back(basegfx::B2DPoint(rPoint.X(), rPoint.Y())); 1682 nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction); 1683 } 1684 1685 nAction--; 1686 1687 if(aPositions.size()) 1688 { 1689 createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor()); 1690 } 1691 1692 break; 1693 } 1694 case META_POINT_ACTION : 1695 { 1696 /** CHECKED, WORKS WELL */ 1697 if(rPropertyHolders.Current().getLineColorActive()) 1698 { 1699 std::vector< basegfx::B2DPoint > aPositions; 1700 1701 while(META_POINT_ACTION == pAction->GetType() && nAction < nCount) 1702 { 1703 const MetaPointAction* pA = (const MetaPointAction*)pAction; 1704 const Point& rPoint = pA->GetPoint(); 1705 aPositions.push_back(basegfx::B2DPoint(rPoint.X(), rPoint.Y())); 1706 nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction); 1707 } 1708 1709 nAction--; 1710 1711 if(aPositions.size()) 1712 { 1713 createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), rPropertyHolders.Current().getLineColor()); 1714 } 1715 } 1716 1717 break; 1718 } 1719 case META_LINE_ACTION : 1720 { 1721 /** CHECKED, WORKS WELL */ 1722 if(rPropertyHolders.Current().getLineColorActive()) 1723 { 1724 basegfx::B2DPolygon aLinePolygon; 1725 LineInfo aLineInfo; 1726 1727 while(META_LINE_ACTION == pAction->GetType() && nAction < nCount) 1728 { 1729 const MetaLineAction* pA = (const MetaLineAction*)pAction; 1730 const Point& rStartPoint = pA->GetStartPoint(); 1731 const Point& rEndPoint = pA->GetEndPoint(); 1732 const basegfx::B2DPoint aStart(rStartPoint.X(), rStartPoint.Y()); 1733 const basegfx::B2DPoint aEnd(rEndPoint.X(), rEndPoint.Y()); 1734 1735 if(aLinePolygon.count()) 1736 { 1737 if(pA->GetLineInfo() == aLineInfo 1738 && aStart == aLinePolygon.getB2DPoint(aLinePolygon.count() - 1)) 1739 { 1740 aLinePolygon.append(aEnd); 1741 } 1742 else 1743 { 1744 aLineInfo.SetLineJoin(basegfx::B2DLINEJOIN_NONE); // It were lines; force to NONE 1745 createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current()); 1746 aLinePolygon.clear(); 1747 aLineInfo = pA->GetLineInfo(); 1748 aLinePolygon.append(aStart); 1749 aLinePolygon.append(aEnd); 1750 } 1751 } 1752 else 1753 { 1754 aLineInfo = pA->GetLineInfo(); 1755 aLinePolygon.append(aStart); 1756 aLinePolygon.append(aEnd); 1757 } 1758 1759 nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction); 1760 } 1761 1762 nAction--; 1763 1764 if(aLinePolygon.count()) 1765 { 1766 aLineInfo.SetLineJoin(basegfx::B2DLINEJOIN_NONE); // It were lines; force to NONE 1767 createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current()); 1768 } 1769 } 1770 1771 break; 1772 } 1773 case META_RECT_ACTION : 1774 { 1775 /** CHECKED, WORKS WELL */ 1776 if(rPropertyHolders.Current().getLineOrFillActive()) 1777 { 1778 const MetaRectAction* pA = (const MetaRectAction*)pAction; 1779 const Rectangle& rRectangle = pA->GetRect(); 1780 1781 if(!rRectangle.IsEmpty()) 1782 { 1783 const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom()); 1784 1785 if(!aRange.isEmpty()) 1786 { 1787 const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange)); 1788 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current()); 1789 } 1790 } 1791 } 1792 1793 break; 1794 } 1795 case META_ROUNDRECT_ACTION : 1796 { 1797 /** CHECKED, WORKS WELL */ 1798 /** The original OutputDevice::DrawRect paints nothing when nHor or nVer is zero; but just 1799 because the tools::Polygon operator creating the rounding does produce nonsense. I assume 1800 this an error and create an unrounded rectangle in that case (implicit in 1801 createPolygonFromRect) 1802 */ 1803 if(rPropertyHolders.Current().getLineOrFillActive()) 1804 { 1805 const MetaRoundRectAction* pA = (const MetaRoundRectAction*)pAction; 1806 const Rectangle& rRectangle = pA->GetRect(); 1807 1808 if(!rRectangle.IsEmpty()) 1809 { 1810 const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom()); 1811 1812 if(!aRange.isEmpty()) 1813 { 1814 const sal_uInt32 nHor(pA->GetHorzRound()); 1815 const sal_uInt32 nVer(pA->GetVertRound()); 1816 basegfx::B2DPolygon aOutline; 1817 1818 if(nHor || nVer) 1819 { 1820 double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0)); 1821 double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0)); 1822 fRadiusX = std::max(0.0, std::min(1.0, fRadiusX)); 1823 fRadiusY = std::max(0.0, std::min(1.0, fRadiusY)); 1824 1825 aOutline = basegfx::tools::createPolygonFromRect(aRange, fRadiusX, fRadiusY); 1826 } 1827 else 1828 { 1829 aOutline = basegfx::tools::createPolygonFromRect(aRange); 1830 } 1831 1832 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current()); 1833 } 1834 } 1835 } 1836 1837 break; 1838 } 1839 case META_ELLIPSE_ACTION : 1840 { 1841 /** CHECKED, WORKS WELL */ 1842 if(rPropertyHolders.Current().getLineOrFillActive()) 1843 { 1844 const MetaEllipseAction* pA = (const MetaEllipseAction*)pAction; 1845 const Rectangle& rRectangle = pA->GetRect(); 1846 1847 if(!rRectangle.IsEmpty()) 1848 { 1849 const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom()); 1850 1851 if(!aRange.isEmpty()) 1852 { 1853 const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromEllipse( 1854 aRange.getCenter(), aRange.getWidth() * 0.5, aRange.getHeight() * 0.5)); 1855 1856 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current()); 1857 } 1858 } 1859 } 1860 1861 break; 1862 } 1863 case META_ARC_ACTION : 1864 { 1865 /** CHECKED, WORKS WELL */ 1866 if(rPropertyHolders.Current().getLineColorActive()) 1867 { 1868 const MetaArcAction* pA = (const MetaArcAction*)pAction; 1869 const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_ARC); 1870 const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon()); 1871 1872 createHairlinePrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current()); 1873 } 1874 1875 break; 1876 } 1877 case META_PIE_ACTION : 1878 { 1879 /** CHECKED, WORKS WELL */ 1880 if(rPropertyHolders.Current().getLineOrFillActive()) 1881 { 1882 const MetaPieAction* pA = (const MetaPieAction*)pAction; 1883 const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_PIE); 1884 const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon()); 1885 1886 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current()); 1887 } 1888 1889 break; 1890 } 1891 case META_CHORD_ACTION : 1892 { 1893 /** CHECKED, WORKS WELL */ 1894 if(rPropertyHolders.Current().getLineOrFillActive()) 1895 { 1896 const MetaChordAction* pA = (const MetaChordAction*)pAction; 1897 const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_CHORD); 1898 const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon()); 1899 1900 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current()); 1901 } 1902 1903 break; 1904 } 1905 case META_POLYLINE_ACTION : 1906 { 1907 /** CHECKED, WORKS WELL */ 1908 if(rPropertyHolders.Current().getLineColorActive()) 1909 { 1910 const MetaPolyLineAction* pA = (const MetaPolyLineAction*)pAction; 1911 createLinePrimitive(pA->GetPolygon().getB2DPolygon(), pA->GetLineInfo(), rTargetHolders.Current(), rPropertyHolders.Current()); 1912 } 1913 1914 break; 1915 } 1916 case META_POLYGON_ACTION : 1917 { 1918 /** CHECKED, WORKS WELL */ 1919 if(rPropertyHolders.Current().getLineOrFillActive()) 1920 { 1921 const MetaPolygonAction* pA = (const MetaPolygonAction*)pAction; 1922 basegfx::B2DPolygon aOutline(pA->GetPolygon().getB2DPolygon()); 1923 1924 // the metafile play interprets the polygons from MetaPolygonAction 1925 // always as closed and always paints an edge from last to first point, 1926 // so force to closed here to emulate that 1927 if(aOutline.count() > 1 && !aOutline.isClosed()) 1928 { 1929 aOutline.setClosed(true); 1930 } 1931 1932 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current()); 1933 } 1934 1935 break; 1936 } 1937 case META_POLYPOLYGON_ACTION : 1938 { 1939 /** CHECKED, WORKS WELL */ 1940 if(rPropertyHolders.Current().getLineOrFillActive()) 1941 { 1942 const MetaPolyPolygonAction* pA = (const MetaPolyPolygonAction*)pAction; 1943 basegfx::B2DPolyPolygon aPolyPolygonOutline(pA->GetPolyPolygon().getB2DPolyPolygon()); 1944 1945 // the metafile play interprets the single polygons from MetaPolyPolygonAction 1946 // always as closed and always paints an edge from last to first point, 1947 // so force to closed here to emulate that 1948 for(sal_uInt32 b(0); b < aPolyPolygonOutline.count(); b++) 1949 { 1950 basegfx::B2DPolygon aPolygonOutline(aPolyPolygonOutline.getB2DPolygon(b)); 1951 1952 if(aPolygonOutline.count() > 1 && !aPolygonOutline.isClosed()) 1953 { 1954 aPolygonOutline.setClosed(true); 1955 aPolyPolygonOutline.setB2DPolygon(b, aPolygonOutline); 1956 } 1957 } 1958 1959 createHairlineAndFillPrimitive(aPolyPolygonOutline, rTargetHolders.Current(), rPropertyHolders.Current()); 1960 } 1961 1962 break; 1963 } 1964 case META_TEXT_ACTION : 1965 { 1966 /** CHECKED, WORKS WELL */ 1967 const MetaTextAction* pA = (const MetaTextAction*)pAction; 1968 sal_uInt32 nTextLength(pA->GetLen()); 1969 const sal_uInt32 nTextIndex(pA->GetIndex()); 1970 const sal_uInt32 nStringLength(pA->GetText().Len()); 1971 1972 if(nTextLength + nTextIndex > nStringLength) 1973 { 1974 nTextLength = nStringLength - nTextIndex; 1975 } 1976 1977 if(nTextLength && rPropertyHolders.Current().getTextColorActive()) 1978 { 1979 const std::vector< double > aDXArray; 1980 proccessMetaTextAction( 1981 pA->GetPoint(), 1982 pA->GetText(), 1983 nTextIndex, 1984 nTextLength, 1985 aDXArray, 1986 rTargetHolders.Current(), 1987 rPropertyHolders.Current()); 1988 } 1989 1990 break; 1991 } 1992 case META_TEXTARRAY_ACTION : 1993 { 1994 /** CHECKED, WORKS WELL */ 1995 const MetaTextArrayAction* pA = (const MetaTextArrayAction*)pAction; 1996 sal_uInt32 nTextLength(pA->GetLen()); 1997 const sal_uInt32 nTextIndex(pA->GetIndex()); 1998 const sal_uInt32 nStringLength(pA->GetText().Len()); 1999 2000 if(nTextLength + nTextIndex > nStringLength) 2001 { 2002 nTextLength = nTextIndex > nStringLength ? 0 : nStringLength - nTextIndex; 2003 } 2004 2005 if(nTextLength && rPropertyHolders.Current().getTextColorActive()) 2006 { 2007 // preapare DXArray (if used) 2008 std::vector< double > aDXArray; 2009 sal_Int32* pDXArray = pA->GetDXArray(); 2010 2011 if(pDXArray) 2012 { 2013 aDXArray.reserve(nTextLength); 2014 2015 for(sal_uInt32 a(0); a < nTextLength; a++) 2016 { 2017 aDXArray.push_back((double)(*(pDXArray + a))); 2018 } 2019 } 2020 2021 proccessMetaTextAction( 2022 pA->GetPoint(), 2023 pA->GetText(), 2024 nTextIndex, 2025 nTextLength, 2026 aDXArray, 2027 rTargetHolders.Current(), 2028 rPropertyHolders.Current()); 2029 } 2030 2031 break; 2032 } 2033 case META_STRETCHTEXT_ACTION : 2034 { 2035 // #i108440# StarMath uses MetaStretchTextAction, thus support is needed. 2036 // It looks as if it pretty never really uses a width different from 2037 // the default text-layout width, but it's not possible to be sure. 2038 // Implemented getting the DXArray and checking for scale at all. If 2039 // scale is more than 3.5% different, scale the DXArray before usage. 2040 // New status: 2041 2042 /** CHECKED, WORKS WELL */ 2043 const MetaStretchTextAction* pA = (const MetaStretchTextAction*)pAction; 2044 sal_uInt32 nTextLength(pA->GetLen()); 2045 const sal_uInt32 nTextIndex(pA->GetIndex()); 2046 const sal_uInt32 nStringLength(pA->GetText().Len()); 2047 2048 if(nTextLength + nTextIndex > nStringLength) 2049 { 2050 nTextLength = nStringLength - nTextIndex; 2051 } 2052 2053 if(nTextLength && rPropertyHolders.Current().getTextColorActive()) 2054 { 2055 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice; 2056 aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont()); 2057 2058 ::std::vector< double > aTextArray( 2059 aTextLayouterDevice.getTextArray( 2060 pA->GetText(), 2061 nTextIndex, 2062 nTextLength)); 2063 2064 if(!aTextArray.empty()) 2065 { 2066 const double fTextLength(aTextArray.back()); 2067 2068 if(0.0 != fTextLength && pA->GetWidth()) 2069 { 2070 const double fRelative(pA->GetWidth() / fTextLength); 2071 2072 if(fabs(fRelative - 1.0) >= 0.035) 2073 { 2074 // when derivation is more than 3,5% from default text size, 2075 // scale the DXArray 2076 for(sal_uInt32 a(0); a < aTextArray.size(); a++) 2077 { 2078 aTextArray[a] *= fRelative; 2079 } 2080 } 2081 } 2082 } 2083 2084 proccessMetaTextAction( 2085 pA->GetPoint(), 2086 pA->GetText(), 2087 nTextIndex, 2088 nTextLength, 2089 aTextArray, 2090 rTargetHolders.Current(), 2091 rPropertyHolders.Current()); 2092 } 2093 2094 break; 2095 } 2096 case META_TEXTRECT_ACTION : 2097 { 2098 /** CHECKED, WORKS WELL */ 2099 // OSL_ENSURE(false, "META_TEXTRECT_ACTION requested (!)"); 2100 const MetaTextRectAction* pA = (const MetaTextRectAction*)pAction; 2101 const Rectangle& rRectangle = pA->GetRect(); 2102 const sal_uInt32 nStringLength(pA->GetText().Len()); 2103 2104 if(!rRectangle.IsEmpty() && 0 != nStringLength) 2105 { 2106 // The problem with this action is that it describes unlayouted text 2107 // and the layout capabilities are in EditEngine/Outliner in SVX. The 2108 // same problem is true for VCL which internally has implementations 2109 // to layout text in this case. There exists even a call 2110 // OutputDevice::AddTextRectActions(...) to create the needed actions 2111 // as 'sub-content' of a Metafile. Unfortunately i do not have an 2112 // OutputDevice here since this interpreter tries to work without 2113 // VCL AFAP. 2114 // Since AddTextRectActions is the only way as long as we do not have 2115 // a simple text layouter available, i will try to add it to the 2116 // TextLayouterDevice isloation. 2117 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice; 2118 aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont()); 2119 GDIMetaFile aGDIMetaFile; 2120 2121 aTextLayouterDevice.addTextRectActions( 2122 rRectangle, pA->GetText(), pA->GetStyle(), aGDIMetaFile); 2123 2124 if(aGDIMetaFile.GetActionCount()) 2125 { 2126 // cerate sub-content 2127 drawinglayer::primitive2d::Primitive2DSequence xSubContent; 2128 { 2129 rTargetHolders.Push(); 2130 // #i# for sub-Mteafile contents, do start with new, default render state 2131 rPropertyHolders.PushDefault(); 2132 interpretMetafile(aGDIMetaFile, rTargetHolders, rPropertyHolders, rViewInformation); 2133 xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current()); 2134 rPropertyHolders.Pop(); 2135 rTargetHolders.Pop(); 2136 } 2137 2138 if(xSubContent.hasElements()) 2139 { 2140 // add with transformation 2141 rTargetHolders.Current().append( 2142 new drawinglayer::primitive2d::TransformPrimitive2D( 2143 rPropertyHolders.Current().getTransformation(), 2144 xSubContent)); 2145 } 2146 } 2147 } 2148 2149 break; 2150 } 2151 case META_BMP_ACTION : 2152 { 2153 /** CHECKED, WORKS WELL */ 2154 const MetaBmpAction* pA = (const MetaBmpAction*)pAction; 2155 const BitmapEx aBitmapEx(pA->GetBitmap()); 2156 2157 createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current()); 2158 2159 break; 2160 } 2161 case META_BMPSCALE_ACTION : 2162 { 2163 /** CHECKED, WORKS WELL */ 2164 const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*)pAction; 2165 const Bitmap aBitmapEx(pA->GetBitmap()); 2166 2167 createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current()); 2168 2169 break; 2170 } 2171 case META_BMPSCALEPART_ACTION : 2172 { 2173 /** CHECKED, WORKS WELL */ 2174 const MetaBmpScalePartAction* pA = (const MetaBmpScalePartAction*)pAction; 2175 const Bitmap& rBitmap = pA->GetBitmap(); 2176 2177 if(!rBitmap.IsEmpty()) 2178 { 2179 Bitmap aCroppedBitmap(rBitmap); 2180 const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); 2181 2182 if(!aCropRectangle.IsEmpty()) 2183 { 2184 aCroppedBitmap.Crop(aCropRectangle); 2185 } 2186 2187 const BitmapEx aCroppedBitmapEx(aCroppedBitmap); 2188 createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current()); 2189 } 2190 2191 break; 2192 } 2193 case META_BMPEX_ACTION : 2194 { 2195 /** CHECKED, WORKS WELL: Simply same as META_BMP_ACTION */ 2196 const MetaBmpExAction* pA = (const MetaBmpExAction*)pAction; 2197 const BitmapEx& rBitmapEx = pA->GetBitmapEx(); 2198 2199 createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current()); 2200 2201 break; 2202 } 2203 case META_BMPEXSCALE_ACTION : 2204 { 2205 /** CHECKED, WORKS WELL: Simply same as META_BMPSCALE_ACTION */ 2206 const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*)pAction; 2207 const BitmapEx& rBitmapEx = pA->GetBitmapEx(); 2208 2209 createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current()); 2210 2211 break; 2212 } 2213 case META_BMPEXSCALEPART_ACTION : 2214 { 2215 /** CHECKED, WORKS WELL: Simply same as META_BMPSCALEPART_ACTION */ 2216 const MetaBmpExScalePartAction* pA = (const MetaBmpExScalePartAction*)pAction; 2217 const BitmapEx& rBitmapEx = pA->GetBitmapEx(); 2218 2219 if(!rBitmapEx.IsEmpty()) 2220 { 2221 BitmapEx aCroppedBitmapEx(rBitmapEx); 2222 const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); 2223 2224 if(!aCropRectangle.IsEmpty()) 2225 { 2226 aCroppedBitmapEx.Crop(aCropRectangle); 2227 } 2228 2229 createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current()); 2230 } 2231 2232 break; 2233 } 2234 case META_MASK_ACTION : 2235 { 2236 /** CHECKED, WORKS WELL: Simply same as META_BMP_ACTION */ 2237 const MetaMaskAction* pA = (const MetaMaskAction*)pAction; 2238 const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor())); 2239 2240 createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current()); 2241 2242 break; 2243 } 2244 case META_MASKSCALE_ACTION : 2245 { 2246 /** CHECKED, WORKS WELL: Simply same as META_BMPSCALE_ACTION */ 2247 const MetaMaskScaleAction* pA = (const MetaMaskScaleAction*)pAction; 2248 const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor())); 2249 2250 createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current()); 2251 2252 break; 2253 } 2254 case META_MASKSCALEPART_ACTION : 2255 { 2256 /** CHECKED, WORKS WELL: Simply same as META_BMPSCALEPART_ACTION */ 2257 const MetaMaskScalePartAction* pA = (const MetaMaskScalePartAction*)pAction; 2258 const Bitmap& rBitmap = pA->GetBitmap(); 2259 2260 if(!rBitmap.IsEmpty()) 2261 { 2262 Bitmap aCroppedBitmap(rBitmap); 2263 const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); 2264 2265 if(!aCropRectangle.IsEmpty()) 2266 { 2267 aCroppedBitmap.Crop(aCropRectangle); 2268 } 2269 2270 const BitmapEx aCroppedBitmapEx(createMaskBmpEx(aCroppedBitmap, pA->GetColor())); 2271 createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current()); 2272 } 2273 2274 break; 2275 } 2276 case META_GRADIENT_ACTION : 2277 { 2278 /** CHECKED, WORKS WELL */ 2279 const MetaGradientAction* pA = (const MetaGradientAction*)pAction; 2280 const Rectangle& rRectangle = pA->GetRect(); 2281 2282 if(!rRectangle.IsEmpty()) 2283 { 2284 basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom()); 2285 2286 if(!aRange.isEmpty()) 2287 { 2288 const Gradient& rGradient = pA->GetGradient(); 2289 const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); 2290 basegfx::B2DPolyPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange)); 2291 2292 if(aAttribute.getStartColor() == aAttribute.getEndColor()) 2293 { 2294 // not really a gradient. Create filled rectangle 2295 createFillPrimitive( 2296 aOutline, 2297 rTargetHolders.Current(), 2298 rPropertyHolders.Current()); 2299 } 2300 else 2301 { 2302 // really a gradient 2303 aRange.transform(rPropertyHolders.Current().getTransformation()); 2304 drawinglayer::primitive2d::Primitive2DSequence xGradient(1); 2305 2306 if(rPropertyHolders.Current().isRasterOpInvert()) 2307 { 2308 // use a special version of FillGradientPrimitive2D which creates 2309 // non-overlapping geometry on decomposition to makethe old XOR 2310 // paint 'trick' work. 2311 xGradient[0] = drawinglayer::primitive2d::Primitive2DReference( 2312 new drawinglayer::primitive2d::NonOverlappingFillGradientPrimitive2D( 2313 aRange, 2314 aAttribute)); 2315 } 2316 else 2317 { 2318 xGradient[0] = drawinglayer::primitive2d::Primitive2DReference( 2319 new drawinglayer::primitive2d::FillGradientPrimitive2D( 2320 aRange, 2321 aAttribute)); 2322 } 2323 2324 // #i112300# clip against polygon representing the rectangle from 2325 // the action. This is implicitely done using a temp Clipping in VCL 2326 // when a MetaGradientAction is executed 2327 aOutline.transform(rPropertyHolders.Current().getTransformation()); 2328 rTargetHolders.Current().append( 2329 new drawinglayer::primitive2d::MaskPrimitive2D( 2330 aOutline, 2331 xGradient)); 2332 } 2333 } 2334 } 2335 2336 break; 2337 } 2338 case META_HATCH_ACTION : 2339 { 2340 /** CHECKED, WORKS WELL */ 2341 const MetaHatchAction* pA = (const MetaHatchAction*)pAction; 2342 basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon()); 2343 2344 if(aOutline.count()) 2345 { 2346 const Hatch& rHatch = pA->GetHatch(); 2347 const drawinglayer::attribute::FillHatchAttribute aAttribute(createFillHatchAttribute(rHatch)); 2348 2349 aOutline.transform(rPropertyHolders.Current().getTransformation()); 2350 2351 const basegfx::B2DRange aObjectRange(aOutline.getB2DRange()); 2352 const drawinglayer::primitive2d::Primitive2DReference aFillHatch( 2353 new drawinglayer::primitive2d::FillHatchPrimitive2D( 2354 aObjectRange, 2355 basegfx::BColor(), 2356 aAttribute)); 2357 2358 rTargetHolders.Current().append( 2359 new drawinglayer::primitive2d::MaskPrimitive2D( 2360 aOutline, 2361 drawinglayer::primitive2d::Primitive2DSequence(&aFillHatch, 1))); 2362 } 2363 2364 break; 2365 } 2366 case META_WALLPAPER_ACTION : 2367 { 2368 /** CHECKED, WORKS WELL */ 2369 const MetaWallpaperAction* pA = (const MetaWallpaperAction*)pAction; 2370 Rectangle aWallpaperRectangle(pA->GetRect()); 2371 2372 if(!aWallpaperRectangle.IsEmpty()) 2373 { 2374 const Wallpaper& rWallpaper = pA->GetWallpaper(); 2375 const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle()); 2376 basegfx::B2DRange aWallpaperRange( 2377 aWallpaperRectangle.Left(), aWallpaperRectangle.Top(), 2378 aWallpaperRectangle.Right(), aWallpaperRectangle.Bottom()); 2379 2380 if(WALLPAPER_NULL != eWallpaperStyle) 2381 { 2382 if(rWallpaper.IsBitmap()) 2383 { 2384 // create bitmap background. Caution: This 2385 // also will create gradient/color background(s) 2386 // when the bitmap is transparent or not tiled 2387 CreateAndAppendBitmapWallpaper( 2388 aWallpaperRange, 2389 rWallpaper, 2390 rTargetHolders.Current(), 2391 rPropertyHolders.Current()); 2392 } 2393 else if(rWallpaper.IsGradient()) 2394 { 2395 // create gradient background 2396 rTargetHolders.Current().append( 2397 CreateGradientWallpaper( 2398 aWallpaperRange, 2399 rWallpaper.GetGradient(), 2400 rPropertyHolders.Current())); 2401 } 2402 else if(!rWallpaper.GetColor().GetTransparency()) 2403 { 2404 // create color background 2405 rTargetHolders.Current().append( 2406 CreateColorWallpaper( 2407 aWallpaperRange, 2408 rWallpaper.GetColor().getBColor(), 2409 rPropertyHolders.Current())); 2410 } 2411 } 2412 } 2413 2414 break; 2415 } 2416 case META_CLIPREGION_ACTION : 2417 { 2418 /** CHECKED, WORKS WELL */ 2419 const MetaClipRegionAction* pA = (const MetaClipRegionAction*)pAction; 2420 2421 if(pA->IsClipping()) 2422 { 2423 // new clipping. Get PolyPolygon and transform with current transformation 2424 basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(pA->GetRegion())); 2425 2426 aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation()); 2427 HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders); 2428 } 2429 else 2430 { 2431 // end clipping 2432 const basegfx::B2DPolyPolygon aEmptyPolyPolygon; 2433 2434 HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders); 2435 } 2436 2437 break; 2438 } 2439 case META_ISECTRECTCLIPREGION_ACTION : 2440 { 2441 /** CHECKED, WORKS WELL */ 2442 const MetaISectRectClipRegionAction* pA = (const MetaISectRectClipRegionAction*)pAction; 2443 const Rectangle& rRectangle = pA->GetRect(); 2444 2445 if(rRectangle.IsEmpty()) 2446 { 2447 // intersect with empty rectangle will always give empty 2448 // ClipPolyPolygon; start new clipping with empty PolyPolygon 2449 const basegfx::B2DPolyPolygon aEmptyPolyPolygon; 2450 2451 HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders); 2452 } 2453 else 2454 { 2455 // create transformed ClipRange 2456 basegfx::B2DRange aClipRange( 2457 rRectangle.Left(), rRectangle.Top(), 2458 rRectangle.Right(), rRectangle.Bottom()); 2459 2460 aClipRange.transform(rPropertyHolders.Current().getTransformation()); 2461 2462 if(rPropertyHolders.Current().getClipPolyPolygonActive()) 2463 { 2464 if(0 == rPropertyHolders.Current().getClipPolyPolygon().count()) 2465 { 2466 // nothing to do, empty active clipPolyPolygon will stay 2467 // empty when intersecting 2468 } 2469 else 2470 { 2471 // AND existing region and new ClipRange 2472 const basegfx::B2DPolyPolygon aOriginalPolyPolygon( 2473 rPropertyHolders.Current().getClipPolyPolygon()); 2474 basegfx::B2DPolyPolygon aClippedPolyPolygon; 2475 2476 if(aOriginalPolyPolygon.count()) 2477 { 2478 aClippedPolyPolygon = basegfx::tools::clipPolyPolygonOnRange( 2479 aOriginalPolyPolygon, 2480 aClipRange, 2481 true, 2482 false); 2483 } 2484 2485 if(aClippedPolyPolygon != aOriginalPolyPolygon) 2486 { 2487 // start new clipping with intersected region 2488 HandleNewClipRegion( 2489 aClippedPolyPolygon, 2490 rTargetHolders, 2491 rPropertyHolders); 2492 } 2493 } 2494 } 2495 else 2496 { 2497 // start new clipping with ClipRange 2498 const basegfx::B2DPolyPolygon aNewClipPolyPolygon( 2499 basegfx::tools::createPolygonFromRect(aClipRange)); 2500 2501 HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders); 2502 } 2503 } 2504 2505 break; 2506 } 2507 case META_ISECTREGIONCLIPREGION_ACTION : 2508 { 2509 /** CHECKED, WORKS WELL */ 2510 const MetaISectRegionClipRegionAction* pA = (const MetaISectRegionClipRegionAction*)pAction; 2511 const Region& rNewRegion = pA->GetRegion(); 2512 2513 if(rNewRegion.IsEmpty()) 2514 { 2515 // intersect with empty region will always give empty 2516 // region; start new clipping with empty PolyPolygon 2517 const basegfx::B2DPolyPolygon aEmptyPolyPolygon; 2518 2519 HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders); 2520 } 2521 else 2522 { 2523 // get new ClipPolyPolygon, transform it with current transformation 2524 basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(rNewRegion)); 2525 aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation()); 2526 2527 if(rPropertyHolders.Current().getClipPolyPolygonActive()) 2528 { 2529 if(0 == rPropertyHolders.Current().getClipPolyPolygon().count()) 2530 { 2531 // nothing to do, empty active clipPolyPolygon will stay empty 2532 // when intersecting with any region 2533 } 2534 else 2535 { 2536 // AND existing and new region 2537 const basegfx::B2DPolyPolygon aOriginalPolyPolygon( 2538 rPropertyHolders.Current().getClipPolyPolygon()); 2539 basegfx::B2DPolyPolygon aClippedPolyPolygon; 2540 2541 if(aOriginalPolyPolygon.count()) 2542 { 2543 aClippedPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon( 2544 aOriginalPolyPolygon, aNewClipPolyPolygon, true, false); 2545 } 2546 2547 if(aClippedPolyPolygon != aOriginalPolyPolygon) 2548 { 2549 // start new clipping with intersected ClipPolyPolygon 2550 HandleNewClipRegion(aClippedPolyPolygon, rTargetHolders, rPropertyHolders); 2551 } 2552 } 2553 } 2554 else 2555 { 2556 // start new clipping with new ClipPolyPolygon 2557 HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders); 2558 } 2559 } 2560 2561 break; 2562 } 2563 case META_MOVECLIPREGION_ACTION : 2564 { 2565 /** CHECKED, WORKS WELL */ 2566 const MetaMoveClipRegionAction* pA = (const MetaMoveClipRegionAction*)pAction; 2567 2568 if(rPropertyHolders.Current().getClipPolyPolygonActive()) 2569 { 2570 if(0 == rPropertyHolders.Current().getClipPolyPolygon().count()) 2571 { 2572 // nothing to do 2573 } 2574 else 2575 { 2576 const sal_Int32 nHor(pA->GetHorzMove()); 2577 const sal_Int32 nVer(pA->GetVertMove()); 2578 2579 if(0 != nHor || 0 != nVer) 2580 { 2581 // prepare translation, add current transformation 2582 basegfx::B2DVector aVector(pA->GetHorzMove(), pA->GetVertMove()); 2583 aVector *= rPropertyHolders.Current().getTransformation(); 2584 basegfx::B2DHomMatrix aTransform( 2585 basegfx::tools::createTranslateB2DHomMatrix(aVector)); 2586 2587 // transform existing region 2588 basegfx::B2DPolyPolygon aClipPolyPolygon( 2589 rPropertyHolders.Current().getClipPolyPolygon()); 2590 2591 aClipPolyPolygon.transform(aTransform); 2592 HandleNewClipRegion(aClipPolyPolygon, rTargetHolders, rPropertyHolders); 2593 } 2594 } 2595 } 2596 2597 break; 2598 } 2599 case META_LINECOLOR_ACTION : 2600 { 2601 /** CHECKED, WORKS WELL */ 2602 const MetaLineColorAction* pA = (const MetaLineColorAction*)pAction; 2603 const bool bActive(pA->IsSetting()); 2604 2605 rPropertyHolders.Current().setLineColorActive(bActive); 2606 if(bActive) 2607 rPropertyHolders.Current().setLineColor(pA->GetColor().getBColor()); 2608 2609 break; 2610 } 2611 case META_FILLCOLOR_ACTION : 2612 { 2613 /** CHECKED, WORKS WELL */ 2614 const MetaFillColorAction* pA = (const MetaFillColorAction*)pAction; 2615 const bool bActive(pA->IsSetting()); 2616 2617 rPropertyHolders.Current().setFillColorActive(bActive); 2618 if(bActive) 2619 rPropertyHolders.Current().setFillColor(pA->GetColor().getBColor()); 2620 2621 break; 2622 } 2623 case META_TEXTCOLOR_ACTION : 2624 { 2625 /** SIMPLE, DONE */ 2626 const MetaTextColorAction* pA = (const MetaTextColorAction*)pAction; 2627 const bool bActivate(COL_TRANSPARENT != pA->GetColor().GetColor()); 2628 2629 rPropertyHolders.Current().setTextColorActive(bActivate); 2630 rPropertyHolders.Current().setTextColor(pA->GetColor().getBColor()); 2631 2632 break; 2633 } 2634 case META_TEXTFILLCOLOR_ACTION : 2635 { 2636 /** SIMPLE, DONE */ 2637 const MetaTextFillColorAction* pA = (const MetaTextFillColorAction*)pAction; 2638 const bool bWithColorArgument(pA->IsSetting()); 2639 2640 if(bWithColorArgument) 2641 { 2642 // emulate OutputDevice::SetTextFillColor(...) WITH argument 2643 const Color& rFontFillColor = pA->GetColor(); 2644 rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor()); 2645 rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor.GetColor()); 2646 } 2647 else 2648 { 2649 // emulate SetFillColor() <- NO argument (!) 2650 rPropertyHolders.Current().setTextFillColorActive(false); 2651 } 2652 2653 break; 2654 } 2655 case META_TEXTALIGN_ACTION : 2656 { 2657 /** SIMPLE, DONE */ 2658 const MetaTextAlignAction* pA = (const MetaTextAlignAction*)pAction; 2659 const TextAlign aNewTextAlign = pA->GetTextAlign(); 2660 2661 // TextAlign is applied to the current font (as in 2662 // OutputDevice::SetTextAlign which would be used when 2663 // playing the Metafile) 2664 if(rPropertyHolders.Current().getFont().GetAlign() != aNewTextAlign) 2665 { 2666 Font aNewFont(rPropertyHolders.Current().getFont()); 2667 aNewFont.SetAlign(aNewTextAlign); 2668 rPropertyHolders.Current().setFont(aNewFont); 2669 } 2670 2671 break; 2672 } 2673 case META_MAPMODE_ACTION : 2674 { 2675 /** CHECKED, WORKS WELL */ 2676 // the most necessary MapMode to be interpreted is MAP_RELATIVE, 2677 // but also the others may occur. Even not yet supported ones 2678 // may need to be added here later 2679 const MetaMapModeAction* pA = (const MetaMapModeAction*)pAction; 2680 const MapMode& rMapMode = pA->GetMapMode(); 2681 basegfx::B2DHomMatrix aMapping; 2682 2683 if(MAP_RELATIVE == rMapMode.GetMapUnit()) 2684 { 2685 aMapping = getTransformFromMapMode(rMapMode); 2686 } 2687 else 2688 { 2689 switch(rMapMode.GetMapUnit()) 2690 { 2691 case MAP_100TH_MM : 2692 { 2693 if(MAP_TWIP == rPropertyHolders.Current().getMapUnit()) 2694 { 2695 // MAP_TWIP -> MAP_100TH_MM 2696 const double fTwipTo100thMm(127.0 / 72.0); 2697 aMapping.scale(fTwipTo100thMm, fTwipTo100thMm); 2698 } 2699 break; 2700 } 2701 case MAP_TWIP : 2702 { 2703 if(MAP_100TH_MM == rPropertyHolders.Current().getMapUnit()) 2704 { 2705 // MAP_100TH_MM -> MAP_TWIP 2706 const double f100thMmToTwip(72.0 / 127.0); 2707 aMapping.scale(f100thMmToTwip, f100thMmToTwip); 2708 } 2709 break; 2710 } 2711 default : 2712 { 2713 OSL_ENSURE(false, "interpretMetafile: META_MAPMODE_ACTION with unsupported MapUnit (!)"); 2714 break; 2715 } 2716 } 2717 2718 aMapping = getTransformFromMapMode(rMapMode) * aMapping; 2719 rPropertyHolders.Current().setMapUnit(rMapMode.GetMapUnit()); 2720 } 2721 2722 if(!aMapping.isIdentity()) 2723 { 2724 aMapping = aMapping * rPropertyHolders.Current().getTransformation(); 2725 rPropertyHolders.Current().setTransformation(aMapping); 2726 } 2727 2728 break; 2729 } 2730 case META_FONT_ACTION : 2731 { 2732 /** SIMPLE, DONE */ 2733 const MetaFontAction* pA = (const MetaFontAction*)pAction; 2734 rPropertyHolders.Current().setFont(pA->GetFont()); 2735 Size aFontSize(pA->GetFont().GetSize()); 2736 2737 if(0 == aFontSize.Height()) 2738 { 2739 // this should not happen but i got Metafiles where this was the 2740 // case. A height needs to be guessed (similar to OutputDevice::ImplNewFont()) 2741 Font aCorrectedFont(pA->GetFont()); 2742 2743 // guess 16 pixel (as in VCL) 2744 aFontSize = Size(0, 16); 2745 2746 // convert to target MapUnit if not pixels 2747 aFontSize = Application::GetDefaultDevice()->LogicToLogic( 2748 aFontSize, MAP_PIXEL, rPropertyHolders.Current().getMapUnit()); 2749 2750 aCorrectedFont.SetSize(aFontSize); 2751 rPropertyHolders.Current().setFont(aCorrectedFont); 2752 } 2753 2754 // older Metafiles have no META_TEXTCOLOR_ACTION which defines 2755 // the FontColor now, so use the Font's color when not transparent 2756 const Color& rFontColor = pA->GetFont().GetColor(); 2757 const bool bActivate(COL_TRANSPARENT != rFontColor.GetColor()); 2758 2759 if(bActivate) 2760 { 2761 rPropertyHolders.Current().setTextColor(rFontColor.getBColor()); 2762 } 2763 2764 // caution: do NOT decativate here on transparet, see 2765 // OutputDevice::SetFont(..) for more info 2766 // rPropertyHolders.Current().setTextColorActive(bActivate); 2767 2768 // for fill color emulate a MetaTextFillColorAction with !transparent as bool, 2769 // see OutputDevice::SetFont(..) the if(mpMetaFile) case 2770 if(bActivate) 2771 { 2772 const Color& rFontFillColor = pA->GetFont().GetFillColor(); 2773 rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor()); 2774 rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor.GetColor()); 2775 } 2776 else 2777 { 2778 rPropertyHolders.Current().setTextFillColorActive(false); 2779 } 2780 2781 break; 2782 } 2783 case META_PUSH_ACTION : 2784 { 2785 /** CHECKED, WORKS WELL */ 2786 const MetaPushAction* pA = (const MetaPushAction*)pAction; 2787 rPropertyHolders.Push(pA->GetFlags()); 2788 2789 break; 2790 } 2791 case META_POP_ACTION : 2792 { 2793 /** CHECKED, WORKS WELL */ 2794 const bool bRegionMayChange(rPropertyHolders.Current().getPushFlags() & PUSH_CLIPREGION); 2795 const bool bRasterOpMayChange(rPropertyHolders.Current().getPushFlags() & PUSH_RASTEROP); 2796 2797 if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive()) 2798 { 2799 // end evtl. clipping 2800 const basegfx::B2DPolyPolygon aEmptyPolyPolygon; 2801 2802 HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders); 2803 } 2804 2805 if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive()) 2806 { 2807 // end evtl. RasterOp 2808 HandleNewRasterOp(ROP_OVERPAINT, rTargetHolders, rPropertyHolders); 2809 } 2810 2811 rPropertyHolders.Pop(); 2812 2813 if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive()) 2814 { 2815 // start evtl. RasterOp 2816 HandleNewRasterOp(rPropertyHolders.Current().getRasterOp(), rTargetHolders, rPropertyHolders); 2817 } 2818 2819 if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive()) 2820 { 2821 // start evtl. clipping 2822 HandleNewClipRegion( 2823 rPropertyHolders.Current().getClipPolyPolygon(), rTargetHolders, rPropertyHolders); 2824 } 2825 2826 break; 2827 } 2828 case META_RASTEROP_ACTION : 2829 { 2830 /** CHECKED, WORKS WELL */ 2831 const MetaRasterOpAction* pA = (const MetaRasterOpAction*)pAction; 2832 const RasterOp aRasterOp = pA->GetRasterOp(); 2833 2834 HandleNewRasterOp(aRasterOp, rTargetHolders, rPropertyHolders); 2835 2836 break; 2837 } 2838 case META_TRANSPARENT_ACTION : 2839 { 2840 /** CHECKED, WORKS WELL */ 2841 const MetaTransparentAction* pA = (const MetaTransparentAction*)pAction; 2842 const basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon()); 2843 2844 if(aOutline.count()) 2845 { 2846 const sal_uInt16 nTransparence(pA->GetTransparence()); 2847 2848 if(0 == nTransparence) 2849 { 2850 // not transparent 2851 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current()); 2852 } 2853 else if(nTransparence >= 100) 2854 { 2855 // fully or more than transparent 2856 } 2857 else 2858 { 2859 // transparent. Create new target 2860 rTargetHolders.Push(); 2861 2862 // create primitives there and get them 2863 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current()); 2864 const drawinglayer::primitive2d::Primitive2DSequence aSubContent( 2865 rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current())); 2866 2867 // back to old target 2868 rTargetHolders.Pop(); 2869 2870 if(aSubContent.hasElements()) 2871 { 2872 rTargetHolders.Current().append( 2873 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 2874 aSubContent, 2875 nTransparence * 0.01)); 2876 } 2877 } 2878 } 2879 2880 break; 2881 } 2882 case META_EPS_ACTION : 2883 { 2884 /** CHECKED, WORKS WELL */ 2885 // To support this action, i have added a EpsPrimitive2D which will 2886 // by default decompose to the Metafile replacement data. To support 2887 // this EPS on screen, the renderer visualizing this has to support 2888 // that primitive and visualize the Eps file (e.g. printing) 2889 const MetaEPSAction* pA = (const MetaEPSAction*)pAction; 2890 const Rectangle aRectangle(pA->GetPoint(), pA->GetSize()); 2891 2892 if(!aRectangle.IsEmpty()) 2893 { 2894 // create object transform 2895 basegfx::B2DHomMatrix aObjectTransform; 2896 2897 aObjectTransform.set(0, 0, aRectangle.GetWidth()); 2898 aObjectTransform.set(1, 1, aRectangle.GetHeight()); 2899 aObjectTransform.set(0, 2, aRectangle.Left()); 2900 aObjectTransform.set(1, 2, aRectangle.Top()); 2901 2902 // add current transformation 2903 aObjectTransform = rPropertyHolders.Current().getTransformation() * aObjectTransform; 2904 2905 // embed using EpsPrimitive 2906 rTargetHolders.Current().append( 2907 new drawinglayer::primitive2d::EpsPrimitive2D( 2908 aObjectTransform, 2909 pA->GetLink(), 2910 pA->GetSubstitute())); 2911 } 2912 2913 break; 2914 } 2915 case META_REFPOINT_ACTION : 2916 { 2917 /** SIMPLE, DONE */ 2918 // only used for hatch and line pattern offsets, pretty much no longer 2919 // supported today 2920 // const MetaRefPointAction* pA = (const MetaRefPointAction*)pAction; 2921 break; 2922 } 2923 case META_TEXTLINECOLOR_ACTION : 2924 { 2925 /** SIMPLE, DONE */ 2926 const MetaTextLineColorAction* pA = (const MetaTextLineColorAction*)pAction; 2927 const bool bActive(pA->IsSetting()); 2928 2929 rPropertyHolders.Current().setTextLineColorActive(bActive); 2930 if(bActive) 2931 rPropertyHolders.Current().setTextLineColor(pA->GetColor().getBColor()); 2932 2933 break; 2934 } 2935 case META_TEXTLINE_ACTION : 2936 { 2937 /** CHECKED, WORKS WELL */ 2938 // actually creates overline, underline and strikeouts, so 2939 // these should be isolated from TextDecoratedPortionPrimitive2D 2940 // to own primitives. Done, available now. 2941 // 2942 // This Metaaction seems not to be used (was not used in any 2943 // checked files). It's used in combination with the current 2944 // Font. 2945 const MetaTextLineAction* pA = (const MetaTextLineAction*)pAction; 2946 2947 proccessMetaTextLineAction( 2948 *pA, 2949 rTargetHolders.Current(), 2950 rPropertyHolders.Current()); 2951 2952 break; 2953 } 2954 case META_FLOATTRANSPARENT_ACTION : 2955 { 2956 /** CHECKED, WORKS WELL */ 2957 const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*)pAction; 2958 const basegfx::B2DRange aTargetRange( 2959 pA->GetPoint().X(), 2960 pA->GetPoint().Y(), 2961 pA->GetPoint().X() + pA->GetSize().Width(), 2962 pA->GetPoint().Y() + pA->GetSize().Height()); 2963 2964 if(!aTargetRange.isEmpty()) 2965 { 2966 const GDIMetaFile& rContent = pA->GetGDIMetaFile(); 2967 2968 if(rContent.GetActionCount()) 2969 { 2970 // create the sub-content with no embedding specific to the 2971 // sub-metafile, this seems not to be used. 2972 drawinglayer::primitive2d::Primitive2DSequence xSubContent; 2973 { 2974 rTargetHolders.Push(); 2975 // #i# for sub-Mteafile contents, do start with new, default render state 2976 rPropertyHolders.PushDefault(); 2977 interpretMetafile(rContent, rTargetHolders, rPropertyHolders, rViewInformation); 2978 xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current()); 2979 rPropertyHolders.Pop(); 2980 rTargetHolders.Pop(); 2981 } 2982 2983 if(xSubContent.hasElements()) 2984 { 2985 // prepare sub-content transform 2986 basegfx::B2DHomMatrix aSubTransform; 2987 2988 // create SourceRange 2989 const basegfx::B2DRange aSourceRange( 2990 rContent.GetPrefMapMode().GetOrigin().X(), 2991 rContent.GetPrefMapMode().GetOrigin().Y(), 2992 rContent.GetPrefMapMode().GetOrigin().X() + rContent.GetPrefSize().Width(), 2993 rContent.GetPrefMapMode().GetOrigin().Y() + rContent.GetPrefSize().Height()); 2994 2995 // apply mapping if aTargetRange and aSourceRange are not equal 2996 if(!aSourceRange.equal(aTargetRange)) 2997 { 2998 aSubTransform.translate(-aSourceRange.getMinX(), -aSourceRange.getMinY()); 2999 aSubTransform.scale( 3000 aTargetRange.getWidth() / (basegfx::fTools::equalZero(aSourceRange.getWidth()) ? 1.0 : aSourceRange.getWidth()), 3001 aTargetRange.getHeight() / (basegfx::fTools::equalZero(aSourceRange.getHeight()) ? 1.0 : aSourceRange.getHeight())); 3002 aSubTransform.translate(aTargetRange.getMinX(), aTargetRange.getMinY()); 3003 } 3004 3005 // apply general current transformation 3006 aSubTransform = rPropertyHolders.Current().getTransformation() * aSubTransform; 3007 3008 // evtl. embed sub-content to it's transformation 3009 if(!aSubTransform.isIdentity()) 3010 { 3011 const drawinglayer::primitive2d::Primitive2DReference aEmbeddedTransform( 3012 new drawinglayer::primitive2d::TransformPrimitive2D( 3013 aSubTransform, 3014 xSubContent)); 3015 3016 xSubContent = drawinglayer::primitive2d::Primitive2DSequence(&aEmbeddedTransform, 1); 3017 } 3018 3019 // check if gradient is a real gradient 3020 const Gradient& rGradient = pA->GetGradient(); 3021 const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); 3022 3023 if(aAttribute.getStartColor() == aAttribute.getEndColor()) 3024 { 3025 // not really a gradient; create UnifiedTransparencePrimitive2D 3026 rTargetHolders.Current().append( 3027 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 3028 xSubContent, 3029 aAttribute.getStartColor().luminance())); 3030 } 3031 else 3032 { 3033 // really a gradient. Create gradient sub-content (with correct scaling) 3034 basegfx::B2DRange aRange(aTargetRange); 3035 aRange.transform(rPropertyHolders.Current().getTransformation()); 3036 3037 // prepare gradient for transparent content 3038 const drawinglayer::primitive2d::Primitive2DReference xTransparence( 3039 new drawinglayer::primitive2d::FillGradientPrimitive2D( 3040 aRange, 3041 aAttribute)); 3042 3043 // create transparence primitive 3044 rTargetHolders.Current().append( 3045 new drawinglayer::primitive2d::TransparencePrimitive2D( 3046 xSubContent, 3047 drawinglayer::primitive2d::Primitive2DSequence(&xTransparence, 1))); 3048 } 3049 } 3050 } 3051 } 3052 3053 break; 3054 } 3055 case META_GRADIENTEX_ACTION : 3056 { 3057 /** SIMPLE, DONE */ 3058 // This is only a data holder which is interpreted inside comment actions, 3059 // see META_COMMENT_ACTION for more info 3060 // const MetaGradientExAction* pA = (const MetaGradientExAction*)pAction; 3061 break; 3062 } 3063 case META_LAYOUTMODE_ACTION : 3064 { 3065 /** SIMPLE, DONE */ 3066 const MetaLayoutModeAction* pA = (const MetaLayoutModeAction*)pAction; 3067 rPropertyHolders.Current().setLayoutMode(pA->GetLayoutMode()); 3068 break; 3069 } 3070 case META_TEXTLANGUAGE_ACTION : 3071 { 3072 /** SIMPLE, DONE */ 3073 const MetaTextLanguageAction* pA = (const MetaTextLanguageAction*)pAction; 3074 rPropertyHolders.Current().setLanguageType(pA->GetTextLanguage()); 3075 break; 3076 } 3077 case META_OVERLINECOLOR_ACTION : 3078 { 3079 /** SIMPLE, DONE */ 3080 const MetaOverlineColorAction* pA = (const MetaOverlineColorAction*)pAction; 3081 const bool bActive(pA->IsSetting()); 3082 3083 rPropertyHolders.Current().setOverlineColorActive(bActive); 3084 if(bActive) 3085 rPropertyHolders.Current().setOverlineColor(pA->GetColor().getBColor()); 3086 3087 break; 3088 } 3089 case META_COMMENT_ACTION : 3090 { 3091 /** CHECKED, WORKS WELL */ 3092 // I already implemented 3093 // XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END 3094 // XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END, 3095 // but opted to remove these again; it works well without them 3096 // and makes the code less dependent from those Metafile Add-Ons 3097 const MetaCommentAction* pA = (const MetaCommentAction*)pAction; 3098 3099 if(COMPARE_EQUAL == pA->GetComment().CompareIgnoreCaseToAscii("XGRAD_SEQ_BEGIN")) 3100 { 3101 // XGRAD_SEQ_BEGIN, XGRAD_SEQ_END should be supported since the 3102 // pure recorded paint of the gradients uses the XOR paint functionality 3103 // ('trick'). This is (and will be) broblematic with AntAliasing, so it's 3104 // better to use this info 3105 const MetaGradientExAction* pMetaGradientExAction = 0; 3106 bool bDone(false); 3107 sal_uInt32 b(nAction + 1); 3108 3109 for(; !bDone && b < nCount; b++) 3110 { 3111 pAction = rMetaFile.GetAction(b); 3112 3113 if(META_GRADIENTEX_ACTION == pAction->GetType()) 3114 { 3115 pMetaGradientExAction = (const MetaGradientExAction*)pAction; 3116 } 3117 else if(META_COMMENT_ACTION == pAction->GetType()) 3118 { 3119 if(COMPARE_EQUAL == ((const MetaCommentAction*)pAction)->GetComment().CompareIgnoreCaseToAscii("XGRAD_SEQ_END")) 3120 { 3121 bDone = true; 3122 } 3123 } 3124 } 3125 3126 if(bDone && pMetaGradientExAction) 3127 { 3128 // consume actions and skip forward 3129 nAction = b - 1; 3130 3131 // get geometry data 3132 basegfx::B2DPolyPolygon aPolyPolygon(pMetaGradientExAction->GetPolyPolygon().getB2DPolyPolygon()); 3133 3134 if(aPolyPolygon.count()) 3135 { 3136 // transform geometry 3137 aPolyPolygon.transform(rPropertyHolders.Current().getTransformation()); 3138 3139 // get and check if gradient is a real gradient 3140 const Gradient& rGradient = pMetaGradientExAction->GetGradient(); 3141 const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); 3142 3143 if(aAttribute.getStartColor() == aAttribute.getEndColor()) 3144 { 3145 // not really a gradient 3146 rTargetHolders.Current().append( 3147 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( 3148 aPolyPolygon, 3149 aAttribute.getStartColor())); 3150 } 3151 else 3152 { 3153 // really a gradient 3154 rTargetHolders.Current().append( 3155 new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D( 3156 aPolyPolygon, 3157 aAttribute)); 3158 } 3159 } 3160 } 3161 } 3162 3163 break; 3164 } 3165 default: 3166 { 3167 OSL_ENSURE(false, "Unknown MetaFile Action (!)"); 3168 break; 3169 } 3170 } 3171 } 3172 } 3173 } // end of anonymous namespace 3174 3175 ////////////////////////////////////////////////////////////////////////////// 3176 3177 namespace drawinglayer 3178 { 3179 namespace primitive2d 3180 { create2DDecomposition(const geometry::ViewInformation2D & rViewInformation) const3181 Primitive2DSequence MetafilePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const 3182 { 3183 // prepare target and porperties; each will have one default entry 3184 TargetHolders aTargetHolders; 3185 PropertyHolders aPropertyHolders; 3186 3187 // set target MapUnit at Properties 3188 aPropertyHolders.Current().setMapUnit(getMetaFile().GetPrefMapMode().GetMapUnit()); 3189 3190 // interpret the Metafile 3191 interpretMetafile(getMetaFile(), aTargetHolders, aPropertyHolders, rViewInformation); 3192 3193 // get the content. There should be ony one target, as in the start condition, 3194 // but iterating will be the right thing to do when some push/pop is not closed 3195 Primitive2DSequence xRetval; 3196 3197 while(aTargetHolders.size() > 1) 3198 { 3199 appendPrimitive2DSequenceToPrimitive2DSequence(xRetval, 3200 aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current())); 3201 aTargetHolders.Pop(); 3202 } 3203 3204 appendPrimitive2DSequenceToPrimitive2DSequence(xRetval, 3205 aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current())); 3206 3207 if(xRetval.hasElements()) 3208 { 3209 // get target size 3210 const Rectangle aMtfTarget(getMetaFile().GetPrefMapMode().GetOrigin(), getMetaFile().GetPrefSize()); 3211 3212 // create transformation 3213 basegfx::B2DHomMatrix aAdaptedTransform; 3214 3215 aAdaptedTransform.translate(-aMtfTarget.Left(), -aMtfTarget.Top()); 3216 aAdaptedTransform.scale( 3217 aMtfTarget.getWidth() ? 1.0 / aMtfTarget.getWidth() : 1.0, 3218 aMtfTarget.getHeight() ? 1.0 / aMtfTarget.getHeight() : 1.0); 3219 aAdaptedTransform = getTransform() * aAdaptedTransform; 3220 3221 // embed to target transformation 3222 const Primitive2DReference aEmbeddedTransform( 3223 new TransformPrimitive2D( 3224 aAdaptedTransform, 3225 xRetval)); 3226 3227 xRetval = Primitive2DSequence(&aEmbeddedTransform, 1); 3228 } 3229 3230 return xRetval; 3231 } 3232 MetafilePrimitive2D(const basegfx::B2DHomMatrix & rMetaFileTransform,const GDIMetaFile & rMetaFile)3233 MetafilePrimitive2D::MetafilePrimitive2D( 3234 const basegfx::B2DHomMatrix& rMetaFileTransform, 3235 const GDIMetaFile& rMetaFile) 3236 : BufferedDecompositionPrimitive2D(), 3237 maMetaFileTransform(rMetaFileTransform), 3238 maMetaFile(rMetaFile) 3239 { 3240 } 3241 operator ==(const BasePrimitive2D & rPrimitive) const3242 bool MetafilePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 3243 { 3244 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) 3245 { 3246 const MetafilePrimitive2D& rCompare = (MetafilePrimitive2D&)rPrimitive; 3247 3248 return (getTransform() == rCompare.getTransform() 3249 && getMetaFile() == rCompare.getMetaFile()); 3250 } 3251 3252 return false; 3253 } 3254 getB2DRange(const geometry::ViewInformation2D &) const3255 basegfx::B2DRange MetafilePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const 3256 { 3257 // use own implementation to quickly answer the getB2DRange question. The 3258 // MetafilePrimitive2D assumes that all geometry is inside of the shape. If 3259 // this is not the case (i have already seen some wrong Metafiles) it should 3260 // be embedded to a MaskPrimitive2D 3261 basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0); 3262 aRetval.transform(getTransform()); 3263 3264 return aRetval; 3265 } 3266 3267 // provide unique ID 3268 ImplPrimitrive2DIDBlock(MetafilePrimitive2D, PRIMITIVE2D_ID_METAFILEPRIMITIVE2D) 3269 3270 } // end of namespace primitive2d 3271 } // end of namespace drawinglayer 3272 3273 ////////////////////////////////////////////////////////////////////////////// 3274 // eof 3275