[Libreoffice-commits] core.git: sw/qa sw/source

Miklos Vajna vmiklos at collabora.co.uk
Thu Jul 31 00:55:28 PDT 2014


 sw/qa/extras/ooxmlexport/data/sdt-2-para.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx |   28 +++++++++----
 sw/source/filter/ww8/docxattributeoutput.cxx  |   55 +++++++++++++++++++++++++-
 sw/source/filter/ww8/docxattributeoutput.hxx  |    8 +++
 sw/source/filter/ww8/docxexport.cxx           |    7 +++
 sw/source/filter/ww8/docxexport.hxx           |    3 +
 sw/source/filter/ww8/docxsdrexport.cxx        |   23 ++++++++++
 sw/source/filter/ww8/docxsdrexport.hxx        |    5 ++
 8 files changed, 119 insertions(+), 10 deletions(-)

New commits:
commit 4a39475e355b256dc0a922d21b21e695aaa5577b
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Thu Jul 31 09:36:32 2014 +0200

    DOCX export: handle exact end of paragraph w:sdt tags
    
    Previously every paragraph SDT was closed immediately after the
    paragraph end. This commit adds support for having multiple paragraphs
    inside an SDT.
    
    A few testcases implicitly tested that such SDT's are lost on save,
    adjust the relevant XPath expressions now that this works.
    
    Change-Id: I07802b3e067600b087b7e0f9b2e7b3ba17c3379a

diff --git a/sw/qa/extras/ooxmlexport/data/sdt-2-para.docx b/sw/qa/extras/ooxmlexport/data/sdt-2-para.docx
new file mode 100644
index 0000000..b6d6565
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/sdt-2-para.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
index 9b4b380..76cb592 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
@@ -126,7 +126,7 @@ DECLARE_OOXMLEXPORT_TEST(testHyperlineIsEnd, "hyperlink.docx")
     // If  document.xml miss any ending tag then parseExport() returns NULL which fail the test case.
     CPPUNIT_ASSERT(pXmlDoc) ;
     // Check hyperlink is properly open.
-    assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:hyperlink",1);
+    assertXPath(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p/w:hyperlink",1);
 }
 
 DECLARE_OOXMLEXPORT_TEST(testFdo69649, "fdo69649.docx")
@@ -136,7 +136,7 @@ DECLARE_OOXMLEXPORT_TEST(testFdo69649, "fdo69649.docx")
     if (!pXmlDoc)
         return;
 
-    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[21]/w:hyperlink/w:r[5]/w:t", "15");
+    assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p[21]/w:hyperlink/w:r[5]/w:t", "15");
 }
 
 DECLARE_OOXMLEXPORT_TEST(testFieldFlagO,"TOC_field_f.docx")
@@ -183,7 +183,7 @@ DECLARE_OOXMLEXPORT_TEST(testPreserveWfieldTOC, "PreserveWfieldTOC.docx")
     if (!pXmlDoc)
         return;
 
-    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p/w:r[2]/w:instrText", " TOC \\z \\w \\f \\o \"1-3\" \\h");
+    assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p/w:r[2]/w:instrText", " TOC \\z \\w \\f \\o \"1-3\" \\h");
 }
 
 DECLARE_OOXMLEXPORT_TEST(testFieldFlagB,"TOC_field_b.docx")
@@ -204,7 +204,7 @@ DECLARE_OOXMLEXPORT_TEST(testPreserveXfieldTOC, "PreserveXfieldTOC.docx")
     if (!pXmlDoc)
         return;
 
-    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p/w:r[2]/w:instrText", " TOC \\x \\f \\o \"1-3\" \\h");
+    assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p/w:r[2]/w:instrText", " TOC \\x \\f \\o \"1-3\" \\h");
 }
 
 DECLARE_OOXMLEXPORT_TEST(testFDO77715,"FDO77715.docx")
@@ -225,7 +225,7 @@ DECLARE_OOXMLEXPORT_TEST(testTOCFlag_u,"testTOCFlag_u.docx")
 
     // FIXME "p[2]" will have to be "p[1]", once the TOC import code is fixed
     // not to insert an empty paragraph before TOC.
-    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p/w:r[2]/w:instrText", " TOC \\z \\o \"1-9\" \\u \\h");
+    assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p/w:r[2]/w:instrText", " TOC \\z \\o \"1-9\" \\u \\h");
 }
 
 DECLARE_OOXMLEXPORT_TEST(testfdo73596_RunInStyle,"fdo73596_RunInStyle.docx")
@@ -337,7 +337,7 @@ DECLARE_OOXMLEXPORT_TEST(testPageref, "testPageref.docx")
     if (!pXmlDoc)
         return;
 
-    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[1]/w:hyperlink/w:r[3]/w:instrText", "PAGEREF _Toc355095261 \\h");
+    assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p[2]/w:hyperlink/w:r[3]/w:instrText", "PAGEREF _Toc355095261 \\h");
 }
 
 DECLARE_OOXMLEXPORT_TEST(testAlphabeticalIndex_AutoColumn,"alphabeticalIndex_AutoColumn.docx")
@@ -375,7 +375,7 @@ DECLARE_OOXMLEXPORT_TEST(testBibliography,"FDO75133.docx")
     if (!pXmlDoc)
         return;
 
-    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p/w:r[2]/w:instrText", " BIBLIOGRAPHY ");
+    assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p/w:r[2]/w:instrText", " BIBLIOGRAPHY ");
     assertXPath(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtPr/w:docPartObj/w:docPartGallery", "val", "Bibliographies");
     assertXPath(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtPr/w:docPartObj/w:docPartUnique", 1);
 }
@@ -453,7 +453,7 @@ DECLARE_OOXMLEXPORT_TEST(testFDO78654 , "fdo78654.docx")
         return;
     // In case of two "Hyperlink" tags in one paragraph and one of them
     // contains "PAGEREF" field then field end tag was missing from hyperlink.
-    assertXPath ( pXmlDoc, "/w:document/w:body/w:p[2]/w:hyperlink[3]/w:r[5]/w:fldChar", "fldCharType", "end" );
+    assertXPath ( pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p[2]/w:hyperlink[3]/w:r[5]/w:fldChar", "fldCharType", "end" );
 }
 
 
@@ -520,6 +520,18 @@ DECLARE_OOXMLEXPORT_TEST(testParagraphSdt, "paragraph-sdt.docx")
     }
 }
 
+DECLARE_OOXMLEXPORT_TEST(testSdt2Run, "sdt-2-para.docx")
+{
+    if (xmlDocPtr pXmlDoc = parseExport())
+    {
+        // The problem was that <w:sdt> was closed after "first", not after "second", so the second assert failed.
+        assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p[1]/w:r/w:t", "first");
+        assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p[2]/w:r/w:t", "second");
+        // Make sure the third paragraph is still outside <w:sdt>.
+        assertXPathContent(pXmlDoc, "/w:document/w:body/w:p/w:r/w:t", "third");
+    }
+}
+
 #endif
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 0ff219e..9078f8b 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -274,6 +274,29 @@ void DocxAttributeOutput::StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pText
         }
     }
 
+    // Look up the "sdt end before this paragraph" property early, when it
+    // would normally arrive, it would be too late (would be after the
+    // paragraph start has been written).
+    bool bEndParaSdt = false;
+    SwTxtNode* pTxtNode = m_rExport.pCurPam->GetNode().GetTxtNode();
+    if (pTxtNode && pTxtNode->GetpSwAttrSet())
+    {
+        const SfxItemSet* pSet = pTxtNode->GetpSwAttrSet();
+        if (const SfxPoolItem* pItem = pSet->GetItem(RES_PARATR_GRABBAG))
+        {
+            const SfxGrabBagItem& rParaGrabBag = static_cast<const SfxGrabBagItem&>(*pItem);
+            const std::map<OUString, com::sun::star::uno::Any>& rMap = rParaGrabBag.GetGrabBag();
+            bEndParaSdt = m_bStartedParaSdt && rMap.find("ParaSdtEndBefore") != rMap.end();
+        }
+    }
+    if (bEndParaSdt)
+    {
+        // This is the common case: "close sdt before the current paragraph" was requrested by the next paragraph.
+        EndSdtBlock();
+        bEndParaSdt = false;
+        m_bStartedParaSdt = false;
+    }
+
     // this mark is used to be able to enclose the paragraph inside a sdr tag.
     // We will only know if we have to do that later.
     m_pSerializer->mark();
@@ -518,7 +541,8 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT
     }
 
     m_pSerializer->endElementNS( XML_w, XML_p );
-    if( !m_bAnchorLinkedToNode )
+    // on export sdt blocks are never nested ATM
+    if( !m_bAnchorLinkedToNode && !m_bStartedParaSdt )
         WriteSdtBlock( m_nParagraphSdtPrToken, m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrDataBindingAttrs, m_aParagraphSdtPrAlias, /*bPara=*/true );
     else
     {
@@ -618,7 +642,13 @@ void DocxAttributeOutput::WriteSdtBlock( sal_Int32& nSdtPrToken,
 
         // write the ending tags after the paragraph
         if (bPara)
-            EndSdtBlock();
+        {
+            m_bStartedParaSdt = true;
+            if (m_tableReference->m_bTableCellOpen)
+                m_tableReference->m_bTableCellParaSdtOpen = true;
+            if (m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen())
+                m_rExport.SdrExporter().setParagraphSdtOpen(true);
+        }
         else
             // Support multiple runs inside a run-evel SDT: don't close the SDT block yet.
             m_bStartedCharSdt = true;
@@ -2902,10 +2932,14 @@ void DocxAttributeOutput::StartTableCell( ww8::WW8TableNodeInfoInner::Pointer_t
 
 void DocxAttributeOutput::EndTableCell( )
 {
+    if (m_tableReference->m_bTableCellParaSdtOpen)
+        EndParaSdtBlock();
+
     m_pSerializer->endElementNS( XML_w, XML_tc );
 
     m_bBtLr = false;
     m_tableReference->m_bTableCellOpen = false;
+    m_tableReference->m_bTableCellParaSdtOpen = false;
 }
 
 void DocxAttributeOutput::TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
@@ -4583,6 +4617,7 @@ void DocxAttributeOutput::WritePostponedCustomShape()
     if(m_postponedCustomShape == NULL)
         return;
 
+    bool bStartedParaSdt = m_bStartedParaSdt;
     for( std::list< PostponedDrawing >::iterator it = m_postponedCustomShape->begin();
          it != m_postponedCustomShape->end();
          ++it )
@@ -4592,6 +4627,7 @@ void DocxAttributeOutput::WritePostponedCustomShape()
         else
             m_rExport.SdrExporter().writeDMLAndVMLDrawing(it->object, *(it->frame), *(it->point), m_anchorId++);
     }
+    m_bStartedParaSdt = bStartedParaSdt;
     delete m_postponedCustomShape;
     m_postponedCustomShape = NULL;
 }
@@ -4607,6 +4643,7 @@ void DocxAttributeOutput::WritePostponedDMLDrawing()
     std::list<PostponedOLE>* postponedOLE = m_postponedOLE;
     m_postponedOLE = 0;
 
+    bool bStartedParaSdt = m_bStartedParaSdt;
     for( std::list< PostponedDrawing >::iterator it = postponedDMLDrawing->begin();
          it != postponedDMLDrawing->end();
          ++it )
@@ -4617,6 +4654,7 @@ void DocxAttributeOutput::WritePostponedDMLDrawing()
         else
             m_rExport.SdrExporter().writeDMLAndVMLDrawing(it->object, *(it->frame), *(it->point), m_anchorId++);
     }
+    m_bStartedParaSdt = bStartedParaSdt;
 
     delete postponedDMLDrawing;
     m_postponedOLE = postponedOLE;
@@ -4672,6 +4710,7 @@ void DocxAttributeOutput::OutputFlyFrame_Impl( const sw::Frame &rFrame, const Po
                         OUString sShapeType = xShape->getShapeType();
                         if ( m_postponedDMLDrawing == NULL )
                         {
+                            bool bStartedParaSdt = m_bStartedParaSdt;
                             if ( IsAlternateContentChoiceOpen() )
                             {
                                 // Do not write w:drawing inside w:drawing. Instead Postpone the Inner Drawing.
@@ -4682,6 +4721,7 @@ void DocxAttributeOutput::OutputFlyFrame_Impl( const sw::Frame &rFrame, const Po
                             }
                             else
                                 m_rExport.SdrExporter().writeDMLAndVMLDrawing( pSdrObj, rFrame.GetFrmFmt(), rNdTopLeft, m_anchorId++);
+                            m_bStartedParaSdt = bStartedParaSdt;
 
                             m_bPostponedProcessingFly = false ;
                         }
@@ -5086,6 +5126,16 @@ void DocxAttributeOutput::SectionBreak( sal_uInt8 nC, const WW8_SepInfo* pSectio
     }
 }
 
+void DocxAttributeOutput::EndParaSdtBlock()
+{
+    if (m_bStartedParaSdt)
+    {
+        // Paragraph-level SDT still open? Close it now.
+        EndSdtBlock();
+        m_bStartedParaSdt = false;
+    }
+}
+
 void DocxAttributeOutput::StartSection()
 {
     m_pSerializer->startElementNS( XML_w, XML_sectPr, FSEND );
@@ -7921,6 +7971,7 @@ DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, FSHelperPtr pSeri
       m_pHyperlinkAttrList( NULL ),
       m_bEndCharSdt(false),
       m_bStartedCharSdt(false),
+      m_bStartedParaSdt(false),
       m_pColorAttrList( NULL ),
       m_pBackgroundAttrList( NULL ),
       m_endPageRef( false ),
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index ff4ed70..6ba3b48 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -113,6 +113,9 @@ struct TableReference
     /// Remember if we are in an open cell, or not.
     bool m_bTableCellOpen;
 
+    /// If paragraph sdt got opened in this table cell.
+    bool m_bTableCellParaSdtOpen;
+
     /// Remember the current table depth.
     sal_uInt32 m_nTableDepth;
 
@@ -120,6 +123,7 @@ struct TableReference
 
     TableReference()
         : m_bTableCellOpen(false),
+        m_bTableCellParaSdtOpen(false),
         m_nTableDepth(0)
     {
     }
@@ -364,6 +368,8 @@ public:
     void WriteBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds );
     void WriteAnnotationMarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds );
     void ClearRelIdCache();
+    /// End possibly opened paragraph sdt block.
+    void EndParaSdtBlock();
 
 private:
     /// Initialize the structures where we are going to collect some of the paragraph properties.
@@ -717,6 +723,8 @@ private:
     bool m_bEndCharSdt;
     /// If an SDT around runs is currently open.
     bool m_bStartedCharSdt;
+    /// If an SDT around paragraphs is currently open.
+    bool m_bStartedParaSdt;
     /// Attributes of the run color
     ::sax_fastparser::FastAttributeList *m_pColorAttrList;
     /// Attributes of the paragraph background
diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx
index 35b5572..932ec15 100644
--- a/sw/source/filter/ww8/docxexport.cxx
+++ b/sw/source/filter/ww8/docxexport.cxx
@@ -86,6 +86,11 @@ AttributeOutputBase& DocxExport::AttrOutput() const
     return *m_pAttrOutput;
 }
 
+DocxAttributeOutput& DocxExport::DocxAttrOutput() const
+{
+    return *m_pAttrOutput;
+}
+
 MSWordSections& DocxExport::Sections() const
 {
     return *m_pSections;
@@ -711,6 +716,7 @@ void DocxExport::WriteHeaderFooter( const SwFmt& rFmt, bool bHeader, const char*
     m_pAttrOutput->switchHeaderFooter(true, m_nHeadersFootersInSection++);
     // do the work
     WriteHeaderFooterText( rFmt, bHeader );
+    m_pAttrOutput->EndParaSdtBlock();
 
     //When the stream changes the cache which is maintained for the graphics in case of alternate content is not cleared.
     //So clearing the alternate content graphic cache.
@@ -1309,6 +1315,7 @@ void DocxExport::WriteMainText()
     WriteText();
 
     // the last section info
+    m_pAttrOutput->EndParaSdtBlock();
     const WW8_SepInfo *pSectionInfo = m_pSections? m_pSections->CurrentSectionInfo(): NULL;
     if ( pSectionInfo )
         SectionProperties( *pSectionInfo );
diff --git a/sw/source/filter/ww8/docxexport.hxx b/sw/source/filter/ww8/docxexport.hxx
index 9e2b6b7..3cda882 100644
--- a/sw/source/filter/ww8/docxexport.hxx
+++ b/sw/source/filter/ww8/docxexport.hxx
@@ -110,6 +110,9 @@ public:
     /// Access to the attribute output class.
     virtual AttributeOutputBase& AttrOutput() const SAL_OVERRIDE;
 
+    /// Access to the derived attribute output class.
+    virtual DocxAttributeOutput& DocxAttrOutput() const;
+
     /// Access to the sections/headers/footres.
     virtual MSWordSections& Sections() const SAL_OVERRIDE;
 
diff --git a/sw/source/filter/ww8/docxsdrexport.cxx b/sw/source/filter/ww8/docxsdrexport.cxx
index 9429443..181c12b 100644
--- a/sw/source/filter/ww8/docxsdrexport.cxx
+++ b/sw/source/filter/ww8/docxsdrexport.cxx
@@ -42,6 +42,7 @@
 #include <drawdoc.hxx>
 #include <docxsdrexport.hxx>
 #include <docxexport.hxx>
+#include <docxattributeoutput.hxx>
 #include <docxexportfilter.hxx>
 #include <writerhelper.hxx>
 #include <comphelper/seqstream.hxx>
@@ -153,6 +154,7 @@ struct DocxSdrExport::Impl
     OStringBuffer m_aTextFrameStyle;
     bool m_bFrameBtLr;
     bool m_bDrawingOpen;
+    bool m_bParagraphSdtOpen;
     bool m_bParagraphHasDrawing; ///Flag for checking drawing in a paragraph.
     bool m_bFlyFrameGraphic;
     sax_fastparser::FastAttributeList* m_pFlyFillAttrList;
@@ -179,6 +181,7 @@ struct DocxSdrExport::Impl
           m_pTextboxAttrList(0),
           m_bFrameBtLr(false),
           m_bDrawingOpen(false),
+          m_bParagraphSdtOpen(false),
           m_bParagraphHasDrawing(false),
           m_bFlyFrameGraphic(false),
           m_pFlyFillAttrList(0),
@@ -264,6 +267,16 @@ bool DocxSdrExport::IsDrawingOpen()
     return m_pImpl->m_bDrawingOpen;
 }
 
+bool DocxSdrExport::isParagraphSdtOpen()
+{
+    return m_pImpl->m_bParagraphSdtOpen;
+}
+
+void DocxSdrExport::setParagraphSdtOpen(bool bParagraphSdtOpen)
+{
+    m_pImpl->m_bParagraphSdtOpen = bParagraphSdtOpen;
+}
+
 bool DocxSdrExport::IsDMLAndVMLDrawingOpen()
 {
     return m_pImpl->m_bDMLAndVMLDrawingOpen;
@@ -1402,6 +1415,11 @@ void DocxSdrExport::writeDMLTextFrame(sw::Frame* pParentFrame, int nAnchorId, bo
         m_pImpl->m_bFrameBtLr = checkFrameBtlr(m_pImpl->m_rExport.pDoc->GetNodes()[nStt], 0);
         m_pImpl->m_bFlyFrameGraphic = true;
         m_pImpl->m_rExport.WriteText();
+        if (m_pImpl->m_bParagraphSdtOpen)
+        {
+            m_pImpl->m_rExport.DocxAttrOutput().EndParaSdtBlock();
+            m_pImpl->m_bParagraphSdtOpen = false;
+        }
         m_pImpl->m_bFlyFrameGraphic = false;
         m_pImpl->m_bFrameBtLr = false;
 
@@ -1515,6 +1533,11 @@ void DocxSdrExport::writeVMLTextFrame(sw::Frame* pParentFrame, bool bTextBoxOnly
     pFS->startElementNS(XML_w, XML_txbxContent, FSEND);
     m_pImpl->m_bFlyFrameGraphic = true;
     m_pImpl->m_rExport.WriteText();
+    if (m_pImpl->m_bParagraphSdtOpen)
+    {
+        m_pImpl->m_rExport.DocxAttrOutput().EndParaSdtBlock();
+        m_pImpl->m_bParagraphSdtOpen = false;
+    }
     m_pImpl->m_bFlyFrameGraphic = false;
     pFS->endElementNS(XML_w, XML_txbxContent);
     if (!bTextBoxOnly)
diff --git a/sw/source/filter/ww8/docxsdrexport.hxx b/sw/source/filter/ww8/docxsdrexport.hxx
index 5c98a42..8ee6cd7 100644
--- a/sw/source/filter/ww8/docxsdrexport.hxx
+++ b/sw/source/filter/ww8/docxsdrexport.hxx
@@ -64,6 +64,11 @@ public:
     /// Same, as DocxAttributeOutput::m_bBtLr, but for textframe rotation.
     bool getFrameBtLr();
 
+    /// Is paragraph sdt open in the current drawing?
+    bool isParagraphSdtOpen();
+    /// Set if paragraph sdt open in the current drawing.
+    void setParagraphSdtOpen(bool bParagraphSdtOpen);
+
     bool IsDrawingOpen();
     bool IsDMLAndVMLDrawingOpen();
     bool IsParagraphHasDrawing();


More information about the Libreoffice-commits mailing list