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 // MARKER(update_precomp.py): autogen include statement, do not remove 23 #include "precompiled_vcl.hxx" 24 25 #include <vcl/gdimetafiletools.hxx> 26 #include <vcl/metaact.hxx> 27 #include <basegfx/polygon/b2dpolygonclipper.hxx> 28 #include <basegfx/matrix/b2dhommatrixtools.hxx> 29 #include <basegfx/polygon/b2dpolypolygontools.hxx> 30 #include <basegfx/polygon/b2dpolygontools.hxx> 31 #include <vcl/virdev.hxx> 32 #include <vcl/svapp.hxx> 33 #include <vcl/graphictools.hxx> 34 35 ////////////////////////////////////////////////////////////////////////////// 36 // helpers 37 38 namespace 39 { 40 bool handleGeometricContent( 41 const basegfx::B2DPolyPolygon& rClip, 42 const basegfx::B2DPolyPolygon& rSource, 43 GDIMetaFile& rTarget, 44 bool bStroke) 45 { 46 if(rSource.count() && rClip.count()) 47 { 48 const basegfx::B2DPolyPolygon aResult( 49 basegfx::tools::clipPolyPolygonOnPolyPolygon( 50 rSource, 51 rClip, 52 true, // inside 53 bStroke)); 54 55 if(aResult.count()) 56 { 57 if(aResult == rSource) 58 { 59 // not clipped, but inside. Add original 60 return false; 61 } 62 else 63 { 64 // add clipped geometry 65 if(bStroke) 66 { 67 for(sal_uInt32 a(0); a < aResult.count(); a++) 68 { 69 rTarget.AddAction( 70 new MetaPolyLineAction( 71 Polygon(aResult.getB2DPolygon(a)))); 72 } 73 } 74 else 75 { 76 rTarget.AddAction( 77 new MetaPolyPolygonAction( 78 PolyPolygon(aResult))); 79 } 80 } 81 } 82 } 83 84 return true; 85 } 86 87 bool handleGradientContent( 88 const basegfx::B2DPolyPolygon& rClip, 89 const basegfx::B2DPolyPolygon& rSource, 90 const Gradient& rGradient, 91 GDIMetaFile& rTarget) 92 { 93 if(rSource.count() && rClip.count()) 94 { 95 const basegfx::B2DPolyPolygon aResult( 96 basegfx::tools::clipPolyPolygonOnPolyPolygon( 97 rSource, 98 rClip, 99 true, // inside 100 false)); // stroke 101 102 if(aResult.count()) 103 { 104 if(aResult == rSource) 105 { 106 // not clipped, but inside. Add original 107 return false; 108 } 109 else 110 { 111 // add clipped geometry 112 rTarget.AddAction( 113 new MetaGradientExAction( 114 PolyPolygon(aResult), 115 rGradient)); 116 } 117 } 118 } 119 120 return true; 121 } 122 123 bool handleBitmapContent( 124 const basegfx::B2DPolyPolygon& rClip, 125 const Point& rPoint, 126 const Size& rSize, 127 const BitmapEx& rBitmapEx, 128 GDIMetaFile& rTarget) 129 { 130 if(!rSize.Width() || !rSize.Height() || rBitmapEx.IsEmpty()) 131 { 132 // bitmap or size is empty 133 return true; 134 } 135 136 const basegfx::B2DRange aLogicBitmapRange( 137 rPoint.X(), rPoint.Y(), 138 rPoint.X() + rSize.Width(), rPoint.Y() + rSize.Height()); 139 const basegfx::B2DPolyPolygon aClipOfBitmap( 140 basegfx::tools::clipPolyPolygonOnRange( 141 rClip, 142 aLogicBitmapRange, 143 true, 144 false)); // stroke 145 146 if(!aClipOfBitmap.count()) 147 { 148 // outside clip region 149 return true; 150 } 151 152 // inside or overlapping. Use area to find out if it is completely 153 // covering (inside) or overlapping 154 const double fClipArea(basegfx::tools::getArea(aClipOfBitmap)); 155 const double fBitmapArea( 156 aLogicBitmapRange.getWidth() * aLogicBitmapRange.getWidth() + 157 aLogicBitmapRange.getHeight() * aLogicBitmapRange.getHeight()); 158 const double fFactor(fClipArea / fBitmapArea); 159 160 if(basegfx::fTools::more(fFactor, 1.0 - 0.001)) 161 { 162 // completely covering (with 0.1% tolerance) 163 return false; 164 } 165 166 // needs clipping (with 0.1% tolerance). Prepare VirtualDevice 167 // in pixel mode for alpha channel painting (black is transparent, 168 // white to paint 100% opacity) 169 const Size aSizePixel(rBitmapEx.GetSizePixel()); 170 VirtualDevice aVDev; 171 172 aVDev.SetOutputSizePixel(aSizePixel); 173 aVDev.EnableMapMode(false); 174 aVDev.SetFillColor(COL_WHITE); 175 aVDev.SetLineColor(); 176 177 if(rBitmapEx.IsTransparent()) 178 { 179 // use given alpha channel 180 aVDev.DrawBitmap(Point(0, 0), rBitmapEx.GetAlpha().GetBitmap()); 181 } 182 else 183 { 184 // reset alpha channel 185 aVDev.SetBackground(Wallpaper(Color(COL_BLACK))); 186 aVDev.Erase(); 187 } 188 189 // transform polygon from clipping to pixel coordinates 190 basegfx::B2DPolyPolygon aPixelPoly(aClipOfBitmap); 191 basegfx::B2DHomMatrix aTransform; 192 193 aTransform.translate(-aLogicBitmapRange.getMinX(), -aLogicBitmapRange.getMinY()); 194 aTransform.scale( 195 static_cast< double >(aSizePixel.Width()) / aLogicBitmapRange.getWidth(), 196 static_cast< double >(aSizePixel.Height()) / aLogicBitmapRange.getHeight()); 197 aPixelPoly.transform(aTransform); 198 199 // to fill the non-covered parts, use the Xor fill rule of 200 // PolyPolygon painting. Start with a all-covering polygon and 201 // add the clip polygon one 202 basegfx::B2DPolyPolygon aInvertPixelPoly; 203 204 aInvertPixelPoly.append( 205 basegfx::tools::createPolygonFromRect( 206 basegfx::B2DRange( 207 0.0, 0.0, 208 aSizePixel.Width(), aSizePixel.Height()))); 209 aInvertPixelPoly.append(aPixelPoly); 210 211 // paint as alpha 212 aVDev.DrawPolyPolygon(aInvertPixelPoly); 213 214 // get created alpha mask and set defaults 215 AlphaMask aAlpha( 216 aVDev.GetBitmap( 217 Point(0, 0), 218 aSizePixel)); 219 220 aAlpha.SetPrefSize(rBitmapEx.GetPrefSize()); 221 aAlpha.SetPrefMapMode(rBitmapEx.GetPrefMapMode()); 222 223 // add new action replacing the old one 224 rTarget.AddAction( 225 new MetaBmpExScaleAction( 226 Point( 227 basegfx::fround(aLogicBitmapRange.getMinX()), 228 basegfx::fround(aLogicBitmapRange.getMinY())), 229 Size( 230 basegfx::fround(aLogicBitmapRange.getWidth()), 231 basegfx::fround(aLogicBitmapRange.getHeight())), 232 BitmapEx(rBitmapEx.GetBitmap(), aAlpha))); 233 234 return true; 235 } 236 237 void addSvtGraphicStroke(const SvtGraphicStroke& rStroke, GDIMetaFile& rTarget) 238 { 239 // write SvtGraphicFill 240 SvMemoryStream aMemStm; 241 aMemStm << rStroke; 242 rTarget.AddAction( 243 new MetaCommentAction( 244 "XPATHSTROKE_SEQ_BEGIN", 245 0, 246 static_cast< const sal_uInt8* >(aMemStm.GetData()), 247 aMemStm.Seek(STREAM_SEEK_TO_END))); 248 } 249 250 void addSvtGraphicFill(const SvtGraphicFill &rFilling, GDIMetaFile& rTarget) 251 { 252 // write SvtGraphicFill 253 SvMemoryStream aMemStm; 254 aMemStm << rFilling; 255 rTarget.AddAction( 256 new MetaCommentAction( 257 "XPATHFILL_SEQ_BEGIN", 258 0, 259 static_cast< const sal_uInt8* >(aMemStm.GetData()), 260 aMemStm.Seek(STREAM_SEEK_TO_END))); 261 } 262 } // end of anonymous namespace 263 264 ////////////////////////////////////////////////////////////////////////////// 265 // #121267# Tooling to internally clip geometry against internal clip regions 266 267 void clipMetafileContentAgainstOwnRegions(GDIMetaFile& rSource) 268 { 269 const sal_uLong nObjCount(rSource.GetActionCount()); 270 271 if(!nObjCount) 272 { 273 return; 274 } 275 276 // prepare target data container and push/pop stack data 277 GDIMetaFile aTarget; 278 bool bChanged(false); 279 std::vector< basegfx::B2DPolyPolygon > aClips; 280 std::vector< sal_uInt16 > aPushFlags; 281 std::vector< MapMode > aMapModes; 282 283 // start with empty region 284 aClips.push_back(basegfx::B2DPolyPolygon()); 285 286 // start with default MapMode (MAP_PIXEL) 287 aMapModes.push_back(MapMode()); 288 289 for(sal_uLong i(0); i < nObjCount; ++i) 290 { 291 const MetaAction* pAction(rSource.GetAction(i)); 292 const sal_uInt16 nType(pAction->GetType()); 293 bool bDone(false); 294 295 // basic operation takes care of clipregion actions (four) and push/pop of these 296 // to steer the currently set clip region. There *is* an active 297 // clip region when (aClips.size() && aClips.back().count()), see 298 // below 299 switch(nType) 300 { 301 case META_CLIPREGION_ACTION : 302 { 303 const MetaClipRegionAction* pA = static_cast< const MetaClipRegionAction* >(pAction); 304 305 if(pA->IsClipping()) 306 { 307 const Region& rRegion = pA->GetRegion(); 308 const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon()); 309 310 aClips.back() = aNewClip; 311 } 312 else 313 { 314 aClips.back() = basegfx::B2DPolyPolygon(); 315 } 316 317 break; 318 } 319 320 case META_ISECTRECTCLIPREGION_ACTION : 321 { 322 const MetaISectRectClipRegionAction* pA = static_cast< const MetaISectRectClipRegionAction* >(pAction); 323 const Rectangle& rRect = pA->GetRect(); 324 325 if(!rRect.IsEmpty() && aClips.size() && aClips.back().count()) 326 { 327 const basegfx::B2DRange aClipRange( 328 rRect.Left(), rRect.Top(), 329 rRect.Right(), rRect.Bottom()); 330 331 aClips.back() = basegfx::tools::clipPolyPolygonOnRange( 332 aClips.back(), 333 aClipRange, 334 true, // inside 335 false); // stroke 336 } 337 break; 338 } 339 340 case META_ISECTREGIONCLIPREGION_ACTION : 341 { 342 const MetaISectRegionClipRegionAction* pA = static_cast< const MetaISectRegionClipRegionAction* >(pAction); 343 const Region& rRegion = pA->GetRegion(); 344 345 if(!rRegion.IsEmpty() && aClips.size() && aClips.back().count()) 346 { 347 const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon()); 348 349 aClips.back() = basegfx::tools::clipPolyPolygonOnPolyPolygon( 350 aClips.back(), 351 aNewClip, 352 true, // inside 353 false); // stroke 354 } 355 break; 356 } 357 358 case META_MOVECLIPREGION_ACTION : 359 { 360 const MetaMoveClipRegionAction* pA = static_cast< const MetaMoveClipRegionAction* >(pAction); 361 const long aHorMove(pA->GetHorzMove()); 362 const long aVerMove(pA->GetVertMove()); 363 364 if((aHorMove || aVerMove) && aClips.size() && aClips.back().count()) 365 { 366 aClips.back().transform( 367 basegfx::tools::createTranslateB2DHomMatrix( 368 aHorMove, 369 aVerMove)); 370 } 371 break; 372 } 373 374 case META_PUSH_ACTION : 375 { 376 const MetaPushAction* pA = static_cast< const MetaPushAction* >(pAction); 377 const sal_uInt16 nFlags(pA->GetFlags()); 378 379 aPushFlags.push_back(nFlags); 380 381 if(nFlags & PUSH_CLIPREGION) 382 { 383 aClips.push_back(aClips.back()); 384 } 385 386 if(nFlags & PUSH_MAPMODE) 387 { 388 aMapModes.push_back(aMapModes.back()); 389 } 390 break; 391 } 392 393 case META_POP_ACTION : 394 { 395 const MetaPopAction* pA = static_cast< const MetaPopAction* >(pAction); 396 397 if(aPushFlags.size()) 398 { 399 const sal_uInt16 nFlags(aPushFlags.back()); 400 aPushFlags.pop_back(); 401 402 if(nFlags & PUSH_CLIPREGION) 403 { 404 if(aClips.size() > 1) 405 { 406 aClips.pop_back(); 407 } 408 else 409 { 410 OSL_ENSURE(false, "Wrong POP() in ClipRegions (!)"); 411 } 412 } 413 414 if(nFlags & PUSH_MAPMODE) 415 { 416 if(aMapModes.size() > 1) 417 { 418 aMapModes.pop_back(); 419 } 420 else 421 { 422 OSL_ENSURE(false, "Wrong POP() in MapModes (!)"); 423 } 424 } 425 } 426 else 427 { 428 OSL_ENSURE(false, "Invalid pop() without push() (!)"); 429 } 430 431 break; 432 } 433 434 case META_MAPMODE_ACTION : 435 { 436 const MetaMapModeAction* pA = static_cast< const MetaMapModeAction* >(pAction); 437 438 aMapModes.back() = pA->GetMapMode(); 439 break; 440 } 441 442 default: 443 { 444 break; 445 } 446 } 447 448 // this area contains all actions which could potentially be clipped. Since 449 // this tooling is only a fallback (see comments in header), only the needed 450 // actions will be implemented. Extend using the pattern for the already 451 // implemented actions. 452 if(aClips.size() && aClips.back().count()) 453 { 454 switch(nType) 455 { 456 // 457 // pixel actions, just check on inside 458 // 459 case META_PIXEL_ACTION : 460 { 461 const MetaPixelAction* pA = static_cast< const MetaPixelAction* >(pAction); 462 const Point& rPoint = pA->GetPoint(); 463 464 if(!basegfx::tools::isInside( 465 aClips.back(), 466 basegfx::B2DPoint(rPoint.X(), rPoint.Y()))) 467 { 468 // when not inside, do not add original 469 bDone = true; 470 } 471 break; 472 } 473 474 case META_POINT_ACTION : 475 { 476 const MetaPointAction* pA = static_cast< const MetaPointAction* >(pAction); 477 const Point& rPoint = pA->GetPoint(); 478 479 if(!basegfx::tools::isInside( 480 aClips.back(), 481 basegfx::B2DPoint(rPoint.X(), rPoint.Y()))) 482 { 483 // when not inside, do not add original 484 bDone = true; 485 } 486 break; 487 } 488 489 // 490 // geometry actions 491 // 492 case META_LINE_ACTION : 493 { 494 const MetaLineAction* pA = static_cast< const MetaLineAction* >(pAction); 495 const Point& rStart(pA->GetStartPoint()); 496 const Point& rEnd(pA->GetEndPoint()); 497 basegfx::B2DPolygon aLine; 498 499 aLine.append(basegfx::B2DPoint(rStart.X(), rStart.Y())); 500 aLine.append(basegfx::B2DPoint(rEnd.X(), rEnd.Y())); 501 502 bDone = handleGeometricContent( 503 aClips.back(), 504 basegfx::B2DPolyPolygon(aLine), 505 aTarget, 506 true); // stroke 507 break; 508 } 509 510 case META_RECT_ACTION : 511 { 512 const MetaRectAction* pA = static_cast< const MetaRectAction* >(pAction); 513 const Rectangle& rRect = pA->GetRect(); 514 515 if(rRect.IsEmpty()) 516 { 517 bDone = true; 518 } 519 else 520 { 521 522 bDone = handleGeometricContent( 523 aClips.back(), 524 basegfx::B2DPolyPolygon( 525 basegfx::tools::createPolygonFromRect( 526 basegfx::B2DRange( 527 rRect.Left(), rRect.Top(), 528 rRect.Right(), rRect.Bottom()))), 529 aTarget, 530 false); // stroke 531 } 532 break; 533 } 534 535 case META_ROUNDRECT_ACTION : 536 { 537 const MetaRoundRectAction* pA = static_cast< const MetaRoundRectAction* >(pAction); 538 const Rectangle& rRect = pA->GetRect(); 539 540 if(rRect.IsEmpty()) 541 { 542 bDone = true; 543 } 544 else 545 { 546 const sal_uInt32 nHor(pA->GetHorzRound()); 547 const sal_uInt32 nVer(pA->GetVertRound()); 548 const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()); 549 basegfx::B2DPolygon aOutline; 550 551 if(nHor || nVer) 552 { 553 double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0)); 554 double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0)); 555 fRadiusX = std::max(0.0, std::min(1.0, fRadiusX)); 556 fRadiusY = std::max(0.0, std::min(1.0, fRadiusY)); 557 558 aOutline = basegfx::tools::createPolygonFromRect(aRange, fRadiusX, fRadiusY); 559 } 560 else 561 { 562 aOutline = basegfx::tools::createPolygonFromRect(aRange); 563 } 564 565 bDone = handleGeometricContent( 566 aClips.back(), 567 basegfx::B2DPolyPolygon(aOutline), 568 aTarget, 569 false); // stroke 570 } 571 break; 572 } 573 574 case META_ELLIPSE_ACTION : 575 { 576 const MetaEllipseAction* pA = static_cast< const MetaEllipseAction* >(pAction); 577 const Rectangle& rRect = pA->GetRect(); 578 579 if(rRect.IsEmpty()) 580 { 581 bDone = true; 582 } 583 else 584 { 585 const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()); 586 587 bDone = handleGeometricContent( 588 aClips.back(), 589 basegfx::B2DPolyPolygon( 590 basegfx::tools::createPolygonFromEllipse( 591 aRange.getCenter(), 592 aRange.getWidth() * 0.5, 593 aRange.getHeight() * 0.5)), 594 aTarget, 595 false); // stroke 596 } 597 break; 598 } 599 600 case META_ARC_ACTION : 601 { 602 const MetaArcAction* pA = static_cast< const MetaArcAction* >(pAction); 603 const Rectangle& rRect = pA->GetRect(); 604 605 if(rRect.IsEmpty()) 606 { 607 bDone = true; 608 } 609 else 610 { 611 const Polygon aToolsPoly( 612 rRect, 613 pA->GetStartPoint(), 614 pA->GetEndPoint(), 615 POLY_ARC); 616 617 bDone = handleGeometricContent( 618 aClips.back(), 619 basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()), 620 aTarget, 621 true); // stroke 622 } 623 break; 624 } 625 626 case META_PIE_ACTION : 627 { 628 const MetaPieAction* pA = static_cast< const MetaPieAction* >(pAction); 629 const Rectangle& rRect = pA->GetRect(); 630 631 if(rRect.IsEmpty()) 632 { 633 bDone = true; 634 } 635 else 636 { 637 const Polygon aToolsPoly( 638 rRect, 639 pA->GetStartPoint(), 640 pA->GetEndPoint(), 641 POLY_PIE); 642 643 bDone = handleGeometricContent( 644 aClips.back(), 645 basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()), 646 aTarget, 647 false); // stroke 648 } 649 break; 650 } 651 652 case META_CHORD_ACTION : 653 { 654 const MetaChordAction* pA = static_cast< const MetaChordAction* >(pAction); 655 const Rectangle& rRect = pA->GetRect(); 656 657 if(rRect.IsEmpty()) 658 { 659 bDone = true; 660 } 661 else 662 { 663 const Polygon aToolsPoly( 664 rRect, 665 pA->GetStartPoint(), 666 pA->GetEndPoint(), 667 POLY_CHORD); 668 669 bDone = handleGeometricContent( 670 aClips.back(), 671 basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()), 672 aTarget, 673 false); // stroke 674 } 675 break; 676 } 677 678 case META_POLYLINE_ACTION : 679 { 680 const MetaPolyLineAction* pA = static_cast< const MetaPolyLineAction* >(pAction); 681 682 bDone = handleGeometricContent( 683 aClips.back(), 684 basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()), 685 aTarget, 686 true); // stroke 687 break; 688 } 689 690 case META_POLYGON_ACTION : 691 { 692 const MetaPolygonAction* pA = static_cast< const MetaPolygonAction* >(pAction); 693 694 bDone = handleGeometricContent( 695 aClips.back(), 696 basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()), 697 aTarget, 698 false); // stroke 699 break; 700 } 701 702 case META_POLYPOLYGON_ACTION : 703 { 704 const MetaPolyPolygonAction* pA = static_cast< const MetaPolyPolygonAction* >(pAction); 705 const PolyPolygon& rPoly = pA->GetPolyPolygon(); 706 707 bDone = handleGeometricContent( 708 aClips.back(), 709 rPoly.getB2DPolyPolygon(), 710 aTarget, 711 false); // stroke 712 break; 713 } 714 715 // 716 // bitmap actions, create BitmapEx with alpha channel derived 717 // from clipping 718 // 719 case META_BMPEX_ACTION : 720 { 721 const MetaBmpExAction* pA = static_cast< const MetaBmpExAction* >(pAction); 722 const BitmapEx& rBitmapEx = pA->GetBitmapEx(); 723 724 // the logical size depends on the PrefSize of the given bitmap in 725 // combination with the current MapMode 726 Size aLogicalSize(rBitmapEx.GetPrefSize()); 727 728 if(MAP_PIXEL == rBitmapEx.GetPrefMapMode().GetMapUnit()) 729 { 730 aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit()); 731 } 732 else 733 { 734 aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmapEx.GetPrefMapMode(), aMapModes.back().GetMapUnit()); 735 } 736 737 bDone = handleBitmapContent( 738 aClips.back(), 739 pA->GetPoint(), 740 aLogicalSize, 741 rBitmapEx, 742 aTarget); 743 break; 744 } 745 746 case META_BMP_ACTION : 747 { 748 const MetaBmpAction* pA = static_cast< const MetaBmpAction* >(pAction); 749 const Bitmap& rBitmap = pA->GetBitmap(); 750 751 // the logical size depends on the PrefSize of the given bitmap in 752 // combination with the current MapMode 753 Size aLogicalSize(rBitmap.GetPrefSize()); 754 755 if(MAP_PIXEL == rBitmap.GetPrefMapMode().GetMapUnit()) 756 { 757 aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit()); 758 } 759 else 760 { 761 aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmap.GetPrefMapMode(), aMapModes.back().GetMapUnit()); 762 } 763 764 bDone = handleBitmapContent( 765 aClips.back(), 766 pA->GetPoint(), 767 aLogicalSize, 768 BitmapEx(rBitmap), 769 aTarget); 770 break; 771 } 772 773 case META_BMPEXSCALE_ACTION : 774 { 775 const MetaBmpExScaleAction* pA = static_cast< const MetaBmpExScaleAction* >(pAction); 776 777 bDone = handleBitmapContent( 778 aClips.back(), 779 pA->GetPoint(), 780 pA->GetSize(), 781 pA->GetBitmapEx(), 782 aTarget); 783 break; 784 } 785 786 case META_BMPSCALE_ACTION : 787 { 788 const MetaBmpScaleAction* pA = static_cast< const MetaBmpScaleAction* >(pAction); 789 790 bDone = handleBitmapContent( 791 aClips.back(), 792 pA->GetPoint(), 793 pA->GetSize(), 794 BitmapEx(pA->GetBitmap()), 795 aTarget); 796 break; 797 } 798 799 case META_BMPEXSCALEPART_ACTION : 800 { 801 const MetaBmpExScalePartAction* pA = static_cast< const MetaBmpExScalePartAction* >(pAction); 802 const BitmapEx& rBitmapEx = pA->GetBitmapEx(); 803 804 if(rBitmapEx.IsEmpty()) 805 { 806 // empty content 807 bDone = true; 808 } 809 else 810 { 811 BitmapEx aCroppedBitmapEx(rBitmapEx); 812 const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); 813 814 if(aCropRectangle.IsEmpty()) 815 { 816 // empty content 817 bDone = true; 818 } 819 else 820 { 821 aCroppedBitmapEx.Crop(aCropRectangle); 822 bDone = handleBitmapContent( 823 aClips.back(), 824 pA->GetDestPoint(), 825 pA->GetDestSize(), 826 aCroppedBitmapEx, 827 aTarget); 828 } 829 } 830 break; 831 } 832 833 case META_BMPSCALEPART_ACTION : 834 { 835 const MetaBmpScalePartAction* pA = static_cast< const MetaBmpScalePartAction* >(pAction); 836 const Bitmap& rBitmap = pA->GetBitmap(); 837 838 if(rBitmap.IsEmpty()) 839 { 840 // empty content 841 bDone = true; 842 } 843 else 844 { 845 Bitmap aCroppedBitmap(rBitmap); 846 const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); 847 848 if(aCropRectangle.IsEmpty()) 849 { 850 // empty content 851 bDone = true; 852 } 853 else 854 { 855 aCroppedBitmap.Crop(aCropRectangle); 856 bDone = handleBitmapContent( 857 aClips.back(), 858 pA->GetDestPoint(), 859 pA->GetDestSize(), 860 BitmapEx(aCroppedBitmap), 861 aTarget); 862 } 863 } 864 break; 865 } 866 867 // 868 // need to handle all those 'hacks' which hide data in comments 869 // 870 case META_COMMENT_ACTION : 871 { 872 const MetaCommentAction* pA = static_cast< const MetaCommentAction* >(pAction); 873 const ByteString& rComment = pA->GetComment(); 874 875 if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XGRAD_SEQ_BEGIN")) 876 { 877 // nothing to do; this just means that between here and XGRAD_SEQ_END 878 // exists a META_GRADIENTEX_ACTION mixed with Xor-tricked painiting 879 // commands. This comment is used to scan over these and filter for 880 // the gradient action. It is needed to support META_GRADIENTEX_ACTION 881 // in this processor to solve usages. 882 } 883 else if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XPATHFILL_SEQ_BEGIN")) 884 { 885 SvtGraphicFill aFilling; 886 PolyPolygon aPath; 887 888 { // read SvtGraphicFill 889 SvMemoryStream aMemStm((void*)pA->GetData(), pA->GetDataSize(),STREAM_READ); 890 aMemStm >> aFilling; 891 } 892 893 aFilling.getPath(aPath); 894 895 if(aPath.Count()) 896 { 897 const basegfx::B2DPolyPolygon aSource(aPath.getB2DPolyPolygon()); 898 const basegfx::B2DPolyPolygon aResult( 899 basegfx::tools::clipPolyPolygonOnPolyPolygon( 900 aSource, 901 aClips.back(), 902 true, // inside 903 false)); // stroke 904 905 if(aResult.count()) 906 { 907 if(aResult != aSource) 908 { 909 // add clipped geometry 910 aFilling.setPath(PolyPolygon(aResult)); 911 addSvtGraphicFill(aFilling, aTarget); 912 bDone = true; 913 } 914 } 915 else 916 { 917 // exchange with empty polygon 918 aFilling.setPath(PolyPolygon()); 919 addSvtGraphicFill(aFilling, aTarget); 920 bDone = true; 921 } 922 } 923 } 924 else if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XPATHSTROKE_SEQ_BEGIN")) 925 { 926 SvtGraphicStroke aStroke; 927 Polygon aPath; 928 929 { // read SvtGraphicFill 930 SvMemoryStream aMemStm((void*)pA->GetData(), pA->GetDataSize(),STREAM_READ); 931 aMemStm >> aStroke; 932 } 933 934 aStroke.getPath(aPath); 935 936 if(aPath.GetSize()) 937 { 938 const basegfx::B2DPolygon aSource(aPath.getB2DPolygon()); 939 const basegfx::B2DPolyPolygon aResult( 940 basegfx::tools::clipPolygonOnPolyPolygon( 941 aSource, 942 aClips.back(), 943 true, // inside 944 true)); // stroke 945 946 if(aResult.count()) 947 { 948 if(aResult.count() > 1 || aResult.getB2DPolygon(0) != aSource) 949 { 950 // add clipped geometry 951 for(sal_uInt32 a(0); a < aResult.count(); a++) 952 { 953 aStroke.setPath(Polygon(aResult.getB2DPolygon(a))); 954 addSvtGraphicStroke(aStroke, aTarget); 955 } 956 957 bDone = true; 958 } 959 } 960 else 961 { 962 // exchange with empty polygon 963 aStroke.setPath(Polygon()); 964 addSvtGraphicStroke(aStroke, aTarget); 965 bDone = true; 966 } 967 968 } 969 } 970 break; 971 } 972 973 // 974 // need to handle gradient fills (hopefully only unroated ones) 975 // 976 977 case META_GRADIENT_ACTION : 978 { 979 const MetaGradientAction* pA = static_cast< const MetaGradientAction* >(pAction); 980 const Rectangle& rRect = pA->GetRect(); 981 982 if(rRect.IsEmpty()) 983 { 984 bDone = true; 985 } 986 else 987 { 988 bDone = handleGradientContent( 989 aClips.back(), 990 basegfx::B2DPolyPolygon( 991 basegfx::tools::createPolygonFromRect( 992 basegfx::B2DRange( 993 rRect.Left(), rRect.Top(), 994 rRect.Right(), rRect.Bottom()))), 995 pA->GetGradient(), 996 aTarget); 997 } 998 999 1000 break; 1001 } 1002 1003 case META_GRADIENTEX_ACTION : 1004 { 1005 const MetaGradientExAction* pA = static_cast< const MetaGradientExAction* >(pAction); 1006 const PolyPolygon& rPolyPoly = pA->GetPolyPolygon(); 1007 1008 bDone = handleGradientContent( 1009 aClips.back(), 1010 rPolyPoly.getB2DPolyPolygon(), 1011 pA->GetGradient(), 1012 aTarget); 1013 break; 1014 } 1015 1016 // not (yet) supported actions 1017 // 1018 // META_NULL_ACTION 1019 // META_TEXT_ACTION 1020 // META_TEXTARRAY_ACTION 1021 // META_STRETCHTEXT_ACTION 1022 // META_TEXTRECT_ACTION 1023 // META_MASK_ACTION 1024 // META_MASKSCALE_ACTION 1025 // META_MASKSCALEPART_ACTION 1026 // META_HATCH_ACTION 1027 // META_WALLPAPER_ACTION 1028 // META_FILLCOLOR_ACTION 1029 // META_TEXTCOLOR_ACTION 1030 // META_TEXTFILLCOLOR_ACTION 1031 // META_TEXTALIGN_ACTION 1032 // META_MAPMODE_ACTION 1033 // META_FONT_ACTION 1034 // META_TRANSPARENT_ACTION 1035 // META_EPS_ACTION 1036 // META_REFPOINT_ACTION 1037 // META_TEXTLINECOLOR_ACTION 1038 // META_TEXTLINE_ACTION 1039 // META_FLOATTRANSPARENT_ACTION 1040 // META_LAYOUTMODE_ACTION 1041 // META_TEXTLANGUAGE_ACTION 1042 // META_OVERLINECOLOR_ACTION 1043 1044 // if an action is not handled at all, it will simply get copied to the 1045 // target (see below). This is the default for all non-implemented actions 1046 default: 1047 { 1048 break; 1049 } 1050 } 1051 } 1052 1053 if(bDone) 1054 { 1055 bChanged = true; 1056 } 1057 else 1058 { 1059 const_cast< MetaAction* >(pAction)->Duplicate(); 1060 aTarget.AddAction(const_cast< MetaAction* >(pAction)); 1061 } 1062 } 1063 1064 if(bChanged) 1065 { 1066 // when changed, copy back and do not forget to set MapMode 1067 // and PrefSize 1068 aTarget.SetPrefMapMode(rSource.GetPrefMapMode()); 1069 aTarget.SetPrefSize(rSource.GetPrefSize()); 1070 rSource = aTarget; 1071 } 1072 } 1073 1074 ////////////////////////////////////////////////////////////////////////////// 1075 1076 bool VCL_DLLPUBLIC usesClipActions(const GDIMetaFile& rSource) 1077 { 1078 const sal_uLong nObjCount(rSource.GetActionCount()); 1079 1080 for(sal_uLong i(0); i < nObjCount; ++i) 1081 { 1082 const MetaAction* pAction(rSource.GetAction(i)); 1083 const sal_uInt16 nType(pAction->GetType()); 1084 1085 switch(nType) 1086 { 1087 case META_CLIPREGION_ACTION : 1088 case META_ISECTRECTCLIPREGION_ACTION : 1089 case META_ISECTREGIONCLIPREGION_ACTION : 1090 case META_MOVECLIPREGION_ACTION : 1091 { 1092 return true; 1093 break; 1094 } 1095 1096 default: break; 1097 } 1098 } 1099 1100 return false; 1101 } 1102 1103 ////////////////////////////////////////////////////////////////////////////// 1104 // eof 1105