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

#include <com/sun/star/awt/Size.hpp>
#include <com/sun/star/container/XNamed.hpp>
#include <com/sun/star/drawing/PointSequenceSequence.hpp>
#include <com/sun/star/drawing/XShape.hpp>
#include <com/sun/star/drawing/LineStyle.hpp>
#include <com/sun/star/graphic/XGraphic.hpp>
#include <com/sun/star/graphic/GraphicProvider.hpp>
#include <com/sun/star/graphic/XGraphicProvider.hpp>
#include <com/sun/star/io/BufferSizeExceededException.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/table/BorderLine2.hpp>
#include <com/sun/star/text/HoriOrientation.hpp>
#include <com/sun/star/text/RelOrientation.hpp>
#include <com/sun/star/text/TextContentAnchorType.hpp>
#include <com/sun/star/text/VertOrientation.hpp>
#include <com/sun/star/text/WrapTextMode.hpp>
#include <com/sun/star/text/XTextContent.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/table/ShadowFormat.hpp>

#include <svx/svditer.hxx>
#include <svx/svdobj.hxx>
#include <svx/svdogrp.hxx>
#include <svx/svdtrans.hxx>
#include <svx/unoapi.hxx>
#include <cppuhelper/implbase.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <rtl/math.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/string.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <comphelper/sequence.hxx>
#include <oox/drawingml/drawingmltypes.hxx>

#include "DomainMapper.hxx"
#include <dmapper/GraphicZOrderHelper.hxx>
#include <ooxml/resourceids.hxx>

#include "ConversionHelper.hxx"
#include "GraphicHelpers.hxx"
#include "GraphicImport.hxx"
#include "PropertyMap.hxx"
#include "TagLogger.hxx"
#include "util.hxx"

#include <comphelper/propertysequence.hxx>
#include <algorithm>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/range/b2drange.hxx>
#include <basegfx/numeric/ftools.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <o3tl/unit_conversion.hxx>
#include <oox/export/drawingml.hxx>
#include <utility>
#include <unoframe.hxx>
#include <unotxdoc.hxx>

using namespace css;

namespace
{
bool isTopGroupObj(const uno::Reference<drawing::XShape>& xShape)
{
    SdrObject* pObject = SdrObject::getSdrObjectFromXShape(xShape);
    if (!pObject)
        return false;

    if (pObject->getParentSdrObjectFromSdrObject())
        return false;

    return pObject->IsGroupObject();
}
}

namespace writerfilter::dmapper
{

namespace {

class XInputStreamHelper : public cppu::WeakImplHelper<io::XInputStream>
{
    const sal_uInt8* m_pBuffer;
    const sal_Int32  m_nLength;
    sal_Int32        m_nPosition;
public:
    XInputStreamHelper(const sal_uInt8* buf, size_t len);

    virtual ::sal_Int32 SAL_CALL readBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nBytesToRead ) override;
    virtual ::sal_Int32 SAL_CALL readSomeBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nMaxBytesToRead ) override;
    virtual void SAL_CALL skipBytes( ::sal_Int32 nBytesToSkip ) override;
    virtual ::sal_Int32 SAL_CALL available(  ) override;
    virtual void SAL_CALL closeInput(  ) override;
};

}

XInputStreamHelper::XInputStreamHelper(const sal_uInt8* buf, size_t len) :
        m_pBuffer( buf ),
        m_nLength( len ),
        m_nPosition( 0 )
{
}

sal_Int32 XInputStreamHelper::readBytes( uno::Sequence<sal_Int8>& aData, sal_Int32 nBytesToRead )
{
    return readSomeBytes( aData, nBytesToRead );
}

sal_Int32 XInputStreamHelper::readSomeBytes( uno::Sequence<sal_Int8>& aData, sal_Int32 nMaxBytesToRead )
{
    sal_Int32 nRet = 0;
    if( nMaxBytesToRead > 0 )
    {
        if( nMaxBytesToRead > m_nLength - m_nPosition )
            nRet = m_nLength - m_nPosition;
        else
            nRet = nMaxBytesToRead;
        aData.realloc( nRet );
        sal_Int8* pData = aData.getArray();
        if( nRet )
        {
            memcpy( pData, m_pBuffer + m_nPosition, nRet );
            m_nPosition += nRet;
        }
    }
    return nRet;
}


void XInputStreamHelper::skipBytes( sal_Int32 nBytesToSkip )
{
    if( nBytesToSkip < 0 || m_nPosition + nBytesToSkip > m_nLength)
        throw io::BufferSizeExceededException();
    m_nPosition += nBytesToSkip;
}


sal_Int32 XInputStreamHelper::available(  )
{
    return m_nLength - m_nPosition;
}


void XInputStreamHelper::closeInput(  )
{
}

void GraphicImport::setXSize(sal_Int32 _nXSize)
{
    m_nXSize = _nXSize;
    m_bXSizeValid = true;
}

sal_uInt32 GraphicImport::getXSize() const
{
    return m_nXSize;
}

bool GraphicImport::isXSizeValid() const
{
    return m_bXSizeValid;
}

void GraphicImport::setYSize(sal_Int32 _nYSize)
{
    m_nYSize = _nYSize;
    m_bYSizeValid = true;
}

sal_uInt32 GraphicImport::getYSize() const
{
    return m_nYSize;
}

bool GraphicImport::isYSizeValid() const
{
    return m_bYSizeValid;
}

void GraphicImport::applyMargins(const uno::Reference< beans::XPropertySet >& xGraphicObjectProperties) const
{
    xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN ), uno::Any(m_nLeftMargin));
    xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN ), uno::Any(m_nRightMargin));
    xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_TOP_MARGIN ), uno::Any(m_nTopMargin));
    xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN ), uno::Any(m_nBottomMargin));
}

void GraphicImport::applyPosition(const uno::Reference< beans::XPropertySet >& xGraphicObjectProperties) const
{
    xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_HORI_ORIENT          ),
            uno::Any(m_nHoriOrient));
    xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_VERT_ORIENT          ),
            uno::Any(m_nVertOrient));
}

void GraphicImport::applyRelativePosition(const uno::Reference< beans::XPropertySet >& xGraphicObjectProperties, bool bRelativeOnly) const
{
    if (!bRelativeOnly)
        xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_HORI_ORIENT_POSITION),
                                                   uno::Any(m_nLeftPosition));
    xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_HORI_ORIENT_RELATION ),
            uno::Any(m_nHoriRelation));
    xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_PAGE_TOGGLE),
                                               uno::Any(m_bPageToggle));
    if (!bRelativeOnly)
        xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_VERT_ORIENT_POSITION),
                                                   uno::Any(m_nTopPosition));
    xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_VERT_ORIENT_RELATION ),
            uno::Any(m_nVertRelation));
}

void GraphicImport::applyZOrder(uno::Reference<beans::XPropertySet> const & xGraphicObjectProperties) const
{
    std::optional<sal_Int64> oZOrder = m_oZOrder;
    if (m_rGraphicImportType == GraphicImportType::IMPORT_AS_DETECTED_INLINE
        && !m_rDomainMapper.IsInShape())
    {
        oZOrder = SAL_MIN_INT64;
    }
    else if (!oZOrder)
        return;
    else
    {
        const bool bBehindText = m_bBehindDoc && !m_bOpaque;
        GraphicZOrderHelper::adjustRelativeHeight(*oZOrder, /*IsZIndex=*/false, bBehindText,
                                                  m_rDomainMapper.IsInHeaderFooter());
    }

    // TODO: it is possible that RTF has been wrong all along as well. Always true here?
    const bool bLastDuplicateWins(!m_rDomainMapper.IsRTFImport()
        || m_rGraphicImportType == GraphicImportType::IMPORT_AS_DETECTED_INLINE);

    GraphicZOrderHelper& rZOrderHelper = m_rDomainMapper.graphicZOrderHelper();
    xGraphicObjectProperties->setPropertyValue(getPropertyName(PROP_Z_ORDER),
        uno::Any(rZOrderHelper.findZOrder(*oZOrder, bLastDuplicateWins)));
    rZOrderHelper.addItem(xGraphicObjectProperties, *oZOrder);
}

void GraphicImport::applyName(uno::Reference<beans::XPropertySet> const & xGraphicObjectProperties) const
{
    try
    {
        if (!m_sName.isEmpty())
        {
            uno::Reference<container::XNamed> const xNamed(xGraphicObjectProperties, uno::UNO_QUERY_THROW);
            xNamed->setName(m_sName);
        }
        // else: name is automatically generated by SwDoc::MakeFlySection_()

        xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_DESCRIPTION ),
            uno::Any( m_sAlternativeText ));
        xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_TITLE ),
            uno::Any( m_title ));
    }
    catch( const uno::Exception& )
    {
        TOOLS_WARN_EXCEPTION("writerfilter", "failed");
    }
}

void GraphicImport::applyHyperlink(uno::Reference<beans::XPropertySet> const & xShapeProps, bool bIsShape)
{
    // Graphic objects have a different hyperlink prop than shapes
    auto aHyperlinkProp = bIsShape ? PROP_HYPERLINK : PROP_HYPER_LINK_U_R_L;
    if (!m_sHyperlinkURL.isEmpty())
    {
        xShapeProps->setPropertyValue(
            getPropertyName(aHyperlinkProp), uno::Any(m_sHyperlinkURL));
    }
}

/// Getter for m_aInteropGrabBag, but also merges in the values from other members if they are set.
comphelper::SequenceAsHashMap const & GraphicImport::getInteropGrabBag()
{
    comphelper::SequenceAsHashMap aEffectExtent;
    if (m_oEffectExtentLeft)
        aEffectExtent[u"l"_ustr] <<= *m_oEffectExtentLeft;
    if (m_oEffectExtentTop)
        aEffectExtent[u"t"_ustr] <<= *m_oEffectExtentTop;
    if (m_oEffectExtentRight)
        aEffectExtent[u"r"_ustr] <<= *m_oEffectExtentRight;
    if (m_oEffectExtentBottom)
        aEffectExtent[u"b"_ustr] <<= *m_oEffectExtentBottom;
    if (!aEffectExtent.empty())
        m_aInteropGrabBag[u"CT_EffectExtent"_ustr] <<= aEffectExtent.getAsConstPropertyValueList();
    return m_aInteropGrabBag;
}

GraphicImport::GraphicImport(uno::Reference<uno::XComponentContext> xComponentContext,
                             rtl::Reference<SwXTextDocument> xTextDoc,
                             DomainMapper& rDMapper,
                             GraphicImportType & rImportType,
                             std::pair<OUString, OUString>& rPositionOffsets,
                             std::pair<OUString, OUString>& rAligns,
                             std::queue<OUString>& rPositivePercentages)
: LoggedProperties("GraphicImport")
, LoggedTable("GraphicImport")
, LoggedStream("GraphicImport")
, m_nXSize(0)
, m_bXSizeValid(false)
, m_nYSize(0)
, m_bYSizeValid(false)
, m_rGraphicImportType(rImportType)
, m_rDomainMapper( rDMapper )
, m_nLeftPosition(0)
, m_nTopPosition(0)
, m_bUseSimplePos(false)
, m_nHoriOrient(   text::HoriOrientation::NONE )
, m_nHoriRelation( text::RelOrientation::FRAME )
, m_nVertOrient(  text::VertOrientation::NONE )
, m_nVertRelation( text::RelOrientation::FRAME )
, m_nWrap(text::WrapTextMode_NONE)
, m_bLayoutInCell(true)
, m_bCompatForcedLayoutInCell(false)
, m_bOpaque( !rDMapper.IsInHeaderFooter() )
, m_bBehindDoc(false)
, m_bContour(false)
, m_bContourOutside(true)
, m_nLeftMargin(319)
, m_nRightMargin(319)
, m_nTopMargin(0)
, m_nBottomMargin(0)
, m_bShadow(false)
, m_nShadowXDistance(0)
, m_nShadowYDistance(0)
, m_nShadowColor(0)
, m_nShadowTransparence(0)
, m_nContrast(0)
, m_nBrightness(0)
, m_eColorMode( drawing::ColorMode_STANDARD )
, m_bIsGraphic(false)
, m_bSizeProtected(false)
, m_bPositionProtected(false)
, m_bHidden(false)
, m_nShapeOptionType(0)
, m_rPositionOffsets(rPositionOffsets)
, m_rAligns(rAligns)
, m_rPositivePercentages(rPositivePercentages)
, m_xComponentContext(std::move(xComponentContext))
, m_xTextDoc(std::move(xTextDoc))
{
}

GraphicImport::~GraphicImport()
{
}

css::awt::Point GraphicImport::GetGraphicObjectPosition() const
{
    return (css::awt::Point(m_nLeftPosition, m_nTopPosition));
}

bool GraphicImport::GetLayoutInCell() const
{
    return m_bLayoutInCell;
}

void GraphicImport::handleWrapTextValue(sal_uInt32 nVal)
{
    switch (nVal)
    {
    case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_bothSides:
        m_nWrap = text::WrapTextMode_PARALLEL;
        break;
    case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_left:
        m_nWrap = text::WrapTextMode_LEFT;
        break;
    case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_right:
        m_nWrap = text::WrapTextMode_RIGHT;
        break;
    case NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_largest:
        m_nWrap = text::WrapTextMode_DYNAMIC;
        break;
    default:;
    }
}

void GraphicImport::putPropertyToFrameGrabBag( const OUString& sPropertyName, const uno::Any& aPropertyValue )
{
    beans::PropertyValue aProperty;
    aProperty.Name = sPropertyName;
    aProperty.Value = aPropertyValue;

    if (!m_xShape.is())
        return;

    uno::Reference< beans::XPropertySet > xSet(m_xShape, uno::UNO_QUERY_THROW);

    uno::Reference< beans::XPropertySetInfo > xSetInfo(xSet->getPropertySetInfo());
    if (!xSetInfo.is())
        return;

    OUString aGrabBagPropName;
    uno::Reference<lang::XServiceInfo> xServiceInfo(m_xShape, uno::UNO_QUERY_THROW);
    if (xServiceInfo->supportsService(u"com.sun.star.text.TextFrame"_ustr))
        aGrabBagPropName = "FrameInteropGrabBag";
    else
        aGrabBagPropName = "InteropGrabBag";

    if (xSetInfo->hasPropertyByName(aGrabBagPropName))
    {
        //Add pProperty to the end of the Sequence for aGrabBagPropName
        uno::Sequence<beans::PropertyValue> aTmp;
        xSet->getPropertyValue(aGrabBagPropName) >>= aTmp;
        std::vector<beans::PropertyValue> aGrabBag(comphelper::sequenceToContainer<std::vector<beans::PropertyValue> >(aTmp));
        aGrabBag.push_back(aProperty);

        xSet->setPropertyValue(aGrabBagPropName, uno::Any(comphelper::containerToSequence(aGrabBag)));
    }
}

static bool lcl_bHasGroupSlantedChild(const SdrObject* pObj)
{
    // Returns true, if a child object differs more than 0.02deg from horizontal or vertical.
    // Because lines sometimes are imported as customshapes, a horizontal or vertical line
    // might not have exactly 0, 90, 180, or 270 degree as rotate angle.
    if (!pObj)
        return false;
    if (!pObj->IsGroupObject())
        return false;
    SdrObjList* pSubList = pObj->GetSubList();
    if (!pSubList)
        return false;
    SdrObjListIter aIterator(pSubList, SdrIterMode::DeepNoGroups);
    while (aIterator.IsMore())
    {
        const SdrObject* pSubObj = aIterator.Next();
        const Degree100 nRotateAngle = NormAngle36000(pSubObj->GetRotateAngle());
        const sal_uInt16 nRot = nRotateAngle.get();
        if ((3 < nRot && nRot < 8997) || (9003 < nRot && nRot < 17997)
            || (18003 < nRot && nRot < 26997) || (27003 < nRot && nRot < 35997))
            return true;
    }
    return false;
}

void GraphicImport::lcl_correctWord2007EffectExtent(const sal_Int32 nMSOAngle)
{
    // Word versions older than 14 do not swap width and height (see lcl_doMSOWidthHeightSwap)
    // and therefore generate different effectExtent. We correct them here.
    sal_Int16 nAngleDeg = (nMSOAngle / 60000) % 180;
    if (nAngleDeg < 45 || nAngleDeg >= 135)
        return;

    sal_Int32 nDiff = o3tl::convert(
        (double(getXSize()) - double(getYSize())) / 2.0,
        o3tl::Length::mm100, o3tl::Length::emu);
    if (m_oEffectExtentLeft)
        *m_oEffectExtentLeft += nDiff;
    if (m_oEffectExtentRight)
        *m_oEffectExtentRight += nDiff;
    if (m_oEffectExtentTop)
        *m_oEffectExtentTop -= nDiff;
    if (m_oEffectExtentBottom)
        *m_oEffectExtentBottom -= nDiff;
}

void GraphicImport::lcl_adjustMarginsAndOrientation()
{
    if (m_nHoriRelation != text::RelOrientation::CHAR)
    {
        const bool bRightSide = m_nHoriRelation == text::RelOrientation::PAGE_RIGHT;
        const bool bLeftSide = m_nHoriRelation == text::RelOrientation::PAGE_LEFT;
        const bool bPageOrMargin
            = m_nHoriRelation == text::RelOrientation::PAGE_PRINT_AREA // margin
                || m_nHoriRelation == text::RelOrientation::PAGE_FRAME; // page

        assert(bRightSide || bLeftSide || bPageOrMargin
            || m_nHoriRelation == text::RelOrientation::PRINT_AREA // column
            || m_nHoriRelation == text::RelOrientation::FRAME /*column*/ );

        // emulation: when impossible to wrap text on a side, remove the margin gap
        if (m_nHoriOrient == text::HoriOrientation::LEFT && !bRightSide)
            m_nLeftMargin = 0;
        else if (m_nHoriOrient == text::HoriOrientation::RIGHT && !bLeftSide)
            m_nRightMargin = 0;
        else if (m_nHoriOrient == text::HoriOrientation::INSIDE)
        {
            if (bPageOrMargin)
            {
                m_bPageToggle = true;
                m_nHoriOrient = text::HoriOrientation::LEFT;
                // LO currently has to emulate removing the gap on the non-text side,
                // so for these mirrored situations where the shape could be on either side,
                // either we have to keep the gap (wrong position, but proper text margin)
                // or else remove the gap on both sides (losing potentially important text margin).
                // Historically both gaps were removed, so I've kept that logic intact
                // as I expanded the scope to include non-graphic shapes.
                m_nLeftMargin = 0;
                m_nRightMargin = 0;
            }
        }
        else if (m_nHoriOrient == text::HoriOrientation::OUTSIDE)
        {
            if (bPageOrMargin)
            {
                m_bPageToggle = true;
                m_nHoriOrient = text::HoriOrientation::RIGHT;
                m_nLeftMargin = 0;
                m_nRightMargin = 0;
            }
        }
    }
}

static void lcl_doMSOWidthHeightSwap(awt::Point& rLeftTop, awt::Size& rSize,
                                       const sal_Int32 nMSOAngle)
{
    if (nMSOAngle == 0)
        return;
    // convert nMSOAngle to degree in [0°,180°[
    sal_Int16 nAngleDeg = (nMSOAngle / 60000) % 180;
    if (nAngleDeg >= 45 && nAngleDeg < 135)
    {
        // keep center of rectangle given in rLeftTop and rSize
        sal_Int32 aTemp = rSize.Width - rSize.Height;
        rLeftTop.X += aTemp / 2;
        rLeftTop.Y -= aTemp / 2;
        std::swap(rSize.Width, rSize.Height);
    }
    return;
}

void GraphicImport::lcl_expandRectangleByEffectExtent(awt::Point& rLeftTop, awt::Size& rSize)
{
    sal_Int32 nEffectExtent = (m_oEffectExtentLeft)
                                  ? oox::drawingml::convertEmuToHmm(*m_oEffectExtentLeft)
                                  : 0;
    rLeftTop.X -= nEffectExtent;
    rSize.Width += nEffectExtent;
    nEffectExtent = (m_oEffectExtentRight)
                        ? oox::drawingml::convertEmuToHmm(*m_oEffectExtentRight)
                        : 0;
    rSize.Width += nEffectExtent;
    nEffectExtent = (m_oEffectExtentTop)
                        ? oox::drawingml::convertEmuToHmm(*m_oEffectExtentTop)
                        : 0;
    rLeftTop.Y -= nEffectExtent;
    rSize.Height += nEffectExtent;
    nEffectExtent = (m_oEffectExtentBottom)
                        ? oox::drawingml::convertEmuToHmm(*m_oEffectExtentBottom)
                        : 0;
    rSize.Height += nEffectExtent;
}

void GraphicImport::lcl_attribute(Id nName, const Value& rValue)
{
    sal_Int32 nIntValue = rValue.getInt();
    switch( nName )
    {
        case NS_ooxml::LN_OfficeArtExtension_Decorative_val:
            m_bDecorative = true;
        break;
        case NS_ooxml::LN_CT_Hyperlink_URL:
            m_sHyperlinkURL = rValue.getString();
        break;
        case NS_ooxml::LN_blip: //the binary graphic data in a shape
            {
            writerfilter::Reference<Properties>::Pointer_t pProperties = rValue.getProperties();
            if( pProperties )
            {
                pProperties->resolve(*this);
            }
        }
        break;
        case NS_ooxml::LN_payload :
        {
            writerfilter::Reference<BinaryObj>::Pointer_t pPictureData = rValue.getBinary();
            if( pPictureData )
                pPictureData->resolve(*this);
        }
        break;

        //border properties
        case NS_ooxml::LN_CT_Border_sz:
            m_aBorders[BORDER_TOP].nLineWidth = nIntValue;
        break;
        case NS_ooxml::LN_CT_Border_val:
            //graphic borders don't support different line types
        break;
        case NS_ooxml::LN_CT_Border_space:
        break;
        case NS_ooxml::LN_CT_Border_shadow:
            m_aBorders[BORDER_TOP].bHasShadow = nIntValue != 0;
        break;
        case NS_ooxml::LN_CT_Border_frame:
            break;
        case NS_ooxml::LN_CT_PositiveSize2D_cx:
        case NS_ooxml::LN_CT_PositiveSize2D_cy:
        {
            sal_Int32 nDim = oox::drawingml::convertEmuToHmm(nIntValue);
            // drawingML equivalent of oox::vml::ShapeType::getAbsRectangle():
            // make sure a shape isn't hidden implicitly just because it has
            // zero height or width.
            if (nDim == 0)
                nDim = 1;

            if( nName == NS_ooxml::LN_CT_PositiveSize2D_cx )
                setXSize(nDim);
            else
                setYSize(nDim);
        }
        break;
        case NS_ooxml::LN_CT_EffectExtent_l:
            m_oEffectExtentLeft = nIntValue;
            break;
        case NS_ooxml::LN_CT_EffectExtent_t:
            m_oEffectExtentTop = nIntValue;
            break;
        case NS_ooxml::LN_CT_EffectExtent_r:
            m_oEffectExtentRight = nIntValue;
            break;
        case NS_ooxml::LN_CT_EffectExtent_b:
            m_oEffectExtentBottom = nIntValue;
            break;
        case NS_ooxml::LN_CT_NonVisualDrawingProps_id:
            //id of the object - ignored
        break;
        case NS_ooxml::LN_CT_NonVisualDrawingProps_name:
            //name of the object
            m_sName = rValue.getString();
        break;
        case NS_ooxml::LN_CT_NonVisualDrawingProps_descr:
            //alternative text
            m_sAlternativeText = rValue.getString();
        break;
        case NS_ooxml::LN_CT_NonVisualDrawingProps_title:
            //alternative text
            m_title = rValue.getString();
        break;
        case NS_ooxml::LN_CT_NonVisualDrawingProps_hidden:
            m_bHidden = (nIntValue == 1);
        break;
        case NS_ooxml::LN_CT_GraphicalObjectFrameLocking_noChangeAspect:
            //disallow aspect ratio change - ignored
        break;
        case NS_ooxml::LN_CT_GraphicalObjectFrameLocking_noMove:
            m_bPositionProtected = true;
        break;
        case NS_ooxml::LN_CT_GraphicalObjectFrameLocking_noResize:
            m_bSizeProtected = true;
        break;
        case NS_ooxml::LN_CT_Anchor_distT:
        case NS_ooxml::LN_CT_Anchor_distB:
        case NS_ooxml::LN_CT_Anchor_distL:
        case NS_ooxml::LN_CT_Anchor_distR:
        {
            m_nShapeOptionType = nName;
            ProcessShapeOptions(rValue);
        }
        break;
        case NS_ooxml::LN_CT_Anchor_simplePos_attr:
            m_bUseSimplePos = nIntValue > 0;
        break;
        case NS_ooxml::LN_CT_Anchor_relativeHeight: // unsigned content
        {
            // undocumented - based on testing: both 0 and 1 are equivalent to the maximum 503316479
            const sal_Int32 nMaxAllowed = 0x1DFFFFFF;
            if (nIntValue < 2 || nIntValue > nMaxAllowed)
                m_oZOrder = nMaxAllowed;
            else
                m_oZOrder = nIntValue;

            // all relativeHeight objects (i.e. DOCX graphics that use GraphicImport),
            // no matter how high their value, are below the lowest z-index shape (in same layer)
            // so emulate that by pretending that they are below text (in the hell-layer).
            // Please be assured that this does not actually place it in the hell-layer.
            m_oZOrder = *m_oZOrder - (nMaxAllowed + 1);
        }
        break;
        case NS_ooxml::LN_CT_Anchor_behindDoc:
            if (nIntValue > 0)
            {
                m_bOpaque = false;
                m_bBehindDoc = true;
            }
        break;
        case NS_ooxml::LN_CT_Anchor_locked:
        break;
        case NS_ooxml::LN_CT_Anchor_layoutInCell:
            // Starting in MSO 2013, anchors are ALWAYS considered to be laid out in table cell.
            m_bCompatForcedLayoutInCell = !nIntValue
                && m_rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() > 14
                && m_rDomainMapper.IsInTable();
            m_bLayoutInCell = m_bCompatForcedLayoutInCell || nIntValue;
        break;
        case NS_ooxml::LN_CT_Anchor_hidden:
        break;
        case NS_ooxml::LN_CT_Anchor_allowOverlap:
            m_bAllowOverlap = nIntValue != 0;
            break;
        case NS_ooxml::LN_CT_Anchor_wp14_anchorId:
        case NS_ooxml::LN_CT_Inline_wp14_anchorId:
        {
            auto aBuffer = OUString::number(nIntValue, 16).toAsciiUpperCase();
            m_sAnchorId = RepeatedUChar('0', 8 - aBuffer.length) + aBuffer;
        }
        break;
        case NS_ooxml::LN_CT_Point2D_x:
            m_nLeftPosition = oox::drawingml::convertEmuToHmm(nIntValue);
            m_nHoriRelation = text::RelOrientation::PAGE_FRAME;
            m_nHoriOrient = text::HoriOrientation::NONE;
        break;
        case NS_ooxml::LN_CT_Point2D_y:
            m_nTopPosition = oox::drawingml::convertEmuToHmm(nIntValue);
            m_nVertRelation = text::RelOrientation::PAGE_FRAME;
            m_nVertOrient = text::VertOrientation::NONE;
        break;
        case NS_ooxml::LN_CT_WrapTight_wrapText:
            m_bContour = true;
            m_bContourOutside = true;

            handleWrapTextValue(rValue.getInt());

            break;
        case NS_ooxml::LN_CT_WrapThrough_wrapText:
            m_bContour = true;
            m_bContourOutside = false;

            handleWrapTextValue(rValue.getInt());

            break;
        case NS_ooxml::LN_CT_WrapSquare_wrapText:
            handleWrapTextValue(rValue.getInt());
            break;
        case NS_ooxml::LN_CT_BlipFillProperties_srcRect:
            m_oCrop.emplace(rValue.getAny().get<text::GraphicCrop>());
            break;
        case NS_ooxml::LN_shape:
            {
                uno::Reference< drawing::XShape> xShape;
                rValue.getAny( ) >>= xShape;
                if ( xShape.is( ) )
                {
                    if (!m_bLayoutInCell && m_rDomainMapper.IsInTable()
                        && m_rGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)
                    {
                        // Microsoft apparently forces layoutInCell behaviour
                        // if either horizontal orientation is based on character
                        // or vertical orientation is based on line
                        // so make it explicit instead of trying to hack in tons of adjustments.
                        if (m_nVertRelation == text::RelOrientation::TEXT_LINE
                            || m_nHoriRelation == text::RelOrientation::CHAR)
                        {
                            m_bLayoutInCell = true;
                            m_bCompatForcedLayoutInCell = true;
                        }
                    }

                    if (m_bLayoutInCell && m_rDomainMapper.IsInTable())
                    {
                        // Microsoft is buggy and inconsistent in how they handle layoutInCell.
                        // Map wrongly-implemented settings to the closest implemented setting

                        // "page" is implemented as if it was "margin" - to cell spacing, not edge
                        if (m_nVertRelation == text::RelOrientation::PAGE_FRAME)
                            m_nVertRelation = text::RelOrientation::PAGE_PRINT_AREA;
                        // only "from top" and "top" are appropriate. Others are implemented as Top
                        if (m_nVertOrient != text::VertOrientation::NONE)
                            m_nVertOrient = text::VertOrientation::TOP;
                    }
                    else if (!m_bLayoutInCell && m_rDomainMapper.IsInTable())
                    {
                        // if the object is horizontally aligned to the paragraph,
                        // Microsoft strangely doesn't orient the object
                        // to the cell paragraph it is anchored to
                        // but to the paragraph that contains the entire table
                        // (which is equivalent to page margins).
                        // Amazingly, a VERT orientation to "line" can pin the horizontal alignment
                        // back to the cell paragraph.
                        if (m_nHoriRelation == text::RelOrientation::FRAME
                            && m_nVertRelation != text::RelOrientation::TEXT_LINE)
                        {
                            m_nHoriRelation = text::RelOrientation::PAGE_PRINT_AREA;
                        }
                    }
                    // Is it a graphic image
                    bool bUseShape = true;
                    try
                    {
                        uno::Reference< beans::XPropertySet > xShapeProps
                            ( xShape, uno::UNO_QUERY_THROW );

                        uno::Reference<graphic::XGraphic> xGraphic;
                        xShapeProps->getPropertyValue(u"Graphic"_ustr) >>= xGraphic;

                        sal_Int32 nRotation = 0;
                        xShapeProps->getPropertyValue(u"RotateAngle"_ustr) >>= nRotation;

                        css::beans::PropertyValues aGrabBag;
                        xShapeProps->getPropertyValue(u"InteropGrabBag"_ustr) >>= aGrabBag;
                        // if the shape contains effects in the grab bag, we should not transform it
                        // in a XTextContent so those effects can be preserved
                        bool bContainsEffects = std::any_of(std::cbegin(aGrabBag), std::cend(aGrabBag), [](const auto& rProp) {
                            return rProp.Name == "EffectProperties"
                                || rProp.Name == "3DEffectProperties"
                                || rProp.Name == "ArtisticEffectProperties";
                        });

                        xShapeProps->getPropertyValue(u"Shadow"_ustr) >>= m_bShadow;
                        if (m_bShadow)
                        {
                            xShapeProps->getPropertyValue(u"ShadowXDistance"_ustr) >>= m_nShadowXDistance;
                            xShapeProps->getPropertyValue(u"ShadowYDistance"_ustr) >>= m_nShadowYDistance;
                            xShapeProps->getPropertyValue(u"ShadowColor"_ustr) >>= m_nShadowColor;
                            xShapeProps->getPropertyValue(u"ShadowTransparence"_ustr) >>= m_nShadowTransparence;
                        }

                        xShapeProps->getPropertyValue(u"GraphicColorMode"_ustr) >>= m_eColorMode;
                        xShapeProps->getPropertyValue(u"AdjustLuminance"_ustr) >>= m_nBrightness;
                        xShapeProps->getPropertyValue(u"AdjustContrast"_ustr) >>= m_nContrast;

                        // fdo#70457: transform XShape into a SwXTextGraphicObject only if there's no rotation
                        if ( nRotation == 0 && !bContainsEffects )
                            m_xGraphicObject = createGraphicObject( xGraphic, xShapeProps );

                        bUseShape = !m_xGraphicObject.is( );

                        if ( !bUseShape )
                        {
                            // Define the object size
                            awt::Size aSize = xShape->getSize( );
                            m_xGraphicObject->setPropertyValue(u"Height"_ustr,
                                   uno::Any( aSize.Height ) );
                            m_xGraphicObject->setPropertyValue(u"Width"_ustr,
                                   uno::Any( aSize.Width ) );

                            text::GraphicCrop aGraphicCrop( 0, 0, 0, 0 );
                            uno::Reference< beans::XPropertySet > xSourceGraphProps( xShape, uno::UNO_QUERY );
                            uno::Any aAny = xSourceGraphProps->getPropertyValue(u"GraphicCrop"_ustr);
                            if (m_oCrop)
                            {   // RTF: RTFValue from resolvePict()
                                m_xGraphicObject->setPropertyValue(u"GraphicCrop"_ustr,
                                        uno::Any(*m_oCrop));
                            }
                            else if (aAny >>= aGraphicCrop)
                            {   // DOCX: imported in oox BlipFillContext
                                m_xGraphicObject->setPropertyValue(u"GraphicCrop"_ustr,
                                    uno::Any( aGraphicCrop ) );
                            }

                            // We need to drop the shape here somehow
                            uno::Reference< lang::XComponent > xShapeComponent( xShape, uno::UNO_QUERY );
                            xShapeComponent->dispose( );
                        }
                    }
                    catch( const beans::UnknownPropertyException & )
                    {
                        // It isn't a graphic image
                    }

                    if ( bUseShape )
                        m_xShape = xShape;

                    if ( m_xShape.is( ) )
                    {
                        uno::Reference< beans::XPropertySet > xShapeProps
                            (m_xShape, uno::UNO_QUERY_THROW);


                        xShapeProps->setPropertyValue
                            (getPropertyName(PROP_ANCHOR_TYPE),
                             uno::Any
                             (text::TextContentAnchorType_AS_CHARACTER));

                        // In Word, if a shape is anchored inline, that
                        // excludes being in the background.
                        xShapeProps->setPropertyValue(u"Opaque"_ustr, uno::Any(true));

                        uno::Reference<lang::XServiceInfo> xServiceInfo(m_xShape, uno::UNO_QUERY_THROW);

                        // TextFrames can't be rotated. But for anything else,
                        // make sure that setting size doesn't affect rotation,
                        // that would not match Word's definition of rotation.
                        bool bKeepRotation = false;
                        if (!xServiceInfo->supportsService(u"com.sun.star.text.TextFrame"_ustr))
                        {
                            bKeepRotation = true;
                            try
                            {
                                xShapeProps->setPropertyValue(getPropertyName(PROP_TEXT_RANGE),
                                    uno::Any(m_rDomainMapper.GetCurrentTextRange()));
                            }
                            catch (lang::IllegalArgumentException const&)
                            {
                                SAL_WARN("writerfilter", "GraphicImport::lcl_attribute: cannot set anchor");
                            }
                        }

                        awt::Size aSize(m_xShape->getSize());

                        // One purpose of the next part is, to set the logic rectangle of the SdrObject
                        // to nXSize and nYSize from import. That doesn't work for groups or lines,
                        // because they do not have a logic rectangle and m_xShape->getSize and
                        // m_xShape->setSize would work on the snap rectangle. In case a shape is
                        // rotated, non-uniform scaling the snap rectangle will introduce shearing on
                        // the shape. In case group or line is rotated, nXSize and nYSize contain the
                        // unrotated size from oox. The rotation is already incorporated into group
                        // children and line points. We must not scale them to unrotated size. Exclude
                        // those shapes here.

                        // Get MSO rotation angle. GetRotateAngle from SdrObject is not suitable
                        // here, because it returns the rotate angle of the first child for groups
                        // and slope angle for lines, even if line or group had not been rotated.
                        // Import in oox has put the rotation from oox file into InteropGrabBag.
                        comphelper::SequenceAsHashMap aInteropGrabBag(xShapeProps->getPropertyValue(u"InteropGrabBag"_ustr));
                        sal_Int32 nOOXAngle(0);
                        aInteropGrabBag.getValue(u"mso-rotation-angle"_ustr) >>= nOOXAngle; // 1/60000 deg
                        // tdf#143455: A diagram is imported as group, but has no valid object list
                        // and contour wrap is different to Word. As workaround diagrams are excluded
                        // here in various places.
                        const SdrObject* pDiagramCandidate(SdrObject::getSdrObjectFromXShape(m_xShape));
                        const bool bIsDiagram(nullptr != pDiagramCandidate && pDiagramCandidate->isDiagram());
                        // tdf#143476: A lockedCanvas (Word2007) is imported as group, but has not
                        // got size and position. Values from m_Impl has to be used.
                        bool bIsLockedCanvas(false);
                        aInteropGrabBag.getValue(u"LockedCanvas"_ustr) >>= bIsLockedCanvas;
                        bool bIsWordprocessingCanvas(false);
                        aInteropGrabBag.getValue(u"WordprocessingCanvas"_ustr) >>= bIsWordprocessingCanvas;
                        const bool bIsGroupOrLine = (xServiceInfo->supportsService(u"com.sun.star.drawing.GroupShape"_ustr)
                            && !bIsDiagram && !bIsLockedCanvas && !bIsWordprocessingCanvas)
                            || xServiceInfo->supportsService(u"com.sun.star.drawing.LineShape"_ustr);
                        SdrObject* pShape = SdrObject::getSdrObjectFromXShape(m_xShape);
                        if (!bIsGroupOrLine || (!nOOXAngle && !lcl_bHasGroupSlantedChild(pShape)))
                        {
                            if (isXSizeValid())
                                aSize.Width = getXSize();
                            if (isYSizeValid())
                                aSize.Height = getYSize();
                        }

                        Degree100 nRotation;
                        if (bKeepRotation)
                        {
                            // Use internal API, getPropertyValue("RotateAngle")
                            // would use GetObjectRotation(), which is not what
                            // we want.
                            if (pShape)
                                nRotation = pShape->GetRotateAngle();
                        }

                        // tdf#157960: SdrEdgeObj::NbcResize would reset the adjustment values of
                        // connectors to default zero. Thus we do not resize in case of a group that
                        // represents a Word drawing canvas.
                        if (!bIsWordprocessingCanvas)
                            m_xShape->setSize(aSize);

                        if (bKeepRotation)
                        {
                            xShapeProps->setPropertyValue(u"RotateAngle"_ustr, uno::Any(nRotation.get()));
                        }

                        m_bIsGraphic = true;

                        if (!m_sAnchorId.isEmpty())
                        {
                            putPropertyToFrameGrabBag(u"AnchorId"_ustr, uno::Any(m_sAnchorId));
                        }

                        // Calculate mso unrotated rectangle and its center, needed below
                        awt::Size aImportSize(m_xShape->getSize()); // here only fallback
                        if (isXSizeValid())
                            aImportSize.Width = getXSize(); // Hmm
                        if (isYSizeValid())
                            aImportSize.Height = getYSize(); // Hmm
                        const awt::Point aImportPosition(GetGraphicObjectPosition()); // Hmm
                        double fCentrumX = aImportPosition.X + aImportSize.Width / 2.0;
                        double fCentrumY = aImportPosition.Y + aImportSize.Height / 2.0;

                        // In case of group and lines, transformations are incorporated in the child
                        // shapes or points respectively in LO. MSO has rotation as separate property.
                        // The position refers to the unrotated rectangle of MSO. We need to adapt it
                        // to the left-top of the transformed shape.
                        awt::Size aLOSize(m_xShape->getSize()); // LO snap rectangle size in Hmm
                        if (bIsGroupOrLine  && !(mpWrapPolygon))
                        {
                            // Set LO position. MSO rotation is done on shape center.
                            if(pShape && pShape->IsGroupObject())
                            {
                                tools::Rectangle aSnapRect = pShape->GetSnapRect(); // Twips
                                m_nLeftPosition = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.Left());
                                m_nTopPosition = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.Top());
                                aLOSize.Width = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.getOpenWidth());
                                aLOSize.Height = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.getOpenHeight());
                            }
                            else
                            {
                                m_nLeftPosition = fCentrumX - aLOSize.Width / 2.0;
                                m_nTopPosition = fCentrumY - aLOSize.Height / 2.0;
                            }
                            m_xShape->setPosition(GetGraphicObjectPosition());
                        }
                        // ToDo: Rotated shapes with position type "Alignment" (UI of Word) have
                        // wrong position. Word aligns the unrotated logic rectangle, LO the rotated
                        // snap rectangle.

                        // Margin correction

                        // tdf#143475: Word 2007 (vers 12) calculates effectExtent for rotated images
                        // based on the unrotated image without width-height-swap. We correct this to
                        // those values, which would be calculated if width-height-swap was used.
                        if (m_rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() < 14
                            && xServiceInfo->supportsService(u"com.sun.star.drawing.GraphicObjectShape"_ustr)
                            && nOOXAngle != 0)
                        {
                            lcl_correctWord2007EffectExtent(nOOXAngle);
                        }

                        if (m_rGraphicImportType == IMPORT_AS_DETECTED_INLINE)
                        {
                            if (nOOXAngle == 0)
                            {
                                // EffectExtent contains all needed additional space, including fat
                                // stroke and shadow. Simple add it to the margins.
                                sal_Int32 nEffectExtent = (m_oEffectExtentLeft)
                                    ? oox::drawingml::convertEmuToHmm(*m_oEffectExtentLeft)
                                    : 0;
                                m_nLeftMargin += nEffectExtent;
                                nEffectExtent = (m_oEffectExtentRight)
                                    ? oox::drawingml::convertEmuToHmm(*m_oEffectExtentRight) : 0;
                                m_nRightMargin += nEffectExtent;
                                nEffectExtent = (m_oEffectExtentTop)
                                    ? oox::drawingml::convertEmuToHmm(*m_oEffectExtentTop) : 0;
                                m_nTopMargin += nEffectExtent;
                                nEffectExtent = (m_oEffectExtentBottom)
                                    ? oox::drawingml::convertEmuToHmm(*m_oEffectExtentBottom) : 0;
                                m_nBottomMargin += nEffectExtent;
                            }
                            else
                            {
                                // As of June 2021 LibreOffice uses an area, which is large enough to
                                // contain the rotated snap rectangle. MSO uses a smaller area, so
                                // that the rotated snap rectangle covers text.
                                awt::Point aMSOBaseLeftTop = aImportPosition;
                                awt::Size aMSOBaseSize = aImportSize;
                                lcl_doMSOWidthHeightSwap(aMSOBaseLeftTop, aMSOBaseSize, nOOXAngle);
                                lcl_expandRectangleByEffectExtent(aMSOBaseLeftTop, aMSOBaseSize);

                                // Get LO SnapRect from SdrObject if possible
                                awt::Rectangle aLOSnapRect;
                                // For case we have no SdrObject, initialize with values from m_pImpl
                                aLOSnapRect.X = m_nLeftPosition;
                                aLOSnapRect.Y = m_nTopPosition;
                                aLOSnapRect.Width = aLOSize.Width;
                                aLOSnapRect.Height = aLOSize.Height;
                                if (pShape)
                                {
                                    tools::Rectangle aSnapRect = pShape->GetSnapRect(); // Twip
                                    aLOSnapRect.X = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.Left());
                                    aLOSnapRect.Y = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.Top());
                                    aLOSnapRect.Width = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.getOpenWidth());
                                    aLOSnapRect.Height = ConversionHelper::convertTwipToMm100_Limited(aSnapRect.getOpenHeight());
                                }

                                m_nLeftMargin  += aLOSnapRect.X - aMSOBaseLeftTop.X;
                                m_nRightMargin += aMSOBaseLeftTop.X + aMSOBaseSize.Width
                                                         - (aLOSnapRect.X + aLOSnapRect.Width);
                                m_nTopMargin  += aLOSnapRect.Y - aMSOBaseLeftTop.Y;
                                m_nBottomMargin += aMSOBaseLeftTop.Y + aMSOBaseSize.Height
                                                          - (aLOSnapRect.Y + aLOSnapRect.Height);
                                // tdf#141880 LibreOffice cannot handle negative vertical margins.
                                // Those cases are caught below at common place.
                            }
                        } // end IMPORT_AS_DETECTED_INLINE
                        else if ((m_nWrap == text::WrapTextMode_PARALLEL
                                  || m_nWrap == text::WrapTextMode_DYNAMIC
                                  || m_nWrap == text::WrapTextMode_LEFT
                                  || m_nWrap == text::WrapTextMode_RIGHT
                                  || m_nWrap == text::WrapTextMode_NONE)
                                  && !(mpWrapPolygon) && !bIsDiagram && !bIsWordprocessingCanvas)
                        {
                            // For wrap "Square" an area is defined around which the text wraps. MSO
                            // describes the area by a base rectangle and effectExtent. LO uses the
                            // shape bounding box and margins. We adapt the margins to get the same
                            // area as MSO.
                            awt::Point aMSOBaseLeftTop = aImportPosition;
                            awt::Size aMSOBaseSize = aImportSize;
                            lcl_doMSOWidthHeightSwap(aMSOBaseLeftTop, aMSOBaseSize, nOOXAngle);
                            lcl_expandRectangleByEffectExtent(aMSOBaseLeftTop, aMSOBaseSize);

                            // Get LO bound rectangle from SdrObject if possible
                            awt::Rectangle aLOBoundRect;
                            // For case we have no SdrObject, initialize with values from m_pImpl
                            aLOBoundRect.X = m_nLeftPosition;
                            aLOBoundRect.Y = m_nTopPosition;
                            aLOBoundRect.Width = aLOSize.Width;
                            aLOBoundRect.Height = aLOSize.Height;
                            if (pShape)
                            {
                                tools::Rectangle aBoundRect = pShape->GetCurrentBoundRect(); // Twip
                                aLOBoundRect.X = ConversionHelper::convertTwipToMm100_Limited(aBoundRect.Left());
                                aLOBoundRect.Y = ConversionHelper::convertTwipToMm100_Limited(aBoundRect.Top());
                                aLOBoundRect.Width = ConversionHelper::convertTwipToMm100_Limited(aBoundRect.getOpenWidth());
                                aLOBoundRect.Height = ConversionHelper::convertTwipToMm100_Limited(aBoundRect.getOpenHeight());
                            }

                            m_nLeftMargin += aLOBoundRect.X - aMSOBaseLeftTop.X;
                            m_nRightMargin += aMSOBaseLeftTop.X + aMSOBaseSize.Width
                                                     - (aLOBoundRect.X + aLOBoundRect.Width);
                            m_nTopMargin += aLOBoundRect.Y - aMSOBaseLeftTop.Y;
                            m_nBottomMargin += aMSOBaseLeftTop.Y + aMSOBaseSize.Height
                                                      - (aLOBoundRect.Y + aLOBoundRect.Height);
                        }
                        else if (mpWrapPolygon && !bIsDiagram && !bIsWordprocessingCanvas)
                        {
                            // Word uses a wrap polygon, LibreOffice has no explicit wrap polygon
                            // but creates the wrap contour based on the shape geometry, without
                            // stroke width and shadow, but with rotation and flip. The concepts
                            // are not compatible. We approximate Word's rendering by setting
                            // wrap margins.

                            // Build a range from the wrap polygon from Word.
                            const drawing::PointSequenceSequence aWrapPolygon
                                = mpWrapPolygon->getPointSequenceSequence();
                            basegfx::B2DPolyPolygon aB2DWrapPolyPolygon
                                = basegfx::utils::UnoPointSequenceSequenceToB2DPolyPolygon(
                                    aWrapPolygon);
                            // Wrap polygon values are relative to 0..21600|0..21600.
                            // Scale to shape size (in Hmm).
                            basegfx::B2DHomMatrix aMatrix = basegfx::utils::createScaleB2DHomMatrix(
                                aImportSize.Width / 21600.0, aImportSize.Height / 21600.0);
                            aB2DWrapPolyPolygon.transform(aMatrix);

                            // Shape geometry will be rotated, rotate wrap polygon too.
                            if (nOOXAngle != 0)
                            {
                                aMatrix = basegfx::utils::createRotateAroundPoint(
                                    aImportSize.Width / 2.0, aImportSize.Height / 2.0,
                                    basegfx::deg2rad<60000>(nOOXAngle));
                                aB2DWrapPolyPolygon.transform(aMatrix);
                            }
                            basegfx::B2DRange aB2DWrapRange = aB2DWrapPolyPolygon.getB2DRange();

                            // Build a range from shape geometry
                            basegfx::B2DRange aShapeRange;
                            if (pShape)
                            {
                                basegfx::B2DPolyPolygon aShapePolygon = pShape->TakeXorPoly(); // Twips
                                aMatrix = basegfx::utils::createScaleB2DHomMatrix(
                                    o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100),
                                    o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100));
                                aShapePolygon.transform(aMatrix);
                                // Wrap polygon treats left/top of shape as origin, shift shape polygon accordingly
                                aShapePolygon.translate(-aImportPosition.X, -aImportPosition.Y);
                                aShapeRange = aShapePolygon.getB2DRange();
                            }
                            else // can this happen?
                            {
                                aShapeRange
                                    = basegfx::B2DRange(0, 0, aImportSize.Width, aImportSize.Height);
                                if (nOOXAngle != 0)
                                {
                                    aMatrix = basegfx::utils::createRotateB2DHomMatrix(
                                        basegfx::deg2rad<60000>(nOOXAngle));
                                    aShapeRange.transform(aMatrix);
                                }
                            }

                            // Add difference between shape and wrap range to margin and remember
                            // difference in Twips for export.
                            comphelper::SequenceAsHashMap aAnchorDistDiff;

                            const double fTopDiff = aShapeRange.getMinY() - aB2DWrapRange.getMinY();
                            m_nTopMargin += basegfx::fround(fTopDiff);
                            aAnchorDistDiff[u"distTDiff"_ustr] <<= basegfx::fround(
                                o3tl::convert(fTopDiff, o3tl::Length::mm100, o3tl::Length::twip));

                            const double fBottomDiff = aB2DWrapRange.getMaxY() - aShapeRange.getMaxY();
                            m_nBottomMargin += basegfx::fround(fBottomDiff);
                            aAnchorDistDiff[u"distBDiff"_ustr] <<= basegfx::fround(
                                o3tl::convert(fBottomDiff, o3tl::Length::mm100, o3tl::Length::twip));

                            const double fLeftDiff = aShapeRange.getMinX() - aB2DWrapRange.getMinX();
                            m_nLeftMargin += basegfx::fround(fLeftDiff);
                            aAnchorDistDiff[u"distLDiff"_ustr] <<= basegfx::fround(
                                o3tl::convert(fLeftDiff, o3tl::Length::mm100, o3tl::Length::twip));

                            const double fRightDiff = aB2DWrapRange.getMaxX() - aShapeRange.getMaxX();
                            m_nRightMargin += basegfx::fround(fRightDiff);
                            aAnchorDistDiff[u"distRDiff"_ustr] <<= basegfx::fround(
                                o3tl::convert(fRightDiff, o3tl::Length::mm100, o3tl::Length::twip));

                            m_aInteropGrabBag[u"AnchorDistDiff"_ustr]
                                <<= aAnchorDistDiff.getAsConstPropertyValueList();

                            // FixMe: tdf#141880. LibreOffice cannot handle negative horizontal margin in contour wrap
                            if (m_nLeftMargin < 0)
                                m_nLeftMargin = 0;
                            if (m_nRightMargin < 0)
                                m_nRightMargin = 0;
                        }
                        else if (!bIsDiagram && !bIsWordprocessingCanvas) // text::WrapTextMode_THROUGH
                        {
                            // Word writes and evaluates the effectExtent in case of position
                            // type 'Alignment' (UI). We move these values to margin to approximate
                            // Word's rendering.
                            if (m_oEffectExtentLeft)
                            {
                                m_nLeftMargin
                                    += oox::drawingml::convertEmuToHmm(*m_oEffectExtentLeft);
                            }
                            if (m_oEffectExtentTop)
                            {
                                m_nTopMargin
                                    += oox::drawingml::convertEmuToHmm(*m_oEffectExtentTop);
                            }
                            if (m_oEffectExtentRight)
                            {
                                m_nRightMargin
                                    += oox::drawingml::convertEmuToHmm(*m_oEffectExtentRight);
                            }
                            if (m_oEffectExtentBottom)
                            {
                                m_nBottomMargin
                                    += oox::drawingml::convertEmuToHmm(*m_oEffectExtentBottom);
                            }
                        }

                        // FixMe: tdf#141880 LibreOffice cannot handle negative vertical margins
                        // although they are allowed in ODF.
                        if (m_nTopMargin < 0)
                            m_nTopMargin = 0;
                        if (m_nBottomMargin < 0)
                            m_nBottomMargin = 0;
                    }

                    if (bUseShape && m_rGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)
                    {
                        // If we are here, this is a drawingML shape. For those, only dmapper (and not oox) knows the anchoring infos (just like for Writer pictures).
                        // But they aren't Writer pictures, either (which are already handled above).
                        uno::Reference< beans::XPropertySet > xShapeProps(m_xShape, uno::UNO_QUERY_THROW);

                        if (m_nHoriRelation == text::RelOrientation::FRAME
                            && (m_nHoriOrient == text::HoriOrientation::LEFT
                                || m_nHoriOrient == text::HoriOrientation::RIGHT
                                || m_nHoriOrient == text::HoriOrientation::INSIDE
                                || m_nHoriOrient == text::HoriOrientation::OUTSIDE))
                        {
                            // before compat15, relative left/right/inside/outside honored margins.
                            if (m_rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() < 15)
                                m_nHoriRelation = text::RelOrientation::PRINT_AREA;
                        }

                        lcl_adjustMarginsAndOrientation();

                        // Anchored: Word only supports at-char in that case.
                        text::TextContentAnchorType eAnchorType = text::TextContentAnchorType_AT_CHARACTER;

                        if (m_bHidden)
                        {
                            xShapeProps->setPropertyValue(u"Visible"_ustr, uno::Any(false));
                            xShapeProps->setPropertyValue(u"Printable"_ustr, uno::Any(false));
                        }

                        // Avoid setting AnchorType for TextBoxes till SwTextBoxHelper::syncProperty() doesn't handle transition.
                        bool bTextBox = false;
                        xShapeProps->getPropertyValue(u"TextBox"_ustr) >>= bTextBox;

                        // The positioning change caused by LayoutInCell doesn't sync well
                        // in the text / frame duo. So the compatibility fix only correctly
                        // positions the frame and not the text currently.
                        // tdf#135943: Instead of half-fixing and making a complete mess,
                        // just avoid until layout's repositioning is sync'd to the text frame.
                        if (m_bLayoutInCell && bTextBox)
                            m_bLayoutInCell = !m_bCompatForcedLayoutInCell;

                        xShapeProps->setPropertyValue(u"AnchorType"_ustr, uno::Any(eAnchorType));

                        if (m_nVertRelation == text::RelOrientation::TEXT_LINE)
                        {
                            // Word's "line" is "below the bottom of the line", our TEXT_LINE is
                            // "towards top, from the bottom of the line", so invert the vertical
                            // position.
                            awt::Point aPoint = xShape->getPosition();
                            aPoint.Y *= -1;
                            xShape->setPosition(aPoint);
                        }

                        if (m_bLayoutInCell && bTextBox && m_rDomainMapper.IsInTable()
                            && m_nHoriRelation == text::RelOrientation::PAGE_FRAME)
                        {
                            // This claim is a bit of a stretch, but should be OK for horizontal.
                            // For layoutInCell, MSO does apply PAGE_FRAME and PAGE_PRINT_AREA
                            // horizontal adjustments onto the cell's frame and print area.
                            // Note that FRAME cannot be substituted for vertical (only first para).
                            m_nHoriRelation = text::RelOrientation::FRAME;
                        }

                        if(m_rDomainMapper.IsInTable())
                            xShapeProps->setPropertyValue(getPropertyName(PROP_FOLLOW_TEXT_FLOW),
                                uno::Any(m_bLayoutInCell));
                        //only the position orientation is handled in applyPosition()
                        applyPosition(xShapeProps);

                        uno::Reference<lang::XServiceInfo> xServiceInfo(m_xShape, uno::UNO_QUERY_THROW);
                        if (xServiceInfo->supportsService(u"com.sun.star.drawing.GroupShape"_ustr) ||
                                xServiceInfo->supportsService(u"com.sun.star.drawing.GraphicObjectShape"_ustr))
                        {
                            // You would expect that position and rotation are
                            // independent, but they are not. Till we are not
                            // there yet to handle all scaling, translation and
                            // rotation with a single transformation matrix,
                            // make sure there is no graphic rotation set when we set
                            // the position.
                            sal_Int32 nRotation = 0;
                            if (xServiceInfo->supportsService(u"com.sun.star.drawing.GraphicObjectShape"_ustr))
                            {
                                xShapeProps->getPropertyValue(u"RotateAngle"_ustr) >>= nRotation;
                            }
                            if (nRotation)
                                xShapeProps->setPropertyValue(u"RotateAngle"_ustr, uno::Any(sal_Int32(0)));

                            // Position of the groupshape should be set after children have been added.
                            // Long-term we should get rid of positioning group
                            // shapes, though. Do it for top-level ones with
                            // absolute page position as a start.
                            // fdo#80555: also set position for graphic shapes here
                            if (!isTopGroupObj(m_xShape)
                                || m_nHoriRelation != text::RelOrientation::PAGE_FRAME
                                || m_nVertRelation != text::RelOrientation::PAGE_FRAME)
                                m_xShape->setPosition(
                                    awt::Point(m_nLeftPosition, m_nTopPosition));

                            if (nRotation)
                                xShapeProps->setPropertyValue(u"RotateAngle"_ustr, uno::Any(nRotation));
                        }


                        applyRelativePosition(xShapeProps, /*bRelativeOnly=*/true);

                        xShapeProps->setPropertyValue(u"SurroundContour"_ustr, uno::Any(m_bContour));
                        xShapeProps->setPropertyValue(u"ContourOutside"_ustr, uno::Any(m_bContourOutside));
                        applyMargins(xShapeProps);
                        xShapeProps->setPropertyValue(u"Opaque"_ustr, uno::Any(m_bOpaque));
                        xShapeProps->setPropertyValue(u"Surround"_ustr, uno::Any(static_cast<sal_Int32>(m_nWrap)));
                        applyZOrder(xShapeProps);
                        applyName(xShapeProps);
                        applyHyperlink(xShapeProps, bUseShape);
                        xShapeProps->setPropertyValue(u"AllowOverlap"_ustr,
                                                      uno::Any(m_bAllowOverlap));

                        // Get the grab-bag set by oox, merge with our one and then put it back.
                        comphelper::SequenceAsHashMap aInteropGrabBag(xShapeProps->getPropertyValue(u"InteropGrabBag"_ustr));
                        aInteropGrabBag.update(getInteropGrabBag());
                        xShapeProps->setPropertyValue(u"InteropGrabBag"_ustr, uno::Any(aInteropGrabBag.getAsConstPropertyValueList()));
                    }
                    else if (bUseShape && m_rGraphicImportType == IMPORT_AS_DETECTED_INLINE)
                    {
                        uno::Reference< beans::XPropertySet > xShapeProps(m_xShape, uno::UNO_QUERY_THROW);
                        applyMargins(xShapeProps);
                        applyZOrder(xShapeProps);
                        applyName(xShapeProps);
                        comphelper::SequenceAsHashMap aInteropGrabBag(xShapeProps->getPropertyValue(u"InteropGrabBag"_ustr));
                        aInteropGrabBag.update(getInteropGrabBag());
                        xShapeProps->setPropertyValue(u"InteropGrabBag"_ustr, uno::Any(aInteropGrabBag.getAsConstPropertyValueList()));
                    }
                }
            }
        break;
        case NS_ooxml::LN_CT_Inline_distT:
            m_nTopMargin = 0;
        break;
        case NS_ooxml::LN_CT_Inline_distB:
            m_nBottomMargin = 0;
        break;
        case NS_ooxml::LN_CT_Inline_distL:
            m_nLeftMargin = 0;
        break;
        case NS_ooxml::LN_CT_Inline_distR:
            m_nRightMargin = 0;
        break;
        case NS_ooxml::LN_CT_GraphicalObjectData_uri:
            rValue.getString();
            //TODO: does it need to be handled?
        break;
        case NS_ooxml::LN_CT_SizeRelH_relativeFrom:
            {
                switch (nIntValue)
                {
                case NS_ooxml::LN_ST_SizeRelFromH_margin:
                    if (m_xShape.is())
                    {
                        uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
                        xPropertySet->setPropertyValue(u"RelativeWidthRelation"_ustr, uno::Any(text::RelOrientation::FRAME));
                    }
                    break;
                case NS_ooxml::LN_ST_SizeRelFromH_leftMargin:
                case NS_ooxml::LN_ST_SizeRelFromH_outsideMargin:
                    if (m_xShape.is())
                    {
                        // Here we handle the relative size of the width of some shape.
                        // The size of the shape's width is going to be relative to the size of the left margin.
                        // E.g.: (left margin = 8 && relative size = 150%) -> width of some shape = 12.
                        uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
                        xPropertySet->setPropertyValue(u"RelativeWidthRelation"_ustr, uno::Any(text::RelOrientation::PAGE_LEFT));
                    }
                    break;
                case NS_ooxml::LN_ST_SizeRelFromH_rightMargin:
                case NS_ooxml::LN_ST_SizeRelFromH_insideMargin:
                    if (m_xShape.is())
                    {
                        // Same as the left margin above.
                        uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
                        xPropertySet->setPropertyValue(u"RelativeWidthRelation"_ustr, uno::Any(text::RelOrientation::PAGE_RIGHT));
                    }
                    break;
                case NS_ooxml::LN_ST_SizeRelFromH_page:
                    if (m_xShape.is())
                    {
                        uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
                        xPropertySet->setPropertyValue(u"RelativeWidthRelation"_ustr, uno::Any(text::RelOrientation::PAGE_FRAME));
                    }
                    break;
                default:
                    SAL_WARN("writerfilter", "GraphicImport::lcl_attribute: unhandled NS_ooxml::LN_CT_SizeRelH_relativeFrom value: " << nIntValue);
                    break;
                }
            }
            break;
        case NS_ooxml::LN_CT_SizeRelV_relativeFrom:
            {
                switch (nIntValue)
                {
                case NS_ooxml::LN_ST_SizeRelFromV_margin:
                    if (m_xShape.is())
                    {
                        uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
                        xPropertySet->setPropertyValue(u"RelativeHeightRelation"_ustr, uno::Any(text::RelOrientation::FRAME));
                    }
                    break;
                case NS_ooxml::LN_ST_SizeRelFromV_page:
                    if (m_xShape.is())
                    {
                        uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
                        xPropertySet->setPropertyValue(u"RelativeHeightRelation"_ustr, uno::Any(text::RelOrientation::PAGE_FRAME));
                    }
                    break;
                case NS_ooxml::LN_ST_SizeRelFromV_topMargin:
                    if (m_xShape.is())
                    {
                        uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
                        xPropertySet->setPropertyValue(u"RelativeHeightRelation"_ustr, uno::Any(text::RelOrientation::PAGE_PRINT_AREA));
                    }
                    break;
                case NS_ooxml::LN_ST_SizeRelFromV_bottomMargin:
                    if (m_xShape.is())
                    {
                        uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
                        xPropertySet->setPropertyValue(u"RelativeHeightRelation"_ustr, uno::Any(text::RelOrientation::PAGE_PRINT_AREA_BOTTOM));
                    }
                    break;
                default:
                    SAL_WARN("writerfilter", "GraphicImport::lcl_attribute: unhandled NS_ooxml::LN_CT_SizeRelV_relativeFrom value: " << nIntValue);
                    break;
                }
            }
            break;
        default:
#ifdef DBG_UTIL
            TagLogger::getInstance().element("unhandled");
#endif
            break;
    }
}

uno::Reference<text::XTextContent> GraphicImport::GetGraphicObject()
{
    uno::Reference<text::XTextContent> xResult;

    if (m_xGraphicObject.is())
        xResult = m_xGraphicObject;
    else if (m_xShape.is())
    {
        xResult.set(m_xShape, uno::UNO_QUERY_THROW);
    }

    return xResult;
}


void GraphicImport::ProcessShapeOptions(Value const & rValue)
{
    sal_Int32 nIntValue = rValue.getInt();
    switch( m_nShapeOptionType )
    {
        case NS_ooxml::LN_CT_Anchor_distL:
            m_nLeftMargin = nIntValue / 360;
            m_nLeftMarginOrig = m_nLeftMargin;
        break;
        case NS_ooxml::LN_CT_Anchor_distT:
            //todo: changes have to be applied depending on the orientation, see SwWW8ImplReader::AdjustULWrapForWordMargins()
            m_nTopMargin = nIntValue / 360;
        break;
        case NS_ooxml::LN_CT_Anchor_distR:
            //todo: changes have to be applied depending on the orientation, see SwWW8ImplReader::AdjustLRWrapForWordMargins()
            m_nRightMargin = nIntValue / 360;
        break;
        case NS_ooxml::LN_CT_Anchor_distB:
            //todo: changes have to be applied depending on the orientation, see SwWW8ImplReader::AdjustULWrapForWordMargins()
            m_nBottomMargin = nIntValue / 360;
        break;
        default:
            OSL_FAIL( "shape option unsupported?");
    }
}


void GraphicImport::lcl_sprm(Sprm& rSprm)
{
    sal_uInt32 nSprmId = rSprm.getId();

    switch(nSprmId)
    {
        case NS_ooxml::LN_CT_Inline_extent:
        case NS_ooxml::LN_CT_Inline_effectExtent:
        case NS_ooxml::LN_CT_Inline_docPr:
        case NS_ooxml::LN_CT_Inline_cNvGraphicFramePr:
        case NS_ooxml::LN_CT_NonVisualGraphicFrameProperties_graphicFrameLocks:
        case NS_ooxml::LN_CT_Inline_a_graphic:
        case NS_ooxml::LN_CT_Anchor_simplePos_elem:
        case NS_ooxml::LN_CT_Anchor_extent:
        case NS_ooxml::LN_CT_Anchor_effectExtent:
        case NS_ooxml::LN_EG_WrapType_wrapSquare:
        case NS_ooxml::LN_EG_WrapType_wrapTight:
        case NS_ooxml::LN_EG_WrapType_wrapThrough:
        case NS_ooxml::LN_CT_Anchor_docPr:
        case NS_ooxml::LN_CT_NonVisualDrawingProps_extLst:
        case NS_ooxml::LN_CT_Anchor_cNvGraphicFramePr:
        case NS_ooxml::LN_CT_Anchor_a_graphic:
        case NS_ooxml::LN_CT_WrapPath_start:
        case NS_ooxml::LN_CT_WrapPath_lineTo:
        case NS_ooxml::LN_graphic_graphic:
        case NS_ooxml::LN_pic_pic:
        case NS_ooxml::LN_dgm_relIds:
        case NS_ooxml::LN_lc_lockedCanvas:
        case NS_ooxml::LN_c_chart:
        case NS_ooxml::LN_wps_wsp:
        case NS_ooxml::LN_wpg_wgp:
        case NS_ooxml::LN_sizeRelH_sizeRelH:
        case NS_ooxml::LN_sizeRelV_sizeRelV:
        case NS_ooxml::LN_hlinkClick_hlinkClick:
        case NS_ooxml::LN_wpc_wpc:
        {
            writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
            if( pProperties )
            {
                pProperties->resolve(*this);
            }

            // We'll map these to PARALLEL, save the original wrap type.
            if (nSprmId == NS_ooxml::LN_EG_WrapType_wrapTight)
                m_aInteropGrabBag[u"EG_WrapType"_ustr] <<= u"wrapTight"_ustr;
            else if (nSprmId == NS_ooxml::LN_EG_WrapType_wrapThrough)
                m_aInteropGrabBag[u"EG_WrapType"_ustr] <<= u"wrapThrough"_ustr;

            switch (nSprmId)
            {
                case NS_ooxml::LN_EG_WrapType_wrapSquare:
                case NS_ooxml::LN_EG_WrapType_wrapThrough:
                case NS_ooxml::LN_EG_WrapType_wrapTight:
                {
                    // tdf#137850: Word >= 2013 seems to ignore bBehindDoc except for wrapNone, but older versions honour it.
                    if (m_bBehindDoc && m_rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() > 14)
                        m_bOpaque = !m_rDomainMapper.IsInHeaderFooter();
                }
                break;
            }

        }
        break;
        case NS_ooxml::LN_CT_WrapTight_wrapPolygon:
        case NS_ooxml::LN_CT_WrapThrough_wrapPolygon:
            {
                WrapPolygonHandler aHandler;

                resolveSprmProps(aHandler, rSprm);

                mpWrapPolygon = aHandler.getPolygon();

                // Save the wrap path in case we can't handle it natively: drawinglayer shapes, TextFrames.
                m_aInteropGrabBag[u"CT_WrapPath"_ustr] <<= mpWrapPolygon->getPointSequenceSequence();
            }
            break;
        case NS_ooxml::LN_CT_Anchor_positionH:
        {
            // Use a special handler for the positioning
            auto pHandler = std::make_shared<PositionHandler>( m_rPositionOffsets, m_rAligns );
            writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
            if( pProperties )
            {
                pProperties->resolve( *pHandler );
                if( !m_bUseSimplePos )
                {
                    m_nHoriRelation = pHandler->relation();
                    m_bPageToggle = pHandler->GetPageToggle();
                    m_nHoriOrient = pHandler->orientation();
                    m_nLeftPosition = pHandler->position();
                }
            }
        }
        break;
        case NS_ooxml::LN_CT_Anchor_positionV:
        {
            // Use a special handler for the positioning
            auto pHandler = std::make_shared<PositionHandler>( m_rPositionOffsets, m_rAligns);
            writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
            if( pProperties )
            {
                pProperties->resolve( *pHandler );
                if( !m_bUseSimplePos )
                {
                    m_nVertRelation = pHandler->relation();
                    m_nVertOrient = pHandler->orientation();
                    m_nTopPosition = pHandler->position();
                }
            }
        }
        break;
        case NS_ooxml::LN_CT_SizeRelH_pctWidth:
        case NS_ooxml::LN_CT_SizeRelV_pctHeight:
            if (m_rPositivePercentages.empty())
                break;

            if (m_xShape.is())
            {
                sal_Int16 nPositivePercentage = rtl::math::round(m_rPositivePercentages.front().toDouble() / oox::drawingml::PER_PERCENT);

                if (nPositivePercentage)
                {
                    uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
                    OUString aProperty = nSprmId == NS_ooxml::LN_CT_SizeRelH_pctWidth ? u"RelativeWidth"_ustr : u"RelativeHeight"_ustr;

                    sal_Int32 nTextPreRotateAngle = 0;
                    uno::Any aAny;
                    if (xPropertySet->getPropertySetInfo()->hasPropertyByName(
                            u"CustomShapeGeometry"_ustr))
                    {
                        aAny = xPropertySet->getPropertyValue(u"CustomShapeGeometry"_ustr);
                    }
                    comphelper::SequenceAsHashMap aCustomShapeGeometry(aAny);
                    auto it = aCustomShapeGeometry.find(u"TextPreRotateAngle"_ustr);
                    if (it != aCustomShapeGeometry.end())
                    {
                        nTextPreRotateAngle = it->second.get<sal_Int32>();
                    }
                    if (nTextPreRotateAngle == 0)
                    {
                        xPropertySet->setPropertyValue(aProperty,
                                                       uno::Any(nPositivePercentage));
                    }
                }
            }

            // Make sure the token is consumed even if xShape is an empty
            // reference.
            m_rPositivePercentages.pop();
            break;
        case NS_ooxml::LN_EG_WrapType_wrapNone:
            //depending on the behindDoc attribute text wraps through behind or in front of the object
            m_nWrap = text::WrapTextMode_THROUGH;

            // Wrap though means the margins defined earlier should not be
            // respected.
            m_nLeftMargin = 0;
            m_nTopMargin = 0;
            m_nRightMargin = 0;
            m_nBottomMargin = 0;
        break;
        case NS_ooxml::LN_EG_WrapType_wrapTopAndBottom:
            // tdf#137850: Word >= 2013 seems to ignore bBehindDoc except for wrapNone, but older versions honour it.
            if (m_bBehindDoc && m_rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() > 14)
                 m_bOpaque = !m_rDomainMapper.IsInHeaderFooter();
            m_nWrap = text::WrapTextMode_NONE;
        break;
        case NS_ooxml::LN_CT_GraphicalObject_graphicData:
            {
                m_bIsGraphic = true;

                writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
                if( pProperties )
                    pProperties->resolve(*this);
            }
        break;
        case NS_ooxml::LN_CT_NonVisualDrawingProps_a_hlinkClick:
            {
                writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
                if( pProperties )
                    pProperties->resolve( *this );
            }
        break;
        default:
            SAL_WARN("writerfilter", "GraphicImport::lcl_sprm: unhandled token: " << nSprmId);
        break;
    }
}

void GraphicImport::lcl_entry(const writerfilter::Reference<Properties>::Pointer_t& /*ref*/)
{
}

rtl::Reference<SwXTextGraphicObject> GraphicImport::createGraphicObject(uno::Reference<graphic::XGraphic> const & rxGraphic,
                                                                      uno::Reference<beans::XPropertySet> const & xShapeProps)
{
    rtl::Reference<SwXTextGraphicObject> xGraphicObject;
    try
    {
        if (rxGraphic.is())
        {
            xGraphicObject = m_xTextDoc->createTextGraphicObject();
            xGraphicObject->setPropertyValue(getPropertyName(PROP_GRAPHIC), uno::Any(rxGraphic));
            xGraphicObject->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE),
                uno::Any( m_rGraphicImportType == IMPORT_AS_DETECTED_ANCHOR ?
                                    text::TextContentAnchorType_AT_CHARACTER :
                                    text::TextContentAnchorType_AS_CHARACTER ));

            //shapes have only one border
            table::BorderLine2 aBorderLine;
            GraphicBorderLine& rBorderLine = m_aBorders[0];
            if (rBorderLine.isEmpty() && xShapeProps.is() && xShapeProps->getPropertyValue(u"LineStyle"_ustr).get<drawing::LineStyle>() != drawing::LineStyle_NONE)
            {
                // In case we got no border tokens and we have the
                // original shape, then use its line properties as the
                // border.
                aBorderLine.Color = xShapeProps->getPropertyValue(u"LineColor"_ustr).get<sal_Int32>();
                aBorderLine.LineWidth = xShapeProps->getPropertyValue(u"LineWidth"_ustr).get<sal_Int32>();
            }
            else
            {
                aBorderLine.Color = 0;
                aBorderLine.InnerLineWidth = 0;
                aBorderLine.OuterLineWidth = static_cast<sal_Int16>(rBorderLine.nLineWidth);
                aBorderLine.LineDistance = 0;
            }
            PropertyIds const aBorderProps[] =
            {
                PROP_LEFT_BORDER,
                PROP_RIGHT_BORDER,
                PROP_TOP_BORDER,
                PROP_BOTTOM_BORDER
            };

            for(PropertyIds const & rBorderProp : aBorderProps)
                xGraphicObject->setPropertyValue(getPropertyName(rBorderProp), uno::Any(aBorderLine));

            // setting graphic object shadow properties
            if (m_bShadow)
            {
                // Shadow width is approximated by average of X and Y
                table::ShadowFormat aShadow;
                sal_uInt32 nShadowColor = m_nShadowColor & 0x00FFFFFF; // The shadow color we get is RGB only.
                sal_Int32 nShadowWidth = (abs(m_nShadowXDistance)
                                          + abs(m_nShadowYDistance)) / 2;

                aShadow.ShadowWidth = nShadowWidth;
                sal_uInt8 nShadowTransparence = float(m_nShadowTransparence) * 2.55;
                nShadowColor |= (nShadowTransparence << 24); // Add transparence to the color.
                aShadow.Color = nShadowColor;
                // Distances -ve for top and right, +ve for bottom and left
                if (m_nShadowXDistance > 0)
                {
                    if (m_nShadowYDistance > 0)
                        aShadow.Location = table::ShadowLocation_BOTTOM_RIGHT;
                    else
                        aShadow.Location = table::ShadowLocation_TOP_RIGHT;
                }
                else
                {
                    if (m_nShadowYDistance > 0)
                        aShadow.Location = table::ShadowLocation_BOTTOM_LEFT;
                    else
                        aShadow.Location = table::ShadowLocation_TOP_LEFT;
                }

                xGraphicObject->setPropertyValue(getPropertyName(PROP_SHADOW_FORMAT), uno::Any(aShadow));
            }

            // setting properties for all types
            if( m_bPositionProtected )
                xGraphicObject->setPropertyValue(getPropertyName( PROP_POSITION_PROTECTED ),
                    uno::Any(true));
            if( m_bSizeProtected )
                xGraphicObject->setPropertyValue(getPropertyName( PROP_SIZE_PROTECTED ),
                    uno::Any(true));

            xGraphicObject->setPropertyValue(getPropertyName(PROP_DECORATIVE), uno::Any(m_bDecorative));
            if (m_rGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)
            {
                if (m_bHidden)
                    xGraphicObject->setPropertyValue(u"Visible"_ustr, uno::Any(false));

                if (m_nHoriRelation == text::RelOrientation::FRAME
                    && (m_nHoriOrient == text::HoriOrientation::LEFT
                        || m_nHoriOrient == text::HoriOrientation::RIGHT
                        || m_nHoriOrient == text::HoriOrientation::INSIDE
                        || m_nHoriOrient == text::HoriOrientation::OUTSIDE))
                {
                    // before compat15, relative left/right/inside/outside honored margins.
                    if (m_rDomainMapper.GetSettingsTable()->GetWordCompatibilityMode() < 15)
                        m_nHoriRelation = text::RelOrientation::PRINT_AREA;
                }

                lcl_adjustMarginsAndOrientation();

                // adjust top/bottom margins
                if( m_nVertOrient == text::VertOrientation::TOP &&
                    ( m_nVertRelation == text::RelOrientation::PAGE_PRINT_AREA ||
                        m_nVertRelation == text::RelOrientation::PAGE_FRAME))
                    m_nTopMargin = 0;
                if( m_nVertOrient == text::VertOrientation::BOTTOM &&
                    ( m_nVertRelation == text::RelOrientation::PAGE_PRINT_AREA ||
                        m_nVertRelation == text::RelOrientation::PAGE_FRAME))
                    m_nBottomMargin = 0;
                if( m_nVertOrient == text::VertOrientation::BOTTOM &&
                    m_nVertRelation == text::RelOrientation::PAGE_PRINT_AREA )
                    m_nBottomMargin = 0;

                if (m_nVertRelation == text::RelOrientation::TEXT_LINE)
                {
                    // Word's "line" is "below the bottom of the line", our TEXT_LINE is
                    // "towards top, from the bottom of the line", so invert the vertical position.
                    m_nTopPosition *= -1;
                }

                applyPosition(xGraphicObject);
                applyRelativePosition(xGraphicObject);
                if( !m_bOpaque )
                {
                    xGraphicObject->setPropertyValue(getPropertyName( PROP_OPAQUE ), uno::Any(m_bOpaque));
                }
                xGraphicObject->setPropertyValue(getPropertyName( PROP_SURROUND ),
                    uno::Any(static_cast<sal_Int32>(m_nWrap)));
                if( m_rDomainMapper.IsInTable())
                    xGraphicObject->setPropertyValue(getPropertyName( PROP_FOLLOW_TEXT_FLOW ),
                        uno::Any(m_bLayoutInCell));

                xGraphicObject->setPropertyValue(getPropertyName(PROP_ALLOW_OVERLAP),
                                                           uno::Any(m_bAllowOverlap));

                xGraphicObject->setPropertyValue(getPropertyName( PROP_SURROUND_CONTOUR ),
                    uno::Any(m_bContour));
                xGraphicObject->setPropertyValue(getPropertyName( PROP_CONTOUR_OUTSIDE ),
                    uno::Any(m_bContourOutside));
                applyMargins(xGraphicObject);
            }

            xGraphicObject->setPropertyValue(getPropertyName( PROP_ADJUST_CONTRAST ),
                uno::Any(static_cast<sal_Int16>(m_nContrast)));
            xGraphicObject->setPropertyValue(getPropertyName( PROP_ADJUST_LUMINANCE ),
                uno::Any(static_cast<sal_Int16>(m_nBrightness)));
            if(m_eColorMode != drawing::ColorMode_STANDARD)
            {
                xGraphicObject->setPropertyValue(getPropertyName( PROP_GRAPHIC_COLOR_MODE ),
                    uno::Any(m_eColorMode));
            }

            // copy the image fill area properties
            xGraphicObject->setPropertyValue(u"FillBackground"_ustr,
                                             xShapeProps->getPropertyValue(u"FillBackground"_ustr));
            xGraphicObject->setPropertyValue(u"FillBitmap"_ustr,
                                             xShapeProps->getPropertyValue(u"FillBitmap"_ustr));
            xGraphicObject->setPropertyValue(
                u"FillBitmapLogicalSize"_ustr, xShapeProps->getPropertyValue(u"FillBitmapLogicalSize"_ustr));
            xGraphicObject->setPropertyValue(u"FillBitmapMode"_ustr,
                                             xShapeProps->getPropertyValue(u"FillBitmapMode"_ustr));
            xGraphicObject->setPropertyValue(u"FillBitmapOffsetX"_ustr,
                                             xShapeProps->getPropertyValue(u"FillBitmapOffsetX"_ustr));
            xGraphicObject->setPropertyValue(
                u"FillBitmapPositionOffsetX"_ustr,
                xShapeProps->getPropertyValue(u"FillBitmapPositionOffsetX"_ustr));
            xGraphicObject->setPropertyValue(
                u"FillBitmapPositionOffsetY"_ustr,
                xShapeProps->getPropertyValue(u"FillBitmapPositionOffsetY"_ustr));
            xGraphicObject->setPropertyValue(
                u"FillBitmapRectanglePoint"_ustr,
                xShapeProps->getPropertyValue(u"FillBitmapRectanglePoint"_ustr));
            xGraphicObject->setPropertyValue(u"FillBitmapSizeX"_ustr,
                                             xShapeProps->getPropertyValue(u"FillBitmapSizeX"_ustr));
            xGraphicObject->setPropertyValue(u"FillBitmapSizeY"_ustr,
                                             xShapeProps->getPropertyValue(u"FillBitmapSizeY"_ustr));
            xGraphicObject->setPropertyValue(u"FillBitmapStretch"_ustr,
                                             xShapeProps->getPropertyValue(u"FillBitmapStretch"_ustr));
            xGraphicObject->setPropertyValue(u"FillBitmapTile"_ustr,
                                             xShapeProps->getPropertyValue(u"FillBitmapTile"_ustr));
            xGraphicObject->setPropertyValue(u"FillBitmapURL"_ustr,
                                             xShapeProps->getPropertyValue(u"FillBitmapURL"_ustr));
            xGraphicObject->setPropertyValue(u"FillColor"_ustr,
                                             xShapeProps->getPropertyValue(u"FillColor"_ustr));
            xGraphicObject->setPropertyValue(u"FillColor2"_ustr,
                                             xShapeProps->getPropertyValue(u"FillColor2"_ustr));
            xGraphicObject->setPropertyValue(u"FillComplexColor"_ustr,
                                             xShapeProps->getPropertyValue(u"FillComplexColor"_ustr));
            xGraphicObject->setPropertyValue(u"FillGradient"_ustr,
                                             xShapeProps->getPropertyValue(u"FillGradient"_ustr));
            xGraphicObject->setPropertyValue(
                u"FillGradientStepCount"_ustr, xShapeProps->getPropertyValue(u"FillGradientStepCount"_ustr));
            xGraphicObject->setPropertyValue(u"FillHatch"_ustr,
                                             xShapeProps->getPropertyValue(u"FillHatch"_ustr));
            xGraphicObject->setPropertyValue(u"FillStyle"_ustr,
                                             xShapeProps->getPropertyValue(u"FillStyle"_ustr));
            xGraphicObject->setPropertyValue(u"FillTransparence"_ustr,
                                             xShapeProps->getPropertyValue(u"FillTransparence"_ustr));
            xGraphicObject->setPropertyValue(
                u"FillTransparenceGradient"_ustr,
                xShapeProps->getPropertyValue(u"FillTransparenceGradient"_ustr));

            applyZOrder(xGraphicObject);

            //there seems to be no way to detect the original size via _real_ API
            uno::Reference< beans::XPropertySet > xGraphicProperties(rxGraphic, uno::UNO_QUERY_THROW);

            if (mpWrapPolygon)
            {
                uno::Any aContourPolyPolygon;
                awt::Size aGraphicSize;
                WrapPolygon::Pointer_t pCorrected;
                xGraphicProperties->getPropertyValue(getPropertyName(PROP_SIZE100th_M_M)) >>= aGraphicSize;
                if (aGraphicSize.Width && aGraphicSize.Height)
                {
                    pCorrected = mpWrapPolygon->correctWordWrapPolygon(aGraphicSize);
                }
                else
                {
                    xGraphicProperties->getPropertyValue(getPropertyName(PROP_SIZE_PIXEL)) >>= aGraphicSize;
                    if (aGraphicSize.Width && aGraphicSize.Height)
                    {
                        pCorrected = mpWrapPolygon->correctWordWrapPolygonPixel(aGraphicSize);
                    }
                }

                text::GraphicCrop aGraphicCrop;
                xShapeProps->getPropertyValue(u"GraphicCrop"_ustr) >>= aGraphicCrop;
                if (aGraphicCrop.Top != 0 || aGraphicCrop.Bottom != 0 || aGraphicCrop.Left != 0
                    || aGraphicCrop.Right != 0)
                {
                    // Word's wrap polygon deals with a canvas which has the size of the already
                    // cropped graphic, correct our polygon to have the same render result.
                    pCorrected = pCorrected->correctCrop(aGraphicSize, aGraphicCrop);
                }

                if (pCorrected)
                {
                    aContourPolyPolygon <<= pCorrected->getPointSequenceSequence();
                    xGraphicObject->setPropertyValue(getPropertyName(PROP_CONTOUR_POLY_POLYGON),
                        aContourPolyPolygon);
                    // We should bring it to front, even if wp:anchor's behindDoc="1",
                    // because otherwise paragraph background (if set) overlaps the graphic
                    // TODO: if paragraph's background becomes bottommost, then remove this hack
                    xGraphicObject->setPropertyValue(u"Opaque"_ustr, uno::Any(true));
                }
            }


            if (m_rGraphicImportType == IMPORT_AS_DETECTED_INLINE
                || m_rGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)
            {
                if( getXSize() && getYSize() )
                    xGraphicObject->setPropertyValue(getPropertyName(PROP_SIZE),
                        uno::Any( awt::Size( getXSize(), getYSize() )));
                applyMargins(xGraphicObject);
                applyName(xGraphicObject);
                applyHyperlink(xGraphicObject, false);
            }

            // Handle horizontal flip.
            bool bMirrored = false;
            xShapeProps->getPropertyValue(u"IsMirrored"_ustr) >>= bMirrored;
            if (bMirrored)
            {
                xGraphicObject->setPropertyValue(u"HoriMirroredOnEvenPages"_ustr,
                                                           uno::Any(true));
                xGraphicObject->setPropertyValue(u"HoriMirroredOnOddPages"_ustr,
                                                           uno::Any(true));
            }
        }
    }
    catch( const uno::Exception& )
    {
        TOOLS_WARN_EXCEPTION("writerfilter", "");
    }
    return xGraphicObject;
}


void GraphicImport::data(const sal_uInt8* buf, size_t len)
{
    uno::Reference< io::XInputStream > xIStream = new XInputStreamHelper( buf, len );
    beans::PropertyValues aMediaProperties{ comphelper::makePropertyValue(
        getPropertyName(PROP_INPUT_STREAM), xIStream) };

    uno::Reference<beans::XPropertySet> xPropertySet;
    uno::Reference<graphic::XGraphicProvider> xGraphicProvider(graphic::GraphicProvider::create(m_xComponentContext));
    uno::Reference<graphic::XGraphic> xGraphic = xGraphicProvider->queryGraphic(aMediaProperties);
    m_xGraphicObject = createGraphicObject(xGraphic, xPropertySet);
}


void GraphicImport::lcl_startSectionGroup()
{
}


void GraphicImport::lcl_endSectionGroup()
{
}


void GraphicImport::lcl_startParagraphGroup()
{
}


void GraphicImport::lcl_endParagraphGroup()
{
}

void GraphicImport::lcl_startCharacterGroup()
{
}

void GraphicImport::lcl_endCharacterGroup()
{
}

void GraphicImport::lcl_text(const sal_uInt8 * /*_data*/, size_t /*len*/)
{
}

void GraphicImport::lcl_utext(const sal_Unicode * /*_data*/, size_t /*len*/)
{
}

void GraphicImport::lcl_props(const writerfilter::Reference<Properties>::Pointer_t& /*ref*/)
{
}

void GraphicImport::lcl_table(Id /*name*/, const writerfilter::Reference<Table>::Pointer_t& /*ref*/)
{
}

void GraphicImport::lcl_substream(Id /*name*/, const writerfilter::Reference<Stream>::Pointer_t& /*ref*/)
{
}

void GraphicImport::lcl_startShape(uno::Reference<drawing::XShape> const&)
{
}

void GraphicImport::lcl_endShape( )
{
}

bool GraphicImport::IsGraphic() const
{
    return m_bIsGraphic;
}

sal_Int32 GraphicImport::GetLeftMarginOrig() const
{
    return m_nLeftMarginOrig;
}

}

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