/* -*- 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 <config_wasm_strip.h>

#include <cassert>

#include <sal/log.hxx>
#include <com/sun/star/document/XEventsSupplier.hpp>
#include <com/sun/star/container/XNameReplace.hpp>
#include <com/sun/star/presentation/ClickAction.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>
#include <com/sun/star/drawing/LineStyle.hpp>
#include <utility>
#include <xmloff/unointerfacetouniqueidentifiermapper.hxx>
#include <com/sun/star/drawing/XGluePointsSupplier.hpp>
#include <com/sun/star/drawing/GluePoint2.hpp>
#include <com/sun/star/media/ZoomLevel.hpp>
#include <com/sun/star/awt/Rectangle.hpp>
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include "ximpshap.hxx"
#include <xmloff/XMLBase64ImportContext.hxx>
#include <xmloff/XMLShapeStyleContext.hxx>
#include <xmloff/xmluconv.hxx>
#include <com/sun/star/container/XNamed.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/awt/XControlModel.hpp>
#include <com/sun/star/drawing/XControlShape.hpp>
#include <com/sun/star/drawing/PointSequenceSequence.hpp>
#include <com/sun/star/drawing/PointSequence.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/util/XCloneable.hpp>
#include <com/sun/star/beans/XMultiPropertyStates.hpp>
#include <xexptran.hxx>
#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/drawing/HomogenMatrix3.hpp>
#include <com/sun/star/graphic/XGraphic.hpp>
#include <com/sun/star/style/XStyle.hpp>

#include <sax/tools/converter.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <comphelper/mediamimetype.hxx>

#include <xmloff/families.hxx>
#include<xmloff/xmlnamespace.hxx>
#include <xmloff/xmltoken.hxx>
#include <EnhancedCustomShapeToken.hxx>
#include <XMLReplacementImageContext.hxx>
#include <XMLImageMapContext.hxx>
#include "sdpropls.hxx"
#include "eventimp.hxx"
#include "descriptionimp.hxx"
#include "SignatureLineContext.hxx"
#include "QRCodeContext.hxx"
#include "ximpcustomshape.hxx"
#include <XMLEmbeddedObjectImportContext.hxx>
#include <xmloff/xmlerror.hxx>
#include <xmloff/table/XMLTableImport.hxx>
#include <xmloff/ProgressBarHelper.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp>
#include <com/sun/star/container/XChild.hpp>
#include <com/sun/star/text/XTextDocument.hpp>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/point/b2dpoint.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/vector/b2dvector.hxx>
#include <tools/urlobj.hxx>
#include <o3tl/any.hxx>
#include <o3tl/safeint.hxx>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::style;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::document;
using namespace ::xmloff::token;
using namespace ::xmloff::EnhancedCustomShapeToken;

SvXMLEnumMapEntry<drawing::Alignment> const aXML_GlueAlignment_EnumMap[] =
{
    { XML_TOP_LEFT,     drawing::Alignment_TOP_LEFT },
    { XML_TOP,          drawing::Alignment_TOP },
    { XML_TOP_RIGHT,    drawing::Alignment_TOP_RIGHT },
    { XML_LEFT,         drawing::Alignment_LEFT },
    { XML_CENTER,       drawing::Alignment_CENTER },
    { XML_RIGHT,        drawing::Alignment_RIGHT },
    { XML_BOTTOM_LEFT,  drawing::Alignment_BOTTOM_LEFT },
    { XML_BOTTOM,       drawing::Alignment_BOTTOM },
    { XML_BOTTOM_RIGHT, drawing::Alignment_BOTTOM_RIGHT },
    { XML_TOKEN_INVALID, drawing::Alignment(0) }
};

SvXMLEnumMapEntry<drawing::EscapeDirection> const aXML_GlueEscapeDirection_EnumMap[] =
{
    { XML_AUTO,         drawing::EscapeDirection_SMART },
    { XML_LEFT,         drawing::EscapeDirection_LEFT },
    { XML_RIGHT,        drawing::EscapeDirection_RIGHT },
    { XML_UP,           drawing::EscapeDirection_UP },
    { XML_DOWN,         drawing::EscapeDirection_DOWN },
    { XML_HORIZONTAL,   drawing::EscapeDirection_HORIZONTAL },
    { XML_VERTICAL,     drawing::EscapeDirection_VERTICAL },
    { XML_TOKEN_INVALID, drawing::EscapeDirection(0) }
};

static bool ImpIsEmptyURL( std::u16string_view rURL )
{
    if( rURL.empty() )
        return true;

    // #i13140# Also compare against 'toplevel' URLs. which also
    // result in empty filename strings.
    if( rURL == u"#./" )
        return true;

    return false;
}


SdXMLShapeContext::SdXMLShapeContext(
    SvXMLImport& rImport,
    css::uno::Reference< css::xml::sax::XFastAttributeList> xAttrList,
    uno::Reference< drawing::XShapes > xShapes,
    bool bTemporaryShape)
    : SvXMLShapeContext( rImport, bTemporaryShape )
    , mxShapes(std::move( xShapes ))
    , mxAttrList(std::move(xAttrList))
    , mbListContextPushed( false )
    , mnStyleFamily(XmlStyleFamily::SD_GRAPHICS_ID)
    , mbIsPlaceholder(false)
    , mbClearDefaultAttributes( true )
    , mbIsUserTransformed(false)
    , mnZOrder(-1)
    , maSize(1, 1)
    , mnRelWidth(0)
    , mnRelHeight(0)
    , maPosition(0, 0)
    , mbVisible(true)
    , mbPrintable(true)
    , mbHaveXmlId(false)
    , mbTextBox(false)
{
}

SdXMLShapeContext::~SdXMLShapeContext()
{
}

css::uno::Reference< css::xml::sax::XFastContextHandler > SdXMLShapeContext::createFastChildContext(
    sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
{
    SvXMLImportContextRef xContext;
    // #i68101#
    if( nElement == XML_ELEMENT(SVG, XML_TITLE) || nElement == XML_ELEMENT(SVG, XML_DESC)
        || nElement == XML_ELEMENT(SVG_COMPAT, XML_TITLE) || nElement == XML_ELEMENT(SVG_COMPAT, XML_DESC) )
    {
        xContext = new SdXMLDescriptionContext( GetImport(), nElement, mxShape );
    }
    else if( nElement == XML_ELEMENT(LO_EXT, XML_SIGNATURELINE) )
    {
        xContext = new SignatureLineContext( GetImport(), nElement, xAttrList, mxShape );
    }
    else if( nElement == XML_ELEMENT(LO_EXT, XML_QRCODE) )
    {
        xContext = new QRCodeContext( GetImport(), nElement, xAttrList, mxShape );
    }
    else if( nElement == XML_ELEMENT(OFFICE, XML_EVENT_LISTENERS) )
    {
        xContext = new SdXMLEventsContext( GetImport(), mxShape );
    }
    else if( nElement == XML_ELEMENT(DRAW, XML_GLUE_POINT) )
    {
        addGluePoint( xAttrList );
    }
    else if( nElement == XML_ELEMENT(DRAW, XML_THUMBNAIL) )
    {
        // search attributes for xlink:href
        maThumbnailURL = xAttrList->getOptionalValue(XML_ELEMENT(XLINK, XML_HREF));
    }
    else
    {
        // create text cursor on demand
        if( !mxCursor.is() )
        {
            uno::Reference< text::XText > xText( mxShape, uno::UNO_QUERY );
            if( xText.is() )
            {
                rtl::Reference < XMLTextImportHelper > xTxtImport =
                    GetImport().GetTextImport();
                mxOldCursor = xTxtImport->GetCursor();
                mxCursor = xText->createTextCursor();
                if( mxCursor.is() )
                {
                    xTxtImport->SetCursor( mxCursor );
                }

                // remember old list item and block (#91964#) and reset them
                // for the text frame
                xTxtImport->PushListContext();
                mbListContextPushed = true;
            }
        }

        // if we have a text cursor, let's try to import some text
        if( mxCursor.is() )
        {
            xContext = GetImport().GetTextImport()->CreateTextChildContext(
                GetImport(), nElement, xAttrList,
                ( mbTextBox ? XMLTextType::TextBox : XMLTextType::Shape ) );
        }
    }

    if (!xContext)
        XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);

    return xContext;
}

void SdXMLShapeContext::addGluePoint( const uno::Reference< xml::sax::XFastAttributeList>& xAttrList )
{
    // get the gluepoints container for this shape if it's not already there
    if( !mxGluePoints.is() )
    {
        uno::Reference< drawing::XGluePointsSupplier > xSupplier( mxShape, uno::UNO_QUERY );
        if( !xSupplier.is() )
            return;

        mxGluePoints.set( xSupplier->getGluePoints(), UNO_QUERY );

        if( !mxGluePoints.is() )
            return;
    }

    drawing::GluePoint2 aGluePoint;
    aGluePoint.IsUserDefined = true;
    aGluePoint.Position.X = 0;
    aGluePoint.Position.Y = 0;
    aGluePoint.Escape = drawing::EscapeDirection_SMART;
    aGluePoint.PositionAlignment = drawing::Alignment_CENTER;
    aGluePoint.IsRelative = true;

    sal_Int32 nId = -1;

    // read attributes for the 3DScene
    for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
    {
        switch(aIter.getToken())
        {
            case XML_ELEMENT(SVG, XML_X):
            case XML_ELEMENT(SVG_COMPAT, XML_X):
                GetImport().GetMM100UnitConverter().convertMeasureToCore(
                        aGluePoint.Position.X, aIter.toView());
                break;
            case XML_ELEMENT(SVG, XML_Y):
            case XML_ELEMENT(SVG_COMPAT, XML_Y):
                GetImport().GetMM100UnitConverter().convertMeasureToCore(
                        aGluePoint.Position.Y, aIter.toView());
                break;
            case XML_ELEMENT(DRAW, XML_ID):
                nId = aIter.toInt32();
                break;
            case XML_ELEMENT(DRAW, XML_ALIGN):
            {
                drawing::Alignment eKind;
                if( SvXMLUnitConverter::convertEnum( eKind, aIter.toView(), aXML_GlueAlignment_EnumMap ) )
                {
                    aGluePoint.PositionAlignment = eKind;
                    aGluePoint.IsRelative = false;
                }
                break;
            }
            case XML_ELEMENT(DRAW, XML_ESCAPE_DIRECTION):
            {
                SvXMLUnitConverter::convertEnum( aGluePoint.Escape, aIter.toView(), aXML_GlueEscapeDirection_EnumMap );
                break;
            }
            default:
                XMLOFF_WARN_UNKNOWN("xmloff", aIter);
        }
    }

    if( nId != -1 )
    {
        try
        {
            sal_Int32 nInternalId = mxGluePoints->insert( uno::Any( aGluePoint ) );
            GetImport().GetShapeImport()->addGluePointMapping( mxShape, nId, nInternalId );
        }
        catch(const uno::Exception&)
        {
            DBG_UNHANDLED_EXCEPTION( "xmloff", "during setting of gluepoints");
        }
    }
}

void SdXMLShapeContext::startFastElement (sal_Int32 /*nElement*/,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/)
{
    GetImport().GetShapeImport()->finishShape( mxShape, mxAttrList, mxShapes );
}

void SdXMLShapeContext::endFastElement(sal_Int32 )
{
    if(mxCursor.is())
    {
        // tdf#72776 force UpdateData in the EditSource so we will not override text in SdrOutliner
        if( mxLockable.is() )
        {
            mxLockable->removeActionLock();
            mxLockable->addActionLock();
        }

        // delete addition newline
        mxCursor->gotoEnd( false );
        mxCursor->goLeft( 1, true );
        mxCursor->setString( u""_ustr );

        // reset cursor
        GetImport().GetTextImport()->ResetCursor();
    }

    if(mxOldCursor.is())
        GetImport().GetTextImport()->SetCursor( mxOldCursor );

    // reinstall old list item (if necessary) #91964#
    if (mbListContextPushed) {
        GetImport().GetTextImport()->PopListContext();
    }

    if( !msHyperlink.isEmpty() ) try
    {
        uno::Reference< beans::XPropertySet > xProp( mxShape, uno::UNO_QUERY );

        if ( xProp.is() && xProp->getPropertySetInfo()->hasPropertyByName( u"Hyperlink"_ustr ) )
            xProp->setPropertyValue( u"Hyperlink"_ustr, uno::Any( msHyperlink ) );
        Reference< XEventsSupplier > xEventsSupplier( mxShape, UNO_QUERY );

        if( xEventsSupplier.is() )
        {
            Reference< XNameReplace > xEvents( xEventsSupplier->getEvents(), UNO_SET_THROW );

            uno::Sequence< beans::PropertyValue > aProperties{
                { /* Name   */ u"EventType"_ustr,
                  /* Handle */ -1,
                  /* Value  */ uno::Any(u"Presentation"_ustr),
                  /* State  */ beans::PropertyState_DIRECT_VALUE },

                { /* Name   */ u"ClickAction"_ustr,
                  /* Handle */ -1,
                  /* Value  */ uno::Any(css::presentation::ClickAction_DOCUMENT),
                  /* State  */ beans::PropertyState_DIRECT_VALUE },

                { /* Name   */ u"Bookmark"_ustr,
                  /* Handle */ -1,
                  /* Value  */ uno::Any(msHyperlink),
                  /* State  */ beans::PropertyState_DIRECT_VALUE }
            };

            xEvents->replaceByName( u"OnClick"_ustr, Any( aProperties ) );
        }
        else
        {
            // in draw use the Bookmark property
            Reference< beans::XPropertySet > xSet( mxShape, UNO_QUERY_THROW );
            xSet->setPropertyValue( u"Bookmark"_ustr, Any( msHyperlink ) );
            xSet->setPropertyValue(u"OnClick"_ustr, Any( css::presentation::ClickAction_DOCUMENT ) );
        }
    }
    catch(const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("xmloff", "while setting hyperlink");
    }

    if( mxLockable.is() )
        mxLockable->removeActionLock();
}

void SdXMLShapeContext::AddShape(uno::Reference< drawing::XShape >& xShape)
{
    if(xShape.is())
    {
        // set shape local
        mxShape = xShape;

        if(!maShapeName.isEmpty())
        {
            uno::Reference< container::XNamed > xNamed( mxShape, uno::UNO_QUERY );
            if( xNamed.is() )
                xNamed->setName( maShapeName );
        }

        rtl::Reference< XMLShapeImportHelper > xImp( GetImport().GetShapeImport() );
        xImp->addShape( xShape, mxAttrList, mxShapes );

        if( mbClearDefaultAttributes )
        {
            uno::Reference<beans::XMultiPropertyStates> xMultiPropertyStates(xShape, uno::UNO_QUERY );
            if (xMultiPropertyStates.is())
                xMultiPropertyStates->setAllPropertiesToDefault();
        }

        if( !mbVisible || !mbPrintable ) try
        {
            uno::Reference< beans::XPropertySet > xSet( xShape, uno::UNO_QUERY_THROW );
            if( !mbVisible )
                xSet->setPropertyValue(u"Visible"_ustr, uno::Any( false ) );

            if( !mbPrintable )
                xSet->setPropertyValue(u"Printable"_ustr, uno::Any( false ) );
        }
        catch(const Exception&)
        {
            DBG_UNHANDLED_EXCEPTION( "xmloff", "while setting visible or printable" );
        }

        if(!mbTemporaryShape && (!GetImport().HasTextImport()
            || !GetImport().GetTextImport()->IsInsideDeleteContext()))
        {
            xImp->shapeWithZIndexAdded( xShape, mnZOrder );
        }

        if (mnRelWidth || mnRelHeight)
        {
            uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
            uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
            if (mnRelWidth && xPropertySetInfo->hasPropertyByName(u"RelativeWidth"_ustr))
                xPropertySet->setPropertyValue(u"RelativeWidth"_ustr, uno::Any(mnRelWidth));
            if (mnRelHeight && xPropertySetInfo->hasPropertyByName(u"RelativeHeight"_ustr))
                xPropertySet->setPropertyValue(u"RelativeHeight"_ustr, uno::Any(mnRelHeight));
        }

        if( !maShapeId.isEmpty() )
        {
            uno::Reference< uno::XInterface > xRef( static_cast<uno::XInterface *>(xShape.get()) );
            GetImport().getInterfaceToIdentifierMapper().registerReference( maShapeId, xRef );
        }

        // #91065# count only if counting for shape import is enabled
        if(GetImport().GetShapeImport()->IsHandleProgressBarEnabled())
        {
            // #80365# increment progress bar at load once for each draw object
            GetImport().GetProgressBarHelper()->Increment();
        }
    }

    mxLockable.set( xShape, UNO_QUERY );

    if( mxLockable.is() )
        mxLockable->addActionLock();

}

void SdXMLShapeContext::AddShape(OUString const & serviceName)
{
    uno::Reference< lang::XMultiServiceFactory > xServiceFact(GetImport().GetModel(), uno::UNO_QUERY);
    if(!xServiceFact.is())
        return;

    try
    {
        /* Since fix for issue i33294 the Writer model doesn't support
           com.sun.star.drawing.OLE2Shape anymore.
           To handle Draw OLE objects it's decided to import these
           objects as com.sun.star.drawing.OLE2Shape and convert these
           objects after the import into com.sun.star.drawing.GraphicObjectShape.
        */
        uno::Reference< drawing::XShape > xShape;
        if ( serviceName == "com.sun.star.drawing.OLE2Shape" &&
             uno::Reference< text::XTextDocument >(GetImport().GetModel(), uno::UNO_QUERY).is() )
        {
            xShape.set(xServiceFact->createInstance(u"com.sun.star.drawing.temporaryForXMLImportOLE2Shape"_ustr), uno::UNO_QUERY);
        }
        else if (serviceName == "com.sun.star.drawing.GraphicObjectShape"
                 || serviceName == "com.sun.star.drawing.AppletShape"
                 || serviceName == "com.sun.star.drawing.FrameShape"
                 || serviceName == "com.sun.star.drawing.MediaShape"
                 || serviceName == "com.sun.star.drawing.OLE2Shape"
                 || serviceName == "com.sun.star.drawing.PluginShape"
                 || serviceName == "com.sun.star.presentation.MediaShape")
        {
            // On adding another entry to this list of service names to pass an argument via the WithArguments variant
            // you may need to adjust the more obscure OReportDefinition::createInstanceWithArguments as well as the
            // more obvious SvxUnoDrawMSFactory::createInstanceWithArguments
            xShape.set( xServiceFact->createInstanceWithArguments(serviceName, { css::uno::Any(GetImport().GetDocumentBase()) }),
                        css::uno::UNO_QUERY);
        }
        else
        {
            xShape.set(xServiceFact->createInstance(serviceName), uno::UNO_QUERY);
        }
        if( xShape.is() )
            AddShape( xShape );
    }
    catch(const uno::Exception& e)
    {
        TOOLS_WARN_EXCEPTION("xmloff", "AddShape " << serviceName);
        uno::Sequence<OUString> aSeq { serviceName };
        GetImport().SetError( XMLERROR_FLAG_ERROR | XMLERROR_API,
                              aSeq, e.Message, nullptr );
    }
}

void SdXMLShapeContext::SetTransformation()
{
    if(!mxShape.is())
        return;

    uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);
    if(!xPropSet.is())
        return;

    maUsedTransformation.identity();

    if(maSize.Width != 1 || maSize.Height != 1)
    {
        // take care there are no zeros used by error
        if(0 == maSize.Width)
            maSize.Width = 1;
        if(0 == maSize.Height)
            maSize.Height = 1;

        // set global size. This should always be used.
        maUsedTransformation.scale(maSize.Width, maSize.Height);
    }

    if(maPosition.X != 0 || maPosition.Y != 0)
    {
        // if global position is used, add it to transformation
        maUsedTransformation.translate(maPosition.X, maPosition.Y);
    }

    if(mnTransform.NeedsAction())
    {
        // transformation is used, apply to object.
        // NOTICE: The transformation is applied AFTER evtl. used
        // global positioning and scaling is used, so any shear or
        // rotate used herein is applied around the (0,0) position
        // of the PAGE object !!!
        ::basegfx::B2DHomMatrix aMat;
        mnTransform.GetFullTransform(aMat);

        // now add to transformation
        maUsedTransformation *= aMat;
    }

    // now set transformation for this object

    // maUsedTransformtion contains the mathematical correct matrix, which if
    // applied to a unit square would generate the transformed shape. But the property
    // "Transformation" contains a matrix, which can be used in TRSetBaseGeometry
    // and would be created by TRGetBaseGeometry. And those use a mathematically wrong
    // sign for the shearing angle. So we need to adapt the matrix here.
    basegfx::B2DTuple aScale;
    basegfx::B2DTuple aTranslate;
    double fRotate;
    double fShearX;
    maUsedTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
    basegfx::B2DHomMatrix aB2DHomMatrix;
    aB2DHomMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
                                aScale,
                                -fShearX,
                                fRotate,
                            aTranslate);
    drawing::HomogenMatrix3 aUnoMatrix;

    aUnoMatrix.Line1.Column1 = aB2DHomMatrix.get(0, 0);
    aUnoMatrix.Line1.Column2 = aB2DHomMatrix.get(0, 1);
    aUnoMatrix.Line1.Column3 = aB2DHomMatrix.get(0, 2);

    aUnoMatrix.Line2.Column1 = aB2DHomMatrix.get(1, 0);
    aUnoMatrix.Line2.Column2 = aB2DHomMatrix.get(1, 1);
    aUnoMatrix.Line2.Column3 = aB2DHomMatrix.get(1, 2);

    aUnoMatrix.Line3.Column1 = 0;
    aUnoMatrix.Line3.Column2 = 0;
    aUnoMatrix.Line3.Column3 = 1;

    xPropSet->setPropertyValue(u"Transformation"_ustr, Any(aUnoMatrix));
}

void SdXMLShapeContext::SetStyle( bool bSupportsStyle /* = true */)
{
    try
    {
        uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);
        if( !xPropSet.is() )
            return;

        do
        {
            // set style on shape
            if(maDrawStyleName.isEmpty())
                break;

            const SvXMLStyleContext* pStyle = nullptr;
            bool bAutoStyle(false);

            if(GetImport().GetShapeImport()->GetAutoStylesContext())
                pStyle = GetImport().GetShapeImport()->GetAutoStylesContext()->FindStyleChildContext(mnStyleFamily, maDrawStyleName);

            if(pStyle)
                bAutoStyle = true;

            if(!pStyle && GetImport().GetShapeImport()->GetStylesContext())
                pStyle = GetImport().GetShapeImport()->GetStylesContext()->FindStyleChildContext(mnStyleFamily, maDrawStyleName);

            OUString aStyleName = maDrawStyleName;
            uno::Reference< style::XStyle > xStyle;

            XMLPropStyleContext* pDocStyle
                = dynamic_cast<XMLShapeStyleContext*>(const_cast<SvXMLStyleContext*>(pStyle));
            if (pDocStyle)
            {
                if( pDocStyle->GetStyle().is() )
                {
                    xStyle = pDocStyle->GetStyle();
                }
                else
                {
                    aStyleName = pDocStyle->GetParentName();
                }
            }

            if( !xStyle.is() && !aStyleName.isEmpty() )
            {
                try
                {

                    uno::Reference< style::XStyleFamiliesSupplier > xFamiliesSupplier( GetImport().GetModel(), uno::UNO_QUERY );

                    if( xFamiliesSupplier.is() )
                    {
                        uno::Reference< container::XNameAccess > xFamilies( xFamiliesSupplier->getStyleFamilies() );
                        if( xFamilies.is() )
                        {

                            uno::Reference< container::XNameAccess > xFamily;

                            if( XmlStyleFamily::SD_PRESENTATION_ID == mnStyleFamily )
                            {
                                aStyleName = GetImport().GetStyleDisplayName(
                                    XmlStyleFamily::SD_PRESENTATION_ID,
                                    aStyleName );
                                sal_Int32 nPos = aStyleName.lastIndexOf( '-' );
                                if( -1 != nPos )
                                {
                                    OUString aFamily( aStyleName.copy( 0, nPos ) );

                                    xFamilies->getByName( aFamily ) >>= xFamily;
                                    aStyleName = aStyleName.copy( nPos + 1 );
                                }
                            }
                            else
                            {
                                // get graphics family
                                if (xFamilies->hasByName(u"graphics"_ustr))
                                    xFamilies->getByName(u"graphics"_ustr) >>= xFamily;
                                else
                                    xFamilies->getByName(u"GraphicStyles"_ustr) >>= xFamily;

                                aStyleName = GetImport().GetStyleDisplayName(
                                    XmlStyleFamily::SD_GRAPHICS_ID,
                                    aStyleName );
                            }

                            if( xFamily.is() )
                                xFamily->getByName( aStyleName ) >>= xStyle;
                        }
                    }
                }
                catch(const uno::Exception&)
                {
                    DBG_UNHANDLED_EXCEPTION( "xmloff", "finding style for shape" );
                }
            }

            if( bSupportsStyle && xStyle.is() )
            {
                try
                {
                    // set style on object
                    xPropSet->setPropertyValue(u"Style"_ustr, Any(xStyle));
                }
                catch(const uno::Exception&)
                {
                    DBG_UNHANDLED_EXCEPTION( "xmloff", "setting style for shape" );
                }
            }

            // Writer shapes: if this one has a TextBox, set it here. We need to do it before
            // pDocStyle->FillPropertySet, because setting some properties depend on the format
            // having RES_CNTNT attribute (e.g., UNO_NAME_TEXT_(LEFT|RIGHT|UPPER|LOWER)DIST; see
            // SwTextBoxHelper::syncProperty, which indirectly calls SwTextBoxHelper::isTextBox)
            uno::Reference<beans::XPropertySetInfo> xPropertySetInfo
                = xPropSet->getPropertySetInfo();
            static constexpr OUString sTextBox = u"TextBox"_ustr;
            if (xPropertySetInfo->hasPropertyByName(sTextBox))
                xPropSet->setPropertyValue(sTextBox, uno::Any(mbTextBox));

            // if this is an auto style, set its properties
            if(bAutoStyle && pDocStyle)
            {
                // set PropertySet on object
                pDocStyle->FillPropertySet(xPropSet);
            }

        } while(false);

        // try to set text auto style
        do
        {
            // set style on shape
            if( maTextStyleName.isEmpty() )
                break;

            if( nullptr == GetImport().GetShapeImport()->GetAutoStylesContext())
                break;

            const SvXMLStyleContext* pTempStyle = GetImport().GetShapeImport()->GetAutoStylesContext()->FindStyleChildContext(XmlStyleFamily::TEXT_PARAGRAPH, maTextStyleName);
            XMLPropStyleContext* pStyle = const_cast<XMLPropStyleContext*>(dynamic_cast<const XMLPropStyleContext*>( pTempStyle ) ); // use temp var, PTR_CAST is a bad macro, FindStyleChildContext will be called twice
            if( pStyle == nullptr )
                break;

            // set PropertySet on object
            pStyle->FillPropertySet(xPropSet);

        } while(false);
    }
    catch(const uno::Exception&)
    {
    }
}

void SdXMLShapeContext::SetLayer()
{
    if( maLayerName.isEmpty() )
        return;

    try
    {
        uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);
        if(xPropSet.is() )
        {
            xPropSet->setPropertyValue(u"LayerName"_ustr, Any(maLayerName));
            return;
        }
    }
    catch(const uno::Exception&)
    {
    }
}

void SdXMLShapeContext::SetThumbnail()
{
    if( maThumbnailURL.isEmpty() )
        return;

    try
    {
        uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);
        if( !xPropSet.is() )
            return;

        uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xPropSet->getPropertySetInfo() );
        if( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName( u"ThumbnailGraphic"_ustr ) )
        {
            // load the thumbnail graphic and export it to a wmf stream so we can set
            // it at the api

            uno::Reference<graphic::XGraphic> xGraphic = GetImport().loadGraphicByURL(maThumbnailURL);
            xPropSet->setPropertyValue(u"ThumbnailGraphic"_ustr, uno::Any(xGraphic));
        }
    }
    catch(const uno::Exception&)
    {
    }
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    sal_Int32 nTmp;
    switch (aIter.getToken())
    {
        case XML_ELEMENT(DRAW, XML_ZINDEX):
        case XML_ELEMENT(DRAW_EXT, XML_ZINDEX):
            mnZOrder = aIter.toInt32();
            break;
        case XML_ELEMENT(DRAW, XML_ID):
        case XML_ELEMENT(DRAW_EXT, XML_ID):
            if (!mbHaveXmlId) { maShapeId = aIter.toString(); }
            break;
        case XML_ELEMENT(DRAW, XML_NAME):
        case XML_ELEMENT(DRAW_EXT, XML_NAME):
            maShapeName = aIter.toString();
            break;
        case XML_ELEMENT(DRAW, XML_STYLE_NAME):
        case XML_ELEMENT(DRAW_EXT, XML_STYLE_NAME):
            maDrawStyleName = aIter.toString();
            break;
        case XML_ELEMENT(DRAW, XML_TEXT_STYLE_NAME):
        case XML_ELEMENT(DRAW_EXT, XML_TEXT_STYLE_NAME):
            maTextStyleName = aIter.toString();
            break;
        case XML_ELEMENT(DRAW, XML_LAYER):
        case XML_ELEMENT(DRAW_EXT, XML_LAYER):
            maLayerName = aIter.toString();
            break;
        case XML_ELEMENT(DRAW, XML_DISPLAY):
        case XML_ELEMENT(DRAW_EXT, XML_DISPLAY):
            mbVisible = IsXMLToken( aIter, XML_ALWAYS ) || IsXMLToken( aIter, XML_SCREEN );
            mbPrintable = IsXMLToken( aIter, XML_ALWAYS ) || IsXMLToken( aIter, XML_PRINTER );
            break;
        case XML_ELEMENT(PRESENTATION, XML_USER_TRANSFORMED):
            mbIsUserTransformed = IsXMLToken( aIter, XML_TRUE );
            break;
        case XML_ELEMENT(PRESENTATION, XML_PLACEHOLDER):
            mbIsPlaceholder = IsXMLToken( aIter, XML_TRUE );
            if( mbIsPlaceholder )
                mbClearDefaultAttributes = false;
            break;
        case XML_ELEMENT(PRESENTATION, XML_CLASS):
            maPresentationClass = aIter.toString();
            break;
        case XML_ELEMENT(PRESENTATION, XML_STYLE_NAME):
            maDrawStyleName = aIter.toString();
            mnStyleFamily = XmlStyleFamily::SD_PRESENTATION_ID;
            break;
        case XML_ELEMENT(SVG, XML_X):
        case XML_ELEMENT(SVG_COMPAT, XML_X):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maPosition.X, aIter.toView());
            break;
        case XML_ELEMENT(SVG, XML_Y):
        case XML_ELEMENT(SVG_COMPAT, XML_Y):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maPosition.Y, aIter.toView());
            break;
        case XML_ELEMENT(SVG, XML_WIDTH):
        case XML_ELEMENT(SVG_COMPAT, XML_WIDTH):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maSize.Width, aIter.toView());
            if (maSize.Width > 0)
                maSize.Width = o3tl::saturating_add<sal_Int32>(maSize.Width, 1);
            else if (maSize.Width < 0)
                maSize.Width = o3tl::saturating_add<sal_Int32>(maSize.Width, -1);
            break;
        case XML_ELEMENT(SVG, XML_HEIGHT):
        case XML_ELEMENT(SVG_COMPAT, XML_HEIGHT):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maSize.Height, aIter.toView());
            if (maSize.Height > 0)
                maSize.Height = o3tl::saturating_add<sal_Int32>(maSize.Height, 1);
            else if (maSize.Height < 0)
                maSize.Height = o3tl::saturating_add<sal_Int32>(maSize.Height, -1);
            break;
        case XML_ELEMENT(DRAW, XML_TRANSFORM):
        case XML_ELEMENT(DRAW_EXT, XML_TRANSFORM):
        case XML_ELEMENT(SVG, XML_TRANSFORM):
        case XML_ELEMENT(SVG_COMPAT, XML_TRANSFORM):
            // because of #85127# take svg:transform into account and handle like
            // draw:transform for compatibility
            mnTransform.SetString(aIter.toString(), GetImport().GetMM100UnitConverter());
            break;
        case XML_ELEMENT(STYLE, XML_REL_WIDTH):
            if (sax::Converter::convertPercent(nTmp, aIter.toView()))
                mnRelWidth = static_cast<sal_Int16>(nTmp);
            break;
        case XML_ELEMENT(STYLE, XML_REL_HEIGHT):
            if (sax::Converter::convertPercent(nTmp, aIter.toView()))
                mnRelHeight = static_cast<sal_Int16>(nTmp);
            break;
        case XML_ELEMENT(NONE, XML_ID):
        case XML_ELEMENT(XML, XML_ID):
            maShapeId = aIter.toString();
            mbHaveXmlId = true;
            break;
        default:
            return false;
    }
    return true;
}

bool SdXMLShapeContext::isPresentationShape() const
{
    if( !maPresentationClass.isEmpty() && const_cast<SdXMLShapeContext*>(this)->GetImport().GetShapeImport()->IsPresentationShapesSupported() )
    {
        if(XmlStyleFamily::SD_PRESENTATION_ID == mnStyleFamily)
        {
            return true;
        }

        if( IsXMLToken( maPresentationClass, XML_HEADER ) || IsXMLToken( maPresentationClass, XML_FOOTER ) ||
            IsXMLToken( maPresentationClass, XML_PAGE_NUMBER ) || IsXMLToken( maPresentationClass, XML_DATE_TIME ) )
        {
            return true;
        }
    }

    return false;
}

SdXMLRectShapeContext::SdXMLRectShapeContext(
    SvXMLImport& rImport,
    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes > const & rShapes,
    bool bTemporaryShape)
:   SdXMLShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ),
    mnRadius( 0 )
{
}

SdXMLRectShapeContext::~SdXMLRectShapeContext()
{
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLRectShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch (aIter.getToken())
    {
        case XML_ELEMENT(DRAW, XML_CORNER_RADIUS):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    mnRadius, aIter.toView());
            break;
        default:
            return SdXMLShapeContext::processAttribute( aIter );
    }
    return true;
}

void SdXMLRectShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // create rectangle shape
    AddShape(u"com.sun.star.drawing.RectangleShape"_ustr);
    if(!mxShape.is())
        return;

    // Add, set Style and properties from base shape
    SetStyle();
    SetLayer();

    // set pos, size, shear and rotate
    SetTransformation();

    if(mnRadius)
    {
        uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);
        if(xPropSet.is())
        {
            try
            {
                xPropSet->setPropertyValue(u"CornerRadius"_ustr, uno::Any( mnRadius ) );
            }
            catch(const uno::Exception&)
            {
                DBG_UNHANDLED_EXCEPTION( "xmloff", "setting corner radius");
            }
        }
    }
    SdXMLShapeContext::startFastElement(nElement, xAttrList);
}


SdXMLLineShapeContext::SdXMLLineShapeContext(
    SvXMLImport& rImport,
    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes > const & rShapes,
    bool bTemporaryShape)
:   SdXMLShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ),
    mnX1( 0 ),
    mnY1( 0 ),
    mnX2( 1 ),
    mnY2( 1 )
{
}

SdXMLLineShapeContext::~SdXMLLineShapeContext()
{
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLLineShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch (aIter.getToken())
    {
        case XML_ELEMENT(SVG, XML_X1):
        case XML_ELEMENT(SVG_COMPAT, XML_X1):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    mnX1, aIter.toView());
            break;
        case XML_ELEMENT(SVG, XML_Y1):
        case XML_ELEMENT(SVG_COMPAT, XML_Y1):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    mnY1, aIter.toView());
            break;
        case XML_ELEMENT(SVG, XML_X2):
        case XML_ELEMENT(SVG_COMPAT, XML_X2):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    mnX2, aIter.toView());
            break;
        case XML_ELEMENT(SVG, XML_Y2):
        case XML_ELEMENT(SVG_COMPAT, XML_Y2):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    mnY2, aIter.toView());
            break;
        default:
            return SdXMLShapeContext::processAttribute( aIter );
    }
    return true;
}

void SdXMLLineShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // #85920# use SetTransformation() to handle import of simple lines.
    // This is necessary to take into account all anchor positions and
    // other things. All shape imports use the same import schemata now.
    // create necessary shape (Line Shape)
    AddShape(u"com.sun.star.drawing.PolyLineShape"_ustr);

    if(!mxShape.is())
        return;

    // Add, set Style and properties from base shape
    SetStyle();
    SetLayer();

    // get sizes and offsets
    awt::Point aTopLeft(mnX1, mnY1);
    awt::Point aBottomRight(mnX2, mnY2);

    if(mnX1 > mnX2)
    {
        aTopLeft.X = mnX2;
        aBottomRight.X = mnX1;
    }

    if(mnY1 > mnY2)
    {
        aTopLeft.Y = mnY2;
        aBottomRight.Y = mnY1;
    }

    // set local parameters on shape
    uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);
    if(xPropSet.is())
    {
        drawing::PointSequenceSequence aPolyPoly(1);
        drawing::PointSequence* pOuterSequence = aPolyPoly.getArray();
        pOuterSequence->realloc(2);
        awt::Point* pInnerSequence = pOuterSequence->getArray();

        *pInnerSequence = awt::Point(o3tl::saturating_sub(mnX1, aTopLeft.X), o3tl::saturating_sub(mnY1, aTopLeft.Y));
        pInnerSequence++;
        *pInnerSequence = awt::Point(o3tl::saturating_sub(mnX2, aTopLeft.X), o3tl::saturating_sub(mnY2, aTopLeft.Y));

        xPropSet->setPropertyValue(u"Geometry"_ustr, Any(aPolyPoly));
    }

    // Size is included in point coordinates
    maSize.Width = 1;
    maSize.Height = 1;
    maPosition.X = aTopLeft.X;
    maPosition.Y = aTopLeft.Y;

    // set pos, size, shear and rotate and get copy of matrix
    SetTransformation();

    SdXMLShapeContext::startFastElement(nElement, xAttrList);
}


SdXMLEllipseShapeContext::SdXMLEllipseShapeContext(
    SvXMLImport& rImport,
    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes > const & rShapes,
    bool bTemporaryShape)
:   SdXMLShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ),
    mnCX( 0 ),
    mnCY( 0 ),
    mnRX( 1 ),
    mnRY( 1 ),
    meKind( drawing::CircleKind_FULL ),
    mnStartAngle( 0 ),
    mnEndAngle( 0 )
{
}

SdXMLEllipseShapeContext::~SdXMLEllipseShapeContext()
{
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLEllipseShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch (aIter.getToken())
    {
        case XML_ELEMENT(SVG, XML_RX):
        case XML_ELEMENT(SVG_COMPAT, XML_RX):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    mnRX, aIter.toView());
            break;
        case XML_ELEMENT(SVG, XML_RY):
        case XML_ELEMENT(SVG_COMPAT, XML_RY):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    mnRY, aIter.toView());
            break;
        case XML_ELEMENT(SVG, XML_CX):
        case XML_ELEMENT(SVG_COMPAT, XML_CX):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    mnCX, aIter.toView());
            break;
        case XML_ELEMENT(SVG, XML_CY):
        case XML_ELEMENT(SVG_COMPAT, XML_CY):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    mnCY, aIter.toView());
            break;
        case XML_ELEMENT(SVG, XML_R):
        case XML_ELEMENT(SVG_COMPAT, XML_R):
            // single radius, it's a circle and both radii are the same
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    mnRX, aIter.toView());
            mnRY = mnRX;
            break;
        case XML_ELEMENT(DRAW, XML_KIND):
            SvXMLUnitConverter::convertEnum( meKind, aIter.toView(), aXML_CircleKind_EnumMap );
            break;
        case XML_ELEMENT(DRAW, XML_START_ANGLE):
        {
            double dStartAngle;
            if (::sax::Converter::convertAngle( dStartAngle, aIter.toView()))
                mnStartAngle = static_cast<sal_Int32>(basegfx::fround(dStartAngle * 100));
            break;
        }
        case XML_ELEMENT(DRAW, XML_END_ANGLE):
        {
            double dEndAngle;
            if (::sax::Converter::convertAngle( dEndAngle, aIter.toView()))
                mnEndAngle = static_cast<sal_Int32>(basegfx::fround(dEndAngle * 100));
            break;
        }
        default:
            return SdXMLShapeContext::processAttribute( aIter );
    }
    return true;
}

void SdXMLEllipseShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // create rectangle shape
    AddShape(u"com.sun.star.drawing.EllipseShape"_ustr);
    if(!mxShape.is())
        return;

    // Add, set Style and properties from base shape
    SetStyle();
    SetLayer();

    if(mnCX != 0 || mnCY != 0 || mnRX != 1 || mnRY != 1)
    {
        // #i121972# center/radius is used, put to pos and size
        maSize.Width = 2 * mnRX;
        maSize.Height = 2 * mnRY;
        maPosition.X = mnCX - mnRX;
        maPosition.Y = mnCY - mnRY;
    }
    // set pos, size, shear and rotate
    SetTransformation();

    if( meKind != drawing::CircleKind_FULL )
    {
        uno::Reference< beans::XPropertySet > xPropSet( mxShape, uno::UNO_QUERY );
        if( xPropSet.is() )
        {
            // calculate the correct start and end angle
            sal_Int32 mnOldStartAngle = mnStartAngle;
            sal_Int32 mnOldEndAngle = mnEndAngle;
            basegfx::B2DTuple aScale;
            basegfx::B2DTuple aTranslate;
            double fRotate;
            double fShearX;
            maUsedTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
            if (aScale.getX() < 0 || aScale.getY() < 0)
            {
                // The angle for a horizontal flip is the same as the angle for a
                // vertical flip because a vertical flip is treated as a horizontal
                // flip plus a rotation.

                // To perform the flip, the start and end angle are switched and we
                // use the fact performing a horizontal flip on a shape will change
                // the angle that a radius makes with the origin to 180 degrees
                // minus that angle (we use 54000 hundredths of a degree to get the
                // modulus operation to give a value between 0 and 36000).

                mnStartAngle = (54000 - mnOldEndAngle) % 36000;
                mnEndAngle = (54000 - mnOldStartAngle) % 36000;
            }

            xPropSet->setPropertyValue(u"CircleKind"_ustr, Any( meKind) );
            xPropSet->setPropertyValue(u"CircleStartAngle"_ustr, Any(mnStartAngle) );
            xPropSet->setPropertyValue(u"CircleEndAngle"_ustr, Any(mnEndAngle) );
        }
    }

    SdXMLShapeContext::startFastElement(nElement, xAttrList);
}


SdXMLPolygonShapeContext::SdXMLPolygonShapeContext(
    SvXMLImport& rImport,
    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes > const & rShapes, bool bClosed, bool bTemporaryShape)
:   SdXMLShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ),
    mbClosed( bClosed )
{
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLPolygonShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch (aIter.getToken())
    {
        case XML_ELEMENT(SVG, XML_VIEWBOX):
        case XML_ELEMENT(SVG_COMPAT, XML_VIEWBOX):
            maViewBox = aIter.toString();
            break;
        case XML_ELEMENT(DRAW, XML_POINTS):
            maPoints = aIter.toString();
            break;
        default:
            return SdXMLShapeContext::processAttribute( aIter);
    }
    return true;
}

SdXMLPolygonShapeContext::~SdXMLPolygonShapeContext()
{
}

void SdXMLPolygonShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // Add, set Style and properties from base shape
    if(mbClosed)
        AddShape(u"com.sun.star.drawing.PolyPolygonShape"_ustr);
    else
        AddShape(u"com.sun.star.drawing.PolyLineShape"_ustr);

    if( !mxShape.is() )
        return;

    SetStyle();
    SetLayer();

    // set local parameters on shape
    uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);
    if(xPropSet.is())
    {
        // set polygon
        if(!maPoints.isEmpty() && !maViewBox.isEmpty())
        {
            const SdXMLImExViewBox aViewBox(maViewBox, GetImport().GetMM100UnitConverter());
            basegfx::B2DVector aSize(aViewBox.GetWidth(), aViewBox.GetHeight());

            // Is this correct? It overrides ViewBox stuff; OTOH it makes no
            // sense to have the geometry content size different from object size
            if(maSize.Width != 0 && maSize.Height != 0)
            {
                aSize = basegfx::B2DVector(maSize.Width, maSize.Height);
            }

            basegfx::B2DPolygon aPolygon;

            if(basegfx::utils::importFromSvgPoints(aPolygon, maPoints))
            {
                if(aPolygon.count())
                {
                    const basegfx::B2DRange aSourceRange(
                        aViewBox.GetX(), aViewBox.GetY(),
                        aViewBox.GetX() + aViewBox.GetWidth(), aViewBox.GetY() + aViewBox.GetHeight());
                    const basegfx::B2DRange aTargetRange(
                        aViewBox.GetX(), aViewBox.GetY(),
                        aViewBox.GetX() + aSize.getX(), aViewBox.GetY() + aSize.getY());

                    if(!aSourceRange.equal(aTargetRange))
                    {
                        aPolygon.transform(
                            basegfx::utils::createSourceRangeTargetRangeTransform(
                                aSourceRange,
                                aTargetRange));
                    }

                    css::drawing::PointSequenceSequence aPointSequenceSequence;
                    basegfx::utils::B2DPolyPolygonToUnoPointSequenceSequence(basegfx::B2DPolyPolygon(aPolygon), aPointSequenceSequence);
                    xPropSet->setPropertyValue(u"Geometry"_ustr, Any(aPointSequenceSequence));
                    // Size is now contained in the point coordinates, adapt maSize for
                    // to use the correct transformation matrix in SetTransformation()
                    maSize.Width = 1;
                    maSize.Height = 1;
                }
            }
        }
    }

    // set pos, size, shear and rotate and get copy of matrix
    SetTransformation();

    SdXMLShapeContext::startFastElement(nElement, xAttrList);
}


SdXMLPathShapeContext::SdXMLPathShapeContext(
    SvXMLImport& rImport,
    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes > const & rShapes,
    bool bTemporaryShape)
:   SdXMLShapeContext( rImport, xAttrList, rShapes, bTemporaryShape )
{
}

SdXMLPathShapeContext::~SdXMLPathShapeContext()
{
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLPathShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch (aIter.getToken())
    {
        case XML_ELEMENT(SVG, XML_VIEWBOX):
        case XML_ELEMENT(SVG_COMPAT, XML_VIEWBOX):
            maViewBox = aIter.toString();
            break;
        case XML_ELEMENT(SVG, XML_D):
        case XML_ELEMENT(SVG_COMPAT, XML_D):
            maD = aIter.toString();
            break;
        default:
            return SdXMLShapeContext::processAttribute( aIter );
    }
    return true;
}

void SdXMLPathShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // create polygon shape
    if(maD.isEmpty())
        return;

    const SdXMLImExViewBox aViewBox(maViewBox, GetImport().GetMM100UnitConverter());
    basegfx::B2DVector aSize(aViewBox.GetWidth(), aViewBox.GetHeight());

    // Is this correct? It overrides ViewBox stuff; OTOH it makes no
    // sense to have the geometry content size different from object size
    if(maSize.Width != 0 && maSize.Height != 0)
    {
        aSize = basegfx::B2DVector(maSize.Width, maSize.Height);
    }

    basegfx::B2DPolyPolygon aPolyPolygon;

    if(!basegfx::utils::importFromSvgD(aPolyPolygon, maD, GetImport().needFixPositionAfterZ(), nullptr))
        return;

    if(!aPolyPolygon.count())
        return;

    const basegfx::B2DRange aSourceRange(
        aViewBox.GetX(), aViewBox.GetY(),
        aViewBox.GetX() + aViewBox.GetWidth(), aViewBox.GetY() + aViewBox.GetHeight());
    const basegfx::B2DRange aTargetRange(
        aViewBox.GetX(), aViewBox.GetY(),
        aViewBox.GetX() + aSize.getX(), aViewBox.GetY() + aSize.getY());

    if(!aSourceRange.equal(aTargetRange))
    {
        aPolyPolygon.transform(
            basegfx::utils::createSourceRangeTargetRangeTransform(
                aSourceRange,
                aTargetRange));
    }

    // create shape
    OUString service;

    if(aPolyPolygon.areControlPointsUsed())
    {
        if(aPolyPolygon.isClosed())
        {
            service = "com.sun.star.drawing.ClosedBezierShape";
        }
        else
        {
            service = "com.sun.star.drawing.OpenBezierShape";
        }
    }
    else
    {
        if(aPolyPolygon.isClosed())
        {
            service = "com.sun.star.drawing.PolyPolygonShape";
        }
        else
        {
            service = "com.sun.star.drawing.PolyLineShape";
        }
    }

    // Add, set Style and properties from base shape
    AddShape(service);

    // #89344# test for mxShape.is() and not for mxShapes.is() to support
    // shape import helper classes WITHOUT XShapes (member mxShapes). This
    // is used by the writer.
    if( !mxShape.is() )
        return;

    SetStyle();
    SetLayer();

    // set local parameters on shape
    uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);

    if(xPropSet.is())
    {
        uno::Any aAny;

        // set polygon data
        if(aPolyPolygon.areControlPointsUsed())
        {
            drawing::PolyPolygonBezierCoords aSourcePolyPolygon;

            basegfx::utils::B2DPolyPolygonToUnoPolyPolygonBezierCoords(
                aPolyPolygon,
                aSourcePolyPolygon);
            aAny <<= aSourcePolyPolygon;
        }
        else
        {
            drawing::PointSequenceSequence aSourcePolyPolygon;

            basegfx::utils::B2DPolyPolygonToUnoPointSequenceSequence(
                aPolyPolygon,
                aSourcePolyPolygon);
            aAny <<= aSourcePolyPolygon;
        }

        xPropSet->setPropertyValue(u"Geometry"_ustr, aAny);
        // Size is now contained in the point coordinates, adapt maSize for
        // to use the correct transformation matrix in SetTransformation()
        maSize.Width = 1;
        maSize.Height = 1;
    }

    // set pos, size, shear and rotate
    SetTransformation();

    SdXMLShapeContext::startFastElement(nElement, xAttrList);
}


SdXMLTextBoxShapeContext::SdXMLTextBoxShapeContext(
    SvXMLImport& rImport,
    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes > const & rShapes)
:   SdXMLShapeContext( rImport, xAttrList, rShapes, false/*bTemporaryShape*/ ),
    mnRadius(0),
    maChainNextName(u""_ustr)
{
}

SdXMLTextBoxShapeContext::~SdXMLTextBoxShapeContext()
{
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLTextBoxShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch (aIter.getToken())
    {
        case XML_ELEMENT(DRAW, XML_CORNER_RADIUS):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    mnRadius, aIter.toView());
            break;
        case XML_ELEMENT(DRAW, XML_CHAIN_NEXT_NAME):
            maChainNextName = aIter.toString();
            break;
        default:
            return SdXMLShapeContext::processAttribute( aIter );
    }
    return true;
}

void SdXMLTextBoxShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // create textbox shape
    bool bIsPresShape = false;
    bool bClearText = false;

    OUString service;

    if( isPresentationShape() )
    {
        // check if the current document supports presentation shapes
        if( GetImport().GetShapeImport()->IsPresentationShapesSupported() )
        {
            if( IsXMLToken( maPresentationClass, XML_SUBTITLE ))
            {
                // XmlShapeType::PresSubtitleShape
                service = "com.sun.star.presentation.SubtitleShape";
            }
            else if( IsXMLToken( maPresentationClass, XML_PRESENTATION_OUTLINE ) )
            {
                // XmlShapeType::PresOutlinerShape
                service = "com.sun.star.presentation.OutlinerShape";
            }
            else if( IsXMLToken( maPresentationClass, XML_NOTES ) )
            {
                // XmlShapeType::PresNotesShape
                service = "com.sun.star.presentation.NotesShape";
            }
            else if( IsXMLToken( maPresentationClass, XML_HEADER ) )
            {
                // XmlShapeType::PresHeaderShape
                service = "com.sun.star.presentation.HeaderShape";
                bClearText = true;
            }
            else if( IsXMLToken( maPresentationClass, XML_FOOTER ) )
            {
                // XmlShapeType::PresFooterShape
                service = "com.sun.star.presentation.FooterShape";
                bClearText = true;
            }
            else if( IsXMLToken( maPresentationClass, XML_PAGE_NUMBER ) )
            {
                // XmlShapeType::PresSlideNumberShape
                service = "com.sun.star.presentation.SlideNumberShape";
                bClearText = true;
            }
            else if( IsXMLToken( maPresentationClass, XML_DATE_TIME ) )
            {
                // XmlShapeType::PresDateTimeShape
                service = "com.sun.star.presentation.DateTimeShape";
                bClearText = true;
            }
            else //  IsXMLToken( maPresentationClass, XML_TITLE ) )
            {
                // XmlShapeType::PresTitleTextShape
                service = "com.sun.star.presentation.TitleTextShape";
            }
            bIsPresShape = true;
        }
    }

    if( service.isEmpty() )
    {
        // normal text shape
        service = "com.sun.star.drawing.TextShape";
    }

    // Add, set Style and properties from base shape
    AddShape(service);

    if( !mxShape.is() )
        return;

    SetStyle();
    SetLayer();

    if(bIsPresShape)
    {
        uno::Reference< beans::XPropertySet > xProps(mxShape, uno::UNO_QUERY);
        if(xProps.is())
        {
            uno::Reference< beans::XPropertySetInfo > xPropsInfo( xProps->getPropertySetInfo() );
            if( xPropsInfo.is() )
            {
                if( !mbIsPlaceholder && xPropsInfo->hasPropertyByName(u"IsEmptyPresentationObject"_ustr))
                    xProps->setPropertyValue(u"IsEmptyPresentationObject"_ustr, css::uno::Any(false) );

                if( mbIsUserTransformed && xPropsInfo->hasPropertyByName(u"IsPlaceholderDependent"_ustr))
                    xProps->setPropertyValue(u"IsPlaceholderDependent"_ustr, css::uno::Any(false) );
            }
        }
    }

    if( bClearText )
    {
        uno::Reference< text::XText > xText( mxShape, uno::UNO_QUERY );
        xText->setString( u""_ustr );
    }

    // set parameters on shape
//A AW->CL: Eventually You need to strip scale and translate from the transformation
//A to reach the same goal again.
//A     if(!bIsPresShape || mbIsUserTransformed)
//A     {
//A         // set pos and size on shape, this should remove binding
//A         // to presentation object on masterpage
//A         SetSizeAndPosition();
//A     }

    // set pos, size, shear and rotate
    SetTransformation();

    if(mnRadius)
    {
        uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);
        if(xPropSet.is())
        {
            try
            {
                xPropSet->setPropertyValue(u"CornerRadius"_ustr, uno::Any( mnRadius ) );
            }
            catch(const uno::Exception&)
            {
                DBG_UNHANDLED_EXCEPTION( "xmloff", "setting corner radius");
            }
        }
    }

    if(!maChainNextName.isEmpty())
    {
        uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);
        if(xPropSet.is())
        {
            try
            {
                xPropSet->setPropertyValue(u"TextChainNextName"_ustr,
                                           uno::Any( maChainNextName ) );
            }
            catch(const uno::Exception&)
            {
                DBG_UNHANDLED_EXCEPTION( "xmloff", "setting name of next chain link");
            }
        }
    }

    SdXMLShapeContext::startFastElement(nElement, xAttrList);
}


SdXMLControlShapeContext::SdXMLControlShapeContext(
    SvXMLImport& rImport,
    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes > const & rShapes,
    bool bTemporaryShape)
:   SdXMLShapeContext( rImport, xAttrList, rShapes, bTemporaryShape )
{
}

SdXMLControlShapeContext::~SdXMLControlShapeContext()
{
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLControlShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch (aIter.getToken())
    {
        case XML_ELEMENT(DRAW, XML_CONTROL):
            maFormId = aIter.toString();
            break;
        default:
            return SdXMLShapeContext::processAttribute( aIter );
    }
    return true;
}

void SdXMLControlShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // create Control shape
    // add, set style and properties from base shape
    AddShape(u"com.sun.star.drawing.ControlShape"_ustr);
    if( !mxShape.is() )
        return;

    SAL_WARN_IF( !!maFormId.isEmpty(), "xmloff", "draw:control without a form:id attribute!" );
    if( !maFormId.isEmpty() )
    {
        if( GetImport().IsFormsSupported() )
        {
            uno::Reference< awt::XControlModel > xControlModel( GetImport().GetFormImport()->lookupControl( maFormId ), uno::UNO_QUERY );
            if( xControlModel.is() )
            {
                uno::Reference< drawing::XControlShape > xControl( mxShape, uno::UNO_QUERY );
                if( xControl.is() )
                    xControl->setControl(  xControlModel );

            }
        }
    }

    SetStyle();
    SetLayer();

    // set pos, size, shear and rotate
    SetTransformation();

    SdXMLShapeContext::startFastElement(nElement, xAttrList);
}


SdXMLConnectorShapeContext::SdXMLConnectorShapeContext(
    SvXMLImport& rImport,
    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes > const & rShapes,
    bool bTemporaryShape)
:   SdXMLShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ),
    maStart(0,0),
    maEnd(1,1),
    mnType( drawing::ConnectorType_STANDARD ),
    mnStartGlueId(-1),
    mnEndGlueId(-1),
    mnDelta1(0),
    mnDelta2(0),
    mnDelta3(0),
    mbLikelyOOXMLCurve(true)
{
}

SdXMLConnectorShapeContext::~SdXMLConnectorShapeContext()
{
}

bool SvXMLImport::needFixPositionAfterZ() const
{
    bool bWrongPositionAfterZ( false );
    sal_Int32 nUPD( 0 );
    sal_Int32 nBuildId( 0 );
    if ( getBuildIds( nUPD, nBuildId ) && // test OOo and old versions of LibO and AOO
       ( ( ( nUPD == 641 ) || ( nUPD == 645 ) || ( nUPD == 680 ) || ( nUPD == 300 ) ||
           ( nUPD == 310 ) || ( nUPD == 320 ) || ( nUPD == 330 ) || ( nUPD == 340 ) ||
           ( nUPD == 350 && nBuildId < 202 ) )
       || (getGeneratorVersion() == SvXMLImport::AOO_40x))) // test if AOO 4.0.x
           // apparently bug was fixed in AOO by i#123433 f15874d8f976f3874bdbcb53429eeefa65c28841
    {
        bWrongPositionAfterZ = true;
    }
    return bWrongPositionAfterZ;
}

namespace
{
bool lcl_IsLikelyOOXMLCurve(const basegfx::B2DPolygon& rPolygon)
{
    sal_uInt32 nCount = rPolygon.count();
    if (!rPolygon.areControlPointsUsed() or nCount < 2)
        return false; // no curve at all

    basegfx::B2DVector aStartVec(rPolygon.getNextControlPoint(0) - rPolygon.getB2DPoint(0));
    basegfx::B2DVector aEndVec(rPolygon.getPrevControlPoint(nCount-1) - rPolygon.getB2DPoint(nCount - 1));
    // LibreOffice uses one point less than OOXML for the same underlying bentConnector or
    // STANDARD connector, respectively. A deeper inspection is only needed in case of 2 resulting
    // points. Those connector paths look like a quarter ellipse.
    switch (nCount)
    {
        case 2:
        {
            // In case start and end direction are parallel, it cannot be OOXML because that case
            // introduces a handle on the path and the curve has three points then.
            if (basegfx::areParallel(aStartVec, aEndVec))
                return false;
            // OOXML sets the control point at 1/2, LibreOffice at 2/3 of width or height.
            // A tolerance is used because +-1 deviations due to integer arithmetic in many places.
            basegfx::B2DRange aRect(rPolygon.getB2DPoint(0), rPolygon.getB2DPoint(1));
            if ((basegfx::fTools::equalZero(aStartVec.getX())
                     && basegfx::fTools::equal(aStartVec.getLength() * 2.0, aRect.getHeight(), 2.0))
                || (basegfx::fTools::equalZero(aStartVec.getY())
                     && basegfx::fTools::equal(aStartVec.getLength() * 2.0, aRect.getWidth(), 2.0)))
                return true;
        }
        break;
        case 3:
        case 5:
            return basegfx::areParallel(aStartVec, aEndVec);
        break;
        case 4: // start and end direction are orthogonal
            return basegfx::fTools::equalZero(aStartVec.scalar( aEndVec));
        break;
        default:
            return false;
    }
    return false;
}
} // end namespace

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLConnectorShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch( aIter.getToken() )
    {
        case XML_ELEMENT(DRAW, XML_START_SHAPE):
            maStartShapeId = aIter.toString();
            break;
        case XML_ELEMENT(DRAW, XML_START_GLUE_POINT):
            mnStartGlueId = aIter.toInt32();
            break;
        case XML_ELEMENT(DRAW, XML_END_SHAPE):
            maEndShapeId = aIter.toString();
            break;
        case XML_ELEMENT(DRAW, XML_END_GLUE_POINT):
            mnEndGlueId = aIter.toInt32();
            break;
        case XML_ELEMENT(DRAW, XML_LINE_SKEW):
        {
            OUString sValue = aIter.toString();
            SvXMLTokenEnumerator aTokenEnum( sValue );
            std::u16string_view aToken;
            if( aTokenEnum.getNextToken( aToken ) )
            {
                GetImport().GetMM100UnitConverter().convertMeasureToCore(
                        mnDelta1, aToken);
                if( aTokenEnum.getNextToken( aToken ) )
                {
                    GetImport().GetMM100UnitConverter().convertMeasureToCore(
                            mnDelta2, aToken);
                    if( aTokenEnum.getNextToken( aToken ) )
                    {
                        GetImport().GetMM100UnitConverter().convertMeasureToCore(
                                mnDelta3, aToken);
                    }
                }
            }
            break;
        }
        case XML_ELEMENT(DRAW, XML_TYPE):
        {
            (void)SvXMLUnitConverter::convertEnum( mnType, aIter.toView(), aXML_ConnectionKind_EnumMap );
            break;
        }
        // #121965# draw:transform may be used in ODF1.2, e.g. exports from MS seem to use these
        case XML_ELEMENT(DRAW, XML_TRANSFORM):
            mnTransform.SetString(aIter.toString(), GetImport().GetMM100UnitConverter());
            break;

        case XML_ELEMENT(SVG, XML_X1):
        case XML_ELEMENT(SVG_COMPAT, XML_X1):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maStart.X, aIter.toView());
            break;
        case XML_ELEMENT(SVG, XML_Y1):
        case XML_ELEMENT(SVG_COMPAT, XML_Y1):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maStart.Y, aIter.toView());
            break;
        case XML_ELEMENT(SVG, XML_X2):
        case XML_ELEMENT(SVG_COMPAT, XML_X2):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maEnd.X, aIter.toView());
            break;
        case XML_ELEMENT(SVG, XML_Y2):
        case XML_ELEMENT(SVG_COMPAT, XML_Y2):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maEnd.Y, aIter.toView());
            break;
        case XML_ELEMENT(SVG, XML_D):
        case XML_ELEMENT(SVG_COMPAT, XML_D):
        {
            basegfx::B2DPolyPolygon aPolyPolygon;

            if(basegfx::utils::importFromSvgD(aPolyPolygon, aIter.toString(), GetImport().needFixPositionAfterZ(), nullptr))
            {
                if(aPolyPolygon.count())
                {
                    drawing::PolyPolygonBezierCoords aSourcePolyPolygon;

                    basegfx::utils::B2DPolyPolygonToUnoPolyPolygonBezierCoords(
                        aPolyPolygon,
                        aSourcePolyPolygon);
                    maPath <<= aSourcePolyPolygon;

                    mbLikelyOOXMLCurve = lcl_IsLikelyOOXMLCurve(aPolyPolygon.getB2DPolygon(0));
                }
            }
            break;
        }
        default:
            return SdXMLShapeContext::processAttribute( aIter );
    }
    return true;
}

void SdXMLConnectorShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // For security reasons, do not add empty connectors. There may have been an error in EA2
    // that created empty, far set off connectors (e.g. 63 meters below top of document). This
    // is not guaranteed, but it's definitely safe to not add empty connectors.
    bool bDoAdd(true);

    if(    maStartShapeId.isEmpty()
        && maEndShapeId.isEmpty()
        && maStart.X == maEnd.X
        && maStart.Y == maEnd.Y
        && 0 == mnDelta1
        && 0 == mnDelta2
        && 0 == mnDelta3
        )
    {
        bDoAdd = false;
    }

    if(!bDoAdd)
        return;

    // create Connector shape
    // add, set style and properties from base shape
    AddShape(u"com.sun.star.drawing.ConnectorShape"_ustr);
    if(!mxShape.is())
        return;

    // #121965# if draw:transform is used, apply directly to the start
    // and end positions before using these
    if(mnTransform.NeedsAction())
    {
        // transformation is used, apply to object.
        ::basegfx::B2DHomMatrix aMat;
        mnTransform.GetFullTransform(aMat);

        if(!aMat.isIdentity())
        {
            basegfx::B2DPoint aStart(maStart.X, maStart.Y);
            basegfx::B2DPoint aEnd(maEnd.X, maEnd.Y);

            aStart = aMat * aStart;
            aEnd = aMat * aEnd;

            maStart.X = basegfx::fround(aStart.getX());
            maStart.Y = basegfx::fround(aStart.getY());
            maEnd.X = basegfx::fround(aEnd.getX());
            maEnd.Y = basegfx::fround(aEnd.getY());
        }
    }

    uno::Reference< beans::XPropertySet > xProps( mxShape, uno::UNO_QUERY );
    if (xProps.is())
        xProps->setPropertyValue(u"EdgeOOXMLCurve"_ustr, Any(mbLikelyOOXMLCurve));

    // add connection ids
    if( !maStartShapeId.isEmpty() )
        GetImport().GetShapeImport()->addShapeConnection( mxShape, true, maStartShapeId, mnStartGlueId );
    if( !maEndShapeId.isEmpty() )
        GetImport().GetShapeImport()->addShapeConnection( mxShape, false, maEndShapeId, mnEndGlueId );

    if( xProps.is() )
    {
        xProps->setPropertyValue(u"StartPosition"_ustr, Any(maStart));
        xProps->setPropertyValue(u"EndPosition"_ustr, Any(maEnd) );
        xProps->setPropertyValue(u"EdgeKind"_ustr, Any(mnType) );
        xProps->setPropertyValue(u"EdgeLine1Delta"_ustr, Any(mnDelta1) );
        xProps->setPropertyValue(u"EdgeLine2Delta"_ustr, Any(mnDelta2) );
        xProps->setPropertyValue(u"EdgeLine3Delta"_ustr, Any(mnDelta3) );
    }
    SetStyle();
    SetLayer();

    if ( maPath.hasValue() )
    {
        // #i115492#
        // Ignore svg:d attribute for text documents created by OpenOffice.org
        // versions before OOo 3.3, because these OOo versions are storing
        // svg:d values not using the correct unit.
        bool bApplySVGD( true );
        if ( uno::Reference< text::XTextDocument >(GetImport().GetModel(), uno::UNO_QUERY).is() )
        {
            sal_Int32 nUPD( 0 );
            sal_Int32 nBuild( 0 );
            const bool bBuildIdFound = GetImport().getBuildIds( nUPD, nBuild );
            if ( GetImport().IsTextDocInOOoFileFormat() ||
                 ( bBuildIdFound &&
                   ( ( nUPD == 641 ) || ( nUPD == 645 ) ||  // prior OOo 2.0
                     ( nUPD == 680 ) ||                     // OOo 2.x
                     ( nUPD == 300 ) ||                     // OOo 3.0 - OOo 3.0.1
                     ( nUPD == 310 ) ||                     // OOo 3.1 - OOo 3.1.1
                     ( nUPD == 320 ) ) ) )                  // OOo 3.2 - OOo 3.2.1
            {
                bApplySVGD = false;
            }
        }

        if ( bApplySVGD )
        {
            // tdf#83360 use path data only when redundant data of start and end point coordinates of
            // path start/end and connector start/end is equal. This is to avoid using erroneous
            // or inconsistent path data at import of foreign formats. Office itself always
            // writes out a consistent data set. Not using it when there is inconsistency
            // is okay since the path data is redundant, buffered data just to avoid recalculation
            // of the connector's layout at load time, no real information would be lost.
            // A 'connected' end has prio to direct coordinate data in Start/EndPosition
            // to the path data (which should have the start/end redundant in the path)
            const drawing::PolyPolygonBezierCoords* pSource = static_cast< const drawing::PolyPolygonBezierCoords* >(maPath.getValue());
            const sal_uInt32 nSequenceCount(pSource->Coordinates.getLength());
            bool bStartEqual(false);
            bool bEndEqual(false);

            if(nSequenceCount)
            {
                const drawing::PointSequence& rStartSeq = pSource->Coordinates[0];
                const sal_uInt32 nStartCount = rStartSeq.getLength();

                if(nStartCount)
                {
                    const awt::Point& rStartPoint = rStartSeq.getConstArray()[0];

                    if(rStartPoint.X == maStart.X && rStartPoint.Y == maStart.Y)
                    {
                        bStartEqual = true;
                    }
                }

                const drawing::PointSequence& rEndSeq = pSource->Coordinates[nSequenceCount - 1];
                const sal_uInt32 nEndCount = rEndSeq.getLength();

                if(nEndCount)
                {
                    const awt::Point& rEndPoint = rEndSeq.getConstArray()[nEndCount - 1];

                    if(rEndPoint.X == maEnd.X && rEndPoint.Y == maEnd.Y)
                    {
                        bEndEqual = true;
                    }
                }
            }

            if(!bStartEqual || !bEndEqual)
            {
                bApplySVGD = false;
            }
        }

        if ( bApplySVGD )
        {
            assert(maPath.getValueType() == cppu::UnoType<drawing::PolyPolygonBezierCoords>::get());
            xProps->setPropertyValue(u"PolyPolygonBezier"_ustr, maPath);
        }
    }

    SdXMLShapeContext::startFastElement(nElement, xAttrList);
}


SdXMLMeasureShapeContext::SdXMLMeasureShapeContext(
    SvXMLImport& rImport,
    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes > const & rShapes,
    bool bTemporaryShape)
:   SdXMLShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ),
    maStart(0,0),
    maEnd(1,1)
{
}

SdXMLMeasureShapeContext::~SdXMLMeasureShapeContext()
{
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLMeasureShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch( aIter.getToken() )
    {
        case XML_ELEMENT(SVG, XML_X1):
        case XML_ELEMENT(SVG_COMPAT, XML_X1):
        {
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maStart.X, aIter.toView());
            break;
        }
        case XML_ELEMENT(SVG, XML_Y1):
        case XML_ELEMENT(SVG_COMPAT, XML_Y1):
        {
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maStart.Y, aIter.toView());
            break;
        }
        case XML_ELEMENT(SVG, XML_X2):
        case XML_ELEMENT(SVG_COMPAT, XML_X2):
        {
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maEnd.X, aIter.toView());
            break;
        }
        case XML_ELEMENT(SVG, XML_Y2):
        case XML_ELEMENT(SVG_COMPAT, XML_Y2):
        {
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maEnd.Y, aIter.toView());
            break;
        }
        default:
            return SdXMLShapeContext::processAttribute( aIter );
    }
    return true;
}

void SdXMLMeasureShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // create Measure shape
    // add, set style and properties from base shape
    AddShape(u"com.sun.star.drawing.MeasureShape"_ustr);
    if(!mxShape.is())
        return;

    SetStyle();
    SetLayer();

    uno::Reference< beans::XPropertySet > xProps( mxShape, uno::UNO_QUERY );
    if( xProps.is() )
    {
        xProps->setPropertyValue(u"StartPosition"_ustr, Any(maStart));
        xProps->setPropertyValue(u"EndPosition"_ustr, Any(maEnd) );
    }

    // delete pre created fields
    uno::Reference< text::XText > xText( mxShape, uno::UNO_QUERY );
    if( xText.is() )
    {
        xText->setString( u" "_ustr );
    }

    SdXMLShapeContext::startFastElement(nElement, xAttrList);
}

void SdXMLMeasureShapeContext::endFastElement(sal_Int32 nElement)
{
    do
    {
        // delete pre created fields
        uno::Reference< text::XText > xText( mxShape, uno::UNO_QUERY );
        if( !xText.is() )
            break;

        uno::Reference< text::XTextCursor > xCursor( xText->createTextCursor() );
        if( !xCursor.is() )
            break;

        xCursor->collapseToStart();
        xCursor->goRight( 1, true );
        xCursor->setString( u""_ustr );
    }
    while(false);

    SdXMLShapeContext::endFastElement(nElement);
}


SdXMLPageShapeContext::SdXMLPageShapeContext(
    SvXMLImport& rImport,
    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes > const & rShapes,
    bool bTemporaryShape)
:   SdXMLShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ), mnPageNumber(0)
{
    mbClearDefaultAttributes = false;
}

SdXMLPageShapeContext::~SdXMLPageShapeContext()
{
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLPageShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    if( aIter.getToken() == XML_ELEMENT(DRAW, XML_PAGE_NUMBER) )
        mnPageNumber = aIter.toInt32();
    else
        return SdXMLShapeContext::processAttribute( aIter );
    return true;
}

void SdXMLPageShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // create Page shape
    // add, set style and properties from base shape

    // #86163# take into account which type of PageShape needs to
    // be constructed. It's a presentation shape if presentation:XML_CLASS == XML_PAGE.
    bool bIsPresentation = !maPresentationClass.isEmpty() &&
           GetImport().GetShapeImport()->IsPresentationShapesSupported();

    uno::Reference< lang::XServiceInfo > xInfo( mxShapes, uno::UNO_QUERY );
    const bool bIsOnHandoutPage = xInfo.is() && xInfo->supportsService(u"com.sun.star.presentation.HandoutMasterPage"_ustr);

    if( bIsOnHandoutPage )
    {
        AddShape(u"com.sun.star.presentation.HandoutShape"_ustr);
    }
    else
    {
        if(bIsPresentation && !IsXMLToken( maPresentationClass, XML_PAGE ) )
        {
            bIsPresentation = false;
        }

        if(bIsPresentation)
        {
            AddShape(u"com.sun.star.presentation.PageShape"_ustr);
        }
        else
        {
            AddShape(u"com.sun.star.drawing.PageShape"_ustr);
        }
    }

    if(!mxShape.is())
        return;

    SetStyle();
    SetLayer();

    // set pos, size, shear and rotate
    SetTransformation();

    uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);
    if(xPropSet.is())
    {
        uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xPropSet->getPropertySetInfo() );
        static constexpr OUString aPageNumberStr(u"PageNumber"_ustr);
        if( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(aPageNumberStr))
            xPropSet->setPropertyValue(aPageNumberStr, uno::Any( mnPageNumber ));
    }

    SdXMLShapeContext::startFastElement(nElement, xAttrList);
}


SdXMLCaptionShapeContext::SdXMLCaptionShapeContext(
    SvXMLImport& rImport,
    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes > const & rShapes,
    bool bTemporaryShape)
:   SdXMLShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ),
    // #86616# for correct edge rounding import mnRadius needs to be initialized
    mnRadius( 0 )
{
}

SdXMLCaptionShapeContext::~SdXMLCaptionShapeContext()
{
}

void SdXMLCaptionShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // create Caption shape
    // add, set style and properties from base shape
    AddShape(u"com.sun.star.drawing.CaptionShape"_ustr);
    if( !mxShape.is() )
        return;

    SetStyle();
    SetLayer();

    uno::Reference< beans::XPropertySet > xProps( mxShape, uno::UNO_QUERY );

    // SJ: If AutoGrowWidthItem is set, SetTransformation will lead to the wrong SnapRect
    // because NbcAdjustTextFrameWidthAndHeight() is called (text is set later and center alignment
    // is the default setting, so the top left reference point that is used by the caption point is
    // no longer correct) There are two ways to solve this problem, temporarily disabling the
    // autogrowwidth as we are doing here or to apply the CaptionPoint after setting text
    bool bIsAutoGrowWidth = false;
    if ( xProps.is() )
    {
        uno::Any aAny( xProps->getPropertyValue(u"TextAutoGrowWidth"_ustr) );
        aAny >>= bIsAutoGrowWidth;

        if ( bIsAutoGrowWidth )
            xProps->setPropertyValue(u"TextAutoGrowWidth"_ustr, uno::Any( false ) );
    }

    // set pos, size, shear and rotate
    SetTransformation();
    if( xProps.is() )
        xProps->setPropertyValue(u"CaptionPoint"_ustr, uno::Any( maCaptionPoint ) );

    if ( bIsAutoGrowWidth )
        xProps->setPropertyValue(u"TextAutoGrowWidth"_ustr, uno::Any( true ) );

    if(mnRadius)
    {
        uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);
        if(xPropSet.is())
        {
            try
            {
                xPropSet->setPropertyValue(u"CornerRadius"_ustr, uno::Any( mnRadius ) );
            }
            catch(const uno::Exception&)
            {
                DBG_UNHANDLED_EXCEPTION( "xmloff", "setting corner radius");
            }
        }
    }

    SdXMLShapeContext::startFastElement(nElement, xAttrList);
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLCaptionShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch (aIter.getToken())
    {
        case XML_ELEMENT(DRAW, XML_CAPTION_POINT_X):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maCaptionPoint.X, aIter.toView());
            break;
        case XML_ELEMENT(DRAW, XML_CAPTION_POINT_Y):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    maCaptionPoint.Y, aIter.toView());
            break;
        case XML_ELEMENT(DRAW, XML_CORNER_RADIUS):
            GetImport().GetMM100UnitConverter().convertMeasureToCore(
                    mnRadius, aIter.toView());
            break;
        default:
            return SdXMLShapeContext::processAttribute( aIter );
    }
    return true;
}


SdXMLGraphicObjectShapeContext::SdXMLGraphicObjectShapeContext(
    SvXMLImport& rImport, const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference<drawing::XShapes> const& rShapes)
    : SdXMLShapeContext(rImport, xAttrList, rShapes, false /*bTemporaryShape*/)
    , mnPage(-1)
{
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLGraphicObjectShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch (aIter.getToken())
    {
    case XML_ELEMENT(XLINK, XML_HREF):
        maURL = aIter.toString();
        break;
    case XML_ELEMENT(DRAW, XML_MIME_TYPE):
    case XML_ELEMENT(LO_EXT, XML_MIME_TYPE):
        msMimeType = aIter.toString();
        break;
    case XML_ELEMENT(LO_EXT, XML_PAGE_NUMBER):
        mnPage = aIter.toInt32();
        break;
    default:
        return SdXMLShapeContext::processAttribute(aIter);
    }
    return true;
}

void SdXMLGraphicObjectShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // create graphic object shape
    OUString service;

    if( IsXMLToken( maPresentationClass, XML_GRAPHIC ) && GetImport().GetShapeImport()->IsPresentationShapesSupported() )
    {
        service = "com.sun.star.presentation.GraphicObjectShape";
    }
    else
    {
        service = "com.sun.star.drawing.GraphicObjectShape";
    }

    AddShape(service);

    if(!mxShape.is())
        return;

    SetStyle();
    SetLayer();

    uno::Reference< beans::XPropertySet > xPropset(mxShape, uno::UNO_QUERY);
    if(xPropset.is())
    {
        // since OOo 1.x had no line or fill style for graphics, but may create
        // documents with them, we have to override them here
        sal_Int32 nUPD, nBuildId;
        if( GetImport().getBuildIds( nUPD, nBuildId ) && (nUPD == 645) ) try
        {
            xPropset->setPropertyValue(u"FillStyle"_ustr, Any( FillStyle_NONE ) );
            xPropset->setPropertyValue(u"LineStyle"_ustr, Any( LineStyle_NONE ) );
        }
        catch(const Exception&)
        {
        }

        uno::Reference< beans::XPropertySetInfo > xPropsInfo( xPropset->getPropertySetInfo() );
        if( xPropsInfo.is() && xPropsInfo->hasPropertyByName(u"IsEmptyPresentationObject"_ustr))
            xPropset->setPropertyValue(u"IsEmptyPresentationObject"_ustr, css::uno::Any( mbIsPlaceholder ) );

        if( !mbIsPlaceholder )
        {
            if( !maURL.isEmpty() )
            {
                uno::Reference<graphic::XGraphic> xGraphic
                    = GetImport().loadGraphicByURL(maURL, mnPage);
                if (xGraphic.is())
                {
                    xPropset->setPropertyValue(u"Graphic"_ustr, uno::Any(xGraphic));
                }
            }
        }
    }

    if(mbIsUserTransformed)
    {
        uno::Reference< beans::XPropertySet > xProps(mxShape, uno::UNO_QUERY);
        if(xProps.is())
        {
            uno::Reference< beans::XPropertySetInfo > xPropsInfo( xProps->getPropertySetInfo() );
            if( xPropsInfo.is() )
            {
                if( xPropsInfo->hasPropertyByName(u"IsPlaceholderDependent"_ustr))
                    xProps->setPropertyValue(u"IsPlaceholderDependent"_ustr, css::uno::Any(false) );
            }
        }
    }

    // set pos, size, shear and rotate
    SetTransformation();

    SdXMLShapeContext::startFastElement(nElement, xAttrList);
}

void SdXMLGraphicObjectShapeContext::endFastElement(sal_Int32 nElement)
{
    if (mxBase64Stream.is())
    {
        uno::Reference<graphic::XGraphic> xGraphic(GetImport().loadGraphicFromBase64(mxBase64Stream));
        if (xGraphic.is())
        {
            uno::Reference<beans::XPropertySet> xProperties(mxShape, uno::UNO_QUERY);
            if (xProperties.is())
            {
                xProperties->setPropertyValue(u"Graphic"_ustr, uno::Any(xGraphic));
            }
        }
    }

    SdXMLShapeContext::endFastElement(nElement);
}

css::uno::Reference< css::xml::sax::XFastContextHandler > SdXMLGraphicObjectShapeContext::createFastChildContext(
    sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
{
    css::uno::Reference< css::xml::sax::XFastContextHandler > xContext;

    if( nElement == XML_ELEMENT(OFFICE, XML_BINARY_DATA) )
    {
        if( maURL.isEmpty() && !mxBase64Stream.is() )
        {
            mxBase64Stream = GetImport().GetStreamForGraphicObjectURLFromBase64();
            if( mxBase64Stream.is() )
                xContext = new XMLBase64ImportContext( GetImport(),
                                                    mxBase64Stream );
        }
    }

    // delegate to parent class if no context could be created
    if (!xContext)
        xContext = SdXMLShapeContext::createFastChildContext(nElement,
                                                         xAttrList);

    if (!xContext)
        XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);

    return xContext;
}

SdXMLGraphicObjectShapeContext::~SdXMLGraphicObjectShapeContext()
{

}


SdXMLChartShapeContext::SdXMLChartShapeContext(
    SvXMLImport& rImport,
    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes > const & rShapes,
    bool bTemporaryShape)
:   SdXMLShapeContext( rImport, xAttrList, rShapes, bTemporaryShape )
{
}

void SdXMLChartShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    const bool bIsPresentation = isPresentationShape();

    AddShape(
        bIsPresentation
        ? u"com.sun.star.presentation.ChartShape"_ustr
        : u"com.sun.star.drawing.OLE2Shape"_ustr);

    if(!mxShape.is())
        return;

    SetStyle();
    SetLayer();

    if( !mbIsPlaceholder )
    {
        uno::Reference< beans::XPropertySet > xProps(mxShape, uno::UNO_QUERY);
        if(xProps.is())
        {
            uno::Reference< beans::XPropertySetInfo > xPropsInfo( xProps->getPropertySetInfo() );
            if( xPropsInfo.is() && xPropsInfo->hasPropertyByName(u"IsEmptyPresentationObject"_ustr))
                xProps->setPropertyValue(u"IsEmptyPresentationObject"_ustr, css::uno::Any(false) );

            uno::Any aAny;

            xProps->setPropertyValue(u"CLSID"_ustr, Any(u"12DCAE26-281F-416F-a234-c3086127382e"_ustr) );

            aAny = xProps->getPropertyValue(u"Model"_ustr);
            uno::Reference< frame::XModel > xChartModel;
            if( aAny >>= xChartModel )
            {
#if !ENABLE_WASM_STRIP_CHART
                // WASM_CHART change
                // TODO: Maybe use SdXMLGraphicObjectShapeContext completely instead
                // or try to create as mbIsPlaceholder object adding a Chart visualization
                // that should be available somehow
                mxChartContext.set( GetImport().GetChartImport()->CreateChartContext( GetImport(), xChartModel ) );
#endif
            }
        }
    }

    if(mbIsUserTransformed)
    {
        uno::Reference< beans::XPropertySet > xProps(mxShape, uno::UNO_QUERY);
        if(xProps.is())
        {
            uno::Reference< beans::XPropertySetInfo > xPropsInfo( xProps->getPropertySetInfo() );
            if( xPropsInfo.is() )
            {
                if( xPropsInfo->hasPropertyByName(u"IsPlaceholderDependent"_ustr))
                    xProps->setPropertyValue(u"IsPlaceholderDependent"_ustr, css::uno::Any(false) );
            }
        }
    }

    // set pos, size, shear and rotate
    SetTransformation();

    SdXMLShapeContext::startFastElement(nElement, xAttrList);

    if( mxChartContext.is() )
        mxChartContext->startFastElement( nElement, xAttrList );
}

void SdXMLChartShapeContext::endFastElement(sal_Int32 nElement)
{
    if( mxChartContext.is() )
        mxChartContext->endFastElement(nElement);

    SdXMLShapeContext::endFastElement(nElement);
}

void SdXMLChartShapeContext::characters( const OUString& rChars )
{
    if( mxChartContext.is() )
        mxChartContext->characters( rChars );
}

css::uno::Reference< css::xml::sax::XFastContextHandler > SdXMLChartShapeContext::createFastChildContext(
    sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
{
    if( mxChartContext.is() )
        return mxChartContext->createFastChildContext( nElement, xAttrList );

    return nullptr;
}


SdXMLObjectShapeContext::SdXMLObjectShapeContext( SvXMLImport& rImport,
        const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
        css::uno::Reference< css::drawing::XShapes > const & rShapes)
: SdXMLShapeContext( rImport, xAttrList, rShapes, false/*bTemporaryShape*/ )
{
}

SdXMLObjectShapeContext::~SdXMLObjectShapeContext()
{
}

void SdXMLObjectShapeContext::startFastElement (sal_Int32 /*nElement*/,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/)
{
    // #96717# in theorie, if we don't have a URL we shouldn't even
    // export this OLE shape. But practically it's too risky right now
    // to change this so we better dispose this on load
    //if( !mbIsPlaceholder && ImpIsEmptyURL(maHref) )
    //  return;

    // #100592# this BugFix prevents that a shape is created. CL
    // is thinking about an alternative.
    // #i13140# Check for more than empty string in maHref, there are
    // other possibilities that maHref results in empty container
    // storage names
    if( !(GetImport().getImportFlags() & SvXMLImportFlags::EMBEDDED) && !mbIsPlaceholder && ImpIsEmptyURL(maHref) )
        return;

    OUString service(u"com.sun.star.drawing.OLE2Shape"_ustr);

    bool bIsPresShape = !maPresentationClass.isEmpty() && GetImport().GetShapeImport()->IsPresentationShapesSupported();

    if( bIsPresShape )
    {
        if( IsXMLToken( maPresentationClass, XML_CHART ) )
        {
            service = "com.sun.star.presentation.ChartShape";
        }
        else if( IsXMLToken( maPresentationClass, XML_TABLE ) )
        {
            service = "com.sun.star.presentation.CalcShape";
        }
        else if( IsXMLToken( maPresentationClass, XML_OBJECT ) )
        {
            service = "com.sun.star.presentation.OLE2Shape";
        }
    }

    AddShape(service);

    if( !mxShape.is() )
        return;

    SetLayer();

    if(bIsPresShape)
    {
        uno::Reference< beans::XPropertySet > xProps(mxShape, uno::UNO_QUERY);
        if(xProps.is())
        {
            uno::Reference< beans::XPropertySetInfo > xPropsInfo( xProps->getPropertySetInfo() );
            if( xPropsInfo.is() )
            {
                if( !mbIsPlaceholder && xPropsInfo->hasPropertyByName(u"IsEmptyPresentationObject"_ustr))
                    xProps->setPropertyValue(u"IsEmptyPresentationObject"_ustr, css::uno::Any(false) );

                if( mbIsUserTransformed && xPropsInfo->hasPropertyByName(u"IsPlaceholderDependent"_ustr))
                    xProps->setPropertyValue(u"IsPlaceholderDependent"_ustr, css::uno::Any(false) );
            }
        }
    }

    if( !mbIsPlaceholder && !maHref.isEmpty() )
    {
        uno::Reference< beans::XPropertySet > xProps( mxShape, uno::UNO_QUERY );

        if( xProps.is() )
        {
            OUString aPersistName = GetImport().ResolveEmbeddedObjectURL( maHref, maCLSID );

            if ( GetImport().IsPackageURL( maHref ) )
            {
                static constexpr OUString  sURL( u"vnd.sun.star.EmbeddedObject:"_ustr );

                if ( aPersistName.startsWith( sURL ) )
                    aPersistName = aPersistName.copy( sURL.getLength() );

                xProps->setPropertyValue(u"PersistName"_ustr,
                                          uno::Any( aPersistName ) );
            }
            else
            {
                // this is OOo link object
                xProps->setPropertyValue(u"LinkURL"_ustr,
                                          uno::Any( aPersistName ) );
            }
        }
    }

    // set pos, size, shear and rotate
    SetTransformation();

    SetStyle();

    GetImport().GetShapeImport()->finishShape( mxShape, mxAttrList, mxShapes );
}

void SdXMLObjectShapeContext::endFastElement(sal_Int32 nElement)
{
    if (GetImport().isGeneratorVersionOlderThan(
                SvXMLImport::OOo_34x, SvXMLImport::LO_41x)) // < LO 4.0
    {
        // #i118485#
        // If it's an old file from us written before OOo3.4, we need to correct
        // FillStyle and LineStyle for OLE2 objects. The error was that the old paint
        // implementations just ignored added fill/linestyles completely, thus
        // those objects need to be corrected to not show blue and hairline which
        // always was the default, but would be shown now
        uno::Reference< beans::XPropertySet > xProps(mxShape, uno::UNO_QUERY);

        if( xProps.is() )
        {
            xProps->setPropertyValue(u"FillStyle"_ustr, uno::Any(drawing::FillStyle_NONE));
            xProps->setPropertyValue(u"LineStyle"_ustr, uno::Any(drawing::LineStyle_NONE));
        }
    }

    if( mxBase64Stream.is() )
    {
        OUString aPersistName( GetImport().ResolveEmbeddedObjectURLFromBase64() );
        static constexpr OUStringLiteral  sURL( u"vnd.sun.star.EmbeddedObject:" );

        aPersistName = aPersistName.copy( sURL.getLength() );

        uno::Reference< beans::XPropertySet > xProps(mxShape, uno::UNO_QUERY);
        if( xProps.is() )
            xProps->setPropertyValue(u"PersistName"_ustr, uno::Any( aPersistName ) );
    }

    SdXMLShapeContext::endFastElement(nElement);
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLObjectShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch( aIter.getToken() )
    {
        case XML_ELEMENT(DRAW, XML_CLASS_ID):
            maCLSID = aIter.toString();
            break;
        case XML_ELEMENT(XLINK, XML_HREF):
            maHref = aIter.toString();
            break;
        default:
            return SdXMLShapeContext::processAttribute( aIter );
    }
    return true;
}

css::uno::Reference< css::xml::sax::XFastContextHandler > SdXMLObjectShapeContext::createFastChildContext(
    sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
{
    if(nElement == XML_ELEMENT(OFFICE, XML_BINARY_DATA))
    {
        mxBase64Stream = GetImport().GetStreamForEmbeddedObjectURLFromBase64();
        if( mxBase64Stream.is() )
            return new XMLBase64ImportContext( GetImport(), mxBase64Stream );
    }
    else if( nElement == XML_ELEMENT(OFFICE, XML_DOCUMENT) ||
             nElement == XML_ELEMENT(MATH, XML_MATH) )
    {
        rtl::Reference<XMLEmbeddedObjectImportContext> xEContext(
            new XMLEmbeddedObjectImportContext(GetImport(), nElement, xAttrList));
        maCLSID = xEContext->GetFilterCLSID();
        if( !maCLSID.isEmpty() )
        {
            uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);
            if( xPropSet.is() )
            {
                xPropSet->setPropertyValue(u"CLSID"_ustr, uno::Any( maCLSID ) );

                // A hack: getting the model of newly created OLE object will eventually call
                // SdrOle2Obj::AddOwnLightClient, which will try to update scaling, using the
                // incomplete object data. Avoid that by setting the special property.
                xPropSet->setPropertyValue(u"IgnoreOLEObjectScale"_ustr, uno::Any(true));

                uno::Reference< lang::XComponent > xComp;
                xPropSet->getPropertyValue(u"Model"_ustr) >>= xComp;

                xPropSet->setPropertyValue(u"IgnoreOLEObjectScale"_ustr, uno::Any(false));

                SAL_WARN_IF( !xComp.is(), "xmloff", "no xModel for own OLE format" );
                xEContext->SetComponent(xComp);
            }
        }
        return xEContext;
    }

    // delegate to parent class if no context could be created
    return SdXMLShapeContext::createFastChildContext(nElement, xAttrList);
}

SdXMLAppletShapeContext::SdXMLAppletShapeContext( SvXMLImport& rImport,
        const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
        css::uno::Reference< css::drawing::XShapes > const & rShapes)
: SdXMLShapeContext( rImport, xAttrList, rShapes, false/*bTemporaryShape*/ ),
  mbIsScript( false )
{
}

SdXMLAppletShapeContext::~SdXMLAppletShapeContext()
{
}

void SdXMLAppletShapeContext::startFastElement (sal_Int32 /*nElement*/,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/)
{
    AddShape(u"com.sun.star.drawing.AppletShape"_ustr);

    if( mxShape.is() )
    {
        SetLayer();

        // set pos, size, shear and rotate
        SetTransformation();
        GetImport().GetShapeImport()->finishShape( mxShape, mxAttrList, mxShapes );
    }
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLAppletShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch( aIter.getToken() )
    {
        case XML_ELEMENT(DRAW, XML_APPLET_NAME):
            maAppletName = aIter.toString();
            break;
        case XML_ELEMENT(DRAW, XML_CODE):
            maAppletCode = aIter.toString();
            break;
        case XML_ELEMENT(DRAW, XML_MAY_SCRIPT):
            mbIsScript = IsXMLToken( aIter, XML_TRUE );
            break;
        case XML_ELEMENT(XLINK, XML_HREF):
            maHref = GetImport().GetAbsoluteReference(aIter.toString());
            break;
        default:
            return SdXMLShapeContext::processAttribute( aIter );
    }
    return true;
}

void SdXMLAppletShapeContext::endFastElement(sal_Int32 nElement)
{
    uno::Reference< beans::XPropertySet > xProps( mxShape, uno::UNO_QUERY );
    if( xProps.is() )
    {
        if ( maSize.Width && maSize.Height )
        {
            // the visual area for applet must be set on loading
            awt::Rectangle aRect( 0, 0, maSize.Width, maSize.Height );
            xProps->setPropertyValue(u"VisibleArea"_ustr, Any(aRect) );
        }

        if( maParams.hasElements() )
        {
            xProps->setPropertyValue(u"AppletCommands"_ustr, Any(maParams) );
        }

        if( !maHref.isEmpty() )
        {
            xProps->setPropertyValue(u"AppletCodeBase"_ustr, Any(maHref) );
        }

        if( !maAppletName.isEmpty() )
        {
            xProps->setPropertyValue(u"AppletName"_ustr, Any(maAppletName) );
        }

        if( mbIsScript )
        {
            xProps->setPropertyValue(u"AppletIsScript"_ustr, Any(mbIsScript) );

        }

        if( !maAppletCode.isEmpty() )
        {
            xProps->setPropertyValue(u"AppletCode"_ustr, Any(maAppletCode) );
        }

        xProps->setPropertyValue(u"AppletDocBase"_ustr, Any(GetImport().GetDocumentBase()) );

        SetThumbnail();
    }

    SdXMLShapeContext::endFastElement(nElement);
}

css::uno::Reference< css::xml::sax::XFastContextHandler > SdXMLAppletShapeContext::createFastChildContext(
    sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
{
    if( nElement == XML_ELEMENT(DRAW, XML_PARAM) )
    {
        OUString aParamName, aParamValue;
        // now parse the attribute list and look for draw:name and draw:value
        for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
        {
            if( aIter.getToken() == XML_ELEMENT(DRAW, XML_NAME) )
                aParamName = aIter.toString();
            if( aIter.getToken() == XML_ELEMENT(DRAW, XML_VALUE) )
                aParamValue = aIter.toString();
        }

        if( !aParamName.isEmpty() )
        {
            sal_Int32 nIndex = maParams.getLength();
            maParams.realloc( nIndex + 1 );
            auto pParams = maParams.getArray();
            pParams[nIndex].Name = aParamName;
            pParams[nIndex].Handle = -1;
            pParams[nIndex].Value <<= aParamValue;
            pParams[nIndex].State = beans::PropertyState_DIRECT_VALUE;
        }

        return new SvXMLImportContext( GetImport() );
    }

    return SdXMLShapeContext::createFastChildContext( nElement, xAttrList );
}


SdXMLPluginShapeContext::SdXMLPluginShapeContext( SvXMLImport& rImport,
        const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
        css::uno::Reference< css::drawing::XShapes > const & rShapes) :
SdXMLShapeContext( rImport, xAttrList, rShapes, false/*bTemporaryShape*/ ),
mbMedia( false )
{
}

SdXMLPluginShapeContext::~SdXMLPluginShapeContext()
{
}

void SdXMLPluginShapeContext::startFastElement (sal_Int32 /*nElement*/,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{

    // watch for MimeType attribute to see if we have a media object
    for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
    {
        if( aIter.getToken() == XML_ELEMENT(DRAW, XML_MIME_TYPE) )
        {
            if (::comphelper::IsMediaMimeType(aIter.toView()))
                mbMedia = true;
            // leave this loop
            break;
        }
    }

    OUString service;

    bool bIsPresShape = false;

    if( mbMedia )
    {
        service = "com.sun.star.drawing.MediaShape";

        bIsPresShape = !maPresentationClass.isEmpty() && GetImport().GetShapeImport()->IsPresentationShapesSupported();
        if( bIsPresShape )
        {
            if( IsXMLToken( maPresentationClass, XML_OBJECT ) )
            {
                service = "com.sun.star.presentation.MediaShape";
            }
        }
    }
    else
        service = "com.sun.star.drawing.PluginShape";

    AddShape(service);

    if( !mxShape.is() )
        return;

    if (mbMedia)
    {
        // The media may have a crop, apply it.
        SetStyle(/*bSupportsStyle=*/false);
    }

    SetLayer();

    if(bIsPresShape)
    {
        uno::Reference< beans::XPropertySet > xProps( mxShape, uno::UNO_QUERY );
        if(xProps.is())
        {
            uno::Reference< beans::XPropertySetInfo > xPropsInfo( xProps->getPropertySetInfo() );
            if( xPropsInfo.is() )
            {
                if( !mbIsPlaceholder && xPropsInfo->hasPropertyByName(u"IsEmptyPresentationObject"_ustr))
                    xProps->setPropertyValue(u"IsEmptyPresentationObject"_ustr, css::uno::Any(false) );

                if( mbIsUserTransformed && xPropsInfo->hasPropertyByName(u"IsPlaceholderDependent"_ustr))
                    xProps->setPropertyValue(u"IsPlaceholderDependent"_ustr, css::uno::Any(false) );
            }
        }
    }

    // set pos, size, shear and rotate
    SetTransformation();
    GetImport().GetShapeImport()->finishShape( mxShape, mxAttrList, mxShapes );
}

static OUString
lcl_GetMediaReference(SvXMLImport const& rImport, OUString const& rURL)
{
    if (rImport.IsPackageURL(rURL))
    {
        return "vnd.sun.star.Package:" + rURL;
    }
    else
    {
        return rImport.GetAbsoluteReference(rURL);
    }
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLPluginShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch( aIter.getToken() )
    {
        case XML_ELEMENT(DRAW, XML_MIME_TYPE):
            maMimeType = aIter.toString();
            break;
        case XML_ELEMENT(XLINK, XML_HREF):
            maHref = lcl_GetMediaReference(GetImport(), aIter.toString());
            break;
        default:
            return SdXMLShapeContext::processAttribute( aIter );
    }
    return true;
}

void SdXMLPluginShapeContext::endFastElement(sal_Int32 nElement)
{
    uno::Reference< beans::XPropertySet > xProps( mxShape, uno::UNO_QUERY );

    if( xProps.is() )
    {
        if ( maSize.Width && maSize.Height )
        {
            static constexpr OUString sVisibleArea(  u"VisibleArea"_ustr  );
            uno::Reference< beans::XPropertySetInfo > aXPropSetInfo( xProps->getPropertySetInfo() );
            if ( !aXPropSetInfo.is() || aXPropSetInfo->hasPropertyByName( sVisibleArea ) )
            {
                // the visual area for a plugin must be set on loading
                awt::Rectangle aRect( 0, 0, maSize.Width, maSize.Height );
                xProps->setPropertyValue( sVisibleArea, Any(aRect) );
            }
        }

        if( !mbMedia )
        {
            // in case we have a plugin object
            if( maParams.hasElements() )
            {
                xProps->setPropertyValue(u"PluginCommands"_ustr, Any(maParams) );
            }

            if( !maMimeType.isEmpty() )
            {
                xProps->setPropertyValue(u"PluginMimeType"_ustr, Any(maMimeType) );
            }

            if( !maHref.isEmpty() )
            {
                xProps->setPropertyValue(u"PluginURL"_ustr, Any(maHref) );
            }
        }
        else
        {
            // in case we have a media object
            xProps->setPropertyValue( u"MediaURL"_ustr, uno::Any(maHref));
            // could be from old times when a format was unsupported
            // likely already guessed a possibly more accurate MIME type from MediaURL, don't override
            bool bUpdateMimeType = false;
            if (maMimeType != AVMEDIA_MIMETYPE_COMMON)
                bUpdateMimeType = true;
            else
            {
                OUString aExistingMimeType;
                xProps->getPropertyValue(u"MediaMimeType"_ustr) >>= aExistingMimeType;
                if (aExistingMimeType.isEmpty())
                    bUpdateMimeType = true;
            }
            if (bUpdateMimeType)
                xProps->setPropertyValue(u"MediaMimeType"_ustr, uno::Any(maMimeType) );

            for (const auto& rParam : maParams)
            {
                const OUString& rName = rParam.Name;

                if( rName == "Loop" )
                {
                    OUString aValueStr;
                    rParam.Value >>= aValueStr;
                    xProps->setPropertyValue(u"Loop"_ustr,
                        uno::Any( aValueStr == "true" ) );
                }
                else if( rName == "Mute" )
                {
                    OUString aValueStr;
                    rParam.Value >>= aValueStr;
                    xProps->setPropertyValue(u"Mute"_ustr,
                        uno::Any( aValueStr == "true" ) );
                }
                else if( rName == "VolumeDB" )
                {
                    OUString aValueStr;
                    rParam.Value >>= aValueStr;
                    xProps->setPropertyValue(u"VolumeDB"_ustr,
                                                uno::Any( static_cast< sal_Int16 >( aValueStr.toInt32() ) ) );
                }
                else if( rName == "Zoom" )
                {
                    OUString            aZoomStr;
                    media::ZoomLevel    eZoomLevel;

                    rParam.Value >>= aZoomStr;

                    if( aZoomStr == "25%" )
                        eZoomLevel = media::ZoomLevel_ZOOM_1_TO_4;
                    else if( aZoomStr == "50%" )
                        eZoomLevel = media::ZoomLevel_ZOOM_1_TO_2;
                    else if( aZoomStr == "100%" )
                        eZoomLevel = media::ZoomLevel_ORIGINAL;
                    else if( aZoomStr == "200%" )
                        eZoomLevel = media::ZoomLevel_ZOOM_2_TO_1;
                    else if( aZoomStr == "400%" )
                        eZoomLevel = media::ZoomLevel_ZOOM_4_TO_1;
                    else if( aZoomStr == "fit" )
                        eZoomLevel = media::ZoomLevel_FIT_TO_WINDOW;
                    else if( aZoomStr == "fixedfit" )
                        eZoomLevel = media::ZoomLevel_FIT_TO_WINDOW_FIXED_ASPECT;
                    else if( aZoomStr == "fullscreen" )
                        eZoomLevel = media::ZoomLevel_FULLSCREEN;
                    else
                        eZoomLevel = media::ZoomLevel_NOT_AVAILABLE;

                    xProps->setPropertyValue(u"Zoom"_ustr, uno::Any( eZoomLevel ) );
                }
            }
        }

        SetThumbnail();
    }

    SdXMLShapeContext::endFastElement(nElement);
}

css::uno::Reference< css::xml::sax::XFastContextHandler > SdXMLPluginShapeContext::createFastChildContext(
    sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
{
    if( nElement == XML_ELEMENT(DRAW, XML_PARAM) )
    {
        OUString aParamName, aParamValue;
        // now parse the attribute list and look for draw:name and draw:value
        for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
        {
            if( aIter.getToken() == XML_ELEMENT(DRAW, XML_NAME) )
                aParamName = aIter.toString();
            else if( aIter.getToken() == XML_ELEMENT(DRAW, XML_VALUE) )
                aParamValue = aIter.toString();
        }

        if( !aParamName.isEmpty() )
        {
            sal_Int32 nIndex = maParams.getLength();
            maParams.realloc( nIndex + 1 );
            auto pParams = maParams.getArray();
            pParams[nIndex].Name = aParamName;
            pParams[nIndex].Handle = -1;
            pParams[nIndex].Value <<= aParamValue;
            pParams[nIndex].State = beans::PropertyState_DIRECT_VALUE;
        }

        return new SvXMLImportContext( GetImport() );
    }

    return SdXMLShapeContext::createFastChildContext( nElement, xAttrList );
}


SdXMLFloatingFrameShapeContext::SdXMLFloatingFrameShapeContext( SvXMLImport& rImport,
        const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
        css::uno::Reference< css::drawing::XShapes > const & rShapes)
: SdXMLShapeContext( rImport, xAttrList, rShapes, false/*bTemporaryShape*/ )
{
}

SdXMLFloatingFrameShapeContext::~SdXMLFloatingFrameShapeContext()
{
}

uno::Reference<drawing::XShape> SdXMLFloatingFrameShapeContext::CreateFloatingFrameShape() const
{
    uno::Reference<lang::XMultiServiceFactory> xServiceFact(GetImport().GetModel(), uno::UNO_QUERY);
    if (!xServiceFact.is())
        return nullptr;
    uno::Reference<drawing::XShape> xShape(
            xServiceFact->createInstance(u"com.sun.star.drawing.FrameShape"_ustr), uno::UNO_QUERY);
    return xShape;
}

void SdXMLFloatingFrameShapeContext::startFastElement (sal_Int32 /*nElement*/,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/)
{
    uno::Reference<drawing::XShape> xShape(SdXMLFloatingFrameShapeContext::CreateFloatingFrameShape());

    uno::Reference< beans::XPropertySet > xProps(xShape, uno::UNO_QUERY);
    // set FrameURL before AddShape, we have to do it again later because it
    // gets cleared when the SdrOle2Obj is attached to the XShape.  But we want
    // FrameURL to exist when AddShape triggers SetPersistName which itself
    // triggers SdrOle2Obj::CheckFileLink_Impl and at that point we want to
    // know what URL will end up being used. So bodge this by setting FrameURL
    // to the temp pre-SdrOle2Obj attached properties and we can smuggle it
    // eventually into SdrOle2Obj::SetPersistName at the right point after
    // PersistName is set but before SdrOle2Obj::CheckFileLink_Impl is called
    // in order to inform the link manager that this is an IFrame that links to
    // a URL
    if (xProps && !maHref.isEmpty())
        xProps->setPropertyValue(u"FrameURL"_ustr, Any(maHref));

    AddShape(xShape);

    if( !mxShape.is() )
        return;

    SetLayer();

    // set pos, size, shear and rotate
    SetTransformation();

    if( xProps.is() )
    {
        if( !maFrameName.isEmpty() )
        {
            xProps->setPropertyValue(u"FrameName"_ustr, Any(maFrameName) );
        }

        if( !maHref.isEmpty() )
        {
            if (INetURLObject(maHref).IsExoticProtocol())
                GetImport().NotifyMacroEventRead();

            xProps->setPropertyValue(u"FrameURL"_ustr, Any(maHref) );
        }
    }

    SetStyle();

    GetImport().GetShapeImport()->finishShape( mxShape, mxAttrList, mxShapes );
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLFloatingFrameShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    switch( aIter.getToken() )
    {
        case XML_ELEMENT(DRAW, XML_FRAME_NAME):
            maFrameName = aIter.toString();
            break;
        case XML_ELEMENT(XLINK, XML_HREF):
            maHref = GetImport().GetAbsoluteReference(aIter.toString());
            break;
        default:
            return SdXMLShapeContext::processAttribute( aIter );
    }
    return true;
}

void SdXMLFloatingFrameShapeContext::endFastElement(sal_Int32 nElement)
{
    uno::Reference< beans::XPropertySet > xProps( mxShape, uno::UNO_QUERY );

    if( xProps.is() )
    {
        if ( maSize.Width && maSize.Height )
        {
            // the visual area for a floating frame must be set on loading
            awt::Rectangle aRect( 0, 0, maSize.Width, maSize.Height );
            xProps->setPropertyValue(u"VisibleArea"_ustr, Any(aRect) );
        }
    }

    SetThumbnail();
    SdXMLShapeContext::endFastElement(nElement);
}


SdXMLFrameShapeContext::SdXMLFrameShapeContext( SvXMLImport& rImport,
        const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
        css::uno::Reference< css::drawing::XShapes > const & rShapes,
        bool bTemporaryShape)
: SdXMLShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ),
    mbSupportsReplacement( false )
{
    uno::Reference < util::XCloneable > xClone( xAttrList, uno::UNO_QUERY );
    if( xClone.is() )
        mxAttrList.set( xClone->createClone(), uno::UNO_QUERY );
    else
        mxAttrList = new sax_fastparser::FastAttributeList(xAttrList);
}

SdXMLFrameShapeContext::~SdXMLFrameShapeContext()
{
}

void SdXMLFrameShapeContext::removeGraphicFromImportContext(const SvXMLImportContext& rContext)
{
    const SdXMLGraphicObjectShapeContext* pSdXMLGraphicObjectShapeContext = dynamic_cast< const SdXMLGraphicObjectShapeContext* >(&rContext);

    if(!pSdXMLGraphicObjectShapeContext)
        return;

    try
    {
        uno::Reference< container::XChild > xChild(pSdXMLGraphicObjectShapeContext->getShape(), uno::UNO_QUERY_THROW);

        uno::Reference< drawing::XShapes > xParent(xChild->getParent(), uno::UNO_QUERY_THROW);

        // remove from parent
        xParent->remove(pSdXMLGraphicObjectShapeContext->getShape());

        // dispose
        uno::Reference< lang::XComponent > xComp(pSdXMLGraphicObjectShapeContext->getShape(), UNO_QUERY);

        if(xComp.is())
        {
            xComp->dispose();
        }
    }
    catch( uno::Exception& )
    {
        DBG_UNHANDLED_EXCEPTION( "xmloff", "Error in cleanup of multiple graphic object import." );
    }
}

namespace
{
uno::Reference<beans::XPropertySet> getGraphicPropertySetFromImportContext(const SvXMLImportContext& rContext)
{
    uno::Reference<beans::XPropertySet> aPropertySet;
    const SdXMLGraphicObjectShapeContext* pSdXMLGraphicObjectShapeContext = dynamic_cast<const SdXMLGraphicObjectShapeContext*>(&rContext);

    if (pSdXMLGraphicObjectShapeContext)
        aPropertySet.set(pSdXMLGraphicObjectShapeContext->getShape(), uno::UNO_QUERY);

    return aPropertySet;
}

} // end anonymous namespace

uno::Reference<graphic::XGraphic> SdXMLFrameShapeContext::getGraphicFromImportContext(const SvXMLImportContext& rContext) const
{
    uno::Reference<graphic::XGraphic> xGraphic;
    try
    {
        const uno::Reference<beans::XPropertySet> xPropertySet = getGraphicPropertySetFromImportContext(rContext);

        if (xPropertySet.is())
        {
            xPropertySet->getPropertyValue(u"Graphic"_ustr) >>= xGraphic;
        }
    }
    catch( uno::Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("xmloff", "Error in cleanup of multiple graphic object import.");
    }

    return xGraphic;
}

OUString SdXMLFrameShapeContext::getMimeTypeFromImportContext(const SvXMLImportContext& rContext) const
{
    OUString aMimeType;
    const SdXMLGraphicObjectShapeContext* pSdXMLGraphicObjectShapeContext = dynamic_cast<const SdXMLGraphicObjectShapeContext*>(&rContext);

    if (pSdXMLGraphicObjectShapeContext)
        aMimeType = pSdXMLGraphicObjectShapeContext->getMimeType();
    return aMimeType;
}

OUString SdXMLFrameShapeContext::getGraphicPackageURLFromImportContext(const SvXMLImportContext& rContext) const
{
    OUString aRetval;
    const SdXMLGraphicObjectShapeContext* pSdXMLGraphicObjectShapeContext = dynamic_cast< const SdXMLGraphicObjectShapeContext* >(&rContext);

    if(pSdXMLGraphicObjectShapeContext)
    {
        try
        {
            const uno::Reference< beans::XPropertySet > xPropSet(pSdXMLGraphicObjectShapeContext->getShape(), uno::UNO_QUERY_THROW);

            xPropSet->getPropertyValue(u"GraphicStreamURL"_ustr) >>= aRetval;
        }
        catch( uno::Exception& )
        {
            DBG_UNHANDLED_EXCEPTION( "xmloff", "Error in cleanup of multiple graphic object import." );
        }
    }

    return aRetval;
}

css::uno::Reference< css::xml::sax::XFastContextHandler > SdXMLFrameShapeContext::createFastChildContext(
    sal_Int32 nElement,
    const uno::Reference< xml::sax::XFastAttributeList>& xAttrList )
{
    SvXMLImportContextRef xContext;
    if( !mxImplContext.is() )
    {
        SvXMLShapeContext* pShapeContext = XMLShapeImportHelper::CreateFrameChildContext(
                        GetImport(), nElement, xAttrList, mxShapes, mxAttrList );

        xContext = pShapeContext;

        // propagate the hyperlink to child context
        if ( !msHyperlink.isEmpty() )
            pShapeContext->setHyperlink( msHyperlink );

        auto nToken = nElement & TOKEN_MASK;
        bool bMedia = false;
        // Ignore gltf model if necessary and so the fallback image will be imported
        if( nToken == XML_PLUGIN )
        {
            SdXMLPluginShapeContext* pPluginContext = dynamic_cast<SdXMLPluginShapeContext*>(pShapeContext);
            if( pPluginContext && pPluginContext->getMimeType() == "model/vnd.gltf+json" )
            {
                mxImplContext = nullptr;
                return new SvXMLImportContext(GetImport());
            }
            else if (pPluginContext && ::comphelper::IsMediaMimeType(pPluginContext->getMimeType()))
            {
                // The media may have a preview, import it.
                bMedia = true;
            }
        }

        mxImplContext = xContext;
        mbSupportsReplacement = (nToken == XML_OBJECT ) || (nToken == XML_OBJECT_OLE) || bMedia;
        setSupportsMultipleContents(nToken == XML_IMAGE);

        if(getSupportsMultipleContents() && dynamic_cast< SdXMLGraphicObjectShapeContext* >(xContext.get()))
        {
            if ( !maShapeId.isEmpty() )
                GetImport().getInterfaceToIdentifierMapper().reserveIdentifier( maShapeId );

            addContent(*mxImplContext);
        }
    }
    else if(getSupportsMultipleContents() && nElement == XML_ELEMENT(DRAW, XML_IMAGE))
    {
        // read another image
        xContext = XMLShapeImportHelper::CreateFrameChildContext(
            GetImport(), nElement, xAttrList, mxShapes, mxAttrList);
        mxImplContext = xContext;

        if(dynamic_cast< SdXMLGraphicObjectShapeContext* >(xContext.get()))
        {
            addContent(*mxImplContext);
        }
    }
    else if( mbSupportsReplacement && !mxReplImplContext.is() &&
             nElement == XML_ELEMENT(DRAW, XML_IMAGE) )
    {
        // read replacement image
        SvXMLImportContext *pImplContext = mxImplContext.get();
        SdXMLShapeContext *pSContext =
            dynamic_cast<SdXMLShapeContext*>( pImplContext  );
        if( pSContext )
        {
            uno::Reference < beans::XPropertySet > xPropSet(
                    pSContext->getShape(), uno::UNO_QUERY );
            if( xPropSet.is() )
            {
                xContext = new XMLReplacementImageContext( GetImport(),
                                    nElement, xAttrList, xPropSet );
                mxReplImplContext = xContext;
            }
        }
    }
    else if( nElement == XML_ELEMENT(SVG, XML_TITLE) || // #i68101#
             nElement == XML_ELEMENT(SVG_COMPAT, XML_TITLE) ||
             nElement == XML_ELEMENT(SVG, XML_DESC) ||
             nElement == XML_ELEMENT(SVG_COMPAT, XML_DESC) ||
             nElement == XML_ELEMENT(OFFICE, XML_EVENT_LISTENERS) ||
             nElement == XML_ELEMENT(DRAW, XML_GLUE_POINT) ||
             nElement == XML_ELEMENT(DRAW, XML_THUMBNAIL) )
    {
        if (getSupportsMultipleContents())
        {   // tdf#103567 ensure props are set on surviving shape
            // note: no more draw:image can be added once we get here
            mxImplContext = solveMultipleImages();
        }
        SvXMLImportContext *pImplContext = mxImplContext.get();
        xContext = static_cast<SvXMLImportContext*>(dynamic_cast<SdXMLShapeContext&>(*pImplContext).createFastChildContext( nElement,
                                                                        xAttrList ).get());
    }
    else if ( nElement == XML_ELEMENT(DRAW, XML_IMAGE_MAP) )
    {
        if (getSupportsMultipleContents())
        {   // tdf#103567 ensure props are set on surviving shape
            // note: no more draw:image can be added once we get here
            mxImplContext = solveMultipleImages();
        }
        SdXMLShapeContext *pSContext = dynamic_cast< SdXMLShapeContext* >( mxImplContext.get() );
        if( pSContext )
        {
            uno::Reference < beans::XPropertySet > xPropSet( pSContext->getShape(), uno::UNO_QUERY );
            if (xPropSet.is())
            {
                xContext = new XMLImageMapContext(GetImport(), xPropSet);
            }
        }
    }
    else if ( nElement == XML_ELEMENT(LO_EXT, XML_SIGNATURELINE) )
    {
        SdXMLShapeContext* pSContext = dynamic_cast<SdXMLShapeContext*>(mxImplContext.get());
        if (pSContext)
        {
            uno::Reference<beans::XPropertySet> xPropSet(pSContext->getShape(), uno::UNO_QUERY);
            if (xPropSet.is())
            {
                xContext = new SignatureLineContext(GetImport(), nElement, xAttrList,
                                                    pSContext->getShape());
            }
        }
    }
    else if ( nElement == XML_ELEMENT(LO_EXT, XML_QRCODE))
    {
        SdXMLShapeContext* pSContext = dynamic_cast<SdXMLShapeContext*>(mxImplContext.get());
        if (pSContext)
        {
            uno::Reference<beans::XPropertySet> xPropSet(pSContext->getShape(), uno::UNO_QUERY);
            if (xPropSet.is())
            {
                xContext = new QRCodeContext(GetImport(), nElement, xAttrList,
                                                    pSContext->getShape());
            }
        }
    }

    return xContext;
}

void SdXMLFrameShapeContext::startFastElement (sal_Int32 /*nElement*/,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/)
{
    // ignore
}

void SdXMLFrameShapeContext::endFastElement(sal_Int32 nElement)
{
    // solve if multiple image child contexts were imported
    SvXMLImportContextRef const pSelectedContext(solveMultipleImages());
    const SdXMLGraphicObjectShapeContext* pShapeContext(
        dynamic_cast<const SdXMLGraphicObjectShapeContext*>(pSelectedContext.get()));
    if ( pShapeContext )
    {
        assert( mxImplContext.is() );
        const uno::Reference< uno::XInterface > xShape( pShapeContext->getShape() );
        GetImport().getInterfaceToIdentifierMapper().registerReservedReference( maShapeId, xShape );
    }

    if( !mxImplContext.is() )
    {
        // now check if this is an empty presentation object
        for( auto& aIter : sax_fastparser::castToFastAttributeList(mxAttrList) )
        {
            switch (aIter.getToken())
            {
                case XML_ELEMENT(PRESENTATION, XML_PLACEHOLDER):
                    mbIsPlaceholder = IsXMLToken( aIter, XML_TRUE );
                    break;
                case XML_ELEMENT(PRESENTATION, XML_CLASS):
                    maPresentationClass = aIter.toString();
                    break;
                default:;
            }
        }

        if( (!maPresentationClass.isEmpty()) && mbIsPlaceholder )
        {
            uno::Reference< xml::sax::XFastAttributeList> xEmpty;

            enum XMLTokenEnum eToken = XML_TEXT_BOX;

            if( IsXMLToken( maPresentationClass, XML_GRAPHIC ) )
            {
                eToken = XML_IMAGE;

            }
            else if( IsXMLToken( maPresentationClass, XML_PAGE ) )
            {
                eToken = XML_PAGE_THUMBNAIL;
            }
            else if( IsXMLToken( maPresentationClass, XML_CHART ) ||
                     IsXMLToken( maPresentationClass, XML_TABLE ) ||
                     IsXMLToken( maPresentationClass, XML_OBJECT ) )
            {
                eToken = XML_OBJECT;
            }

            auto x = XML_ELEMENT(DRAW, eToken);
            mxImplContext = XMLShapeImportHelper::CreateFrameChildContext(
                    GetImport(), x, mxAttrList, mxShapes, xEmpty );

            if( mxImplContext.is() )
            {
                mxImplContext->startFastElement( x, mxAttrList );
                mxImplContext->endFastElement(x);
            }
        }
    }

    mxImplContext = nullptr;
    SdXMLShapeContext::endFastElement(nElement);
}

bool SdXMLFrameShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    bool bId( false );

    switch ( aIter.getToken() )
    {
        case XML_ELEMENT(DRAW, XML_ID):
        case XML_ELEMENT(DRAW_EXT, XML_ID):
        case XML_ELEMENT(NONE, XML_ID):
        case XML_ELEMENT(XML, XML_ID) :
            bId = true;
            break;
        default:;
    }

    if ( bId )
        return SdXMLShapeContext::processAttribute( aIter );
    return true; // deliberately ignoring other attributes
}


SdXMLCustomShapeContext::SdXMLCustomShapeContext(
    SvXMLImport& rImport,
    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes > const & rShapes)
:   SdXMLShapeContext( rImport, xAttrList, rShapes, false/*bTemporaryShape*/ )
{
    // See the XMLTextFrameContext ctor, a frame has Writer content (and not
    // editeng) if its autostyle has a parent style. Do the same for shapes as well.
    for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
    {
        if (aIter.getToken() == XML_ELEMENT(DRAW, XML_STYLE_NAME))
        {
            OUString aStyleName = aIter.toString();
            if(!aStyleName.isEmpty())
            {
                rtl::Reference<XMLTextImportHelper> xTxtImport = GetImport().GetTextImport();
                XMLPropStyleContext* pStyle = xTxtImport->FindAutoFrameStyle(aStyleName);
                // Note that this an API name, so intentionally not localized.
                // Also allow other Frame styles with the same prefix, we just want to reject
                // Graphics after all.
                if (pStyle && pStyle->GetParentName().startsWith("Frame"))
                {
                    mbTextBox = true;
                    break;
                }
            }
        }
    }
}

SdXMLCustomShapeContext::~SdXMLCustomShapeContext()
{
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLCustomShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    if( aIter.getToken() == XML_ELEMENT(DRAW, XML_ENGINE) )
    {
        maCustomShapeEngine = aIter.toString();
    }
    else if (aIter.getToken() == XML_ELEMENT(DRAW, XML_DATA) )
    {
        maCustomShapeData = aIter.toString();
    }
    else
        return SdXMLShapeContext::processAttribute( aIter );
    return true;
}

void SdXMLCustomShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    // create rectangle shape
    AddShape(u"com.sun.star.drawing.CustomShape"_ustr);
    if ( !mxShape.is() )
        return;

    // Add, set Style and properties from base shape
    SetStyle();
    SetLayer();

    // set pos, size, shear and rotate
    SetTransformation();

    try
    {
        uno::Reference< beans::XPropertySet > xPropSet( mxShape, uno::UNO_QUERY );
        if( xPropSet.is() )
        {
            if ( !maCustomShapeEngine.isEmpty() )
            {
                xPropSet->setPropertyValue( EASGet( EAS_CustomShapeEngine ), Any(maCustomShapeEngine) );
            }
            if ( !maCustomShapeData.isEmpty() )
            {
                xPropSet->setPropertyValue( EASGet( EAS_CustomShapeData ), Any(maCustomShapeData) );
            }
        }
    }
    catch(const uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION( "xmloff", "setting enhanced customshape geometry" );
    }
    SdXMLShapeContext::startFastElement(nElement, xAttrList);
}

void SdXMLCustomShapeContext::endFastElement(sal_Int32 nElement)
{
    // Customshapes remember mirror state in its enhanced geometry.
    // SetTransformation() in StartElement() may have applied mirroring, but that is not yet
    // contained. Merge that information here before writing the property.
    if(!maUsedTransformation.isIdentity())
    {
        basegfx::B2DVector aScale, aTranslate;
        double fRotate, fShearX;

        maUsedTransformation.decompose(aScale, aTranslate, fRotate, fShearX);

        if (aScale.getX() < 0.0)
        {
            static constexpr OUString sName(u"MirroredX"_ustr);
            //fdo#84043 Merge, if property exists, otherwise append it
            auto aI = std::find_if(maCustomShapeGeometry.begin(), maCustomShapeGeometry.end(),
                [](beans::PropertyValue& rValue) { return rValue.Name == sName; });
            if (aI != maCustomShapeGeometry.end())
            {
                beans::PropertyValue& rItem = *aI;
                bool bMirroredX = *o3tl::doAccess<bool>(rItem.Value);
                rItem.Value <<= !bMirroredX;
                rItem.Handle = -1;
                rItem.State = beans::PropertyState_DIRECT_VALUE;
            }
            else
            {
                beans::PropertyValue* pItem;
                maCustomShapeGeometry.emplace_back();
                pItem = &maCustomShapeGeometry.back();
                pItem->Name = sName;
                pItem->Handle = -1;
                pItem->Value <<= true;
                pItem->State = beans::PropertyState_DIRECT_VALUE;
            }
        }

        if (aScale.getY() < 0.0)
        {
            static constexpr OUString sName(u"MirroredY"_ustr);
            //fdo#84043 Merge, if property exists, otherwise append it
            auto aI = std::find_if(maCustomShapeGeometry.begin(), maCustomShapeGeometry.end(),
                [](beans::PropertyValue& rValue) { return rValue.Name == sName; });
            if (aI != maCustomShapeGeometry.end())
            {
                beans::PropertyValue& rItem = *aI;
                bool bMirroredY = *o3tl::doAccess<bool>(rItem.Value);
                rItem.Value <<= !bMirroredY;
                rItem.Handle = -1;
                rItem.State = beans::PropertyState_DIRECT_VALUE;
            }
            else
            {
                beans::PropertyValue* pItem;
                maCustomShapeGeometry.emplace_back();
                pItem = &maCustomShapeGeometry.back();
                pItem->Name = sName;
                pItem->Handle = -1;
                pItem->Value <<= true;
                pItem->State = beans::PropertyState_DIRECT_VALUE;
            }
        }
    }

    if ( !maCustomShapeGeometry.empty() )
    {
        // converting the vector to a sequence
        uno::Sequence< beans::PropertyValue > aSeq( comphelper::containerToSequence(maCustomShapeGeometry) );

        try
        {
            uno::Reference< beans::XPropertySet > xPropSet( mxShape, uno::UNO_QUERY );
            if( xPropSet.is() )
            {
                xPropSet->setPropertyValue( u"CustomShapeGeometry"_ustr, Any(aSeq) );
            }
        }
        catch(const uno::Exception&)
        {
            DBG_UNHANDLED_EXCEPTION( "xmloff", "setting enhanced customshape geometry" );
        }

        sal_Int32 nUPD;
        sal_Int32 nBuild;
        if (GetImport().getBuildIds(nUPD, nBuild))
        {
            if( ((nUPD >= 640 && nUPD <= 645) || (nUPD == 680)) && (nBuild <= 9221) )
            {
                Reference< drawing::XEnhancedCustomShapeDefaulter > xDefaulter( mxShape, UNO_QUERY );
                if( xDefaulter.is() )
                {
                    xDefaulter->createCustomShapeDefaults( u""_ustr );
                }
            }
        }
    }

    SdXMLShapeContext::endFastElement(nElement);

    // tdf#98163 call a custom slot to be able to reset the UNO API
    // implementations held on the SdrObjects of type
    // SdrObjCustomShape - those tend to linger until the entire file
    // is loaded. For large files with a lot of these, 32bit systems
    // may crash due to being out of resources after ca. 4200
    // Outliners and VirtualDevices used there as RefDevice
    try
    {
        uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY);

        if(xPropSet.is())
        {
            xPropSet->setPropertyValue(
                u"FlushCustomShapeUnoApiObjects"_ustr, css::uno::Any(true));
        }
    }
    catch(const uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("xmloff", "flushing after load");
    }
}

css::uno::Reference< css::xml::sax::XFastContextHandler > SdXMLCustomShapeContext::createFastChildContext(
    sal_Int32 nElement,
    const uno::Reference< xml::sax::XFastAttributeList>& xAttrList )
{
    css::uno::Reference< css::xml::sax::XFastContextHandler > xContext;
    if ( nElement == XML_ELEMENT(DRAW, XML_ENHANCED_GEOMETRY) )
    {
        uno::Reference< beans::XPropertySet > xPropSet( mxShape,uno::UNO_QUERY );
        if ( xPropSet.is() )
            xContext = new XMLEnhancedCustomShapeContext( GetImport(), mxShape, maCustomShapeGeometry );
    }
    // delegate to parent class if no context could be created
    if (!xContext)
        xContext = SdXMLShapeContext::createFastChildContext( nElement,
                                                         xAttrList);
    return xContext;
}

SdXMLTableShapeContext::SdXMLTableShapeContext( SvXMLImport& rImport, const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList, css::uno::Reference< css::drawing::XShapes > const & rShapes )
: SdXMLShapeContext( rImport, xAttrList, rShapes, false )
{
}

SdXMLTableShapeContext::~SdXMLTableShapeContext()
{
}

void SdXMLTableShapeContext::startFastElement (sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList)
{
    OUString service(u"com.sun.star.drawing.TableShape"_ustr);

    bool bIsPresShape = !maPresentationClass.isEmpty() && GetImport().GetShapeImport()->IsPresentationShapesSupported();
    if( bIsPresShape )
    {
        if( IsXMLToken( maPresentationClass, XML_TABLE ) )
        {
            service = "com.sun.star.presentation.TableShape";
        }
    }

    AddShape(service);

    if( !mxShape.is() )
        return;

    SetLayer();

    uno::Reference< beans::XPropertySet > xProps(mxShape, uno::UNO_QUERY);

    if(bIsPresShape && xProps.is())
    {
        uno::Reference< beans::XPropertySetInfo > xPropsInfo( xProps->getPropertySetInfo() );
        if( xPropsInfo.is() )
        {
            if( !mbIsPlaceholder && xPropsInfo->hasPropertyByName(u"IsEmptyPresentationObject"_ustr))
                xProps->setPropertyValue(u"IsEmptyPresentationObject"_ustr, css::uno::Any(false) );

            if( mbIsUserTransformed && xPropsInfo->hasPropertyByName(u"IsPlaceholderDependent"_ustr))
                xProps->setPropertyValue(u"IsPlaceholderDependent"_ustr, css::uno::Any(false) );
        }
    }

    SetStyle();

    if( xProps.is() )
    {
        if( !msTemplateStyleName.isEmpty() ) try
        {
            Reference< XStyleFamiliesSupplier > xFamiliesSupp( GetImport().GetModel(), UNO_QUERY_THROW );
            Reference< XNameAccess > xFamilies( xFamiliesSupp->getStyleFamilies() );
            Reference< XNameAccess > xTableFamily( xFamilies->getByName( u"table"_ustr ), UNO_QUERY_THROW );
            Reference< XStyle > xTableStyle( xTableFamily->getByName( msTemplateStyleName ), UNO_QUERY_THROW );
            xProps->setPropertyValue(u"TableTemplate"_ustr, Any( xTableStyle ) );
        }
        catch(const Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("xmloff.draw");
        }

        const XMLPropertyMapEntry* pEntry = &aXMLTableShapeAttributes[0];
        for( int i = 0; !pEntry->IsEnd() && (i < 6); i++, pEntry++ )
        {
            try
            {
                xProps->setPropertyValue( pEntry->getApiName(), Any( maTemplateStylesUsed[i] ) );
            }
            catch(const Exception&)
            {
                DBG_UNHANDLED_EXCEPTION("xmloff.draw");
            }
        }
    }

    GetImport().GetShapeImport()->finishShape( mxShape, mxAttrList, mxShapes );

    const rtl::Reference< XMLTableImport >& xTableImport( GetImport().GetShapeImport()->GetShapeTableImport() );
    if( xTableImport.is() && xProps.is() )
    {
        uno::Reference< table::XColumnRowRange > xColumnRowRange(
            xProps->getPropertyValue(u"Model"_ustr), uno::UNO_QUERY );

        if( xColumnRowRange.is() )
            mxTableImportContext = xTableImport->CreateTableContext( xColumnRowRange );

        if( mxTableImportContext.is() )
            mxTableImportContext->startFastElement( nElement, xAttrList );
    }
}

void SdXMLTableShapeContext::endFastElement(sal_Int32 nElement)
{
    if( mxTableImportContext.is() )
        mxTableImportContext->endFastElement(nElement);

    SdXMLShapeContext::endFastElement(nElement);

    if( mxShape.is() )
    {
        // set pos, size, shear and rotate
        SetTransformation();
    }
}

// this is called from the parent group for each unparsed attribute in the attribute list
bool SdXMLTableShapeContext::processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & aIter )
{
    auto nElement = aIter.getToken();
    if( IsTokenInNamespace(nElement, XML_NAMESPACE_TABLE) )
    {
        if( (nElement & TOKEN_MASK) == XML_TEMPLATE_NAME )
        {
            msTemplateStyleName = aIter.toString();
        }
        else
        {
            int i = 0;
            const XMLPropertyMapEntry* pEntry = &aXMLTableShapeAttributes[0];
            while( !pEntry->IsEnd() && (i < 6) )
            {
                if( (nElement & TOKEN_MASK) == pEntry->meXMLName )
                {
                    if( IsXMLToken( aIter, XML_TRUE ) )
                        maTemplateStylesUsed[i] = true;
                    break;
                }
                pEntry++;
                i++;
            }
        }
    }
    return SdXMLShapeContext::processAttribute( aIter );
}

css::uno::Reference< css::xml::sax::XFastContextHandler > SdXMLTableShapeContext::createFastChildContext(
    sal_Int32 nElement,
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
{
    if( mxTableImportContext.is() && IsTokenInNamespace(nElement, XML_NAMESPACE_TABLE) )
        return mxTableImportContext->createFastChildContext(nElement, xAttrList);
    return SdXMLShapeContext::createFastChildContext(nElement, xAttrList);
}

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