blob: 980dd887545e6d9e55c20e47bf1639093c441b05 [file] [log] [blame]
/**************************************************************
*
* 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 <vcl/gdimetafiletools.hxx>
#include <vcl/metaact.hxx>
#include <basegfx/polygon/b2dpolygonclipper.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <vcl/virdev.hxx>
#include <vcl/svapp.hxx>
#include <vcl/graphictools.hxx>
//////////////////////////////////////////////////////////////////////////////
// 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 :
{
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