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.melenchuk@cib.de>
This commit is contained in:
Vasily Melenchuk
2022-09-28 09:58:00 +02:00
parent 914d246e0e
commit da100343e5
4 changed files with 69 additions and 11 deletions
Binary file not shown.
@@ -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: */
+54 -10
View File
@@ -104,13 +104,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&)
{
@@ -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<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;
}
@@ -191,10 +221,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(xDocument, m_sDataBindingXPath);
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(aSource.second, m_sDataBindingXPath);
if (xResult.is() && xResult->getNodeList() && xResult->getNodeList()->getLength()
&& xResult->getString().getLength())
+2 -1
View File
@@ -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>
@@ -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<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)