sw/qa/extras/ooxmlexport/data/tdf147724.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport18.cxx | 13 +++++ writerfilter/source/dmapper/SdtHelper.cxx | 64 ++++++++++++++++++++++----- writerfilter/source/dmapper/SdtHelper.hxx | 3 - 4 files changed, 69 insertions(+), 11 deletions(-)
New commits: commit 316549de11552a6e719b3d7231fcc4adbfda27f3 Author: Vasily Melenchuk <vasily.melenc...@cib.de> AuthorDate: Sun Sep 11 15:46:09 2022 +0300 Commit: Thorsten Behrens <thorsten.behr...@allotropia.de> CommitDate: Fri Sep 30 08:41:15 2022 +0200 tdf#147724: DOCX STD import: use storeid to indetify custom XML We need to identify XML document to use by storeid in sdt block and in properties of external XML file. Otherwise are possible content collisions when same xpath can evaluate successfully absolutely unrelated custom XML. Change-Id: I6d201a539130b110046deb1818340513cc47a061 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/139771 Tested-by: Jenkins Reviewed-by: Vasily Melenchuk <vasily.melenc...@cib.de> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140702 Reviewed-by: Thorsten Behrens <thorsten.behr...@allotropia.de> 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 1bf487a1f3bc..8a7009805f13 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx @@ -70,6 +70,19 @@ CPPUNIT_TEST_FIXTURE(Test, testCellSdtRedline) loadAndSave("cell-sdt-redline.docx"); } +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 8f5e809efdec..31c83d5a3ea6 100644 --- a/writerfilter/source/dmapper/SdtHelper.cxx +++ b/writerfilter/source/dmapper/SdtHelper.cxx @@ -103,13 +103,13 @@ void SdtHelper::loadPropertiesXMLs() if (!xDomBuilder.is()) return; - std::vector<uno::Reference<xml::dom::XDocument>> 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&) { @@ -122,7 +122,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&) { @@ -135,12 +137,40 @@ void SdtHelper::loadPropertiesXMLs() // Add custom XMLs uno::Sequence<uno::Reference<xml::dom::XDocument>> aCustomXmls = m_rDM_Impl.getDocumentReference()->getCustomXmlDomList(); - for (const auto& xDoc : aCustomXmls) + uno::Sequence<uno::Reference<xml::dom::XDocument>> aCustomXmlProps + = m_rDM_Impl.getDocumentReference()->getCustomXmlDomPropsList(); + if (aCustomXmls.getLength()) { - aPropDocs.push_back(xDoc); + uno::Reference<XXPathAPI> 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<XXPathObject> 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; } @@ -190,10 +220,24 @@ std::optional<OUString> 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<XXPathObject> 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<XXPathObject> xResult = xXpathAPI->eval(xDocument, m_sDataBindingXPath); + uno::Reference<XXPathObject> 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 c817285095e7..11bdfdcd12fc 100644 --- a/writerfilter/source/dmapper/SdtHelper.hxx +++ b/writerfilter/source/dmapper/SdtHelper.hxx @@ -11,6 +11,7 @@ #include <vector> #include <optional> +#include <unordered_map> #include <com/sun/star/beans/PropertyValue.hpp> #include <com/sun/star/text/XTextRange.hpp> @@ -92,7 +93,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<css::uno::Reference<css::xml::dom::XDocument>> m_xPropertiesXMLs; + std::unordered_map<OUString, css::uno::Reference<css::xml::dom::XDocument>> m_xPropertiesXMLs; /// Check if m_xPropertiesXMLs is initialized and loaded (need extra flag to distinguish /// empty sequence from not yet initialized)