summaryrefslogtreecommitdiffstats
path: root/writerperfect/source/common
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--writerperfect/source/common/DirectoryStream.cxx222
-rw-r--r--writerperfect/source/common/DocumentHandler.cxx179
-rw-r--r--writerperfect/source/common/WPFTEncodingDialog.cxx121
-rw-r--r--writerperfect/source/common/WPXSvInputStream.cxx785
4 files changed, 1307 insertions, 0 deletions
diff --git a/writerperfect/source/common/DirectoryStream.cxx b/writerperfect/source/common/DirectoryStream.cxx
new file mode 100644
index 0000000000..9664fbee97
--- /dev/null
+++ b/writerperfect/source/common/DirectoryStream.cxx
@@ -0,0 +1,222 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* writerperfect
+ * Version: MPL 2.0 / LGPLv2.1+
+ *
+ * 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/.
+ *
+ * Major Contributor(s):
+ * Copyright (C) 2007 Fridrich Strba (fridrich.strba@bluewin.ch)
+ *
+ * For minor contributions see the git repository.
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of the GNU Lesser General Public License Version 2.1 or later
+ * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
+ * applicable instead of those above.
+ *
+ * For further information visit http://libwpd.sourceforge.net
+ */
+
+#include <memory>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <comphelper/processfactory.hxx>
+
+#include <rtl/ustring.hxx>
+
+#include <ucbhelper/content.hxx>
+
+#include <DirectoryStream.hxx>
+#include <WPXSvInputStream.hxx>
+#include <utility>
+
+namespace io = com::sun::star::io;
+namespace sdbc = com::sun::star::sdbc;
+namespace ucb = com::sun::star::ucb;
+namespace uno = com::sun::star::uno;
+
+namespace writerperfect
+{
+namespace
+{
+uno::Reference<io::XInputStream> findStream(ucbhelper::Content& rContent, std::u16string_view rName)
+{
+ uno::Reference<io::XInputStream> xInputStream;
+
+ uno::Sequence<OUString> lPropNames{ "Title" };
+ try
+ {
+ const uno::Reference<sdbc::XResultSet> xResultSet(
+ rContent.createCursor(lPropNames, ucbhelper::INCLUDE_DOCUMENTS_ONLY));
+ if (xResultSet->first())
+ {
+ const uno::Reference<ucb::XContentAccess> xContentAccess(xResultSet,
+ uno::UNO_QUERY_THROW);
+ const uno::Reference<sdbc::XRow> xRow(xResultSet, uno::UNO_QUERY_THROW);
+ do
+ {
+ const OUString aTitle(xRow->getString(1));
+ if (aTitle == rName)
+ {
+ const uno::Reference<ucb::XContent> xSubContent(xContentAccess->queryContent());
+ ucbhelper::Content aSubContent(xSubContent,
+ uno::Reference<ucb::XCommandEnvironment>(),
+ comphelper::getProcessComponentContext());
+ xInputStream = aSubContent.openStream();
+ break;
+ }
+ } while (xResultSet->next());
+ }
+ }
+ catch (const uno::RuntimeException&)
+ {
+ // ignore
+ }
+ catch (const uno::Exception&)
+ {
+ // ignore
+ }
+
+ return xInputStream;
+}
+}
+
+struct DirectoryStream::Impl
+{
+ explicit Impl(uno::Reference<ucb::XContent> xContent);
+
+ uno::Reference<ucb::XContent> xContent;
+};
+
+DirectoryStream::Impl::Impl(uno::Reference<ucb::XContent> _xContent)
+ : xContent(std::move(_xContent))
+{
+}
+
+DirectoryStream::DirectoryStream(const css::uno::Reference<css::ucb::XContent>& xContent)
+ : m_pImpl(isDirectory(xContent) ? new Impl(xContent) : nullptr)
+{
+}
+
+DirectoryStream::~DirectoryStream() {}
+
+bool DirectoryStream::isDirectory(const css::uno::Reference<css::ucb::XContent>& xContent)
+{
+ try
+ {
+ if (!xContent.is())
+ return false;
+
+ ucbhelper::Content aContent(xContent, uno::Reference<ucb::XCommandEnvironment>(),
+ comphelper::getProcessComponentContext());
+ return aContent.isFolder();
+ }
+ catch (...)
+ {
+ return false;
+ }
+}
+
+std::unique_ptr<DirectoryStream>
+DirectoryStream::createForParent(const css::uno::Reference<css::ucb::XContent>& xContent)
+{
+ try
+ {
+ if (!xContent.is())
+ return nullptr;
+
+ std::unique_ptr<DirectoryStream> pDir;
+
+ const uno::Reference<css::container::XChild> xChild(xContent, uno::UNO_QUERY);
+ if (xChild.is())
+ {
+ const uno::Reference<ucb::XContent> xDirContent(xChild->getParent(), uno::UNO_QUERY);
+ if (xDirContent.is())
+ {
+ pDir = std::make_unique<DirectoryStream>(xDirContent);
+ if (!pDir->isStructured())
+ pDir.reset();
+ }
+ }
+
+ return pDir;
+ }
+ catch (...)
+ {
+ return nullptr;
+ }
+}
+
+css::uno::Reference<css::ucb::XContent> DirectoryStream::getContent() const
+{
+ if (!m_pImpl)
+ return css::uno::Reference<css::ucb::XContent>();
+ return m_pImpl->xContent;
+}
+
+bool DirectoryStream::isStructured() { return m_pImpl != nullptr; }
+
+unsigned DirectoryStream::subStreamCount()
+{
+ // TODO: implement me
+ return 0;
+}
+
+const char* DirectoryStream::subStreamName(unsigned /* id */)
+{
+ // TODO: implement me
+ return nullptr;
+}
+
+bool DirectoryStream::existsSubStream(const char* /* name */)
+{
+ // TODO: implement me
+ return false;
+}
+
+librevenge::RVNGInputStream* DirectoryStream::getSubStreamByName(const char* const pName)
+{
+ if (!m_pImpl)
+ return nullptr;
+
+ ucbhelper::Content aContent(m_pImpl->xContent, uno::Reference<ucb::XCommandEnvironment>(),
+ comphelper::getProcessComponentContext());
+ const uno::Reference<io::XInputStream> xInputStream(
+ findStream(aContent, OUString::createFromAscii(pName)));
+ if (xInputStream.is())
+ return new WPXSvInputStream(xInputStream);
+
+ return nullptr;
+}
+
+librevenge::RVNGInputStream* DirectoryStream::getSubStreamById(unsigned /* id */)
+{
+ // TODO: implement me
+ return nullptr;
+}
+
+const unsigned char* DirectoryStream::read(unsigned long, unsigned long& nNumBytesRead)
+{
+ nNumBytesRead = 0;
+ return nullptr;
+}
+
+int DirectoryStream::seek(long, librevenge::RVNG_SEEK_TYPE) { return -1; }
+
+long DirectoryStream::tell() { return 0; }
+
+bool DirectoryStream::isEnd() { return true; }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerperfect/source/common/DocumentHandler.cxx b/writerperfect/source/common/DocumentHandler.cxx
new file mode 100644
index 0000000000..28730ab5ee
--- /dev/null
+++ b/writerperfect/source/common/DocumentHandler.cxx
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * For further information visit http://libwpd.sourceforge.net
+ */
+
+#include <DocumentHandler.hxx>
+
+#include <string.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/XAttributeList.hpp>
+#include <comphelper/attributelist.hxx>
+#include <xmloff/xmlimp.hxx>
+
+namespace writerperfect
+{
+const unsigned char librvng_utf8_skip_data[256]
+ = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1 };
+
+static const char* librvng_utf8_next_char(const char* p)
+{
+ return p + librvng_utf8_skip_data[*reinterpret_cast<unsigned char const*>(p)];
+}
+
+static void unescapeXML(const char* s, const unsigned long sz, librevenge::RVNGString& res)
+{
+ const char* p = s;
+ const char* const end = p + sz;
+ while (p != end)
+ {
+ const char* const next = librvng_utf8_next_char(p);
+ if (next > end)
+ {
+ // oops, the string is invalid
+ break;
+ }
+ if (p + 4 <= end && p + 1 == next && *p == '&')
+ {
+ // look for &amp; , &lt; , &gt; , &apos; , &quot;
+ bool escapedChar = false;
+ switch (*(p + 1))
+ {
+ case 'a':
+ if (p + 5 <= end && strncmp(p, "&amp;", 5) == 0)
+ {
+ res.append('&');
+ p += 5;
+ escapedChar = true;
+ }
+ else if (p + 6 <= end && strncmp(p, "&apos;", 6) == 0)
+ {
+ res.append('\'');
+ p += 6;
+ escapedChar = true;
+ }
+ break;
+ case 'g':
+ if (strncmp(p, "&gt;", 4) == 0)
+ {
+ res.append('>');
+ p += 4;
+ escapedChar = true;
+ }
+ break;
+ case 'l':
+ if (strncmp(p, "&lt;", 4) == 0)
+ {
+ res.append('<');
+ p += 4;
+ escapedChar = true;
+ }
+ break;
+ case 'q':
+ if (p + 6 <= end && strncmp(p, "&quot;", 6) == 0)
+ {
+ res.append('"');
+ p += 6;
+ escapedChar = true;
+ }
+ break;
+ default:
+ break;
+ }
+ if (escapedChar)
+ continue;
+ }
+
+ while (p != next)
+ {
+ res.append(*p);
+ ++p;
+ }
+ p = next;
+ }
+}
+
+using com::sun::star::uno::Reference;
+using com::sun::star::xml::sax::XDocumentHandler;
+
+DocumentHandler::DocumentHandler(Reference<XDocumentHandler> const& xHandler)
+ : mxHandler(xHandler)
+{
+ if (SvXMLImport* pFastHandler = dynamic_cast<SvXMLImport*>(mxHandler.get()))
+ mxHandler.set(new SvXMLLegacyToFastDocHandler(pFastHandler));
+}
+
+void DocumentHandler::startDocument() { mxHandler->startDocument(); }
+
+void DocumentHandler::endDocument() { mxHandler->endDocument(); }
+
+void DocumentHandler::startElement(const char* psName,
+ const librevenge::RVNGPropertyList& xPropList)
+{
+ rtl::Reference<comphelper::AttributeList> pAttrList = new comphelper::AttributeList();
+ librevenge::RVNGPropertyList::Iter i(xPropList);
+ for (i.rewind(); i.next();)
+ {
+ // filter out librevenge elements
+ if (strncmp(i.key(), "librevenge", 10) != 0)
+ {
+ size_t keyLength = strlen(i.key());
+ OUString sName(i.key(), keyLength, RTL_TEXTENCODING_UTF8);
+ OUString sValue(i()->getStr().cstr(), i()->getStr().len(), RTL_TEXTENCODING_UTF8);
+
+ // libodfgen xml-encodes some attribute's value, so check if the value is encoded or not
+ for (int j = 0; j < 9; ++j)
+ {
+ // list of the encoded attributes followed by their lengths
+ static char const* listEncoded[9]
+ = { "draw:name", "svg:font-family", "style:condition",
+ "style:num-prefix", "style:num-suffix", "table:formula",
+ "text:bullet-char", "text:label", "xlink:href" };
+ static size_t const listEncodedLength[9] = { 9, 15, 15, 16, 16, 13, 16, 10, 10 };
+ if (keyLength == listEncodedLength[j]
+ && strncmp(i.key(), listEncoded[j], keyLength) == 0)
+ {
+ librevenge::RVNGString decodedValue("");
+ unescapeXML(i()->getStr().cstr(),
+ static_cast<unsigned long>(i()->getStr().len()), decodedValue);
+ sValue
+ = OUString(decodedValue.cstr(), decodedValue.len(), RTL_TEXTENCODING_UTF8);
+ break;
+ }
+ }
+ pAttrList->AddAttribute(sName, sValue);
+ }
+ }
+
+ OUString sElementName(psName, strlen(psName), RTL_TEXTENCODING_UTF8);
+ mxHandler->startElement(sElementName, pAttrList);
+}
+
+void DocumentHandler::endElement(const char* psName)
+{
+ OUString sElementName(psName, strlen(psName), RTL_TEXTENCODING_UTF8);
+ mxHandler->endElement(sElementName);
+}
+
+void DocumentHandler::characters(const librevenge::RVNGString& sCharacters)
+{
+ OUString sCharU16(sCharacters.cstr(), strlen(sCharacters.cstr()), RTL_TEXTENCODING_UTF8);
+ mxHandler->characters(sCharU16);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerperfect/source/common/WPFTEncodingDialog.cxx b/writerperfect/source/common/WPFTEncodingDialog.cxx
new file mode 100644
index 0000000000..d9fc0d9c19
--- /dev/null
+++ b/writerperfect/source/common/WPFTEncodingDialog.cxx
@@ -0,0 +1,121 @@
+/* -*- 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 <sal/config.h>
+
+#include <cstddef>
+#include <string_view>
+#include <utility>
+
+#include <WPFTEncodingDialog.hxx>
+
+namespace writerperfect
+{
+namespace
+{
+std::pair<std::u16string_view, std::u16string_view> const s_encodings[]
+ = { { u"MacArabic", u"Arabic (Apple Macintosh)" },
+ { u"CP864", u"Arabic (DOS/OS2-864)" },
+ { u"CP1006", u"Arabic (IBM-1006)" },
+ { u"CP1256", u"Arabic (Windows-1256)" },
+ { u"CP775", u"Baltic (DOS/OS2-775)" },
+ { u"CP1257", u"Baltic (Windows-1257)" },
+ { u"MacCeltic", u"Celtic (Apple Macintosh)" },
+ { u"MacCyrillic", u"Cyrillic (Apple Macintosh)" },
+ { u"CP855", u"Cyrillic (DOS/OS2-855)" },
+ { u"CP866", u"Cyrillic (DOS/OS2-866/Russian)" },
+ { u"CP1251", u"Cyrillic (Windows-1251)" },
+ { u"MacCEurope", u"Eastern Europe (Apple Macintosh)" },
+ { u"MacCroatian", u"Eastern Europe (Apple Macintosh/Croatian)" },
+ { u"MacRomanian", u"Eastern Europe (Apple Macintosh/Romanian)" },
+ { u"CP852", u"Eastern Europe (DOS/OS2-852)" },
+ { u"CP1250", u"Eastern Europe (Windows-1250/WinLatin 2)" },
+ { u"MacGreek", u"Greek (Apple Macintosh)" },
+ { u"CP737", u"Greek (DOS/OS2-737)" },
+ { u"CP869", u"Greek (DOS/OS2-869/Greek-2)" },
+ { u"CP875", u"Greek (DOS/OS2-875)" },
+ { u"CP1253", u"Greek (Windows-1253)" },
+ { u"MacHebrew", u"Hebrew (Apple Macintosh)" },
+ { u"CP424", u"Hebrew (DOS/OS2-424)" },
+ { u"CP856", u"Hebrew (DOS/OS2-856)" },
+ { u"CP862", u"Hebrew (DOS/OS2-862)" },
+ { u"CP1255", u"Hebrew (Windows-1255)" },
+ { u"CP500", u"International (DOS/OS2-500)" },
+ { u"CP932", u"Japanese (Windows-932)" },
+ { u"MacThai", u"Thai (Apple Macintosh)" },
+ { u"CP874", u"Thai (DOS/OS2-874)" },
+ { u"CP950", u"Traditional Chinese (Windows-950)" },
+ { u"MacTurkish", u"Turkish (Apple Macintosh)" },
+ { u"CP857", u"Turkish (DOS/OS2-857)" },
+ { u"CP1026", u"Turkish (DOS/OS2-1026)" },
+ { u"CP1254", u"Turkish (Windows-1254)" },
+ { u"CP1258", u"Vietnamese (Windows-1258)" },
+ { u"MacRoman", u"Western Europe (Apple Macintosh)" },
+ { u"MacIceland", u"Western Europe (Apple Macintosh/Icelandic)" },
+ { u"CP037", u"Western Europe (DOS/OS2-037/US-Canada)" },
+ { u"CP437", u"Western Europe (DOS/OS2-437/US)" },
+ { u"CP850", u"Western Europe (DOS/OS2-850)" },
+ { u"CP860", u"Western Europe (DOS/OS2-860/Portuguese)" },
+ { u"CP861", u"Western Europe (DOS/OS2-861/Icelandic)" },
+ { u"CP863", u"Western Europe (DOS/OS2-863/French)" },
+ { u"CP865", u"Western Europe (DOS/OS2-865/Nordic)" },
+ { u"CP1252", u"Western Europe (Windows-1252/WinLatin 1)" } };
+
+std::size_t const numEncodings = SAL_N_ELEMENTS(s_encodings);
+
+void insertEncodings(weld::ComboBox& box)
+{
+ for (std::size_t i = 0; i < numEncodings; ++i)
+ box.append(OUString(s_encodings[i].first), OUString(s_encodings[i].second));
+}
+
+void selectEncoding(weld::ComboBox& box, const OUString& encoding) { box.set_active_id(encoding); }
+
+OUString getEncoding(const weld::ComboBox& box) { return box.get_active_id(); }
+}
+
+WPFTEncodingDialog::WPFTEncodingDialog(weld::Window* pParent, const OUString& title,
+ const OUString& encoding)
+ : GenericDialogController(pParent, "writerperfect/ui/wpftencodingdialog.ui",
+ "WPFTEncodingDialog")
+ , m_userHasCancelled(false)
+ , m_xLbCharset(m_xBuilder->weld_combo_box("comboboxtext"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+{
+ m_xBtnCancel->connect_clicked(LINK(this, WPFTEncodingDialog, CancelHdl));
+
+ insertEncodings(*m_xLbCharset);
+ m_xLbCharset->make_sorted();
+ selectEncoding(*m_xLbCharset, encoding);
+
+ m_xDialog->set_title(title);
+}
+
+WPFTEncodingDialog::~WPFTEncodingDialog() {}
+
+OUString WPFTEncodingDialog::GetEncoding() const { return getEncoding(*m_xLbCharset); }
+
+IMPL_LINK_NOARG(WPFTEncodingDialog, CancelHdl, weld::Button&, void)
+{
+ m_userHasCancelled = true;
+ m_xDialog->response(RET_CANCEL);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerperfect/source/common/WPXSvInputStream.cxx b/writerperfect/source/common/WPXSvInputStream.cxx
new file mode 100644
index 0000000000..dee82500de
--- /dev/null
+++ b/writerperfect/source/common/WPXSvInputStream.cxx
@@ -0,0 +1,785 @@
+/* -*- 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 <WPXSvInputStream.hxx>
+
+#include <com/sun/star/packages/zip/XZipFileAccess2.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/seekableinput.hxx>
+#include <o3tl/safeint.hxx>
+#include <rtl/string.hxx>
+#include <sal/log.hxx>
+
+#include <unotools/streamwrap.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+
+#include <climits>
+#include <limits>
+#include <memory>
+#include <string_view>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+namespace writerperfect
+{
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+
+namespace container = com::sun::star::container;
+namespace packages = com::sun::star::packages;
+
+namespace
+{
+class PositionHolder
+{
+public:
+ explicit PositionHolder(const Reference<XSeekable>& rxSeekable);
+ ~PositionHolder();
+ PositionHolder(const PositionHolder&) = delete;
+ PositionHolder& operator=(const PositionHolder&) = delete;
+
+private:
+ const Reference<XSeekable> mxSeekable;
+ const sal_uInt64 mnPosition;
+};
+} // anonymous namespace
+
+PositionHolder::PositionHolder(const Reference<XSeekable>& rxSeekable)
+ : mxSeekable(rxSeekable)
+ , mnPosition(rxSeekable->getPosition())
+{
+}
+
+PositionHolder::~PositionHolder()
+{
+ try
+ {
+ mxSeekable->seek(mnPosition);
+ }
+ catch (...)
+ {
+ }
+}
+
+static OUString lcl_normalizeSubStreamPath(const OUString& rPath)
+{
+ // accept paths which begin by '/'
+ // TODO: maybe this should do a full normalization
+ if (rPath.startsWith("/") && rPath.getLength() >= 2)
+ return rPath.copy(1);
+ return rPath;
+}
+
+static OUString concatPath(std::u16string_view lhs, const OUString& rhs)
+{
+ if (lhs.empty())
+ return rhs;
+ return OUString::Concat(lhs) + "/" + rhs;
+}
+
+namespace
+{
+struct OLEStreamData
+{
+ OLEStreamData(OString aName, OString rvngName)
+ : name(std::move(aName))
+ , RVNGname(std::move(rvngName))
+ {
+ }
+
+ tools::SvRef<SotStorageStream> stream;
+
+ /** Name of the stream.
+ *
+ * This is not @c OUString, because we need to be able to
+ * produce const char* from it.
+ */
+ OString name;
+ /** librevenge name of the stream.
+ *
+ * This is not @c OUString, because we need to be able to
+ * produce const char* from it.
+ */
+ OString RVNGname;
+};
+} // anonymous namespace
+
+typedef std::unordered_map<OUString, std::size_t> NameMap_t;
+typedef std::unordered_map<OUString, tools::SvRef<SotStorage>> OLEStorageMap_t;
+
+/** Representation of an OLE2 storage.
+ *
+ * This class tries to bring a bit of sanity to use of SotStorage with
+ * respect to the needs of @c librevenge::RVNGInputStream API. It
+ * holds all nested storages for the whole lifetime (more precisely,
+ * since initialization, which is performed by calling @c
+ * initialize()), thus ensuring that no created stream is destroyed
+ * just because its parent storage went out of scope. It also holds a
+ * bidirectional map of stream names to their indexes (index of a
+ * stream is determined by deep-first traversal), which is also
+ * populated during initialization (member variables @c maStreams and
+ * @c maNameMap).
+ *
+ * Streams are created on demand (and saved, for the same reason as
+ * storages).
+ */
+struct OLEStorageImpl
+{
+ OLEStorageImpl();
+
+ void initialize(std::unique_ptr<SvStream> pStream);
+
+ tools::SvRef<SotStorageStream> getStream(const OUString& rPath);
+ tools::SvRef<SotStorageStream> const& getStream(std::size_t nId);
+
+private:
+ void traverse(const tools::SvRef<SotStorage>& rStorage, std::u16string_view rPath);
+
+ tools::SvRef<SotStorageStream> createStream(const OUString& rPath);
+
+public:
+ tools::SvRef<SotStorage> mxRootStorage; //< root storage of the OLE2
+ OLEStorageMap_t maStorageMap; //< map of all sub storages by name
+ ::std::vector<OLEStreamData> maStreams; //< list of streams and their names
+ NameMap_t maNameMap; //< map of stream names to indexes (into @c maStreams)
+ bool mbInitialized;
+};
+
+OLEStorageImpl::OLEStorageImpl()
+ : mbInitialized(false)
+{
+}
+
+void OLEStorageImpl::initialize(std::unique_ptr<SvStream> pStream)
+{
+ if (!pStream)
+ return;
+
+ mxRootStorage = new SotStorage(pStream.release(), true);
+
+ traverse(mxRootStorage, u"");
+
+ mbInitialized = true;
+}
+
+tools::SvRef<SotStorageStream> OLEStorageImpl::getStream(const OUString& rPath)
+{
+ const OUString aPath(lcl_normalizeSubStreamPath(rPath));
+ NameMap_t::iterator aIt = maNameMap.find(aPath);
+
+ // For the while don't return stream in this situation.
+ // Later, given how libcdr's zip stream implementation behaves,
+ // return the first stream in the storage if there is one.
+ if (maNameMap.end() == aIt)
+ return tools::SvRef<SotStorageStream>();
+
+ if (!maStreams[aIt->second].stream.is())
+ maStreams[aIt->second].stream
+ = createStream(OStringToOUString(maStreams[aIt->second].name, RTL_TEXTENCODING_UTF8));
+
+ return maStreams[aIt->second].stream;
+}
+
+tools::SvRef<SotStorageStream> const& OLEStorageImpl::getStream(const std::size_t nId)
+{
+ if (!maStreams[nId].stream.is())
+ maStreams[nId].stream
+ = createStream(OStringToOUString(maStreams[nId].name, RTL_TEXTENCODING_UTF8));
+
+ return maStreams[nId].stream;
+}
+
+void OLEStorageImpl::traverse(const tools::SvRef<SotStorage>& rStorage, std::u16string_view rPath)
+{
+ SvStorageInfoList infos;
+
+ rStorage->FillInfoList(&infos);
+
+ for (const auto& info : infos)
+ {
+ if (info.IsStream())
+ {
+ OUString baseName = info.GetName(), rvngName = baseName;
+ // librevenge::RVNGOLEStream ignores the first character when is a control code, so ...
+ if (!rvngName.isEmpty() && rvngName.toChar() < 32)
+ rvngName = rvngName.copy(1);
+ maStreams.emplace_back(
+ OUStringToOString(concatPath(rPath, baseName), RTL_TEXTENCODING_UTF8),
+ OUStringToOString(concatPath(rPath, rvngName), RTL_TEXTENCODING_UTF8));
+ maNameMap[concatPath(rPath, rvngName)] = maStreams.size() - 1;
+ }
+ else if (info.IsStorage())
+ {
+ const OUString aPath = concatPath(rPath, info.GetName());
+ tools::SvRef<SotStorage> aStorage
+ = rStorage->OpenSotStorage(info.GetName(), StreamMode::STD_READ);
+ maStorageMap[aPath] = aStorage;
+
+ // deep-first traversal
+ traverse(aStorage, aPath);
+ }
+ else
+ {
+ SAL_WARN("writerperfect",
+ "OLEStorageImpl::traverse: invalid storage entry, neither stream nor file");
+ }
+ }
+}
+
+tools::SvRef<SotStorageStream> OLEStorageImpl::createStream(const OUString& rPath)
+{
+ const sal_Int32 nDelim = rPath.lastIndexOf(u'/');
+
+ if (-1 == nDelim)
+ return mxRootStorage->OpenSotStream(rPath, StreamMode::STD_READ);
+
+ const OUString aDir = rPath.copy(0, nDelim);
+ const OUString aName = rPath.copy(nDelim + 1);
+
+ const OLEStorageMap_t::const_iterator aIt = maStorageMap.find(aDir);
+
+ if (maStorageMap.end() == aIt)
+ return nullptr;
+
+ return aIt->second->OpenSotStream(aName, StreamMode::STD_READ);
+}
+
+namespace
+{
+struct ZipStreamData
+{
+ explicit ZipStreamData(OString aName);
+
+ css::uno::Reference<css::io::XInputStream> xStream;
+
+ /** Name of the stream.
+ *
+ * This is not @c OUString, because we need to be able to
+ * produce const char* from it.
+ */
+ OString aName;
+};
+} // anonymous namespace
+
+/** Representation of a Zip storage.
+ *
+ * This is quite similar to OLEStorageImpl, except that we do not need
+ * to keep all storages (folders) open.
+ */
+struct ZipStorageImpl
+{
+ explicit ZipStorageImpl(const css::uno::Reference<css::container::XNameAccess>& rxContainer);
+
+ /** Initialize for access.
+ *
+ * This creates a bidirectional map of stream names to their
+ * indexes (index of a stream is determined by deep-first
+ * traversal).
+ */
+ void initialize();
+
+ Reference<css::io::XInputStream> getStream(const OUString& rPath);
+ Reference<css::io::XInputStream> const& getStream(std::size_t nId);
+
+private:
+ void traverse(const Reference<container::XNameAccess>& rxEnum);
+
+ Reference<XInputStream> createStream(const OUString& rPath);
+
+public:
+ Reference<container::XNameAccess> mxContainer; //< root of the Zip
+ ::std::vector<ZipStreamData> maStreams; //< list of streams and their names
+ NameMap_t maNameMap; //< map of stream names to indexes (into @c maStreams)
+ bool mbInitialized;
+};
+
+ZipStreamData::ZipStreamData(OString _aName)
+ : aName(std::move(_aName))
+{
+}
+
+ZipStorageImpl::ZipStorageImpl(const Reference<container::XNameAccess>& rxContainer)
+ : mxContainer(rxContainer)
+ , mbInitialized(false)
+{
+ assert(mxContainer.is());
+}
+
+void ZipStorageImpl::initialize()
+{
+ traverse(mxContainer);
+
+ mbInitialized = true;
+}
+
+Reference<XInputStream> ZipStorageImpl::getStream(const OUString& rPath)
+{
+ const OUString aPath(lcl_normalizeSubStreamPath(rPath));
+ NameMap_t::iterator aIt = maNameMap.find(aPath);
+
+ // For the while don't return stream in this situation.
+ // Later, given how libcdr's zip stream implementation behaves,
+ // return the first stream in the storage if there is one.
+ if (maNameMap.end() == aIt)
+ return Reference<XInputStream>();
+
+ if (!maStreams[aIt->second].xStream.is())
+ maStreams[aIt->second].xStream = createStream(aPath);
+
+ return maStreams[aIt->second].xStream;
+}
+
+Reference<XInputStream> const& ZipStorageImpl::getStream(const std::size_t nId)
+{
+ if (!maStreams[nId].xStream.is())
+ maStreams[nId].xStream
+ = createStream(OStringToOUString(maStreams[nId].aName, RTL_TEXTENCODING_UTF8));
+
+ return maStreams[nId].xStream;
+}
+
+void ZipStorageImpl::traverse(const Reference<container::XNameAccess>& rxContainer)
+{
+ const Sequence<OUString> lNames = rxContainer->getElementNames();
+
+ maStreams.reserve(lNames.getLength());
+
+ for (const auto& rName : lNames)
+ {
+ if (!rName.endsWith("/")) // skip dirs
+ {
+ maStreams.emplace_back(OUStringToOString(rName, RTL_TEXTENCODING_UTF8));
+ maNameMap[rName] = maStreams.size() - 1;
+ }
+ }
+}
+
+Reference<XInputStream> ZipStorageImpl::createStream(const OUString& rPath)
+{
+ Reference<XInputStream> xStream;
+
+ try
+ {
+ const Reference<XInputStream> xInputStream(mxContainer->getByName(rPath), UNO_QUERY_THROW);
+ const Reference<XSeekable> xSeekable(xInputStream, UNO_QUERY);
+
+ if (xSeekable.is())
+ xStream = xInputStream;
+ else
+ xStream.set(new comphelper::OSeekableInputWrapper(
+ xInputStream, comphelper::getProcessComponentContext()));
+ }
+ catch (const Exception&)
+ {
+ // nothing needed
+ }
+
+ return xStream;
+}
+
+WPXSvInputStream::WPXSvInputStream(Reference<XInputStream> const& xStream)
+ : mxStream(xStream)
+ , mxSeekable(xStream, UNO_QUERY)
+ , maData(0)
+ , mnLength(0)
+ , mnReadBufferPos(0)
+ , mbCheckedOLE(false)
+ , mbCheckedZip(false)
+{
+ if (!xStream.is() || !mxStream.is())
+ mnLength = 0;
+ else
+ {
+ if (!mxSeekable.is())
+ mnLength = 0;
+ else
+ {
+ try
+ {
+ mnLength = mxSeekable->getLength();
+ if (0 < mxSeekable->getPosition())
+ mxSeekable->seek(0);
+ }
+ catch (...)
+ {
+ SAL_WARN("writerperfect", "mnLength = mxSeekable->getLength() threw exception");
+ mnLength = 0;
+ }
+ }
+ }
+}
+
+const unsigned char* WPXSvInputStream::read(unsigned long numBytes, unsigned long& numBytesRead)
+{
+ numBytesRead = 0;
+
+ if (numBytes == 0 || isEnd())
+ return nullptr;
+
+ numBytesRead = mxStream->readSomeBytes(maData, numBytes);
+ if (numBytesRead == 0)
+ return nullptr;
+
+ return reinterpret_cast<const unsigned char*>(maData.getConstArray());
+}
+
+tools::Long WPXSvInputStream::tellImpl()
+{
+ if ((mnLength == 0) || !mxStream.is() || !mxSeekable.is())
+ return -1;
+ else
+ {
+ const sal_Int64 tmpPosition = mxSeekable->getPosition();
+ if (tmpPosition < 0)
+ return -1;
+#if SAL_TYPES_SIZEOFLONG == 4
+ if (tmpPosition > LONG_MAX)
+ return -1;
+#endif
+ return static_cast<tools::Long>(tmpPosition);
+ }
+}
+
+int WPXSvInputStream::seek(tools::Long offset)
+{
+ if ((mnLength == 0) || !mxStream.is() || !mxSeekable.is())
+ return -1;
+
+ const sal_Int64 tmpPosition = mxSeekable->getPosition();
+ if (tmpPosition < 0)
+ return -1;
+
+ try
+ {
+ mxSeekable->seek(offset);
+ }
+ catch (...)
+ {
+ SAL_WARN("writerperfect", "mxSeekable->seek(offset) threw exception");
+ return -1;
+ }
+
+ return 0;
+}
+
+bool WPXSvInputStream::isStructured()
+{
+ if ((mnLength == 0) || !mxStream.is() || !mxSeekable.is())
+ return false;
+
+ PositionHolder pos(mxSeekable);
+ mxSeekable->seek(0);
+
+ if (isOLE())
+ return true;
+
+ mxSeekable->seek(0);
+
+ return isZip();
+}
+
+unsigned WPXSvInputStream::subStreamCount()
+{
+ if ((mnLength == 0) || !mxStream.is() || !mxSeekable.is())
+ return 0;
+
+ PositionHolder pos(mxSeekable);
+ mxSeekable->seek(0);
+
+ if (isOLE())
+ {
+ ensureOLEIsInitialized();
+
+ return mpOLEStorage->maStreams.size();
+ }
+
+ mxSeekable->seek(0);
+
+ if (isZip())
+ {
+ ensureZipIsInitialized();
+
+ return mpZipStorage->maStreams.size();
+ }
+
+ return 0;
+}
+
+const char* WPXSvInputStream::subStreamName(const unsigned id)
+{
+ if ((mnLength == 0) || !mxStream.is() || !mxSeekable.is())
+ return nullptr;
+
+ PositionHolder pos(mxSeekable);
+ mxSeekable->seek(0);
+
+ if (isOLE())
+ {
+ ensureOLEIsInitialized();
+
+ if (mpOLEStorage->maStreams.size() <= id)
+ return nullptr;
+
+ return mpOLEStorage->maStreams[id].RVNGname.getStr();
+ }
+
+ mxSeekable->seek(0);
+
+ if (isZip())
+ {
+ ensureZipIsInitialized();
+
+ if (mpZipStorage->maStreams.size() <= id)
+ return nullptr;
+
+ return mpZipStorage->maStreams[id].aName.getStr();
+ }
+
+ return nullptr;
+}
+
+bool WPXSvInputStream::existsSubStream(const char* const name)
+{
+ if (!name)
+ return false;
+
+ if ((mnLength == 0) || !mxStream.is() || !mxSeekable.is())
+ return false;
+
+ PositionHolder pos(mxSeekable);
+ mxSeekable->seek(0);
+
+ const OUString aName(OStringToOUString(std::string_view(name), RTL_TEXTENCODING_UTF8));
+
+ if (isOLE())
+ {
+ ensureOLEIsInitialized();
+ return mpOLEStorage->maNameMap.end() != mpOLEStorage->maNameMap.find(aName);
+ }
+
+ mxSeekable->seek(0);
+
+ if (isZip())
+ {
+ ensureZipIsInitialized();
+ return mpZipStorage->maNameMap.end() != mpZipStorage->maNameMap.find(aName);
+ }
+
+ return false;
+}
+
+librevenge::RVNGInputStream* WPXSvInputStream::getSubStreamByName(const char* const name)
+{
+ if (!name)
+ return nullptr;
+
+ if ((mnLength == 0) || !mxStream.is() || !mxSeekable.is())
+ return nullptr;
+
+ PositionHolder pos(mxSeekable);
+ mxSeekable->seek(0);
+
+ const OUString aName(OStringToOUString(std::string_view(name), RTL_TEXTENCODING_UTF8));
+
+ if (isOLE())
+ {
+ ensureOLEIsInitialized();
+ return createWPXStream(mpOLEStorage->getStream(aName));
+ }
+
+ mxSeekable->seek(0);
+
+ if (isZip())
+ {
+ ensureZipIsInitialized();
+
+ try
+ {
+ return createWPXStream(mpZipStorage->getStream(aName));
+ }
+ catch (const Exception&)
+ {
+ // nothing needed
+ }
+ }
+
+ return nullptr;
+}
+
+librevenge::RVNGInputStream* WPXSvInputStream::getSubStreamById(const unsigned id)
+{
+ if ((mnLength == 0) || !mxStream.is() || !mxSeekable.is())
+ return nullptr;
+
+ PositionHolder pos(mxSeekable);
+ mxSeekable->seek(0);
+
+ if (isOLE())
+ {
+ ensureOLEIsInitialized();
+
+ if (mpOLEStorage->maStreams.size() <= id)
+ return nullptr;
+
+ return createWPXStream(mpOLEStorage->getStream(id));
+ }
+
+ mxSeekable->seek(0);
+
+ if (isZip())
+ {
+ ensureZipIsInitialized();
+
+ if (mpZipStorage->maStreams.size() <= id)
+ return nullptr;
+
+ try
+ {
+ return createWPXStream(mpZipStorage->getStream(id));
+ }
+ catch (const Exception&)
+ {
+ // nothing needed
+ }
+ }
+ return nullptr;
+}
+
+librevenge::RVNGInputStream*
+WPXSvInputStream::createWPXStream(const tools::SvRef<SotStorageStream>& rxStorage)
+{
+ if (rxStorage.is())
+ {
+ Reference<XInputStream> xContents(new utl::OSeekableInputStreamWrapper(rxStorage.get()));
+ return new WPXSvInputStream(xContents);
+ }
+ return nullptr;
+}
+
+librevenge::RVNGInputStream*
+WPXSvInputStream::createWPXStream(const Reference<XInputStream>& rxStream)
+{
+ if (rxStream.is())
+ return new WPXSvInputStream(rxStream);
+ else
+ return nullptr;
+}
+
+bool WPXSvInputStream::isOLE()
+{
+ if (!mbCheckedOLE)
+ {
+ assert(0 == mxSeekable->getPosition());
+
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(mxStream));
+ if (pStream && SotStorage::IsOLEStorage(pStream.get()))
+ mpOLEStorage.reset(new OLEStorageImpl());
+
+ mbCheckedOLE = true;
+ }
+
+ return bool(mpOLEStorage);
+}
+
+bool WPXSvInputStream::isZip()
+{
+ if (!mbCheckedZip)
+ {
+ assert(0 == mxSeekable->getPosition());
+
+ try
+ {
+ const Reference<XComponentContext> xContext(comphelper::getProcessComponentContext(),
+ UNO_SET_THROW);
+ const Reference<packages::zip::XZipFileAccess2> xZip(
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.packages.zip.ZipFileAccess", { Any(mxStream) }, xContext),
+ UNO_QUERY_THROW);
+ mpZipStorage.reset(new ZipStorageImpl(xZip));
+ }
+ catch (const Exception&)
+ {
+ // ignore
+ }
+
+ mbCheckedZip = true;
+ }
+
+ return bool(mpZipStorage);
+}
+
+void WPXSvInputStream::ensureOLEIsInitialized()
+{
+ assert(mpOLEStorage);
+
+ if (!mpOLEStorage->mbInitialized)
+ mpOLEStorage->initialize(utl::UcbStreamHelper::CreateStream(mxStream));
+}
+
+void WPXSvInputStream::ensureZipIsInitialized()
+{
+ assert(mpZipStorage);
+
+ if (!mpZipStorage->mbInitialized)
+ mpZipStorage->initialize();
+}
+
+WPXSvInputStream::~WPXSvInputStream() {}
+
+long WPXSvInputStream::tell()
+{
+ tools::Long retVal = tellImpl();
+ return retVal + static_cast<tools::Long>(mnReadBufferPos);
+}
+
+int WPXSvInputStream::seek(long offset, librevenge::RVNG_SEEK_TYPE seekType)
+{
+ sal_Int64 tmpOffset = offset;
+ if (seekType == librevenge::RVNG_SEEK_CUR)
+ tmpOffset += tell();
+ if (seekType == librevenge::RVNG_SEEK_END)
+ tmpOffset += mnLength;
+
+ int retVal = 0;
+ if (tmpOffset < 0)
+ {
+ tmpOffset = 0;
+ retVal = -1;
+ }
+ if (tmpOffset > mnLength)
+ {
+ tmpOffset = mnLength;
+ retVal = -1;
+ }
+
+ if (tmpOffset < tellImpl() && o3tl::make_unsigned(tmpOffset) >= o3tl::make_unsigned(tellImpl()))
+ {
+ mnReadBufferPos = static_cast<unsigned long>(tmpOffset - tellImpl());
+ return retVal;
+ }
+
+ if (seek(tmpOffset))
+ return -1;
+ return retVal;
+}
+
+bool WPXSvInputStream::isEnd()
+{
+ if (mnReadBufferPos != 0)
+ return false;
+ if ((mnLength == 0) || !mxStream.is() || !mxSeekable.is())
+ return true;
+ return (mxSeekable->getPosition() >= mnLength);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */