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