summaryrefslogtreecommitdiffstats
path: root/filter/source/xsltfilter
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /filter/source/xsltfilter
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'filter/source/xsltfilter')
-rw-r--r--filter/source/xsltfilter/LibXSLTTransformer.cxx571
-rw-r--r--filter/source/xsltfilter/LibXSLTTransformer.hxx185
-rw-r--r--filter/source/xsltfilter/OleHandler.cxx214
-rw-r--r--filter/source/xsltfilter/OleHandler.hxx93
-rw-r--r--filter/source/xsltfilter/XSLTFilter.cxx659
-rw-r--r--filter/source/xsltfilter/xsltfilter.component30
6 files changed, 1752 insertions, 0 deletions
diff --git a/filter/source/xsltfilter/LibXSLTTransformer.cxx b/filter/source/xsltfilter/LibXSLTTransformer.cxx
new file mode 100644
index 000000000..1a7c34805
--- /dev/null
+++ b/filter/source/xsltfilter/LibXSLTTransformer.cxx
@@ -0,0 +1,571 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <algorithm>
+#include <cstring>
+#include <map>
+#include <optional>
+#include <string_view>
+#include <utility>
+#include <vector>
+#include <libxml/parser.h>
+#include <libxml/xmlIO.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/xmlstring.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+#include <libxslt/variables.h>
+#include <libxslt/extensions.h>
+#include <libexslt/exslt.h>
+
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <osl/file.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XStreamListener.hpp>
+
+#include "LibXSLTTransformer.hxx"
+#include "OleHandler.hxx"
+
+using namespace ::cppu;
+using namespace ::osl;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::registry;
+using ::std::pair;
+
+namespace XSLT
+{
+ const char* const LibXSLTTransformer::PARAM_SOURCE_URL = "sourceURL";
+ const char* const LibXSLTTransformer::PARAM_SOURCE_BASE_URL =
+ "sourceBaseURL";
+ const char* const LibXSLTTransformer::PARAM_TARGET_URL = "targetURL";
+ const char* const LibXSLTTransformer::PARAM_TARGET_BASE_URL =
+ "targetBaseURL";
+ const char* const LibXSLTTransformer::PARAM_DOCTYPE_PUBLIC = "publicType";
+
+ const sal_Int32 Reader::OUTPUT_BUFFER_SIZE = 4096;
+
+ const sal_Int32 Reader::INPUT_BUFFER_SIZE = 4096;
+
+ namespace {
+
+ /**
+ * ParserInputBufferCallback forwards IO call-backs to libxml stream IO.
+ */
+ struct ParserInputBufferCallback
+ {
+ static int
+ on_read(void * context, char * buffer, int len)
+ {
+ Reader * tmp = static_cast<Reader*> (context);
+ return tmp->read(buffer, len);
+ }
+ static int
+ on_close(void * )
+ {
+ return 0;
+ }
+ };
+ /**
+ * ParserOutputBufferCallback forwards IO call-backs to libxml stream IO.
+ */
+ struct ParserOutputBufferCallback
+ {
+ static int
+ on_write(void * context, const char * buffer, int len)
+ {
+ Reader * tmp = static_cast<Reader*> (context);
+ return tmp->write(buffer, len);
+ }
+ static int
+ on_close(void * context)
+ {
+ Reader * tmp = static_cast<Reader*> (context);
+ tmp->closeOutput();
+ return 0;
+ }
+ };
+ /**
+ * ExtFuncOleCB forwards XPath extension function calls registered with libxslt to the OleHandler instance that actually
+ * provides the implementation for those functions.
+ *
+ * The OLE extension module currently supplies two functions
+ * insertByName: registers an OLE object to be later inserted into the output tree.
+ * getByName: reads a previously registered OLE object and returns a base64 encoded string representation.
+ */
+ struct ExtFuncOleCB
+ {
+ static void *
+ init(xsltTransformContextPtr, const xmlChar*)
+ {
+ return nullptr;
+ }
+ static void
+ insertByName(xmlXPathParserContextPtr ctxt, int nargs)
+ {
+ xsltTransformContextPtr tctxt;
+ void *data;
+ if (nargs != 2) {
+ xsltGenericError(xsltGenericErrorContext,
+ "insertByName: requires exactly 2 arguments\n");
+ return;
+ }
+ tctxt = xsltXPathGetTransformContext(ctxt);
+ if (tctxt == nullptr) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltExtFunctionTest: failed to get the transformation context\n");
+ return;
+ }
+ // XXX: someone with better knowledge of libxslt might come up with a better
+ // idea to pass the OleHandler than by attaching it to tctxt->_private. See also
+ // below.
+ data = tctxt->_private;
+ if (data == nullptr) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltExtFunctionTest: failed to get module data\n");
+ return;
+ }
+ OleHandler * oh = static_cast<OleHandler*> (data);
+
+ xmlXPathObjectPtr value = valuePop(ctxt);
+ value = ensureStringValue(value, ctxt);
+ xmlXPathObjectPtr streamName = valuePop(ctxt);
+ streamName = ensureStringValue(streamName, ctxt);
+
+ oh->insertByName(OStringToOUString(reinterpret_cast<char*>(streamName->stringval), RTL_TEXTENCODING_UTF8),
+ std::string_view(reinterpret_cast<char*>(value->stringval)));
+ valuePush(ctxt, xmlXPathNewCString(""));
+ }
+
+ static xmlXPathObjectPtr ensureStringValue(xmlXPathObjectPtr obj, const xmlXPathParserContextPtr ctxt)
+ {
+ if (obj->type != XPATH_STRING) {
+ valuePush(ctxt, obj);
+ xmlXPathStringFunction(ctxt, 1);
+ obj = valuePop(ctxt);
+ }
+ return obj;
+ }
+
+ static void getByName(xmlXPathParserContextPtr ctxt, int nargs)
+ {
+ xsltTransformContextPtr tctxt;
+ void *data;
+ if (nargs != 1) {
+ xsltGenericError(xsltGenericErrorContext,
+ "getByName: requires exactly 1 argument\n");
+ return;
+ }
+
+ tctxt = xsltXPathGetTransformContext(ctxt);
+ if (tctxt == nullptr) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltExtFunctionTest: failed to get the transformation context\n");
+ return;
+ }
+ // XXX: someone with better knowledge of libxslt might come up with a better
+ // idea to pass the OleHandler than by attaching it to tctxt->_private
+ data = tctxt->_private;
+ if (data == nullptr) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltExtFunctionTest: failed to get module data\n");
+ return;
+ }
+ OleHandler * oh = static_cast<OleHandler*> (data);
+ xmlXPathObjectPtr streamName = valuePop(ctxt);
+ streamName = ensureStringValue(streamName, ctxt);
+ const OString content = oh->getByName(OStringToOUString(reinterpret_cast<char*>(streamName->stringval), RTL_TEXTENCODING_UTF8));
+ valuePush(ctxt, xmlXPathNewCString(content.getStr()));
+ xmlXPathFreeObject(streamName);
+ }
+ };
+
+ }
+
+ Reader::Reader(LibXSLTTransformer* transformer) :
+ Thread("LibXSLTTransformer"), m_transformer(transformer),
+ m_readBuf(INPUT_BUFFER_SIZE), m_writeBuf(OUTPUT_BUFFER_SIZE),
+ m_tcontext(nullptr)
+ {
+ LIBXML_TEST_VERSION;
+ }
+ ;
+
+ int
+ Reader::read(char * buffer, int len)
+ {
+ // const char *ptr = (const char *) context;
+ if (buffer == nullptr || len < 0)
+ return -1;
+ sal_Int32 n;
+ css::uno::Reference<XInputStream> xis = m_transformer->getInputStream();
+ n = xis->readBytes(m_readBuf, len);
+ if (n > 0)
+ {
+ memcpy(buffer, m_readBuf.getArray(), n);
+ }
+ return n;
+ }
+
+ int
+ Reader::write(const char * buffer, int len)
+ {
+ if (buffer == nullptr || len < 0)
+ return -1;
+ if (len > 0)
+ {
+ css::uno::Reference<XOutputStream> xos = m_transformer->getOutputStream();
+ sal_Int32 writeLen = len;
+ sal_Int32 bufLen = ::std::min(writeLen, OUTPUT_BUFFER_SIZE);
+ const sal_uInt8* memPtr =
+ reinterpret_cast<const sal_uInt8*> (buffer);
+ while (writeLen > 0)
+ {
+ sal_Int32 n = ::std::min(writeLen, bufLen);
+ m_writeBuf.realloc(n);
+ memcpy(m_writeBuf.getArray(), memPtr,
+ static_cast<size_t> (n));
+ xos->writeBytes(m_writeBuf);
+ memPtr += n;
+ writeLen -= n;
+ }
+ }
+ return len;
+ }
+
+ void
+ Reader::closeOutput()
+ {
+ css::uno::Reference<XOutputStream> xos = m_transformer->getOutputStream();
+ if (xos.is())
+ {
+ xos->flush();
+ xos->closeOutput();
+ }
+ m_transformer->done();
+ }
+
+ void
+ Reader::execute()
+ {
+ OSL_ASSERT(m_transformer != nullptr);
+ OSL_ASSERT(m_transformer->getInputStream().is());
+ OSL_ASSERT(m_transformer->getOutputStream().is());
+ OSL_ASSERT(!m_transformer->getStyleSheetURL().isEmpty() || !m_transformer->getStyleSheetText().isEmpty());
+ ::std::map<const char*, OString> pmap = m_transformer->getParameters();
+ ::std::vector< const char* > params( pmap.size() * 2 + 1 ); // build parameters
+ int paramIndex = 0;
+ for (auto const& elem : pmap)
+ {
+ params[paramIndex++] = elem.first;
+ params[paramIndex++] = elem.second.getStr();
+ }
+ params[paramIndex] = nullptr;
+ xmlDocPtr doc = xmlReadIO(&ParserInputBufferCallback::on_read,
+ &ParserInputBufferCallback::on_close,
+ static_cast<void*> (this), nullptr, nullptr, 0);
+ xsltStylesheetPtr styleSheet = nullptr;
+ if (m_transformer->getStyleSheetURL().getLength())
+ styleSheet = xsltParseStylesheetFile(
+ reinterpret_cast<const xmlChar *>(m_transformer->getStyleSheetURL().getStr()));
+ else if (m_transformer->getStyleSheetText().getLength())
+ {
+ xmlDocPtr styleSheetDoc = xmlReadMemory(
+ m_transformer->getStyleSheetText().getStr(),
+ m_transformer->getStyleSheetText().getLength(),
+ "noname.xml", nullptr, 0);
+
+ styleSheet = xsltParseStylesheetDoc(styleSheetDoc);
+ }
+
+ if (!styleSheet)
+ {
+ m_transformer->error("No stylesheet was created");
+ }
+
+ xmlDocPtr result = nullptr;
+ exsltRegisterAll();
+ registerExtensionModule();
+#ifdef DEBUG_FILTER_LIBXSLTTRANSFORMER
+ xsltSetGenericDebugFunc(stderr, NULL);
+ xsltDebugDumpExtensions(NULL);
+#endif
+ std::optional<OleHandler> oh(std::in_place, m_transformer->getComponentContext());
+ if (styleSheet)
+ {
+ xsltTransformContextPtr tcontext = xsltNewTransformContext(
+ styleSheet, doc);
+ {
+ std::scoped_lock<std::mutex> g(m_mutex);
+ m_tcontext = tcontext;
+ }
+ oh->registercontext(m_tcontext);
+ xsltQuoteUserParams(m_tcontext, params.data());
+ result = xsltApplyStylesheetUser(styleSheet, doc, nullptr, nullptr, nullptr,
+ m_tcontext);
+ }
+
+ if (result)
+ {
+ xmlCharEncodingHandlerPtr encoder = xmlGetCharEncodingHandler(
+ XML_CHAR_ENCODING_UTF8);
+ xmlOutputBufferPtr outBuf = xmlAllocOutputBuffer(encoder);
+ outBuf->context = static_cast<void *> (this);
+ outBuf->writecallback = &ParserOutputBufferCallback::on_write;
+ outBuf->closecallback = &ParserOutputBufferCallback::on_close;
+ xsltSaveResultTo(outBuf, result, styleSheet);
+ (void)xmlOutputBufferClose(outBuf);
+ }
+ else
+ {
+ xmlErrorPtr lastErr = xmlGetLastError();
+ OUString msg;
+ if (lastErr)
+ msg = OStringToOUString(lastErr->message, RTL_TEXTENCODING_UTF8);
+ else
+ msg = "Unknown XSLT transformation error";
+
+ m_transformer->error(msg);
+ }
+ oh.reset();
+ xsltFreeStylesheet(styleSheet);
+ xsltTransformContextPtr tcontext = nullptr;
+ {
+ std::scoped_lock<std::mutex> g(m_mutex);
+ std::swap(m_tcontext, tcontext);
+ }
+ xsltFreeTransformContext(tcontext);
+ xmlFreeDoc(doc);
+ xmlFreeDoc(result);
+ }
+
+ void
+ Reader::registerExtensionModule()
+ {
+ const xmlChar* oleModuleURI = reinterpret_cast<const xmlChar *>(EXT_MODULE_OLE_URI);
+ xsltRegisterExtModule(oleModuleURI, &ExtFuncOleCB::init, nullptr);
+ xsltRegisterExtModuleFunction(
+ reinterpret_cast<const xmlChar*>("insertByName"),
+ oleModuleURI,
+ &ExtFuncOleCB::insertByName);
+ xsltRegisterExtModuleFunction(
+ reinterpret_cast<const xmlChar*>("getByName"),
+ oleModuleURI,
+ &ExtFuncOleCB::getByName);
+
+ }
+
+ void Reader::forceStateStopped()
+ {
+ std::scoped_lock<std::mutex> g(m_mutex);
+ if (!m_tcontext)
+ return;
+ //tdf#100057 If we force a cancel, libxslt will of course just keep on going unless something
+ //tells it to stop. Here we force the stopped state so that libxslt will stop processing
+ //and so Reader::execute will complete and we can join cleanly
+ m_tcontext->state = XSLT_STATE_STOPPED;
+ }
+
+ Reader::~Reader()
+ {
+ }
+
+ LibXSLTTransformer::LibXSLTTransformer(
+ css::uno::Reference<XComponentContext> xContext) :
+ m_xContext(std::move(xContext))
+ {
+ }
+
+ // XServiceInfo
+ sal_Bool LibXSLTTransformer::supportsService(const OUString& sServiceName)
+ {
+ return cppu::supportsService(this, sServiceName);
+ }
+ OUString LibXSLTTransformer::getImplementationName()
+ {
+ return "com.sun.star.comp.documentconversion.XSLTFilter";
+ }
+ css::uno::Sequence< OUString > LibXSLTTransformer::getSupportedServiceNames()
+ {
+ return { "com.sun.star.documentconversion.XSLTFilter" };
+ }
+
+ void
+ LibXSLTTransformer::setInputStream(
+ const css::uno::Reference<XInputStream>& inputStream)
+ {
+ m_rInputStream = inputStream;
+ }
+
+ css::uno::Reference<XInputStream>
+ LibXSLTTransformer::getInputStream()
+ {
+ return m_rInputStream;
+ }
+
+ void
+ LibXSLTTransformer::setOutputStream(
+ const css::uno::Reference<XOutputStream>& outputStream)
+ {
+ m_rOutputStream = outputStream;
+ }
+
+ css::uno::Reference<XOutputStream>
+ LibXSLTTransformer::getOutputStream()
+ {
+ return m_rOutputStream;
+ }
+
+ void
+ LibXSLTTransformer::addListener(const css::uno::Reference<XStreamListener>& listener)
+ {
+ m_listeners.push_front(listener);
+ }
+
+ void
+ LibXSLTTransformer::removeListener(
+ const css::uno::Reference<XStreamListener>& listener)
+ {
+ m_listeners.erase( std::remove(m_listeners.begin(), m_listeners.end(), listener ), m_listeners.end() );
+ }
+
+ void
+ LibXSLTTransformer::start()
+ {
+ for (const css::uno::Reference<XStreamListener>& xl : m_listeners)
+ {
+ xl->started();
+ }
+ OSL_ENSURE(!m_Reader.is(), "Somebody forgot to call terminate *and* holds a reference to this LibXSLTTransformer instance");
+ m_Reader = new Reader(this);
+ m_Reader->launch();
+ }
+
+ void
+ LibXSLTTransformer::error(const OUString& msg)
+ {
+ Any arg;
+ arg <<= Exception(msg, *this);
+ for (const css::uno::Reference<XStreamListener>& xl : m_listeners)
+ {
+ if (xl.is())
+ {
+ xl->error(arg);
+ }
+ }
+ }
+
+ void
+ LibXSLTTransformer::done()
+ {
+ for (const css::uno::Reference<XStreamListener>& xl : m_listeners)
+ {
+ if (xl.is())
+ {
+ xl->closed();
+ }
+ }
+ }
+
+ void
+ LibXSLTTransformer::terminate()
+ {
+ if (m_Reader.is())
+ {
+ m_Reader->terminate();
+ m_Reader->forceStateStopped();
+ m_Reader->join();
+ }
+ m_Reader.clear();
+ m_parameters.clear();
+ }
+
+ void
+ LibXSLTTransformer::initialize(const Sequence<Any>& args)
+ {
+ Sequence<Any> params;
+ if (!(args[0] >>= params))
+ { // backward compatibility for old clients using createInstance
+ params = args;
+ }
+ xmlSubstituteEntitiesDefault(0);
+ m_parameters.clear();
+ for (const Any& p : std::as_const(params))
+ {
+ NamedValue nv;
+ p >>= nv;
+ OString nameUTF8 = OUStringToOString(nv.Name,
+ RTL_TEXTENCODING_UTF8);
+ OUString value;
+ OString valueUTF8;
+ if (nv.Value >>= value)
+ {
+ valueUTF8 = OUStringToOString(value,
+ RTL_TEXTENCODING_UTF8);
+ }
+ else
+ {
+ // ignore non-string parameters
+ continue;
+ }
+ if (nameUTF8 == "StylesheetURL")
+ {
+ m_styleSheetURL = valueUTF8;
+ }
+ if (nameUTF8 == "StylesheetText")
+ {
+ m_styleSheetText = valueUTF8;
+ }
+ else if (nameUTF8 == "SourceURL")
+ {
+ m_parameters.insert(pair<const char*, OString> (
+ PARAM_SOURCE_URL, valueUTF8));
+ }
+ else if (nameUTF8 == "SourceBaseURL")
+ {
+ m_parameters.insert(pair<const char*, OString> (
+ PARAM_SOURCE_BASE_URL, valueUTF8));
+ }
+ else if (nameUTF8 == "TargetURL")
+ {
+ m_parameters.insert(pair<const char*, OString> (
+ PARAM_TARGET_URL, valueUTF8));
+ }
+ else if (nameUTF8 == "TargetBaseURL")
+ {
+ m_parameters.insert(pair<const char*, OString> (
+ PARAM_TARGET_BASE_URL, valueUTF8));
+ }
+ else if (nameUTF8 == "DoctypePublic")
+ {
+ m_parameters.insert(pair<const char*, OString> (
+ PARAM_DOCTYPE_PUBLIC, valueUTF8));
+ }
+ }
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+filter_LibXSLTTransformer_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new XSLT::LibXSLTTransformer(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/filter/source/xsltfilter/LibXSLTTransformer.hxx b/filter/source/xsltfilter/LibXSLTTransformer.hxx
new file mode 100644
index 000000000..8696bda6a
--- /dev/null
+++ b/filter/source/xsltfilter/LibXSLTTransformer.hxx
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <deque>
+#include <map>
+#include <mutex>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlIO.h>
+#include <libxslt/transform.h>
+#include <libxml/xpathInternals.h>
+
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <rtl/ref.hxx>
+
+#include <salhelper/thread.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XStreamListener.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/xml/xslt/XXSLTTransformer.hpp>
+
+using namespace ::cppu;
+using namespace ::osl;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::uno;
+
+using ::std::map;
+
+#define EXT_MODULE_OLE_URI "http://libreoffice.org/2011/xslt/ole"
+
+namespace XSLT
+{
+
+ class LibXSLTTransformer;
+
+ /*
+ * Reader provides a worker thread to perform the actual transformation.
+ * It pipes the streams provided by a LibXSLTTransformer
+ * instance through libxslt.
+ */
+ class Reader : public salhelper::Thread
+ {
+ public:
+ Reader(LibXSLTTransformer* transformer);
+ int read(char * buffer, int len);
+ int write(const char * buffer, int len);
+ void forceStateStopped();
+ void closeOutput();
+
+ private:
+ virtual ~Reader() override;
+
+ static const sal_Int32 OUTPUT_BUFFER_SIZE;
+ static const sal_Int32 INPUT_BUFFER_SIZE;
+ rtl::Reference<LibXSLTTransformer> m_transformer;
+ Sequence<sal_Int8> m_readBuf;
+ Sequence<sal_Int8> m_writeBuf;
+
+ std::mutex m_mutex;
+ xsltTransformContextPtr m_tcontext;
+
+ virtual void execute() override;
+ static void registerExtensionModule();
+ };
+
+ /*
+ * LibXSLTTransformer provides a transforming pipe service to XSLTFilter.
+ *
+ * It implements XActiveDataSource, XActiveDataSink and XActiveDataControl
+ * to consume data. It also notifies upstream of important events such as
+ * begin and end of the transformation and of any errors that occur during
+ * transformation.
+ *
+ * TODO: Error reporting leaves room for improvement, currently.
+ *
+ * The actual transformation is done by a worker thread.
+ *
+ * See Reader below.
+ */
+ class LibXSLTTransformer : public WeakImplHelper<css::xml::xslt::XXSLTTransformer, css::lang::XServiceInfo>
+ {
+ private:
+ static const char* const PARAM_SOURCE_URL;
+ static const char* const PARAM_SOURCE_BASE_URL;
+ static const char* const PARAM_TARGET_URL;
+ static const char* const PARAM_TARGET_BASE_URL;
+ static const char* const PARAM_DOCTYPE_PUBLIC;
+
+ // the UNO ServiceFactory
+ css::uno::Reference<css::uno::XComponentContext> m_xContext;
+
+ css::uno::Reference<XInputStream> m_rInputStream;
+
+ css::uno::Reference<XOutputStream> m_rOutputStream;
+
+ typedef ::std::deque<css::uno::Reference<XStreamListener> > ListenerList;
+
+ ListenerList m_listeners;
+
+ OString m_styleSheetURL;
+ OString m_styleSheetText;
+
+ ::std::map<const char *, OString> m_parameters;
+
+ rtl::Reference<Reader> m_Reader;
+
+ protected:
+ virtual ~LibXSLTTransformer() override {
+ if (m_Reader.is()) {
+ m_Reader->terminate();
+ m_Reader->forceStateStopped();
+ m_Reader->join();
+ }
+ }
+
+ public:
+
+ // ctor...
+ LibXSLTTransformer(css::uno::Reference<css::uno::XComponentContext> x);
+
+ // XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override;
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XActiveDataSink
+ virtual void SAL_CALL
+ setInputStream(const css::uno::Reference<XInputStream>& inputStream) override;
+ virtual css::uno::Reference<XInputStream> SAL_CALL
+ getInputStream() override;
+ // XActiveDataSource
+ virtual void SAL_CALL
+ setOutputStream(const css::uno::Reference<XOutputStream>& outputStream) override;
+ virtual css::uno::Reference<XOutputStream> SAL_CALL
+ getOutputStream() override;
+ // XActiveDataControl
+ virtual void SAL_CALL
+ addListener(const css::uno::Reference<XStreamListener>& listener) override;
+ virtual void SAL_CALL
+ removeListener(const css::uno::Reference<XStreamListener>& listener) override;
+ virtual void SAL_CALL
+ start() override;
+ virtual void SAL_CALL
+ terminate() override;
+ virtual void SAL_CALL
+ initialize(const Sequence<Any>& params) override;
+
+ void
+ done();
+
+ void
+ error(const OUString& msg);
+
+ const OString&
+ getStyleSheetURL() const { return m_styleSheetURL; }
+
+ const OString& getStyleSheetText() const { return m_styleSheetText; }
+
+ const ::std::map<const char*, OString>&
+ getParameters() const { return m_parameters; }
+
+ const css::uno::Reference<css::uno::XComponentContext>&
+ getComponentContext() const {
+ return m_xContext;
+ }
+
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/xsltfilter/OleHandler.cxx b/filter/source/xsltfilter/OleHandler.cxx
new file mode 100644
index 000000000..bb2089935
--- /dev/null
+++ b/filter/source/xsltfilter/OleHandler.cxx
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+
+#include <rtl/ustrbuf.hxx>
+
+#include <package/Inflater.hxx>
+#include <package/Deflater.hxx>
+
+#include <cppuhelper/factory.hxx>
+#include <comphelper/base64.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include "OleHandler.hxx"
+#include <optional>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::embed;
+
+
+namespace XSLT
+{
+ Reference<XStream> OleHandler::createTempFile() {
+ Reference<XStream> tempFile = TempFile::create(m_xContext);
+ OSL_ASSERT(tempFile.is());
+ return tempFile;
+ }
+
+ void OleHandler::ensureCreateRootStorage()
+ {
+ if (m_storage == nullptr || m_rootStream == nullptr)
+ {
+ m_rootStream = createTempFile();
+ Sequence<Any> args{ Any(m_rootStream->getInputStream()) };
+
+ Reference<XNameContainer> cont(
+ Reference<XMultiServiceFactory>(m_xContext->getServiceManager(), UNO_QUERY_THROW)
+ ->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", args), UNO_QUERY);
+ m_storage = cont;
+ }
+ }
+
+ void OleHandler::initRootStorageFromBase64(std::string_view content)
+ {
+ Sequence<sal_Int8> oleData;
+ ::comphelper::Base64::decode(oleData, OStringToOUString(
+ content, RTL_TEXTENCODING_UTF8));
+ m_rootStream = createTempFile();
+ Reference<XOutputStream> xOutput = m_rootStream->getOutputStream();
+ xOutput->writeBytes(oleData);
+ xOutput->flush();
+ //Get the input stream and seek to begin
+ Reference<XSeekable> xSeek(m_rootStream->getInputStream(), UNO_QUERY);
+ xSeek->seek(0);
+
+ //create a com.sun.star.embed.OLESimpleStorage from the temp stream
+ Sequence<Any> args{ Any(xSeek) };
+ Reference<XNameContainer> cont(
+ Reference<XMultiServiceFactory>(m_xContext->getServiceManager(), UNO_QUERY_THROW)
+ ->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", args), UNO_QUERY);
+ m_storage = cont;
+ }
+
+ OString
+ OleHandler::encodeSubStorage(const OUString& streamName)
+ {
+ if (!m_storage || !m_storage->hasByName(streamName))
+ {
+ return "Not Found:";// + streamName;
+ }
+
+ Reference<XInputStream> subStream(m_storage->getByName(streamName), UNO_QUERY);
+ if (!subStream.is())
+ {
+ return "Not Found:";// + streamName;
+ }
+ //The first four byte are the length of the uncompressed data
+ Sequence<sal_Int8> aLength(4);
+ Reference<XSeekable> xSeek(subStream, UNO_QUERY);
+ xSeek->seek(0);
+ //Get the uncompressed length
+ int readbytes = subStream->readBytes(aLength, 4);
+ if (4 != readbytes)
+ {
+ return "Can not read the length.";
+ }
+ sal_Int32 const oleLength = (static_cast<sal_uInt8>(aLength[0]) << 0U)
+ | (static_cast<sal_uInt8>(aLength[1]) << 8U)
+ | (static_cast<sal_uInt8>(aLength[2]) << 16U)
+ | (static_cast<sal_uInt8>(aLength[3]) << 24U);
+ if (oleLength < 0)
+ {
+ return "invalid oleLength";
+ }
+ Sequence<sal_Int8> content(oleLength);
+ //Read all bytes. The compressed length should be less than the uncompressed length
+ readbytes = subStream->readBytes(content, oleLength);
+ if (oleLength < readbytes)
+ {
+ return "oleLength";// +oleLength + readBytes;
+ }
+
+ // Decompress the bytes
+ std::optional< ::ZipUtils::Inflater> decompresser(std::in_place, false);
+ decompresser->setInput(content);
+ Sequence<sal_Int8> result(oleLength);
+ decompresser->doInflateSegment(result, 0, oleLength);
+ decompresser->end();
+ decompresser.reset();
+ //return the base64 string of the uncompressed data
+ OUStringBuffer buf(oleLength);
+ ::comphelper::Base64::encode(buf, result);
+ return OUStringToOString(buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US);
+ }
+
+ void
+ OleHandler::insertByName(const OUString& streamName, std::string_view content)
+ {
+ if ( streamName == "oledata.mso" )
+ {
+ initRootStorageFromBase64(content);
+ }
+ else
+ {
+ ensureCreateRootStorage();
+ insertSubStorage(streamName, content);
+ }
+ }
+
+ OString
+ OleHandler::getByName(const OUString& streamName)
+ {
+ if ( streamName == "oledata.mso" )
+ {
+ //get the length and seek to 0
+ Reference<XSeekable> xSeek (m_rootStream, UNO_QUERY);
+ int oleLength = static_cast<int>(xSeek->getLength());
+ xSeek->seek(0);
+ //read all bytes
+ Reference<XInputStream> xInput = m_rootStream->getInputStream();
+ Sequence<sal_Int8> oledata(oleLength);
+ xInput->readBytes(oledata, oleLength);
+ //return the base64 encoded string
+ OUStringBuffer buf(oleLength);
+ ::comphelper::Base64::encode(buf, oledata);
+ return OUStringToOString(buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US);
+ }
+ return encodeSubStorage(streamName);
+ }
+
+ void
+ OleHandler::insertSubStorage(const OUString& streamName, std::string_view content)
+ {
+ //decode the base64 string
+ Sequence<sal_Int8> oledata;
+ ::comphelper::Base64::decode(oledata,
+ OStringToOUString(content, RTL_TEXTENCODING_ASCII_US));
+ //create a temp stream to write data to
+ Reference<XStream> subStream = createTempFile();
+ Reference<XInputStream> xInput = subStream->getInputStream();
+ Reference<XOutputStream> xOutput = subStream->getOutputStream();
+ //write the length to the temp stream
+ Sequence<sal_Int8> header{
+ static_cast<sal_Int8>((oledata.getLength() >> 0) & 0xFF),
+ static_cast<sal_Int8>((oledata.getLength() >> 8) & 0xFF),
+ static_cast<sal_Int8>((oledata.getLength() >> 16) & 0xFF),
+ static_cast<sal_Int8>((oledata.getLength() >> 24) & 0xFF)
+ };
+ xOutput->writeBytes(header);
+
+ // Compress the bytes
+ Sequence<sal_Int8> output(oledata.getLength());
+ std::optional< ::ZipUtils::Deflater> compresser(std::in_place, sal_Int32(3), false);
+ compresser->setInputSegment(oledata);
+ compresser->finish();
+ int compressedDataLength = compresser->doDeflateSegment(output, oledata.getLength());
+ compresser.reset();
+ //realloc the data length
+ output.realloc(compressedDataLength);
+
+ //write the compressed data to the temp stream
+ xOutput->writeBytes(output);
+ //seek to 0
+ Reference<XSeekable> xSeek(xInput, UNO_QUERY);
+ xSeek->seek(0);
+
+ //insert the temp stream as a sub stream and use an XTransactedObject to commit it immediately
+ Reference<XTransactedObject> xTransact(m_storage, UNO_QUERY);
+ Any entry;
+ entry <<= xInput;
+ m_storage->insertByName(streamName, entry);
+ xTransact->commit();
+ }
+
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/filter/source/xsltfilter/OleHandler.hxx b/filter/source/xsltfilter/OleHandler.hxx
new file mode 100644
index 000000000..d59429aac
--- /dev/null
+++ b/filter/source/xsltfilter/OleHandler.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+#include <cstdio>
+#include <cstring>
+#include <map>
+#include <string_view>
+#include <utility>
+#include <vector>
+#include <iostream>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlIO.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+#include <libxslt/variables.h>
+
+#include <cppuhelper/factory.hxx>
+#include <osl/module.h>
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XStream.hpp>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::io;
+
+namespace XSLT
+{
+ /*
+ * OleHandler provides implementations for the XSLT extension functions used by the WordML 2003 XSLT filters.
+ *
+ * The extension functions takes base64 encoded string representations of embedded OLE objects provided by the XML filter framework,
+ * stores them into a com.sun.star.embed.OLESimpleStorage and retrieves them later as individual base64 OLE objects.
+ *
+ * The implementation is ported from the former Java based implementation XSLTOleExtrater (sic)
+ *
+ * I believe the whole thing should provide round-trip editing of embedded OLE objects.
+ * I'm not sure if it currently does anything meaningful, because the Java implementation seems to be broken both in OOo and LibO.
+ *
+ */
+ class OleHandler
+ {
+ public:
+ OleHandler(css::uno::Reference<XComponentContext> xContext)
+ : m_xContext(std::move(xContext))
+ , m_tcontext(nullptr)
+ {
+ }
+ ~OleHandler()
+ {
+ if (m_tcontext)
+ m_tcontext->_private = nullptr;
+ }
+ void insertByName(const OUString& streamName, std::string_view content);
+ OString getByName(const OUString& streamName);
+ void registercontext(xsltTransformContextPtr context)
+ {
+ assert(context);
+ m_tcontext = context;
+ m_tcontext->_private = this;
+ }
+
+ private:
+ css::uno::Reference<XComponentContext> m_xContext;
+ css::uno::Reference<XNameContainer> m_storage;
+ css::uno::Reference<XStream> m_rootStream;
+ xsltTransformContextPtr m_tcontext;
+
+ void ensureCreateRootStorage();
+ OString encodeSubStorage(const OUString& streamName);
+ void insertSubStorage(const OUString& streamName, std::string_view content);
+ void initRootStorageFromBase64(std::string_view content);
+ css::uno::Reference<XStream> createTempFile();
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/xsltfilter/XSLTFilter.cxx b/filter/source/xsltfilter/XSLTFilter.cxx
new file mode 100644
index 000000000..1052a3816
--- /dev/null
+++ b/filter/source/xsltfilter/XSLTFilter.cxx
@@ -0,0 +1,659 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <sax/tools/documenthandleradapter.hxx>
+
+#include <osl/diagnose.h>
+#include <osl/time.h>
+#include <osl/conditn.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+#include <rtl/ref.hxx>
+
+#include <comphelper/interaction.hxx>
+
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <com/sun/star/uno/Any.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/XFastParser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/XImportFilter.hpp>
+#include <com/sun/star/xml/XImportFilter2.hpp>
+#include <com/sun/star/xml/XExportFilter.hpp>
+
+#include <com/sun/star/util/theMacroExpander.hpp>
+
+#include <com/sun/star/io/Pipe.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XActiveDataSource.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XStreamListener.hpp>
+#include <com/sun/star/util/PathSubstitution.hpp>
+#include <com/sun/star/util/XStringSubstitution.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
+#include <com/sun/star/xml/xslt/XSLT2Transformer.hpp>
+#include <com/sun/star/xml/xslt/XSLTTransformer.hpp>
+#include <utility>
+
+#define TRANSFORMATION_TIMEOUT_SEC 60
+
+using namespace ::cppu;
+using namespace ::osl;
+using namespace ::sax;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::registry;
+using namespace ::com::sun::star::xml;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::task;
+
+namespace XSLT
+{
+ namespace {
+
+ class XSLTFilter;
+ class XSLTFilterStreamListener : public WeakImplHelper<XStreamListener>
+ {
+ public:
+ XSLTFilterStreamListener(XSLTFilter& rParent) : m_rParent(rParent) {}
+
+ // XStreamListener
+ virtual void SAL_CALL
+ error(const Any& a) override;
+ virtual void SAL_CALL
+ closed() override;
+ virtual void SAL_CALL
+ terminated() override;
+ virtual void SAL_CALL
+ started() override;
+ virtual void SAL_CALL
+ disposing(const EventObject& e) override;
+ private:
+ XSLTFilter& m_rParent;
+ };
+
+ /*
+ * XSLTFilter reads flat XML streams from the XML filter framework and passes
+ * them to an XSLT transformation service. XSLT transformation errors are
+ * reported to XSLTFilter.
+ *
+ * Currently, our transformation service is libxslt based, so it
+ * only supports XSLT 1.0. There is a possibility to use XSLT 2.0
+ * supporting service from an extension for a specific filter; the
+ * service must support com.sun.star.xml.xslt.XSLT2Transformer.
+ */
+ class XSLTFilter : public WeakImplHelper<XImportFilter, XImportFilter2, XExportFilter,
+ ExtendedDocumentHandlerAdapter, XServiceInfo>
+ {
+ friend class XSLTFilterStreamListener;
+ private:
+
+ // the UNO ServiceFactory
+ css::uno::Reference<XComponentContext> m_xContext;
+
+ // DocumentHandler interface of the css::xml::sax::Writer service
+ css::uno::Reference<XOutputStream> m_rOutputStream;
+
+ css::uno::Reference<xslt::XXSLTTransformer> m_tcontrol;
+
+ osl::Condition m_cTransformed;
+ bool m_bTerminated;
+ bool m_bError;
+
+ OUString m_aExportBaseUrl;
+
+ OUString
+ rel2abs(const OUString&);
+ OUString
+ expandUrl(const OUString&);
+
+ css::uno::Reference<xslt::XXSLTTransformer> impl_createTransformer(const OUString& rTransformer, const Sequence<Any>& rArgs);
+
+ public:
+
+ // ctor...
+ explicit XSLTFilter(css::uno::Reference<XComponentContext> x);
+
+ // XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override;
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XImportFilter
+ virtual sal_Bool SAL_CALL
+ importer(const Sequence<PropertyValue>& aSourceData, const css::uno::Reference<
+ XDocumentHandler>& xHandler,
+ const Sequence<OUString>& msUserData) override;
+
+ // XImportFilter2
+ virtual sal_Bool SAL_CALL
+ importer(const Sequence<PropertyValue>& aSourceData, const css::uno::Reference<
+ XFastParser>& xFastParser,
+ const Sequence<OUString>& msUserData) override;
+
+ // XExportFilter
+ virtual sal_Bool SAL_CALL
+ exporter(const Sequence<PropertyValue>& aSourceData, const Sequence<
+ OUString>& msUserData) override;
+
+ // XDocumentHandler
+ virtual void SAL_CALL
+ startDocument() override;
+ virtual void SAL_CALL
+ endDocument() override;
+ };
+
+ }
+
+ XSLTFilter::XSLTFilter(css::uno::Reference<XComponentContext> x):
+ m_xContext(std::move(x)), m_bTerminated(false), m_bError(false)
+ {}
+
+ void
+ XSLTFilterStreamListener::disposing(const EventObject&)
+ {
+ }
+
+ // XServiceInfo
+ sal_Bool XSLTFilter::supportsService(const OUString& sServiceName)
+ {
+ return cppu::supportsService(this, sServiceName);
+ }
+ OUString XSLTFilter::getImplementationName()
+ {
+ return "com.sun.star.comp.documentconversion.XSLTFilter";
+ }
+ css::uno::Sequence< OUString > XSLTFilter::getSupportedServiceNames()
+ {
+ return { "com.sun.star.documentconversion.XSLTFilter" };
+ }
+
+ OUString
+ XSLTFilter::expandUrl(const OUString& sUrl)
+ {
+ OUString sExpandedUrl;
+ try
+ {
+ css::uno::Reference<XMacroExpander>
+ xMacroExpander = theMacroExpander::get(m_xContext);
+ sExpandedUrl = xMacroExpander->expandMacros(sUrl);
+ sal_Int32 nPos = sExpandedUrl.indexOf( "vnd.sun.star.expand:" );
+ if (nPos != -1)
+ sExpandedUrl = sExpandedUrl.copy(nPos + 20);
+ }
+ catch (const Exception&)
+ {
+ }
+ return sExpandedUrl;
+ }
+
+ css::uno::Reference<xslt::XXSLTTransformer>
+ XSLTFilter::impl_createTransformer(const OUString& rTransformer, const Sequence<Any>& rArgs)
+ {
+ css::uno::Reference<xslt::XXSLTTransformer> xTransformer;
+
+ // check if the filter needs XSLT-2.0-capable transformer
+ // COMPATIBILITY: libreoffice 3.5/3.6 used to save the impl.
+ // name of the XSLT 2.0 transformation service there, so check
+ // for that too (it is sufficient to check that there is _a_
+ // service name there)
+ if (rTransformer.toBoolean() || rTransformer.startsWith("com.sun."))
+ {
+ try
+ {
+ xTransformer = xslt::XSLT2Transformer::create(m_xContext, rArgs);
+ }
+ catch (const Exception&)
+ {
+ // TODO: put a dialog telling about the need to install
+ // xslt2-transformer extension here
+ SAL_WARN("filter.xslt", "could not create XSLT 2.0 transformer");
+ throw;
+ }
+ }
+
+ // instantiation of XSLT 2.0 transformer service failed, or the
+ // filter does not need it
+ if (!xTransformer.is())
+ {
+ xTransformer = xslt::XSLTTransformer::create(m_xContext, rArgs);
+ }
+
+ return xTransformer;
+ }
+
+ void
+ XSLTFilterStreamListener::started()
+ {
+ m_rParent.m_cTransformed.reset();
+ }
+ void
+ XSLTFilterStreamListener::error(const Any& a)
+ {
+ SAL_WARN("filter.xslt", "XSLTFilter::error was called: " << exceptionToString(a));
+ m_rParent.m_bError = true;
+ m_rParent.m_cTransformed.set();
+ }
+ void
+ XSLTFilterStreamListener::closed()
+ {
+ m_rParent.m_cTransformed.set();
+ }
+ void
+ XSLTFilterStreamListener::terminated()
+ {
+ m_rParent.m_bTerminated = true;
+ m_rParent.m_cTransformed.set();
+ }
+
+ OUString
+ XSLTFilter::rel2abs(const OUString& s)
+ {
+
+ css::uno::Reference<XStringSubstitution>
+ subs(css::util::PathSubstitution::create(m_xContext));
+ OUString aWorkingDir(subs->getSubstituteVariableValue( "$(progurl)" ));
+ INetURLObject aObj(aWorkingDir);
+ aObj.setFinalSlash();
+ bool bWasAbsolute;
+ INetURLObject aURL = aObj.smartRel2Abs(s, bWasAbsolute, false,
+ INetURLObject::EncodeMechanism::WasEncoded, RTL_TEXTENCODING_UTF8, true);
+ return aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ }
+
+ sal_Bool
+ XSLTFilter::importer(const Sequence<PropertyValue>& aSourceData,
+ const css::uno::Reference<XDocumentHandler>& xHandler, const Sequence<
+ OUString>& msUserData)
+ {
+ if (msUserData.getLength() < 5)
+ return false;
+
+ OUString udStyleSheet = rel2abs(msUserData[4]);
+
+ // get information from media descriptor
+ // the input stream that represents the imported file
+ // is most important here since we need to supply it to
+ // the sax parser that drives the supplied document handler
+ OUString aName, aURL;
+ css::uno::Reference<XInputStream> xInputStream;
+ css::uno::Reference<XInteractionHandler> xInterActionHandler;
+ for (const auto& sourceDataItem : aSourceData)
+ {
+ aName = sourceDataItem.Name;
+ Any value = sourceDataItem.Value;
+ if ( aName == "InputStream" )
+ value >>= xInputStream;
+ else if ( aName == "URL" )
+ value >>= aURL;
+ else if ( aName == "InteractionHandler" )
+ value >>= xInterActionHandler;
+ }
+ OSL_ASSERT(xInputStream.is());
+ if (!xInputStream.is())
+ return false;
+
+ // create transformer
+ Sequence<Any> args{ Any(NamedValue("StylesheetURL", Any(expandUrl(udStyleSheet)))),
+ Any(NamedValue("SourceURL", Any(aURL))),
+ Any(NamedValue("SourceBaseURL", Any(INetURLObject(aURL).getBase()))) };
+ m_tcontrol = impl_createTransformer(msUserData[1], args);
+
+ OSL_ASSERT(xHandler.is());
+ OSL_ASSERT(xInputStream.is());
+ OSL_ASSERT(m_tcontrol.is());
+ if (xHandler.is() && xInputStream.is() && m_tcontrol.is())
+ {
+ try
+ {
+ css::uno::Reference<css::io::XSeekable> xSeek(xInputStream, UNO_QUERY);
+ if (xSeek.is())
+ xSeek->seek(0);
+
+ // we want to be notified when the processing is done...
+ m_tcontrol->addListener(new XSLTFilterStreamListener(*this));
+
+ // connect input to transformer
+ m_tcontrol->setInputStream(xInputStream);
+
+ // create pipe
+ css::uno::Reference<XOutputStream> pipeout =
+ Pipe::create(m_xContext);
+ css::uno::Reference<XInputStream> pipein(pipeout, UNO_QUERY);
+
+ //connect transformer to pipe
+ m_tcontrol->setOutputStream(pipeout);
+
+ // connect pipe to sax parser
+ InputSource aInput;
+ aInput.sSystemId = aURL;
+ aInput.sPublicId = aURL;
+ aInput.aInputStream = pipein;
+
+ css::uno::Reference< css::xml::sax::XFastParser > xFastParser = dynamic_cast<
+ css::xml::sax::XFastParser* >( xHandler.get() );
+
+ // transform
+ m_tcontrol->start();
+ TimeValue timeout = { TRANSFORMATION_TIMEOUT_SEC, 0};
+ osl::Condition::Result result(m_cTransformed.wait(&timeout));
+ while (osl::Condition::result_timeout == result) {
+ if (xInterActionHandler.is()) {
+ Sequence<Any> excArgs(0);
+ css::ucb::InteractiveAugmentedIOException exc(
+ "Timeout!",
+ static_cast< OWeakObject * >( this ),
+ InteractionClassification_ERROR,
+ css::ucb::IOErrorCode_GENERAL,
+ excArgs);
+ Any r;
+ r <<= exc;
+ rtl::Reference<::comphelper::OInteractionRequest> pRequest = new ::comphelper::OInteractionRequest(r);
+ rtl::Reference<::comphelper::OInteractionRetry> pRetry = new ::comphelper::OInteractionRetry;
+ rtl::Reference<::comphelper::OInteractionAbort> pAbort = new ::comphelper::OInteractionAbort;
+ pRequest->addContinuation(pRetry);
+ pRequest->addContinuation(pAbort);
+ xInterActionHandler->handle(pRequest);
+ if (pAbort->wasSelected()) {
+ m_bError = true;
+ m_cTransformed.set();
+ }
+ }
+ result = m_cTransformed.wait(&timeout);
+ };
+ if (!m_bError) {
+ if( xFastParser.is() )
+ xFastParser->parseStream( aInput );
+ else
+ {
+ // create SAX parser that will read the document file
+ // and provide events to xHandler passed to this call
+ css::uno::Reference<XParser> xSaxParser = Parser::create(m_xContext);
+ // set doc handler
+ xSaxParser->setDocumentHandler(xHandler);
+ xSaxParser->parseStream( aInput );
+ }
+ }
+ m_tcontrol->terminate();
+ return !m_bError;
+ }
+ catch( const Exception& )
+ {
+ // something went wrong
+ TOOLS_WARN_EXCEPTION("filter.xslt", "");
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ sal_Bool
+ XSLTFilter::importer(const Sequence<PropertyValue>& aSourceData,
+ const css::uno::Reference<XFastParser>& xFastParser, const Sequence<
+ OUString>& msUserData)
+ {
+ if (msUserData.getLength() < 5)
+ return false;
+
+ OUString udStyleSheet = rel2abs(msUserData[4]);
+
+ // get information from media descriptor
+ // the input stream that represents the imported file
+ // is most important here since we need to supply it to
+ // the sax parser that drives the supplied document handler
+ sal_Int32 nLength = aSourceData.getLength();
+ OUString aName, aURL;
+ css::uno::Reference<XInputStream> xInputStream;
+ css::uno::Reference<XInteractionHandler> xInterActionHandler;
+ for (sal_Int32 i = 0; i < nLength; i++)
+ {
+ aName = aSourceData[i].Name;
+ Any value = aSourceData[i].Value;
+ if ( aName == "InputStream" )
+ value >>= xInputStream;
+ else if ( aName == "URL" )
+ value >>= aURL;
+ else if ( aName == "InteractionHandler" )
+ value >>= xInterActionHandler;
+ }
+ OSL_ASSERT(xInputStream.is());
+ if (!xInputStream.is())
+ return false;
+
+ // create transformer
+ Sequence<Any> args{ Any(NamedValue("StylesheetURL", Any(expandUrl(udStyleSheet)))),
+ Any(NamedValue("SourceURL", Any(aURL))),
+ Any(NamedValue("SourceBaseURL", Any(INetURLObject(aURL).getBase()))) };
+ m_tcontrol = impl_createTransformer(msUserData[1], args);
+
+ assert(xFastParser.is());
+ OSL_ASSERT(xInputStream.is());
+ OSL_ASSERT(m_tcontrol.is());
+ if (xFastParser.is() && xInputStream.is() && m_tcontrol.is())
+ {
+ try
+ {
+ css::uno::Reference<css::io::XSeekable> xSeek(xInputStream, UNO_QUERY);
+ if (xSeek.is())
+ xSeek->seek(0);
+
+ // we want to be notified when the processing is done...
+ m_tcontrol->addListener(new XSLTFilterStreamListener(*this));
+
+ // connect input to transformer
+ m_tcontrol->setInputStream(xInputStream);
+
+ // create pipe
+ css::uno::Reference<XOutputStream> pipeout =
+ Pipe::create(m_xContext);
+ css::uno::Reference<XInputStream> pipein(pipeout, UNO_QUERY);
+
+ //connect transformer to pipe
+ m_tcontrol->setOutputStream(pipeout);
+
+ // connect pipe to sax parser
+ InputSource aInput;
+ aInput.sSystemId = aURL;
+ aInput.sPublicId = aURL;
+ aInput.aInputStream = pipein;
+
+ // transform
+ m_tcontrol->start();
+ TimeValue timeout = { TRANSFORMATION_TIMEOUT_SEC, 0};
+ osl::Condition::Result result(m_cTransformed.wait(&timeout));
+ while (osl::Condition::result_timeout == result) {
+ if (xInterActionHandler.is()) {
+ Sequence<Any> excArgs(0);
+ css::ucb::InteractiveAugmentedIOException exc(
+ "Timeout!",
+ static_cast< OWeakObject * >( this ),
+ InteractionClassification_ERROR,
+ css::ucb::IOErrorCode_GENERAL,
+ excArgs);
+ Any r;
+ r <<= exc;
+ rtl::Reference<::comphelper::OInteractionRequest> pRequest = new ::comphelper::OInteractionRequest(r);
+ rtl::Reference<::comphelper::OInteractionRetry> pRetry = new ::comphelper::OInteractionRetry;
+ rtl::Reference<::comphelper::OInteractionAbort> pAbort = new ::comphelper::OInteractionAbort;
+ pRequest->addContinuation(pRetry);
+ pRequest->addContinuation(pAbort);
+ xInterActionHandler->handle(pRequest);
+ if (pAbort->wasSelected()) {
+ m_bError = true;
+ m_cTransformed.set();
+ }
+ }
+ result = m_cTransformed.wait(&timeout);
+ };
+ if (!m_bError)
+ xFastParser->parseStream( aInput );
+ m_tcontrol->terminate();
+ return !m_bError;
+ }
+ catch( const Exception& )
+ {
+ // something went wrong
+ TOOLS_WARN_EXCEPTION("filter.xslt", "");
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ sal_Bool
+ XSLTFilter::exporter(const Sequence<PropertyValue>& aSourceData,
+ const Sequence<OUString>& msUserData)
+ {
+ if (msUserData.getLength() < 6)
+ return false;
+
+ // get interesting values from user data
+ OUString udStyleSheet = rel2abs(msUserData[5]);
+
+ // read source data
+ // we are especially interested in the output stream
+ // since that is where our xml-writer will push the data
+ // from its data-source interface
+ OUString aName, sURL;
+ OUString aDoctypePublic;
+ // css::uno::Reference<XOutputStream> rOutputStream;
+ sal_Int32 nLength = aSourceData.getLength();
+ for (sal_Int32 i = 0; i < nLength; i++)
+ {
+ aName = aSourceData[i].Name;
+ if ( aName == "DocType_Public" )
+ aSourceData[i].Value >>= aDoctypePublic;
+ else if ( aName == "OutputStream" )
+ aSourceData[i].Value >>= m_rOutputStream;
+ else if ( aName == "URL" )
+ aSourceData[i].Value >>= sURL;
+ }
+
+ if (!getDelegate().is())
+ {
+ // get the document writer
+ setDelegate(css::uno::Reference<XExtendedDocumentHandler>(
+ Writer::create(m_xContext),
+ UNO_QUERY_THROW));
+ }
+
+ // create transformer
+ INetURLObject ineturl(sURL);
+ ineturl.removeSegment();
+ m_aExportBaseUrl = ineturl.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ Sequence<Any> args{ Any(NamedValue("StylesheetURL", Any(expandUrl(udStyleSheet)))),
+ Any(NamedValue("TargetURL", Any(sURL))),
+ Any(NamedValue("DoctypePublic", Any(aDoctypePublic))),
+ Any(NamedValue("TargetBaseURL", Any(m_aExportBaseUrl))) };
+ m_tcontrol = impl_createTransformer(msUserData[1], args);
+
+ OSL_ASSERT(m_rOutputStream.is());
+ OSL_ASSERT(m_tcontrol.is());
+ if (m_tcontrol.is() && m_rOutputStream.is())
+ {
+ // we want to be notified when the processing is done...
+ m_tcontrol->addListener(new XSLTFilterStreamListener(*this));
+
+ // create pipe
+ css::uno::Reference<XOutputStream> pipeout =
+ Pipe::create(m_xContext);
+ css::uno::Reference<XInputStream> pipein(pipeout, UNO_QUERY);
+
+ // connect sax writer to pipe
+ css::uno::Reference<XActiveDataSource> xmlsource(getDelegate(),
+ UNO_QUERY);
+ xmlsource->setOutputStream(pipeout);
+
+ // connect pipe to transformer
+ m_tcontrol->setInputStream(pipein);
+
+ // connect transformer to output
+ m_tcontrol->setOutputStream(m_rOutputStream);
+
+ // we will start receiving events after returning 'true'.
+ // we will start the transformation as soon as we receive the startDocument
+ // event.
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // for the DocumentHandler implementation, we just proxy the
+ // events to the XML writer that we created upon the output stream
+ // that was provided by the XMLFilterAdapter
+ void
+ XSLTFilter::startDocument()
+ {
+ ExtendedDocumentHandlerAdapter::startDocument();
+ m_tcontrol->start();
+ }
+
+ void
+ XSLTFilter::endDocument()
+ {
+ ExtendedDocumentHandlerAdapter::endDocument();
+ // wait for the transformer to finish
+ m_cTransformed.wait();
+ m_tcontrol->terminate();
+ if (m_bError || m_bTerminated)
+ throw RuntimeException();
+ }
+
+
+}
+
+// Component management
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+filter_XSLTFilter_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new XSLT::XSLTFilter(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/xsltfilter/xsltfilter.component b/filter/source/xsltfilter/xsltfilter.component
new file mode 100644
index 000000000..e4298a7a0
--- /dev/null
+++ b/filter/source/xsltfilter/xsltfilter.component
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.documentconversion.XSLTFilter"
+ constructor="filter_XSLTFilter_get_implementation">
+ <service name="com.sun.star.documentconversion.XSLTFilter"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.documentconversion.LibXSLTTransformer"
+ constructor="filter_LibXSLTTransformer_get_implementation">
+ <service name="com.sun.star.xml.xslt.XSLTTransformer"/>
+ </implementation>
+</component>