/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_vcl.hxx" #include #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////// // helpers namespace { bool handleGeometricContent( const basegfx::B2DPolyPolygon& rClip, const basegfx::B2DPolyPolygon& rSource, GDIMetaFile& rTarget, bool bStroke) { if(rSource.count() && rClip.count()) { const basegfx::B2DPolyPolygon aResult( basegfx::tools::clipPolyPolygonOnPolyPolygon( rSource, rClip, true, // inside bStroke)); if(aResult.count()) { if(aResult == rSource) { // not clipped, but inside. Add original return false; } else { // add clipped geometry if(bStroke) { for(sal_uInt32 a(0); a < aResult.count(); a++) { rTarget.AddAction( new MetaPolyLineAction( Polygon(aResult.getB2DPolygon(a)))); } } else { rTarget.AddAction( new MetaPolyPolygonAction( PolyPolygon(aResult))); } } } } return true; } bool handleGradientContent( const basegfx::B2DPolyPolygon& rClip, const basegfx::B2DPolyPolygon& rSource, const Gradient& rGradient, GDIMetaFile& rTarget) { if(rSource.count() && rClip.count()) { const basegfx::B2DPolyPolygon aResult( basegfx::tools::clipPolyPolygonOnPolyPolygon( rSource, rClip, true, // inside false)); // stroke if(aResult.count()) { if(aResult == rSource) { // not clipped, but inside. Add original return false; } else { // add clipped geometry rTarget.AddAction( new MetaGradientExAction( PolyPolygon(aResult), rGradient)); } } } return true; } bool handleBitmapContent( const basegfx::B2DPolyPolygon& rClip, const Point& rPoint, const Size& rSize, const BitmapEx& rBitmapEx, GDIMetaFile& rTarget) { if(!rSize.Width() || !rSize.Height() || rBitmapEx.IsEmpty()) { // bitmap or size is empty return true; } const basegfx::B2DRange aLogicBitmapRange( rPoint.X(), rPoint.Y(), rPoint.X() + rSize.Width(), rPoint.Y() + rSize.Height()); const basegfx::B2DPolyPolygon aClipOfBitmap( basegfx::tools::clipPolyPolygonOnRange( rClip, aLogicBitmapRange, true, false)); // stroke if(!aClipOfBitmap.count()) { // outside clip region return true; } // inside or overlapping. Use area to find out if it is completely // covering (inside) or overlapping const double fClipArea(basegfx::tools::getArea(aClipOfBitmap)); const double fBitmapArea( aLogicBitmapRange.getWidth() * aLogicBitmapRange.getWidth() + aLogicBitmapRange.getHeight() * aLogicBitmapRange.getHeight()); const double fFactor(fClipArea / fBitmapArea); if(basegfx::fTools::more(fFactor, 1.0 - 0.001)) { // completely covering (with 0.1% tolerance) return false; } // needs clipping (with 0.1% tolerance). Prepare VirtualDevice // in pixel mode for alpha channel painting (black is transparent, // white to paint 100% opacity) const Size aSizePixel(rBitmapEx.GetSizePixel()); VirtualDevice aVDev; aVDev.SetOutputSizePixel(aSizePixel); aVDev.EnableMapMode(false); aVDev.SetFillColor(COL_WHITE); aVDev.SetLineColor(); if(rBitmapEx.IsTransparent()) { // use given alpha channel aVDev.DrawBitmap(Point(0, 0), rBitmapEx.GetAlpha().GetBitmap()); } else { // reset alpha channel aVDev.SetBackground(Wallpaper(Color(COL_BLACK))); aVDev.Erase(); } // transform polygon from clipping to pixel coordinates basegfx::B2DPolyPolygon aPixelPoly(aClipOfBitmap); basegfx::B2DHomMatrix aTransform; aTransform.translate(-aLogicBitmapRange.getMinX(), -aLogicBitmapRange.getMinY()); aTransform.scale( static_cast< double >(aSizePixel.Width()) / aLogicBitmapRange.getWidth(), static_cast< double >(aSizePixel.Height()) / aLogicBitmapRange.getHeight()); aPixelPoly.transform(aTransform); // to fill the non-covered parts, use the Xor fill rule of // PolyPolygon painting. Start with a all-covering polygon and // add the clip polygon one basegfx::B2DPolyPolygon aInvertPixelPoly; aInvertPixelPoly.append( basegfx::tools::createPolygonFromRect( basegfx::B2DRange( 0.0, 0.0, aSizePixel.Width(), aSizePixel.Height()))); aInvertPixelPoly.append(aPixelPoly); // paint as alpha aVDev.DrawPolyPolygon(aInvertPixelPoly); // get created alpha mask and set defaults AlphaMask aAlpha( aVDev.GetBitmap( Point(0, 0), aSizePixel)); aAlpha.SetPrefSize(rBitmapEx.GetPrefSize()); aAlpha.SetPrefMapMode(rBitmapEx.GetPrefMapMode()); // add new action replacing the old one rTarget.AddAction( new MetaBmpExScaleAction( Point( basegfx::fround(aLogicBitmapRange.getMinX()), basegfx::fround(aLogicBitmapRange.getMinY())), Size( basegfx::fround(aLogicBitmapRange.getWidth()), basegfx::fround(aLogicBitmapRange.getHeight())), BitmapEx(rBitmapEx.GetBitmap(), aAlpha))); return true; } void addSvtGraphicStroke(const SvtGraphicStroke& rStroke, GDIMetaFile& rTarget) { // write SvtGraphicFill SvMemoryStream aMemStm; aMemStm << rStroke; rTarget.AddAction( new MetaCommentAction( "XPATHSTROKE_SEQ_BEGIN", 0, static_cast< const sal_uInt8* >(aMemStm.GetData()), aMemStm.Seek(STREAM_SEEK_TO_END))); } void addSvtGraphicFill(const SvtGraphicFill &rFilling, GDIMetaFile& rTarget) { // write SvtGraphicFill SvMemoryStream aMemStm; aMemStm << rFilling; rTarget.AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", 0, static_cast< const sal_uInt8* >(aMemStm.GetData()), aMemStm.Seek(STREAM_SEEK_TO_END))); } } // end of anonymous namespace ////////////////////////////////////////////////////////////////////////////// // #121267# Tooling to internally clip geometry against internal clip regions void clipMetafileContentAgainstOwnRegions(GDIMetaFile& rSource) { const sal_uLong nObjCount(rSource.GetActionCount()); if(!nObjCount) { return; } // prepare target data container and push/pop stack data GDIMetaFile aTarget; bool bChanged(false); std::vector< basegfx::B2DPolyPolygon > aClips; std::vector< sal_uInt16 > aPushFlags; std::vector< MapMode > aMapModes; // start with empty region aClips.push_back(basegfx::B2DPolyPolygon()); // start with default MapMode (MAP_PIXEL) aMapModes.push_back(MapMode()); for(sal_uLong i(0); i < nObjCount; ++i) { const MetaAction* pAction(rSource.GetAction(i)); const sal_uInt16 nType(pAction->GetType()); bool bDone(false); // basic operation takes care of clipregion actions (four) and push/pop of these // to steer the currently set clip region. There *is* an active // clip region when (aClips.size() && aClips.back().count()), see // below switch(nType) { case META_CLIPREGION_ACTION : { const MetaClipRegionAction* pA = static_cast< const MetaClipRegionAction* >(pAction); if(pA->IsClipping()) { const Region& rRegion = pA->GetRegion(); const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon()); aClips.back() = aNewClip; } else { aClips.back() = basegfx::B2DPolyPolygon(); } break; } case META_ISECTRECTCLIPREGION_ACTION : { const MetaISectRectClipRegionAction* pA = static_cast< const MetaISectRectClipRegionAction* >(pAction); const Rectangle& rRect = pA->GetRect(); if(!rRect.IsEmpty() && aClips.size() && aClips.back().count()) { const basegfx::B2DRange aClipRange( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()); aClips.back() = basegfx::tools::clipPolyPolygonOnRange( aClips.back(), aClipRange, true, // inside false); // stroke } break; } case META_ISECTREGIONCLIPREGION_ACTION : { const MetaISectRegionClipRegionAction* pA = static_cast< const MetaISectRegionClipRegionAction* >(pAction); const Region& rRegion = pA->GetRegion(); if(!rRegion.IsEmpty() && aClips.size() && aClips.back().count()) { const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon()); aClips.back() = basegfx::tools::clipPolyPolygonOnPolyPolygon( aClips.back(), aNewClip, true, // inside false); // stroke } break; } case META_MOVECLIPREGION_ACTION : { const MetaMoveClipRegionAction* pA = static_cast< const MetaMoveClipRegionAction* >(pAction); const long aHorMove(pA->GetHorzMove()); const long aVerMove(pA->GetVertMove()); if((aHorMove || aVerMove) && aClips.size() && aClips.back().count()) { aClips.back().transform( basegfx::tools::createTranslateB2DHomMatrix( aHorMove, aVerMove)); } break; } case META_PUSH_ACTION : { const MetaPushAction* pA = static_cast< const MetaPushAction* >(pAction); const sal_uInt16 nFlags(pA->GetFlags()); aPushFlags.push_back(nFlags); if(nFlags & PUSH_CLIPREGION) { aClips.push_back(aClips.back()); } if(nFlags & PUSH_MAPMODE) { aMapModes.push_back(aMapModes.back()); } break; } case META_POP_ACTION : { const MetaPopAction* pA = static_cast< const MetaPopAction* >(pAction); if(aPushFlags.size()) { const sal_uInt16 nFlags(aPushFlags.back()); aPushFlags.pop_back(); if(nFlags & PUSH_CLIPREGION) { if(aClips.size() > 1) { aClips.pop_back(); } else { OSL_ENSURE(false, "Wrong POP() in ClipRegions (!)"); } } if(nFlags & PUSH_MAPMODE) { if(aMapModes.size() > 1) { aMapModes.pop_back(); } else { OSL_ENSURE(false, "Wrong POP() in MapModes (!)"); } } } else { OSL_ENSURE(false, "Invalid pop() without push() (!)"); } break; } case META_MAPMODE_ACTION : { const MetaMapModeAction* pA = static_cast< const MetaMapModeAction* >(pAction); aMapModes.back() = pA->GetMapMode(); break; } default: { break; } } // this area contains all actions which could potentially be clipped. Since // this tooling is only a fallback (see comments in header), only the needed // actions will be implemented. Extend using the pattern for the already // implemented actions. if(aClips.size() && aClips.back().count()) { switch(nType) { // // pixel actions, just check on inside // case META_PIXEL_ACTION : { const MetaPixelAction* pA = static_cast< const MetaPixelAction* >(pAction); const Point& rPoint = pA->GetPoint(); if(!basegfx::tools::isInside( aClips.back(), basegfx::B2DPoint(rPoint.X(), rPoint.Y()))) { // when not inside, do not add original bDone = true; } break; } case META_POINT_ACTION : { const MetaPointAction* pA = static_cast< const MetaPointAction* >(pAction); const Point& rPoint = pA->GetPoint(); if(!basegfx::tools::isInside( aClips.back(), basegfx::B2DPoint(rPoint.X(), rPoint.Y()))) { // when not inside, do not add original bDone = true; } break; } // // geometry actions // case META_LINE_ACTION : { const MetaLineAction* pA = static_cast< const MetaLineAction* >(pAction); const Point& rStart(pA->GetStartPoint()); const Point& rEnd(pA->GetEndPoint()); basegfx::B2DPolygon aLine; aLine.append(basegfx::B2DPoint(rStart.X(), rStart.Y())); aLine.append(basegfx::B2DPoint(rEnd.X(), rEnd.Y())); bDone = handleGeometricContent( aClips.back(), basegfx::B2DPolyPolygon(aLine), aTarget, true); // stroke break; } case META_RECT_ACTION : { const MetaRectAction* pA = static_cast< const MetaRectAction* >(pAction); const Rectangle& rRect = pA->GetRect(); if(rRect.IsEmpty()) { bDone = true; } else { bDone = handleGeometricContent( aClips.back(), basegfx::B2DPolyPolygon( basegfx::tools::createPolygonFromRect( basegfx::B2DRange( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()))), aTarget, false); // stroke } break; } case META_ROUNDRECT_ACTION : { const MetaRoundRectAction* pA = static_cast< const MetaRoundRectAction* >(pAction); const Rectangle& rRect = pA->GetRect(); if(rRect.IsEmpty()) { bDone = true; } else { const sal_uInt32 nHor(pA->GetHorzRound()); const sal_uInt32 nVer(pA->GetVertRound()); const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()); basegfx::B2DPolygon aOutline; if(nHor || nVer) { double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0)); double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0)); fRadiusX = std::max(0.0, std::min(1.0, fRadiusX)); fRadiusY = std::max(0.0, std::min(1.0, fRadiusY)); aOutline = basegfx::tools::createPolygonFromRect(aRange, fRadiusX, fRadiusY); } else { aOutline = basegfx::tools::createPolygonFromRect(aRange); } bDone = handleGeometricContent( aClips.back(), basegfx::B2DPolyPolygon(aOutline), aTarget, false); // stroke } break; } case META_ELLIPSE_ACTION : { const MetaEllipseAction* pA = static_cast< const MetaEllipseAction* >(pAction); const Rectangle& rRect = pA->GetRect(); if(rRect.IsEmpty()) { bDone = true; } else { const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()); bDone = handleGeometricContent( aClips.back(), basegfx::B2DPolyPolygon( basegfx::tools::createPolygonFromEllipse( aRange.getCenter(), aRange.getWidth() * 0.5, aRange.getHeight() * 0.5)), aTarget, false); // stroke } break; } case META_ARC_ACTION : { const MetaArcAction* pA = static_cast< const MetaArcAction* >(pAction); const Rectangle& rRect = pA->GetRect(); if(rRect.IsEmpty()) { bDone = true; } else { const Polygon aToolsPoly( rRect, pA->GetStartPoint(), pA->GetEndPoint(), POLY_ARC); bDone = handleGeometricContent( aClips.back(), basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()), aTarget, true); // stroke } break; } case META_PIE_ACTION : { const MetaPieAction* pA = static_cast< const MetaPieAction* >(pAction); const Rectangle& rRect = pA->GetRect(); if(rRect.IsEmpty()) { bDone = true; } else { const Polygon aToolsPoly( rRect, pA->GetStartPoint(), pA->GetEndPoint(), POLY_PIE); bDone = handleGeometricContent( aClips.back(), basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()), aTarget, false); // stroke } break; } case META_CHORD_ACTION : { const MetaChordAction* pA = static_cast< const MetaChordAction* >(pAction); const Rectangle& rRect = pA->GetRect(); if(rRect.IsEmpty()) { bDone = true; } else { const Polygon aToolsPoly( rRect, pA->GetStartPoint(), pA->GetEndPoint(), POLY_CHORD); bDone = handleGeometricContent( aClips.back(), basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()), aTarget, false); // stroke } break; } case META_POLYLINE_ACTION : { const MetaPolyLineAction* pA = static_cast< const MetaPolyLineAction* >(pAction); bDone = handleGeometricContent( aClips.back(), basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()), aTarget, true); // stroke break; } case META_POLYGON_ACTION : { const MetaPolygonAction* pA = static_cast< const MetaPolygonAction* >(pAction); bDone = handleGeometricContent( aClips.back(), basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()), aTarget, false); // stroke break; } case META_POLYPOLYGON_ACTION : { const MetaPolyPolygonAction* pA = static_cast< const MetaPolyPolygonAction* >(pAction); const PolyPolygon& rPoly = pA->GetPolyPolygon(); bDone = handleGeometricContent( aClips.back(), rPoly.getB2DPolyPolygon(), aTarget, false); // stroke break; } // // bitmap actions, create BitmapEx with alpha channel derived // from clipping // case META_BMPEX_ACTION : { const MetaBmpExAction* pA = static_cast< const MetaBmpExAction* >(pAction); const BitmapEx& rBitmapEx = pA->GetBitmapEx(); // the logical size depends on the PrefSize of the given bitmap in // combination with the current MapMode Size aLogicalSize(rBitmapEx.GetPrefSize()); if(MAP_PIXEL == rBitmapEx.GetPrefMapMode().GetMapUnit()) { aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit()); } else { aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmapEx.GetPrefMapMode(), aMapModes.back().GetMapUnit()); } bDone = handleBitmapContent( aClips.back(), pA->GetPoint(), aLogicalSize, rBitmapEx, aTarget); break; } case META_BMP_ACTION : { const MetaBmpAction* pA = static_cast< const MetaBmpAction* >(pAction); const Bitmap& rBitmap = pA->GetBitmap(); // the logical size depends on the PrefSize of the given bitmap in // combination with the current MapMode Size aLogicalSize(rBitmap.GetPrefSize()); if(MAP_PIXEL == rBitmap.GetPrefMapMode().GetMapUnit()) { aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit()); } else { aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmap.GetPrefMapMode(), aMapModes.back().GetMapUnit()); } bDone = handleBitmapContent( aClips.back(), pA->GetPoint(), aLogicalSize, BitmapEx(rBitmap), aTarget); break; } case META_BMPEXSCALE_ACTION : { const MetaBmpExScaleAction* pA = static_cast< const MetaBmpExScaleAction* >(pAction); bDone = handleBitmapContent( aClips.back(), pA->GetPoint(), pA->GetSize(), pA->GetBitmapEx(), aTarget); break; } case META_BMPSCALE_ACTION : { const MetaBmpScaleAction* pA = static_cast< const MetaBmpScaleAction* >(pAction); bDone = handleBitmapContent( aClips.back(), pA->GetPoint(), pA->GetSize(), BitmapEx(pA->GetBitmap()), aTarget); break; } case META_BMPEXSCALEPART_ACTION : { const MetaBmpExScalePartAction* pA = static_cast< const MetaBmpExScalePartAction* >(pAction); const BitmapEx& rBitmapEx = pA->GetBitmapEx(); if(rBitmapEx.IsEmpty()) { // empty content bDone = true; } else { BitmapEx aCroppedBitmapEx(rBitmapEx); const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); if(aCropRectangle.IsEmpty()) { // empty content bDone = true; } else { aCroppedBitmapEx.Crop(aCropRectangle); bDone = handleBitmapContent( aClips.back(), pA->GetDestPoint(), pA->GetDestSize(), aCroppedBitmapEx, aTarget); } } break; } case META_BMPSCALEPART_ACTION : { const MetaBmpScalePartAction* pA = static_cast< const MetaBmpScalePartAction* >(pAction); const Bitmap& rBitmap = pA->GetBitmap(); if(rBitmap.IsEmpty()) { // empty content bDone = true; } else { Bitmap aCroppedBitmap(rBitmap); const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); if(aCropRectangle.IsEmpty()) { // empty content bDone = true; } else { aCroppedBitmap.Crop(aCropRectangle); bDone = handleBitmapContent( aClips.back(), pA->GetDestPoint(), pA->GetDestSize(), BitmapEx(aCroppedBitmap), aTarget); } } break; } // // need to handle all those 'hacks' which hide data in comments // case META_COMMENT_ACTION : { const MetaCommentAction* pA = static_cast< const MetaCommentAction* >(pAction); const ByteString& rComment = pA->GetComment(); if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XGRAD_SEQ_BEGIN")) { // nothing to do; this just means that between here and XGRAD_SEQ_END // exists a META_GRADIENTEX_ACTION mixed with Xor-tricked painiting // commands. This comment is used to scan over these and filter for // the gradient action. It is needed to support META_GRADIENTEX_ACTION // in this processor to solve usages. } else if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XPATHFILL_SEQ_BEGIN")) { SvtGraphicFill aFilling; PolyPolygon aPath; { // read SvtGraphicFill SvMemoryStream aMemStm((void*)pA->GetData(), pA->GetDataSize(),STREAM_READ); aMemStm >> aFilling; } aFilling.getPath(aPath); if(aPath.Count()) { const basegfx::B2DPolyPolygon aSource(aPath.getB2DPolyPolygon()); const basegfx::B2DPolyPolygon aResult( basegfx::tools::clipPolyPolygonOnPolyPolygon( aSource, aClips.back(), true, // inside false)); // stroke if(aResult.count()) { if(aResult != aSource) { // add clipped geometry aFilling.setPath(PolyPolygon(aResult)); addSvtGraphicFill(aFilling, aTarget); bDone = true; } } else { // exchange with empty polygon aFilling.setPath(PolyPolygon()); addSvtGraphicFill(aFilling, aTarget); bDone = true; } } } else if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XPATHSTROKE_SEQ_BEGIN")) { SvtGraphicStroke aStroke; Polygon aPath; { // read SvtGraphicFill SvMemoryStream aMemStm((void*)pA->GetData(), pA->GetDataSize(),STREAM_READ); aMemStm >> aStroke; } aStroke.getPath(aPath); if(aPath.GetSize()) { const basegfx::B2DPolygon aSource(aPath.getB2DPolygon()); const basegfx::B2DPolyPolygon aResult( basegfx::tools::clipPolygonOnPolyPolygon( aSource, aClips.back(), true, // inside true)); // stroke if(aResult.count()) { if(aResult.count() > 1 || aResult.getB2DPolygon(0) != aSource) { // add clipped geometry for(sal_uInt32 a(0); a < aResult.count(); a++) { aStroke.setPath(Polygon(aResult.getB2DPolygon(a))); addSvtGraphicStroke(aStroke, aTarget); } bDone = true; } } else { // exchange with empty polygon aStroke.setPath(Polygon()); addSvtGraphicStroke(aStroke, aTarget); bDone = true; } } } break; } // // need to handle gradient fills (hopefully only unroated ones) // case META_GRADIENT_ACTION : { const MetaGradientAction* pA = static_cast< const MetaGradientAction* >(pAction); const Rectangle& rRect = pA->GetRect(); if(rRect.IsEmpty()) { bDone = true; } else { bDone = handleGradientContent( aClips.back(), basegfx::B2DPolyPolygon( basegfx::tools::createPolygonFromRect( basegfx::B2DRange( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()))), pA->GetGradient(), aTarget); } break; } case META_GRADIENTEX_ACTION : { const MetaGradientExAction* pA = static_cast< const MetaGradientExAction* >(pAction); const PolyPolygon& rPolyPoly = pA->GetPolyPolygon(); bDone = handleGradientContent( aClips.back(), rPolyPoly.getB2DPolyPolygon(), pA->GetGradient(), aTarget); break; } // not (yet) supported actions // // META_NULL_ACTION // META_TEXT_ACTION // META_TEXTARRAY_ACTION // META_STRETCHTEXT_ACTION // META_TEXTRECT_ACTION // META_MASK_ACTION // META_MASKSCALE_ACTION // META_MASKSCALEPART_ACTION // META_HATCH_ACTION // META_WALLPAPER_ACTION // META_FILLCOLOR_ACTION // META_TEXTCOLOR_ACTION // META_TEXTFILLCOLOR_ACTION // META_TEXTALIGN_ACTION // META_MAPMODE_ACTION // META_FONT_ACTION // META_TRANSPARENT_ACTION // META_EPS_ACTION // META_REFPOINT_ACTION // META_TEXTLINECOLOR_ACTION // META_TEXTLINE_ACTION // META_FLOATTRANSPARENT_ACTION // META_LAYOUTMODE_ACTION // META_TEXTLANGUAGE_ACTION // META_OVERLINECOLOR_ACTION // if an action is not handled at all, it will simply get copied to the // target (see below). This is the default for all non-implemented actions default: { break; } } } if(bDone) { bChanged = true; } else { const_cast< MetaAction* >(pAction)->Duplicate(); aTarget.AddAction(const_cast< MetaAction* >(pAction)); } } if(bChanged) { // when changed, copy back and do not forget to set MapMode // and PrefSize aTarget.SetPrefMapMode(rSource.GetPrefMapMode()); aTarget.SetPrefSize(rSource.GetPrefSize()); rSource = aTarget; } } ////////////////////////////////////////////////////////////////////////////// bool VCL_DLLPUBLIC usesClipActions(const GDIMetaFile& rSource) { const sal_uLong nObjCount(rSource.GetActionCount()); for(sal_uLong i(0); i < nObjCount; ++i) { const MetaAction* pAction(rSource.GetAction(i)); const sal_uInt16 nType(pAction->GetType()); switch(nType) { case META_CLIPREGION_ACTION : case META_ISECTRECTCLIPREGION_ACTION : case META_ISECTREGIONCLIPREGION_ACTION : case META_MOVECLIPREGION_ACTION : { return true; break; } default: break; } } return false; } ////////////////////////////////////////////////////////////////////////////// // eof