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