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 properties 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 Primitive target vector. It 403 encapsulates the new/delete functionality and allows 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 transparency 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 which change the Region of the current context. It takes care of creating the 956 current embedded 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 unnecessarily 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 changing 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 wanted 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 strikeout 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 UnderlineAbove 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 which 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 comments, 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 // prepare 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 isolation. 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 2131 // for sub-Metafile contents, do start with new, default render state 2132 // #124686# ...but copy font, this is already set accordingly 2133 const Font& rTargetFont = rPropertyHolders.Current().getFont(); 2134 rPropertyHolders.PushDefault(); 2135 rPropertyHolders.Current().setFont(rTargetFont); 2136 2137 interpretMetafile(aGDIMetaFile, rTargetHolders, rPropertyHolders, rViewInformation); 2138 xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current()); 2139 rPropertyHolders.Pop(); 2140 rTargetHolders.Pop(); 2141 } 2142 2143 if(xSubContent.hasElements()) 2144 { 2145 // add with transformation 2146 rTargetHolders.Current().append( 2147 new drawinglayer::primitive2d::TransformPrimitive2D( 2148 rPropertyHolders.Current().getTransformation(), 2149 xSubContent)); 2150 } 2151 } 2152 } 2153 2154 break; 2155 } 2156 case META_BMP_ACTION : 2157 { 2158 /** CHECKED, WORKS WELL */ 2159 const MetaBmpAction* pA = (const MetaBmpAction*)pAction; 2160 const BitmapEx aBitmapEx(pA->GetBitmap()); 2161 2162 createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current()); 2163 2164 break; 2165 } 2166 case META_BMPSCALE_ACTION : 2167 { 2168 /** CHECKED, WORKS WELL */ 2169 const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*)pAction; 2170 const Bitmap aBitmapEx(pA->GetBitmap()); 2171 2172 createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current()); 2173 2174 break; 2175 } 2176 case META_BMPSCALEPART_ACTION : 2177 { 2178 /** CHECKED, WORKS WELL */ 2179 const MetaBmpScalePartAction* pA = (const MetaBmpScalePartAction*)pAction; 2180 const Bitmap& rBitmap = pA->GetBitmap(); 2181 2182 if(!rBitmap.IsEmpty()) 2183 { 2184 Bitmap aCroppedBitmap(rBitmap); 2185 const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); 2186 2187 if(!aCropRectangle.IsEmpty()) 2188 { 2189 aCroppedBitmap.Crop(aCropRectangle); 2190 } 2191 2192 const BitmapEx aCroppedBitmapEx(aCroppedBitmap); 2193 createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current()); 2194 } 2195 2196 break; 2197 } 2198 case META_BMPEX_ACTION : 2199 { 2200 /** CHECKED, WORKS WELL: Simply same as META_BMP_ACTION */ 2201 const MetaBmpExAction* pA = (const MetaBmpExAction*)pAction; 2202 const BitmapEx& rBitmapEx = pA->GetBitmapEx(); 2203 2204 createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current()); 2205 2206 break; 2207 } 2208 case META_BMPEXSCALE_ACTION : 2209 { 2210 /** CHECKED, WORKS WELL: Simply same as META_BMPSCALE_ACTION */ 2211 const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*)pAction; 2212 const BitmapEx& rBitmapEx = pA->GetBitmapEx(); 2213 2214 createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current()); 2215 2216 break; 2217 } 2218 case META_BMPEXSCALEPART_ACTION : 2219 { 2220 /** CHECKED, WORKS WELL: Simply same as META_BMPSCALEPART_ACTION */ 2221 const MetaBmpExScalePartAction* pA = (const MetaBmpExScalePartAction*)pAction; 2222 const BitmapEx& rBitmapEx = pA->GetBitmapEx(); 2223 2224 if(!rBitmapEx.IsEmpty()) 2225 { 2226 BitmapEx aCroppedBitmapEx(rBitmapEx); 2227 const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); 2228 2229 if(!aCropRectangle.IsEmpty()) 2230 { 2231 aCroppedBitmapEx.Crop(aCropRectangle); 2232 } 2233 2234 createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current()); 2235 } 2236 2237 break; 2238 } 2239 case META_MASK_ACTION : 2240 { 2241 /** CHECKED, WORKS WELL: Simply same as META_BMP_ACTION */ 2242 const MetaMaskAction* pA = (const MetaMaskAction*)pAction; 2243 const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor())); 2244 2245 createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current()); 2246 2247 break; 2248 } 2249 case META_MASKSCALE_ACTION : 2250 { 2251 /** CHECKED, WORKS WELL: Simply same as META_BMPSCALE_ACTION */ 2252 const MetaMaskScaleAction* pA = (const MetaMaskScaleAction*)pAction; 2253 const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor())); 2254 2255 createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current()); 2256 2257 break; 2258 } 2259 case META_MASKSCALEPART_ACTION : 2260 { 2261 /** CHECKED, WORKS WELL: Simply same as META_BMPSCALEPART_ACTION */ 2262 const MetaMaskScalePartAction* pA = (const MetaMaskScalePartAction*)pAction; 2263 const Bitmap& rBitmap = pA->GetBitmap(); 2264 2265 if(!rBitmap.IsEmpty()) 2266 { 2267 Bitmap aCroppedBitmap(rBitmap); 2268 const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); 2269 2270 if(!aCropRectangle.IsEmpty()) 2271 { 2272 aCroppedBitmap.Crop(aCropRectangle); 2273 } 2274 2275 const BitmapEx aCroppedBitmapEx(createMaskBmpEx(aCroppedBitmap, pA->GetColor())); 2276 createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current()); 2277 } 2278 2279 break; 2280 } 2281 case META_GRADIENT_ACTION : 2282 { 2283 /** CHECKED, WORKS WELL */ 2284 const MetaGradientAction* pA = (const MetaGradientAction*)pAction; 2285 const Rectangle& rRectangle = pA->GetRect(); 2286 2287 if(!rRectangle.IsEmpty()) 2288 { 2289 basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom()); 2290 2291 if(!aRange.isEmpty()) 2292 { 2293 const Gradient& rGradient = pA->GetGradient(); 2294 const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); 2295 basegfx::B2DPolyPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange)); 2296 2297 if(aAttribute.getStartColor() == aAttribute.getEndColor()) 2298 { 2299 // not really a gradient. Create filled rectangle 2300 createFillPrimitive( 2301 aOutline, 2302 rTargetHolders.Current(), 2303 rPropertyHolders.Current()); 2304 } 2305 else 2306 { 2307 // really a gradient 2308 aRange.transform(rPropertyHolders.Current().getTransformation()); 2309 drawinglayer::primitive2d::Primitive2DSequence xGradient(1); 2310 2311 if(rPropertyHolders.Current().isRasterOpInvert()) 2312 { 2313 // use a special version of FillGradientPrimitive2D which creates 2314 // non-overlapping geometry on decomposition to make the old XOR 2315 // paint 'trick' work. 2316 xGradient[0] = drawinglayer::primitive2d::Primitive2DReference( 2317 new drawinglayer::primitive2d::NonOverlappingFillGradientPrimitive2D( 2318 aRange, 2319 aAttribute)); 2320 } 2321 else 2322 { 2323 xGradient[0] = drawinglayer::primitive2d::Primitive2DReference( 2324 new drawinglayer::primitive2d::FillGradientPrimitive2D( 2325 aRange, 2326 aAttribute)); 2327 } 2328 2329 // #i112300# clip against polygon representing the rectangle from 2330 // the action. This is implicitly done using a temp Clipping in VCL 2331 // when a MetaGradientAction is executed 2332 aOutline.transform(rPropertyHolders.Current().getTransformation()); 2333 rTargetHolders.Current().append( 2334 new drawinglayer::primitive2d::MaskPrimitive2D( 2335 aOutline, 2336 xGradient)); 2337 } 2338 } 2339 } 2340 2341 break; 2342 } 2343 case META_HATCH_ACTION : 2344 { 2345 /** CHECKED, WORKS WELL */ 2346 const MetaHatchAction* pA = (const MetaHatchAction*)pAction; 2347 basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon()); 2348 2349 if(aOutline.count()) 2350 { 2351 const Hatch& rHatch = pA->GetHatch(); 2352 const drawinglayer::attribute::FillHatchAttribute aAttribute(createFillHatchAttribute(rHatch)); 2353 2354 aOutline.transform(rPropertyHolders.Current().getTransformation()); 2355 2356 const basegfx::B2DRange aObjectRange(aOutline.getB2DRange()); 2357 const drawinglayer::primitive2d::Primitive2DReference aFillHatch( 2358 new drawinglayer::primitive2d::FillHatchPrimitive2D( 2359 aObjectRange, 2360 basegfx::BColor(), 2361 aAttribute)); 2362 2363 rTargetHolders.Current().append( 2364 new drawinglayer::primitive2d::MaskPrimitive2D( 2365 aOutline, 2366 drawinglayer::primitive2d::Primitive2DSequence(&aFillHatch, 1))); 2367 } 2368 2369 break; 2370 } 2371 case META_WALLPAPER_ACTION : 2372 { 2373 /** CHECKED, WORKS WELL */ 2374 const MetaWallpaperAction* pA = (const MetaWallpaperAction*)pAction; 2375 Rectangle aWallpaperRectangle(pA->GetRect()); 2376 2377 if(!aWallpaperRectangle.IsEmpty()) 2378 { 2379 const Wallpaper& rWallpaper = pA->GetWallpaper(); 2380 const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle()); 2381 basegfx::B2DRange aWallpaperRange( 2382 aWallpaperRectangle.Left(), aWallpaperRectangle.Top(), 2383 aWallpaperRectangle.Right(), aWallpaperRectangle.Bottom()); 2384 2385 if(WALLPAPER_NULL != eWallpaperStyle) 2386 { 2387 if(rWallpaper.IsBitmap()) 2388 { 2389 // create bitmap background. Caution: This 2390 // also will create gradient/color background(s) 2391 // when the bitmap is transparent or not tiled 2392 CreateAndAppendBitmapWallpaper( 2393 aWallpaperRange, 2394 rWallpaper, 2395 rTargetHolders.Current(), 2396 rPropertyHolders.Current()); 2397 } 2398 else if(rWallpaper.IsGradient()) 2399 { 2400 // create gradient background 2401 rTargetHolders.Current().append( 2402 CreateGradientWallpaper( 2403 aWallpaperRange, 2404 rWallpaper.GetGradient(), 2405 rPropertyHolders.Current())); 2406 } 2407 else if(!rWallpaper.GetColor().GetTransparency()) 2408 { 2409 // create color background 2410 rTargetHolders.Current().append( 2411 CreateColorWallpaper( 2412 aWallpaperRange, 2413 rWallpaper.GetColor().getBColor(), 2414 rPropertyHolders.Current())); 2415 } 2416 } 2417 } 2418 2419 break; 2420 } 2421 case META_CLIPREGION_ACTION : 2422 { 2423 /** CHECKED, WORKS WELL */ 2424 const MetaClipRegionAction* pA = (const MetaClipRegionAction*)pAction; 2425 2426 if(pA->IsClipping()) 2427 { 2428 // new clipping. Get PolyPolygon and transform with current transformation 2429 basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(pA->GetRegion())); 2430 2431 aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation()); 2432 HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders); 2433 } 2434 else 2435 { 2436 // end clipping 2437 const basegfx::B2DPolyPolygon aEmptyPolyPolygon; 2438 2439 HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders); 2440 } 2441 2442 break; 2443 } 2444 case META_ISECTRECTCLIPREGION_ACTION : 2445 { 2446 /** CHECKED, WORKS WELL */ 2447 const MetaISectRectClipRegionAction* pA = (const MetaISectRectClipRegionAction*)pAction; 2448 const Rectangle& rRectangle = pA->GetRect(); 2449 2450 if(rRectangle.IsEmpty()) 2451 { 2452 // intersect with empty rectangle will always give empty 2453 // ClipPolyPolygon; start new clipping with empty PolyPolygon 2454 const basegfx::B2DPolyPolygon aEmptyPolyPolygon; 2455 2456 HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders); 2457 } 2458 else 2459 { 2460 // create transformed ClipRange 2461 basegfx::B2DRange aClipRange( 2462 rRectangle.Left(), rRectangle.Top(), 2463 rRectangle.Right(), rRectangle.Bottom()); 2464 2465 aClipRange.transform(rPropertyHolders.Current().getTransformation()); 2466 2467 if(rPropertyHolders.Current().getClipPolyPolygonActive()) 2468 { 2469 if(0 == rPropertyHolders.Current().getClipPolyPolygon().count()) 2470 { 2471 // nothing to do, empty active clipPolyPolygon will stay 2472 // empty when intersecting 2473 } 2474 else 2475 { 2476 // AND existing region and new ClipRange 2477 const basegfx::B2DPolyPolygon aOriginalPolyPolygon( 2478 rPropertyHolders.Current().getClipPolyPolygon()); 2479 basegfx::B2DPolyPolygon aClippedPolyPolygon; 2480 2481 if(aOriginalPolyPolygon.count()) 2482 { 2483 aClippedPolyPolygon = basegfx::tools::clipPolyPolygonOnRange( 2484 aOriginalPolyPolygon, 2485 aClipRange, 2486 true, 2487 false); 2488 } 2489 2490 if(aClippedPolyPolygon != aOriginalPolyPolygon) 2491 { 2492 // start new clipping with intersected region 2493 HandleNewClipRegion( 2494 aClippedPolyPolygon, 2495 rTargetHolders, 2496 rPropertyHolders); 2497 } 2498 } 2499 } 2500 else 2501 { 2502 // start new clipping with ClipRange 2503 const basegfx::B2DPolyPolygon aNewClipPolyPolygon( 2504 basegfx::tools::createPolygonFromRect(aClipRange)); 2505 2506 HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders); 2507 } 2508 } 2509 2510 break; 2511 } 2512 case META_ISECTREGIONCLIPREGION_ACTION : 2513 { 2514 /** CHECKED, WORKS WELL */ 2515 const MetaISectRegionClipRegionAction* pA = (const MetaISectRegionClipRegionAction*)pAction; 2516 const Region& rNewRegion = pA->GetRegion(); 2517 2518 if(rNewRegion.IsEmpty()) 2519 { 2520 // intersect with empty region will always give empty 2521 // region; start new clipping with empty PolyPolygon 2522 const basegfx::B2DPolyPolygon aEmptyPolyPolygon; 2523 2524 HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders); 2525 } 2526 else 2527 { 2528 // get new ClipPolyPolygon, transform it with current transformation 2529 basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(rNewRegion)); 2530 aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation()); 2531 2532 if(rPropertyHolders.Current().getClipPolyPolygonActive()) 2533 { 2534 if(0 == rPropertyHolders.Current().getClipPolyPolygon().count()) 2535 { 2536 // nothing to do, empty active clipPolyPolygon will stay empty 2537 // when intersecting with any region 2538 } 2539 else 2540 { 2541 // AND existing and new region 2542 const basegfx::B2DPolyPolygon aOriginalPolyPolygon( 2543 rPropertyHolders.Current().getClipPolyPolygon()); 2544 basegfx::B2DPolyPolygon aClippedPolyPolygon; 2545 2546 if(aOriginalPolyPolygon.count()) 2547 { 2548 aClippedPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon( 2549 aOriginalPolyPolygon, aNewClipPolyPolygon, true, false); 2550 } 2551 2552 if(aClippedPolyPolygon != aOriginalPolyPolygon) 2553 { 2554 // start new clipping with intersected ClipPolyPolygon 2555 HandleNewClipRegion(aClippedPolyPolygon, rTargetHolders, rPropertyHolders); 2556 } 2557 } 2558 } 2559 else 2560 { 2561 // start new clipping with new ClipPolyPolygon 2562 HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders); 2563 } 2564 } 2565 2566 break; 2567 } 2568 case META_MOVECLIPREGION_ACTION : 2569 { 2570 /** CHECKED, WORKS WELL */ 2571 const MetaMoveClipRegionAction* pA = (const MetaMoveClipRegionAction*)pAction; 2572 2573 if(rPropertyHolders.Current().getClipPolyPolygonActive()) 2574 { 2575 if(0 == rPropertyHolders.Current().getClipPolyPolygon().count()) 2576 { 2577 // nothing to do 2578 } 2579 else 2580 { 2581 const sal_Int32 nHor(pA->GetHorzMove()); 2582 const sal_Int32 nVer(pA->GetVertMove()); 2583 2584 if(0 != nHor || 0 != nVer) 2585 { 2586 // prepare translation, add current transformation 2587 basegfx::B2DVector aVector(pA->GetHorzMove(), pA->GetVertMove()); 2588 aVector *= rPropertyHolders.Current().getTransformation(); 2589 basegfx::B2DHomMatrix aTransform( 2590 basegfx::tools::createTranslateB2DHomMatrix(aVector)); 2591 2592 // transform existing region 2593 basegfx::B2DPolyPolygon aClipPolyPolygon( 2594 rPropertyHolders.Current().getClipPolyPolygon()); 2595 2596 aClipPolyPolygon.transform(aTransform); 2597 HandleNewClipRegion(aClipPolyPolygon, rTargetHolders, rPropertyHolders); 2598 } 2599 } 2600 } 2601 2602 break; 2603 } 2604 case META_LINECOLOR_ACTION : 2605 { 2606 /** CHECKED, WORKS WELL */ 2607 const MetaLineColorAction* pA = (const MetaLineColorAction*)pAction; 2608 const bool bActive(pA->IsSetting()); 2609 2610 rPropertyHolders.Current().setLineColorActive(bActive); 2611 if(bActive) 2612 rPropertyHolders.Current().setLineColor(pA->GetColor().getBColor()); 2613 2614 break; 2615 } 2616 case META_FILLCOLOR_ACTION : 2617 { 2618 /** CHECKED, WORKS WELL */ 2619 const MetaFillColorAction* pA = (const MetaFillColorAction*)pAction; 2620 const bool bActive(pA->IsSetting()); 2621 2622 rPropertyHolders.Current().setFillColorActive(bActive); 2623 if(bActive) 2624 rPropertyHolders.Current().setFillColor(pA->GetColor().getBColor()); 2625 2626 break; 2627 } 2628 case META_TEXTCOLOR_ACTION : 2629 { 2630 /** SIMPLE, DONE */ 2631 const MetaTextColorAction* pA = (const MetaTextColorAction*)pAction; 2632 const bool bActivate(COL_TRANSPARENT != pA->GetColor().GetColor()); 2633 2634 rPropertyHolders.Current().setTextColorActive(bActivate); 2635 rPropertyHolders.Current().setTextColor(pA->GetColor().getBColor()); 2636 2637 break; 2638 } 2639 case META_TEXTFILLCOLOR_ACTION : 2640 { 2641 /** SIMPLE, DONE */ 2642 const MetaTextFillColorAction* pA = (const MetaTextFillColorAction*)pAction; 2643 const bool bWithColorArgument(pA->IsSetting()); 2644 2645 if(bWithColorArgument) 2646 { 2647 // emulate OutputDevice::SetTextFillColor(...) WITH argument 2648 const Color& rFontFillColor = pA->GetColor(); 2649 rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor()); 2650 rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor.GetColor()); 2651 } 2652 else 2653 { 2654 // emulate SetFillColor() <- NO argument (!) 2655 rPropertyHolders.Current().setTextFillColorActive(false); 2656 } 2657 2658 break; 2659 } 2660 case META_TEXTALIGN_ACTION : 2661 { 2662 /** SIMPLE, DONE */ 2663 const MetaTextAlignAction* pA = (const MetaTextAlignAction*)pAction; 2664 const TextAlign aNewTextAlign = pA->GetTextAlign(); 2665 2666 // TextAlign is applied to the current font (as in 2667 // OutputDevice::SetTextAlign which would be used when 2668 // playing the Metafile) 2669 if(rPropertyHolders.Current().getFont().GetAlign() != aNewTextAlign) 2670 { 2671 Font aNewFont(rPropertyHolders.Current().getFont()); 2672 aNewFont.SetAlign(aNewTextAlign); 2673 rPropertyHolders.Current().setFont(aNewFont); 2674 } 2675 2676 break; 2677 } 2678 case META_MAPMODE_ACTION : 2679 { 2680 /** CHECKED, WORKS WELL */ 2681 // the most necessary MapMode to be interpreted is MAP_RELATIVE, 2682 // but also the others may occur. Even not yet supported ones 2683 // may need to be added here later 2684 const MetaMapModeAction* pA = (const MetaMapModeAction*)pAction; 2685 const MapMode& rMapMode = pA->GetMapMode(); 2686 basegfx::B2DHomMatrix aMapping; 2687 2688 if(MAP_RELATIVE == rMapMode.GetMapUnit()) 2689 { 2690 aMapping = getTransformFromMapMode(rMapMode); 2691 } 2692 else 2693 { 2694 switch(rMapMode.GetMapUnit()) 2695 { 2696 case MAP_100TH_MM : 2697 { 2698 if(MAP_TWIP == rPropertyHolders.Current().getMapUnit()) 2699 { 2700 // MAP_TWIP -> MAP_100TH_MM 2701 const double fTwipTo100thMm(127.0 / 72.0); 2702 aMapping.scale(fTwipTo100thMm, fTwipTo100thMm); 2703 } 2704 break; 2705 } 2706 case MAP_TWIP : 2707 { 2708 if(MAP_100TH_MM == rPropertyHolders.Current().getMapUnit()) 2709 { 2710 // MAP_100TH_MM -> MAP_TWIP 2711 const double f100thMmToTwip(72.0 / 127.0); 2712 aMapping.scale(f100thMmToTwip, f100thMmToTwip); 2713 } 2714 break; 2715 } 2716 default : 2717 { 2718 OSL_ENSURE(false, "interpretMetafile: META_MAPMODE_ACTION with unsupported MapUnit (!)"); 2719 break; 2720 } 2721 } 2722 2723 aMapping = getTransformFromMapMode(rMapMode) * aMapping; 2724 rPropertyHolders.Current().setMapUnit(rMapMode.GetMapUnit()); 2725 } 2726 2727 if(!aMapping.isIdentity()) 2728 { 2729 aMapping = aMapping * rPropertyHolders.Current().getTransformation(); 2730 rPropertyHolders.Current().setTransformation(aMapping); 2731 } 2732 2733 break; 2734 } 2735 case META_FONT_ACTION : 2736 { 2737 /** SIMPLE, DONE */ 2738 const MetaFontAction* pA = (const MetaFontAction*)pAction; 2739 rPropertyHolders.Current().setFont(pA->GetFont()); 2740 Size aFontSize(pA->GetFont().GetSize()); 2741 2742 if(0 == aFontSize.Height()) 2743 { 2744 // this should not happen but I got Metafiles where this was the 2745 // case. A height needs to be guessed (similar to OutputDevice::ImplNewFont()) 2746 Font aCorrectedFont(pA->GetFont()); 2747 2748 // guess 16 pixel (as in VCL) 2749 aFontSize = Size(0, 16); 2750 2751 // convert to target MapUnit if not pixels 2752 aFontSize = Application::GetDefaultDevice()->LogicToLogic( 2753 aFontSize, MAP_PIXEL, rPropertyHolders.Current().getMapUnit()); 2754 2755 aCorrectedFont.SetSize(aFontSize); 2756 rPropertyHolders.Current().setFont(aCorrectedFont); 2757 } 2758 2759 // older Metafiles have no META_TEXTCOLOR_ACTION which defines 2760 // the FontColor now, so use the Font's color when not transparent 2761 const Color& rFontColor = pA->GetFont().GetColor(); 2762 const bool bActivate(COL_TRANSPARENT != rFontColor.GetColor()); 2763 2764 if(bActivate) 2765 { 2766 rPropertyHolders.Current().setTextColor(rFontColor.getBColor()); 2767 } 2768 2769 // caution: do NOT deactivate here on transparent, see 2770 // OutputDevice::SetFont(..) for more info 2771 // rPropertyHolders.Current().setTextColorActive(bActivate); 2772 2773 // for fill color emulate a MetaTextFillColorAction with !transparent as bool, 2774 // see OutputDevice::SetFont(..) the if(mpMetaFile) case 2775 if(bActivate) 2776 { 2777 const Color& rFontFillColor = pA->GetFont().GetFillColor(); 2778 rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor()); 2779 rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor.GetColor()); 2780 } 2781 else 2782 { 2783 rPropertyHolders.Current().setTextFillColorActive(false); 2784 } 2785 2786 break; 2787 } 2788 case META_PUSH_ACTION : 2789 { 2790 /** CHECKED, WORKS WELL */ 2791 const MetaPushAction* pA = (const MetaPushAction*)pAction; 2792 rPropertyHolders.Push(pA->GetFlags()); 2793 2794 break; 2795 } 2796 case META_POP_ACTION : 2797 { 2798 /** CHECKED, WORKS WELL */ 2799 const bool bRegionMayChange(rPropertyHolders.Current().getPushFlags() & PUSH_CLIPREGION); 2800 const bool bRasterOpMayChange(rPropertyHolders.Current().getPushFlags() & PUSH_RASTEROP); 2801 2802 if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive()) 2803 { 2804 // end evtl. clipping 2805 const basegfx::B2DPolyPolygon aEmptyPolyPolygon; 2806 2807 HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders); 2808 } 2809 2810 if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive()) 2811 { 2812 // end evtl. RasterOp 2813 HandleNewRasterOp(ROP_OVERPAINT, rTargetHolders, rPropertyHolders); 2814 } 2815 2816 rPropertyHolders.Pop(); 2817 2818 if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive()) 2819 { 2820 // start evtl. RasterOp 2821 HandleNewRasterOp(rPropertyHolders.Current().getRasterOp(), rTargetHolders, rPropertyHolders); 2822 } 2823 2824 if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive()) 2825 { 2826 // start evtl. clipping 2827 HandleNewClipRegion( 2828 rPropertyHolders.Current().getClipPolyPolygon(), rTargetHolders, rPropertyHolders); 2829 } 2830 2831 break; 2832 } 2833 case META_RASTEROP_ACTION : 2834 { 2835 /** CHECKED, WORKS WELL */ 2836 const MetaRasterOpAction* pA = (const MetaRasterOpAction*)pAction; 2837 const RasterOp aRasterOp = pA->GetRasterOp(); 2838 2839 HandleNewRasterOp(aRasterOp, rTargetHolders, rPropertyHolders); 2840 2841 break; 2842 } 2843 case META_TRANSPARENT_ACTION : 2844 { 2845 /** CHECKED, WORKS WELL */ 2846 const MetaTransparentAction* pA = (const MetaTransparentAction*)pAction; 2847 const basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon()); 2848 2849 if(aOutline.count()) 2850 { 2851 const sal_uInt16 nTransparence(pA->GetTransparence()); 2852 2853 if(0 == nTransparence) 2854 { 2855 // not transparent 2856 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current()); 2857 } 2858 else if(nTransparence >= 100) 2859 { 2860 // fully or more than transparent 2861 } 2862 else 2863 { 2864 // transparent. Create new target 2865 rTargetHolders.Push(); 2866 2867 // create primitives there and get them 2868 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current()); 2869 const drawinglayer::primitive2d::Primitive2DSequence aSubContent( 2870 rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current())); 2871 2872 // back to old target 2873 rTargetHolders.Pop(); 2874 2875 if(aSubContent.hasElements()) 2876 { 2877 rTargetHolders.Current().append( 2878 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 2879 aSubContent, 2880 nTransparence * 0.01)); 2881 } 2882 } 2883 } 2884 2885 break; 2886 } 2887 case META_EPS_ACTION : 2888 { 2889 /** CHECKED, WORKS WELL */ 2890 // To support this action, i have added a EpsPrimitive2D which will 2891 // by default decompose to the Metafile replacement data. To support 2892 // this EPS on screen, the renderer visualizing this has to support 2893 // that primitive and visualize the Eps file (e.g. printing) 2894 const MetaEPSAction* pA = (const MetaEPSAction*)pAction; 2895 const Rectangle aRectangle(pA->GetPoint(), pA->GetSize()); 2896 2897 if(!aRectangle.IsEmpty()) 2898 { 2899 // create object transform 2900 basegfx::B2DHomMatrix aObjectTransform; 2901 2902 aObjectTransform.set(0, 0, aRectangle.GetWidth()); 2903 aObjectTransform.set(1, 1, aRectangle.GetHeight()); 2904 aObjectTransform.set(0, 2, aRectangle.Left()); 2905 aObjectTransform.set(1, 2, aRectangle.Top()); 2906 2907 // add current transformation 2908 aObjectTransform = rPropertyHolders.Current().getTransformation() * aObjectTransform; 2909 2910 // embed using EpsPrimitive 2911 rTargetHolders.Current().append( 2912 new drawinglayer::primitive2d::EpsPrimitive2D( 2913 aObjectTransform, 2914 pA->GetLink(), 2915 pA->GetSubstitute())); 2916 } 2917 2918 break; 2919 } 2920 case META_REFPOINT_ACTION : 2921 { 2922 /** SIMPLE, DONE */ 2923 // only used for hatch and line pattern offsets, pretty much no longer 2924 // supported today 2925 // const MetaRefPointAction* pA = (const MetaRefPointAction*)pAction; 2926 break; 2927 } 2928 case META_TEXTLINECOLOR_ACTION : 2929 { 2930 /** SIMPLE, DONE */ 2931 const MetaTextLineColorAction* pA = (const MetaTextLineColorAction*)pAction; 2932 const bool bActive(pA->IsSetting()); 2933 2934 rPropertyHolders.Current().setTextLineColorActive(bActive); 2935 if(bActive) 2936 rPropertyHolders.Current().setTextLineColor(pA->GetColor().getBColor()); 2937 2938 break; 2939 } 2940 case META_TEXTLINE_ACTION : 2941 { 2942 /** CHECKED, WORKS WELL */ 2943 // actually creates overline, underline and strikeouts, so 2944 // these should be isolated from TextDecoratedPortionPrimitive2D 2945 // to own primitives. Done, available now. 2946 // 2947 // This Metaaction seems not to be used (was not used in any 2948 // checked files). It's used in combination with the current 2949 // Font. 2950 const MetaTextLineAction* pA = (const MetaTextLineAction*)pAction; 2951 2952 proccessMetaTextLineAction( 2953 *pA, 2954 rTargetHolders.Current(), 2955 rPropertyHolders.Current()); 2956 2957 break; 2958 } 2959 case META_FLOATTRANSPARENT_ACTION : 2960 { 2961 /** CHECKED, WORKS WELL */ 2962 const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*)pAction; 2963 const basegfx::B2DRange aTargetRange( 2964 pA->GetPoint().X(), 2965 pA->GetPoint().Y(), 2966 pA->GetPoint().X() + pA->GetSize().Width(), 2967 pA->GetPoint().Y() + pA->GetSize().Height()); 2968 2969 if(!aTargetRange.isEmpty()) 2970 { 2971 const GDIMetaFile& rContent = pA->GetGDIMetaFile(); 2972 2973 if(rContent.GetActionCount()) 2974 { 2975 // create the sub-content with no embedding specific to the 2976 // sub-metafile, this seems not to be used. 2977 drawinglayer::primitive2d::Primitive2DSequence xSubContent; 2978 { 2979 rTargetHolders.Push(); 2980 // #i# for sub-Metafile contents, do start with new, default render state 2981 rPropertyHolders.PushDefault(); 2982 interpretMetafile(rContent, rTargetHolders, rPropertyHolders, rViewInformation); 2983 xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current()); 2984 rPropertyHolders.Pop(); 2985 rTargetHolders.Pop(); 2986 } 2987 2988 if(xSubContent.hasElements()) 2989 { 2990 // prepare sub-content transform 2991 basegfx::B2DHomMatrix aSubTransform; 2992 2993 // create SourceRange 2994 const basegfx::B2DRange aSourceRange( 2995 rContent.GetPrefMapMode().GetOrigin().X(), 2996 rContent.GetPrefMapMode().GetOrigin().Y(), 2997 rContent.GetPrefMapMode().GetOrigin().X() + rContent.GetPrefSize().Width(), 2998 rContent.GetPrefMapMode().GetOrigin().Y() + rContent.GetPrefSize().Height()); 2999 3000 // apply mapping if aTargetRange and aSourceRange are not equal 3001 if(!aSourceRange.equal(aTargetRange)) 3002 { 3003 aSubTransform.translate(-aSourceRange.getMinX(), -aSourceRange.getMinY()); 3004 aSubTransform.scale( 3005 aTargetRange.getWidth() / (basegfx::fTools::equalZero(aSourceRange.getWidth()) ? 1.0 : aSourceRange.getWidth()), 3006 aTargetRange.getHeight() / (basegfx::fTools::equalZero(aSourceRange.getHeight()) ? 1.0 : aSourceRange.getHeight())); 3007 aSubTransform.translate(aTargetRange.getMinX(), aTargetRange.getMinY()); 3008 } 3009 3010 // apply general current transformation 3011 aSubTransform = rPropertyHolders.Current().getTransformation() * aSubTransform; 3012 3013 // evtl. embed sub-content to its transformation 3014 if(!aSubTransform.isIdentity()) 3015 { 3016 const drawinglayer::primitive2d::Primitive2DReference aEmbeddedTransform( 3017 new drawinglayer::primitive2d::TransformPrimitive2D( 3018 aSubTransform, 3019 xSubContent)); 3020 3021 xSubContent = drawinglayer::primitive2d::Primitive2DSequence(&aEmbeddedTransform, 1); 3022 } 3023 3024 // check if gradient is a real gradient 3025 const Gradient& rGradient = pA->GetGradient(); 3026 const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); 3027 3028 if(aAttribute.getStartColor() == aAttribute.getEndColor()) 3029 { 3030 // not really a gradient; create UnifiedTransparencePrimitive2D 3031 rTargetHolders.Current().append( 3032 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 3033 xSubContent, 3034 aAttribute.getStartColor().luminance())); 3035 } 3036 else 3037 { 3038 // really a gradient. Create gradient sub-content (with correct scaling) 3039 basegfx::B2DRange aRange(aTargetRange); 3040 aRange.transform(rPropertyHolders.Current().getTransformation()); 3041 3042 // prepare gradient for transparent content 3043 const drawinglayer::primitive2d::Primitive2DReference xTransparence( 3044 new drawinglayer::primitive2d::FillGradientPrimitive2D( 3045 aRange, 3046 aAttribute)); 3047 3048 // create transparent primitive 3049 rTargetHolders.Current().append( 3050 new drawinglayer::primitive2d::TransparencePrimitive2D( 3051 xSubContent, 3052 drawinglayer::primitive2d::Primitive2DSequence(&xTransparence, 1))); 3053 } 3054 } 3055 } 3056 } 3057 3058 break; 3059 } 3060 case META_GRADIENTEX_ACTION : 3061 { 3062 /** SIMPLE, DONE */ 3063 // This is only a data holder which is interpreted inside comment actions, 3064 // see META_COMMENT_ACTION for more info 3065 // const MetaGradientExAction* pA = (const MetaGradientExAction*)pAction; 3066 break; 3067 } 3068 case META_LAYOUTMODE_ACTION : 3069 { 3070 /** SIMPLE, DONE */ 3071 const MetaLayoutModeAction* pA = (const MetaLayoutModeAction*)pAction; 3072 rPropertyHolders.Current().setLayoutMode(pA->GetLayoutMode()); 3073 break; 3074 } 3075 case META_TEXTLANGUAGE_ACTION : 3076 { 3077 /** SIMPLE, DONE */ 3078 const MetaTextLanguageAction* pA = (const MetaTextLanguageAction*)pAction; 3079 rPropertyHolders.Current().setLanguageType(pA->GetTextLanguage()); 3080 break; 3081 } 3082 case META_OVERLINECOLOR_ACTION : 3083 { 3084 /** SIMPLE, DONE */ 3085 const MetaOverlineColorAction* pA = (const MetaOverlineColorAction*)pAction; 3086 const bool bActive(pA->IsSetting()); 3087 3088 rPropertyHolders.Current().setOverlineColorActive(bActive); 3089 if(bActive) 3090 rPropertyHolders.Current().setOverlineColor(pA->GetColor().getBColor()); 3091 3092 break; 3093 } 3094 case META_COMMENT_ACTION : 3095 { 3096 /** CHECKED, WORKS WELL */ 3097 // I already implemented 3098 // XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END 3099 // XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END, 3100 // but opted to remove these again; it works well without them 3101 // and makes the code less dependent from those Metafile Add-Ons 3102 const MetaCommentAction* pA = (const MetaCommentAction*)pAction; 3103 3104 if(COMPARE_EQUAL == pA->GetComment().CompareIgnoreCaseToAscii("XGRAD_SEQ_BEGIN")) 3105 { 3106 // XGRAD_SEQ_BEGIN, XGRAD_SEQ_END should be supported since the 3107 // pure recorded paint of the gradients uses the XOR paint functionality 3108 // ('trick'). This is (and will be) problematic with AntAliasing, so it's 3109 // better to use this info 3110 const MetaGradientExAction* pMetaGradientExAction = 0; 3111 bool bDone(false); 3112 sal_uInt32 b(nAction + 1); 3113 3114 for(; !bDone && b < nCount; b++) 3115 { 3116 pAction = rMetaFile.GetAction(b); 3117 3118 if(META_GRADIENTEX_ACTION == pAction->GetType()) 3119 { 3120 pMetaGradientExAction = (const MetaGradientExAction*)pAction; 3121 } 3122 else if(META_COMMENT_ACTION == pAction->GetType()) 3123 { 3124 if(COMPARE_EQUAL == ((const MetaCommentAction*)pAction)->GetComment().CompareIgnoreCaseToAscii("XGRAD_SEQ_END")) 3125 { 3126 bDone = true; 3127 } 3128 } 3129 } 3130 3131 if(bDone && pMetaGradientExAction) 3132 { 3133 // consume actions and skip forward 3134 nAction = b - 1; 3135 3136 // get geometry data 3137 basegfx::B2DPolyPolygon aPolyPolygon(pMetaGradientExAction->GetPolyPolygon().getB2DPolyPolygon()); 3138 3139 if(aPolyPolygon.count()) 3140 { 3141 // transform geometry 3142 aPolyPolygon.transform(rPropertyHolders.Current().getTransformation()); 3143 3144 // get and check if gradient is a real gradient 3145 const Gradient& rGradient = pMetaGradientExAction->GetGradient(); 3146 const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); 3147 3148 if(aAttribute.getStartColor() == aAttribute.getEndColor()) 3149 { 3150 // not really a gradient 3151 rTargetHolders.Current().append( 3152 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( 3153 aPolyPolygon, 3154 aAttribute.getStartColor())); 3155 } 3156 else 3157 { 3158 // really a gradient 3159 rTargetHolders.Current().append( 3160 new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D( 3161 aPolyPolygon, 3162 aAttribute)); 3163 } 3164 } 3165 } 3166 } 3167 3168 break; 3169 } 3170 default: 3171 { 3172 OSL_ENSURE(false, "Unknown MetaFile Action (!)"); 3173 break; 3174 } 3175 } 3176 } 3177 } 3178 } // end of anonymous namespace 3179 3180 ////////////////////////////////////////////////////////////////////////////// 3181 3182 namespace drawinglayer 3183 { 3184 namespace primitive2d 3185 { create2DDecomposition(const geometry::ViewInformation2D & rViewInformation) const3186 Primitive2DSequence MetafilePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const 3187 { 3188 // prepare target and properties; each will have one default entry 3189 TargetHolders aTargetHolders; 3190 PropertyHolders aPropertyHolders; 3191 3192 // set target MapUnit at Properties 3193 aPropertyHolders.Current().setMapUnit(getMetaFile().GetPrefMapMode().GetMapUnit()); 3194 3195 // interpret the Metafile 3196 interpretMetafile(getMetaFile(), aTargetHolders, aPropertyHolders, rViewInformation); 3197 3198 // get the content. There should be only one target, as in the start condition, 3199 // but iterating will be the right thing to do when some push/pop is not closed 3200 Primitive2DSequence xRetval; 3201 3202 while(aTargetHolders.size() > 1) 3203 { 3204 appendPrimitive2DSequenceToPrimitive2DSequence(xRetval, 3205 aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current())); 3206 aTargetHolders.Pop(); 3207 } 3208 3209 appendPrimitive2DSequenceToPrimitive2DSequence(xRetval, 3210 aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current())); 3211 3212 if(xRetval.hasElements()) 3213 { 3214 // get target size 3215 const Rectangle aMtfTarget(getMetaFile().GetPrefMapMode().GetOrigin(), getMetaFile().GetPrefSize()); 3216 3217 // create transformation 3218 basegfx::B2DHomMatrix aAdaptedTransform; 3219 3220 aAdaptedTransform.translate(-aMtfTarget.Left(), -aMtfTarget.Top()); 3221 aAdaptedTransform.scale( 3222 aMtfTarget.getWidth() ? 1.0 / aMtfTarget.getWidth() : 1.0, 3223 aMtfTarget.getHeight() ? 1.0 / aMtfTarget.getHeight() : 1.0); 3224 aAdaptedTransform = getTransform() * aAdaptedTransform; 3225 3226 // embed to target transformation 3227 const Primitive2DReference aEmbeddedTransform( 3228 new TransformPrimitive2D( 3229 aAdaptedTransform, 3230 xRetval)); 3231 3232 xRetval = Primitive2DSequence(&aEmbeddedTransform, 1); 3233 } 3234 3235 return xRetval; 3236 } 3237 MetafilePrimitive2D(const basegfx::B2DHomMatrix & rMetaFileTransform,const GDIMetaFile & rMetaFile)3238 MetafilePrimitive2D::MetafilePrimitive2D( 3239 const basegfx::B2DHomMatrix& rMetaFileTransform, 3240 const GDIMetaFile& rMetaFile) 3241 : BufferedDecompositionPrimitive2D(), 3242 maMetaFileTransform(rMetaFileTransform), 3243 maMetaFile(rMetaFile) 3244 { 3245 } 3246 operator ==(const BasePrimitive2D & rPrimitive) const3247 bool MetafilePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 3248 { 3249 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) 3250 { 3251 const MetafilePrimitive2D& rCompare = (MetafilePrimitive2D&)rPrimitive; 3252 3253 return (getTransform() == rCompare.getTransform() 3254 && getMetaFile() == rCompare.getMetaFile()); 3255 } 3256 3257 return false; 3258 } 3259 getB2DRange(const geometry::ViewInformation2D &) const3260 basegfx::B2DRange MetafilePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const 3261 { 3262 // use own implementation to quickly answer the getB2DRange question. The 3263 // MetafilePrimitive2D assumes that all geometry is inside of the shape. If 3264 // this is not the case (i have already seen some wrong Metafiles) it should 3265 // be embedded to a MaskPrimitive2D 3266 basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0); 3267 aRetval.transform(getTransform()); 3268 3269 return aRetval; 3270 } 3271 3272 // provide unique ID 3273 ImplPrimitrive2DIDBlock(MetafilePrimitive2D, PRIMITIVE2D_ID_METAFILEPRIMITIVE2D) 3274 3275 } // end of namespace primitive2d 3276 } // end of namespace drawinglayer 3277 3278 ////////////////////////////////////////////////////////////////////////////// 3279 // eof 3280