summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.co.uk>2014-07-31 09:36:32 +0200
committerMiklos Vajna <vmiklos@collabora.co.uk>2014-07-31 09:55:04 +0200
commit4a39475e355b256dc0a922d21b21e695aaa5577b (patch)
tree9bb8c1e1e4f941b870258e934406f0e52733cf86
parent75dd06b2d70f796bcb0fc3d2b736e9801cea2379 (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.docxbin0 -> 15487 bytes
-rw-r--r--sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx28
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.cxx55
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.hxx8
-rw-r--r--sw/source/filter/ww8/docxexport.cxx7
-rw-r--r--sw/source/filter/ww8/docxexport.hxx3
-rw-r--r--sw/source/filter/ww8/docxsdrexport.cxx23
-rw-r--r--sw/source/filter/ww8/docxsdrexport.hxx5
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
new file mode 100644
index 000000000000..b6d6565d6550
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/sdt-2-para.docx
Binary files differ
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();