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