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 396 if(aPushFlags.size()) 397 { 398 const sal_uInt16 nFlags(aPushFlags.back()); 399 aPushFlags.pop_back(); 400 401 if(nFlags & PUSH_CLIPREGION) 402 { 403 if(aClips.size() > 1) 404 { 405 aClips.pop_back(); 406 } 407 else 408 { 409 OSL_ENSURE(false, "Wrong POP() in ClipRegions (!)"); 410 } 411 } 412 413 if(nFlags & PUSH_MAPMODE) 414 { 415 if(aMapModes.size() > 1) 416 { 417 aMapModes.pop_back(); 418 } 419 else 420 { 421 OSL_ENSURE(false, "Wrong POP() in MapModes (!)"); 422 } 423 } 424 } 425 else 426 { 427 OSL_ENSURE(false, "Invalid pop() without push() (!)"); 428 } 429 430 break; 431 } 432 433 case META_MAPMODE_ACTION : 434 { 435 const MetaMapModeAction* pA = static_cast< const MetaMapModeAction* >(pAction); 436 437 aMapModes.back() = pA->GetMapMode(); 438 break; 439 } 440 441 default: 442 { 443 break; 444 } 445 } 446 447 // this area contains all actions which could potentially be clipped. Since 448 // this tooling is only a fallback (see comments in header), only the needed 449 // actions will be implemented. Extend using the pattern for the already 450 // implemented actions. 451 if(aClips.size() && aClips.back().count()) 452 { 453 switch(nType) 454 { 455 // 456 // pixel actions, just check on inside 457 // 458 case META_PIXEL_ACTION : 459 { 460 const MetaPixelAction* pA = static_cast< const MetaPixelAction* >(pAction); 461 const Point& rPoint = pA->GetPoint(); 462 463 if(!basegfx::tools::isInside( 464 aClips.back(), 465 basegfx::B2DPoint(rPoint.X(), rPoint.Y()))) 466 { 467 // when not inside, do not add original 468 bDone = true; 469 } 470 break; 471 } 472 473 case META_POINT_ACTION : 474 { 475 const MetaPointAction* pA = static_cast< const MetaPointAction* >(pAction); 476 const Point& rPoint = pA->GetPoint(); 477 478 if(!basegfx::tools::isInside( 479 aClips.back(), 480 basegfx::B2DPoint(rPoint.X(), rPoint.Y()))) 481 { 482 // when not inside, do not add original 483 bDone = true; 484 } 485 break; 486 } 487 488 // 489 // geometry actions 490 // 491 case META_LINE_ACTION : 492 { 493 const MetaLineAction* pA = static_cast< const MetaLineAction* >(pAction); 494 const Point& rStart(pA->GetStartPoint()); 495 const Point& rEnd(pA->GetEndPoint()); 496 basegfx::B2DPolygon aLine; 497 498 aLine.append(basegfx::B2DPoint(rStart.X(), rStart.Y())); 499 aLine.append(basegfx::B2DPoint(rEnd.X(), rEnd.Y())); 500 501 bDone = handleGeometricContent( 502 aClips.back(), 503 basegfx::B2DPolyPolygon(aLine), 504 aTarget, 505 true); // stroke 506 break; 507 } 508 509 case META_RECT_ACTION : 510 { 511 const MetaRectAction* pA = static_cast< const MetaRectAction* >(pAction); 512 const Rectangle& rRect = pA->GetRect(); 513 514 if(rRect.IsEmpty()) 515 { 516 bDone = true; 517 } 518 else 519 { 520 521 bDone = handleGeometricContent( 522 aClips.back(), 523 basegfx::B2DPolyPolygon( 524 basegfx::tools::createPolygonFromRect( 525 basegfx::B2DRange( 526 rRect.Left(), rRect.Top(), 527 rRect.Right(), rRect.Bottom()))), 528 aTarget, 529 false); // stroke 530 } 531 break; 532 } 533 534 case META_ROUNDRECT_ACTION : 535 { 536 const MetaRoundRectAction* pA = static_cast< const MetaRoundRectAction* >(pAction); 537 const Rectangle& rRect = pA->GetRect(); 538 539 if(rRect.IsEmpty()) 540 { 541 bDone = true; 542 } 543 else 544 { 545 const sal_uInt32 nHor(pA->GetHorzRound()); 546 const sal_uInt32 nVer(pA->GetVertRound()); 547 const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()); 548 basegfx::B2DPolygon aOutline; 549 550 if(nHor || nVer) 551 { 552 double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0)); 553 double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0)); 554 fRadiusX = std::max(0.0, std::min(1.0, fRadiusX)); 555 fRadiusY = std::max(0.0, std::min(1.0, fRadiusY)); 556 557 aOutline = basegfx::tools::createPolygonFromRect(aRange, fRadiusX, fRadiusY); 558 } 559 else 560 { 561 aOutline = basegfx::tools::createPolygonFromRect(aRange); 562 } 563 564 bDone = handleGeometricContent( 565 aClips.back(), 566 basegfx::B2DPolyPolygon(aOutline), 567 aTarget, 568 false); // stroke 569 } 570 break; 571 } 572 573 case META_ELLIPSE_ACTION : 574 { 575 const MetaEllipseAction* pA = static_cast< const MetaEllipseAction* >(pAction); 576 const Rectangle& rRect = pA->GetRect(); 577 578 if(rRect.IsEmpty()) 579 { 580 bDone = true; 581 } 582 else 583 { 584 const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()); 585 586 bDone = handleGeometricContent( 587 aClips.back(), 588 basegfx::B2DPolyPolygon( 589 basegfx::tools::createPolygonFromEllipse( 590 aRange.getCenter(), 591 aRange.getWidth() * 0.5, 592 aRange.getHeight() * 0.5)), 593 aTarget, 594 false); // stroke 595 } 596 break; 597 } 598 599 case META_ARC_ACTION : 600 { 601 const MetaArcAction* pA = static_cast< const MetaArcAction* >(pAction); 602 const Rectangle& rRect = pA->GetRect(); 603 604 if(rRect.IsEmpty()) 605 { 606 bDone = true; 607 } 608 else 609 { 610 const Polygon aToolsPoly( 611 rRect, 612 pA->GetStartPoint(), 613 pA->GetEndPoint(), 614 POLY_ARC); 615 616 bDone = handleGeometricContent( 617 aClips.back(), 618 basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()), 619 aTarget, 620 true); // stroke 621 } 622 break; 623 } 624 625 case META_PIE_ACTION : 626 { 627 const MetaPieAction* pA = static_cast< const MetaPieAction* >(pAction); 628 const Rectangle& rRect = pA->GetRect(); 629 630 if(rRect.IsEmpty()) 631 { 632 bDone = true; 633 } 634 else 635 { 636 const Polygon aToolsPoly( 637 rRect, 638 pA->GetStartPoint(), 639 pA->GetEndPoint(), 640 POLY_PIE); 641 642 bDone = handleGeometricContent( 643 aClips.back(), 644 basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()), 645 aTarget, 646 false); // stroke 647 } 648 break; 649 } 650 651 case META_CHORD_ACTION : 652 { 653 const MetaChordAction* pA = static_cast< const MetaChordAction* >(pAction); 654 const Rectangle& rRect = pA->GetRect(); 655 656 if(rRect.IsEmpty()) 657 { 658 bDone = true; 659 } 660 else 661 { 662 const Polygon aToolsPoly( 663 rRect, 664 pA->GetStartPoint(), 665 pA->GetEndPoint(), 666 POLY_CHORD); 667 668 bDone = handleGeometricContent( 669 aClips.back(), 670 basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()), 671 aTarget, 672 false); // stroke 673 } 674 break; 675 } 676 677 case META_POLYLINE_ACTION : 678 { 679 const MetaPolyLineAction* pA = static_cast< const MetaPolyLineAction* >(pAction); 680 681 bDone = handleGeometricContent( 682 aClips.back(), 683 basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()), 684 aTarget, 685 true); // stroke 686 break; 687 } 688 689 case META_POLYGON_ACTION : 690 { 691 const MetaPolygonAction* pA = static_cast< const MetaPolygonAction* >(pAction); 692 693 bDone = handleGeometricContent( 694 aClips.back(), 695 basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()), 696 aTarget, 697 false); // stroke 698 break; 699 } 700 701 case META_POLYPOLYGON_ACTION : 702 { 703 const MetaPolyPolygonAction* pA = static_cast< const MetaPolyPolygonAction* >(pAction); 704 const PolyPolygon& rPoly = pA->GetPolyPolygon(); 705 706 bDone = handleGeometricContent( 707 aClips.back(), 708 rPoly.getB2DPolyPolygon(), 709 aTarget, 710 false); // stroke 711 break; 712 } 713 714 // 715 // bitmap actions, create BitmapEx with alpha channel derived 716 // from clipping 717 // 718 case META_BMPEX_ACTION : 719 { 720 const MetaBmpExAction* pA = static_cast< const MetaBmpExAction* >(pAction); 721 const BitmapEx& rBitmapEx = pA->GetBitmapEx(); 722 723 // the logical size depends on the PrefSize of the given bitmap in 724 // combination with the current MapMode 725 Size aLogicalSize(rBitmapEx.GetPrefSize()); 726 727 if(MAP_PIXEL == rBitmapEx.GetPrefMapMode().GetMapUnit()) 728 { 729 aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit()); 730 } 731 else 732 { 733 aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmapEx.GetPrefMapMode(), aMapModes.back().GetMapUnit()); 734 } 735 736 bDone = handleBitmapContent( 737 aClips.back(), 738 pA->GetPoint(), 739 aLogicalSize, 740 rBitmapEx, 741 aTarget); 742 break; 743 } 744 745 case META_BMP_ACTION : 746 { 747 const MetaBmpAction* pA = static_cast< const MetaBmpAction* >(pAction); 748 const Bitmap& rBitmap = pA->GetBitmap(); 749 750 // the logical size depends on the PrefSize of the given bitmap in 751 // combination with the current MapMode 752 Size aLogicalSize(rBitmap.GetPrefSize()); 753 754 if(MAP_PIXEL == rBitmap.GetPrefMapMode().GetMapUnit()) 755 { 756 aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit()); 757 } 758 else 759 { 760 aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmap.GetPrefMapMode(), aMapModes.back().GetMapUnit()); 761 } 762 763 bDone = handleBitmapContent( 764 aClips.back(), 765 pA->GetPoint(), 766 aLogicalSize, 767 BitmapEx(rBitmap), 768 aTarget); 769 break; 770 } 771 772 case META_BMPEXSCALE_ACTION : 773 { 774 const MetaBmpExScaleAction* pA = static_cast< const MetaBmpExScaleAction* >(pAction); 775 776 bDone = handleBitmapContent( 777 aClips.back(), 778 pA->GetPoint(), 779 pA->GetSize(), 780 pA->GetBitmapEx(), 781 aTarget); 782 break; 783 } 784 785 case META_BMPSCALE_ACTION : 786 { 787 const MetaBmpScaleAction* pA = static_cast< const MetaBmpScaleAction* >(pAction); 788 789 bDone = handleBitmapContent( 790 aClips.back(), 791 pA->GetPoint(), 792 pA->GetSize(), 793 BitmapEx(pA->GetBitmap()), 794 aTarget); 795 break; 796 } 797 798 case META_BMPEXSCALEPART_ACTION : 799 { 800 const MetaBmpExScalePartAction* pA = static_cast< const MetaBmpExScalePartAction* >(pAction); 801 const BitmapEx& rBitmapEx = pA->GetBitmapEx(); 802 803 if(rBitmapEx.IsEmpty()) 804 { 805 // empty content 806 bDone = true; 807 } 808 else 809 { 810 BitmapEx aCroppedBitmapEx(rBitmapEx); 811 const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); 812 813 if(aCropRectangle.IsEmpty()) 814 { 815 // empty content 816 bDone = true; 817 } 818 else 819 { 820 aCroppedBitmapEx.Crop(aCropRectangle); 821 bDone = handleBitmapContent( 822 aClips.back(), 823 pA->GetDestPoint(), 824 pA->GetDestSize(), 825 aCroppedBitmapEx, 826 aTarget); 827 } 828 } 829 break; 830 } 831 832 case META_BMPSCALEPART_ACTION : 833 { 834 const MetaBmpScalePartAction* pA = static_cast< const MetaBmpScalePartAction* >(pAction); 835 const Bitmap& rBitmap = pA->GetBitmap(); 836 837 if(rBitmap.IsEmpty()) 838 { 839 // empty content 840 bDone = true; 841 } 842 else 843 { 844 Bitmap aCroppedBitmap(rBitmap); 845 const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); 846 847 if(aCropRectangle.IsEmpty()) 848 { 849 // empty content 850 bDone = true; 851 } 852 else 853 { 854 aCroppedBitmap.Crop(aCropRectangle); 855 bDone = handleBitmapContent( 856 aClips.back(), 857 pA->GetDestPoint(), 858 pA->GetDestSize(), 859 BitmapEx(aCroppedBitmap), 860 aTarget); 861 } 862 } 863 break; 864 } 865 866 // 867 // need to handle all those 'hacks' which hide data in comments 868 // 869 case META_COMMENT_ACTION : 870 { 871 const MetaCommentAction* pA = static_cast< const MetaCommentAction* >(pAction); 872 const ByteString& rComment = pA->GetComment(); 873 874 if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XGRAD_SEQ_BEGIN")) 875 { 876 // nothing to do; this just means that between here and XGRAD_SEQ_END 877 // exists a META_GRADIENTEX_ACTION mixed with Xor-tricked painiting 878 // commands. This comment is used to scan over these and filter for 879 // the gradient action. It is needed to support META_GRADIENTEX_ACTION 880 // in this processor to solve usages. 881 } 882 else if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XPATHFILL_SEQ_BEGIN")) 883 { 884 SvtGraphicFill aFilling; 885 PolyPolygon aPath; 886 887 { // read SvtGraphicFill 888 SvMemoryStream aMemStm((void*)pA->GetData(), pA->GetDataSize(),STREAM_READ); 889 aMemStm >> aFilling; 890 } 891 892 aFilling.getPath(aPath); 893 894 if(aPath.Count()) 895 { 896 const basegfx::B2DPolyPolygon aSource(aPath.getB2DPolyPolygon()); 897 const basegfx::B2DPolyPolygon aResult( 898 basegfx::tools::clipPolyPolygonOnPolyPolygon( 899 aSource, 900 aClips.back(), 901 true, // inside 902 false)); // stroke 903 904 if(aResult.count()) 905 { 906 if(aResult != aSource) 907 { 908 // add clipped geometry 909 aFilling.setPath(PolyPolygon(aResult)); 910 addSvtGraphicFill(aFilling, aTarget); 911 bDone = true; 912 } 913 } 914 else 915 { 916 // exchange with empty polygon 917 aFilling.setPath(PolyPolygon()); 918 addSvtGraphicFill(aFilling, aTarget); 919 bDone = true; 920 } 921 } 922 } 923 else if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XPATHSTROKE_SEQ_BEGIN")) 924 { 925 SvtGraphicStroke aStroke; 926 Polygon aPath; 927 928 { // read SvtGraphicFill 929 SvMemoryStream aMemStm((void*)pA->GetData(), pA->GetDataSize(),STREAM_READ); 930 aMemStm >> aStroke; 931 } 932 933 aStroke.getPath(aPath); 934 935 if(aPath.GetSize()) 936 { 937 const basegfx::B2DPolygon aSource(aPath.getB2DPolygon()); 938 const basegfx::B2DPolyPolygon aResult( 939 basegfx::tools::clipPolygonOnPolyPolygon( 940 aSource, 941 aClips.back(), 942 true, // inside 943 true)); // stroke 944 945 if(aResult.count()) 946 { 947 if(aResult.count() > 1 || aResult.getB2DPolygon(0) != aSource) 948 { 949 // add clipped geometry 950 for(sal_uInt32 a(0); a < aResult.count(); a++) 951 { 952 aStroke.setPath(Polygon(aResult.getB2DPolygon(a))); 953 addSvtGraphicStroke(aStroke, aTarget); 954 } 955 956 bDone = true; 957 } 958 } 959 else 960 { 961 // exchange with empty polygon 962 aStroke.setPath(Polygon()); 963 addSvtGraphicStroke(aStroke, aTarget); 964 bDone = true; 965 } 966 967 } 968 } 969 break; 970 } 971 972 // 973 // need to handle gradient fills (hopefully only unroated ones) 974 // 975 976 case META_GRADIENT_ACTION : 977 { 978 const MetaGradientAction* pA = static_cast< const MetaGradientAction* >(pAction); 979 const Rectangle& rRect = pA->GetRect(); 980 981 if(rRect.IsEmpty()) 982 { 983 bDone = true; 984 } 985 else 986 { 987 bDone = handleGradientContent( 988 aClips.back(), 989 basegfx::B2DPolyPolygon( 990 basegfx::tools::createPolygonFromRect( 991 basegfx::B2DRange( 992 rRect.Left(), rRect.Top(), 993 rRect.Right(), rRect.Bottom()))), 994 pA->GetGradient(), 995 aTarget); 996 } 997 998 999 break; 1000 } 1001 1002 case META_GRADIENTEX_ACTION : 1003 { 1004 const MetaGradientExAction* pA = static_cast< const MetaGradientExAction* >(pAction); 1005 const PolyPolygon& rPolyPoly = pA->GetPolyPolygon(); 1006 1007 bDone = handleGradientContent( 1008 aClips.back(), 1009 rPolyPoly.getB2DPolyPolygon(), 1010 pA->GetGradient(), 1011 aTarget); 1012 break; 1013 } 1014 1015 // not (yet) supported actions 1016 // 1017 // META_NULL_ACTION 1018 // META_TEXT_ACTION 1019 // META_TEXTARRAY_ACTION 1020 // META_STRETCHTEXT_ACTION 1021 // META_TEXTRECT_ACTION 1022 // META_MASK_ACTION 1023 // META_MASKSCALE_ACTION 1024 // META_MASKSCALEPART_ACTION 1025 // META_HATCH_ACTION 1026 // META_WALLPAPER_ACTION 1027 // META_FILLCOLOR_ACTION 1028 // META_TEXTCOLOR_ACTION 1029 // META_TEXTFILLCOLOR_ACTION 1030 // META_TEXTALIGN_ACTION 1031 // META_MAPMODE_ACTION 1032 // META_FONT_ACTION 1033 // META_TRANSPARENT_ACTION 1034 // META_EPS_ACTION 1035 // META_REFPOINT_ACTION 1036 // META_TEXTLINECOLOR_ACTION 1037 // META_TEXTLINE_ACTION 1038 // META_FLOATTRANSPARENT_ACTION 1039 // META_LAYOUTMODE_ACTION 1040 // META_TEXTLANGUAGE_ACTION 1041 // META_OVERLINECOLOR_ACTION 1042 1043 // if an action is not handled at all, it will simply get copied to the 1044 // target (see below). This is the default for all non-implemented actions 1045 default: 1046 { 1047 break; 1048 } 1049 } 1050 } 1051 1052 if(bDone) 1053 { 1054 bChanged = true; 1055 } 1056 else 1057 { 1058 const_cast< MetaAction* >(pAction)->Duplicate(); 1059 aTarget.AddAction(const_cast< MetaAction* >(pAction)); 1060 } 1061 } 1062 1063 if(bChanged) 1064 { 1065 // when changed, copy back and do not forget to set MapMode 1066 // and PrefSize 1067 aTarget.SetPrefMapMode(rSource.GetPrefMapMode()); 1068 aTarget.SetPrefSize(rSource.GetPrefSize()); 1069 rSource = aTarget; 1070 } 1071 } 1072 1073 ////////////////////////////////////////////////////////////////////////////// 1074 1075 bool VCL_DLLPUBLIC usesClipActions(const GDIMetaFile& rSource) 1076 { 1077 const sal_uLong nObjCount(rSource.GetActionCount()); 1078 1079 for(sal_uLong i(0); i < nObjCount; ++i) 1080 { 1081 const MetaAction* pAction(rSource.GetAction(i)); 1082 const sal_uInt16 nType(pAction->GetType()); 1083 1084 switch(nType) 1085 { 1086 case META_CLIPREGION_ACTION : 1087 case META_ISECTRECTCLIPREGION_ACTION : 1088 case META_ISECTREGIONCLIPREGION_ACTION : 1089 case META_MOVECLIPREGION_ACTION : 1090 { 1091 return true; 1092 break; 1093 } 1094 1095 default: break; 1096 } 1097 } 1098 1099 return false; 1100 } 1101 1102 ////////////////////////////////////////////////////////////////////////////// 1103 // eof 1104