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