diff options
author | Miklos Vajna <vmiklos@collabora.co.uk> | 2014-07-31 09:36:32 +0200 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.co.uk> | 2014-07-31 09:55:04 +0200 |
commit | 4a39475e355b256dc0a922d21b21e695aaa5577b (patch) | |
tree | 9bb8c1e1e4f941b870258e934406f0e52733cf86 | |
parent | 75dd06b2d70f796bcb0fc3d2b736e9801cea2379 (diff) |
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
-rw-r--r-- | sw/qa/extras/ooxmlexport/data/sdt-2-para.docx | bin | 0 -> 15487 bytes | |||
-rw-r--r-- | sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx | 28 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.cxx | 55 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.hxx | 8 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxexport.cxx | 7 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxexport.hxx | 3 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxsdrexport.cxx | 23 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxsdrexport.hxx | 5 |
8 files changed, 119 insertions, 10 deletions
diff --git a/sw/qa/extras/ooxmlexport/data/sdt-2-para.docx b/sw/qa/extras/ooxmlexport/data/sdt-2-para.docx Binary files differnew file mode 100644 index 000000000000..b6d6565d6550 --- /dev/null +++ b/sw/qa/extras/ooxmlexport/data/sdt-2-para.docx diff --git a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx index 9b4b380cc271..76cb59206635 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 0ff219ecb189..9078f8be767e 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 ff4ed70bb909..6ba3b484d6ac 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 35b557228ab8..932ec15c10a8 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 9e2b6b7d1ecf..3cda882cad62 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 942944395efc..181c12b49b71 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 5c98a424dbb0..8ee6cd7756cd 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(); |