fdo#78590: Fix for Corruption of para with framePr & drawing object into para

Issue:
 - File contains paragraph with framePr have graphic object in it.
 - So Libreoffice converts framePr into textbox.
 - So after saving file textbox gets exported with drawing object.
 - MS office does not allowed drawing object inside tetxbox.

Change-Id: I673e0e9f6681a189bde1c63a8cb7aea2cac0ab41
Fix: - Export framePr into paragraph and igonre exporting of
       dummy textbox added by LO for framePr.
Reviewed-on: https://gerrit.libreoffice.org/9389
Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
Tested-by: Miklos Vajna <vmiklos@collabora.co.uk>
diff --git a/sw/qa/extras/ooxmlexport/data/FDO78590.docx b/sw/qa/extras/ooxmlexport/data/FDO78590.docx
new file mode 100644
index 0000000..c32b0276b9
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/FDO78590.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
index d97be6f..5b0244e 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
@@ -3635,6 +3635,19 @@ DECLARE_OOXMLEXPORT_TEST(testfdo79817,"fdo79817.docx")
    assertXPath ( pXmlDoc, "/w:document/w:body/w:p[3]/w:sdt/w:sdtPr/w:dataBinding", "xpath","/ns0:properties[1]/documentManagement[1]/ns2:Responsible_x0020_Officer_x0020_Title[1]");
}


DECLARE_OOXMLEXPORT_TEST(testFDO78590, "FDO78590.docx")
{
    xmlDocPtr pXmlDoc = parseExport("word/document.xml");

    if (!pXmlDoc)
        return;

    // This is to ensure that the fld starts and ends inside a hyperlink...
    assertXPath ( pXmlDoc, "/w:document/w:body/w:p[1]/w:pPr/w:framePr", "w", "9851" );
    assertXPath ( pXmlDoc, "/w:document/w:body/w:p[1]/w:pPr/w:framePr", "h", "1669" );
}

#endif

CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/unocore/unoframe.cxx b/sw/source/core/unocore/unoframe.cxx
index e02024c..55c50d4 100644
--- a/sw/source/core/unocore/unoframe.cxx
+++ b/sw/source/core/unocore/unoframe.cxx
@@ -3052,6 +3052,13 @@ void SwXFrame::attachToRange(const uno::Reference< text::XTextRange > & xTextRan
        {
            setPropertyValue(UNO_NAME_DESCRIPTION, *pDescription);
        }

        // For grabbag
        const uno::Any* pFrameIntropgrabbagItem;
        if( pProps->GetProperty(RES_FRMATR_GRABBAG, 0, pFrameIntropgrabbagItem) )
        {
            setPropertyValue(UNO_NAME_FRAME_INTEROP_GRAB_BAG, *pFrameIntropgrabbagItem);
        }
    }
    else
        throw lang::IllegalArgumentException();
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 1ca7ee3..e7144c7 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -305,6 +305,96 @@ static void lcl_deleteAndResetTheLists( ::sax_fastparser::FastAttributeList* &pS
    }
}

void DocxAttributeOutput::PopulateFrameProperties(const SwFrmFmt* pFrmFmt, const Size& rSize)
{

    sax_fastparser::FastAttributeList* attrList = m_pSerializer->createAttrList();

    awt::Point aPos(pFrmFmt->GetHoriOrient().GetPos(), pFrmFmt->GetVertOrient().GetPos());

    attrList->add( FSNS( XML_w, XML_w), OString::number(rSize.Width()));
    attrList->add( FSNS( XML_w, XML_h), OString::number(rSize.Height()));

    attrList->add( FSNS( XML_w, XML_x), OString::number(aPos.X));
    attrList->add( FSNS( XML_w, XML_y), OString::number(aPos.Y));

    const char* relativeFromH;
    const char* relativeFromV;
    switch (pFrmFmt->GetVertOrient().GetRelationOrient())
    {
        case text::RelOrientation::PAGE_PRINT_AREA:
            relativeFromV = "margin";
            break;
        case text::RelOrientation::PAGE_FRAME:
            relativeFromV = "page";
            break;
        case text::RelOrientation::FRAME:
            relativeFromV = "paragraph";
            break;
        case text::RelOrientation::TEXT_LINE:
        default:
            relativeFromV = "line";
            break;
    }

    switch (pFrmFmt->GetHoriOrient().GetRelationOrient())
    {
        case text::RelOrientation::PAGE_PRINT_AREA:
            relativeFromH = "margin";
            break;
        case text::RelOrientation::PAGE_FRAME:
            relativeFromH = "page";
            break;
        case text::RelOrientation::CHAR:
            relativeFromH = "character";
            break;
        case text::RelOrientation::PAGE_RIGHT:
            relativeFromH = "page";
            break;
        case text::RelOrientation::FRAME:
        default:
            relativeFromH = "column";
            break;
    }
    attrList->add( FSNS( XML_w, XML_vAnchor), relativeFromV);
    attrList->add( FSNS( XML_w, XML_hAnchor), relativeFromH);
    attrList->add( FSNS( XML_w, XML_wrap), "notBeside");
    attrList->add( FSNS( XML_w, XML_hRule), "exact");

    sax_fastparser::XFastAttributeListRef xAttrList(attrList);
    m_pSerializer->singleElementNS( XML_w, XML_framePr, xAttrList );
}

bool DocxAttributeOutput::TextBoxIsFramePr(const SwFrmFmt& rFrmFmt)
{
    uno::Reference< drawing::XShape > xShape;
    const SdrObject* pSdrObj = rFrmFmt.FindRealSdrObject();
    if (pSdrObj)
        xShape = uno::Reference< drawing::XShape >(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
    uno::Reference< beans::XPropertySet > xPropertySet(xShape, uno::UNO_QUERY);
    uno::Reference< beans::XPropertySetInfo > xPropSetInfo;
    if (xPropertySet.is())
        xPropSetInfo = xPropertySet->getPropertySetInfo();
    uno::Any aFrameProperties ;
    bool bFrameProperties = 0;
    if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
    {
        uno::Sequence< beans::PropertyValue > propList;
        xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
        for (sal_Int32 nProp=0; nProp < propList.getLength(); ++nProp)
        {
            OUString propName = propList[nProp].Name;
            if (propName == "ParaFrameProperties")
            {
                aFrameProperties = propList[nProp].Value ;
                break;
            }
        }
    }
    aFrameProperties >>= bFrameProperties;
    return bFrameProperties;
}

void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner )
{
    // write the paragraph properties + the run, already in the correct order
@@ -323,59 +413,71 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT
            sw::Frame aFrame = m_aFramesOfParagraph[nIndex];
            m_pSerializer->startElementNS( XML_w, XML_r, FSEND );

            m_pSerializer->startElementNS(XML_mc, XML_AlternateContent, FSEND);
            m_pSerializer->startElementNS(XML_mc, XML_Choice,
                    XML_Requires, "wps",
                    FSEND);
            /**
               This is to avoid AltenateContent within another AlternateContent.
               So when Choice is Open, only write the DML Drawing instead of both DML
               and VML Drawing in another AlternateContent.
            **/
            SetAlternateContentChoiceOpen( true );
            /** FDO#71834 :
               We should probably be renaming the function
               switchHeaderFooter to something like SaveRetrieveTableReference.
               Save the table reference attributes before calling WriteDMLTextFrame,
               otherwise the StartParagraph function will use the previous existing
               table reference attributes since the variable is being shared.
            */
            switchHeaderFooter(true,1);
            /** Save the table info's before writing the shape
                as there might be a new table that might get
                spawned from within the VML & DML block and alter
                the contents.
            */
            ww8::WW8TableInfo::Pointer_t pOldTableInfo = m_rExport.mpTableInfo;
            //Reset the table infos after saving.
            m_rExport.mpTableInfo = ww8::WW8TableInfo::Pointer_t(new ww8::WW8TableInfo());
            const SwFrmFmt& rFrmFmt = aFrame.GetFrmFmt();

            if (!TextBoxIsFramePr(rFrmFmt) || m_bWritingHeaderFooter)
            {
                m_pSerializer->startElementNS(XML_mc, XML_AlternateContent, FSEND);
                m_pSerializer->startElementNS(XML_mc, XML_Choice,
                        XML_Requires, "wps",
                        FSEND);
                /**
                    This is to avoid AltenateContent within another AlternateContent.
                       So when Choice is Open, only write the DML Drawing instead of both DML
                       and VML Drawing in another AlternateContent.
                 **/
                SetAlternateContentChoiceOpen( true );
                /** FDO#71834 :
                       We should probably be renaming the function
                       switchHeaderFooter to something like SaveRetrieveTableReference.
                       Save the table reference attributes before calling WriteDMLTextFrame,
                       otherwise the StartParagraph function will use the previous existing
                       table reference attributes since the variable is being shared.
                */
                switchHeaderFooter(true,1);
                /** Save the table info's before writing the shape
                        as there might be a new table that might get
                        spawned from within the VML & DML block and alter
                        the contents.
                */
                ww8::WW8TableInfo::Pointer_t pOldTableInfo = m_rExport.mpTableInfo;
                //Reset the table infos after saving.
                m_rExport.mpTableInfo = ww8::WW8TableInfo::Pointer_t(new ww8::WW8TableInfo());

            m_rExport.SdrExporter().writeDMLTextFrame(&aFrame, m_anchorId++);
            m_pSerializer->endElementNS(XML_mc, XML_Choice);
            SetAlternateContentChoiceOpen( false );

            // Reset table infos, otherwise the depth of the cells will be incorrect,
            // in case the text frame had table(s) and we try to export the
            // same table second time.
            m_rExport.mpTableInfo = ww8::WW8TableInfo::Pointer_t(new ww8::WW8TableInfo());
            //reset the tableReference.
            switchHeaderFooter(false,0);
                // Reset table infos, otherwise the depth of the cells will be incorrect,
                // in case the text frame had table(s) and we try to export the
                // same table second time.
                m_rExport.mpTableInfo = ww8::WW8TableInfo::Pointer_t(new ww8::WW8TableInfo());
                //reset the tableReference.
                switchHeaderFooter(false,0);

            m_pSerializer->startElementNS(XML_mc, XML_Fallback, FSEND);
            m_rExport.SdrExporter().writeVMLTextFrame(&aFrame);
            /* FDO#71834 :Restore the data here after having written the Shape
               for further processing.
            */
            switchHeaderFooter(false,-1);
            m_rExport.mpTableInfo = pOldTableInfo;
                m_pSerializer->startElementNS(XML_mc, XML_Fallback, FSEND);
                m_rExport.SdrExporter().writeVMLTextFrame(&aFrame);
                /* FDO#71834 :Restore the data here after having written the Shape
                   for further processing.
                */
                switchHeaderFooter(false,-1);
                m_rExport.mpTableInfo = pOldTableInfo;

            m_pSerializer->endElementNS(XML_mc, XML_Fallback);
            m_pSerializer->endElementNS(XML_mc, XML_AlternateContent);

                m_pSerializer->endElementNS(XML_mc, XML_Fallback);
                m_pSerializer->endElementNS(XML_mc, XML_AlternateContent);
            }
            else
            {
                ww8::WW8TableInfo::Pointer_t pOldTableInfo = m_rExport.mpTableInfo;
                //Reset the table infos after saving.
                m_rExport.mpTableInfo = ww8::WW8TableInfo::Pointer_t(new ww8::WW8TableInfo());
                m_rExport.SdrExporter().writeOnlyTextOfFrame(&aFrame);
                m_rExport.mpTableInfo = pOldTableInfo;
            }
            m_pSerializer->endElementNS( XML_w, XML_r );
            m_bParagraphFrameOpen = false;
        }
        if (m_postponedCustomShape)
        if (!m_postponedCustomShape->empty())
        {
            m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
            WritePostponedCustomShape();
@@ -716,6 +818,20 @@ void DocxAttributeOutput::EndParagraphProperties( const SfxItemSet* pParagraphMa
        m_pSerializer->endElementNS( XML_w, XML_rPr );
    }

    if (!m_bWritingHeaderFooter)
    {
        //Check whether we have Frame for paragraph
        for (size_t nIndex = 0; nIndex < m_aFramesOfParagraph.size(); ++nIndex)
        {
            sw::Frame aFrame = m_aFramesOfParagraph[nIndex];
            const SwFrmFmt& rFrmFmt = aFrame.GetFrmFmt();
            if (TextBoxIsFramePr(rFrmFmt))
            {
                const Size aSize = aFrame.GetSize();
                PopulateFrameProperties(&rFrmFmt, aSize);
            }
        }
    }
    m_pSerializer->endElementNS( XML_w, XML_pPr );

    if ( m_nColBreakStatus == COLBRK_WRITE )
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index 324b31a..6465a71 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -416,7 +416,8 @@ private:
    void EndTableCell( );
    void EndTableRow( );
    void EndTable();

    void PopulateFrameProperties(const SwFrmFmt* pFrmFmt, const Size& rSize);
    bool TextBoxIsFramePr(const SwFrmFmt& rFrmFmt);
    /// End cell, row, and even the entire table if necessary.
    void FinishTableRowCell( ww8::WW8TableNodeInfoInner::Pointer_t pInner, bool bForceEmptyParagraph = false );

diff --git a/sw/source/filter/ww8/docxsdrexport.cxx b/sw/source/filter/ww8/docxsdrexport.cxx
index 435dc2b..42e9f9c 100644
--- a/sw/source/filter/ww8/docxsdrexport.cxx
+++ b/sw/source/filter/ww8/docxsdrexport.cxx
@@ -1197,6 +1197,26 @@ void DocxSdrExport::writeDiagram(const SdrObject* sdrObject, const SwFrmFmt& rFr
    }
}

void DocxSdrExport::writeOnlyTextOfFrame(sw::Frame* pParentFrame)
{
    const SwFrmFmt& rFrmFmt = pParentFrame->GetFrmFmt();
    const SwNodeIndex* pNodeIndex = rFrmFmt.GetCntnt().GetCntntIdx();
    sax_fastparser::FSHelperPtr pFS = m_pImpl->m_pSerializer;

    sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex()+1                  : 0;
    sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0;

    //Save data here and restore when out of scope
    ExportDataSaveRestore aDataGuard(m_pImpl->m_rExport, nStt, nEnd, pParentFrame);

    m_pImpl->m_pBodyPrAttrList = pFS->createAttrList();
    m_pImpl->m_bFrameBtLr = checkFrameBtlr(m_pImpl->m_rExport.pDoc->GetNodes()[nStt], 0);
    m_pImpl->m_bFlyFrameGraphic = true;
    m_pImpl->m_rExport.WriteText();
    m_pImpl->m_bFlyFrameGraphic = false;
    m_pImpl->m_bFrameBtLr = false;
}

void DocxSdrExport::writeDMLTextFrame(sw::Frame* pParentFrame, int nAnchorId, bool bTextBoxOnly)
{
    sax_fastparser::FSHelperPtr pFS = m_pImpl->m_pSerializer;
diff --git a/sw/source/filter/ww8/docxsdrexport.hxx b/sw/source/filter/ww8/docxsdrexport.hxx
index 09f2185..5c98a424 100644
--- a/sw/source/filter/ww8/docxsdrexport.hxx
+++ b/sw/source/filter/ww8/docxsdrexport.hxx
@@ -99,6 +99,8 @@ public:
    bool checkFrameBtlr(SwNode* pStartNode, sax_fastparser::FastAttributeList* pTextboxAttrList = 0);
    /// Is this a standalone TextFrame, or used as a TextBox of a shape?
    bool isTextBox(const SwFrmFmt& rFrmFmt);
    /// Writes text from Textbox for <w:framePr>
    void writeOnlyTextOfFrame(sw::Frame* pParentFrame);
};

#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXSDREXPORT_HXX
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index b4b4dde..cbf7ea3 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -788,7 +788,7 @@ void DomainMapper_Impl::CheckUnregisteredFrameConversion( )
            StyleSheetEntryPtr pParaStyle =
                GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(rAppendContext.pLastParagraphProperties->GetParaStyleName());

            uno::Sequence< beans::PropertyValue > aFrameProperties(pParaStyle ? 16: 9);
            uno::Sequence< beans::PropertyValue > aFrameProperties(pParaStyle ? 17: 9);

            if ( pParaStyle.get( ) )
            {
@@ -809,6 +809,7 @@ void DomainMapper_Impl::CheckUnregisteredFrameConversion( )
                pFrameProperties[13].Name = rPropNameSupplier.GetName(PROP_TOP_MARGIN);
                pFrameProperties[14].Name = rPropNameSupplier.GetName(PROP_BOTTOM_MARGIN);
                pFrameProperties[15].Name = rPropNameSupplier.GetName(PROP_BACK_COLOR_TRANSPARENCY);
                pFrameProperties[16].Name = "FrameInteropGrabBag";

                const ParagraphProperties* pStyleProperties = dynamic_cast<const ParagraphProperties*>( pParaStyle->pProperties.get() );
                if (!pStyleProperties)
@@ -893,6 +894,13 @@ void DomainMapper_Impl::CheckUnregisteredFrameConversion( )
                // will be ignored.
                pFrameProperties[15].Value <<= sal_Int32(100);

                beans::PropertyValue aRet;
                uno::Sequence<beans::PropertyValue> aGrabBag(1);
                aRet.Name = "ParaFrameProperties";
                aRet.Value <<= uno::Any(rAppendContext.pLastParagraphProperties->IsFrameMode());
                aGrabBag[0] = aRet;
                pFrameProperties[16].Value <<= aGrabBag;

                lcl_MoveBorderPropertiesToFrame(aFrameProperties,
                    rAppendContext.pLastParagraphProperties->GetStartingRange(),
                    rAppendContext.pLastParagraphProperties->GetEndingRange());