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