diff --git a/sw/qa/extras/ooxmlexport/data/tdf147724.docx b/sw/qa/extras/ooxmlexport/data/tdf147724.docx new file mode 100644 index 000000000000..97f05c921b89 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf147724.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx index 6c4eaa09fa34..b9932f7c199d 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx @@ -104,6 +104,19 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf149551_mongolianVert) assertXPath(pXmlDoc, "//wps:bodyPr", "vert", "mongolianVert"); } +DECLARE_OOXMLEXPORT_TEST(testTdf147724, "tdf147724.docx") +{ + const auto& pLayout = parseLayoutDump(); + + // Ensure we load field value from external XML correctly (it was "HERUNTERLADEN") + assertXPathContent(pLayout, "/root/page[1]/body/txt[1]", "Placeholder -> *ABC*"); + + // This SDT has no storage id, it is not an error, but content can be taken from any suitable XML + // There 2 variants possible, both are acceptable + OUString sFieldResult = getXPathContent(pLayout, "/root/page[1]/body/txt[2]"); + CPPUNIT_ASSERT(sFieldResult == "Placeholder -> *HERUNTERLADEN*" || sFieldResult == "Placeholder -> *ABC*"); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/dmapper/SdtHelper.cxx b/writerfilter/source/dmapper/SdtHelper.cxx index 50ddf4b99b21..8225a6a04bca 100644 --- a/writerfilter/source/dmapper/SdtHelper.cxx +++ b/writerfilter/source/dmapper/SdtHelper.cxx @@ -104,13 +104,13 @@ void SdtHelper::loadPropertiesXMLs() if (!xDomBuilder.is()) return; - std::vector> aPropDocs; - // Load core properties try { auto xCorePropsStream = xImporter->getCorePropertiesStream(m_rDM_Impl.m_xDocumentStorage); - aPropDocs.push_back(xDomBuilder->parse(xCorePropsStream)); + m_xPropertiesXMLs.insert( + { OUString("{6C3C8BC8-F283-45AE-878A-BAB7291924A1}"), // hardcoded id for core props + xDomBuilder->parse(xCorePropsStream) }); } catch (const uno::Exception&) { @@ -123,7 +123,9 @@ void SdtHelper::loadPropertiesXMLs() { auto xExtPropsStream = xImporter->getExtendedPropertiesStream(m_rDM_Impl.m_xDocumentStorage); - aPropDocs.push_back(xDomBuilder->parse(xExtPropsStream)); + m_xPropertiesXMLs.insert( + { OUString("{6668398D-A668-4E3E-A5EB-62B293D839F1}"), // hardcoded id for extended props + xDomBuilder->parse(xExtPropsStream) }); } catch (const uno::Exception&) { @@ -136,12 +138,40 @@ void SdtHelper::loadPropertiesXMLs() // Add custom XMLs uno::Sequence> aCustomXmls = m_rDM_Impl.getDocumentReference()->getCustomXmlDomList(); - for (const auto& xDoc : aCustomXmls) + uno::Sequence> aCustomXmlProps + = m_rDM_Impl.getDocumentReference()->getCustomXmlDomPropsList(); + if (aCustomXmls.getLength()) { - aPropDocs.push_back(xDoc); + uno::Reference xXpathAPI = XPathAPI::create(m_xComponentContext); + xXpathAPI->registerNS("ds", + "http://schemas.openxmlformats.org/officeDocument/2006/customXml"); + sal_Int32 nItem = 0; + // Hereby we assume that items from getCustomXmlDomList() and getCustomXmlDomPropsList() + // are matching each other: + // item1.xml -> itemProps1.xml, item2.xml -> itemProps2.xml + // This does works practically, but is it true in general? + for (const auto& xDoc : aCustomXmls) + { + // Retrieve storeid from properties xml + OUString aStoreId; + uno::Reference xResult + = xXpathAPI->eval(aCustomXmlProps[nItem], "string(/ds:datastoreItem/@ds:itemID)"); + + if (xResult.is() && xResult->getString().getLength()) + { + aStoreId = xResult->getString(); + } + else + { + SAL_WARN("writerfilter", + "SdtHelper::loadPropertiesXMLs: can't fetch storeid for custom doc!"); + } + + m_xPropertiesXMLs.insert({ aStoreId, xDoc }); + nItem++; + } } - m_xPropertiesXMLs = comphelper::containerToSequence(aPropDocs); m_bPropertiesXMLsLoaded = true; } @@ -191,10 +221,24 @@ std::optional SdtHelper::getValueFromDataBinding() lcl_registerNamespaces(m_sDataBindingPrefixMapping, xXpathAPI); - // Iterate all properties xml documents and try to fetch data - for (const auto& xDocument : m_xPropertiesXMLs) + // Find storage by store id and eval xpath there + const auto& aSourceIt = m_xPropertiesXMLs.find(m_sDataBindingStoreItemID); + if (aSourceIt != m_xPropertiesXMLs.end()) { - uno::Reference xResult = xXpathAPI->eval(xDocument, m_sDataBindingXPath); + uno::Reference xResult + = xXpathAPI->eval(aSourceIt->second, m_sDataBindingXPath); + + if (xResult.is() && xResult->getNodeList() && xResult->getNodeList()->getLength() + && xResult->getString().getLength()) + { + return xResult->getString(); + } + } + + // Nothing found? Try to iterate storages and eval xpath + for (const auto& aSource : m_xPropertiesXMLs) + { + uno::Reference xResult = xXpathAPI->eval(aSource.second, m_sDataBindingXPath); if (xResult.is() && xResult->getNodeList() && xResult->getNodeList()->getLength() && xResult->getString().getLength()) diff --git a/writerfilter/source/dmapper/SdtHelper.hxx b/writerfilter/source/dmapper/SdtHelper.hxx index 2cfa19f5e95e..689c57503185 100644 --- a/writerfilter/source/dmapper/SdtHelper.hxx +++ b/writerfilter/source/dmapper/SdtHelper.hxx @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -94,7 +95,7 @@ class SdtHelper final : public virtual SvRefBase bool m_bOutsideAParagraph; /// Storage for all properties documents as xml::dom::XDocument for later querying xpath for data - css::uno::Sequence> m_xPropertiesXMLs; + std::unordered_map> m_xPropertiesXMLs; /// Check if m_xPropertiesXMLs is initialized and loaded (need extra flag to distinguish /// empty sequence from not yet initialized)