/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */

#include "svdfmtf.hxx"
#include <math.h>
#include <editeng/eeitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/wghtitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/crossedoutitem.hxx>
#include <editeng/shdditem.hxx>
#include <svx/xlineit0.hxx>
#include <svx/xlnclit.hxx>
#include <svx/xlncapit.hxx>
#include <svx/xlnwtit.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xflclit.hxx>
#include <svx/xflgrit.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/wrlmitem.hxx>
#include <editeng/contouritem.hxx>
#include <editeng/colritem.hxx>
#include <vcl/alpha.hxx>
#include <vcl/canvastools.hxx>
#include <vcl/metric.hxx>
#include <editeng/charscaleitem.hxx>
#include <svx/xflhtit.hxx>
#include <svx/sdmetitm.hxx>
#include <svx/sdtagitm.hxx>
#include <svx/sdtaitm.hxx>
#include <svx/sdtditm.hxx>
#include <svx/sdtfsitm.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdobj.hxx>
#include <svx/svdotext.hxx>
#include <svx/svdorect.hxx>
#include <svx/svdocirc.hxx>
#include <svx/svdograf.hxx>
#include <svx/svdopath.hxx>
#include <svx/svdetc.hxx>
#include <svl/itemset.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <tools/helpers.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <svx/xlinjoit.hxx>
#include <svx/xlndsit.hxx>
#include <basegfx/polygon/b2dpolygonclipper.hxx>
#include <svx/xbtmpit.hxx>
#include <svx/xfltrit.hxx>
#include <svx/xflbmtit.hxx>
#include <svx/xflbstit.hxx>
#include <svx/svdpntv.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <svx/svditer.hxx>
#include <svx/svdogrp.hxx>
#include <vcl/BitmapTools.hxx>
#include <osl/diagnose.h>

using namespace com::sun::star;

ImpSdrGDIMetaFileImport::ImpSdrGDIMetaFileImport(
    SdrModel& rModel,
    SdrLayerID nLay,
    const tools::Rectangle& rRect)
:   mpVD(VclPtr<VirtualDevice>::Create()),
    maScaleRect(rRect),
    mpModel(&rModel),
    mnLayer(nLay),
    mnLineWidth(0),
    maLineJoin(basegfx::B2DLineJoin::NONE),
    maLineCap(css::drawing::LineCap_BUTT),
    maDash(css::drawing::DashStyle_RECT, 0, 0, 0, 0, 0),
    mbMov(false),
    mbSize(false),
    maOfs(0, 0),
    mfScaleX(1.0),
    mfScaleY(1.0),
    maScaleX(1.0),
    maScaleY(1.0),
    mbFntDirty(true),
    mbLastObjWasPolyWithoutLine(false),
    mbNoLine(false),
    mbNoFill(false),
    mbLastObjWasLine(false)
{
    mpVD->EnableOutput(false);
    mpVD->SetLineColor();
    mpVD->SetFillColor();
    maOldLineColor.SetRed( mpVD->GetLineColor().GetRed() + 1 );
    mpLineAttr = std::make_unique<SfxItemSetFixed<XATTR_LINE_FIRST, XATTR_LINE_LAST>>(rModel.GetItemPool());
    mpFillAttr = std::make_unique<SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST>>(rModel.GetItemPool());
    mpTextAttr = std::make_unique<SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END>>(rModel.GetItemPool());
    checkClip();
}

void ImpSdrGDIMetaFileImport::DoLoopActions(GDIMetaFile const & rMtf, SvdProgressInfo* pProgrInfo, sal_uInt32* pActionsToReport)
{
    const size_t nCount = rMtf.GetActionSize();

    for(size_t a = 0; a < nCount; a++)
    {
        MetaAction* pAct = rMtf.GetAction(a);

        if(!pAct)
        {
            OSL_ENSURE(false, "OOps, no action at valid position (!)");
            pAct = rMtf.GetAction(0);
        }

        switch (pAct->GetType())
        {
            case MetaActionType::PIXEL          : break;
            case MetaActionType::POINT          : break;
            case MetaActionType::LINE           : DoAction(static_cast<MetaLineAction           &>(*pAct)); break;
            case MetaActionType::RECT           : DoAction(static_cast<MetaRectAction           &>(*pAct)); break;
            case MetaActionType::ROUNDRECT      : DoAction(static_cast<MetaRoundRectAction      &>(*pAct)); break;
            case MetaActionType::ELLIPSE        : DoAction(static_cast<MetaEllipseAction        &>(*pAct)); break;
            case MetaActionType::ARC            : DoAction(static_cast<MetaArcAction            &>(*pAct)); break;
            case MetaActionType::PIE            : DoAction(static_cast<MetaPieAction            &>(*pAct)); break;
            case MetaActionType::CHORD          : DoAction(static_cast<MetaChordAction          &>(*pAct)); break;
            case MetaActionType::POLYLINE       : DoAction(static_cast<MetaPolyLineAction       &>(*pAct)); break;
            case MetaActionType::POLYGON        : DoAction(static_cast<MetaPolygonAction        &>(*pAct)); break;
            case MetaActionType::POLYPOLYGON    : DoAction(static_cast<MetaPolyPolygonAction    &>(*pAct)); break;
            case MetaActionType::TEXT           : DoAction(static_cast<MetaTextAction           &>(*pAct)); break;
            case MetaActionType::TEXTARRAY      : DoAction(static_cast<MetaTextArrayAction      &>(*pAct)); break;
            case MetaActionType::STRETCHTEXT    : DoAction(static_cast<MetaStretchTextAction    &>(*pAct)); break;
            case MetaActionType::BMP            : DoAction(static_cast<MetaBmpAction            &>(*pAct)); break;
            case MetaActionType::BMPSCALE       : DoAction(static_cast<MetaBmpScaleAction       &>(*pAct)); break;
            case MetaActionType::BMPEX          : DoAction(static_cast<MetaBmpExAction          &>(*pAct)); break;
            case MetaActionType::BMPEXSCALE     : DoAction(static_cast<MetaBmpExScaleAction     &>(*pAct)); break;
            case MetaActionType::LINECOLOR      : DoAction(static_cast<MetaLineColorAction      &>(*pAct)); break;
            case MetaActionType::FILLCOLOR      : DoAction(static_cast<MetaFillColorAction      &>(*pAct)); break;
            case MetaActionType::TEXTCOLOR      : DoAction(static_cast<MetaTextColorAction      &>(*pAct)); break;
            case MetaActionType::TEXTFILLCOLOR  : DoAction(static_cast<MetaTextFillColorAction  &>(*pAct)); break;
            case MetaActionType::FONT           : DoAction(static_cast<MetaFontAction           &>(*pAct)); break;
            case MetaActionType::TEXTALIGN      : DoAction(static_cast<MetaTextAlignAction      &>(*pAct)); break;
            case MetaActionType::MAPMODE        : DoAction(static_cast<MetaMapModeAction        &>(*pAct)); break;
            case MetaActionType::CLIPREGION     : DoAction(static_cast<MetaClipRegionAction     &>(*pAct)); break;
            case MetaActionType::MOVECLIPREGION : DoAction(static_cast<MetaMoveClipRegionAction &>(*pAct)); break;
            case MetaActionType::ISECTRECTCLIPREGION: DoAction(static_cast<MetaISectRectClipRegionAction&>(*pAct)); break;
            case MetaActionType::ISECTREGIONCLIPREGION: DoAction(static_cast<MetaISectRegionClipRegionAction&>(*pAct)); break;
            case MetaActionType::RASTEROP       : DoAction(static_cast<MetaRasterOpAction       &>(*pAct)); break;
            case MetaActionType::PUSH           : DoAction(static_cast<MetaPushAction           &>(*pAct)); break;
            case MetaActionType::POP            : DoAction(static_cast<MetaPopAction            &>(*pAct)); break;
            case MetaActionType::HATCH          : DoAction(static_cast<MetaHatchAction          &>(*pAct)); break;

            // #i125211# MetaCommentAction may change index, thus hand it over
            case MetaActionType::COMMENT        : DoAction(static_cast<MetaCommentAction&>(*pAct), rMtf, a);
                break;

            // missing actions added
            case MetaActionType::TEXTRECT       : DoAction(static_cast<MetaTextRectAction&>(*pAct)); break;
            case MetaActionType::BMPSCALEPART   : DoAction(static_cast<MetaBmpScalePartAction&>(*pAct)); break;
            case MetaActionType::BMPEXSCALEPART : DoAction(static_cast<MetaBmpExScalePartAction&>(*pAct)); break;
            case MetaActionType::MASK           : DoAction(static_cast<MetaMaskAction&>(*pAct)); break;
            case MetaActionType::MASKSCALE      : DoAction(static_cast<MetaMaskScaleAction&>(*pAct)); break;
            case MetaActionType::MASKSCALEPART  : DoAction(static_cast<MetaMaskScalePartAction&>(*pAct)); break;
            case MetaActionType::GRADIENT       : DoAction(static_cast<MetaGradientAction&>(*pAct)); break;
            case MetaActionType::WALLPAPER      : OSL_ENSURE(false, "Tried to construct SdrObject from MetaWallpaperAction: not supported (!)"); break;
            case MetaActionType::Transparent    : DoAction(static_cast<MetaTransparentAction&>(*pAct)); break;
            case MetaActionType::EPS            : OSL_ENSURE(false, "Tried to construct SdrObject from MetaEPSAction: not supported (!)"); break;
            case MetaActionType::REFPOINT       : DoAction(static_cast<MetaRefPointAction&>(*pAct)); break;
            case MetaActionType::TEXTLINECOLOR  : DoAction(static_cast<MetaTextLineColorAction&>(*pAct)); break;
            case MetaActionType::TEXTLINE       : OSL_ENSURE(false, "Tried to construct SdrObject from MetaTextLineAction: not supported (!)"); break;
            case MetaActionType::FLOATTRANSPARENT : DoAction(static_cast<MetaFloatTransparentAction&>(*pAct)); break;
            case MetaActionType::GRADIENTEX     : DoAction(static_cast<MetaGradientExAction&>(*pAct)); break;
            case MetaActionType::LAYOUTMODE     : DoAction(static_cast<MetaLayoutModeAction&>(*pAct)); break;
            case MetaActionType::TEXTLANGUAGE   : DoAction(static_cast<MetaTextLanguageAction&>(*pAct)); break;
            case MetaActionType::OVERLINECOLOR  : DoAction(static_cast<MetaOverlineColorAction&>(*pAct)); break;
            default: break;
        }

        if(pProgrInfo && pActionsToReport)
        {
            (*pActionsToReport)++;

            if(*pActionsToReport >= 16) // update all 16 actions
            {
                if(!pProgrInfo->ReportActions(*pActionsToReport))
                    break;

                *pActionsToReport = 0;
            }
        }
    }
}

size_t ImpSdrGDIMetaFileImport::DoImport(
    const GDIMetaFile& rMtf,
    SdrObjList& rOL,
    size_t nInsPos,
    SvdProgressInfo* pProgrInfo)
{
    maPrefMapMode = rMtf.GetPrefMapMode();
    mpVD->SetMapMode(maPrefMapMode);

    // setup some global scale parameter
    // mfScaleX, mfScaleY, maScaleX, maScaleY, mbMov, mbSize
    mfScaleX = mfScaleY = 1.0;
    const Size aMtfSize(rMtf.GetPrefSize());

    if(aMtfSize.Width() & aMtfSize.Height() && (!maScaleRect.IsEmpty()))
    {
        maOfs = maScaleRect.TopLeft();

        if(aMtfSize.Width() != (maScaleRect.GetWidth() - 1))
        {
            mfScaleX = static_cast<double>( maScaleRect.GetWidth() - 1 ) / static_cast<double>(aMtfSize.Width());
        }

        if(aMtfSize.Height() != (maScaleRect.GetHeight() - 1))
        {
            mfScaleY = static_cast<double>( maScaleRect.GetHeight() - 1 ) / static_cast<double>(aMtfSize.Height());
        }
    }

    mbMov = maOfs.X()!=0 || maOfs.Y()!=0;
    mbSize = false;
    maScaleX = Fraction( 1, 1 );
    maScaleY = Fraction( 1, 1 );

    if(aMtfSize.Width() != (maScaleRect.GetWidth() - 1))
    {
        maScaleX = Fraction(maScaleRect.GetWidth() - 1, aMtfSize.Width());
        mbSize = true;
    }

    if(aMtfSize.Height() != (maScaleRect.GetHeight() - 1))
    {
        maScaleY = Fraction(maScaleRect.GetHeight() - 1, aMtfSize.Height());
        mbSize = true;
    }

    if(pProgrInfo)
    {
        pProgrInfo->SetActionCount(rMtf.GetActionSize());
    }

    sal_uInt32 nActionsToReport(0);

    // execute
    DoLoopActions(rMtf, pProgrInfo, &nActionsToReport);

    if(pProgrInfo)
    {
        pProgrInfo->ReportActions(nActionsToReport);
        nActionsToReport = 0;
    }

    // To calculate the progress meter, we use GetActionSize()*3.
    // However, maTmpList has a lower entry count limit than GetActionSize(),
    // so the actions that were assumed were too much have to be re-added.
    nActionsToReport = (rMtf.GetActionSize() - maTmpList.size()) * 2;

    // announce all currently unannounced rescales
    if(pProgrInfo)
    {
        pProgrInfo->ReportRescales(nActionsToReport);
        pProgrInfo->SetInsertCount(maTmpList.size());
    }

    nActionsToReport = 0;

    // insert all objects cached in aTmpList now into rOL from nInsPos
    nInsPos = std::min(nInsPos, rOL.GetObjCount());

    for(rtl::Reference<SdrObject>& pObj : maTmpList)
    {
        rOL.NbcInsertObject(pObj.get(), nInsPos);
        nInsPos++;

        if(pProgrInfo)
        {
            nActionsToReport++;

            if(nActionsToReport >= 32) // update all 32 actions
            {
                pProgrInfo->ReportInserts(nActionsToReport);
                nActionsToReport = 0;
            }
        }
    }

    // report all remaining inserts for the last time
    if(pProgrInfo)
    {
        pProgrInfo->ReportInserts(nActionsToReport);
    }

    return maTmpList.size();
}

void ImpSdrGDIMetaFileImport::SetAttributes(SdrObject* pObj, bool bForceTextAttr)
{
    mbNoLine = false;
    mbNoFill = false;
    bool bLine(!bForceTextAttr);
    bool bFill(!pObj || (pObj->IsClosedObj() && !bForceTextAttr));
    bool bText(bForceTextAttr || (pObj && pObj->GetOutlinerParaObject()));

    if(bLine)
    {
        if(mnLineWidth)
        {
            mpLineAttr->Put(XLineWidthItem(mnLineWidth));
        }
        else
        {
            mpLineAttr->Put(XLineWidthItem(0));
        }

        maOldLineColor = mpVD->GetLineColor();

        if(mpVD->IsLineColor())
        {
            mpLineAttr->Put(XLineStyleItem(drawing::LineStyle_SOLID));
            mpLineAttr->Put(XLineColorItem(OUString(), mpVD->GetLineColor()));
        }
        else
        {
            mpLineAttr->Put(XLineStyleItem(drawing::LineStyle_NONE));
        }

        switch(maLineJoin)
        {
            case basegfx::B2DLineJoin::NONE:
                mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_NONE));
                break;
            case basegfx::B2DLineJoin::Bevel:
                mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_BEVEL));
                break;
            case basegfx::B2DLineJoin::Miter:
                mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_MITER));
                break;
            case basegfx::B2DLineJoin::Round:
                mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_ROUND));
                break;
        }

        // Add LineCap support
        mpLineAttr->Put(XLineCapItem(maLineCap));

        if(((maDash.GetDots() && maDash.GetDotLen()) || (maDash.GetDashes() && maDash.GetDashLen())) && maDash.GetDistance())
        {
            mpLineAttr->Put(XLineDashItem(OUString(), maDash));
            // tdf#155211 - change line style to dashed
            mpLineAttr->Put(XLineStyleItem(drawing::LineStyle_DASH));
        }
        else
        {
            mpLineAttr->Put(XLineDashItem(OUString(), XDash()));
        }
    }
    else
    {
        mbNoLine = true;
    }

    if(bFill)
    {
        if(mpVD->IsFillColor())
        {
            mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_SOLID));
            mpFillAttr->Put(XFillColorItem(OUString(), mpVD->GetFillColor()));
        }
        else
        {
            mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_NONE));
        }
    }
    else
    {
        mbNoFill = true;
    }

    if(bText && mbFntDirty)
    {
        vcl::Font aFnt(mpVD->GetFont());
        const sal_uInt32 nHeight(basegfx::fround(implMap(aFnt.GetFontSize()).Height() * mfScaleY));

        mpTextAttr->Put( SvxFontItem( aFnt.GetFamilyTypeMaybeAskConfig(), aFnt.GetFamilyName(), aFnt.GetStyleName(), aFnt.GetPitchMaybeAskConfig(), aFnt.GetCharSet(), EE_CHAR_FONTINFO ) );
        mpTextAttr->Put( SvxFontItem( aFnt.GetFamilyTypeMaybeAskConfig(), aFnt.GetFamilyName(), aFnt.GetStyleName(), aFnt.GetPitchMaybeAskConfig(), aFnt.GetCharSet(), EE_CHAR_FONTINFO_CJK ) );
        mpTextAttr->Put( SvxFontItem( aFnt.GetFamilyTypeMaybeAskConfig(), aFnt.GetFamilyName(), aFnt.GetStyleName(), aFnt.GetPitchMaybeAskConfig(), aFnt.GetCharSet(), EE_CHAR_FONTINFO_CTL ) );
        mpTextAttr->Put(SvxPostureItem(aFnt.GetItalicMaybeAskConfig(), EE_CHAR_ITALIC));
        mpTextAttr->Put(SvxWeightItem(aFnt.GetWeightMaybeAskConfig(), EE_CHAR_WEIGHT));
        mpTextAttr->Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) );
        mpTextAttr->Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) );
        mpTextAttr->Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) );
        mpTextAttr->Put(SvxCharScaleWidthItem(100, EE_CHAR_FONTWIDTH));
        mpTextAttr->Put(SvxUnderlineItem(aFnt.GetUnderline(), EE_CHAR_UNDERLINE));
        mpTextAttr->Put(SvxOverlineItem(aFnt.GetOverline(), EE_CHAR_OVERLINE));
        mpTextAttr->Put(SvxCrossedOutItem(aFnt.GetStrikeout(), EE_CHAR_STRIKEOUT));
        mpTextAttr->Put(SvxShadowedItem(aFnt.IsShadow(), EE_CHAR_SHADOW));

        // #i118485# Setting this item leads to problems (written #i118498# for this)
        // mpTextAttr->Put(SvxAutoKernItem(aFnt.IsKerning(), EE_CHAR_KERNING));

        mpTextAttr->Put(SvxWordLineModeItem(aFnt.IsWordLineMode(), EE_CHAR_WLM));
        mpTextAttr->Put(SvxContourItem(aFnt.IsOutline(), EE_CHAR_OUTLINE));
        mpTextAttr->Put(SvxColorItem(mpVD->GetTextColor(), EE_CHAR_COLOR));
        //... svxfont textitem svditext
        mbFntDirty = false;
    }

    if(!pObj)
        return;

    pObj->SetLayer(mnLayer);

    if(bLine)
    {
        pObj->SetMergedItemSet(*mpLineAttr);
    }

    if(bFill)
    {
        pObj->SetMergedItemSet(*mpFillAttr);
    }

    if(bText)
    {
        pObj->SetMergedItemSet(*mpTextAttr);
        pObj->SetMergedItem(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT));
    }
}

void ImpSdrGDIMetaFileImport::InsertObj(SdrObject* pObj1, bool bScale)
{
    rtl::Reference<SdrObject> pObj = pObj1;
    if(bScale && !maScaleRect.IsEmpty())
    {
        if(mbSize)
        {
            pObj->NbcResize(Point(), maScaleX, maScaleY);
        }

        if(mbMov)
        {
            pObj->NbcMove(Size(maOfs.X(), maOfs.Y()));
        }
    }

    if(isClip())
    {
        const basegfx::B2DPolyPolygon aPoly(pObj->TakeXorPoly());
        const basegfx::B2DRange aOldRange(aPoly.getB2DRange());
        const SdrLayerID aOldLayer(pObj->GetLayer());
        const SfxItemSet aOldItemSet(pObj->GetMergedItemSet());
        const SdrGrafObj* pSdrGrafObj = dynamic_cast< SdrGrafObj* >(pObj.get());
        const SdrTextObj* pSdrTextObj = DynCastSdrTextObj(pObj.get());

        if(pSdrTextObj && pSdrTextObj->HasText())
        {
            // all text objects are created from ImportText and have no line or fill attributes, so
            // it is okay to concentrate on the text itself
            while(true)
            {
                const basegfx::B2DPolyPolygon aTextContour(pSdrTextObj->TakeContour());
                const basegfx::B2DRange aTextRange(aTextContour.getB2DRange());
                const basegfx::B2DRange aClipRange(maClip.getB2DRange());

                // no overlap -> completely outside
                if(!aClipRange.overlaps(aTextRange))
                {
                    pObj.clear();
                    break;
                }

                // when the clip is a rectangle fast check for inside is possible
                if(basegfx::utils::isRectangle(maClip) && aClipRange.isInside(aTextRange))
                {
                    // completely inside ClipRect
                    break;
                }

                // here text needs to be clipped; to do so, convert to SdrObjects with polygons
                // and add these recursively. Delete original object, do not add in this run
                rtl::Reference<SdrObject> pConverted = pSdrTextObj->ConvertToPolyObj(true, true);
                pObj.clear();

                if(pConverted)
                {
                    // recursively add created conversion; per definition this shall not
                    // contain further SdrTextObjs. Visit only non-group objects
                    SdrObjListIter aIter(*pConverted, SdrIterMode::DeepNoGroups);

                    // work with clones; the created conversion may contain group objects
                    // and when working with the original objects the loop itself could
                    // break and the cleanup later would be pretty complicated (only delete group
                    // objects, are these empty, ...?)
                    while(aIter.IsMore())
                    {
                        SdrObject* pCandidate = aIter.Next();
                        OSL_ENSURE(pCandidate && dynamic_cast< SdrObjGroup* >(pCandidate) ==  nullptr, "SdrObjListIter with SdrIterMode::DeepNoGroups error (!)");
                        rtl::Reference<SdrObject> pNewClone(pCandidate->CloneSdrObject(pCandidate->getSdrModelFromSdrObject()));

                        if(pNewClone)
                        {
                            InsertObj(pNewClone.get(), false);
                        }
                        else
                        {
                            OSL_ENSURE(false, "SdrObject::Clone() failed (!)");
                        }
                    }
                }

                break;
            }
        }
        else
        {
            Bitmap aBitmap;

            if(pSdrGrafObj)
            {
                aBitmap = pSdrGrafObj->GetGraphic().GetBitmap();
            }

            pObj.clear();

            if(!aOldRange.isEmpty())
            {
                // clip against ClipRegion
                const basegfx::B2DPolyPolygon aNewPoly(
                    basegfx::utils::clipPolyPolygonOnPolyPolygon(
                        aPoly,
                        maClip,
                        true,
                        !aPoly.isClosed()));
                const basegfx::B2DRange aNewRange(aNewPoly.getB2DRange());

                if(!aNewRange.isEmpty())
                {
                    pObj = new SdrPathObj(
                        *mpModel,
                        aNewPoly.isClosed() ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
                        aNewPoly);

                    pObj->SetLayer(aOldLayer);
                    pObj->SetMergedItemSet(aOldItemSet);

                    if(!aBitmap.IsEmpty())
                    {
                        // aNewRange is inside of aOldRange and defines which part of aBitmapEx is used
                        const double fScaleX(aBitmap.GetSizePixel().Width() / (aOldRange.getWidth() ? aOldRange.getWidth() : 1.0));
                        const double fScaleY(aBitmap.GetSizePixel().Height() / (aOldRange.getHeight() ? aOldRange.getHeight() : 1.0));
                        basegfx::B2DRange aPixel(aNewRange);
                        basegfx::B2DHomMatrix aTrans;

                        aTrans.translate(-aOldRange.getMinX(), -aOldRange.getMinY());
                        aTrans.scale(fScaleX, fScaleY);
                        aPixel.transform(aTrans);

                        const Size aOrigSizePixel(aBitmap.GetSizePixel());
                        const Point aClipTopLeft(
                            basegfx::fround<tools::Long>(floor(std::max(0.0, aPixel.getMinX()))),
                            basegfx::fround<tools::Long>(floor(std::max(0.0, aPixel.getMinY()))));
                        const Size aClipSize(
                            basegfx::fround<tools::Long>(ceil(std::min(static_cast<double>(aOrigSizePixel.Width()), aPixel.getWidth()))),
                            basegfx::fround<tools::Long>(ceil(std::min(static_cast<double>(aOrigSizePixel.Height()), aPixel.getHeight()))));
                        const Bitmap aClippedBitmap(
                            aBitmap,
                            aClipTopLeft,
                            aClipSize);

                        pObj->SetMergedItem(XFillStyleItem(drawing::FillStyle_BITMAP));
                        pObj->SetMergedItem(XFillBitmapItem(OUString(), Graphic(aClippedBitmap)));
                        pObj->SetMergedItem(XFillBmpTileItem(false));
                        pObj->SetMergedItem(XFillBmpStretchItem(true));
                    }
                }
            }
        }
    }

    if(!pObj)
        return;

    // #i111954# check object for visibility
    // used are SdrPathObj, SdrRectObj, SdrCircObj, SdrGrafObj
    bool bVisible(false);

    if(pObj->HasLineStyle())
    {
        bVisible = true;
    }

    if(!bVisible && pObj->HasFillStyle())
    {
        bVisible = true;
    }

    if(!bVisible)
    {
        SdrTextObj* pTextObj = DynCastSdrTextObj(pObj.get());

        if(pTextObj && pTextObj->HasText())
        {
            bVisible = true;
        }
    }

    if(!bVisible)
    {
        SdrGrafObj* pGrafObj = dynamic_cast< SdrGrafObj* >(pObj.get());

        if(pGrafObj)
        {
            // this may be refined to check if the graphic really is visible. It
            // is here to ensure that graphic objects without fill, line and text
            // get created
            bVisible = true;
        }
    }

    if(bVisible)
    {
        maTmpList.push_back(pObj);

        if(dynamic_cast< SdrPathObj* >(pObj.get()))
        {
            const bool bClosed(pObj->IsClosedObj());

            mbLastObjWasPolyWithoutLine = mbNoLine && bClosed;
            mbLastObjWasLine = !bClosed;
        }
        else
        {
            mbLastObjWasPolyWithoutLine = false;
            mbLastObjWasLine = false;
        }
    }
}

void ImpSdrGDIMetaFileImport::DoAction(MetaLineAction const & rAct)
{
    // #i73407# reformulation to use new B2DPolygon classes
    const basegfx::B2DPoint aStart(rAct.GetStartPoint().X(), rAct.GetStartPoint().Y());
    const basegfx::B2DPoint aEnd(rAct.GetEndPoint().X(), rAct.GetEndPoint().Y());

    if(aStart.equal(aEnd))
        return;

    basegfx::B2DPolygon aLine;
    const basegfx::B2DHomMatrix aTransform(implMapMatrix() *
                                           basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));

    aLine.append(aStart);
    aLine.append(aEnd);
    aLine.transform(aTransform);

    const LineInfo& rLineInfo = rAct.GetLineInfo();
    const sal_Int32 nNewLineWidth(rLineInfo.GetWidth());
    bool bCreateLineObject(true);

    if(mbLastObjWasLine && (nNewLineWidth == mnLineWidth) && CheckLastLineMerge(aLine))
    {
        bCreateLineObject = false;
    }

    if(!bCreateLineObject)
        return;

    rtl::Reference<SdrPathObj> pPath = new SdrPathObj(
        *mpModel,
        SdrObjKind::Line,
        basegfx::B2DPolyPolygon(aLine));
    mnLineWidth = nNewLineWidth;
    maLineJoin = rLineInfo.GetLineJoin();
    maLineCap = rLineInfo.GetLineCap();
    maDash = XDash(css::drawing::DashStyle_RECT,
        rLineInfo.GetDotCount(), rLineInfo.GetDotLen(),
        rLineInfo.GetDashCount(), rLineInfo.GetDashLen(),
        rLineInfo.GetDistance());
    SetAttributes(pPath.get());
    mnLineWidth = 0;
    maLineJoin = basegfx::B2DLineJoin::NONE;
    // tdf#169813 - reset default dash options
    maDash = XDash(css::drawing::DashStyle_RECT, 0, 0, 0, 0, 0);
    InsertObj(pPath.get(), false);
}

void ImpSdrGDIMetaFileImport::DoAction(MetaRectAction const & rAct)
{
    rtl::Reference<SdrRectObj> pRect = new SdrRectObj(
        *mpModel,
        rAct.GetRect());
    SetAttributes(pRect.get());
    InsertObj(pRect.get());
}

void ImpSdrGDIMetaFileImport::DoAction(MetaRoundRectAction const & rAct)
{
    rtl::Reference<SdrRectObj> pRect = new SdrRectObj(
        *mpModel,
        rAct.GetRect());
    SetAttributes(pRect.get());
    tools::Long nRad=(rAct.GetHorzRound()+rAct.GetVertRound())/2;
    if (nRad!=0) {
        SfxItemSetFixed<SDRATTR_CORNER_RADIUS, SDRATTR_CORNER_RADIUS> aSet(*mpLineAttr->GetPool());
        aSet.Put(SdrMetricItem(SDRATTR_CORNER_RADIUS, nRad));
        pRect->SetMergedItemSet(aSet);
    }
    InsertObj(pRect.get());
}

void ImpSdrGDIMetaFileImport::DoAction(MetaEllipseAction const & rAct)
{
    rtl::Reference<SdrCircObj> pCirc=new SdrCircObj(
        *mpModel,
        SdrCircKind::Full,
        rAct.GetRect());
    SetAttributes(pCirc.get());
    InsertObj(pCirc.get());
}

void ImpSdrGDIMetaFileImport::DoAction(MetaArcAction const & rAct)
{
    Point aCenter(rAct.GetRect().Center());
    Degree100 nStart=GetAngle(rAct.GetStartPoint()-aCenter);
    Degree100 nEnd=GetAngle(rAct.GetEndPoint()-aCenter);
    rtl::Reference<SdrCircObj> pCirc = new SdrCircObj(
        *mpModel,
        SdrCircKind::Arc,
        rAct.GetRect(),nStart,nEnd);
    SetAttributes(pCirc.get());
    InsertObj(pCirc.get());
}

void ImpSdrGDIMetaFileImport::DoAction(MetaPieAction const & rAct)
{
    Point aCenter(rAct.GetRect().Center());
    Degree100 nStart=GetAngle(rAct.GetStartPoint()-aCenter);
    Degree100 nEnd=GetAngle(rAct.GetEndPoint()-aCenter);
    rtl::Reference<SdrCircObj> pCirc = new SdrCircObj(
        *mpModel,
        SdrCircKind::Section,
        rAct.GetRect(),
        nStart,
        nEnd);
    SetAttributes(pCirc.get());
    InsertObj(pCirc.get());
}

void ImpSdrGDIMetaFileImport::DoAction(MetaChordAction const & rAct)
{
    Point aCenter(rAct.GetRect().Center());
    Degree100 nStart=GetAngle(rAct.GetStartPoint()-aCenter);
    Degree100 nEnd=GetAngle(rAct.GetEndPoint()-aCenter);
    rtl::Reference<SdrCircObj> pCirc = new SdrCircObj(
        *mpModel,
        SdrCircKind::Cut,
        rAct.GetRect(),
        nStart,
        nEnd);
    SetAttributes(pCirc.get());
    InsertObj(pCirc.get());
}

bool ImpSdrGDIMetaFileImport::CheckLastLineMerge(const basegfx::B2DPolygon& rSrcPoly)
{
    // #i102706# Do not merge closed polygons
    if(rSrcPoly.isClosed())
    {
        return false;
    }

    // #i73407# reformulation to use new B2DPolygon classes
    if(mbLastObjWasLine && (maOldLineColor == mpVD->GetLineColor()) && rSrcPoly.count())
    {
        SdrObject* pTmpObj = !maTmpList.empty() ? maTmpList[maTmpList.size() - 1].get() : nullptr;
        SdrPathObj* pLastPoly = dynamic_cast< SdrPathObj* >(pTmpObj);

        if(pLastPoly)
        {
            if(1 == pLastPoly->GetPathPoly().count())
            {
                bool bOk(false);
                basegfx::B2DPolygon aDstPoly(pLastPoly->GetPathPoly().getB2DPolygon(0));

                // #i102706# Do not merge closed polygons
                if(aDstPoly.isClosed())
                {
                    return false;
                }

                if(aDstPoly.count())
                {
                    const sal_uInt32 nMaxDstPnt(aDstPoly.count() - 1);
                    const sal_uInt32 nMaxSrcPnt(rSrcPoly.count() - 1);

                    if(aDstPoly.getB2DPoint(nMaxDstPnt) == rSrcPoly.getB2DPoint(0))
                    {
                        aDstPoly.append(rSrcPoly, 1, rSrcPoly.count() - 1);
                        bOk = true;
                    }
                    else if(aDstPoly.getB2DPoint(0) == rSrcPoly.getB2DPoint(nMaxSrcPnt))
                    {
                        basegfx::B2DPolygon aNew(rSrcPoly);
                        aNew.append(aDstPoly, 1, aDstPoly.count() - 1);
                        aDstPoly = std::move(aNew);
                        bOk = true;
                    }
                    else if(aDstPoly.getB2DPoint(0) == rSrcPoly.getB2DPoint(0))
                    {
                        aDstPoly.flip();
                        aDstPoly.append(rSrcPoly, 1, rSrcPoly.count() - 1);
                        bOk = true;
                    }
                    else if(aDstPoly.getB2DPoint(nMaxDstPnt) == rSrcPoly.getB2DPoint(nMaxSrcPnt))
                    {
                        basegfx::B2DPolygon aNew(rSrcPoly);
                        aNew.flip();
                        aDstPoly.append(aNew, 1, aNew.count() - 1);
                        bOk = true;
                    }
                }

                if(bOk)
                {
                    pLastPoly->NbcSetPathPoly(basegfx::B2DPolyPolygon(aDstPoly));
                }

                return bOk;
            }
        }
    }

    return false;
}

bool ImpSdrGDIMetaFileImport::CheckLastPolyLineAndFillMerge(const basegfx::B2DPolyPolygon & rPolyPolygon)
{
    // #i73407# reformulation to use new B2DPolygon classes
    if(mbLastObjWasPolyWithoutLine)
    {
        SdrObject* pTmpObj = !maTmpList.empty() ? maTmpList[maTmpList.size() - 1].get() : nullptr;
        SdrPathObj* pLastPoly = dynamic_cast< SdrPathObj* >(pTmpObj);

        if(pLastPoly)
        {
            if(pLastPoly->GetPathPoly() == rPolyPolygon)
            {
                SetAttributes(nullptr);

                if(!mbNoLine && mbNoFill)
                {
                    pLastPoly->SetMergedItemSet(*mpLineAttr);

                    return true;
                }
            }
        }
    }

    return false;
}

void ImpSdrGDIMetaFileImport::checkClip()
{
    if(!mpVD->IsClipRegion())
        return;

    maClip = mpVD->GetClipRegion().GetAsB2DPolyPolygon();

    if(isClip())
    {
        const basegfx::B2DHomMatrix aTransform(
            implMapMatrix() *
            basegfx::utils::createScaleTranslateB2DHomMatrix(
                mfScaleX,
                mfScaleY,
                maOfs.X(),
                maOfs.Y()));

        maClip.transform(aTransform);
    }
}

bool ImpSdrGDIMetaFileImport::isClip() const
{
    return !maClip.getB2DRange().isEmpty();
}

void ImpSdrGDIMetaFileImport::DoAction( MetaPolyLineAction const & rAct )
{
    // #i73407# reformulation to use new B2DPolygon classes
    basegfx::B2DPolygon aSource(rAct.GetPolygon().getB2DPolygon());

    if(aSource.count())
    {
        const basegfx::B2DHomMatrix aTransform(implMapMatrix() *
                                               basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
        aSource.transform(aTransform);
    }

    const LineInfo& rLineInfo = rAct.GetLineInfo();
    const sal_Int32 nNewLineWidth(rLineInfo.GetWidth());
    bool bCreateLineObject(true);

    if(mbLastObjWasLine && (nNewLineWidth == mnLineWidth) && CheckLastLineMerge(aSource))
    {
        bCreateLineObject = false;
    }
    else if(mbLastObjWasPolyWithoutLine && CheckLastPolyLineAndFillMerge(basegfx::B2DPolyPolygon(aSource)))
    {
        bCreateLineObject = false;
    }

    if(!bCreateLineObject)
        return;

    rtl::Reference<SdrPathObj> pPath = new SdrPathObj(
        *mpModel,
        aSource.isClosed() ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
        basegfx::B2DPolyPolygon(aSource));
    mnLineWidth = nNewLineWidth;
    maLineJoin = rLineInfo.GetLineJoin();
    maLineCap = rLineInfo.GetLineCap();
    maDash = XDash(css::drawing::DashStyle_RECT,
        rLineInfo.GetDotCount(), rLineInfo.GetDotLen(),
        rLineInfo.GetDashCount(), rLineInfo.GetDashLen(),
        rLineInfo.GetDistance());
    SetAttributes(pPath.get());
    mnLineWidth = 0;
    maLineJoin = basegfx::B2DLineJoin::NONE;
    // tdf#169813 - reset default dash options
    maDash = XDash(css::drawing::DashStyle_RECT, 0, 0, 0, 0, 0);
    InsertObj(pPath.get(), false);
}

void ImpSdrGDIMetaFileImport::DoAction( MetaPolygonAction const & rAct )
{
    // #i73407# reformulation to use new B2DPolygon classes
    basegfx::B2DPolygon aSource(rAct.GetPolygon().getB2DPolygon());

    if(!aSource.count())
        return;

    const basegfx::B2DHomMatrix aTransform(implMapMatrix() *
                                           basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
    aSource.transform(aTransform);

    if(!mbLastObjWasPolyWithoutLine || !CheckLastPolyLineAndFillMerge(basegfx::B2DPolyPolygon(aSource)))
    {
        // #i73407# make sure polygon is closed, it's a filled primitive
        aSource.setClosed(true);
        rtl::Reference<SdrPathObj> pPath = new SdrPathObj(
            *mpModel,
            SdrObjKind::Polygon,
            basegfx::B2DPolyPolygon(aSource));
        SetAttributes(pPath.get());
        InsertObj(pPath.get(), false);
    }
}

void ImpSdrGDIMetaFileImport::DoAction(MetaPolyPolygonAction const & rAct)
{
    // #i73407# reformulation to use new B2DPolygon classes
    basegfx::B2DPolyPolygon aSource(rAct.GetPolyPolygon().getB2DPolyPolygon());

    if(!aSource.count())
        return;

    const basegfx::B2DHomMatrix aTransform(implMapMatrix() *
                                           basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
    aSource.transform(aTransform);

    if(!mbLastObjWasPolyWithoutLine || !CheckLastPolyLineAndFillMerge(aSource))
    {
        // #i73407# make sure polygon is closed, it's a filled primitive
        aSource.setClosed(true);
        rtl::Reference<SdrPathObj> pPath = new SdrPathObj(
            *mpModel,
            SdrObjKind::Polygon,
            std::move(aSource));
        SetAttributes(pPath.get());
        InsertObj(pPath.get(), false);
    }
}

Size ImpSdrGDIMetaFileImport::implMap(const Size& rSz) const
{
    return OutputDevice::LogicToLogic(rSz, mpVD->GetMapMode(), maPrefMapMode);
}

Point ImpSdrGDIMetaFileImport::implMap(const Point& rPt) const
{
    return OutputDevice::LogicToLogic(rPt, mpVD->GetMapMode(), maPrefMapMode);
}

basegfx::B2DHomMatrix ImpSdrGDIMetaFileImport::implMapMatrix() const
{
    return OutputDevice::LogicToLogic(mpVD->GetMapMode(), maPrefMapMode);
}

void ImpSdrGDIMetaFileImport::ImportText( const Point& rPos, const OUString& rStr, const MetaAction& rAct )
{
    // calc text box size, add 5% to make it fit safely

    FontMetric aFontMetric( mpVD->GetFontMetric() );
    vcl::Font aFnt( mpVD->GetFont() );
    TextAlign eAlg( aFnt.GetAlignment() );

    Size aTextSizeMapped(implMap(Size(mpVD->GetTextWidth(rStr), mpVD->GetTextHeight())));

    sal_Int32 nTextWidth = static_cast<sal_Int32>(aTextSizeMapped.Width() * mfScaleX);
    sal_Int32 nTextHeight = static_cast<sal_Int32>(aTextSizeMapped.Height() * mfScaleY);

    Point aPosMapped(implMap(rPos));

    Point aPos(basegfx::fround<tools::Long>(aPosMapped.X() * mfScaleX + maOfs.X()),
               basegfx::fround<tools::Long>(aPosMapped.Y() * mfScaleY + maOfs.Y()));
    Size aSize( nTextWidth, nTextHeight );

    if ( eAlg == ALIGN_BASELINE )
    {
        auto nAscent = implMap(Size(0, aFontMetric.GetAscent())).Height();
        aPos.AdjustY(basegfx::fround<tools::Long>(nAscent * -mfScaleY));
    }
    else if ( eAlg == ALIGN_BOTTOM )
        aPos.AdjustY( -nTextHeight );

    tools::Rectangle aTextRect( aPos, aSize );
    rtl::Reference<SdrRectObj> pText = new SdrRectObj(*mpModel, aTextRect, SdrObjKind::Text);

    pText->SetMergedItem ( makeSdrTextUpperDistItem (0));
    pText->SetMergedItem ( makeSdrTextLowerDistItem (0));
    pText->SetMergedItem ( makeSdrTextRightDistItem (0));
    pText->SetMergedItem ( makeSdrTextLeftDistItem (0));

    if ( aFnt.GetAverageFontWidth() || ( rAct.GetType() == MetaActionType::STRETCHTEXT ) )
    {
        pText->ClearMergedItem( SDRATTR_TEXT_AUTOGROWWIDTH );
        pText->SetMergedItem( makeSdrTextAutoGrowHeightItem( false ) );
        // don't let the margins eat the space needed for the text
        pText->SetMergedItem( SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_ALLLINES) );
    }
    else
    {
        pText->SetMergedItem( makeSdrTextAutoGrowWidthItem( true ) );
    }

    pText->SetLayer(mnLayer);
    pText->NbcSetText( rStr );
    SetAttributes( pText.get(), true );
    pText->SetSnapRect( aTextRect );

    if (!aFnt.IsTransparent())
    {
        SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aAttr(*mpFillAttr->GetPool());
        aAttr.Put(XFillStyleItem(drawing::FillStyle_SOLID));
        aAttr.Put(XFillColorItem(OUString(), aFnt.GetFillColor()));
        pText->SetMergedItemSet(aAttr);
    }
    Degree100 nAngle = to<Degree100>(aFnt.GetOrientation());
    if ( nAngle )
        pText->SdrAttrObj::NbcRotate(aPos,nAngle);
    InsertObj( pText.get(), false );
}

void ImpSdrGDIMetaFileImport::DoAction(MetaTextAction const & rAct)
{
    OUString aStr(rAct.GetText());
    aStr = aStr.copy(rAct.GetIndex(), rAct.GetLen());
    ImportText( rAct.GetPoint(), aStr, rAct );
}

void ImpSdrGDIMetaFileImport::DoAction(MetaTextArrayAction const & rAct)
{
    OUString aStr(rAct.GetText());
    aStr = aStr.copy(rAct.GetIndex(), rAct.GetLen());
    ImportText( rAct.GetPoint(), aStr, rAct );
}

void ImpSdrGDIMetaFileImport::DoAction(MetaStretchTextAction const & rAct)
{
    OUString aStr(rAct.GetText());
    aStr = aStr.copy(rAct.GetIndex(), rAct.GetLen());
    ImportText( rAct.GetPoint(), aStr, rAct );
}

void ImpSdrGDIMetaFileImport::DoAction(MetaBmpAction const & rAct)
{
    tools::Rectangle aRect(rAct.GetPoint(),rAct.GetBitmap().GetSizePixel());
    aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
    rtl::Reference<SdrGrafObj> pGraf = new SdrGrafObj(
        *mpModel,
        Graphic(rAct.GetBitmap()),
        aRect);

    // This action is not creating line and fill, set directly, do not use SetAttributes(..)
    pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
    pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
    InsertObj(pGraf.get());
}

void ImpSdrGDIMetaFileImport::DoAction(MetaBmpScaleAction const & rAct)
{
    tools::Rectangle aRect(rAct.GetPoint(),rAct.GetSize());
    aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
    rtl::Reference<SdrGrafObj> pGraf = new SdrGrafObj(
        *mpModel,
        Graphic(rAct.GetBitmap()),
        aRect);

    // This action is not creating line and fill, set directly, do not use SetAttributes(..)
    pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
    pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
    InsertObj(pGraf.get());
}

void ImpSdrGDIMetaFileImport::DoAction(MetaBmpExAction const & rAct)
{
    tools::Rectangle aRect(rAct.GetPoint(),rAct.GetBitmap().GetSizePixel());
    aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
    rtl::Reference<SdrGrafObj> pGraf = new SdrGrafObj(
        *mpModel,
        rAct.GetBitmap(),
        aRect);

    // This action is not creating line and fill, set directly, do not use SetAttributes(..)
    pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
    pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
    InsertObj(pGraf.get());
}

void ImpSdrGDIMetaFileImport::DoAction(MetaBmpExScaleAction const & rAct)
{
    tools::Rectangle aRect(rAct.GetPoint(),rAct.GetSize());
    aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
    rtl::Reference<SdrGrafObj> pGraf = new SdrGrafObj(
        *mpModel,
        rAct.GetBitmap(),
        aRect);

    // This action is not creating line and fill, set directly, do not use SetAttributes(..)
    pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
    pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
    InsertObj(pGraf.get());
}


void ImpSdrGDIMetaFileImport::DoAction( MetaHatchAction const & rAct )
{
    // #i73407# reformulation to use new B2DPolygon classes
    basegfx::B2DPolyPolygon aSource(rAct.GetPolyPolygon().getB2DPolyPolygon());

    if(!aSource.count())
        return;

    const basegfx::B2DHomMatrix aTransform(implMapMatrix() *
                                           basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
    aSource.transform(aTransform);

    if(mbLastObjWasPolyWithoutLine && CheckLastPolyLineAndFillMerge(aSource))
        return;

    const Hatch& rHatch = rAct.GetHatch();
    rtl::Reference<SdrPathObj> pPath = new SdrPathObj(
        *mpModel,
        SdrObjKind::Polygon,
        std::move(aSource));
    // #i125211# Use the ranges from the SdrObject to create a new empty SfxItemSet
    SfxItemSet aHatchAttr(mpModel->GetItemPool(), pPath->GetMergedItemSet().GetRanges());
    css::drawing::HatchStyle eStyle;

    switch(rHatch.GetStyle())
    {
        case HatchStyle::Triple :
        {
            eStyle = css::drawing::HatchStyle_TRIPLE;
            break;
        }

        case HatchStyle::Double :
        {
            eStyle = css::drawing::HatchStyle_DOUBLE;
            break;
        }

        default:
        {
            eStyle = css::drawing::HatchStyle_SINGLE;
            break;
        }
    }

    SetAttributes(pPath.get());
    aHatchAttr.Put(XFillStyleItem(drawing::FillStyle_HATCH));
    aHatchAttr.Put(XFillHatchItem(XHatch(rHatch.GetColor(), eStyle, rHatch.GetDistance(), rHatch.GetAngle())));
    pPath->SetMergedItemSet(aHatchAttr);

    InsertObj(pPath.get(), false);
}


void ImpSdrGDIMetaFileImport::DoAction(MetaLineColorAction& rAct)
{
    rAct.Execute(mpVD);
}

void ImpSdrGDIMetaFileImport::DoAction(MetaMapModeAction& rAct)
{
    rAct.Execute(mpVD);
    mbLastObjWasPolyWithoutLine = false;
    mbLastObjWasLine = false;
}

void ImpSdrGDIMetaFileImport::DoAction( MetaCommentAction const & rAct, GDIMetaFile const & rMtf, size_t& a) // GDIMetaFile* pMtf )
{
    bool aSkipComment = false;

    if (a < rMtf.GetActionSize() && rAct.GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
    {
        // #i125211# Check if next action is a MetaGradientExAction
        MetaGradientExAction* pAct = dynamic_cast< MetaGradientExAction* >(rMtf.GetAction(a + 1));

        if( pAct && pAct->GetType() == MetaActionType::GRADIENTEX )
        {
            // #i73407# reformulation to use new B2DPolygon classes
            basegfx::B2DPolyPolygon aSource(pAct->GetPolyPolygon().getB2DPolyPolygon());

            if(aSource.count())
            {
                if(!mbLastObjWasPolyWithoutLine || !CheckLastPolyLineAndFillMerge(aSource))
                {
                    const Gradient& rGrad = pAct->GetGradient();
                    rtl::Reference<SdrPathObj> pPath = new SdrPathObj(
                        *mpModel,
                        SdrObjKind::Polygon,
                        std::move(aSource));
                    // #i125211# Use the ranges from the SdrObject to create a new empty SfxItemSet
                    SfxItemSet aGradAttr(mpModel->GetItemPool(), pPath->GetMergedItemSet().GetRanges());
                    basegfx::BGradient aBGradient(
                        basegfx::BColorStops(
                            rGrad.GetStartColor().getBColor(),
                            rGrad.GetEndColor().getBColor()));

                    aBGradient.SetGradientStyle(rGrad.GetStyle());
                    aBGradient.SetAngle(rGrad.GetAngle());
                    aBGradient.SetBorder(rGrad.GetBorder());
                    aBGradient.SetXOffset(rGrad.GetOfsX());
                    aBGradient.SetYOffset(rGrad.GetOfsY());
                    aBGradient.SetStartIntens(rGrad.GetStartIntensity());
                    aBGradient.SetEndIntens(rGrad.GetEndIntensity());
                    aBGradient.SetSteps(rGrad.GetSteps());

                    // no need to use SetAttributes(..) here since line and fill style
                    // need to be set individually
                    // SetAttributes(pPath);

                    // switch line off; if there was one there will be a
                    // MetaActionType::POLYLINE following creating another object
                    aGradAttr.Put(XLineStyleItem(drawing::LineStyle_NONE));

                    // add detected gradient fillstyle
                    aGradAttr.Put(XFillStyleItem(drawing::FillStyle_GRADIENT));
                    aGradAttr.Put(XFillGradientItem(aBGradient));

                    pPath->SetMergedItemSet(aGradAttr);

                    InsertObj(pPath.get());
                }
            }

            aSkipComment = true;
        }
    }

    if(aSkipComment)
    {
        // #i125211# forward until closing MetaCommentAction
        MetaAction* pSkipAct = rMtf.GetAction(++a);

        while( pSkipAct
            && ((pSkipAct->GetType() != MetaActionType::COMMENT )
                || !(static_cast<MetaCommentAction*>(pSkipAct)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END"))))
        {
            pSkipAct = rMtf.GetAction(++a);
        }
    }
}

void ImpSdrGDIMetaFileImport::DoAction(MetaTextRectAction const & rAct)
{
    GDIMetaFile aTemp;

    mpVD->AddTextRectActions(rAct.GetRect(), rAct.GetText(), rAct.GetStyle(), aTemp);
    DoLoopActions(aTemp, nullptr, nullptr);
}

void ImpSdrGDIMetaFileImport::DoAction(MetaBmpScalePartAction const & rAct)
{
    tools::Rectangle aRect(rAct.GetDestPoint(), rAct.GetDestSize());
    Bitmap aBitmap(rAct.GetBitmap());

    aRect.AdjustRight( 1 );
    aRect.AdjustBottom( 1 );
    aBitmap.Crop(tools::Rectangle(rAct.GetSrcPoint(), rAct.GetSrcSize()));
    rtl::Reference<SdrGrafObj> pGraf = new SdrGrafObj(
        *mpModel,
        aBitmap,
        aRect);

    // This action is not creating line and fill, set directly, do not use SetAttributes(..)
    pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
    pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
    InsertObj(pGraf.get());
}

void ImpSdrGDIMetaFileImport::DoAction(MetaBmpExScalePartAction const & rAct)
{
    tools::Rectangle aRect(rAct.GetDestPoint(),rAct.GetDestSize());
    Bitmap aBitmap(rAct.GetBitmap());

    aRect.AdjustRight( 1 );
    aRect.AdjustBottom( 1 );
    aBitmap.Crop(tools::Rectangle(rAct.GetSrcPoint(), rAct.GetSrcSize()));
    rtl::Reference<SdrGrafObj> pGraf = new SdrGrafObj(
        *mpModel,
        aBitmap,
        aRect);

    // This action is not creating line and fill, set directly, do not use SetAttributes(..)
    pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
    pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
    InsertObj(pGraf.get());
}

void ImpSdrGDIMetaFileImport::DoAction(MetaMaskAction const & rAct)
{
    tools::Rectangle aRect(rAct.GetPoint(), rAct.GetBitmap().GetSizePixel());
    Bitmap aBitmap(rAct.GetBitmap(), rAct.GetColor());

    aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
    rtl::Reference<SdrGrafObj> pGraf = new SdrGrafObj(
        *mpModel,
        aBitmap,
        aRect);

    // This action is not creating line and fill, set directly, do not use SetAttributes(..)
    pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
    pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
    InsertObj(pGraf.get());
}

void ImpSdrGDIMetaFileImport::DoAction(MetaMaskScaleAction const & rAct)
{
    tools::Rectangle aRect(rAct.GetPoint(), rAct.GetSize());
    Bitmap aBitmap(rAct.GetBitmap(), rAct.GetColor());

    aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
    rtl::Reference<SdrGrafObj> pGraf = new SdrGrafObj(
        *mpModel,
        aBitmap,
        aRect);

    // This action is not creating line and fill, set directly, do not use SetAttributes(..)
    pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
    pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
    InsertObj(pGraf.get());
}

void ImpSdrGDIMetaFileImport::DoAction(MetaMaskScalePartAction const & rAct)
{
    tools::Rectangle aRect(rAct.GetDestPoint(), rAct.GetDestSize());
    Bitmap aBitmap(rAct.GetBitmap(), rAct.GetColor());

    aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
    aBitmap.Crop(tools::Rectangle(rAct.GetSrcPoint(), rAct.GetSrcSize()));
    rtl::Reference<SdrGrafObj> pGraf = new SdrGrafObj(
        *mpModel,
        aBitmap,
        aRect);

    // This action is not creating line and fill, set directly, do not use SetAttributes(..)
    pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
    pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
    InsertObj(pGraf.get());
}

void ImpSdrGDIMetaFileImport::DoAction(MetaGradientAction const & rAct)
{
    basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rAct.GetRect());

    if(aRange.isEmpty())
        return;

    const basegfx::B2DHomMatrix aTransform(implMapMatrix() *
                                           basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
    aRange.transform(aTransform);
    const Gradient& rGradient = rAct.GetGradient();
    rtl::Reference<SdrRectObj> pRect = new SdrRectObj(
        *mpModel,
        tools::Rectangle(
            floor(aRange.getMinX()),
            floor(aRange.getMinY()),
            ceil(aRange.getMaxX()),
            ceil(aRange.getMaxY())),
        SdrObjKind::Text);
    // #i125211# Use the ranges from the SdrObject to create a new empty SfxItemSet
    SfxItemSet aGradientAttr(mpModel->GetItemPool(), pRect->GetMergedItemSet().GetRanges());
    const XFillGradientItem aXFillGradientItem(
        basegfx::BGradient(
            basegfx::BColorStops(
                rGradient.GetStartColor().getBColor(),
                rGradient.GetEndColor().getBColor()),
            rGradient.GetStyle(),
            rGradient.GetAngle(),
            rGradient.GetOfsX(),
            rGradient.GetOfsY(),
            rGradient.GetBorder(),
            rGradient.GetStartIntensity(),
            rGradient.GetEndIntensity(),
            rGradient.GetSteps()));

    SetAttributes(pRect.get());
    aGradientAttr.Put(XFillStyleItem(drawing::FillStyle_GRADIENT)); // #i125211#
    aGradientAttr.Put(aXFillGradientItem);
    pRect->SetMergedItemSet(aGradientAttr);

    InsertObj(pRect.get(), false);
}

void ImpSdrGDIMetaFileImport::DoAction(MetaTransparentAction const & rAct)
{
    basegfx::B2DPolyPolygon aSource(rAct.GetPolyPolygon().getB2DPolyPolygon());

    if(!aSource.count())
        return;

    const basegfx::B2DHomMatrix aTransform(implMapMatrix() *
                                           basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
    aSource.transform(aTransform);
    aSource.setClosed(true);

    rtl::Reference<SdrPathObj> pPath = new SdrPathObj(
        *mpModel,
        SdrObjKind::Polygon,
        std::move(aSource));
    SetAttributes(pPath.get());
    pPath->SetMergedItem(XFillTransparenceItem(rAct.GetTransparence()));
    InsertObj(pPath.get(), false);
}

void ImpSdrGDIMetaFileImport::DoAction(MetaGradientExAction const & rAct)
{
    basegfx::B2DPolyPolygon aSource(rAct.GetPolyPolygon().getB2DPolyPolygon());

    if(!aSource.count())
        return;

    const basegfx::B2DHomMatrix aTransform(implMapMatrix() *
                                           basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
    aSource.transform(aTransform);

    if(mbLastObjWasPolyWithoutLine && CheckLastPolyLineAndFillMerge(aSource))
        return;

    const Gradient& rGradient = rAct.GetGradient();
    rtl::Reference<SdrPathObj> pPath = new SdrPathObj(
        *mpModel,
        SdrObjKind::Polygon,
        std::move(aSource));
    // #i125211# Use the ranges from the SdrObject to create a new empty SfxItemSet
    SfxItemSet aGradientAttr(mpModel->GetItemPool(), pPath->GetMergedItemSet().GetRanges());
    const XFillGradientItem aXFillGradientItem(
        basegfx::BGradient(
            basegfx::BColorStops(
                rGradient.GetStartColor().getBColor(),
                rGradient.GetEndColor().getBColor()),
            rGradient.GetStyle(),
            rGradient.GetAngle(),
            rGradient.GetOfsX(),
            rGradient.GetOfsY(),
            rGradient.GetBorder(),
            rGradient.GetStartIntensity(),
            rGradient.GetEndIntensity(),
            rGradient.GetSteps()));

    SetAttributes(pPath.get());
    aGradientAttr.Put(XFillStyleItem(drawing::FillStyle_GRADIENT)); // #i125211#
    aGradientAttr.Put(aXFillGradientItem);
    pPath->SetMergedItemSet(aGradientAttr);

    InsertObj(pPath.get(), false);
}

void ImpSdrGDIMetaFileImport::DoAction(MetaFloatTransparentAction const & rAct)
{
    const GDIMetaFile& rMtf = rAct.GetGDIMetaFile();

    if(!rMtf.GetActionSize())
        return;

    const tools::Rectangle aRect(rAct.GetPoint(),rAct.GetSize());

    // convert metafile sub-content to Bitmap
    Bitmap aBitmap(
        convertMetafileToBitmap(
            rMtf,
            vcl::unotools::b2DRectangleFromRectangle(aRect),
            125000));

    // handle colors
    const Gradient& rGradient = rAct.GetGradient();
    basegfx::BColor aStart(rGradient.GetStartColor().getBColor());
    basegfx::BColor aEnd(rGradient.GetEndColor().getBColor());

    if(100 != rGradient.GetStartIntensity())
    {
        aStart *= static_cast<double>(rGradient.GetStartIntensity()) / 100.0;
    }

    if(100 != rGradient.GetEndIntensity())
    {
        aEnd *= static_cast<double>(rGradient.GetEndIntensity()) / 100.0;
    }

    const bool bEqualColors(aStart == aEnd);
    const bool bNoSteps(1 == rGradient.GetSteps());
    bool bCreateObject(true);
    bool bHasNewMask(false);
    AlphaMask aNewMask;
    double fTransparence(0.0);
    bool bFixedTransparence(false);

    if(bEqualColors || bNoSteps)
    {
        // single transparence
        const basegfx::BColor aMedium(basegfx::average(aStart, aEnd));
        fTransparence = aMedium.luminance();

        if(fTransparence <= 0.0)
        {
            // no transparence needed, all done
        }
        else if(basegfx::fTools::moreOrEqual(fTransparence, 1.0))
        {
            // all transparent, no object
            bCreateObject = false;
        }
        else
        {
            // 0.0 < transparence < 1.0, apply fixed transparence
            bFixedTransparence = true;
        }
    }
    else
    {
        // gradient transparence
        ScopedVclPtrInstance< VirtualDevice > pVDev;

        pVDev->SetOutputSizePixel(aBitmap.GetSizePixel());
        pVDev->DrawGradient(tools::Rectangle(Point(0, 0), pVDev->GetOutputSizePixel()), rGradient);

        aNewMask = AlphaMask(pVDev->GetBitmap(Point(0, 0), pVDev->GetOutputSizePixel()));
        aNewMask.Invert(); // convert transparency to alpha
        bHasNewMask = true;
    }

    if(!bCreateObject)
        return;

    if(bHasNewMask || bFixedTransparence)
    {
        if(!aBitmap.HasAlpha())
        {
            // no transparence yet, apply new one
            if(bFixedTransparence)
            {
                sal_uInt8 nTransparence(basegfx::fround(fTransparence * 255.0));

                aNewMask = AlphaMask(aBitmap.GetSizePixel(), &nTransparence);
            }

            aBitmap = Bitmap(aBitmap.CreateColorBitmap(), aNewMask);
        }
        else
        {
            vcl::bitmap::DrawAlphaBitmapAndAlphaGradient(aBitmap, bFixedTransparence, fTransparence, aNewMask);
        }
    }

    // create and add object
    rtl::Reference<SdrGrafObj> pGraf = new SdrGrafObj(
        *mpModel,
        aBitmap,
        aRect);

    // for MetaFloatTransparentAction, do not use SetAttributes(...)
    // since these metafile content is not used to draw line/fill
    // dependent of these setting at the device content
    pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
    pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
    InsertObj(pGraf.get());
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
