void PropertyHolders::PushDefault()
{
PropertyHolder* pNew = new PropertyHolder();
maPropertyHolders.push_back(pNew);
}
void PropertyHolders::Push(vcl::PushFlags nPushFlags)
{ if (bool(nPushFlags))
{
OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: PUSH with no property holders (!)"); if (!maPropertyHolders.empty())
{
PropertyHolder* pNew = new PropertyHolder(*maPropertyHolders.back());
pNew->setPushFlags(nPushFlags);
maPropertyHolders.push_back(pNew);
}
}
}
void PropertyHolders::Pop()
{
OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: POP with no property holders (!)"); const sal_uInt32 nSize(maPropertyHolders.size());
if (nPushFlags != vcl::PushFlags::NONE)
{ if (nSize > 1)
{ // copy back content for all non-set flags
PropertyHolder* pLast = maPropertyHolders[nSize - 2];
if (vcl::PushFlags::ALL != nPushFlags)
{ if (!(nPushFlags & vcl::PushFlags::LINECOLOR))
{
pLast->setLineColor(pTip->getLineColor());
pLast->setLineColorActive(pTip->getLineColorActive());
} if (!(nPushFlags & vcl::PushFlags::FILLCOLOR))
{
pLast->setFillColor(pTip->getFillColor());
pLast->setFillColorActive(pTip->getFillColorActive());
} if (!(nPushFlags & vcl::PushFlags::FONT))
{
pLast->setFont(pTip->getFont());
} if (!(nPushFlags & vcl::PushFlags::TEXTCOLOR))
{
pLast->setTextColor(pTip->getTextColor());
pLast->setTextColorActive(pTip->getTextColorActive());
} if (!(nPushFlags & vcl::PushFlags::MAPMODE))
{
pLast->setTransformation(pTip->getTransformation());
pLast->setMapUnit(pTip->getMapUnit());
} if (!(nPushFlags & vcl::PushFlags::CLIPREGION))
{
pLast->setClipPolyPolygon(pTip->getClipPolyPolygon());
pLast->setClipPolyPolygonActive(pTip->getClipPolyPolygonActive());
} if (!(nPushFlags & vcl::PushFlags::RASTEROP))
{
pLast->setRasterOp(pTip->getRasterOp());
} if (!(nPushFlags & vcl::PushFlags::TEXTFILLCOLOR))
{
pLast->setTextFillColor(pTip->getTextFillColor());
pLast->setTextFillColorActive(pTip->getTextFillColorActive());
} if (!(nPushFlags & vcl::PushFlags::TEXTALIGN))
{ if (pLast->getFont().GetAlignment() != pTip->getFont().GetAlignment())
{
vcl::Font aFont(pLast->getFont());
aFont.SetAlignment(pTip->getFont().GetAlignment());
pLast->setFont(aFont);
}
} if (!(nPushFlags & vcl::PushFlags::REFPOINT))
{ // not supported
} if (!(nPushFlags & vcl::PushFlags::TEXTLINECOLOR))
{
pLast->setTextLineColor(pTip->getTextLineColor());
pLast->setTextLineColorActive(pTip->getTextLineColorActive());
} if (!(nPushFlags & vcl::PushFlags::TEXTLAYOUTMODE))
{
pLast->setLayoutMode(pTip->getLayoutMode());
} if (!(nPushFlags & vcl::PushFlags::TEXTLANGUAGE))
{
pLast->setLanguageType(pTip->getLanguageType());
} if (!(nPushFlags & vcl::PushFlags::OVERLINECOLOR))
{
pLast->setOverlineColor(pTip->getOverlineColor());
pLast->setOverlineColorActive(pTip->getOverlineColorActive());
}
}
}
}
// execute the pop delete maPropertyHolders.back();
maPropertyHolders.pop_back();
}
PropertyHolder& PropertyHolders::Current()
{ static PropertyHolder aDummy;
OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: CURRENT with no property holders (!)"); return maPropertyHolders.empty() ? aDummy : *maPropertyHolders.back();
}
namespace
{ /** helper to convert a vcl::Region to a B2DPolyPolygon whenitdoesnotyetcontainone.Inthefuture thismaybeexpandedtomergethepolygonscreated fromrectanglesoruseaspecialalgotodirectlyturn thespansofregionstoasingle,alreadymerged PolyPolygon.
*/
basegfx::B2DPolyPolygon getB2DPolyPolygonFromRegion(const vcl::Region& rRegion)
{
basegfx::B2DPolyPolygon aRetval;
if (!rRegion.IsEmpty())
{
aRetval = rRegion.GetAsB2DPolyPolygon();
}
return aRetval;
}
}
namespace wmfemfhelper
{ /** Helper class to buffer and hold a Primitive target vector. It encapsulatesthenew/deletefunctionalityandallowstowork onpointersoftheimplementationclasses.Alldatawill beconvertedtounosequencesofunoreferenceswhenaccessingthe data.
*/
TargetHolder::TargetHolder()
{
}
if (!xRetval.empty() && rPropertyHolder.getClipPolyPolygonActive())
{ const basegfx::B2DPolyPolygon& rClipPolyPolygon = rPropertyHolder.getClipPolyPolygon();
if (rClipPolyPolygon.count())
{
xRetval = drawinglayer::primitive2d::Primitive2DContainer{ new drawinglayer::primitive2d::MaskPrimitive2D(
rClipPolyPolygon,
std::move(xRetval))
};
}
}
return xRetval;
}
}
namespace wmfemfhelper
{ /** Helper class which builds a stack on the TargetHolder class */
TargetHolders::TargetHolders()
{
maTargetHolders.push_back(new TargetHolder());
}
void TargetHolders::Pop()
{
OSL_ENSURE(maTargetHolders.size(), "TargetHolders: POP with no property holders (!)"); if (!maTargetHolders.empty())
{ delete maTargetHolders.back();
maTargetHolders.pop_back();
}
}
TargetHolder& TargetHolders::Current()
{ static TargetHolder aDummy;
OSL_ENSURE(maTargetHolders.size(), "TargetHolders: CURRENT with no property holders (!)"); return maTargetHolders.empty() ? aDummy : *maTargetHolders.back();
}
/** helper to create needed line and fill primitives based on current context */ staticvoid createHairlineAndFillPrimitive( const basegfx::B2DPolygon& rPolygon,
TargetHolder& rTarget,
PropertyHolder const & rProperties)
{ if(rProperties.getFillColorActive())
{
createFillPrimitive(basegfx::B2DPolyPolygon(rPolygon), rTarget, rProperties);
}
/** helper to create needed line and fill primitives based on current context */ staticvoid createHairlineAndFillPrimitive( const basegfx::B2DPolyPolygon& rPolyPolygon,
TargetHolder& rTarget,
PropertyHolder const & rProperties)
{ if(rProperties.getFillColorActive())
{
createFillPrimitive(rPolyPolygon, rTarget, rProperties);
}
/** helper to convert from a VCL Gradient definition to the corresponding dataforprimitiverepresentation
*/ static drawinglayer::attribute::FillGradientAttribute createFillGradientAttribute(const Gradient& rGradient)
{ const Color aStartColor(rGradient.GetStartColor()); const sal_uInt16 nStartIntens(rGradient.GetStartIntensity());
basegfx::BColor aStart(aStartColor.getBColor());
/** helper to convert from a VCL Hatch definition to the corresponding dataforprimitiverepresentation
*/ static drawinglayer::attribute::FillHatchAttribute createFillHatchAttribute(const Hatch& rHatch)
{
drawinglayer::attribute::HatchStyle aHatchStyle(drawinglayer::attribute::HatchStyle::Single);
return drawinglayer::attribute::FillHatchAttribute(
aHatchStyle, static_cast<double>(rHatch.GetDistance()),
toRadians(rHatch.GetAngle()),
rHatch.GetColor().getBColor(), 3, // same default as VCL, a minimum of three discrete units (pixels) offset false);
}
/** helper to take needed action on ClipRegion change. This method needs to be called onanyvcl::Regionchange,e.g.attheobviousactionsdoingthis,butalsoatpop-calls whichchangethevcl::Regionofthecurrentcontext.Ittakescareofcreatingthe currentembeddedcontext,setthenewvcl::Regionatthecontextandpossiblyprepare anewtargetforincludingnewgeometryintothecurrentregion
*/ void HandleNewClipRegion( const basegfx::B2DPolyPolygon& rClipPolyPolygon,
TargetHolders& rTargetHolders,
PropertyHolders& rPropertyHolders)
{ constbool bNewActive(rClipPolyPolygon.count());
// #i108636# The handling of new ClipPolyPolygons was not done as good as possible // in the first version of this interpreter; e.g. when a ClipPolyPolygon was set // initially and then using a lot of push/pop actions, the pop always leads // to setting a 'new' ClipPolyPolygon which indeed is the return to the ClipPolyPolygon // of the properties next on the stack.
// This ClipPolyPolygon is identical to the current one, so there is no need to // create a MaskPrimitive2D containing the up-to-now created primitives, but // this was done before. While this does not lead to wrong primitive // representations of the metafile data, it creates unnecessarily expensive // representations. Just detecting when no really 'new' ClipPolyPolygon gets set // solves the problem.
if(!rPropertyHolders.Current().getClipPolyPolygonActive() && !bNewActive)
{ // no active ClipPolyPolygon exchanged by no new one, done return;
}
if(rPropertyHolders.Current().getClipPolyPolygonActive() && bNewActive)
{ // active ClipPolyPolygon and new active ClipPolyPolygon if(rPropertyHolders.Current().getClipPolyPolygon() == rClipPolyPolygon)
{ // new is the same as old, done return;
}
}
// Here the old and the new are definitively different, maybe // old one and/or new one is not active.
// Handle deletion of old ClipPolyPolygon. The process evtl. created primitives which // belong to this active ClipPolyPolygon. These need to be embedded to a // MaskPrimitive2D accordingly. if(rPropertyHolders.Current().getClipPolyPolygonActive() && rTargetHolders.size() > 1)
{
drawinglayer::primitive2d::Primitive2DContainer aSubContent;
// prepare new content holder for new active region
rTargetHolders.Push();
}
}
/** helper to handle the change of RasterOp. It takes care of encapsulating all current geometrytothecurrentRasterOp(ifchanged)andneedstobecalledonanyRasterOp change.ItwillalsostartanewgeometrytargettoembracetothenewRasterOpif achangingRasterOpisused.Currently,RasterOp::XorandRasterOp::Invertaresupportedusing InvertPrimitive2D,andRasterOp::N0byusingaModifiedColorPrimitive2Dtoforcetoblackpaint
*/ staticvoid HandleNewRasterOp(
RasterOp aRasterOp,
TargetHolders& rTargetHolders,
PropertyHolders& rPropertyHolders)
{ // check if currently active if(rPropertyHolders.Current().isRasterOpActive() && rTargetHolders.size() > 1)
{
drawinglayer::primitive2d::Primitive2DContainer aSubContent;
// use wallpaper rect if set if(rWallpaper.IsRect() && !rWallpaper.GetRect().IsEmpty())
{
aWallpaperRange = vcl::unotools::b2DRectangleFromRectangle(rWallpaper.GetRect());
}
rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBitmapWallpaperFill = new drawinglayer::primitive2d::WallpaperBitmapPrimitive2D(
aWallpaperRange,
aBitmapEx,
eWallpaperStyle);
if(rProperty.getTransformation().isIdentity())
{ // add directly
rTarget.append(pBitmapWallpaperFill);
} else
{ // when a transformation is set, embed to it
rTarget.append( new drawinglayer::primitive2d::TransformPrimitive2D(
rProperty.getTransformation(),
drawinglayer::primitive2d::Primitive2DContainer { pBitmapWallpaperFill }));
}
}
/** helper to decide UnderlineAbove for text primitives */ staticbool isUnderlineAbove(const vcl::Font& rFont)
{ if(!rFont.IsVertical())
{ returnfalse;
}
// the underline is right for Japanese only return (LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage());
}
// take text align into account if(ALIGN_BASELINE != rFont.GetAlignment())
{
drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
aTextLayouterDevice.setFont(rFont);
if(pResult && rProperty.getTextFillColorActive())
{ // text background is requested, add and encapsulate both to new primitive
drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
aTextLayouterDevice.setFont(rFont);
aSequence[0] = drawinglayer::primitive2d::Primitive2DReference( new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
basegfx::B2DPolyPolygon(aOutline),
rProperty.getTextFillColor()));
// set as group at pResult
pResult = new drawinglayer::primitive2d::GroupPrimitive2D(std::move(aSequence));
}
}
if(!pResult) return;
// add created text primitive to target if(rProperty.getTransformation().isIdentity())
{
rTarget.append(pResult);
} else
{ // when a transformation is set, embed to it
rTarget.append( new drawinglayer::primitive2d::TransformPrimitive2D(
rProperty.getTransformation(),
drawinglayer::primitive2d::Primitive2DContainer { pResult }));
}
}
/** helper which takes complete care for creating the needed textLine primitives */ staticvoid processMetaTextLineAction( const MetaTextLineAction& rAction,
TargetHolder& rTarget,
PropertyHolder const & rProperty)
{ constdouble fLineWidth(fabs(static_cast<double>(rAction.GetWidth())));
aTargets.push_back( new drawinglayer::primitive2d::TextCharacterStrikeoutPrimitive2D(
aTextTransform,
fLineWidth,
rProperty.getTextColor(),
aStrikeoutChar,
std::move(aFontAttribute),
std::move(aLocale)));
} else
{ // strikeout with geometry
aTargets.push_back( new drawinglayer::primitive2d::TextGeometryStrikeoutPrimitive2D(
aTextTransform,
fLineWidth,
rProperty.getTextColor(),
aTextLayouter.getUnderlineHeight(),
aTextLayouter.getStrikeoutOffset(),
aTextStrikeout));
}
}
if(aTargets.empty()) return;
// add created text primitive to target if(rProperty.getTransformation().isIdentity())
{
rTarget.append(std::move(aTargets));
} else
{ // when a transformation is set, embed to it
rTarget.append( new drawinglayer::primitive2d::TransformPrimitive2D(
rProperty.getTransformation(),
std::move(aTargets)));
}
}
/** This is the main interpreter method. It is designed to handle the given Metafile completelyinsidethegivencontextandtarget.Itmayuseandmodifythecontextand target.Thisdesignallowstocallitselfrecursivelywhichadaptedcontextsand targetsase.g.neededfortheMetaActionType::FLOATTRANSPARENTwherethecontentisexpressed asametafileassub-content.
break;
} case MetaActionType::ROUNDRECT :
{ /** CHECKED, WORKS WELL */ /** The original OutputDevice::DrawRect paints nothing when nHor or nVer is zero; but just becausethetools::Polygonoperatorcreatingtheroundingdoesproducenonsense.Iassume thisanerrorandcreateanunroundedrectangleinthatcase(implicitin createPolygonFromRect)
*/ if(rPropertyHolders.Current().getLineOrFillActive())
{ const MetaRoundRectAction* pA = static_cast<const MetaRoundRectAction*>(pAction); const tools::Rectangle& rRectangle = pA->GetRect();
break;
} case MetaActionType::POLYLINE :
{ /** CHECKED, WORKS WELL */ if(rPropertyHolders.Current().getLineColorActive())
{ const MetaPolyLineAction* pA = static_cast<const MetaPolyLineAction*>(pAction);
createLinePrimitive(pA->GetPolygon().getB2DPolygon(), pA->GetLineInfo(), rTargetHolders.Current(), rPropertyHolders.Current());
}
break;
} case MetaActionType::POLYGON :
{ /** CHECKED, WORKS WELL */ if(rPropertyHolders.Current().getLineOrFillActive())
{ const MetaPolygonAction* pA = static_cast<const MetaPolygonAction*>(pAction);
basegfx::B2DPolygon aOutline(pA->GetPolygon().getB2DPolygon());
// the metafile play interprets the polygons from MetaPolygonAction // always as closed and always paints an edge from last to first point, // so force to closed here to emulate that if(aOutline.count() > 1 && !aOutline.isClosed())
{
aOutline.setClosed(true);
}
break;
} case MetaActionType::POLYPOLYGON :
{ /** CHECKED, WORKS WELL */ if(rPropertyHolders.Current().getLineOrFillActive())
{ const MetaPolyPolygonAction* pA = static_cast<const MetaPolyPolygonAction*>(pAction);
basegfx::B2DPolyPolygon aPolyPolygonOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
// the metafile play interprets the single polygons from MetaPolyPolygonAction // always as closed and always paints an edge from last to first point, // so force to closed here to emulate that for(sal_uInt32 b(0); b < aPolyPolygonOutline.count(); b++)
{
basegfx::B2DPolygon aPolygonOutline(aPolyPolygonOutline.getB2DPolygon(b));
break;
} case MetaActionType::STRETCHTEXT :
{ // #i108440# StarMath uses MetaStretchTextAction, thus support is needed. // It looks as if it pretty never really uses a width different from // the default text-layout width, but it's not possible to be sure. // Implemented getting the DXArray and checking for scale at all. If // scale is more than 3.5% different, scale the DXArray before usage. // New status:
/** CHECKED, WORKS WELL */ const MetaStretchTextAction* pA = static_cast<const MetaStretchTextAction*>(pAction);
sal_uInt32 nTextLength(pA->GetLen()); const sal_uInt32 nTextIndex(pA->GetIndex()); const sal_uInt32 nStringLength(pA->GetText().getLength());
if(fabs(fRelative - 1.0) >= 0.035)
{ // when derivation is more than 3,5% from default text size, // scale the DXArray for(double & a : aTextArray)
{
a *= fRelative;
}
}
}
}
break;
} case MetaActionType::TEXTRECT :
{ /** CHECKED, WORKS WELL */ // OSL_FAIL("MetaActionType::TEXTRECT requested (!)"); const MetaTextRectAction* pA = static_cast<const MetaTextRectAction*>(pAction); const tools::Rectangle& rRectangle = pA->GetRect(); const sal_uInt32 nStringLength(pA->GetText().getLength());
if(!rRectangle.IsEmpty() && 0 != nStringLength)
{ // The problem with this action is that it describes unlayouted text // and the layout capabilities are in EditEngine/Outliner in SVX. The // same problem is true for VCL which internally has implementations // to layout text in this case. There exists even a call // OutputDevice::AddTextRectActions(...) to create the needed actions // as 'sub-content' of a Metafile. Unfortunately i do not have an // OutputDevice here since this interpreter tries to work without // VCL AFAP. // Since AddTextRectActions is the only way as long as we do not have // a simple text layouter available, i will try to add it to the // TextLayouterDevice isolation.
drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont());
GDIMetaFile aGDIMetaFile;
// for sub-Mteafile contents, do start with new, default render state // #i124686# ...but copy font, this is already set accordingly
vcl::Font aTargetFont = rPropertyHolders.Current().getFont();
rPropertyHolders.PushDefault();
rPropertyHolders.Current().setFont(aTargetFont);
if(!xSubContent.empty())
{ // add with transformation
rTargetHolders.Current().append( new drawinglayer::primitive2d::TransformPrimitive2D(
rPropertyHolders.Current().getTransformation(),
std::move(xSubContent)));
}
}
}
break;
} case MetaActionType::BMP :
{ /** CHECKED, WORKS WELL */ const MetaBmpAction* pA = static_cast<const MetaBmpAction*>(pAction); const BitmapEx aBitmapEx(pA->GetBitmap());
break;
} case MetaActionType::BMPSCALE :
{ /** CHECKED, WORKS WELL */ const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction); const BitmapEx aBitmapEx(pA->GetBitmap());
break;
} case MetaActionType::MASK :
{ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMP */ /** Huh, no it isn't!? */ const MetaMaskAction* pA = static_cast<const MetaMaskAction*>(pAction); const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
break;
} case MetaActionType::MASKSCALE :
{ /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALE */ const MetaMaskScaleAction* pA = static_cast<const MetaMaskScaleAction*>(pAction); const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
if (aAttribute.getColorStops().isSingleColor(aSingleColor))
{ // not really a gradient. Create filled rectangle
createFillPrimitive(
aOutline,
rTargetHolders.Current(),
rPropertyHolders.Current());
} else
{ // really a gradient
aRange.transform(rPropertyHolders.Current().getTransformation());
drawinglayer::primitive2d::Primitive2DContainer xGradient(1);
if(rPropertyHolders.Current().isRasterOpInvert())
{ // use a special version of FillGradientPrimitive2D which creates // non-overlapping geometry on decomposition to make the old XOR // paint 'trick' work.
xGradient[0] = drawinglayer::primitive2d::Primitive2DReference( new drawinglayer::primitive2d::NonOverlappingFillGradientPrimitive2D(
aRange,
aAttribute));
} else
{
xGradient[0] = drawinglayer::primitive2d::Primitive2DReference( new drawinglayer::primitive2d::FillGradientPrimitive2D(
aRange,
std::move(aAttribute)));
}
// #i112300# clip against polygon representing the rectangle from // the action. This is implicitly done using a temp Clipping in VCL // when a MetaGradientAction is executed
aOutline.transform(rPropertyHolders.Current().getTransformation());
rTargetHolders.Current().append( new drawinglayer::primitive2d::MaskPrimitive2D(
std::move(aOutline),
std::move(xGradient)));
}
}
}
break;
} case MetaActionType::HATCH :
{ /** CHECKED, WORKS WELL */ const MetaHatchAction* pA = static_cast<const MetaHatchAction*>(pAction);
basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
rTargetHolders.Current().append( new drawinglayer::primitive2d::MaskPrimitive2D(
std::move(aOutline),
drawinglayer::primitive2d::Primitive2DContainer { aFillHatch }));
}
break;
} case MetaActionType::WALLPAPER :
{ /** CHECKED, WORKS WELL */ const MetaWallpaperAction* pA = static_cast<const MetaWallpaperAction*>(pAction);
tools::Rectangle aWallpaperRectangle(pA->GetRect());
if(WallpaperStyle::NONE != eWallpaperStyle)
{ if(rWallpaper.IsBitmap())
{ // create bitmap background. Caution: This // also will create gradient/color background(s) // when the bitmap is transparent or not tiled
CreateAndAppendBitmapWallpaper(
aWallpaperRange,
rWallpaper,
rTargetHolders.Current(),
rPropertyHolders.Current());
} elseif(rWallpaper.IsGradient())
{ // create gradient background
rTargetHolders.Current().append(
CreateGradientWallpaper(
aWallpaperRange,
rWallpaper.GetGradient(),
rPropertyHolders.Current()));
} elseif(!rWallpaper.GetColor().IsTransparent())
{ // create color background
rTargetHolders.Current().append(
CreateColorWallpaper(
aWallpaperRange,
rWallpaper.GetColor().getBColor(),
rPropertyHolders.Current()));
}
}
}
break;
} case MetaActionType::CLIPREGION :
{ /** CHECKED, WORKS WELL */ const MetaClipRegionAction* pA = static_cast<const MetaClipRegionAction*>(pAction);
if(pA->IsClipping())
{ // new clipping. Get tools::PolyPolygon and transform with current transformation
basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(pA->GetRegion()));
break;
} case MetaActionType::ISECTRECTCLIPREGION :
{ /** CHECKED, WORKS WELL */ const MetaISectRectClipRegionAction* pA = static_cast<const MetaISectRectClipRegionAction*>(pAction); const tools::Rectangle& rRectangle = pA->GetRect();
if(rRectangle.IsEmpty())
{ // intersect with empty rectangle will always give empty // ClipPolyPolygon; start new clipping with empty PolyPolygon const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
if(rPropertyHolders.Current().getClipPolyPolygonActive())
{ if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
{ // nothing to do, empty active clipPolyPolygon will stay // empty when intersecting
} else
{ // AND existing region and new ClipRange const basegfx::B2DPolyPolygon aOriginalPolyPolygon(
rPropertyHolders.Current().getClipPolyPolygon());
basegfx::B2DPolyPolygon aClippedPolyPolygon;
break;
} case MetaActionType::ISECTREGIONCLIPREGION :
{ /** CHECKED, WORKS WELL */ const MetaISectRegionClipRegionAction* pA = static_cast<const MetaISectRegionClipRegionAction*>(pAction); const vcl::Region& rNewRegion = pA->GetRegion();
if(rNewRegion.IsEmpty())
{ // intersect with empty region will always give empty // region; start new clipping with empty PolyPolygon const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
} else
{ // get new ClipPolyPolygon, transform it with current transformation
basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(rNewRegion));
aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
if(rPropertyHolders.Current().getClipPolyPolygonActive())
{ if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
{ // nothing to do, empty active clipPolyPolygon will stay empty // when intersecting with any region
} else
{ // AND existing and new region const basegfx::B2DPolyPolygon aOriginalPolyPolygon(
rPropertyHolders.Current().getClipPolyPolygon());
basegfx::B2DPolyPolygon aClippedPolyPolygon;
if(aClippedPolyPolygon != aOriginalPolyPolygon)
{ // start new clipping with intersected ClipPolyPolygon
HandleNewClipRegion(aClippedPolyPolygon, rTargetHolders, rPropertyHolders);
}
}
} else
{ // start new clipping with new ClipPolyPolygon
HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
}
}
break;
} case MetaActionType::MOVECLIPREGION :
{ /** CHECKED, WORKS WELL */ const MetaMoveClipRegionAction* pA = static_cast<const MetaMoveClipRegionAction*>(pAction);
if(rPropertyHolders.Current().getClipPolyPolygonActive())
{ if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
{ // nothing to do
} else
{ const sal_Int32 nHor(pA->GetHorzMove()); const sal_Int32 nVer(pA->GetVertMove());
break;
} case MetaActionType::LINECOLOR :
{ /** CHECKED, WORKS WELL */ const MetaLineColorAction* pA = static_cast<const MetaLineColorAction*>(pAction); // tdf#89901 do as OutDev does: COL_TRANSPARENT deactivates line draw constbool bActive(pA->IsSetting() && COL_TRANSPARENT != pA->GetColor());
break;
} case MetaActionType::FILLCOLOR :
{ /** CHECKED, WORKS WELL */ const MetaFillColorAction* pA = static_cast<const MetaFillColorAction*>(pAction); // tdf#89901 do as OutDev does: COL_TRANSPARENT deactivates polygon fill constbool bActive(pA->IsSetting() && COL_TRANSPARENT != pA->GetColor());
// TextAlign is applied to the current font (as in // OutputDevice::SetTextAlign which would be used when // playing the Metafile) if(rPropertyHolders.Current().getFont().GetAlignment() != aNewTextAlign)
{
vcl::Font aNewFont(rPropertyHolders.Current().getFont());
aNewFont.SetAlignment(aNewTextAlign);
rPropertyHolders.Current().setFont(aNewFont);
}
break;
} case MetaActionType::MAPMODE :
{ /** CHECKED, WORKS WELL */ // the most necessary MapMode to be interpreted is MapUnit::MapRelative, // but also the others may occur. Even not yet supported ones // may need to be added here later const MetaMapModeAction* pA = static_cast<const MetaMapModeAction*>(pAction); const MapMode& rMapMode = pA->GetMapMode();
basegfx::B2DHomMatrix aMapping;
if(MapUnit::MapRelative == rMapMode.GetMapUnit())
{
aMapping = getTransformFromMapMode(rMapMode);
} else
{ constauto eFrom = MapToO3tlLength(rPropertyHolders.Current().getMapUnit()),
eTo = MapToO3tlLength(rMapMode.GetMapUnit()); if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
{ constdouble fConvert(o3tl::convert(1.0, eFrom, eTo));
aMapping.scale(fConvert, fConvert);
} else
OSL_FAIL("implInterpretMetafile: MetaActionType::MAPMODE with unsupported MapUnit (!)");
break;
} case MetaActionType::FONT :
{ /** SIMPLE, DONE */ const MetaFontAction* pA = static_cast<const MetaFontAction*>(pAction);
rPropertyHolders.Current().setFont(pA->GetFont());
Size aFontSize(pA->GetFont().GetFontSize());
if(0 == aFontSize.Height())
{ // this should not happen but i got Metafiles where this was the // case. A height needs to be guessed (similar to OutputDevice::ImplNewFont())
vcl::Font aCorrectedFont(pA->GetFont());
// convert to target MapUnit if not pixels
aFontSize = OutputDevice::LogicToLogic(
aFontSize, MapMode(MapUnit::MapPixel), MapMode(rPropertyHolders.Current().getMapUnit()));
// older Metafiles have no MetaActionType::TEXTCOLOR which defines // the FontColor now, so use the Font's color when not transparent const Color& rFontColor = pA->GetFont().GetColor(); constbool bActivate(COL_TRANSPARENT != rFontColor);
// caution: do NOT deactivate here on transparent, see // OutputDevice::SetFont(..) for more info // rPropertyHolders.Current().setTextColorActive(bActivate);
// for fill color emulate a MetaTextFillColorAction with !transparent as bool, // see OutputDevice::SetFont(..) the if(mpMetaFile) case if(bActivate)
{ const Color& rFontFillColor = pA->GetFont().GetFillColor();
rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor());
rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor);
} else
{
rPropertyHolders.Current().setTextFillColorActive(false);
}
break;
} case MetaActionType::PUSH :
{ /** CHECKED, WORKS WELL */ const MetaPushAction* pA = static_cast<const MetaPushAction*>(pAction);
rPropertyHolders.Push(pA->GetFlags());
break;
} case MetaActionType::POP :
{ /** CHECKED, WORKS WELL */ constbool bRegionMayChange(rPropertyHolders.Current().getPushFlags() & vcl::PushFlags::CLIPREGION); constbool bRasterOpMayChange(rPropertyHolders.Current().getPushFlags() & vcl::PushFlags::RASTEROP);
break;
} case MetaActionType::Transparent :
{ /** CHECKED, WORKS WELL */ const MetaTransparentAction* pA = static_cast<const MetaTransparentAction*>(pAction); const basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
if(0 == nTransparence)
{ // not transparent
createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
} elseif(nTransparence >= 100)
{ // fully or more than transparent
} else
{ // transparent. Create new target
rTargetHolders.Push();
// create primitives there and get them
createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
drawinglayer::primitive2d::Primitive2DContainer aSubContent(
rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current()));
break;
} case MetaActionType::EPS :
{ /** CHECKED, WORKS WELL */ // To support this action, I have added a EpsPrimitive2D which will // by default decompose to the Metafile replacement data. To support // this EPS on screen, the renderer visualizing this has to support // that primitive and visualize the Eps file (e.g. printing) const MetaEPSAction* pA = static_cast<const MetaEPSAction*>(pAction); const tools::Rectangle aRectangle(pA->GetPoint(), pA->GetSize());
// add current transformation
aObjectTransform = rPropertyHolders.Current().getTransformation() * aObjectTransform;
// embed using EpsPrimitive
rTargetHolders.Current().append( new drawinglayer::primitive2d::EpsPrimitive2D(
aObjectTransform,
pA->GetLink(),
pA->GetSubstitute()));
}
break;
} case MetaActionType::REFPOINT :
{ /** SIMPLE, DONE */ // only used for hatch and line pattern offsets, pretty much no longer // supported today // const MetaRefPointAction* pA = (const MetaRefPointAction*)pAction; break;
} case MetaActionType::TEXTLINECOLOR :
{ /** SIMPLE, DONE */ const MetaTextLineColorAction* pA = static_cast<const MetaTextLineColorAction*>(pAction); constbool bActive(pA->IsSetting());
break;
} case MetaActionType::TEXTLINE :
{ /** CHECKED, WORKS WELL */ // actually creates overline, underline and strikeouts, so // these should be isolated from TextDecoratedPortionPrimitive2D // to own primitives. Done, available now. // // This Metaaction seems not to be used (was not used in any // checked files). It's used in combination with the current // Font. const MetaTextLineAction* pA = static_cast<const MetaTextLineAction*>(pAction);
if(rContent.GetActionSize())
{ // create the sub-content with no embedding specific to the // sub-metafile, this seems not to be used.
drawinglayer::primitive2d::Primitive2DContainer xSubContent;
{
rTargetHolders.Push(); // #i# for sub-Mteafile contents, do start with new, default render state
rPropertyHolders.PushDefault();
implInterpretMetafile(rContent, rTargetHolders, rPropertyHolders, rViewInformation);
xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
rPropertyHolders.Pop();
rTargetHolders.Pop();
}
// check if gradient is a real gradient const Gradient& rGradient = pA->GetGradient();
drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
basegfx::BColor aSingleColor;
if (aAttribute.getColorStops().isSingleColor(aSingleColor))
{ // not really a gradient; create UnifiedTransparencePrimitive2D
rTargetHolders.Current().append( new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
std::move(xSubContent),
aSingleColor.luminance()));
} else
{ // really a gradient. Create gradient sub-content (with correct scaling)
basegfx::B2DRange aRange(aTargetRange);
aRange.transform(rPropertyHolders.Current().getTransformation());
// prepare gradient for transparent content const drawinglayer::primitive2d::Primitive2DReference xTransparence( new drawinglayer::primitive2d::FillGradientPrimitive2D(
aRange,
std::move(aAttribute)));
break;
} case MetaActionType::GRADIENTEX :
{ /** SIMPLE, DONE */ // This is only a data holder which is interpreted inside comment actions, // see MetaActionType::COMMENT for more info // const MetaGradientExAction* pA = (const MetaGradientExAction*)pAction; break;
} case MetaActionType::LAYOUTMODE :
{ /** SIMPLE, DONE */ const MetaLayoutModeAction* pA = static_cast<const MetaLayoutModeAction*>(pAction);
rPropertyHolders.Current().setLayoutMode(pA->GetLayoutMode()); break;
} case MetaActionType::TEXTLANGUAGE :
{ /** SIMPLE, DONE */ const MetaTextLanguageAction* pA = static_cast<const MetaTextLanguageAction*>(pAction);
rPropertyHolders.Current().setLanguageType(pA->GetTextLanguage()); break;
} case MetaActionType::OVERLINECOLOR :
{ /** SIMPLE, DONE */ const MetaOverlineColorAction* pA = static_cast<const MetaOverlineColorAction*>(pAction); constbool bActive(pA->IsSetting());
break;
} case MetaActionType::COMMENT :
{ /** CHECKED, WORKS WELL */ // I already implemented // XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END // XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END, // but opted to remove these again; it works well without them // and makes the code less dependent from those Metafile Add-Ons const MetaCommentAction* pA = static_cast<const MetaCommentAction*>(pAction);
if (pA->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
{ // XGRAD_SEQ_BEGIN, XGRAD_SEQ_END should be supported since the // pure recorded paint of the gradients uses the XOR paint functionality // ('trick'). This is (and will be) problematic with AntiAliasing, so it's // better to use this info const MetaGradientExAction* pMetaGradientExAction = nullptr; bool bDone(false);
size_t b(nAction + 1);
// get and check if gradient is a real gradient const Gradient& rGradient = pMetaGradientExAction->GetGradient();
drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
basegfx::BColor aSingleColor;
if (aAttribute.getColorStops().isSingleColor(aSingleColor))
{ // not really a gradient
rTargetHolders.Current().append( new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
std::move(aPolyPolygon),
aSingleColor));
} else
{ // really a gradient
rTargetHolders.Current().append( new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D(
aPolyPolygon,
std::move(aAttribute)));
}
}
}
} elseif (pA->GetComment().equalsIgnoreAsciiCase("EMF_PLUS_HEADER_INFO"))
{ if (aEMFPlus)
{ // error: should not yet exist
SAL_INFO("drawinglayer.emf", "Error: multiple EMF_PLUS_HEADER_INFO");
} else
{
SAL_INFO("drawinglayer.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pA->GetDataSize());
SvMemoryStream aMemoryStream(const_cast<sal_uInt8 *>(pA->GetData()), pA->GetDataSize(), StreamMode::READ);
aEMFPlus.reset( new emfplushelper::EmfPlusHelper(
aMemoryStream,
rTargetHolders,
rPropertyHolders));
}
} elseif (pA->GetComment().equalsIgnoreAsciiCase("EMF_PLUS"))
{ if (!aEMFPlus)
{ // error: should exist
SAL_INFO("drawinglayer.emf", "Error: EMF_PLUS before EMF_PLUS_HEADER_INFO");
} else
{ staticint count = -1, limit = 0x7fffffff;
if (count == -1)
{
count = 0;
if (char *env = getenv("EMF_PLUS_LIMIT"))
{
limit = atoi(env);
SAL_INFO("drawinglayer.emf", "EMF+ records limit: " << limit);
}
}
namespace wmfemfhelper
{
drawinglayer::primitive2d::Primitive2DContainer interpretMetafile( const GDIMetaFile& rMetaFile, const drawinglayer::geometry::ViewInformation2D& rViewInformation)
{ // prepare target and properties; each will have one default entry
drawinglayer::primitive2d::Primitive2DContainer xRetval;
TargetHolders aTargetHolders;
PropertyHolders aPropertyHolders;
// set target MapUnit at Properties
aPropertyHolders.Current().setMapUnit(rMetaFile.GetPrefMapMode().GetMapUnit());
// interpret the Metafile
implInterpretMetafile(rMetaFile, aTargetHolders, aPropertyHolders, rViewInformation);
// get the content. There should be only one target, as in the start condition, // but iterating will be the right thing to do when some push/pop is not closed while (aTargetHolders.size() > 1)
{
xRetval.append(
aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
aTargetHolders.Pop();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.