tdf#135667 DOCX export: fix border line of OLE objects

which wasn't exported.

Note: the enlarged monolithic export function was
split in the following new functions:

- WriteOLEShape() exports the replacement shape of
the OLE object.

- GetOLEStyle() returns the string value of the
style attribute.

- ExportOLESurround() handles the surround settings.

Also add GetVMLShapeTypeDefinition() to reuse picture
frame VML formula string used by VMLExport.

Co-authored-by: Arató Dániel (NISZ)

Change-Id: I29800a50c60a824a14849ac286a18e5e2f97c689
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/102034
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/include/oox/export/vmlexport.hxx b/include/oox/export/vmlexport.hxx
index 06dbbc5..94aeb86 100644
--- a/include/oox/export/vmlexport.hxx
+++ b/include/oox/export/vmlexport.hxx
@@ -145,6 +145,7 @@ public:
    void    SetHashMarkForType(bool bUseHashMarkForType) { m_bUseHashMarkForType = bUseHashMarkForType; }
    void    OverrideShapeIDGen(bool bOverrideShapeIdGeneration,
                            const OString& sShapeIDPrefix = OString());
    static OString GetVMLShapeTypeDefinition(const OString& sShapeID, const bool bIsPictureFrame);

protected:
    /// Add an attribute to the generated <v:shape/> element.
diff --git a/oox/source/export/vmlexport.cxx b/oox/source/export/vmlexport.cxx
index 0a0a634..dea03c5 100644
--- a/oox/source/export/vmlexport.cxx
+++ b/oox/source/export/vmlexport.cxx
@@ -1219,54 +1219,24 @@ sal_uInt32 VMLExport::GenerateShapeId()
        return m_nShapeIDCounter++;
}

sal_Int32 VMLExport::StartShape()
OString VMLExport::GetVMLShapeTypeDefinition( const OString& sShapeID, const bool bIsPictureFrame )
{
    if ( m_nShapeType == ESCHER_ShpInst_Nil )
        return -1;

    // some of the shapes have their own name ;-)
    sal_Int32 nShapeElement = -1;
    bool bReferToShapeType = false;
    switch ( m_nShapeType )
    {
        case ESCHER_ShpInst_NotPrimitive:   nShapeElement = XML_shape;     break;
        case ESCHER_ShpInst_Rectangle:      nShapeElement = XML_rect;      break;
        case ESCHER_ShpInst_RoundRectangle: nShapeElement = XML_roundrect; break;
        case ESCHER_ShpInst_Ellipse:        nShapeElement = XML_oval;      break;
        case ESCHER_ShpInst_Arc:            nShapeElement = XML_arc;       break;
        case ESCHER_ShpInst_Line:           nShapeElement = XML_line;      break;
        case ESCHER_ShpInst_HostControl:
        {
            // We don't have a shape definition for host control in presetShapeDefinitions.xml
            // So use a definition copied from DOCX file created with MSO
            bReferToShapeType = true;
            nShapeElement = XML_shape;
            if ( !m_aShapeTypeWritten[ m_nShapeType ] )
            {
                OString sShapeType =
                    "<v:shapetype id=\"shapetype_" + OString::number(m_nShapeType) +
                        "\" coordsize=\"21600,21600\" o:spt=\"" + OString::number(m_nShapeType) +
    OString sShapeType;
    if ( !bIsPictureFrame )
        // We don't have a shape definition for host control in presetShapeDefinitions.xml
        // So use a definition copied from DOCX file created with MSO
        sShapeType = "<v:shapetype id=\"shapetype_" + sShapeID +
                        "\" coordsize=\"21600,21600\" o:spt=\"" + sShapeID +
                        "\" path=\"m,l,21600l21600,21600l21600,xe\">\n"
                        "<v:stroke joinstyle=\"miter\"/>\n"
                        "<v:path shadowok=\"f\" o:extrusionok=\"f\" strokeok=\"f\" fillok=\"f\" o:connecttype=\"rect\"/>\n"
                        "<o:lock v:ext=\"edit\" shapetype=\"t\"/>\n"
                    "</v:shapetype>";
                m_pSerializer->write(sShapeType);
                m_aShapeTypeWritten[ m_nShapeType ] = true;
            }
            break;
        }
        case ESCHER_ShpInst_PictureFrame:
        {
            // We don't have a shape definition for picture frame in presetShapeDefinitions.xml
            // So use a definition copied from DOCX file created with MSO
            bReferToShapeType = true;
            nShapeElement = XML_shape;
            if ( !m_aShapeTypeWritten[ m_nShapeType ] )
            {
                OString sShapeType =
                    "<v:shapetype id=\"shapetype_" + OString::number(m_nShapeType) +
                        "\" coordsize=\"21600,21600\" o:spt=\"" + OString::number(m_nShapeType) +
    else
        // We don't have a shape definition for picture frame in presetShapeDefinitions.xml
        // So use a definition copied from DOCX file created with MSO
        sShapeType = "<v:shapetype id=\"shapetype_" + sShapeID +
                        "\" coordsize=\"21600,21600\" o:spt=\"" + sShapeID +
                        "\" o:preferrelative=\"t\" path=\"m@4@5l@4@11@9@11@9@5xe\" filled=\"f\" stroked=\"f\">\n"
                        "<v:stroke joinstyle=\"miter\"/>\n"
                        "<v:formulas>\n"
@@ -1286,7 +1256,43 @@ sal_Int32 VMLExport::StartShape()
                        "<v:path o:extrusionok=\"f\" gradientshapeok=\"t\" o:connecttype=\"rect\"/>\n"
                        "<o:lock v:ext=\"edit\" aspectratio=\"t\"/>\n"
                        "</v:shapetype>";
                m_pSerializer->write(sShapeType);
    return sShapeType;
}

sal_Int32 VMLExport::StartShape()
{
    if ( m_nShapeType == ESCHER_ShpInst_Nil )
        return -1;

    // some of the shapes have their own name ;-)
    sal_Int32 nShapeElement = -1;
    bool bReferToShapeType = false;
    switch ( m_nShapeType )
    {
        case ESCHER_ShpInst_NotPrimitive:   nShapeElement = XML_shape;     break;
        case ESCHER_ShpInst_Rectangle:      nShapeElement = XML_rect;      break;
        case ESCHER_ShpInst_RoundRectangle: nShapeElement = XML_roundrect; break;
        case ESCHER_ShpInst_Ellipse:        nShapeElement = XML_oval;      break;
        case ESCHER_ShpInst_Arc:            nShapeElement = XML_arc;       break;
        case ESCHER_ShpInst_Line:           nShapeElement = XML_line;      break;
        case ESCHER_ShpInst_HostControl:
        {
            bReferToShapeType = true;
            nShapeElement = XML_shape;
            if ( !m_aShapeTypeWritten[ m_nShapeType ] )
            {
                m_pSerializer->write(GetVMLShapeTypeDefinition(OString::number(m_nShapeType), false));
                m_aShapeTypeWritten[ m_nShapeType ] = true;
            }
            break;
        }
        case ESCHER_ShpInst_PictureFrame:
        {
            bReferToShapeType = true;
            nShapeElement = XML_shape;
            if ( !m_aShapeTypeWritten[ m_nShapeType ] )
            {
                m_pSerializer->write(GetVMLShapeTypeDefinition(OString::number(m_nShapeType), true));
                m_aShapeTypeWritten[ m_nShapeType ] = true;
            }
            break;
diff --git a/sw/qa/extras/ooxmlexport/data/tdf135667.odt b/sw/qa/extras/ooxmlexport/data/tdf135667.odt
new file mode 100644
index 0000000..2db8ade
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf135667.odt
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx
index 5aad7cc..e29ec68 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx
@@ -662,6 +662,23 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testTdf133702, "tdf133702.docx")
    assertXPath(pXmlDocument, "/w:document/w:body/w:p[1]/w:pPr/w:framePr");
}

DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testTdf135667, "tdf135667.odt")
{
    xmlDocUniquePtr pXmlDocument = parseExport("word/document.xml");

    // This was missing.
    assertXPath(pXmlDocument, "/w:document/w:body/w:p[2]/w:r/w:object/v:shapetype");

    // line settings
    assertXPath(pXmlDocument, "/w:document/w:body/w:p[2]/w:r/w:object/v:shape", "stroked", "t");
    assertXPath(pXmlDocument, "/w:document/w:body/w:p[2]/w:r/w:object/v:shape", "strokecolor", "#FF0000");
    assertXPath(pXmlDocument, "/w:document/w:body/w:p[2]/w:r/w:object/v:shape", "strokeweight", "4pt");

    // line type
    assertXPath(pXmlDocument, "/w:document/w:body/w:p[2]/w:r/w:object/v:shape/v:stroke", "linestyle", "Single");
    assertXPath(pXmlDocument, "/w:document/w:body/w:p[2]/w:r/w:object/v:shape/v:stroke", "dashstyle", "Dash");
}

DECLARE_OOXMLEXPORT_TEST(testImageSpaceSettings, "tdf135047_ImageSpaceSettings.fodt")
{
    // tdf#135047 The spaces of image were not saved.
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 947a7f3..ec81036 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -88,10 +88,14 @@
#include <editeng/charhiddenitem.hxx>
#include <editeng/editobj.hxx>
#include <editeng/keepitem.hxx>
#include <editeng/borderline.hxx>
#include <svx/xdef.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xflclit.hxx>
#include <svx/xflgrit.hxx>
#include <svx/xlineit0.hxx>
#include <svx/xlnclit.hxx>
#include <svx/xlnwtit.hxx>
#include <svx/svdouno.hxx>
#include <svx/unobrushitemhelper.hxx>
#include <svl/grabbagitem.hxx>
@@ -5606,24 +5610,197 @@ void DocxAttributeOutput::WriteOLE( SwOLENode& rNode, const Size& rSize, const S
        m_pSerializer->startElementNS(XML_w, XML_object);
    }

    OString sShapeId = "ole_" + sId;

    //OLE Shape definition
    WriteOLEShape(*pFlyFrameFormat, rSize, sShapeId, sImageId);

    //OLE Object definition
    m_pSerializer->singleElementNS(XML_o, XML_OLEObject,
                                   XML_Type, "Embed",
                                   XML_ProgID, sProgID,
                                   XML_ShapeID, sShapeId.getStr(),
                                   XML_DrawAspect, sDrawAspect,
                                   XML_ObjectID, "_" + OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max())),
                                   FSNS( XML_r, XML_id ), sId );

    m_pSerializer->endElementNS(XML_w, XML_object);
}

void DocxAttributeOutput::WriteOLEShape(const SwFlyFrameFormat& rFrameFormat, const Size& rSize,
                                        const OString& rShapeId, const OUString& rImageId)
{
    assert(m_pSerializer);

    //Here is an attribute list where we collect the attributes what we want to export
    FastAttributeList* pAttr = FastSerializerHelper::createAttrList();
    pAttr->add(XML_id, rShapeId.getStr());

    //export the fixed shape type for picture frame
    m_pSerializer->write(vml::VMLExport::GetVMLShapeTypeDefinition(rShapeId, true));
    pAttr->add(XML_type, "shapetype_" + rShapeId);

    //Export the style attribute for position and size
    pAttr->add(XML_style, GetOLEStyle(rFrameFormat, rSize).getStr());
    //Get the OLE frame
    const SvxBoxItem& rBox = rFrameFormat.GetAttrSet().GetBox();
    OString sLineType;
    OString sDashType;
    //Word does not handle differently the four sides,
    //so we have to choose, and the left one is the winner:
    if (rBox.GetLeft())
    {
        //Get the left border color and width
        const Color aLineColor = rBox.GetLeft()->GetColor();
        const long aLineWidth = rBox.GetLeft()->GetWidth();

        //Convert the left OLE border style to OOXML
        //FIXME improve if it's necessary
        switch (rBox.GetLeft()->GetBorderLineStyle())
        {
            case SvxBorderLineStyle::SOLID:
                sLineType = OString("Single");
                sDashType = OString("Solid");
                break;
            case SvxBorderLineStyle::DASHED:
                sLineType = OString("Single");
                sDashType = OString("Dash");
                break;
            case SvxBorderLineStyle::DASH_DOT:
                sLineType = OString("Single");
                sDashType = OString("DashDot");
                break;
            case SvxBorderLineStyle::DASH_DOT_DOT:
                sLineType = OString("Single");
                sDashType = OString("ShortDashDotDot");
                break;
            case SvxBorderLineStyle::DOTTED:
                sLineType = OString("Single");
                sDashType = OString("Dot");
                break;
            case SvxBorderLineStyle::DOUBLE:
                sLineType = OString("ThinThin");
                sDashType = OString("Solid");
                break;
            case SvxBorderLineStyle::DOUBLE_THIN:
                sLineType = OString("ThinThin");
                sDashType = OString("Solid");
                break;
            case SvxBorderLineStyle::EMBOSSED:
                sLineType = OString("Single");
                sDashType = OString("Solid");
                break;
            case SvxBorderLineStyle::ENGRAVED:
                sLineType = OString("Single");
                sDashType = OString("Solid");
                break;
            case SvxBorderLineStyle::FINE_DASHED:
                sLineType = OString("Single");
                sDashType = OString("Dot");
                break;
            case SvxBorderLineStyle::INSET:
                sLineType = OString("Single");
                sDashType = OString("Solid");
                break;
            case SvxBorderLineStyle::OUTSET:
                sLineType = OString("Single");
                sDashType = OString("Solid");
                break;
            case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
            case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
            case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
                sLineType = OString("ThickThin");
                sDashType = OString("Solid");
                break;
            case SvxBorderLineStyle::THINTHICK_LARGEGAP:
            case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
            case SvxBorderLineStyle::THINTHICK_SMALLGAP:
                sLineType = OString("ThinThick");
                sDashType = OString("Solid");
                break;
            case SvxBorderLineStyle::NONE:
                sLineType = OString("");
                sDashType = OString("");
                break;
            default:
                SAL_WARN("sw.ww8", "Unknown line type on OOXML ELE export!");
                break;
        }

        //If there is a line add it for export
        if (!sLineType.isEmpty() && !sDashType.isEmpty())
        {
            pAttr->add(XML_stroked, "t");
            pAttr->add(XML_strokecolor, "#" + msfilter::util::ConvertColor(aLineColor));
            pAttr->add(XML_strokeweight, OString::number(aLineWidth / 20) + "pt");
        }
    }

    //Let's check the filltype of the OLE
    switch (rFrameFormat.GetAttrSet().Get(XATTR_FILLSTYLE).GetValue())
    {
        case drawing::FillStyle::FillStyle_SOLID:
        {
            //If solid, we get the color and add it to the exporter
            const Color rShapeColor = rFrameFormat.GetAttrSet().Get(XATTR_FILLCOLOR).GetColorValue();
            pAttr->add(XML_filled, "t");
            pAttr->add(XML_fillcolor, "#" + msfilter::util::ConvertColor(rShapeColor));
            break;
        }
        case drawing::FillStyle::FillStyle_GRADIENT:
        case drawing::FillStyle::FillStyle_HATCH:
        case drawing::FillStyle::FillStyle_BITMAP:
            //TODO
            break;
        case drawing::FillStyle::FillStyle_NONE:
        {
            pAttr->add(XML_filled, "f");
            break;
        }
        default:
            SAL_WARN("sw.ww8", "Unknown fill type on OOXML OLE export!");
            break;
    }
    pAttr->addNS(XML_o, XML_ole, ""); //compulsory, even if it's empty
    m_pSerializer->startElementNS(XML_v, XML_shape, pAttr);//Write the collected atttrs...

    if (!sLineType.isEmpty() && !sDashType.isEmpty()) //If there is a line/dash style it is time to export it
    {
        m_pSerializer->singleElementNS(XML_v, XML_stroke, XML_linestyle, sLineType, XML_dashstyle, sDashType);
    }

    // shape filled with the preview image
    m_pSerializer->singleElementNS(XML_v, XML_imagedata,
                                   FSNS(XML_r, XML_id), rImageId,
                                   FSNS(XML_o, XML_title), "");

    //export wrap settings
    if (rFrameFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) //As-char objs does not have surround.
        ExportOLESurround(rFrameFormat.GetSurround());

    m_pSerializer->endElementNS(XML_v, XML_shape);
}

OString DocxAttributeOutput::GetOLEStyle(const SwFlyFrameFormat& rFormat, const Size& rSize)
{
    //tdf#131539: Export OLE positions in docx:
    //This string will store the position output for the xml
    OString aPos;
    //This string will store the relative position for aPos
    OString aAnch;

    if (pFlyFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
    if (rFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
    {
        //Get the horizontal alignment of the OLE via the frame format, to aHAlign
        OString aHAlign = convertToOOXMLHoriOrient(pFlyFrameFormat->GetHoriOrient().GetHoriOrient(),
            pFlyFrameFormat->GetHoriOrient().IsPosToggle());
        OString aHAlign = convertToOOXMLHoriOrient(rFormat.GetHoriOrient().GetHoriOrient(),
            rFormat.GetHoriOrient().IsPosToggle());
        //Get the vertical alignment of the OLE via the frame format to aVAlign
        OString aVAlign = convertToOOXMLVertOrient(pFlyFrameFormat->GetVertOrient().GetVertOrient());
        OString aVAlign = convertToOOXMLVertOrient(rFormat.GetVertOrient().GetVertOrient());

        //Get the relative horizontal positions for the anchors
        OString aHAnch = convertToOOXMLHoriOrientRel(pFlyFrameFormat->GetHoriOrient().GetRelationOrient());
        OString aHAnch = convertToOOXMLHoriOrientRel(rFormat.GetHoriOrient().GetRelationOrient());
        //Get the relative vertical positions for the anchors
        OString aVAnch = convertToOOXMLVertOrientRel(pFlyFrameFormat->GetVertOrient().GetRelationOrient());
        OString aVAnch = convertToOOXMLVertOrientRel(rFormat.GetVertOrient().GetRelationOrient());

        //Choice that the horizontal position is relative or not
        if (!aHAlign.isEmpty())
@@ -5640,81 +5817,61 @@ void DocxAttributeOutput::WriteOLE( SwOLENode& rNode, const Size& rSize, const S

        //Query the positions to aPos from frameformat
        aPos =
            "position:absolute;margin-left:" + OString::number(double(pFlyFrameFormat->GetHoriOrient().GetPos()) / 20) +
            "pt;margin-top:" + OString::number(double(pFlyFrameFormat->GetVertOrient().GetPos()) / 20) + "pt;";
            "position:absolute;margin-left:" + OString::number(double(rFormat.GetHoriOrient().GetPos()) / 20) +
            "pt;margin-top:" + OString::number(double(rFormat.GetVertOrient().GetPos()) / 20) + "pt;";
    }

    OString sShapeStyle = "width:" + OString::number( double( rSize.Width() ) / 20 ) +
                        "pt;height:" + OString::number( double( rSize.Height() ) / 20 ) +
                        "pt"; //from VMLExport::AddRectangleDimensions(), it does: value/20
    OString sShapeId = "ole_" + sId;

    //Export anchor setting, if it exists
    if (!aPos.isEmpty() && !aAnch.isEmpty())
        sShapeStyle = aPos + sShapeStyle  + aAnch;

    // shape definition
    const bool bFilled = pFlyFrameFormat->GetAttrSet().Get(XATTR_FILLSTYLE).GetValue() != FillStyle::FillStyle_NONE;
    const Color rShapeColor = pFlyFrameFormat->GetAttrSet().Get(XATTR_FILLCOLOR).GetColorValue();
    if (bFilled)
    return sShapeStyle;
}

void DocxAttributeOutput::ExportOLESurround(const SwFormatSurround& rWrap)
{
    const bool bIsContour = rWrap.IsContour(); //Has the shape countour or not
    OString sSurround;
    OString sSide;

    //Map the ODF wrap settings to OOXML one
    switch (rWrap.GetSurround())
    {
        m_pSerializer->startElementNS( XML_v, XML_shape,
                                       XML_id, sShapeId.getStr(),
                                       XML_style, sShapeStyle.getStr(),
                                       XML_fillcolor, "#" + msfilter::util::ConvertColor( rShapeColor ),
                                       FSNS( XML_o, XML_ole ), ""); //compulsory, even if it's empty
    }
    else
    {
        m_pSerializer->startElementNS( XML_v, XML_shape,
                                       XML_id, sShapeId.getStr(),
                                       XML_style, sShapeStyle.getStr(),
                                       XML_filled, "f",
                                       FSNS( XML_o, XML_ole ), ""); //compulsory, even if it's empty
        case text::WrapTextMode::WrapTextMode_NONE:
            sSurround = OString("topAndBottom");
            break;
        case text::WrapTextMode::WrapTextMode_PARALLEL:
            sSurround = bIsContour ? OString("tight") : OString("square");
            break;
        case text::WrapTextMode::WrapTextMode_DYNAMIC:
            sSide = OString("largest");
            sSurround = bIsContour ? OString("tight") : OString("square");
            break;
        case text::WrapTextMode::WrapTextMode_LEFT:
            sSide = OString("left");
            sSurround = bIsContour ? OString("tight") : OString("square");
            break;
        case text::WrapTextMode::WrapTextMode_RIGHT:
            sSide = OString("right");
            sSurround = bIsContour ? OString("tight") : OString("square");
            break;
        default:
            SAL_WARN("sw.ww8", "Unknown surround type on OOXML export!");
            break;
    }

    // shape filled with the preview image
    m_pSerializer->singleElementNS( XML_v, XML_imagedata,
                                    FSNS( XML_r, XML_id ), sImageId,
                                    FSNS( XML_o, XML_title ), "" );

    //export wrap settings
    if(pFlyFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
    //if there is a setting export it:
    if (!sSurround.isEmpty())
    {
        const SwFormatSurround aWrap = pFlyFrameFormat->GetSurround();
        const bool bIsCountur = aWrap.IsContour();

        if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_NONE)
            m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "topAndBottom");
        if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_PARALLEL && !bIsCountur)
            m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "square");
        if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_PARALLEL && bIsCountur)
            m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "tight");
        if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_DYNAMIC && !bIsCountur)
            m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "square", XML_side, "largest");
        if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_LEFT && !bIsCountur)
            m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "square", XML_side, "left");
        if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_RIGHT && !bIsCountur)
            m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "square", XML_side, "right");
        if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_DYNAMIC && bIsCountur)
            m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "tight", XML_side, "largest");
        if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_LEFT && bIsCountur)
            m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "tight", XML_side, "left");
        if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_RIGHT && bIsCountur)
            m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "tight", XML_side, "right");
        if (sSide.isEmpty())
            m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, sSurround);
        else
            m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, sSurround, XML_side, sSide);
    }
    m_pSerializer->endElementNS( XML_v, XML_shape );

    // OLE object definition
    m_pSerializer->singleElementNS( XML_o, XML_OLEObject,
                                    XML_Type, "Embed",
                                    XML_ProgID, sProgID,
                                    XML_ShapeID, sShapeId.getStr(),
                                    XML_DrawAspect, sDrawAspect,
                                    XML_ObjectID, "_" + OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max())),
                                    FSNS( XML_r, XML_id ), sId );

    m_pSerializer->endElementNS( XML_w, XML_object );
}

void DocxAttributeOutput::WritePostponedCustomShape()
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index e37819f..e711d88 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -408,6 +408,10 @@ private:
    bool WriteOLEMath( const SwOLENode& rNode, const sal_Int8 nAlign );
    void PostponeOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat );
    void WriteOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* rFlyFrameFormat );
    void WriteOLEShape(const SwFlyFrameFormat& rFrameFormat, const Size& rSize,
                       const OString& rShapeId, const OUString& rImageId);
    static OString GetOLEStyle(const SwFlyFrameFormat& rFormat, const Size& rSize);
    void ExportOLESurround(const SwFormatSurround& rWrap);

    void WriteActiveXControl(const SdrObject* pObject, const SwFrameFormat& rFrameFormat, bool bInsideRun);
    bool ExportAsActiveXControl(const SdrObject* pObject) const;