From ea258e380eb87db2eb468ba5354df9f957b7660f Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Fri, 8 Aug 2014 18:17:43 +0200 Subject: DOCX export: handle date SDT on paragraphs Date SDT's are normally imported as form controls, while most other SDT types are just custom properties on regular text portions or paragraphs. However, given that form controls are not supported in headers/footers, in that case even date SDT's are just custom properties. So support such properties on paragraphs in the exporter to properly roundtrip date SDT's in headers/footers. Change-Id: I19eb73a3673e387a7b8780756ce7426a1851e796 --- sw/qa/extras/ooxmlexport/data/sdt-header.docx | Bin 0 -> 18551 bytes sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx | 8 ++++ sw/source/filter/ww8/docxattributeoutput.cxx | 54 +++++++++++++++++++++++--- sw/source/filter/ww8/docxattributeoutput.hxx | 2 + writerfilter/source/dmapper/DomainMapper.cxx | 2 +- 5 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 sw/qa/extras/ooxmlexport/data/sdt-header.docx diff --git a/sw/qa/extras/ooxmlexport/data/sdt-header.docx b/sw/qa/extras/ooxmlexport/data/sdt-header.docx new file mode 100644 index 000000000000..6a4bdfb2d059 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/sdt-header.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx index 4c44216a17a7..be347b5134b1 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx @@ -600,6 +600,14 @@ DECLARE_OOXMLEXPORT_TEST(testfdo81946, "fdo81946.docx") assertXPath(pXmlDoc, "/w:hdr[1]/w:p[1]/w:sdt[1]/w:sdtContent[1]/w:r[2]/mc:AlternateContent[1]",0); } +DECLARE_OOXMLEXPORT_TEST(testSdtHeader, "sdt-header.docx") +{ + // Problem was that w:sdt elements in headers were lost on import. + if (xmlDocPtr pXmlDoc = parseExport("word/header1.xml")) + // This was 0, w:sdt (and then w:date) was missing. + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:date", 1); +} + #endif CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index a8bcc8903c5a..b0447cfbef2b 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -549,7 +549,7 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT m_pSerializer->endElementNS( XML_w, XML_p ); // on export sdt blocks are never nested ATM if( !m_bAnchorLinkedToNode && !m_bStartedParaSdt ) - WriteSdtBlock( m_nParagraphSdtPrToken, m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrDataBindingAttrs, m_aParagraphSdtPrAlias, /*bPara=*/true ); + WriteSdtBlock( m_nParagraphSdtPrToken, m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrTokenAttributes, m_pParagraphSdtPrDataBindingAttrs, m_aParagraphSdtPrAlias, /*bPara=*/true ); else { //These should be written out to the actual Node and not to the anchor. @@ -591,6 +591,7 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT void DocxAttributeOutput::WriteSdtBlock( sal_Int32& nSdtPrToken, ::sax_fastparser::FastAttributeList*& pSdtPrTokenChildren, + ::sax_fastparser::FastAttributeList*& pSdtPrTokenAttributes, ::sax_fastparser::FastAttributeList*& pSdtPrDataBindingAttrs, OUString& rSdtPrAlias, bool bPara ) @@ -607,7 +608,14 @@ void DocxAttributeOutput::WriteSdtBlock( sal_Int32& nSdtPrToken, if( nSdtPrToken > 0 && pSdtPrTokenChildren ) { - m_pSerializer->startElement( nSdtPrToken, FSEND ); + if (!pSdtPrTokenAttributes) + m_pSerializer->startElement( nSdtPrToken, FSEND ); + else + { + XFastAttributeListRef xAttrList(pSdtPrTokenAttributes); + m_pSerializer->startElement(nSdtPrToken, xAttrList); + pSdtPrTokenAttributes = 0; + } uno::Sequence aChildren = pSdtPrTokenChildren->getFastAttributes(); for( sal_Int32 i=0; i < aChildren.getLength(); ++i ) @@ -619,7 +627,16 @@ void DocxAttributeOutput::WriteSdtBlock( sal_Int32& nSdtPrToken, m_pSerializer->endElement( nSdtPrToken ); } else if( (nSdtPrToken > 0) && nSdtPrToken != FSNS( XML_w, XML_id ) && !(m_bRunTextIsOn && m_rExport.SdrExporter().IsParagraphHasDrawing())) - m_pSerializer->singleElement( nSdtPrToken, FSEND ); + { + if (!pSdtPrTokenAttributes) + m_pSerializer->singleElement( nSdtPrToken, FSEND ); + else + { + XFastAttributeListRef xAttrList(pSdtPrTokenAttributes); + m_pSerializer->singleElement(nSdtPrToken, xAttrList); + pSdtPrTokenAttributes = 0; + } + } if( nSdtPrToken == FSNS( XML_w, XML_id ) || ( bPara && m_bParagraphSdtHasId ) ) //Word won't open a document with an empty id tag, we fill it with a random number @@ -1136,7 +1153,10 @@ void DocxAttributeOutput::EndRun() // enclose in a sdt block, if necessary: if one is already started, then don't do it for now // (so on export sdt blocks are never nested ATM) if ( !m_bAnchorLinkedToNode && !m_bStartedCharSdt ) - WriteSdtBlock( m_nRunSdtPrToken, m_pRunSdtPrTokenChildren, m_pRunSdtPrDataBindingAttrs, m_aRunSdtPrAlias, /*bPara=*/false ); + { + ::sax_fastparser::FastAttributeList* pRunSdtPrTokenAttributes = 0; + WriteSdtBlock( m_nRunSdtPrToken, m_pRunSdtPrTokenChildren, pRunSdtPrTokenAttributes, m_pRunSdtPrDataBindingAttrs, m_aRunSdtPrAlias, /*bPara=*/false ); + } else { //These should be written out to the actual Node and not to the anchor. @@ -7854,8 +7874,30 @@ void DocxAttributeOutput::ParaGrabBag(const SfxGrabBagItem& rItem) } else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id") m_bParagraphSdtHasId = true; + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_date") + { + m_nParagraphSdtPrToken = FSNS(XML_w, XML_date); + uno::Sequence aGrabBag = aPropertyValue.Value.get< uno::Sequence >(); + for (sal_Int32 j=0; j < aGrabBag.getLength(); ++j) + { + OString sValue = OUStringToOString(aGrabBag[j].Value.get(), RTL_TEXTENCODING_UTF8); + + if (aGrabBag[j].Name == "ooxml:CT_SdtDate_fullDate") + AddToAttrList(m_pParagraphSdtPrTokenAttributes, FSNS(XML_w, XML_fullDate), sValue.getStr()); + else if (aGrabBag[j].Name == "ooxml:CT_SdtDate_dateFormat") + AddToAttrList(m_pParagraphSdtPrTokenChildren, FSNS(XML_w, XML_dateFormat), sValue.getStr()); + else if (aGrabBag[j].Name == "ooxml:CT_SdtDate_lid") + AddToAttrList(m_pParagraphSdtPrTokenChildren, FSNS(XML_w, XML_lid), sValue.getStr()); + else if (aGrabBag[j].Name == "ooxml:CT_SdtDate_storeMappedDataAs") + AddToAttrList(m_pParagraphSdtPrTokenChildren, FSNS(XML_w, XML_storeMappedDataAs), sValue.getStr()); + else if (aGrabBag[j].Name == "ooxml:CT_SdtDate_calendar") + AddToAttrList(m_pParagraphSdtPrTokenChildren, FSNS(XML_w, XML_calendar), sValue.getStr()); + else + SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled SdtPr / ooxml:CT_SdtPr_date grab bag property " << aGrabBag[j].Name); + } + } else - SAL_INFO("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled SdtPr grab bag property " << aPropertyValue.Name); + SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled SdtPr grab bag property " << aPropertyValue.Name); } } else if (i->first == "ParaCnfStyle") @@ -8122,6 +8164,7 @@ DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, FSHelperPtr pSeri m_setFootnote(false) , m_nParagraphSdtPrToken(0) , m_pParagraphSdtPrTokenChildren(NULL) + , m_pParagraphSdtPrTokenAttributes(NULL) , m_pParagraphSdtPrDataBindingAttrs(NULL) , m_nRunSdtPrToken(0) , m_pRunSdtPrTokenChildren(NULL) @@ -8146,6 +8189,7 @@ DocxAttributeOutput::~DocxAttributeOutput() delete m_pTableWrt, m_pTableWrt = NULL; delete m_pParagraphSdtPrTokenChildren; m_pParagraphSdtPrTokenChildren = NULL; + delete m_pParagraphSdtPrTokenAttributes; m_pParagraphSdtPrTokenAttributes = NULL; delete m_pParagraphSdtPrDataBindingAttrs; m_pParagraphSdtPrDataBindingAttrs = NULL; delete m_pRunSdtPrTokenChildren; m_pRunSdtPrTokenChildren = NULL; delete m_pRunSdtPrDataBindingAttrs; m_pRunSdtPrDataBindingAttrs = NULL; diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx index 6ba3b484d6ac..90a31d87a70f 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -700,6 +700,7 @@ private: void WriteSdtBlock(sal_Int32& nSdtPrToken, ::sax_fastparser::FastAttributeList*& pSdtPrTokenChildren, + ::sax_fastparser::FastAttributeList*& pSdtPrTokenAttributes, ::sax_fastparser::FastAttributeList*& pSdtPrDataBindingAttrs, OUString& rSdtPrAlias, bool bPara); @@ -900,6 +901,7 @@ private: /// members to control the existence of grabbagged SDT properties in the paragraph sal_Int32 m_nParagraphSdtPrToken; ::sax_fastparser::FastAttributeList *m_pParagraphSdtPrTokenChildren; + ::sax_fastparser::FastAttributeList *m_pParagraphSdtPrTokenAttributes; ::sax_fastparser::FastAttributeList *m_pParagraphSdtPrDataBindingAttrs; /// members to control the existence of grabbagged SDT properties in the text run sal_Int32 m_nRunSdtPrToken; diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx index 0da5178400cf..bd2c226e4d93 100644 --- a/writerfilter/source/dmapper/DomainMapper.cxx +++ b/writerfilter/source/dmapper/DomainMapper.cxx @@ -2358,7 +2358,7 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, PropertyMapPtr rContext ) resourcemodel::resolveSprmProps(*this, rSprm); else { - OUString sName = "ooxml::CT_SdtPr_date"; + OUString sName = "ooxml:CT_SdtPr_date"; enableInteropGrabBag(sName); resourcemodel::resolveSprmProps(*this, rSprm); m_pImpl->m_pSdtHelper->appendToInteropGrabBag(getInteropGrabBag()); -- cgit