docx export: set custom xml values through XPathAPI instead of XSLT
each data binding built its own stylesheet and its own transformation pass over the item, because different bindings can use conflicting namespace prefixes The import side already resolves the same binding with XPathAPI and RegisterDataBindingNamespaces, so select the nodes that way here too and set their text on a copy of the custom xml. Signed-off-by: Caolán McNamara <caolan.mcnamara@collabora.com> Change-Id: I4b64bc56fd8a1c6c6641cf3ea9a44f7fed20206f Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/9941 Tested-by: Jenkins CPCI <releng@collaboraoffice.com> Reviewed-by: Noel Grandin <noel.grandin@collabora.com> (cherry picked from commit fe2f99ef74ef02e803aaf01dcc2b7f6e1cea67f7) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210430 Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Tested-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
committed by
Xisco Fauli
parent
c3ec833e11
commit
5033a915c3
@@ -41,7 +41,11 @@
|
||||
#include <com/sun/star/sdb/CommandType.hpp>
|
||||
#include <com/sun/star/text/XTextFieldsSupplier.hpp>
|
||||
#include <com/sun/star/util/XModifiable.hpp>
|
||||
#include <com/sun/star/xml/xslt/XSLTTransformer.hpp>
|
||||
#include <com/sun/star/xml/xpath/XPathAPI.hpp>
|
||||
#include <com/sun/star/xml/dom/XNode.hpp>
|
||||
#include <com/sun/star/xml/dom/XNodeList.hpp>
|
||||
#include <com/sun/star/xml/xpath/XXPathObject.hpp>
|
||||
#include <databindingnamespaces.hxx>
|
||||
|
||||
#include <oox/token/namespaces.hxx>
|
||||
#include <oox/token/tokens.hxx>
|
||||
@@ -1848,76 +1852,29 @@ void DocxExport::WriteGlossary()
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
class XsltTransformListener : public ::cppu::WeakImplHelper<io::XStreamListener>
|
||||
{
|
||||
public:
|
||||
XsltTransformListener() : m_bDone(false) {}
|
||||
|
||||
void wait() {
|
||||
std::unique_lock<std::mutex> g(m_mutex);
|
||||
m_cond.wait(g, [this]() { return m_bDone; });
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cond;
|
||||
bool m_bDone;
|
||||
|
||||
virtual void SAL_CALL disposing(const lang::EventObject&) noexcept override {}
|
||||
virtual void SAL_CALL started() noexcept override {}
|
||||
virtual void SAL_CALL closed() noexcept override { notifyDone(); }
|
||||
virtual void SAL_CALL terminated() noexcept override { notifyDone(); }
|
||||
virtual void SAL_CALL error(const uno::Any& e) override
|
||||
{
|
||||
notifyDone(); // set on error too, otherwise main thread waits forever
|
||||
SAL_WARN("sw.ww8", e);
|
||||
}
|
||||
|
||||
void notifyDone() {
|
||||
std::scoped_lock<std::mutex> g(m_mutex);
|
||||
m_bDone = true;
|
||||
m_cond.notify_all();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static void lcl_UpdateXmlValues(const SdtData& sdtData, const uno::Reference<css::io::XInputStream>& xInputStream, const uno::Reference<css::io::XOutputStream>& xOutputStream)
|
||||
static void lcl_UpdateXmlValues(const SdtData& rSdtData,
|
||||
const uno::Reference<xml::dom::XDocument>& xDocument)
|
||||
{
|
||||
uno::Sequence<uno::Any> aArgs{
|
||||
// XSLT transformation stylesheet:
|
||||
// - write all elements as is
|
||||
// - but if element matches sdtData.xpath, replace its text content by sdtData.xpath
|
||||
uno::Any(beans::NamedValue(u"StylesheetText"_ustr, uno::Any(OUString("<?xml version=\"1.0\" encoding=\"UTF-8\"?> \
|
||||
<xsl:stylesheet\
|
||||
xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\
|
||||
" + sdtData.namespaces + "\
|
||||
version=\"1.0\">\
|
||||
<xsl:template match=\"@* | node()\">\
|
||||
<xsl:copy>\
|
||||
<xsl:apply-templates select=\"@* | node()\"/>\
|
||||
</xsl:copy>\
|
||||
</xsl:template>\
|
||||
<xsl:template match = \"" + sdtData.xpath + "\">\
|
||||
<xsl:copy>\
|
||||
<xsl:text>" + sdtData.data + "</xsl:text>\
|
||||
</xsl:copy>\
|
||||
</xsl:template>\
|
||||
</xsl:stylesheet>\
|
||||
"))))
|
||||
};
|
||||
uno::Reference<xml::xpath::XXPathAPI> xXPathAPI
|
||||
= xml::xpath::XPathAPI::create(comphelper::getProcessComponentContext());
|
||||
sw::RegisterDataBindingNamespaces(rSdtData.namespaces, xXPathAPI);
|
||||
|
||||
css::uno::Reference<css::xml::xslt::XXSLTTransformer> xTransformer =
|
||||
css::xml::xslt::XSLTTransformer::create(comphelper::getProcessComponentContext(), aArgs);
|
||||
xTransformer->setInputStream(xInputStream);
|
||||
xTransformer->setOutputStream(xOutputStream);
|
||||
|
||||
rtl::Reference<XsltTransformListener> xListener = new XsltTransformListener();
|
||||
xTransformer->addListener(xListener);
|
||||
|
||||
xTransformer->start();
|
||||
xListener->wait();
|
||||
xTransformer->terminate();
|
||||
try
|
||||
{
|
||||
uno::Reference<xml::dom::XNodeList> xNodes
|
||||
= xXPathAPI->eval(xDocument, rSdtData.xpath)->getNodeList();
|
||||
for (sal_Int32 nNode = 0; nNode < xNodes->getLength(); ++nNode)
|
||||
{
|
||||
uno::Reference<xml::dom::XNode> xNode = xNodes->item(nNode);
|
||||
while (uno::Reference<xml::dom::XNode> xChild = xNode->getFirstChild())
|
||||
xNode->removeChild(xChild);
|
||||
xNode->appendChild(xDocument->createTextNode(rSdtData.data));
|
||||
}
|
||||
}
|
||||
catch (uno::Exception const&)
|
||||
{
|
||||
TOOLS_WARN_EXCEPTION("sw.ww8", "custom xml value at " << rSdtData.xpath);
|
||||
}
|
||||
}
|
||||
|
||||
void DocxExport::WriteCustomXml()
|
||||
@@ -1951,49 +1908,27 @@ void DocxExport::WriteCustomXml()
|
||||
oox::getRelationship(Relationship::CUSTOMXML),
|
||||
Concat2View("../customXml/item"+OUString::number(j+1)+".xml" ));
|
||||
|
||||
uno::Reference< xml::sax::XSAXSerializable > serializer( customXmlDom, uno::UNO_QUERY );
|
||||
uno::Reference< xml::sax::XWriter > writer = xml::sax::Writer::create( comphelper::getProcessComponentContext() );
|
||||
|
||||
xCustomXmlItemOutStream = GetFilter().openFragmentStream(
|
||||
"customXml/item" + OUString::number(j + 1) + ".xml", u"application/xml"_ustr);
|
||||
|
||||
uno::Reference<xml::dom::XDocument> xItemDom = customXmlDom;
|
||||
if (m_SdtData.size())
|
||||
{
|
||||
// There are some SDT blocks data with data bindings which can update some custom xml values
|
||||
rtl::Reference< comphelper::UNOMemoryStream > xMemStream = new comphelper::UNOMemoryStream();
|
||||
|
||||
writer->setOutputStream(xMemStream->getOutputStream());
|
||||
|
||||
serializer->serialize(writer, uno::Sequence< beans::StringPair >());
|
||||
|
||||
uno::Reference< io::XStream > xXSLTInStream = xMemStream;
|
||||
rtl::Reference< comphelper::UNOMemoryStream > xXSLTOutStream;
|
||||
// Apply XSLT transformations for each SDT data binding
|
||||
// Seems it is not possible to do this as one transformation: each data binding
|
||||
// can have different namespaces, but with conflicting names (ns0, ns1, etc..)
|
||||
for (size_t i = 0; i < m_SdtData.size(); i++)
|
||||
{
|
||||
if (i == m_SdtData.size() - 1)
|
||||
{
|
||||
// last transformation
|
||||
lcl_UpdateXmlValues(
|
||||
m_SdtData[i], xXSLTInStream->getInputStream(), xCustomXmlItemOutStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
xXSLTOutStream = new comphelper::UNOMemoryStream();
|
||||
lcl_UpdateXmlValues(m_SdtData[i], xXSLTInStream->getInputStream(), xXSLTOutStream->getOutputStream());
|
||||
// Use previous output as an input for next run
|
||||
xXSLTInStream.set( xXSLTOutStream );
|
||||
}
|
||||
}
|
||||
|
||||
// A data-bound content control can hold a value the user has edited since the
|
||||
// document was loaded, so write the current values into the stored XML. The edit
|
||||
// goes to a copy, which leaves the document model as it is.
|
||||
uno::Reference<xml::dom::XDocument> xCopy(customXmlDom->cloneNode(true),
|
||||
uno::UNO_QUERY_THROW);
|
||||
for (const SdtData& rSdtData : m_SdtData)
|
||||
lcl_UpdateXmlValues(rSdtData, xCopy);
|
||||
xItemDom = xCopy;
|
||||
}
|
||||
else
|
||||
{
|
||||
writer->setOutputStream(xCustomXmlItemOutStream);
|
||||
|
||||
serializer->serialize(writer, uno::Sequence< beans::StringPair >());
|
||||
}
|
||||
uno::Reference<xml::sax::XSAXSerializable> xSerializer(xItemDom, uno::UNO_QUERY_THROW);
|
||||
uno::Reference<xml::sax::XWriter> xWriter
|
||||
= xml::sax::Writer::create(comphelper::getProcessComponentContext());
|
||||
xWriter->setOutputStream(xCustomXmlItemOutStream);
|
||||
xSerializer->serialize(xWriter, uno::Sequence<beans::StringPair>());
|
||||
}
|
||||
|
||||
if (customXmlDomProps.is())
|
||||
|
||||
Reference in New Issue
Block a user