diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /filter/source/xsltfilter | |
parent | Initial commit. (diff) | |
download | libreoffice-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.cxx | 571 | ||||
-rw-r--r-- | filter/source/xsltfilter/LibXSLTTransformer.hxx | 185 | ||||
-rw-r--r-- | filter/source/xsltfilter/OleHandler.cxx | 214 | ||||
-rw-r--r-- | filter/source/xsltfilter/OleHandler.hxx | 93 | ||||
-rw-r--r-- | filter/source/xsltfilter/XSLTFilter.cxx | 659 | ||||
-rw-r--r-- | filter/source/xsltfilter/xsltfilter.component | 30 |
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> |