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 /writerperfect/source/writer | |
parent | Initial commit. (diff) | |
download | libreoffice-cb75148ebd0135178ff46f89a30139c44f8d2040.tar.xz libreoffice-cb75148ebd0135178ff46f89a30139c44f8d2040.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 'writerperfect/source/writer')
53 files changed, 6173 insertions, 0 deletions
diff --git a/writerperfect/source/writer/AbiWordImportFilter.cxx b/writerperfect/source/writer/AbiWordImportFilter.cxx new file mode 100644 index 000000000..0661604bc --- /dev/null +++ b/writerperfect/source/writer/AbiWordImportFilter.cxx @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* AbiWordImportFilter: Sets up the filter, and calls DocumentCollector + * to do the actual filtering + * + * 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 <cppuhelper/supportsservice.hxx> + +#include <libabw/libabw.h> + +#include "AbiWordImportFilter.hxx" + +bool AbiWordImportFilter::doImportDocument(weld::Window*, librevenge::RVNGInputStream& rInput, + OdtGenerator& rGenerator, utl::MediaDescriptor&) +{ + return libabw::AbiDocument::parse(&rInput, &rGenerator); +} + +bool AbiWordImportFilter::doDetectFormat(librevenge::RVNGInputStream& rInput, OUString& rTypeName) +{ + if (libabw::AbiDocument::isFileFormatSupported(&rInput)) + { + rTypeName = "writer_AbiWord_Document"; + return true; + } + + return false; +} + +// XServiceInfo +OUString SAL_CALL AbiWordImportFilter::getImplementationName() +{ + return "com.sun.star.comp.Writer.AbiWordImportFilter"; +} + +sal_Bool SAL_CALL AbiWordImportFilter::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence<OUString> SAL_CALL AbiWordImportFilter::getSupportedServiceNames() +{ + return { "com.sun.star.document.ImportFilter", "com.sun.star.document.ExtendedTypeDetection" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_AbiWordImportFilter_get_implementation( + css::uno::XComponentContext* const context, const css::uno::Sequence<css::uno::Any>&) +{ + return cppu::acquire(new AbiWordImportFilter(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/AbiWordImportFilter.hxx b/writerperfect/source/writer/AbiWordImportFilter.hxx new file mode 100644 index 000000000..f9b0f99d9 --- /dev/null +++ b/writerperfect/source/writer/AbiWordImportFilter.hxx @@ -0,0 +1,40 @@ +/* -*- 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 <com/sun/star/uno/XComponentContext.hpp> + +#include <ImportFilter.hxx> + +#include <DocumentHandlerForOdt.hxx> + +/* This component will be instantiated for both import or export. Whether it calls + * setSourceDocument or setTargetDocument determines which Impl function the filter + * member calls */ +class AbiWordImportFilter : public writerperfect::ImportFilter<OdtGenerator> +{ +public: + explicit AbiWordImportFilter(const css::uno::Reference<css::uno::XComponentContext>& rxContext) + : writerperfect::ImportFilter<OdtGenerator>(rxContext) + { + } + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + +private: + virtual bool doDetectFormat(librevenge::RVNGInputStream& rInput, OUString& rTypeName) override; + virtual bool doImportDocument(weld::Window* pParent, librevenge::RVNGInputStream& rInput, + OdtGenerator& rGenerator, utl::MediaDescriptor&) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/EBookImportFilter.cxx b/writerperfect/source/writer/EBookImportFilter.cxx new file mode 100644 index 000000000..3c53f2d9e --- /dev/null +++ b/writerperfect/source/writer/EBookImportFilter.cxx @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* EBookImportFilter: Sets up the filter, and calls DocumentCollector + * to do the actual filtering + * + * 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 <cppuhelper/supportsservice.hxx> +#include <sal/log.hxx> + +#include <libe-book/libe-book.h> + +#include "EBookImportFilter.hxx" + +using libebook::EBOOKDocument; + +bool EBookImportFilter::doImportDocument(weld::Window*, librevenge::RVNGInputStream& rInput, + OdtGenerator& rGenerator, + utl::MediaDescriptor& rDescriptor) +{ + OUString aFilterName; + + rDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] >>= aFilterName; + assert(!aFilterName.isEmpty()); + + if (aFilterName == "Palm_Text_Document") + { + return EBOOKDocument::RESULT_OK == EBOOKDocument::parse(&rInput, &rGenerator); + } + else + { + EBOOKDocument::Type type = EBOOKDocument::TYPE_UNKNOWN; + + if (aFilterName == "BroadBand eBook") + type = EBOOKDocument::TYPE_BBEB; + else if (aFilterName == "FictionBook 2") + type = EBOOKDocument::TYPE_FICTIONBOOK2; + else if (aFilterName == "PalmDoc") + type = EBOOKDocument::TYPE_PALMDOC; + else if (aFilterName == "Plucker eBook") + type = EBOOKDocument::TYPE_PLUCKER; + + if (EBOOKDocument::TYPE_UNKNOWN != type) + return EBOOKDocument::RESULT_OK == EBOOKDocument::parse(&rInput, &rGenerator, type); + } + + return false; +} + +bool EBookImportFilter::doDetectFormat(librevenge::RVNGInputStream& rInput, OUString& rTypeName) +{ + rTypeName.clear(); + + EBOOKDocument::Type type = EBOOKDocument::TYPE_UNKNOWN; + + if (EBOOKDocument::CONFIDENCE_EXCELLENT == EBOOKDocument::isSupported(&rInput, &type)) + { + switch (type) + { + case EBOOKDocument::TYPE_BBEB: + rTypeName = "writer_BroadBand_eBook"; + break; + case EBOOKDocument::TYPE_FICTIONBOOK2: + rTypeName = "writer_FictionBook_2"; + break; + case EBOOKDocument::TYPE_PALMDOC: + rTypeName = "writer_PalmDoc"; + break; + case EBOOKDocument::TYPE_PLUCKER: + rTypeName = "writer_Plucker_eBook"; + break; + case EBOOKDocument::TYPE_PEANUTPRESS: + case EBOOKDocument::TYPE_TEALDOC: + case EBOOKDocument::TYPE_ZTXT: + rTypeName = "Palm_Text_Document"; + break; + default: + SAL_WARN_IF(type != EBOOKDocument::TYPE_UNKNOWN, "writerperfect", + "EBookImportFilter::doDetectFormat: document type " + << type << " detected, but ignored"); + } + } + + return !rTypeName.isEmpty(); +} + +// XServiceInfo +OUString SAL_CALL EBookImportFilter::getImplementationName() +{ + return "org.libreoffice.comp.Writer.EBookImportFilter"; +} + +sal_Bool SAL_CALL EBookImportFilter::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence<OUString> SAL_CALL EBookImportFilter::getSupportedServiceNames() +{ + return { "com.sun.star.document.ImportFilter", "com.sun.star.document.ExtendedTypeDetection" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +org_libreoffice_comp_Writer_EBookImportFilter_get_implementation( + css::uno::XComponentContext* const context, const css::uno::Sequence<css::uno::Any>&) +{ + return cppu::acquire(new EBookImportFilter(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/EBookImportFilter.hxx b/writerperfect/source/writer/EBookImportFilter.hxx new file mode 100644 index 000000000..9ebdc9234 --- /dev/null +++ b/writerperfect/source/writer/EBookImportFilter.hxx @@ -0,0 +1,44 @@ +/* -*- 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/. + */ + +#ifndef EBOOKIMPORTFILTER_HXX +#define EBOOKIMPORTFILTER_HXX + +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <ImportFilter.hxx> + +#include <DocumentHandlerForOdt.hxx> + +/* This component will be instantiated for both import or export. Whether it calls + * setSourceDocument or setTargetDocument determines which Impl function the filter + * member calls */ +class EBookImportFilter : public writerperfect::ImportFilter<OdtGenerator> +{ +public: + explicit EBookImportFilter(const css::uno::Reference<css::uno::XComponentContext>& rxContext) + : writerperfect::ImportFilter<OdtGenerator>(rxContext) + { + } + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + +private: + virtual bool doDetectFormat(librevenge::RVNGInputStream& rInput, OUString& rTypeName) override; + virtual bool doImportDocument(weld::Window* pParent, librevenge::RVNGInputStream& rInput, + OdtGenerator& rGenerator, + utl::MediaDescriptor& rDescriptor) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/EPUBExportDialog.cxx b/writerperfect/source/writer/EPUBExportDialog.cxx new file mode 100644 index 000000000..0bba77a18 --- /dev/null +++ b/writerperfect/source/writer/EPUBExportDialog.cxx @@ -0,0 +1,224 @@ +/* -*- 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 "EPUBExportDialog.hxx" + +#include <libepubgen/libepubgen.h> + +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> +#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp> +#include <comphelper/sequenceashashmap.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/opengrf.hxx> +#include <sax/tools/converter.hxx> +#include <i18nlangtag/languagetag.hxx> + +#include "EPUBExportFilter.hxx" + +using namespace com::sun::star; + +namespace +{ +/// Converts version value to a listbox entry position. +sal_Int32 VersionToPosition(sal_Int32 nVersion) +{ + sal_Int32 nPosition = 0; + + switch (nVersion) + { + case 30: + nPosition = 0; + break; + case 20: + nPosition = 1; + break; + default: + assert(false); + break; + } + + return nPosition; +} + +/// Converts listbox entry position to a version value. +sal_Int32 PositionToVersion(sal_Int32 nPosition) +{ + sal_Int32 nVersion = 0; + + switch (nPosition) + { + case 0: + nVersion = 30; + break; + case 1: + nVersion = 20; + break; + default: + assert(false); + break; + } + + return nVersion; +} +} + +namespace writerperfect +{ +EPUBExportDialog::EPUBExportDialog(weld::Window* pParent, + comphelper::SequenceAsHashMap& rFilterData, + uno::Reference<uno::XComponentContext> xContext, + css::uno::Reference<css::lang::XComponent> xDocument) + : GenericDialogController(pParent, "writerperfect/ui/exportepub.ui", "EpubDialog") + , m_xContext(std::move(xContext)) + , m_rFilterData(rFilterData) + , m_xSourceDocument(std::move(xDocument)) + , m_xVersion(m_xBuilder->weld_combo_box("versionlb")) + , m_xSplit(m_xBuilder->weld_combo_box("splitlb")) + , m_xLayout(m_xBuilder->weld_combo_box("layoutlb")) + , m_xCoverPath(m_xBuilder->weld_entry("coverpath")) + , m_xCoverButton(m_xBuilder->weld_button("coverbutton")) + , m_xMediaDir(m_xBuilder->weld_entry("mediadir")) + , m_xMediaButton(m_xBuilder->weld_button("mediabutton")) + , m_xOKButton(m_xBuilder->weld_button("ok")) + , m_xIdentifier(m_xBuilder->weld_entry("identifier")) + , m_xTitle(m_xBuilder->weld_entry("title")) + , m_xInitialCreator(m_xBuilder->weld_entry("author")) + , m_xLanguage(m_xBuilder->weld_entry("language")) + , m_xDate(m_xBuilder->weld_entry("date")) + +{ + assert(PositionToVersion(m_xVersion->get_active()) == EPUBExportFilter::GetDefaultVersion()); + + auto it = rFilterData.find("EPUBVersion"); + if (it != rFilterData.end()) + { + sal_Int32 nVersion = 0; + if (it->second >>= nVersion) + m_xVersion->set_active(VersionToPosition(nVersion)); + } + m_xVersion->connect_changed(LINK(this, EPUBExportDialog, VersionSelectHdl)); + + it = rFilterData.find("EPUBSplitMethod"); + if (it != rFilterData.end()) + { + sal_Int32 nSplitMethod = 0; + if (it->second >>= nSplitMethod) + // No conversion, 1:1 mapping between libepubgen::EPUBSplitMethod + // and entry positions. + m_xSplit->set_active(nSplitMethod); + } + else + m_xSplit->set_active(EPUBExportFilter::GetDefaultSplitMethod()); + m_xSplit->connect_changed(LINK(this, EPUBExportDialog, SplitSelectHdl)); + + it = rFilterData.find("EPUBLayoutMethod"); + if (it != rFilterData.end()) + { + sal_Int32 nLayoutMethod = 0; + if (it->second >>= nLayoutMethod) + // No conversion, 1:1 mapping between libepubgen::EPUBLayoutMethod + // and entry positions. + m_xLayout->set_active(nLayoutMethod); + } + else + m_xLayout->set_active(EPUBExportFilter::GetDefaultLayoutMethod()); + m_xLayout->connect_changed(LINK(this, EPUBExportDialog, LayoutSelectHdl)); + + m_xCoverButton->connect_clicked(LINK(this, EPUBExportDialog, CoverClickHdl)); + + m_xMediaButton->connect_clicked(LINK(this, EPUBExportDialog, MediaClickHdl)); + + uno::Reference<document::XDocumentPropertiesSupplier> xDPS(m_xSourceDocument, uno::UNO_QUERY); + uno::Reference<document::XDocumentProperties> xDP; + if (xDPS.is()) + xDP = xDPS->getDocumentProperties(); + if (xDP.is()) + { + m_xTitle->set_text(xDP->getTitle()); + m_xInitialCreator->set_text(xDP->getAuthor()); + + OUString aLanguage(LanguageTag::convertToBcp47(xDP->getLanguage(), false)); + m_xLanguage->set_text(aLanguage); + + OUStringBuffer aBuffer; + util::DateTime aDate(xDP->getModificationDate()); + sax::Converter::convertDateTime(aBuffer, aDate, nullptr, true); + m_xDate->set_text(aBuffer.makeStringAndClear()); + } + + m_xOKButton->connect_clicked(LINK(this, EPUBExportDialog, OKClickHdl)); +} + +IMPL_LINK_NOARG(EPUBExportDialog, VersionSelectHdl, weld::ComboBox&, void) +{ + m_rFilterData["EPUBVersion"] <<= PositionToVersion(m_xVersion->get_active()); +} + +IMPL_LINK_NOARG(EPUBExportDialog, SplitSelectHdl, weld::ComboBox&, void) +{ + // No conversion, 1:1 mapping between entry positions and + // libepubgen::EPUBSplitMethod. + m_rFilterData["EPUBSplitMethod"] <<= static_cast<sal_Int32>(m_xSplit->get_active()); +} + +IMPL_LINK_NOARG(EPUBExportDialog, LayoutSelectHdl, weld::ComboBox&, void) +{ + // No conversion, 1:1 mapping between entry positions and + // libepubgen::EPUBLayoutMethod. + m_rFilterData["EPUBLayoutMethod"] <<= static_cast<sal_Int32>(m_xLayout->get_active()); + m_xSplit->set_sensitive(m_xLayout->get_active() != libepubgen::EPUB_LAYOUT_METHOD_FIXED); +} + +IMPL_LINK_NOARG(EPUBExportDialog, CoverClickHdl, weld::Button&, void) +{ + SvxOpenGraphicDialog aDlg("Import", m_xDialog.get()); + aDlg.EnableLink(false); + if (aDlg.Execute() == ERRCODE_NONE) + m_xCoverPath->set_text(aDlg.GetPath()); +} + +IMPL_LINK_NOARG(EPUBExportDialog, MediaClickHdl, weld::Button&, void) +{ + uno::Reference<ui::dialogs::XFolderPicker2> xFolderPicker + = sfx2::createFolderPicker(m_xContext, m_xDialog.get()); + if (xFolderPicker->execute() != ui::dialogs::ExecutableDialogResults::OK) + return; + + m_xMediaDir->set_text(xFolderPicker->getDirectory()); +} + +IMPL_LINK_NOARG(EPUBExportDialog, OKClickHdl, weld::Button&, void) +{ + // General + if (!m_xCoverPath->get_text().isEmpty()) + m_rFilterData["RVNGCoverImage"] <<= m_xCoverPath->get_text(); + if (!m_xMediaDir->get_text().isEmpty()) + m_rFilterData["RVNGMediaDir"] <<= m_xMediaDir->get_text(); + + // Metadata + if (!m_xIdentifier->get_text().isEmpty()) + m_rFilterData["RVNGIdentifier"] <<= m_xIdentifier->get_text(); + if (!m_xTitle->get_text().isEmpty()) + m_rFilterData["RVNGTitle"] <<= m_xTitle->get_text(); + if (!m_xInitialCreator->get_text().isEmpty()) + m_rFilterData["RVNGInitialCreator"] <<= m_xInitialCreator->get_text(); + if (!m_xLanguage->get_text().isEmpty()) + m_rFilterData["RVNGLanguage"] <<= m_xLanguage->get_text(); + if (!m_xDate->get_text().isEmpty()) + m_rFilterData["RVNGDate"] <<= m_xDate->get_text(); + + m_xDialog->response(RET_OK); +} + +EPUBExportDialog::~EPUBExportDialog() = default; + +} // namespace writerperfect + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/EPUBExportDialog.hxx b/writerperfect/source/writer/EPUBExportDialog.hxx new file mode 100644 index 000000000..9e11d5351 --- /dev/null +++ b/writerperfect/source/writer/EPUBExportDialog.hxx @@ -0,0 +1,61 @@ +/* -*- 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 <vcl/weld.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/lang/XComponent.hpp> + +namespace comphelper +{ +class SequenceAsHashMap; +} + +namespace writerperfect +{ +/// EPUB export options dialog. +class EPUBExportDialog : public weld::GenericDialogController +{ +public: + EPUBExportDialog(weld::Window* pParent, comphelper::SequenceAsHashMap& rFilterData, + css::uno::Reference<css::uno::XComponentContext> xContext, + css::uno::Reference<css::lang::XComponent> xDocument); + ~EPUBExportDialog() override; + +private: + DECL_LINK(VersionSelectHdl, weld::ComboBox&, void); + DECL_LINK(SplitSelectHdl, weld::ComboBox&, void); + DECL_LINK(LayoutSelectHdl, weld::ComboBox&, void); + DECL_LINK(CoverClickHdl, weld::Button&, void); + DECL_LINK(MediaClickHdl, weld::Button&, void); + DECL_LINK(OKClickHdl, weld::Button&, void); + + css::uno::Reference<css::uno::XComponentContext> m_xContext; + comphelper::SequenceAsHashMap& m_rFilterData; + css::uno::Reference<css::lang::XComponent> m_xSourceDocument; + + std::unique_ptr<weld::ComboBox> m_xVersion; + std::unique_ptr<weld::ComboBox> m_xSplit; + std::unique_ptr<weld::ComboBox> m_xLayout; + std::unique_ptr<weld::Entry> m_xCoverPath; + std::unique_ptr<weld::Button> m_xCoverButton; + std::unique_ptr<weld::Entry> m_xMediaDir; + std::unique_ptr<weld::Button> m_xMediaButton; + std::unique_ptr<weld::Button> m_xOKButton; + std::unique_ptr<weld::Entry> m_xIdentifier; + std::unique_ptr<weld::Entry> m_xTitle; + std::unique_ptr<weld::Entry> m_xInitialCreator; + std::unique_ptr<weld::Entry> m_xLanguage; + std::unique_ptr<weld::Entry> m_xDate; +}; + +} // namespace writerperfect + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/EPUBExportFilter.cxx b/writerperfect/source/writer/EPUBExportFilter.cxx new file mode 100644 index 000000000..8ac55af19 --- /dev/null +++ b/writerperfect/source/writer/EPUBExportFilter.cxx @@ -0,0 +1,206 @@ +/* -*- 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 "EPUBExportFilter.hxx" + +#include <libepubgen/EPUBTextGenerator.h> +#include <libepubgen/libepubgen-decls.h> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/text/XPageCursor.hpp> +#include <com/sun/star/text/XTextViewCursorSupplier.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <comphelper/genericpropertyset.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <svtools/DocumentToGraphicRenderer.hxx> +#include <vcl/filter/SvmWriter.hxx> +#include <vcl/gdimtf.hxx> +#include <tools/stream.hxx> + +#include "exp/xmlimp.hxx" +#include "EPUBPackage.hxx" + +using namespace com::sun::star; + +namespace writerperfect +{ +EPUBExportFilter::EPUBExportFilter(uno::Reference<uno::XComponentContext> xContext) + : mxContext(std::move(xContext)) +{ +} + +sal_Int32 EPUBExportFilter::GetDefaultVersion() { return 30; } + +sal_Int32 EPUBExportFilter::GetDefaultSplitMethod() +{ + return libepubgen::EPUB_SPLIT_METHOD_HEADING; +} + +sal_Int32 EPUBExportFilter::GetDefaultLayoutMethod() +{ + return libepubgen::EPUB_LAYOUT_METHOD_REFLOWABLE; +} + +sal_Bool EPUBExportFilter::filter(const uno::Sequence<beans::PropertyValue>& rDescriptor) +{ + sal_Int32 nVersion = EPUBExportFilter::GetDefaultVersion(); + sal_Int32 nSplitMethod = EPUBExportFilter::GetDefaultSplitMethod(); + sal_Int32 nLayoutMethod = EPUBExportFilter::GetDefaultLayoutMethod(); + uno::Sequence<beans::PropertyValue> aFilterData; + OUString aFilterOptions; + for (const auto& rProp : rDescriptor) + { + if (rProp.Name == "FilterData") + rProp.Value >>= aFilterData; + else if (rProp.Name == "FilterOptions") + rProp.Value >>= aFilterOptions; + } + + if (aFilterOptions == "layout=fixed") + nLayoutMethod = libepubgen::EPUB_LAYOUT_METHOD_FIXED; + + for (const auto& rProp : std::as_const(aFilterData)) + { + if (rProp.Name == "EPUBVersion") + rProp.Value >>= nVersion; + else if (rProp.Name == "EPUBSplitMethod") + rProp.Value >>= nSplitMethod; + else if (rProp.Name == "EPUBLayoutMethod") + rProp.Value >>= nLayoutMethod; + } + + // Build the export filter chain: the package has direct access to the ZIP + // file, the flat ODF filter has access to the doc model, everything else + // is in-between. + EPUBPackage aPackage(mxContext, rDescriptor); + libepubgen::EPUBTextGenerator aGenerator(&aPackage, nVersion); + aGenerator.setOption(libepubgen::EPUB_GENERATOR_OPTION_SPLIT, nSplitMethod); + aGenerator.setOption(libepubgen::EPUB_GENERATOR_OPTION_LAYOUT, nLayoutMethod); + OUString aSourceURL; + uno::Reference<frame::XModel> xSourceModel(mxSourceDocument, uno::UNO_QUERY); + if (xSourceModel.is()) + aSourceURL = xSourceModel->getURL(); + + std::vector<exp::FixedLayoutPage> aPageMetafiles; + if (nLayoutMethod == libepubgen::EPUB_LAYOUT_METHOD_FIXED) + CreateMetafiles(aPageMetafiles); + + uno::Reference<xml::sax::XDocumentHandler> xExportHandler( + new exp::XMLImport(mxContext, aGenerator, aSourceURL, rDescriptor, aPageMetafiles)); + + uno::Reference<lang::XInitialization> xInitialization( + mxContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.comp.Writer.XMLOasisExporter", mxContext), + uno::UNO_QUERY); + + // A subset of parameters are passed in as a property set. + static comphelper::PropertyMapEntry const aInfoMap[] + = { { OUString("BaseURI"), 0, cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 } }; + uno::Reference<beans::XPropertySet> xInfoSet( + comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap))); + xInfoSet->setPropertyValue("BaseURI", uno::Any(aSourceURL)); + + xInitialization->initialize({ uno::Any(xExportHandler), uno::Any(xInfoSet) }); + uno::Reference<document::XExporter> xExporter(xInitialization, uno::UNO_QUERY); + xExporter->setSourceDocument(mxSourceDocument); + uno::Reference<document::XFilter> xFilter(xInitialization, uno::UNO_QUERY); + + return xFilter->filter(rDescriptor); +} + +void EPUBExportFilter::CreateMetafiles(std::vector<exp::FixedLayoutPage>& rPageMetafiles) +{ + DocumentToGraphicRenderer aRenderer(mxSourceDocument, /*bSelectionOnly=*/false); + uno::Reference<frame::XModel> xModel(mxSourceDocument, uno::UNO_QUERY); + if (!xModel.is()) + return; + + uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier( + xModel->getCurrentController(), uno::UNO_QUERY); + if (!xTextViewCursorSupplier.is()) + return; + + uno::Reference<text::XPageCursor> xCursor(xTextViewCursorSupplier->getViewCursor(), + uno::UNO_QUERY); + if (!xCursor.is()) + return; + + xCursor->jumpToLastPage(); + sal_Int16 nPages = xCursor->getPage(); + for (sal_Int16 nPage = 1; nPage <= nPages; ++nPage) + { + Size aDocumentSizePixel = aRenderer.getDocumentSizeInPixels(nPage); + Size aLogic = aRenderer.getDocumentSizeIn100mm(nPage); + // Get the CSS pixel size of the page (mm100 -> pixel using 96 DPI, independent from system DPI). + Size aCss(static_cast<double>(aLogic.getWidth()) / 26.4583, + static_cast<double>(aLogic.getHeight()) / 26.4583); + Graphic aGraphic = aRenderer.renderToGraphic(nPage, aDocumentSizePixel, aCss, COL_WHITE, + /*bExtOutDevData=*/true); + auto& rGDIMetaFile = const_cast<GDIMetaFile&>(aGraphic.GetGDIMetaFile()); + + // Set preferred map unit and size on the metafile, so the SVG size + // will be correct in MM. + MapMode aMapMode; + aMapMode.SetMapUnit(MapUnit::Map100thMM); + rGDIMetaFile.SetPrefMapMode(aMapMode); + rGDIMetaFile.SetPrefSize(aLogic); + + SvMemoryStream aMemoryStream; + SvmWriter aWriter(aMemoryStream); + aWriter.Write(rGDIMetaFile); + uno::Sequence<sal_Int8> aSequence(static_cast<const sal_Int8*>(aMemoryStream.GetData()), + aMemoryStream.Tell()); + + exp::FixedLayoutPage aPage; + aPage.aMetafile = aSequence; + aPage.aCssPixels = aCss; + aPage.aChapterNames = aRenderer.getChapterNames(); + rPageMetafiles.push_back(aPage); + } +} + +void EPUBExportFilter::cancel() {} + +void EPUBExportFilter::setSourceDocument(const uno::Reference<lang::XComponent>& xDocument) +{ + mxSourceDocument = xDocument; +} + +OUString EPUBExportFilter::getImplementationName() +{ + return "com.sun.star.comp.Writer.EPUBExportFilter"; +} + +sal_Bool EPUBExportFilter::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> EPUBExportFilter::getSupportedServiceNames() +{ + uno::Sequence<OUString> aRet = { OUString("com.sun.star.document.ExportFilter") }; + return aRet; +} + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Writer_EPUBExportFilter_get_implementation( + uno::XComponentContext* pContext, uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new EPUBExportFilter(pContext)); +} + +} // namespace writerperfect + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/EPUBExportFilter.hxx b/writerperfect/source/writer/EPUBExportFilter.hxx new file mode 100644 index 000000000..6cf007ff4 --- /dev/null +++ b/writerperfect/source/writer/EPUBExportFilter.hxx @@ -0,0 +1,71 @@ +/* -*- 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 <vector> + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/document/XExporter.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +namespace com::sun::star::uno +{ +class XComponentContext; +} + +namespace writerperfect +{ +namespace exp +{ +struct FixedLayoutPage; +} + +/// EPUB export XFilter implementation. +class EPUBExportFilter + : public cppu::WeakImplHelper<css::document::XFilter, css::document::XExporter, + css::lang::XServiceInfo> +{ + css::uno::Reference<css::uno::XComponentContext> mxContext; + css::uno::Reference<css::lang::XComponent> mxSourceDocument; + +public: + EPUBExportFilter(css::uno::Reference<css::uno::XComponentContext> xContext); + + // XFilter + sal_Bool SAL_CALL + filter(const css::uno::Sequence<css::beans::PropertyValue>& rDescriptor) override; + void SAL_CALL cancel() override; + + // XExporter + void SAL_CALL + setSourceDocument(const css::uno::Reference<css::lang::XComponent>& xDocument) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override; + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + /// Gives the default EPUB version. + static sal_Int32 GetDefaultVersion(); + /// Gives the default split method. + static sal_Int32 GetDefaultSplitMethod(); + /// Gives the default layout method. + static sal_Int32 GetDefaultLayoutMethod(); + +private: + /// Create page metafiles in case of fixed layout. + void CreateMetafiles(std::vector<exp::FixedLayoutPage>& rPageMetafiles); +}; + +} // namespace writerperfect + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/EPUBExportUIComponent.cxx b/writerperfect/source/writer/EPUBExportUIComponent.cxx new file mode 100644 index 000000000..e8d618b99 --- /dev/null +++ b/writerperfect/source/writer/EPUBExportUIComponent.cxx @@ -0,0 +1,102 @@ +/* -*- 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 "EPUBExportUIComponent.hxx" + +#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> +#include <comphelper/namedvaluecollection.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <vcl/svapp.hxx> + +#include "EPUBExportDialog.hxx" + +using namespace com::sun::star; + +namespace writerperfect +{ +EPUBExportUIComponent::EPUBExportUIComponent(uno::Reference<uno::XComponentContext> xContext) + : mxContext(std::move(xContext)) +{ +} + +uno::Sequence<beans::PropertyValue> EPUBExportUIComponent::getPropertyValues() +{ + maMediaDescriptor["FilterData"] <<= maFilterData.getAsConstPropertyValueList(); + return maMediaDescriptor.getAsConstPropertyValueList(); +} + +void EPUBExportUIComponent::setPropertyValues( + const uno::Sequence<beans::PropertyValue>& rProperties) +{ + maMediaDescriptor.clear(); + maMediaDescriptor << rProperties; + auto it = maMediaDescriptor.find("FilterData"); + if (it != maMediaDescriptor.end()) + { + uno::Sequence<beans::PropertyValue> aFilterData; + if (it->second >>= aFilterData) + { + maFilterData.clear(); + maFilterData << aFilterData; + } + } +} + +OUString EPUBExportUIComponent::getImplementationName() +{ + return "com.sun.star.comp.Writer.EPUBExportUIComponent"; +} + +sal_Bool EPUBExportUIComponent::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> EPUBExportUIComponent::getSupportedServiceNames() +{ + uno::Sequence<OUString> aRet = { OUString("com.sun.star.ui.dialogs.FilterOptionsDialog") }; + return aRet; +} + +void EPUBExportUIComponent::setTitle(const OUString& /*rTitle*/) {} + +void SAL_CALL EPUBExportUIComponent::initialize(const uno::Sequence<uno::Any>& rArguments) +{ + ::comphelper::NamedValueCollection aProperties(rArguments); + if (aProperties.has("ParentWindow")) + aProperties.get("ParentWindow") >>= mxDialogParent; +} + +sal_Int16 EPUBExportUIComponent::execute() +{ + SolarMutexGuard aGuard; + + EPUBExportDialog aDialog(Application::GetFrameWeld(mxDialogParent), maFilterData, mxContext, + mxSourceDocument); + if (aDialog.run() == RET_OK) + return ui::dialogs::ExecutableDialogResults::OK; + return ui::dialogs::ExecutableDialogResults::CANCEL; +} + +void SAL_CALL EPUBExportUIComponent::setSourceDocument( + const css::uno::Reference<css::lang::XComponent>& xDocument) +{ + mxSourceDocument = xDocument; +} + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Writer_EPUBExportUIComponent_get_implementation( + uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new EPUBExportUIComponent(pCtx)); +} + +} // namespace writerperfect + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/EPUBExportUIComponent.hxx b/writerperfect/source/writer/EPUBExportUIComponent.hxx new file mode 100644 index 000000000..71fcc0ace --- /dev/null +++ b/writerperfect/source/writer/EPUBExportUIComponent.hxx @@ -0,0 +1,72 @@ +/* -*- 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 <com/sun/star/beans/XPropertyAccess.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/document/XExporter.hpp> +#include <com/sun/star/awt/XWindow.hpp> + +#include <comphelper/sequenceashashmap.hxx> +#include <cppuhelper/implbase.hxx> + +namespace com::sun::star::uno +{ +class XComponentContext; +} + +namespace writerperfect +{ +/// EPUB export UI component implementation. +class EPUBExportUIComponent + : public cppu::WeakImplHelper<css::beans::XPropertyAccess, css::lang::XInitialization, + css::lang::XServiceInfo, css::ui::dialogs::XExecutableDialog, + css::document::XExporter> +{ +public: + EPUBExportUIComponent(css::uno::Reference<css::uno::XComponentContext> xContext); + + // XPropertyAccess + css::uno::Sequence<css::beans::PropertyValue> SAL_CALL getPropertyValues() override; + void SAL_CALL + setPropertyValues(const css::uno::Sequence<css::beans::PropertyValue>& rProperties) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override; + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // XExecutableDialog + void SAL_CALL setTitle(const OUString& rTitle) override; + sal_Int16 SAL_CALL execute() override; + + // XExporter + void SAL_CALL + setSourceDocument(const css::uno::Reference<css::lang::XComponent>& xDocument) override; + + // XInitialization + void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override; + +private: + /// The full set of property values. + comphelper::SequenceAsHashMap maMediaDescriptor; + /// The filter data key. + comphelper::SequenceAsHashMap maFilterData; + /// UNO context. + css::uno::Reference<css::uno::XComponentContext> mxContext; + css::uno::Reference<css::lang::XComponent> mxSourceDocument; + css::uno::Reference<css::awt::XWindow> mxDialogParent; +}; + +} // namespace writerperfect + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/EPUBPackage.cxx b/writerperfect/source/writer/EPUBPackage.cxx new file mode 100644 index 000000000..727a8ed7e --- /dev/null +++ b/writerperfect/source/writer/EPUBPackage.cxx @@ -0,0 +1,215 @@ +/* -*- 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 "EPUBPackage.hxx" + +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/embed/XHierarchicalStorageAccess.hpp> + +#include <sal/log.hxx> +#include <comphelper/storagehelper.hxx> +#include <unotools/mediadescriptor.hxx> +#include <xmloff/attrlist.hxx> + +using namespace com::sun::star; + +namespace writerperfect +{ +EPUBPackage::EPUBPackage(uno::Reference<uno::XComponentContext> xContext, + const uno::Sequence<beans::PropertyValue>& rDescriptor) + : mxContext(std::move(xContext)) +{ + // Extract the output stream from the descriptor. + utl::MediaDescriptor aMediaDesc(rDescriptor); + auto xStream = aMediaDesc.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_STREAMFOROUTPUT, + uno::Reference<io::XStream>()); + const sal_Int32 nOpenMode = embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE; + mxStorage.set(comphelper::OStorageHelper::GetStorageOfFormatFromStream( + ZIP_STORAGE_FORMAT_STRING, xStream, nOpenMode, mxContext), + uno::UNO_QUERY); + + // The zipped content represents an EPUB Publication. + mxOutputStream.set( + mxStorage->openStreamElementByHierarchicalName("mimetype", embed::ElementModes::READWRITE), + uno::UNO_QUERY); + const OString aMimeType("application/epub+zip"); + uno::Sequence<sal_Int8> aData(reinterpret_cast<const sal_Int8*>(aMimeType.getStr()), + aMimeType.getLength()); + mxOutputStream->writeBytes(aData); + uno::Reference<embed::XTransactedObject> xTransactedObject(mxOutputStream, uno::UNO_QUERY); + xTransactedObject->commit(); + + // MIME type must be uncompressed. + uno::Reference<beans::XPropertySet> xPropertySet(mxOutputStream, uno::UNO_QUERY); + xPropertySet->setPropertyValue("Compressed", uno::Any(false)); + mxOutputStream.clear(); +} + +EPUBPackage::~EPUBPackage() +{ + uno::Reference<embed::XTransactedObject> xTransactedObject(mxStorage, uno::UNO_QUERY); + xTransactedObject->commit(); +} + +void EPUBPackage::openXMLFile(const char* pName) +{ + assert(pName); + assert(!mxOutputStream.is()); + assert(!mxOutputWriter.is()); + + mxOutputStream.set(mxStorage->openStreamElementByHierarchicalName( + OUString::fromUtf8(pName), embed::ElementModes::READWRITE), + uno::UNO_QUERY); + mxOutputWriter = xml::sax::Writer::create(mxContext); + mxOutputWriter->setOutputStream(mxOutputStream); + mxOutputWriter->startDocument(); +} + +void EPUBPackage::openElement(const char* pName, const librevenge::RVNGPropertyList& rAttributes) +{ + assert(mxOutputWriter.is()); + + rtl::Reference<SvXMLAttributeList> pAttributeList(new SvXMLAttributeList()); + + librevenge::RVNGPropertyList::Iter it(rAttributes); + for (it.rewind(); it.next();) + pAttributeList->AddAttribute(OUString::fromUtf8(it.key()), + OUString::fromUtf8(it()->getStr().cstr())); + + mxOutputWriter->startElement(OUString::fromUtf8(pName), pAttributeList); +} + +void EPUBPackage::closeElement(const char* pName) +{ + assert(mxOutputWriter.is()); + + mxOutputWriter->endElement(OUString::fromUtf8(pName)); +} + +void EPUBPackage::insertCharacters(const librevenge::RVNGString& rCharacters) +{ + mxOutputWriter->characters(OUString::fromUtf8(rCharacters.cstr())); +} + +void EPUBPackage::closeXMLFile() +{ + assert(mxOutputWriter.is()); + assert(mxOutputStream.is()); + + mxOutputWriter->endDocument(); + mxOutputWriter.clear(); + + uno::Reference<embed::XTransactedObject> xTransactedObject(mxOutputStream, uno::UNO_QUERY); + xTransactedObject->commit(); + mxOutputStream.clear(); +} + +void EPUBPackage::openCSSFile(const char* pName) +{ + assert(pName); + assert(!mxOutputStream.is()); + + mxOutputStream.set(mxStorage->openStreamElementByHierarchicalName( + OUString::fromUtf8(pName), embed::ElementModes::READWRITE), + uno::UNO_QUERY); +} + +void EPUBPackage::insertRule(const librevenge::RVNGString& rSelector, + const librevenge::RVNGPropertyList& rProperties) +{ + assert(mxOutputStream.is()); + + uno::Reference<io::XSeekable> xSeekable(mxOutputStream, uno::UNO_QUERY); + std::stringstream aStream; + if (xSeekable->getPosition() != 0) + aStream << '\n'; + aStream << rSelector.cstr() << " {\n"; + + librevenge::RVNGPropertyList::Iter it(rProperties); + for (it.rewind(); it.next();) + { + if (it()) + aStream << " " << it.key() << ": " << it()->getStr().cstr() << ";\n"; + } + + aStream << "}\n"; + std::string aString = aStream.str(); + uno::Sequence<sal_Int8> aData(reinterpret_cast<const sal_Int8*>(aString.c_str()), + aString.size()); + mxOutputStream->writeBytes(aData); +} + +void EPUBPackage::closeCSSFile() +{ + assert(mxOutputStream.is()); + + uno::Reference<embed::XTransactedObject> xTransactedObject(mxOutputStream, uno::UNO_QUERY); + xTransactedObject->commit(); + mxOutputStream.clear(); +} + +void EPUBPackage::openBinaryFile(const char* pName) +{ + assert(pName); + assert(!mxOutputStream.is()); + + mxOutputStream.set(mxStorage->openStreamElementByHierarchicalName( + OUString::fromUtf8(pName), embed::ElementModes::READWRITE), + uno::UNO_QUERY); +} + +void EPUBPackage::insertBinaryData(const librevenge::RVNGBinaryData& rData) +{ + assert(mxOutputStream.is()); + + if (rData.empty()) + return; + + uno::Sequence<sal_Int8> aData(reinterpret_cast<const sal_Int8*>(rData.getDataBuffer()), + rData.size()); + mxOutputStream->writeBytes(aData); +} + +void EPUBPackage::closeBinaryFile() +{ + assert(mxOutputStream.is()); + + uno::Reference<embed::XTransactedObject> xTransactedObject(mxOutputStream, uno::UNO_QUERY); + xTransactedObject->commit(); + mxOutputStream.clear(); +} + +void EPUBPackage::openTextFile(const char* pName) +{ + SAL_WARN("writerperfect", "EPUBPackage::openTextFile, " << pName << ": implement me"); +} + +void EPUBPackage::insertText(const librevenge::RVNGString& /*rCharacters*/) +{ + SAL_WARN("writerperfect", "EPUBPackage::insertText: implement me"); +} + +void EPUBPackage::insertLineBreak() +{ + SAL_WARN("writerperfect", "EPUBPackage::insertLineBreak: implement me"); +} + +void EPUBPackage::closeTextFile() +{ + SAL_WARN("writerperfect", "EPUBPackage::closeTextFile: implement me"); +} + +} // namespace writerperfect + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/EPUBPackage.hxx b/writerperfect/source/writer/EPUBPackage.hxx new file mode 100644 index 000000000..726d20673 --- /dev/null +++ b/writerperfect/source/writer/EPUBPackage.hxx @@ -0,0 +1,90 @@ +/* -*- 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 <libepubgen/EPUBPackage.h> + +#include <com/sun/star/uno/Sequence.h> +#include <com/sun/star/uno/Reference.h> + +namespace com::sun::star +{ +namespace beans +{ +struct PropertyValue; +} +namespace embed +{ +class XHierarchicalStorageAccess; +} +namespace io +{ +class XOutputStream; +} +namespace uno +{ +class XComponentContext; +} +namespace xml::sax +{ +class XWriter; +} +} + +namespace writerperfect +{ +/// The epub package has direct access to the resulting ZIP file. +class EPUBPackage : public libepubgen::EPUBPackage +{ + css::uno::Reference<css::uno::XComponentContext> mxContext; + css::uno::Reference<css::embed::XHierarchicalStorageAccess> mxStorage; + css::uno::Reference<css::io::XOutputStream> mxOutputStream; + css::uno::Reference<css::xml::sax::XWriter> mxOutputWriter; + +public: + explicit EPUBPackage(css::uno::Reference<css::uno::XComponentContext> xContext, + const css::uno::Sequence<css::beans::PropertyValue>& rDescriptor); + + ~EPUBPackage() override; + + void openXMLFile(const char* pName) override; + + void openElement(const char* pName, const librevenge::RVNGPropertyList& rAttributes) override; + void closeElement(const char* pName) override; + + void insertCharacters(const librevenge::RVNGString& rCharacters) override; + + void closeXMLFile() override; + + void openCSSFile(const char* pName) override; + + void insertRule(const librevenge::RVNGString& rSelector, + const librevenge::RVNGPropertyList& rProperties) override; + + void closeCSSFile() override; + + void openBinaryFile(const char* pName) override; + + void insertBinaryData(const librevenge::RVNGBinaryData& rData) override; + + void closeBinaryFile() override; + + void openTextFile(const char* pName) override; + + void insertText(const librevenge::RVNGString& rCharacters) override; + + void insertLineBreak() override; + + void closeTextFile() override; +}; + +} // namespace writerperfect + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/MSWorksImportFilter.cxx b/writerperfect/source/writer/MSWorksImportFilter.cxx new file mode 100644 index 000000000..e79120408 --- /dev/null +++ b/writerperfect/source/writer/MSWorksImportFilter.cxx @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* MSWorksImportFilter: Sets up the filter, and calls DocumentCollector + * to do the actual filtering + * + * 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 <cppuhelper/supportsservice.hxx> +#include <tools/diagnose_ex.h> + +#include <libwps/libwps.h> + +#include <WPFTEncodingDialog.hxx> +#include <WPFTResMgr.hxx> +#include "MSWorksImportFilter.hxx" +#include <strings.hrc> + +static bool handleEmbeddedWKSObject(const librevenge::RVNGBinaryData& data, + OdfDocumentHandler* pHandler, const OdfStreamType streamType) +{ + OdsGenerator exporter; + exporter.addDocumentHandler(pHandler, streamType); + return libwps::WPSDocument::parse(data.getDataStream(), &exporter) == libwps::WPS_OK; +} + +bool MSWorksImportFilter::doImportDocument(weld::Window* pParent, + librevenge::RVNGInputStream& rInput, + OdtGenerator& rGenerator, + utl::MediaDescriptor& mediaDescriptor) +{ + libwps::WPSKind kind = libwps::WPS_TEXT; + libwps::WPSCreator creator; + bool needEncoding = false; + const libwps::WPSConfidence confidence + = libwps::WPSDocument::isFileFormatSupported(&rInput, kind, creator, needEncoding); + + std::string fileEncoding; + if ((kind == libwps::WPS_TEXT) && (confidence == libwps::WPS_CONFIDENCE_EXCELLENT) + && needEncoding) + { + OUString encoding; + // first check if we can find the encoding in the filter options (headless mode) + mediaDescriptor[utl::MediaDescriptor::PROP_FILTEROPTIONS] >>= encoding; + if (!encoding.isEmpty()) // TODO: check if the encoding string is valid + fileEncoding = encoding.toUtf8().getStr(); + else + { + OUString title; + + switch (creator) + { + case libwps::WPS_MSWORKS: + title = WpResId(STR_ENCODING_DIALOG_TITLE_MSWORKS); + encoding = "CP850"; + break; + case libwps::WPS_RESERVED_0: // MS Write + title = WpResId(STR_ENCODING_DIALOG_TITLE_MSWRITE); + encoding = "CP1252"; + break; + case libwps::WPS_RESERVED_1: // DosWord + title = WpResId(STR_ENCODING_DIALOG_TITLE_DOSWORD); + encoding = "CP850"; + break; + default: + title = WpResId(STR_ENCODING_DIALOG_TITLE); + encoding = "CP850"; + break; + } + + fileEncoding = encoding.toUtf8().getStr(); // set default to the proposed encoding + try + { + writerperfect::WPFTEncodingDialog aDlg(pParent, title, encoding); + if (aDlg.run() == RET_OK) + { + if (!aDlg.GetEncoding().isEmpty()) + fileEncoding = aDlg.GetEncoding().toUtf8().getStr(); + } + // we can fail because we are in headless mode, the user has cancelled conversion, ... + else if (aDlg.hasUserCalledCancel()) + return false; + } + catch (css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("writerperfect", "ignoring"); + } + } + } + return libwps::WPS_OK + == libwps::WPSDocument::parse(&rInput, &rGenerator, "", fileEncoding.c_str()); +} + +bool MSWorksImportFilter::doDetectFormat(librevenge::RVNGInputStream& rInput, OUString& rTypeName) +{ + libwps::WPSKind kind = libwps::WPS_TEXT; + libwps::WPSCreator creator; + bool needEncoding; + const libwps::WPSConfidence confidence + = libwps::WPSDocument::isFileFormatSupported(&rInput, kind, creator, needEncoding); + + if ((kind == libwps::WPS_TEXT) && (confidence == libwps::WPS_CONFIDENCE_EXCELLENT)) + { + switch (creator) + { + case libwps::WPS_MSWORKS: + rTypeName = "writer_MS_Works_Document"; + break; + case libwps::WPS_RESERVED_0: + rTypeName = "writer_MS_Write"; + break; + case libwps::WPS_RESERVED_1: + rTypeName = "writer_DosWord"; + break; + case libwps::WPS_RESERVED_4: + rTypeName = "writer_PocketWord_File"; + break; + default: + break; + } + } + + return !rTypeName.isEmpty(); +} + +void MSWorksImportFilter::doRegisterHandlers(OdtGenerator& rGenerator) +{ + rGenerator.registerEmbeddedObjectHandler("image/wks-ods", &handleEmbeddedWKSObject); +} + +// XServiceInfo +OUString SAL_CALL MSWorksImportFilter::getImplementationName() +{ + return "com.sun.star.comp.Writer.MSWorksImportFilter"; +} + +sal_Bool SAL_CALL MSWorksImportFilter::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence<OUString> SAL_CALL MSWorksImportFilter::getSupportedServiceNames() +{ + return { "com.sun.star.document.ImportFilter", "com.sun.star.document.ExtendedTypeDetection" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_MSWorksImportFilter_get_implementation( + css::uno::XComponentContext* const context, const css::uno::Sequence<css::uno::Any>&) +{ + return cppu::acquire(new MSWorksImportFilter(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/MSWorksImportFilter.hxx b/writerperfect/source/writer/MSWorksImportFilter.hxx new file mode 100644 index 000000000..629904fda --- /dev/null +++ b/writerperfect/source/writer/MSWorksImportFilter.hxx @@ -0,0 +1,41 @@ +/* -*- 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 <com/sun/star/uno/XComponentContext.hpp> + +#include <ImportFilter.hxx> + +#include <DocumentHandlerForOdt.hxx> + +/* This component will be instantiated for both import or export. Whether it calls + * setSourceDocument or setTargetDocument determines which Impl function the filter + * member calls */ +class MSWorksImportFilter : public writerperfect::ImportFilter<OdtGenerator> +{ +public: + explicit MSWorksImportFilter(const css::uno::Reference<css::uno::XComponentContext>& rxContext) + : writerperfect::ImportFilter<OdtGenerator>(rxContext) + { + } + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + +private: + virtual bool doDetectFormat(librevenge::RVNGInputStream& rInput, OUString& rTypeName) override; + virtual bool doImportDocument(weld::Window* pParent, librevenge::RVNGInputStream& rInput, + OdtGenerator& rGenerator, utl::MediaDescriptor&) override; + virtual void doRegisterHandlers(OdtGenerator& rGenerator) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/MWAWImportFilter.cxx b/writerperfect/source/writer/MWAWImportFilter.cxx new file mode 100644 index 000000000..33fe2d95f --- /dev/null +++ b/writerperfect/source/writer/MWAWImportFilter.cxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* MWAWImportFilter: Sets up the filter, and calls DocumentCollector + * to do the actual filtering + * + * 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 <cppuhelper/supportsservice.hxx> + +#include <libmwaw/libmwaw.hxx> + +#include "MWAWImportFilter.hxx" + +static bool handleEmbeddedMWAWGraphicObject(const librevenge::RVNGBinaryData& data, + OdfDocumentHandler* pHandler, + const OdfStreamType streamType) +{ + OdgGenerator exporter; + exporter.addDocumentHandler(pHandler, streamType); + return MWAWDocument::decodeGraphic(data, &exporter); +} + +static bool handleEmbeddedMWAWSpreadsheetObject(const librevenge::RVNGBinaryData& data, + OdfDocumentHandler* pHandler, + const OdfStreamType streamType) +{ + OdsGenerator exporter; + exporter.registerEmbeddedObjectHandler("image/mwaw-odg", &handleEmbeddedMWAWGraphicObject); + exporter.addDocumentHandler(pHandler, streamType); + return MWAWDocument::decodeSpreadsheet(data, &exporter); +} + +bool MWAWImportFilter::doImportDocument(weld::Window*, librevenge::RVNGInputStream& rInput, + OdtGenerator& rGenerator, utl::MediaDescriptor&) +{ + return MWAWDocument::MWAW_R_OK == MWAWDocument::parse(&rInput, &rGenerator); +} + +bool MWAWImportFilter::doDetectFormat(librevenge::RVNGInputStream& rInput, OUString& rTypeName) +{ + rTypeName.clear(); + + MWAWDocument::Type docType = MWAWDocument::MWAW_T_UNKNOWN; + MWAWDocument::Kind docKind = MWAWDocument::MWAW_K_UNKNOWN; + const MWAWDocument::Confidence confidence + = MWAWDocument::isFileFormatSupported(&rInput, docType, docKind); + + if (confidence == MWAWDocument::MWAW_C_EXCELLENT) + { + if (docKind == MWAWDocument::MWAW_K_TEXT) + { + switch (docType) + { + case MWAWDocument::MWAW_T_CLARISWORKS: + rTypeName = "writer_ClarisWorks"; + break; + case MWAWDocument::MWAW_T_MACWRITE: + case MWAWDocument::MWAW_T_MACWRITEPRO: + rTypeName = "writer_MacWrite"; + break; + case MWAWDocument::MWAW_T_MARINERWRITE: + rTypeName = "writer_Mariner_Write"; + break; + case MWAWDocument::MWAW_T_MICROSOFTWORD: + rTypeName = "writer_Mac_Word"; + break; + case MWAWDocument::MWAW_T_MICROSOFTWORKS: + rTypeName = "writer_Mac_Works"; + break; + case MWAWDocument::MWAW_T_WRITENOW: + rTypeName = "writer_WriteNow"; + break; + default: + rTypeName = "MWAW_Text_Document"; + break; + } + } + } + + return !rTypeName.isEmpty(); +} + +void MWAWImportFilter::doRegisterHandlers(OdtGenerator& rGenerator) +{ + rGenerator.registerEmbeddedObjectHandler("image/mwaw-odg", &handleEmbeddedMWAWGraphicObject); + rGenerator.registerEmbeddedObjectHandler("image/mwaw-ods", + &handleEmbeddedMWAWSpreadsheetObject); +} + +// XServiceInfo +OUString SAL_CALL MWAWImportFilter::getImplementationName() +{ + return "com.sun.star.comp.Writer.MWAWImportFilter"; +} + +sal_Bool SAL_CALL MWAWImportFilter::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence<OUString> SAL_CALL MWAWImportFilter::getSupportedServiceNames() +{ + return { "com.sun.star.document.ImportFilter", "com.sun.star.document.ExtendedTypeDetection" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_MWAWImportFilter_get_implementation( + css::uno::XComponentContext* const context, const css::uno::Sequence<css::uno::Any>&) +{ + return cppu::acquire(new MWAWImportFilter(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/MWAWImportFilter.hxx b/writerperfect/source/writer/MWAWImportFilter.hxx new file mode 100644 index 000000000..b7bb19268 --- /dev/null +++ b/writerperfect/source/writer/MWAWImportFilter.hxx @@ -0,0 +1,41 @@ +/* -*- 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 <com/sun/star/uno/XComponentContext.hpp> + +#include <ImportFilter.hxx> + +#include <DocumentHandlerForOdt.hxx> + +/* This component will be instantiated for both import or export. Whether it calls + * setSourceDocument or setTargetDocument determines which Impl function the filter + * member calls */ +class MWAWImportFilter : public writerperfect::ImportFilter<OdtGenerator> +{ +public: + explicit MWAWImportFilter(const css::uno::Reference<css::uno::XComponentContext>& rxContext) + : writerperfect::ImportFilter<OdtGenerator>(rxContext) + { + } + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + +private: + virtual bool doDetectFormat(librevenge::RVNGInputStream& rInput, OUString& rTypeName) override; + virtual bool doImportDocument(weld::Window* pParent, librevenge::RVNGInputStream& rInput, + OdtGenerator& rGenerator, utl::MediaDescriptor&) override; + virtual void doRegisterHandlers(OdtGenerator& rGenerator) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/PagesImportFilter.cxx b/writerperfect/source/writer/PagesImportFilter.cxx new file mode 100644 index 000000000..9d8bb5eb3 --- /dev/null +++ b/writerperfect/source/writer/PagesImportFilter.cxx @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* PagesImportFilter: Sets up the filter, and calls DocumentCollector + * to do the actual filtering + * + * 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 <libetonyek/libetonyek.h> + +#include <cppuhelper/supportsservice.hxx> + +#include "PagesImportFilter.hxx" + +using libetonyek::EtonyekDocument; + +bool PagesImportFilter::doImportDocument(weld::Window*, librevenge::RVNGInputStream& rInput, + OdtGenerator& rGenerator, utl::MediaDescriptor&) +{ + return EtonyekDocument::parse(&rInput, &rGenerator); +} + +bool PagesImportFilter::doDetectFormat(librevenge::RVNGInputStream& rInput, OUString& rTypeName) +{ + EtonyekDocument::Type type = EtonyekDocument::TYPE_UNKNOWN; + const EtonyekDocument::Confidence confidence = EtonyekDocument::isSupported(&rInput, &type); + if ((confidence == EtonyekDocument::CONFIDENCE_EXCELLENT) + && (type == EtonyekDocument::TYPE_PAGES)) + { + rTypeName = "writer_ApplePages"; + return true; + } + + return false; +} + +// XServiceInfo +OUString SAL_CALL PagesImportFilter::getImplementationName() +{ + return "org.libreoffice.comp.Writer.PagesImportFilter"; +} + +sal_Bool SAL_CALL PagesImportFilter::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence<OUString> SAL_CALL PagesImportFilter::getSupportedServiceNames() +{ + return { "com.sun.star.document.ImportFilter", "com.sun.star.document.ExtendedTypeDetection" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +org_libreoffice_comp_Writer_PagesImportFilter_get_implementation( + css::uno::XComponentContext* const context, const css::uno::Sequence<css::uno::Any>&) +{ + return cppu::acquire(new PagesImportFilter(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/PagesImportFilter.hxx b/writerperfect/source/writer/PagesImportFilter.hxx new file mode 100644 index 000000000..83fc04618 --- /dev/null +++ b/writerperfect/source/writer/PagesImportFilter.hxx @@ -0,0 +1,40 @@ +/* -*- 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 <com/sun/star/uno/XComponentContext.hpp> + +#include <DocumentHandlerForOdt.hxx> +#include <ImportFilter.hxx> + +/* This component will be instantiated for both import or export. Whether it calls + * setSourceDocument or setTargetDocument determines which Impl function the filter + * member calls */ +class PagesImportFilter : public writerperfect::ImportFilter<OdtGenerator> +{ +public: + explicit PagesImportFilter(const css::uno::Reference<css::uno::XComponentContext>& rxContext) + : writerperfect::ImportFilter<OdtGenerator>(rxContext) + { + } + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + +private: + virtual bool doDetectFormat(librevenge::RVNGInputStream& rInput, OUString& rTypeName) override; + virtual bool doImportDocument(weld::Window* pParent, librevenge::RVNGInputStream& rInput, + OdtGenerator& rGenerator, + utl::MediaDescriptor& rDescriptor) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/StarOfficeWriterImportFilter.cxx b/writerperfect/source/writer/StarOfficeWriterImportFilter.cxx new file mode 100644 index 000000000..849ede0d7 --- /dev/null +++ b/writerperfect/source/writer/StarOfficeWriterImportFilter.cxx @@ -0,0 +1,128 @@ +/* -*- 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 <cppuhelper/supportsservice.hxx> + +#include <libstaroffice/libstaroffice.hxx> + +#include <sfx2/passwd.hxx> + +#include "StarOfficeWriterImportFilter.hxx" + +using com::sun::star::uno::Sequence; +using com::sun::star::uno::XComponentContext; +using com::sun::star::uno::XInterface; + +static bool handleEmbeddedSTOFFWriterGraphicObject(const librevenge::RVNGBinaryData& data, + OdfDocumentHandler* pHandler, + const OdfStreamType streamType) +{ + OdgGenerator exporter; + exporter.addDocumentHandler(pHandler, streamType); + return STOFFDocument::decodeGraphic(data, &exporter); +} + +static bool handleEmbeddedSTOFFWriterSpreadsheetObject(const librevenge::RVNGBinaryData& data, + OdfDocumentHandler* pHandler, + const OdfStreamType streamType) +{ + OdsGenerator exporter; + exporter.registerEmbeddedObjectHandler("image/stoff-odg", + &handleEmbeddedSTOFFWriterGraphicObject); + exporter.addDocumentHandler(pHandler, streamType); + return STOFFDocument::decodeSpreadsheet(data, &exporter); +} + +bool StarOfficeWriterImportFilter::doImportDocument(weld::Window* pParent, + librevenge::RVNGInputStream& rInput, + OdtGenerator& rGenerator, utl::MediaDescriptor&) +{ + STOFFDocument::Kind docKind = STOFFDocument::STOFF_K_UNKNOWN; + const STOFFDocument::Confidence confidence + = STOFFDocument::isFileFormatSupported(&rInput, docKind); + OString aUtf8Passwd; + if (confidence == STOFFDocument::STOFF_C_SUPPORTED_ENCRYPTION) + { + // try to ask for a password + try + { + SfxPasswordDialog aPasswdDlg(pParent); + aPasswdDlg.SetMinLen(0); + if (!aPasswdDlg.run()) + return false; + OUString aPasswd = aPasswdDlg.GetPassword(); + aUtf8Passwd = OUStringToOString(aPasswd, RTL_TEXTENCODING_UTF8); + } + catch (...) + { + // ok, we will probably guess it + } + } + return STOFFDocument::STOFF_R_OK + == STOFFDocument::parse(&rInput, &rGenerator, + !aUtf8Passwd.isEmpty() ? aUtf8Passwd.getStr() : nullptr); +} + +bool StarOfficeWriterImportFilter::doDetectFormat(librevenge::RVNGInputStream& rInput, + OUString& rTypeName) +{ + rTypeName.clear(); + + STOFFDocument::Kind docKind = STOFFDocument::STOFF_K_UNKNOWN; + const STOFFDocument::Confidence confidence + = STOFFDocument::isFileFormatSupported(&rInput, docKind); + + if (confidence == STOFFDocument::STOFF_C_EXCELLENT + || confidence == STOFFDocument::STOFF_C_SUPPORTED_ENCRYPTION) + { + switch (docKind) + { + case STOFFDocument::STOFF_K_TEXT: + rTypeName = "StarOffice_Writer"; + break; + default: + break; + } + } + + return !rTypeName.isEmpty(); +} + +void StarOfficeWriterImportFilter::doRegisterHandlers(OdtGenerator& rGenerator) +{ + rGenerator.registerEmbeddedObjectHandler("image/stoff-odg", + &handleEmbeddedSTOFFWriterGraphicObject); + rGenerator.registerEmbeddedObjectHandler("image/stoff-ods", + &handleEmbeddedSTOFFWriterSpreadsheetObject); +} + +// XServiceInfo +OUString SAL_CALL StarOfficeWriterImportFilter::getImplementationName() +{ + return "org.libreoffice.comp.Writer.StarOfficeWriterImportFilter"; +} + +sal_Bool SAL_CALL StarOfficeWriterImportFilter::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence<OUString> SAL_CALL StarOfficeWriterImportFilter::getSupportedServiceNames() +{ + return { "com.sun.star.document.ImportFilter", "com.sun.star.document.ExtendedTypeDetection" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +org_libreoffice_comp_Writer_StarOfficeWriterImportFilter_get_implementation( + css::uno::XComponentContext* const context, const css::uno::Sequence<css::uno::Any>&) +{ + return cppu::acquire(new StarOfficeWriterImportFilter(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/StarOfficeWriterImportFilter.hxx b/writerperfect/source/writer/StarOfficeWriterImportFilter.hxx new file mode 100644 index 000000000..8310f1cbf --- /dev/null +++ b/writerperfect/source/writer/StarOfficeWriterImportFilter.hxx @@ -0,0 +1,42 @@ +/* -*- 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 <com/sun/star/uno/XComponentContext.hpp> + +#include <ImportFilter.hxx> + +#include <DocumentHandlerForOdt.hxx> + +/* This component will be instantiated for both import or export. Whether it calls + * setSourceDocument or setTargetDocument determines which Impl function the filter + * member calls */ +class StarOfficeWriterImportFilter : public writerperfect::ImportFilter<OdtGenerator> +{ +public: + explicit StarOfficeWriterImportFilter( + const css::uno::Reference<css::uno::XComponentContext>& rxContext) + : writerperfect::ImportFilter<OdtGenerator>(rxContext) + { + } + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + +private: + virtual bool doDetectFormat(librevenge::RVNGInputStream& rInput, OUString& rTypeName) override; + virtual bool doImportDocument(weld::Window* pParent, librevenge::RVNGInputStream& rInput, + OdtGenerator& rGenerator, utl::MediaDescriptor&) override; + virtual void doRegisterHandlers(OdtGenerator& rGenerator) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/WordPerfectImportFilter.cxx b/writerperfect/source/writer/WordPerfectImportFilter.cxx new file mode 100644 index 000000000..46e3241ee --- /dev/null +++ b/writerperfect/source/writer/WordPerfectImportFilter.cxx @@ -0,0 +1,238 @@ +/* -*- 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/. + */ +/* "This product is not manufactured, approved, or supported by + * Corel Corporation or Corel Corporation Limited." + */ + +#include <osl/diagnose.h> + +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/xml/sax/XFastDocumentHandler.hpp> +#include <com/sun/star/uno/Reference.h> +#include <cppuhelper/supportsservice.hxx> + +#include <DocumentHandler.hxx> +#include <WPXSvInputStream.hxx> + +#include <sfx2/passwd.hxx> +#include <ucbhelper/content.hxx> +#include <vcl/svapp.hxx> +#include <xmloff/xmlimp.hxx> + +#include <libwpd/libwpd.h> +#include <libwpg/libwpg.h> + +#include "WordPerfectImportFilter.hxx" + +using com::sun::star::uno::Reference; + +using com::sun::star::awt::XWindow; +using com::sun::star::document::XImporter; +using com::sun::star::io::XInputStream; +using com::sun::star::xml::sax::XFastDocumentHandler; + +using writerperfect::DocumentHandler; +using writerperfect::WPXSvInputStream; + +static bool handleEmbeddedWPGObject(const librevenge::RVNGBinaryData& data, + OdfDocumentHandler* pHandler, const OdfStreamType streamType) +{ + OdgGenerator exporter; + exporter.addDocumentHandler(pHandler, streamType); + + libwpg::WPGFileFormat fileFormat = libwpg::WPG_AUTODETECT; + + if (!libwpg::WPGraphics::isSupported(data.getDataStream())) + fileFormat = libwpg::WPG_WPG1; + + return libwpg::WPGraphics::parse(data.getDataStream(), &exporter, fileFormat); +} + +static bool handleEmbeddedWPGImage(const librevenge::RVNGBinaryData& input, + librevenge::RVNGBinaryData& output) +{ + libwpg::WPGFileFormat fileFormat = libwpg::WPG_AUTODETECT; + + if (!libwpg::WPGraphics::isSupported(input.getDataStream())) + fileFormat = libwpg::WPG_WPG1; + + librevenge::RVNGStringVector svgOutput; + librevenge::RVNGSVGDrawingGenerator aSVGGenerator(svgOutput, ""); + + if (!libwpg::WPGraphics::parse(input.getDataStream(), &aSVGGenerator, fileFormat)) + return false; + + if (svgOutput.empty()) + return false; + + assert(1 == svgOutput.size()); + + output.clear(); + output.append(reinterpret_cast<const unsigned char*>(svgOutput[0].cstr()), svgOutput[0].size()); + return true; +} + +bool WordPerfectImportFilter::importImpl( + const css::uno::Sequence<css::beans::PropertyValue>& aDescriptor) +{ + Reference<XInputStream> xInputStream; + Reference<XWindow> xDialogParent; + for (const auto& rValue : aDescriptor) + { + if (rValue.Name == "InputStream") + rValue.Value >>= xInputStream; + else if (rValue.Name == "ParentWindow") + rValue.Value >>= xDialogParent; + } + if (!xInputStream.is()) + { + OSL_ASSERT(false); + return false; + } + + WPXSvInputStream input(xInputStream); + + OString aUtf8Passwd; + + libwpd::WPDConfidence confidence = libwpd::WPDocument::isFileFormatSupported(&input); + + if (libwpd::WPD_CONFIDENCE_SUPPORTED_ENCRYPTION == confidence) + { + int unsuccessfulAttempts = 0; + while (true) + { + SfxPasswordDialog aPasswdDlg(Application::GetFrameWeld(xDialogParent)); + aPasswdDlg.SetMinLen(0); + if (!aPasswdDlg.run()) + return false; + OUString aPasswd = aPasswdDlg.GetPassword(); + aUtf8Passwd = OUStringToOString(aPasswd, RTL_TEXTENCODING_UTF8); + if (libwpd::WPD_PASSWORD_MATCH_OK + == libwpd::WPDocument::verifyPassword(&input, aUtf8Passwd.getStr())) + break; + else + unsuccessfulAttempts++; + if (unsuccessfulAttempts == 3) // timeout after 3 password attempts + return false; + } + } + + // An XML import service: what we push sax messages to. + Reference<XInterface> xInternalFilter + = mxContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.comp.Writer.XMLOasisImporter", mxContext); + assert(xInternalFilter); + css::uno::Reference<css::xml::sax::XFastDocumentHandler> xInternalHandler(xInternalFilter, + css::uno::UNO_QUERY); + assert(xInternalHandler); + + // The XImporter sets up an empty target document for XDocumentHandler to write to. + Reference<XImporter> xImporter(xInternalHandler, css::uno::UNO_QUERY); + xImporter->setTargetDocument(mxDoc); + + // OO Document Handler: abstract class to handle document SAX messages, concrete implementation here + // writes to in-memory target doc + DocumentHandler aHandler( + new SvXMLLegacyToFastDocHandler(static_cast<SvXMLImport*>(xInternalHandler.get()))); + + OdtGenerator collector; + collector.addDocumentHandler(&aHandler, ODF_FLAT_XML); + collector.registerEmbeddedObjectHandler("image/x-wpg", &handleEmbeddedWPGObject); + collector.registerEmbeddedImageHandler("image/x-wpg", &handleEmbeddedWPGImage); + return libwpd::WPD_OK + == libwpd::WPDocument::parse(&input, &collector, + aUtf8Passwd.isEmpty() ? nullptr : aUtf8Passwd.getStr()); +} + +sal_Bool SAL_CALL +WordPerfectImportFilter::filter(const css::uno::Sequence<css::beans::PropertyValue>& aDescriptor) +{ + return importImpl(aDescriptor); +} +void SAL_CALL WordPerfectImportFilter::cancel() {} + +// XImporter +void SAL_CALL +WordPerfectImportFilter::setTargetDocument(const Reference<css::lang::XComponent>& xDoc) +{ + mxDoc = xDoc; +} + +// XExtendedFilterDetection +OUString SAL_CALL +WordPerfectImportFilter::detect(css::uno::Sequence<css::beans::PropertyValue>& Descriptor) +{ + libwpd::WPDConfidence confidence = libwpd::WPD_CONFIDENCE_NONE; + OUString sTypeName; + sal_Int32 nLength = Descriptor.getLength(); + sal_Int32 location = nLength; + const css::beans::PropertyValue* pValue = Descriptor.getConstArray(); + Reference<XInputStream> xInputStream; + for (sal_Int32 i = 0; i < nLength; i++) + { + if (pValue[i].Name == "TypeName") + location = i; + else if (pValue[i].Name == "InputStream") + pValue[i].Value >>= xInputStream; + } + + if (!xInputStream.is()) + return OUString(); + + WPXSvInputStream input(xInputStream); + + confidence = libwpd::WPDocument::isFileFormatSupported(&input); + + if (confidence == libwpd::WPD_CONFIDENCE_EXCELLENT + || confidence == libwpd::WPD_CONFIDENCE_SUPPORTED_ENCRYPTION) + sTypeName = "writer_WordPerfect_Document"; + + if (!sTypeName.isEmpty()) + { + if (location == nLength) + { + Descriptor.realloc(nLength + 1); + Descriptor.getArray()[location].Name = "TypeName"; + } + + Descriptor.getArray()[location].Value <<= sTypeName; + } + + return sTypeName; +} + +// XInitialization +void SAL_CALL +WordPerfectImportFilter::initialize(const css::uno::Sequence<css::uno::Any>& /*aArguments*/) +{ +} + +// XServiceInfo +OUString SAL_CALL WordPerfectImportFilter::getImplementationName() +{ + return "com.sun.star.comp.Writer.WordPerfectImportFilter"; +} + +sal_Bool SAL_CALL WordPerfectImportFilter::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence<OUString> SAL_CALL WordPerfectImportFilter::getSupportedServiceNames() +{ + return { "com.sun.star.document.ImportFilter", "com.sun.star.document.ExtendedTypeDetection" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_WordPerfectImportFilter_get_implementation( + css::uno::XComponentContext* const context, const css::uno::Sequence<css::uno::Any>&) +{ + return cppu::acquire(new WordPerfectImportFilter(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/WordPerfectImportFilter.hxx b/writerperfect/source/writer/WordPerfectImportFilter.hxx new file mode 100644 index 000000000..a9fc331ae --- /dev/null +++ b/writerperfect/source/writer/WordPerfectImportFilter.hxx @@ -0,0 +1,63 @@ +/* -*- 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/. + */ +/* "This product is not manufactured, approved, or supported by + * Corel Corporation or Corel Corporation Limited." + */ +#pragma once + +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/document/XImporter.hpp> +#include <com/sun/star/document/XExtendedFilterDetection.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> + +/* This component will be instantiated for both import or export. Whether it calls + * setSourceDocument or setTargetDocument determines which Impl function the filter + * member calls */ +class WordPerfectImportFilter + : public cppu::WeakImplHelper<css::document::XFilter, css::document::XImporter, + css::document::XExtendedFilterDetection, + css::lang::XInitialization, css::lang::XServiceInfo> +{ + css::uno::Reference<css::uno::XComponentContext> mxContext; + css::uno::Reference<css::lang::XComponent> mxDoc; + + /// @throws css::uno::RuntimeException + bool importImpl(const css::uno::Sequence<css::beans::PropertyValue>& aDescriptor); + +public: + explicit WordPerfectImportFilter( + const css::uno::Reference<css::uno::XComponentContext>& rxContext) + : mxContext(rxContext) + { + } + + // XFilter + virtual sal_Bool SAL_CALL + filter(const css::uno::Sequence<css::beans::PropertyValue>& aDescriptor) override; + virtual void SAL_CALL cancel() override; + + // XImporter + virtual void SAL_CALL + setTargetDocument(const css::uno::Reference<css::lang::XComponent>& xDoc) override; + + //XExtendedFilterDetection + virtual OUString SAL_CALL + detect(css::uno::Sequence<css::beans::PropertyValue>& Descriptor) override; + + // XInitialization + virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& aArguments) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/XMLBase64ImportContext.cxx b/writerperfect/source/writer/exp/XMLBase64ImportContext.cxx new file mode 100644 index 000000000..a7780e2d5 --- /dev/null +++ b/writerperfect/source/writer/exp/XMLBase64ImportContext.cxx @@ -0,0 +1,65 @@ +/* -*- 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 "XMLBase64ImportContext.hxx" + +#include <comphelper/base64.hxx> + +using namespace com::sun::star; + +namespace writerperfect::exp +{ +XMLBase64ImportContext::XMLBase64ImportContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +void XMLBase64ImportContext::startElement( + const OUString& /*rName*/, + const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ +} + +void XMLBase64ImportContext::endElement(const OUString& /*rName*/) +{ + m_aBinaryData.append(static_cast<const unsigned char*>(m_aStream.GetData()), + m_aStream.GetSize()); +} + +void XMLBase64ImportContext::characters(const OUString& rChars) +{ + OUString aTrimmedChars(rChars.trim()); + + if (aTrimmedChars.isEmpty()) + return; + + OUString aChars; + if (!m_aBase64CharsLeft.isEmpty()) + { + aChars = m_aBase64CharsLeft + aTrimmedChars; + m_aBase64CharsLeft.clear(); + } + else + aChars = aTrimmedChars; + + uno::Sequence<sal_Int8> aBuffer((aChars.getLength() / 4) * 3); + const sal_Int32 nCharsDecoded = comphelper::Base64::decodeSomeChars(aBuffer, aChars); + m_aStream.WriteBytes(aBuffer.getArray(), aBuffer.getLength()); + if (nCharsDecoded != aChars.getLength()) + m_aBase64CharsLeft = aChars.copy(nCharsDecoded); +} + +const librevenge::RVNGBinaryData& XMLBase64ImportContext::getBinaryData() const +{ + return m_aBinaryData; +} + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/XMLBase64ImportContext.hxx b/writerperfect/source/writer/exp/XMLBase64ImportContext.hxx new file mode 100644 index 000000000..59558746f --- /dev/null +++ b/writerperfect/source/writer/exp/XMLBase64ImportContext.hxx @@ -0,0 +1,42 @@ +/* -*- 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 <librevenge/RVNGBinaryData.h> + +#include <tools/stream.hxx> + +#include "xmlictxt.hxx" + +namespace writerperfect::exp +{ +/// Handler for <office:binary-data>. +class XMLBase64ImportContext : public XMLImportContext +{ +public: + XMLBase64ImportContext(XMLImport& rImport); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; + void SAL_CALL characters(const OUString& rChars) override; + + const librevenge::RVNGBinaryData& getBinaryData() const; + +private: + librevenge::RVNGBinaryData m_aBinaryData; + SvMemoryStream m_aStream; + OUString m_aBase64CharsLeft; +}; + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/XMLFootnoteImportContext.cxx b/writerperfect/source/writer/exp/XMLFootnoteImportContext.cxx new file mode 100644 index 000000000..cdc08c50b --- /dev/null +++ b/writerperfect/source/writer/exp/XMLFootnoteImportContext.cxx @@ -0,0 +1,126 @@ +/* -*- 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 "XMLFootnoteImportContext.hxx" + +#include "xmlimp.hxx" +#include "xmltext.hxx" + +#include <sal/log.hxx> + +using namespace com::sun::star; + +namespace writerperfect::exp +{ +namespace +{ +/// Handler for <text:note-citation>. +class XMLTextNoteCitationContext : public XMLImportContext +{ +public: + XMLTextNoteCitationContext(XMLImport& rImport, librevenge::RVNGPropertyList& rProperties); + + void SAL_CALL characters(const OUString& rCharacters) override; + void SAL_CALL endElement(const OUString& rName) override; + +private: + librevenge::RVNGPropertyList& m_rProperties; + OUString m_aCharacters; +}; +} + +XMLTextNoteCitationContext::XMLTextNoteCitationContext(XMLImport& rImport, + librevenge::RVNGPropertyList& rProperties) + : XMLImportContext(rImport) + , m_rProperties(rProperties) +{ +} + +void XMLTextNoteCitationContext::endElement(const OUString& /*rName*/) +{ + m_rProperties.insert("librevenge:number", m_aCharacters.toUtf8().getStr()); +} + +void XMLTextNoteCitationContext::characters(const OUString& rCharacters) +{ + m_aCharacters += rCharacters; +} + +namespace +{ +/// Handler for <text:note-body>. +class XMLFootnoteBodyImportContext : public XMLImportContext +{ +public: + XMLFootnoteBodyImportContext(XMLImport& rImport, + const librevenge::RVNGPropertyList& rProperties); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; + +private: + const librevenge::RVNGPropertyList& m_rProperties; +}; +} + +XMLFootnoteBodyImportContext::XMLFootnoteBodyImportContext( + XMLImport& rImport, const librevenge::RVNGPropertyList& rProperties) + : XMLImportContext(rImport) + , m_rProperties(rProperties) +{ +} + +rtl::Reference<XMLImportContext> XMLFootnoteBodyImportContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + return CreateTextChildContext(GetImport(), rName); +} + +void XMLFootnoteBodyImportContext::startElement( + const OUString& /*rName*/, + const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + GetImport().GetGenerator().openFootnote(m_rProperties); +} + +void XMLFootnoteBodyImportContext::endElement(const OUString& /*rName*/) +{ + GetImport().GetGenerator().closeFootnote(); +} + +XMLFootnoteImportContext::XMLFootnoteImportContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +rtl::Reference<XMLImportContext> XMLFootnoteImportContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "text:note-citation") + return new XMLTextNoteCitationContext(GetImport(), m_aProperties); + if (rName == "text:note-body") + return new XMLFootnoteBodyImportContext(GetImport(), m_aProperties); + SAL_WARN("writerperfect", "XMLFootnoteImportContext::CreateChildContext: unhandled " << rName); + return nullptr; +} + +void XMLFootnoteImportContext::startElement( + const OUString& /*rName*/, + const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ +} +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/XMLFootnoteImportContext.hxx b/writerperfect/source/writer/exp/XMLFootnoteImportContext.hxx new file mode 100644 index 000000000..50011d388 --- /dev/null +++ b/writerperfect/source/writer/exp/XMLFootnoteImportContext.hxx @@ -0,0 +1,40 @@ +/* -*- 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 <librevenge/librevenge.h> + +#include <rtl/ref.hxx> + +#include "xmlictxt.hxx" + +namespace writerperfect::exp +{ +/// Handler for <text:note>. +class XMLFootnoteImportContext : public XMLImportContext +{ +public: + XMLFootnoteImportContext(XMLImport& rImport); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + +private: + librevenge::RVNGPropertyList m_aProperties; +}; + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/XMLSectionContext.cxx b/writerperfect/source/writer/exp/XMLSectionContext.cxx new file mode 100644 index 000000000..839400e52 --- /dev/null +++ b/writerperfect/source/writer/exp/XMLSectionContext.cxx @@ -0,0 +1,44 @@ +/* -*- 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 "XMLSectionContext.hxx" + +#include "xmlimp.hxx" +#include "xmltext.hxx" + +using namespace com::sun::star; + +namespace writerperfect::exp +{ +XMLSectionContext::XMLSectionContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +rtl::Reference<XMLImportContext> XMLSectionContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + return CreateTextChildContext(GetImport(), rName); +} + +void XMLSectionContext::startElement( + const OUString& /*rName*/, + const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + GetImport().GetGenerator().openSection(librevenge::RVNGPropertyList()); +} + +void XMLSectionContext::endElement(const OUString& /*rName*/) +{ + GetImport().GetGenerator().closeSection(); +} + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/XMLSectionContext.hxx b/writerperfect/source/writer/exp/XMLSectionContext.hxx new file mode 100644 index 000000000..6a1f1e58a --- /dev/null +++ b/writerperfect/source/writer/exp/XMLSectionContext.hxx @@ -0,0 +1,33 @@ +/* -*- 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 "xmlictxt.hxx" + +namespace writerperfect::exp +{ +/// Handler for <text:section>. +class XMLSectionContext : public XMLImportContext +{ +public: + XMLSectionContext(XMLImport& rImport); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; +}; + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/XMLTextFrameContext.cxx b/writerperfect/source/writer/exp/XMLTextFrameContext.cxx new file mode 100644 index 000000000..4c6ca118e --- /dev/null +++ b/writerperfect/source/writer/exp/XMLTextFrameContext.cxx @@ -0,0 +1,171 @@ +/* -*- 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 "XMLTextFrameContext.hxx" + +#include "XMLBase64ImportContext.hxx" +#include "txtparai.hxx" +#include "xmlimp.hxx" +#include "xmltext.hxx" + +#include <sal/log.hxx> + +using namespace com::sun::star; + +namespace writerperfect::exp +{ +namespace +{ +/// Handler for <draw:text-box>. +class XMLTextBoxContext : public XMLImportContext +{ +public: + XMLTextBoxContext(XMLImport& rImport); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; +}; +} + +XMLTextBoxContext::XMLTextBoxContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +rtl::Reference<XMLImportContext> XMLTextBoxContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + return CreateTextChildContext(GetImport(), rName); +} + +void XMLTextBoxContext::startElement( + const OUString& /*rName*/, + const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + GetImport().GetGenerator().openTextBox(librevenge::RVNGPropertyList()); +} + +void XMLTextBoxContext::endElement(const OUString& /*rName*/) +{ + GetImport().GetGenerator().closeTextBox(); +} + +namespace +{ +/// Handler for <draw:image>. +class XMLTextImageContext : public XMLImportContext +{ +public: + XMLTextImageContext(XMLImport& rImport); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; + +private: + OString m_aMimeType; + rtl::Reference<XMLBase64ImportContext> m_xBinaryData; +}; +} + +XMLTextImageContext::XMLTextImageContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +rtl::Reference<XMLImportContext> XMLTextImageContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "office:binary-data") + { + m_xBinaryData = new XMLBase64ImportContext(GetImport()); + return m_xBinaryData; + } + return nullptr; +} + +void XMLTextImageContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + if (rAttributeName == "loext:mime-type" || rAttributeName == "draw:mime-type") + m_aMimeType = OUStringToOString(xAttribs->getValueByIndex(i), RTL_TEXTENCODING_UTF8); + } +} + +void XMLTextImageContext::endElement(const OUString& /*rName*/) +{ + librevenge::RVNGPropertyList aPropertyList; + + aPropertyList.insert("librevenge:mime-type", m_aMimeType.getStr()); + if (m_xBinaryData.is()) + aPropertyList.insert("office:binary-data", m_xBinaryData->getBinaryData()); + + GetImport().GetGenerator().insertBinaryObject(aPropertyList); +} + +XMLTextFrameContext::XMLTextFrameContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +rtl::Reference<XMLImportContext> XMLTextFrameContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "draw:image") + return new XMLTextImageContext(GetImport()); + if (rName == "draw:text-box") + return new XMLTextBoxContext(GetImport()); + SAL_WARN("writerperfect", "XMLTextFrameContext::CreateChildContext: unhandled " << rName); + return nullptr; +} + +void XMLTextFrameContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + librevenge::RVNGPropertyList aPropertyList; + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + const OUString& rAttributeValue = xAttribs->getValueByIndex(i); + + if (rAttributeName == "draw:style-name") + FillStyles(rAttributeValue, GetImport().GetAutomaticGraphicStyles(), + GetImport().GetGraphicStyles(), aPropertyList); + else + { + OString sName = OUStringToOString(rAttributeName, RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(rAttributeValue, RTL_TEXTENCODING_UTF8); + aPropertyList.insert(sName.getStr(), sValue.getStr()); + } + } + GetImport().GetGenerator().openFrame(aPropertyList); +} + +void XMLTextFrameContext::endElement(const OUString& /*rName*/) +{ + GetImport().GetGenerator().closeFrame(); +} + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/XMLTextFrameContext.hxx b/writerperfect/source/writer/exp/XMLTextFrameContext.hxx new file mode 100644 index 000000000..9c58849e6 --- /dev/null +++ b/writerperfect/source/writer/exp/XMLTextFrameContext.hxx @@ -0,0 +1,38 @@ +/* -*- 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 <rtl/ref.hxx> + +#include "xmlictxt.hxx" + +namespace writerperfect::exp +{ +class XMLBase64ImportContext; + +/// Handler for <draw:frame>. +class XMLTextFrameContext : public XMLImportContext +{ +public: + XMLTextFrameContext(XMLImport& rImport); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; +}; + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/XMLTextListContext.cxx b/writerperfect/source/writer/exp/XMLTextListContext.cxx new file mode 100644 index 000000000..8798c9a39 --- /dev/null +++ b/writerperfect/source/writer/exp/XMLTextListContext.cxx @@ -0,0 +1,33 @@ +/* -*- 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 "XMLTextListContext.hxx" + +#include "XMLTextListItemContext.hxx" + +using namespace com::sun::star; + +namespace writerperfect::exp +{ +XMLTextListContext::XMLTextListContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +rtl::Reference<XMLImportContext> XMLTextListContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "text:list-item") + return new XMLTextListItemContext(GetImport()); + return nullptr; +} + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/XMLTextListContext.hxx b/writerperfect/source/writer/exp/XMLTextListContext.hxx new file mode 100644 index 000000000..8a384d7ba --- /dev/null +++ b/writerperfect/source/writer/exp/XMLTextListContext.hxx @@ -0,0 +1,29 @@ +/* -*- 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 "xmlictxt.hxx" + +namespace writerperfect::exp +{ +/// Handler for <text:list>. +class XMLTextListContext : public XMLImportContext +{ +public: + XMLTextListContext(XMLImport& rImport); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; +}; + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/XMLTextListItemContext.cxx b/writerperfect/source/writer/exp/XMLTextListItemContext.cxx new file mode 100644 index 000000000..36f3f2ce6 --- /dev/null +++ b/writerperfect/source/writer/exp/XMLTextListItemContext.cxx @@ -0,0 +1,36 @@ +/* -*- 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 "XMLTextListItemContext.hxx" + +#include "XMLTextListContext.hxx" +#include "txtparai.hxx" + +using namespace com::sun::star; + +namespace writerperfect::exp +{ +XMLTextListItemContext::XMLTextListItemContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +rtl::Reference<XMLImportContext> XMLTextListItemContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "text:p" || rName == "text:h") + return new XMLParaContext(GetImport()); + if (rName == "text:list") + return new XMLTextListContext(GetImport()); + return nullptr; +} + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/XMLTextListItemContext.hxx b/writerperfect/source/writer/exp/XMLTextListItemContext.hxx new file mode 100644 index 000000000..2f17194d4 --- /dev/null +++ b/writerperfect/source/writer/exp/XMLTextListItemContext.hxx @@ -0,0 +1,29 @@ +/* -*- 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 "xmlictxt.hxx" + +namespace writerperfect::exp +{ +/// Handler for <text:list-item>. +class XMLTextListItemContext : public XMLImportContext +{ +public: + XMLTextListItemContext(XMLImport& rImport); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; +}; + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/txtparai.cxx b/writerperfect/source/writer/exp/txtparai.cxx new file mode 100644 index 000000000..101546c39 --- /dev/null +++ b/writerperfect/source/writer/exp/txtparai.cxx @@ -0,0 +1,634 @@ +/* -*- 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 <sal/config.h> + +#include "txtparai.hxx" + +#include <string_view> + +#include "XMLFootnoteImportContext.hxx" +#include "XMLTextFrameContext.hxx" +#include "xmlimp.hxx" + +#include <sal/log.hxx> + +using namespace com::sun::star; + +namespace +{ +/// Looks for rName in rStyles and fills rPropertyList based on that +/// (rAutomaticStyles and rNamedStyles are a list of possible parents). +void FillStyle(const OUString& rName, std::map<OUString, librevenge::RVNGPropertyList>& rStyles, + std::map<OUString, librevenge::RVNGPropertyList>& rAutomaticStyles, + std::map<OUString, librevenge::RVNGPropertyList>& rNamedStyles, + librevenge::RVNGPropertyList& rPropertyList) +{ + auto itStyle = rStyles.find(rName); + if (itStyle == rStyles.end()) + return; + + const librevenge::RVNGPropertyList& rStyle = itStyle->second; + if (rStyle["style:parent-style-name"]) + { + // The style has a parent. + OUString aParent = OStringToOUString(rStyle["style:parent-style-name"]->getStr().cstr(), + RTL_TEXTENCODING_UTF8); + if (!aParent.isEmpty()) + writerperfect::exp::FillStyles(aParent, rAutomaticStyles, rNamedStyles, rPropertyList); + } + + // Apply properties from named style. + librevenge::RVNGPropertyList::Iter itProp(rStyle); + for (itProp.rewind(); itProp.next();) + { + if (std::string_view("style:parent-style-name") != itProp.key()) + rPropertyList.insert(itProp.key(), itProp()->clone()); + } +} +} + +namespace writerperfect::exp +{ +namespace +{ +/// Handler for <text:sequence>. +class XMLTextSequenceContext : public XMLImportContext +{ +public: + XMLTextSequenceContext(XMLImport& rImport, const librevenge::RVNGPropertyList& rPropertyList); + + void SAL_CALL characters(const OUString& rChars) override; + +private: + librevenge::RVNGPropertyList m_aPropertyList; +}; +} + +XMLTextSequenceContext::XMLTextSequenceContext(XMLImport& rImport, + const librevenge::RVNGPropertyList& rPropertyList) + : XMLImportContext(rImport) +{ + // Inherit properties from parent. + librevenge::RVNGPropertyList::Iter itProp(rPropertyList); + for (itProp.rewind(); itProp.next();) + m_aPropertyList.insert(itProp.key(), itProp()->clone()); +} + +void XMLTextSequenceContext::characters(const OUString& rChars) +{ + GetImport().GetGenerator().openSpan(m_aPropertyList); + + OString sCharU8 = OUStringToOString(rChars, RTL_TEXTENCODING_UTF8); + GetImport().GetGenerator().insertText(librevenge::RVNGString(sCharU8.getStr())); + + GetImport().GetGenerator().closeSpan(); +} + +namespace +{ +/// Handler for <text:span>. +class XMLSpanContext : public XMLImportContext +{ +public: + XMLSpanContext(XMLImport& rImport, const librevenge::RVNGPropertyList& rPropertyList); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL characters(const OUString& rChars) override; + +private: + librevenge::RVNGPropertyList m_aPropertyList; +}; +} + +XMLSpanContext::XMLSpanContext(XMLImport& rImport, + const librevenge::RVNGPropertyList& rPropertyList) + : XMLImportContext(rImport) +{ + // Inherit properties from parent. + librevenge::RVNGPropertyList::Iter itProp(rPropertyList); + for (itProp.rewind(); itProp.next();) + m_aPropertyList.insert(itProp.key(), itProp()->clone()); +} + +rtl::Reference<XMLImportContext> XMLSpanContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + return CreateParagraphOrSpanChildContext(GetImport(), rName, m_aPropertyList); +} + +void XMLSpanContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + const OUString& rAttributeValue = xAttribs->getValueByIndex(i); + if (rAttributeName == "text:style-name") + FillStyles(rAttributeValue, GetImport().GetAutomaticTextStyles(), + GetImport().GetTextStyles(), m_aPropertyList); + else + { + OString sName = OUStringToOString(rAttributeName, RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(rAttributeValue, RTL_TEXTENCODING_UTF8); + m_aPropertyList.insert(sName.getStr(), sValue.getStr()); + } + } +} + +void XMLSpanContext::characters(const OUString& rChars) +{ + GetImport().GetGenerator().openSpan(m_aPropertyList); + + OString sCharU8 = OUStringToOString(rChars, RTL_TEXTENCODING_UTF8); + GetImport().GetGenerator().insertText(librevenge::RVNGString(sCharU8.getStr())); + + GetImport().GetGenerator().closeSpan(); +} + +namespace +{ +/// Handler for <text:ruby>. +class XMLRubyContext : public XMLImportContext +{ +public: + XMLRubyContext(XMLImport& rImport, const librevenge::RVNGPropertyList& rPropertyList); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + void SAL_CALL endElement(const OUString& rName) override; + + void SetRubyText(const OUString& rRubyText) { m_sRubyText = rRubyText; } + + OUString& GetRubyBase() { return m_sRubyBase; } + +private: + OUString m_sRubyText; + OUString m_sRubyBase; + librevenge::RVNGPropertyList m_aPropertyList; +}; + +/// Handler for <text:ruby-text>. +class XMLRubyTextContext : public XMLImportContext +{ +public: + XMLRubyTextContext(XMLImport& rImport, XMLRubyContext& rParent) + : XMLImportContext(rImport) + , m_rParent(rParent) + { + } + + void SAL_CALL characters(const OUString& rChars) override { m_rParent.SetRubyText(rChars); } + +private: + XMLRubyContext& m_rParent; +}; + +/// Handler for <text:ruby-base>. +class XMLRubyBaseContext : public XMLImportContext +{ +public: + XMLRubyBaseContext(XMLImport& rImport, XMLRubyContext& rParent) + : XMLImportContext(rImport) + , m_rParent(rParent) + { + } + + void SAL_CALL characters(const OUString& rChars) override { m_rParent.GetRubyBase() += rChars; } + +private: + XMLRubyContext& m_rParent; +}; +} + +XMLRubyContext::XMLRubyContext(XMLImport& rImport, + const librevenge::RVNGPropertyList& rPropertyList) + : XMLImportContext(rImport) +{ + // Inherit properties from parent. + librevenge::RVNGPropertyList::Iter itProp(rPropertyList); + for (itProp.rewind(); itProp.next();) + m_aPropertyList.insert(itProp.key(), itProp()->clone()); +} + +rtl::Reference<XMLImportContext> XMLRubyContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "text:ruby-base") + return new XMLRubyBaseContext(GetImport(), *this); + if (rName == "text:ruby-text") + return new XMLRubyTextContext(GetImport(), *this); + return nullptr; +} + +void XMLRubyContext::endElement(const OUString& /*rName*/) +{ + OString sRubyText = OUStringToOString(m_sRubyText, RTL_TEXTENCODING_UTF8); + OString sRubyBase = OUStringToOString(m_sRubyBase, RTL_TEXTENCODING_UTF8); + if (sRubyText.getLength()) + m_aPropertyList.insert("text:ruby-text", sRubyText.getStr()); + GetImport().GetGenerator().openSpan(m_aPropertyList); + GetImport().GetGenerator().insertText(sRubyBase.getStr()); + GetImport().GetGenerator().closeSpan(); +} + +namespace +{ +/// Base class for contexts that represent a single character only. +class XMLCharContext : public XMLImportContext +{ +public: + XMLCharContext(XMLImport& rImport, const librevenge::RVNGPropertyList& rPropertyList); + + const librevenge::RVNGPropertyList& GetPropertyList() const { return m_aPropertyList; } + +private: + librevenge::RVNGPropertyList m_aPropertyList; +}; +} + +XMLCharContext::XMLCharContext(XMLImport& rImport, + const librevenge::RVNGPropertyList& rPropertyList) + : XMLImportContext(rImport) +{ + // Inherit properties from parent. + librevenge::RVNGPropertyList::Iter itProp(rPropertyList); + for (itProp.rewind(); itProp.next();) + m_aPropertyList.insert(itProp.key(), itProp()->clone()); +} + +namespace +{ +/// Handler for <text:line-break>. +class XMLLineBreakContext : public XMLCharContext +{ +public: + XMLLineBreakContext(XMLImport& rImport, const librevenge::RVNGPropertyList& rPropertyList); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; +}; +} + +XMLLineBreakContext::XMLLineBreakContext(XMLImport& rImport, + const librevenge::RVNGPropertyList& rPropertyList) + : XMLCharContext(rImport, rPropertyList) +{ +} + +void XMLLineBreakContext::startElement( + const OUString& /*rName*/, + const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + GetImport().GetGenerator().openSpan(GetPropertyList()); + GetImport().GetGenerator().insertLineBreak(); + GetImport().GetGenerator().closeSpan(); +} + +namespace +{ +/// Handler for <text:s>. +class XMLSpaceContext : public XMLCharContext +{ +public: + XMLSpaceContext(XMLImport& rImport, const librevenge::RVNGPropertyList& rPropertyList); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; +}; +} + +XMLSpaceContext::XMLSpaceContext(XMLImport& rImport, + const librevenge::RVNGPropertyList& rPropertyList) + : XMLCharContext(rImport, rPropertyList) +{ +} + +void XMLSpaceContext::startElement( + const OUString& /*rName*/, + const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + GetImport().GetGenerator().openSpan(GetPropertyList()); + GetImport().GetGenerator().insertSpace(); + GetImport().GetGenerator().closeSpan(); +} + +namespace +{ +/// Handler for <text:tab>. +class XMLTabContext : public XMLCharContext +{ +public: + XMLTabContext(XMLImport& rImport, const librevenge::RVNGPropertyList& rPropertyList); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; +}; +} + +XMLTabContext::XMLTabContext(XMLImport& rImport, const librevenge::RVNGPropertyList& rPropertyList) + : XMLCharContext(rImport, rPropertyList) +{ +} + +void XMLTabContext::startElement( + const OUString& /*rName*/, + const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + GetImport().GetGenerator().openSpan(GetPropertyList()); + GetImport().GetGenerator().insertTab(); + GetImport().GetGenerator().closeSpan(); +} + +namespace +{ +/// Handler for <draw:a>. +class XMLTextFrameHyperlinkContext : public XMLImportContext +{ +public: + XMLTextFrameHyperlinkContext(XMLImport& rImport, + const librevenge::RVNGPropertyList& rPropertyList); + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; + void SAL_CALL characters(const OUString& rChars) override; + +private: + librevenge::RVNGPropertyList m_aPropertyList; + PopupState m_ePopupState = PopupState::NONE; +}; +} + +XMLTextFrameHyperlinkContext::XMLTextFrameHyperlinkContext( + XMLImport& rImport, const librevenge::RVNGPropertyList& rPropertyList) + : XMLImportContext(rImport) +{ + // Inherit properties from parent. + librevenge::RVNGPropertyList::Iter itProp(rPropertyList); + for (itProp.rewind(); itProp.next();) + m_aPropertyList.insert(itProp.key(), itProp()->clone()); +} + +rtl::Reference<XMLImportContext> XMLTextFrameHyperlinkContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + return CreateParagraphOrSpanChildContext(GetImport(), rName, m_aPropertyList); +} + +void XMLTextFrameHyperlinkContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + librevenge::RVNGPropertyList aPropertyList; + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + const OUString& rAttributeValue = xAttribs->getValueByIndex(i); + if (rAttributeName == "text:style-name") + // This affects the nested span's properties. + FillStyles(rAttributeValue, GetImport().GetAutomaticTextStyles(), + GetImport().GetTextStyles(), m_aPropertyList); + else + { + if (rAttributeName == "xlink:href") + { + m_ePopupState = GetImport().FillPopupData(rAttributeValue, aPropertyList); + if (m_ePopupState != PopupState::NotConsumed) + continue; + } + + // This affects the link's properties. + OString sName = OUStringToOString(rAttributeName, RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(rAttributeValue, RTL_TEXTENCODING_UTF8); + aPropertyList.insert(sName.getStr(), sValue.getStr()); + } + } + + if (m_ePopupState != PopupState::Ignore) + GetImport().GetGenerator().openLink(aPropertyList); +} + +void XMLTextFrameHyperlinkContext::endElement(const OUString& /*rName*/) +{ + if (m_ePopupState != PopupState::Ignore) + GetImport().GetGenerator().closeLink(); +} + +void XMLTextFrameHyperlinkContext::characters(const OUString& rChars) +{ + GetImport().GetGenerator().openSpan(m_aPropertyList); + + OString sCharU8 = OUStringToOString(rChars, RTL_TEXTENCODING_UTF8); + GetImport().GetGenerator().insertText(librevenge::RVNGString(sCharU8.getStr())); + + GetImport().GetGenerator().closeSpan(); +} + +namespace +{ +/// Handler for <text:a>. +class XMLHyperlinkContext : public XMLImportContext +{ +public: + XMLHyperlinkContext(XMLImport& rImport, const librevenge::RVNGPropertyList& rPropertyList); + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; + void SAL_CALL characters(const OUString& rChars) override; + +private: + librevenge::RVNGPropertyList m_aPropertyList; + PopupState m_ePopupState = PopupState::NONE; +}; +} + +XMLHyperlinkContext::XMLHyperlinkContext(XMLImport& rImport, + const librevenge::RVNGPropertyList& rPropertyList) + : XMLImportContext(rImport) +{ + // Inherit properties from parent. + librevenge::RVNGPropertyList::Iter itProp(rPropertyList); + for (itProp.rewind(); itProp.next();) + m_aPropertyList.insert(itProp.key(), itProp()->clone()); +} + +rtl::Reference<XMLImportContext> XMLHyperlinkContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + return CreateParagraphOrSpanChildContext(GetImport(), rName, m_aPropertyList); +} + +void XMLHyperlinkContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + librevenge::RVNGPropertyList aPropertyList; + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + const OUString& rAttributeValue = xAttribs->getValueByIndex(i); + if (rAttributeName == "text:style-name") + // This affects the nested span's properties. + FillStyles(rAttributeValue, GetImport().GetAutomaticTextStyles(), + GetImport().GetTextStyles(), m_aPropertyList); + else + { + if (rAttributeName == "xlink:href") + { + m_ePopupState = GetImport().FillPopupData(rAttributeValue, aPropertyList); + if (m_ePopupState != PopupState::NotConsumed) + continue; + } + + // This affects the link's properties. + OString sName = OUStringToOString(rAttributeName, RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(rAttributeValue, RTL_TEXTENCODING_UTF8); + aPropertyList.insert(sName.getStr(), sValue.getStr()); + } + } + + if (m_ePopupState != PopupState::Ignore) + GetImport().GetGenerator().openLink(aPropertyList); +} + +void XMLHyperlinkContext::endElement(const OUString& /*rName*/) +{ + if (m_ePopupState != PopupState::Ignore) + GetImport().GetGenerator().closeLink(); +} + +void XMLHyperlinkContext::characters(const OUString& rChars) +{ + GetImport().GetGenerator().openSpan(m_aPropertyList); + + OString sCharU8 = OUStringToOString(rChars, RTL_TEXTENCODING_UTF8); + GetImport().GetGenerator().insertText(librevenge::RVNGString(sCharU8.getStr())); + + GetImport().GetGenerator().closeSpan(); +} + +XMLParaContext::XMLParaContext(XMLImport& rImport, bool bTopLevel) + : XMLImportContext(rImport) + , m_bTopLevel(bTopLevel) +{ +} + +rtl::Reference<XMLImportContext> XMLParaContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "text:a") + return new XMLHyperlinkContext(GetImport(), m_aTextPropertyList); + if (rName == "draw:a") + return new XMLTextFrameHyperlinkContext(GetImport(), m_aTextPropertyList); + if (rName == "text:ruby") + return new XMLRubyContext(GetImport(), m_aTextPropertyList); + return CreateParagraphOrSpanChildContext(GetImport(), rName, m_aTextPropertyList); +} + +void XMLParaContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + librevenge::RVNGPropertyList aPropertyList; + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + const OUString& rAttributeValue = xAttribs->getValueByIndex(i); + if (rAttributeName == "text:style-name") + { + m_aStyleName = rAttributeValue; + FillStyles(m_aStyleName, GetImport().GetAutomaticParagraphStyles(), + GetImport().GetParagraphStyles(), aPropertyList); + FillStyles(m_aStyleName, GetImport().GetAutomaticTextStyles(), + GetImport().GetTextStyles(), m_aTextPropertyList); + if (m_bTopLevel) + GetImport().HandlePageSpan(aPropertyList); + } + else + { + OString sName = OUStringToOString(rAttributeName, RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(rAttributeValue, RTL_TEXTENCODING_UTF8); + aPropertyList.insert(sName.getStr(), sValue.getStr()); + } + } + + GetImport().GetGenerator().openParagraph(aPropertyList); +} + +void XMLParaContext::endElement(const OUString& /*rName*/) +{ + GetImport().GetGenerator().closeParagraph(); +} + +void XMLParaContext::characters(const OUString& rChars) +{ + librevenge::RVNGPropertyList aPropertyList; + if (!m_aStyleName.isEmpty()) + FillStyles(m_aStyleName, GetImport().GetAutomaticTextStyles(), GetImport().GetTextStyles(), + aPropertyList); + GetImport().GetGenerator().openSpan(aPropertyList); + + OString sCharU8 = OUStringToOString(rChars, RTL_TEXTENCODING_UTF8); + GetImport().GetGenerator().insertText(librevenge::RVNGString(sCharU8.getStr())); + + GetImport().GetGenerator().closeSpan(); +} + +rtl::Reference<XMLImportContext> +CreateParagraphOrSpanChildContext(XMLImport& rImport, const OUString& rName, + const librevenge::RVNGPropertyList& rTextPropertyList) +{ + if (rName == "text:span") + return new XMLSpanContext(rImport, rTextPropertyList); + if (rName == "text:line-break") + return new XMLLineBreakContext(rImport, rTextPropertyList); + if (rName == "text:s") + return new XMLSpaceContext(rImport, rTextPropertyList); + if (rName == "text:tab") + return new XMLTabContext(rImport, rTextPropertyList); + if (rName == "draw:frame") + return new XMLTextFrameContext(rImport); + if (rName == "text:sequence") + return new XMLTextSequenceContext(rImport, rTextPropertyList); + if (rName == "text:note") + return new XMLFootnoteImportContext(rImport); + SAL_WARN("writerperfect", "CreateParagraphOrSpanChildContext: unhandled " << rName); + return nullptr; +} + +void FillStyles(const OUString& rName, + std::map<OUString, librevenge::RVNGPropertyList>& rAutomaticStyles, + std::map<OUString, librevenge::RVNGPropertyList>& rNamedStyles, + librevenge::RVNGPropertyList& rPropertyList) +{ + FillStyle(rName, rAutomaticStyles, rAutomaticStyles, rNamedStyles, rPropertyList); + FillStyle(rName, rNamedStyles, rAutomaticStyles, rNamedStyles, rPropertyList); +} + +} // namespace writerperfect + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/txtparai.hxx b/writerperfect/source/writer/exp/txtparai.hxx new file mode 100644 index 000000000..e759dc8f5 --- /dev/null +++ b/writerperfect/source/writer/exp/txtparai.hxx @@ -0,0 +1,59 @@ +/* -*- 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 <map> + +#include <librevenge/librevenge.h> + +#include "xmlictxt.hxx" + +namespace writerperfect::exp +{ +/// Handler for <text:p>/<text:h>. +class XMLParaContext : public XMLImportContext +{ +public: + XMLParaContext(XMLImport& rImport, bool bTopLevel = false); + + rtl::Reference<XMLImportContext> CreateChildContext( + const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) override; + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; + void SAL_CALL characters(const OUString& rChars) override; + +private: + OUString m_aStyleName; + /// List of properties spans should inherit from this paragraph. + librevenge::RVNGPropertyList m_aTextPropertyList; + /// If the context is a direct child of XMLBodyContentContext. + /// Only direct child of XMLBodyContentContext has to handle page span. + bool m_bTopLevel; +}; + +/// Shared child context factory for paragraph and span contexts. +rtl::Reference<XMLImportContext> +CreateParagraphOrSpanChildContext(XMLImport& rImport, const OUString& rName, + const librevenge::RVNGPropertyList& rTextPropertyList); + +/// Looks for rName in rAutomaticStyles (and failing that, in rNamedStyles) and +/// fills rPropertyList based on that. +void FillStyles(const OUString& rName, + std::map<OUString, librevenge::RVNGPropertyList>& rAutomaticStyles, + std::map<OUString, librevenge::RVNGPropertyList>& rNamedStyles, + librevenge::RVNGPropertyList& rPropertyList); + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/txtstyli.cxx b/writerperfect/source/writer/exp/txtstyli.cxx new file mode 100644 index 000000000..53ac4b4e5 --- /dev/null +++ b/writerperfect/source/writer/exp/txtstyli.cxx @@ -0,0 +1,415 @@ +/* -*- 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 "txtstyli.hxx" + +#include "xmlfmt.hxx" + +using namespace com::sun::star; + +namespace writerperfect::exp +{ +namespace +{ +/// Handler for <style:paragraph-properties>. +class XMLParagraphPropertiesContext : public XMLImportContext +{ +public: + XMLParagraphPropertiesContext(XMLImport& rImport, XMLStyleContext& rStyle); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + +private: + XMLStyleContext& mrStyle; +}; +} + +XMLParagraphPropertiesContext::XMLParagraphPropertiesContext(XMLImport& rImport, + XMLStyleContext& rStyle) + : XMLImportContext(rImport) + , mrStyle(rStyle) +{ +} + +void XMLParagraphPropertiesContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + OString sName = OUStringToOString(xAttribs->getNameByIndex(i), RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(xAttribs->getValueByIndex(i), RTL_TEXTENCODING_UTF8); + mrStyle.GetParagraphPropertyList().insert(sName.getStr(), sValue.getStr()); + } +} + +namespace +{ +/// Handler for <style:text-properties>. +class XMLTextPropertiesContext : public XMLImportContext +{ +public: + XMLTextPropertiesContext(XMLImport& rImport, XMLStyleContext& rStyle); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + +private: + XMLStyleContext& mrStyle; +}; +} + +XMLTextPropertiesContext::XMLTextPropertiesContext(XMLImport& rImport, XMLStyleContext& rStyle) + : XMLImportContext(rImport) + , mrStyle(rStyle) +{ +} + +void XMLTextPropertiesContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + OString sName = OUStringToOString(xAttribs->getNameByIndex(i), RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(xAttribs->getValueByIndex(i), RTL_TEXTENCODING_UTF8); + mrStyle.GetTextPropertyList().insert(sName.getStr(), sValue.getStr()); + } +} + +namespace +{ +/// Handler for <style:graphic-properties>. +class XMLGraphicPropertiesContext : public XMLImportContext +{ +public: + XMLGraphicPropertiesContext(XMLImport& rImport, XMLStyleContext& rStyle); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + +private: + XMLStyleContext& mrStyle; +}; +} + +XMLGraphicPropertiesContext::XMLGraphicPropertiesContext(XMLImport& rImport, + XMLStyleContext& rStyle) + : XMLImportContext(rImport) + , mrStyle(rStyle) +{ +} + +void XMLGraphicPropertiesContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + OString sName = OUStringToOString(xAttribs->getNameByIndex(i), RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(xAttribs->getValueByIndex(i), RTL_TEXTENCODING_UTF8); + mrStyle.GetGraphicPropertyList().insert(sName.getStr(), sValue.getStr()); + } +} + +namespace +{ +/// Handler for <style:page-layout-properties>. +class XMLPageLayoutPropertiesContext : public XMLImportContext +{ +public: + XMLPageLayoutPropertiesContext(XMLImport& rImport, XMLStyleContext& rStyle); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + +private: + XMLStyleContext& mrStyle; +}; +} + +XMLPageLayoutPropertiesContext::XMLPageLayoutPropertiesContext(XMLImport& rImport, + XMLStyleContext& rStyle) + : XMLImportContext(rImport) + , mrStyle(rStyle) +{ +} + +void XMLPageLayoutPropertiesContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + OString sName = OUStringToOString(xAttribs->getNameByIndex(i), RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(xAttribs->getValueByIndex(i), RTL_TEXTENCODING_UTF8); + // We only care about writing-mode for now. + if (sName != "style:writing-mode") + continue; + + mrStyle.GetPageLayoutPropertyList().insert(sName.getStr(), sValue.getStr()); + } +} + +namespace +{ +/// Handler for <style:table-properties>. +class XMLTablePropertiesContext : public XMLImportContext +{ +public: + XMLTablePropertiesContext(XMLImport& rImport, XMLStyleContext& rStyle); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + +private: + XMLStyleContext& mrStyle; +}; +} + +XMLTablePropertiesContext::XMLTablePropertiesContext(XMLImport& rImport, XMLStyleContext& rStyle) + : XMLImportContext(rImport) + , mrStyle(rStyle) +{ +} + +void XMLTablePropertiesContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + OString sName = OUStringToOString(xAttribs->getNameByIndex(i), RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(xAttribs->getValueByIndex(i), RTL_TEXTENCODING_UTF8); + if (sName == "style:rel-width") + // Make sure this is passed through as a string, and not parsed as a double. + mrStyle.GetTablePropertyList().insert( + sName.getStr(), librevenge::RVNGPropertyFactory::newStringProp(sValue.getStr())); + else + mrStyle.GetTablePropertyList().insert(sName.getStr(), sValue.getStr()); + } +} + +namespace +{ +/// Handler for <style:table-row-properties>. +class XMLTableRowPropertiesContext : public XMLImportContext +{ +public: + XMLTableRowPropertiesContext(XMLImport& rImport, XMLStyleContext& rStyle); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + +private: + XMLStyleContext& mrStyle; +}; +} + +XMLTableRowPropertiesContext::XMLTableRowPropertiesContext(XMLImport& rImport, + XMLStyleContext& rStyle) + : XMLImportContext(rImport) + , mrStyle(rStyle) +{ +} + +void XMLTableRowPropertiesContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + OString sName = OUStringToOString(xAttribs->getNameByIndex(i), RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(xAttribs->getValueByIndex(i), RTL_TEXTENCODING_UTF8); + mrStyle.GetRowPropertyList().insert(sName.getStr(), sValue.getStr()); + } +} + +namespace +{ +/// Handler for <style:table-column-properties>. +class XMLTableColumnPropertiesContext : public XMLImportContext +{ +public: + XMLTableColumnPropertiesContext(XMLImport& rImport, XMLStyleContext& rStyle); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + +private: + XMLStyleContext& mrStyle; +}; +} + +XMLTableColumnPropertiesContext::XMLTableColumnPropertiesContext(XMLImport& rImport, + XMLStyleContext& rStyle) + : XMLImportContext(rImport) + , mrStyle(rStyle) +{ +} + +void XMLTableColumnPropertiesContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + OString sName = OUStringToOString(xAttribs->getNameByIndex(i), RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(xAttribs->getValueByIndex(i), RTL_TEXTENCODING_UTF8); + mrStyle.GetColumnPropertyList().insert(sName.getStr(), sValue.getStr()); + } +} + +namespace +{ +/// Handler for <style:table-cell-properties>. +class XMLTableCellPropertiesContext : public XMLImportContext +{ +public: + XMLTableCellPropertiesContext(XMLImport& rImport, XMLStyleContext& rStyle); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + +private: + XMLStyleContext& mrStyle; +}; +} + +XMLTableCellPropertiesContext::XMLTableCellPropertiesContext(XMLImport& rImport, + XMLStyleContext& rStyle) + : XMLImportContext(rImport) + , mrStyle(rStyle) +{ +} + +void XMLTableCellPropertiesContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + OString sName = OUStringToOString(xAttribs->getNameByIndex(i), RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(xAttribs->getValueByIndex(i), RTL_TEXTENCODING_UTF8); + mrStyle.GetCellPropertyList().insert(sName.getStr(), sValue.getStr()); + } +} + +XMLStyleContext::XMLStyleContext(XMLImport& rImport, XMLStylesContext& rStyles) + : XMLImportContext(rImport) + , m_rStyles(rStyles) +{ +} + +rtl::Reference<XMLImportContext> XMLStyleContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "style:paragraph-properties") + return new XMLParagraphPropertiesContext(GetImport(), *this); + if (rName == "style:text-properties") + return new XMLTextPropertiesContext(GetImport(), *this); + if (rName == "style:table-cell-properties") + return new XMLTableCellPropertiesContext(GetImport(), *this); + if (rName == "style:table-column-properties") + return new XMLTableColumnPropertiesContext(GetImport(), *this); + if (rName == "style:table-row-properties") + return new XMLTableRowPropertiesContext(GetImport(), *this); + if (rName == "style:table-properties") + return new XMLTablePropertiesContext(GetImport(), *this); + if (rName == "style:graphic-properties") + return new XMLGraphicPropertiesContext(GetImport(), *this); + if (rName == "style:page-layout-properties") + return new XMLPageLayoutPropertiesContext(GetImport(), *this); + return nullptr; +} + +void XMLStyleContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + const OUString& rAttributeValue = xAttribs->getValueByIndex(i); + if (rAttributeName == "style:name") + m_aName = rAttributeValue; + else if (rAttributeName == "style:family") + m_aFamily = rAttributeValue; + + // Remember properties of the style itself, e.g. parent name. + OString sName = OUStringToOString(rAttributeName, RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(rAttributeValue, RTL_TEXTENCODING_UTF8); + m_aTextPropertyList.insert(sName.getStr(), sValue.getStr()); + m_aParagraphPropertyList.insert(sName.getStr(), sValue.getStr()); + m_aGraphicPropertyList.insert(sName.getStr(), sValue.getStr()); + m_aPageLayoutPropertyList.insert(sName.getStr(), sValue.getStr()); + m_aMasterPagePropertyList.insert(sName.getStr(), sValue.getStr()); + m_aTablePropertyList.insert(sName.getStr(), sValue.getStr()); + } +} + +void XMLStyleContext::endElement(const OUString& rName) +{ + if (m_aName.isEmpty()) + return; + + if (m_aFamily == "text" || m_aFamily == "paragraph") + m_rStyles.GetCurrentTextStyles()[m_aName] = m_aTextPropertyList; + if (m_aFamily == "paragraph") + m_rStyles.GetCurrentParagraphStyles()[m_aName] = m_aParagraphPropertyList; + else if (m_aFamily == "table-cell") + m_rStyles.GetCurrentCellStyles()[m_aName] = m_aCellPropertyList; + else if (m_aFamily == "table-column") + m_rStyles.GetCurrentColumnStyles()[m_aName] = m_aColumnPropertyList; + else if (m_aFamily == "table-row") + m_rStyles.GetCurrentRowStyles()[m_aName] = m_aRowPropertyList; + else if (m_aFamily == "table") + m_rStyles.GetCurrentTableStyles()[m_aName] = m_aTablePropertyList; + else if (m_aFamily == "graphic") + m_rStyles.GetCurrentGraphicStyles()[m_aName] = m_aGraphicPropertyList; + else if (rName == "style:page-layout") + m_rStyles.GetCurrentPageLayouts()[m_aName] = m_aPageLayoutPropertyList; + else if (rName == "style:master-page") + m_rStyles.GetCurrentMasterStyles()[m_aName] = m_aMasterPagePropertyList; +} + +librevenge::RVNGPropertyList& XMLStyleContext::GetTextPropertyList() { return m_aTextPropertyList; } + +librevenge::RVNGPropertyList& XMLStyleContext::GetParagraphPropertyList() +{ + return m_aParagraphPropertyList; +} + +librevenge::RVNGPropertyList& XMLStyleContext::GetCellPropertyList() { return m_aCellPropertyList; } + +librevenge::RVNGPropertyList& XMLStyleContext::GetColumnPropertyList() +{ + return m_aColumnPropertyList; +} + +librevenge::RVNGPropertyList& XMLStyleContext::GetRowPropertyList() { return m_aRowPropertyList; } + +librevenge::RVNGPropertyList& XMLStyleContext::GetTablePropertyList() +{ + return m_aTablePropertyList; +} + +librevenge::RVNGPropertyList& XMLStyleContext::GetGraphicPropertyList() +{ + return m_aGraphicPropertyList; +} + +librevenge::RVNGPropertyList& XMLStyleContext::GetPageLayoutPropertyList() +{ + return m_aPageLayoutPropertyList; +} + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/txtstyli.hxx b/writerperfect/source/writer/exp/txtstyli.hxx new file mode 100644 index 000000000..9247619bc --- /dev/null +++ b/writerperfect/source/writer/exp/txtstyli.hxx @@ -0,0 +1,60 @@ +/* -*- 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 <librevenge/librevenge.h> + +#include "xmlictxt.hxx" + +namespace writerperfect::exp +{ +class XMLStylesContext; + +/// Handler for <style:style>. +class XMLStyleContext : public XMLImportContext +{ +public: + XMLStyleContext(XMLImport& rImport, XMLStylesContext& rStyles); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; + + librevenge::RVNGPropertyList& GetTextPropertyList(); + librevenge::RVNGPropertyList& GetParagraphPropertyList(); + librevenge::RVNGPropertyList& GetCellPropertyList(); + librevenge::RVNGPropertyList& GetColumnPropertyList(); + librevenge::RVNGPropertyList& GetRowPropertyList(); + librevenge::RVNGPropertyList& GetTablePropertyList(); + librevenge::RVNGPropertyList& GetGraphicPropertyList(); + librevenge::RVNGPropertyList& GetPageLayoutPropertyList(); + +private: + OUString m_aName; + OUString m_aFamily; + librevenge::RVNGPropertyList m_aTextPropertyList; + librevenge::RVNGPropertyList m_aParagraphPropertyList; + librevenge::RVNGPropertyList m_aCellPropertyList; + librevenge::RVNGPropertyList m_aColumnPropertyList; + librevenge::RVNGPropertyList m_aRowPropertyList; + librevenge::RVNGPropertyList m_aTablePropertyList; + librevenge::RVNGPropertyList m_aGraphicPropertyList; + librevenge::RVNGPropertyList m_aPageLayoutPropertyList; + librevenge::RVNGPropertyList m_aMasterPagePropertyList; + XMLStylesContext& m_rStyles; +}; + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/xmlfmt.cxx b/writerperfect/source/writer/exp/xmlfmt.cxx new file mode 100644 index 000000000..4ac70b85a --- /dev/null +++ b/writerperfect/source/writer/exp/xmlfmt.cxx @@ -0,0 +1,295 @@ +/* -*- 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 "xmlfmt.hxx" + +#include "XMLBase64ImportContext.hxx" +#include "txtstyli.hxx" +#include "xmlimp.hxx" + +#include <sal/log.hxx> + +using namespace com::sun::star; + +namespace writerperfect::exp +{ +XMLStylesContext::XMLStylesContext(XMLImport& rImport, StyleType eType) + : XMLImportContext(rImport) + , m_rParagraphStyles(eType == StyleType_AUTOMATIC ? GetImport().GetAutomaticParagraphStyles() + : GetImport().GetParagraphStyles()) + , m_rTextStyles(eType == StyleType_AUTOMATIC ? GetImport().GetAutomaticTextStyles() + : GetImport().GetTextStyles()) + , m_rCellStyles(eType == StyleType_AUTOMATIC ? GetImport().GetAutomaticCellStyles() + : GetImport().GetCellStyles()) + , m_rColumnStyles(eType == StyleType_AUTOMATIC ? GetImport().GetAutomaticColumnStyles() + : GetImport().GetColumnStyles()) + , m_rRowStyles(eType == StyleType_AUTOMATIC ? GetImport().GetAutomaticRowStyles() + : GetImport().GetRowStyles()) + , m_rTableStyles(eType == StyleType_AUTOMATIC ? GetImport().GetAutomaticTableStyles() + : GetImport().GetTableStyles()) + , m_rGraphicStyles(eType == StyleType_AUTOMATIC ? GetImport().GetAutomaticGraphicStyles() + : GetImport().GetGraphicStyles()) + , m_rPageLayouts(GetImport().GetPageLayouts()) + , m_rMasterStyles(GetImport().GetMasterStyles()) +{ +} + +rtl::Reference<XMLImportContext> XMLStylesContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "style:style" || rName == "style:page-layout" || rName == "style:master-page") + return new XMLStyleContext(GetImport(), *this); + return nullptr; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLStylesContext::GetCurrentParagraphStyles() +{ + return m_rParagraphStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLStylesContext::GetCurrentTextStyles() +{ + return m_rTextStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLStylesContext::GetCurrentCellStyles() +{ + return m_rCellStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLStylesContext::GetCurrentColumnStyles() +{ + return m_rColumnStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLStylesContext::GetCurrentRowStyles() +{ + return m_rRowStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLStylesContext::GetCurrentTableStyles() +{ + return m_rTableStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLStylesContext::GetCurrentGraphicStyles() +{ + return m_rGraphicStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLStylesContext::GetCurrentPageLayouts() +{ + return m_rPageLayouts; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLStylesContext::GetCurrentMasterStyles() +{ + return m_rMasterStyles; +} + +namespace +{ +/// Handler for <style:font-face>. +class XMLFontFaceContext : public XMLImportContext +{ +public: + XMLFontFaceContext(XMLImport& rImport); + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + OUString const& GetName() const { return maName; } + +private: + OUString maName; +}; + +/// Handler for <svg:font-face-src>. +class XMLFontFaceSrcContext : public XMLImportContext +{ +public: + XMLFontFaceSrcContext(XMLImport& rImport, XMLFontFaceContext& rFontFace); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + +private: + XMLFontFaceContext& mrFontFace; +}; + +/// Handler for <svg:font-face-uri>. +class XMLFontFaceUriContext : public XMLImportContext +{ +public: + XMLFontFaceUriContext(XMLImport& rImport, XMLFontFaceContext const& rFontFace); + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + librevenge::RVNGPropertyList& GetPropertyList() { return maPropertyList; } + +private: + librevenge::RVNGPropertyList maPropertyList; + rtl::Reference<XMLBase64ImportContext> mxBinaryData; +}; + +/// Handler for <svg:font-face-format>. +class XMLFontFaceFormatContext : public XMLImportContext +{ +public: + XMLFontFaceFormatContext(XMLImport& rImport, XMLFontFaceUriContext& rFontFaceUri); + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + +private: + XMLFontFaceUriContext& mrFontFaceUri; +}; +} + +XMLFontFaceFormatContext::XMLFontFaceFormatContext(XMLImport& rImport, + XMLFontFaceUriContext& rFontFaceUri) + : XMLImportContext(rImport) + , mrFontFaceUri(rFontFaceUri) +{ +} + +void XMLFontFaceFormatContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + const OUString& rAttributeValue = xAttribs->getValueByIndex(i); + if (rAttributeName == "svg:string") + { + OString aAttributeValueU8 = OUStringToOString(rAttributeValue, RTL_TEXTENCODING_UTF8); + mrFontFaceUri.GetPropertyList().insert("librevenge:mime-type", + aAttributeValueU8.getStr()); + } + } +} + +XMLFontFaceUriContext::XMLFontFaceUriContext(XMLImport& rImport, + XMLFontFaceContext const& rFontFace) + : XMLImportContext(rImport) +{ + OString aNameU8 = OUStringToOString(rFontFace.GetName(), RTL_TEXTENCODING_UTF8); + maPropertyList.insert("librevenge:name", aNameU8.getStr()); +} + +void XMLFontFaceUriContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + const OUString& rAttributeValue = xAttribs->getValueByIndex(i); + if (rAttributeName == "loext:font-style") + { + OString aAttributeValueU8 = OUStringToOString(rAttributeValue, RTL_TEXTENCODING_UTF8); + maPropertyList.insert("librevenge:font-style", aAttributeValueU8.getStr()); + } + else if (rAttributeName == "loext:font-weight") + { + OString aAttributeValueU8 = OUStringToOString(rAttributeValue, RTL_TEXTENCODING_UTF8); + maPropertyList.insert("librevenge:font-weight", aAttributeValueU8.getStr()); + } + } +} + +void XMLFontFaceUriContext::endElement(const OUString& /*rName*/) +{ + if (mxBinaryData.is()) + maPropertyList.insert("office:binary-data", mxBinaryData->getBinaryData()); + GetImport().GetGenerator().defineEmbeddedFont(maPropertyList); +} + +rtl::Reference<XMLImportContext> XMLFontFaceUriContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "office:binary-data") + { + mxBinaryData = new XMLBase64ImportContext(GetImport()); + return mxBinaryData; + } + if (rName == "svg:font-face-format") + return new XMLFontFaceFormatContext(GetImport(), *this); + + SAL_WARN("writerperfect", "XMLFontFaceUriContext::CreateChildContext: unhandled " << rName); + return nullptr; +} + +XMLFontFaceSrcContext::XMLFontFaceSrcContext(XMLImport& rImport, XMLFontFaceContext& rFontFace) + : XMLImportContext(rImport) + , mrFontFace(rFontFace) +{ +} + +rtl::Reference<XMLImportContext> XMLFontFaceSrcContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "svg:font-face-uri") + return new XMLFontFaceUriContext(GetImport(), mrFontFace); + SAL_WARN("writerperfect", "XMLFontFaceSrcContext::CreateChildContext: unhandled " << rName); + return nullptr; +} + +XMLFontFaceContext::XMLFontFaceContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +void XMLFontFaceContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + const OUString& rAttributeValue = xAttribs->getValueByIndex(i); + if (rAttributeName == "style:name") + maName = rAttributeValue; + } +} + +rtl::Reference<XMLImportContext> XMLFontFaceContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "svg:font-face-src") + return new XMLFontFaceSrcContext(GetImport(), *this); + SAL_WARN("writerperfect", "XMLFontFaceContext::CreateChildContext: unhandled " << rName); + return nullptr; +} + +XMLFontFaceDeclsContext::XMLFontFaceDeclsContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +rtl::Reference<XMLImportContext> XMLFontFaceDeclsContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "style:font-face") + return new XMLFontFaceContext(GetImport()); + return nullptr; +} + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/xmlfmt.hxx b/writerperfect/source/writer/exp/xmlfmt.hxx new file mode 100644 index 000000000..22849be7e --- /dev/null +++ b/writerperfect/source/writer/exp/xmlfmt.hxx @@ -0,0 +1,73 @@ +/* -*- 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 <map> + +#include "xmlictxt.hxx" + +namespace librevenge +{ +class RVNGPropertyList; +} + +namespace writerperfect::exp +{ +/// Handler for <office:automatic-styles>/<office:styles>. +class XMLStylesContext : public XMLImportContext +{ +public: + enum StyleType + { + StyleType_NONE, + StyleType_AUTOMATIC + }; + XMLStylesContext(XMLImport& rImport, StyleType eType); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + std::map<OUString, librevenge::RVNGPropertyList>& GetCurrentParagraphStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetCurrentTextStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetCurrentCellStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetCurrentColumnStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetCurrentRowStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetCurrentTableStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetCurrentGraphicStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetCurrentPageLayouts(); + std::map<OUString, librevenge::RVNGPropertyList>& GetCurrentMasterStyles(); + +private: + std::map<OUString, librevenge::RVNGPropertyList>& m_rParagraphStyles; + std::map<OUString, librevenge::RVNGPropertyList>& m_rTextStyles; + std::map<OUString, librevenge::RVNGPropertyList>& m_rCellStyles; + std::map<OUString, librevenge::RVNGPropertyList>& m_rColumnStyles; + std::map<OUString, librevenge::RVNGPropertyList>& m_rRowStyles; + std::map<OUString, librevenge::RVNGPropertyList>& m_rTableStyles; + std::map<OUString, librevenge::RVNGPropertyList>& m_rGraphicStyles; + std::map<OUString, librevenge::RVNGPropertyList>& m_rPageLayouts; + std::map<OUString, librevenge::RVNGPropertyList>& m_rMasterStyles; +}; + +/// Handler for <office:font-face-decls>. +class XMLFontFaceDeclsContext : public XMLImportContext +{ +public: + XMLFontFaceDeclsContext(XMLImport& rImport); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; +}; + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/xmlictxt.cxx b/writerperfect/source/writer/exp/xmlictxt.cxx new file mode 100644 index 000000000..ebfe58402 --- /dev/null +++ b/writerperfect/source/writer/exp/xmlictxt.cxx @@ -0,0 +1,56 @@ +/* -*- 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 "xmlictxt.hxx" + +#include "xmlimp.hxx" + +using namespace com::sun::star; + +namespace writerperfect::exp +{ +XMLImportContext::XMLImportContext(XMLImport& rImport) + : mrImport(rImport) +{ +} + +rtl::Reference<XMLImportContext> XMLImportContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + return mrImport.CreateContext(rName, xAttribs); +} + +void XMLImportContext::startDocument() {} + +void XMLImportContext::endDocument() {} + +void XMLImportContext::startElement( + const OUString& /*rName*/, + const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ +} + +void XMLImportContext::endElement(const OUString& /*rName*/) {} + +void XMLImportContext::characters(const OUString& /*rChars*/) {} + +void XMLImportContext::ignorableWhitespace(const OUString& /*rWhitespaces*/) {} + +void XMLImportContext::processingInstruction(const OUString& /*rTarget*/, const OUString& /*rData*/) +{ +} + +void XMLImportContext::setDocumentLocator( + const css::uno::Reference<css::xml::sax::XLocator>& /*xLocator*/) +{ +} + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/xmlictxt.hxx b/writerperfect/source/writer/exp/xmlictxt.hxx new file mode 100644 index 000000000..dc05449d3 --- /dev/null +++ b/writerperfect/source/writer/exp/xmlictxt.hxx @@ -0,0 +1,51 @@ +/* -*- 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 <com/sun/star/xml/sax/XDocumentHandler.hpp> + +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> + +namespace writerperfect::exp +{ +class XMLImport; + +/// Base class for a handler of a single XML element during ODF -> librevenge conversion. +class XMLImportContext : public cppu::WeakImplHelper<css::xml::sax::XDocumentHandler> +{ +public: + XMLImportContext(XMLImport& rImport); + XMLImport& GetImport() { return mrImport; } + + virtual rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs); + + // XDocumentHandler + void SAL_CALL startDocument() override; + void SAL_CALL endDocument() override; + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; + void SAL_CALL characters(const OUString& rChars) override; + void SAL_CALL ignorableWhitespace(const OUString& rWhitespaces) override; + void SAL_CALL processingInstruction(const OUString& rTarget, const OUString& rData) override; + void SAL_CALL + setDocumentLocator(const css::uno::Reference<css::xml::sax::XLocator>& xLocator) override; + +private: + XMLImport& mrImport; +}; + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/xmlimp.cxx b/writerperfect/source/writer/exp/xmlimp.cxx new file mode 100644 index 000000000..ae7b33518 --- /dev/null +++ b/writerperfect/source/writer/exp/xmlimp.cxx @@ -0,0 +1,621 @@ +/* -*- 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 <sal/config.h> + +#include "xmlimp.hxx" + +#include <string_view> + +#include <initializer_list> +#include <unordered_map> + +#include <com/sun/star/svg/XSVGWriter.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/xml/sax/InputSource.hpp> +#include <com/sun/star/xml/sax/Parser.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> +#include <comphelper/propertyvalue.hxx> +#include <rtl/uri.hxx> +#include <tools/gen.hxx> +#include <tools/stream.hxx> +#include <tools/urlobj.hxx> +#include <unotools/streamwrap.hxx> +#include <tools/diagnose_ex.h> + +#include "xmlfmt.hxx" +#include "xmlictxt.hxx" +#include "xmlmetai.hxx" +#include "xmltext.hxx" + +using namespace com::sun::star; + +namespace writerperfect::exp +{ +namespace +{ +/// Looks up mime type for a given image extension. +OUString GetMimeType(const OUString& rExtension) +{ + static const std::unordered_map<OUString, OUString> vMimeTypes = { + { "gif", "image/gif" }, + { "jpg", "image/jpeg" }, + { "png", "image/png" }, + { "svg", "image/svg+xml" }, + }; + + auto it = vMimeTypes.find(rExtension); + return it == vMimeTypes.end() ? OUString() : it->second; +} + +/// Determines the base directory for cover images, XMP metadata, popup images. +OUString FindMediaDir(const OUString& rDocumentBaseURL, + const uno::Sequence<beans::PropertyValue>& rFilterData) +{ + OUString aMediaDir; + + // See if filter data contains a media directory explicitly. + auto pProp = std::find_if( + rFilterData.begin(), rFilterData.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "RVNGMediaDir"; }); + if (pProp != rFilterData.end()) + pProp->Value >>= aMediaDir; + + if (!aMediaDir.isEmpty()) + return aMediaDir + "/"; + + // Not set explicitly, try to pick it up from the base directory. + INetURLObject aURL(rDocumentBaseURL); + try + { + aMediaDir = rtl::Uri::convertRelToAbs(rDocumentBaseURL, aURL.GetBase()) + "/"; + } + catch (const rtl::MalformedUriException&) + { + DBG_UNHANDLED_EXCEPTION("writerperfect"); + } + return aMediaDir; +} + +/// Picks up a cover image from the base directory. +OUString FindCoverImage(const OUString& rDocumentBaseURL, OUString& rMimeType, + const uno::Sequence<beans::PropertyValue>& rFilterData) +{ + OUString aRet; + + // See if filter data contains a cover image explicitly. + auto pProp = std::find_if( + rFilterData.begin(), rFilterData.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "RVNGCoverImage"; }); + if (pProp != rFilterData.end()) + pProp->Value >>= aRet; + + if (!aRet.isEmpty()) + { + INetURLObject aRetURL(aRet); + rMimeType = GetMimeType(aRetURL.GetFileExtension()); + return aRet; + } + + // Not set explicitly, try to pick it up from the base directory. + if (rDocumentBaseURL.isEmpty()) + return aRet; + + static const std::initializer_list<std::u16string_view> vExtensions + = { u"gif", u"jpg", u"png", u"svg" }; + + OUString aMediaDir = FindMediaDir(rDocumentBaseURL, rFilterData); + for (const auto& rExtension : vExtensions) + { + aRet = aMediaDir + "cover." + rExtension; + if (!aRet.isEmpty()) + { + SvFileStream aStream(aRet, StreamMode::READ); + if (aStream.IsOpen()) + { + rMimeType = GetMimeType(OUString(rExtension)); + // File exists. + return aRet; + } + + aRet.clear(); + } + } + + return aRet; +} + +/// Picks up XMP metadata from the base directory. +void FindXMPMetadata(const uno::Reference<uno::XComponentContext>& xContext, + const OUString& rDocumentBaseURL, + const uno::Sequence<beans::PropertyValue>& rFilterData, + librevenge::RVNGPropertyList& rMetaData) +{ + // See if filter data contains metadata explicitly. + OUString aValue; + for (const auto& rProp : rFilterData) + { + if (rProp.Name == "RVNGIdentifier") + { + rProp.Value >>= aValue; + if (!aValue.isEmpty()) + rMetaData.insert("dc:identifier", aValue.toUtf8().getStr()); + } + else if (rProp.Name == "RVNGTitle") + { + rProp.Value >>= aValue; + if (!aValue.isEmpty()) + rMetaData.insert("dc:title", aValue.toUtf8().getStr()); + } + else if (rProp.Name == "RVNGInitialCreator") + { + rProp.Value >>= aValue; + if (!aValue.isEmpty()) + rMetaData.insert("meta:initial-creator", aValue.toUtf8().getStr()); + } + else if (rProp.Name == "RVNGLanguage") + { + rProp.Value >>= aValue; + if (!aValue.isEmpty()) + rMetaData.insert("dc:language", aValue.toUtf8().getStr()); + } + else if (rProp.Name == "RVNGDate") + { + rProp.Value >>= aValue; + if (!aValue.isEmpty()) + rMetaData.insert("dc:date", aValue.toUtf8().getStr()); + } + } + + // If not set explicitly, try to pick it up from the base directory. + if (rDocumentBaseURL.isEmpty()) + return; + + OUString aMediaDir = FindMediaDir(rDocumentBaseURL, rFilterData); + OUString aURL = aMediaDir + "metadata.xmp"; + SvFileStream aStream(aURL, StreamMode::READ); + if (!aStream.IsOpen()) + return; + + xml::sax::InputSource aInputSource; + uno::Reference<io::XInputStream> xStream(new utl::OStreamWrapper(aStream)); + aInputSource.aInputStream = xStream; + uno::Reference<xml::sax::XParser> xParser = xml::sax::Parser::create(xContext); + rtl::Reference<XMPParser> xXMP(new XMPParser(rMetaData)); + xParser->setDocumentHandler(xXMP); + try + { + xParser->parseStream(aInputSource); + } + catch (const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("writerperfect", "parseStream() failed"); + return; + } +} + +/// Handler for <office:body>. +class XMLBodyContext : public XMLImportContext +{ +public: + XMLBodyContext(XMLImport& rImport); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const uno::Reference<xml::sax::XAttributeList>& /*xAttribs*/) override; +}; +} + +XMLBodyContext::XMLBodyContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +rtl::Reference<XMLImportContext> +XMLBodyContext::CreateChildContext(const OUString& rName, + const uno::Reference<xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "office:text") + return new XMLBodyContentContext(GetImport()); + return nullptr; +} + +namespace +{ +/// Handler for <office:document>. +class XMLOfficeDocContext : public XMLImportContext +{ +public: + XMLOfficeDocContext(XMLImport& rImport); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const uno::Reference<xml::sax::XAttributeList>& /*xAttribs*/) override; + + // Handles metafile for a single page. + void HandleFixedLayoutPage(const FixedLayoutPage& rPage, bool bFirst); +}; +} + +XMLOfficeDocContext::XMLOfficeDocContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +rtl::Reference<XMLImportContext> XMLOfficeDocContext::CreateChildContext( + const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "office:meta") + return new XMLMetaDocumentContext(GetImport()); + if (rName == "office:automatic-styles") + return new XMLStylesContext(GetImport(), XMLStylesContext::StyleType_AUTOMATIC); + if (rName == "office:styles") + return new XMLStylesContext(GetImport(), XMLStylesContext::StyleType_NONE); + if (rName == "office:master-styles") + return new XMLStylesContext(GetImport(), XMLStylesContext::StyleType_NONE); + if (rName == "office:font-face-decls") + return new XMLFontFaceDeclsContext(GetImport()); + if (rName == "office:body") + { + if (GetImport().GetPageMetafiles().empty()) + return new XMLBodyContext(GetImport()); + + // Ignore text from doc model in the fixed layout case, instead + // insert the page metafiles. + bool bFirst = true; + for (const auto& rPage : GetImport().GetPageMetafiles()) + { + HandleFixedLayoutPage(rPage, bFirst); + if (bFirst) + bFirst = false; + } + } + return nullptr; +} + +void XMLOfficeDocContext::HandleFixedLayoutPage(const FixedLayoutPage& rPage, bool bFirst) +{ + uno::Reference<uno::XComponentContext> xCtx = GetImport().GetComponentContext(); + uno::Reference<xml::sax::XWriter> xSaxWriter = xml::sax::Writer::create(xCtx); + if (!xSaxWriter.is()) + return; + + // [-loplugin:redundantfcast] false positive: + uno::Sequence<uno::Any> aArguments = { uno::Any(uno::Sequence<beans::PropertyValue>( + { comphelper::makePropertyValue("DTDString", false) })) }; + uno::Reference<svg::XSVGWriter> xSVGWriter( + xCtx->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.svg.SVGWriter", aArguments, xCtx), + uno::UNO_QUERY); + if (!xSVGWriter.is()) + return; + + SvMemoryStream aMemoryStream; + xSaxWriter->setOutputStream(new utl::OStreamWrapper(aMemoryStream)); + + xSVGWriter->write(xSaxWriter, rPage.aMetafile); + + // Have all the info, invoke the generator. + librevenge::RVNGPropertyList aPageProperties; + // Pixel -> inch. + double fWidth = rPage.aCssPixels.getWidth(); + fWidth /= 96; + aPageProperties.insert("fo:page-width", fWidth); + double fHeight = rPage.aCssPixels.getHeight(); + fHeight /= 96; + aPageProperties.insert("fo:page-height", fHeight); + + if (!rPage.aChapterNames.empty()) + { + // Name of chapters starting on this page. + librevenge::RVNGPropertyListVector aChapterNames; + for (const auto& rName : rPage.aChapterNames) + { + librevenge::RVNGPropertyList aChapter; + aChapter.insert("librevenge:name", rName.toUtf8().getStr()); + aChapterNames.append(aChapter); + } + aPageProperties.insert("librevenge:chapter-names", aChapterNames); + } + + GetImport().GetGenerator().openPageSpan(aPageProperties); + librevenge::RVNGPropertyList aParagraphProperties; + if (!bFirst) + // All pages except the first one needs a page break before the page + // metafile. + aParagraphProperties.insert("fo:break-before", "page"); + GetImport().GetGenerator().openParagraph(aParagraphProperties); + librevenge::RVNGPropertyList aImageProperties; + aImageProperties.insert("librevenge:mime-type", "image/svg+xml"); + librevenge::RVNGBinaryData aBinaryData; + aBinaryData.append(static_cast<const unsigned char*>(aMemoryStream.GetData()), + aMemoryStream.GetSize()); + aImageProperties.insert("office:binary-data", aBinaryData); + GetImport().GetGenerator().insertBinaryObject(aImageProperties); + GetImport().GetGenerator().closeParagraph(); + GetImport().GetGenerator().closePageSpan(); +} + +XMLImport::XMLImport(const uno::Reference<uno::XComponentContext>& xContext, + librevenge::RVNGTextInterface& rGenerator, const OUString& rURL, + const uno::Sequence<beans::PropertyValue>& rDescriptor, + const std::vector<FixedLayoutPage>& rPageMetafiles) + : mrGenerator(rGenerator) + , mxContext(xContext) + , mbIsInPageSpan(false) + , mrPageMetafiles(rPageMetafiles) +{ + uno::Sequence<beans::PropertyValue> aFilterData; + auto pDescriptor = std::find_if( + rDescriptor.begin(), rDescriptor.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "FilterData"; }); + if (pDescriptor != rDescriptor.end()) + pDescriptor->Value >>= aFilterData; + + maMediaDir = FindMediaDir(rURL, aFilterData); + + OUString aMimeType; + OUString aCoverImage = FindCoverImage(rURL, aMimeType, aFilterData); + if (!aCoverImage.isEmpty()) + { + librevenge::RVNGBinaryData aBinaryData; + SvFileStream aStream(aCoverImage, StreamMode::READ); + SvMemoryStream aMemoryStream; + aMemoryStream.WriteStream(aStream); + aBinaryData.append(static_cast<const unsigned char*>(aMemoryStream.GetData()), + aMemoryStream.GetSize()); + librevenge::RVNGPropertyList aCoverImageProperties; + aCoverImageProperties.insert("office:binary-data", aBinaryData); + aCoverImageProperties.insert("librevenge:mime-type", aMimeType.toUtf8().getStr()); + maCoverImages.append(aCoverImageProperties); + } + + FindXMPMetadata(mxContext, rURL, aFilterData, maMetaData); + + mxUriReferenceFactory = uri::UriReferenceFactory::create(mxContext); +} + +const librevenge::RVNGPropertyListVector& XMLImport::GetCoverImages() const +{ + return maCoverImages; +} + +const librevenge::RVNGPropertyList& XMLImport::GetMetaData() const { return maMetaData; } + +namespace +{ +/// Finds out if a file URL exists. +bool FileURLExists(const OUString& rURL) +{ + SvFileStream aStream(rURL, StreamMode::READ); + return aStream.IsOpen(); +} +} + +PopupState XMLImport::FillPopupData(const OUString& rURL, librevenge::RVNGPropertyList& rPropList) +{ + uno::Reference<uri::XUriReference> xUriRef; + try + { + xUriRef = mxUriReferenceFactory->parse(rURL); + } + catch (const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("writerperfect", "XUriReference::parse() failed"); + } + bool bAbsolute = true; + if (xUriRef.is()) + bAbsolute = xUriRef->isAbsolute(); + if (bAbsolute) + return PopupState::NotConsumed; + + // Default case: relative URL, popup data was in the same directory as the + // document at insertion time. + OUString aAbs = maMediaDir + rURL; + if (!FileURLExists(aAbs)) + // Fallback case: relative URL, popup data was in the default media + // directory at insertion time. + aAbs = maMediaDir + "../" + rURL; + + if (!FileURLExists(aAbs)) + // Relative link, but points to non-existing file: don't emit that to + // librevenge, since it will be invalid anyway. + return PopupState::Ignore; + + SvFileStream aStream(aAbs, StreamMode::READ); + librevenge::RVNGBinaryData aBinaryData; + SvMemoryStream aMemoryStream; + aMemoryStream.WriteStream(aStream); + aBinaryData.append(static_cast<const unsigned char*>(aMemoryStream.GetData()), + aMemoryStream.GetSize()); + rPropList.insert("office:binary-data", aBinaryData); + + INetURLObject aAbsURL(aAbs); + OUString aMimeType = GetMimeType(aAbsURL.GetFileExtension()); + rPropList.insert("librevenge:mime-type", aMimeType.toUtf8().getStr()); + + return PopupState::Consumed; +} + +const std::vector<FixedLayoutPage>& XMLImport::GetPageMetafiles() const { return mrPageMetafiles; } + +const uno::Reference<uno::XComponentContext>& XMLImport::GetComponentContext() const +{ + return mxContext; +} + +rtl::Reference<XMLImportContext> +XMLImport::CreateContext(std::u16string_view rName, + const uno::Reference<xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == u"office:document") + return new XMLOfficeDocContext(*this); + return nullptr; +} + +librevenge::RVNGTextInterface& XMLImport::GetGenerator() const { return mrGenerator; } + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetAutomaticTextStyles() +{ + return maAutomaticTextStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetAutomaticParagraphStyles() +{ + return maAutomaticParagraphStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetAutomaticCellStyles() +{ + return maAutomaticCellStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetAutomaticColumnStyles() +{ + return maAutomaticColumnStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetAutomaticRowStyles() +{ + return maAutomaticRowStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetAutomaticTableStyles() +{ + return maAutomaticTableStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetAutomaticGraphicStyles() +{ + return maAutomaticGraphicStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetTextStyles() +{ + return maTextStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetParagraphStyles() +{ + return maParagraphStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetCellStyles() +{ + return maCellStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetColumnStyles() +{ + return maColumnStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetRowStyles() { return maRowStyles; } + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetTableStyles() +{ + return maTableStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetGraphicStyles() +{ + return maGraphicStyles; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetPageLayouts() +{ + return maPageLayouts; +} + +std::map<OUString, librevenge::RVNGPropertyList>& XMLImport::GetMasterStyles() +{ + return maMasterStyles; +} + +void XMLImport::startDocument() { mrGenerator.startDocument(librevenge::RVNGPropertyList()); } + +void XMLImport::endDocument() { mrGenerator.endDocument(); } + +void XMLImport::startElement(const OUString& rName, + const uno::Reference<xml::sax::XAttributeList>& xAttribs) +{ + rtl::Reference<XMLImportContext> xContext; + if (!maContexts.empty()) + { + if (maContexts.top().is()) + xContext = maContexts.top()->CreateChildContext(rName, xAttribs); + } + else + xContext = CreateContext(rName, xAttribs); + + if (xContext.is()) + xContext->startElement(rName, xAttribs); + + maContexts.push(xContext); +} + +void XMLImport::endElement(const OUString& rName) +{ + if (maContexts.empty()) + return; + + if (maContexts.top().is()) + maContexts.top()->endElement(rName); + + maContexts.pop(); +} + +void XMLImport::characters(const OUString& rChars) +{ + if (maContexts.top().is()) + maContexts.top()->characters(rChars); +} + +void XMLImport::ignorableWhitespace(const OUString& /*rWhitespaces*/) {} + +void XMLImport::processingInstruction(const OUString& /*rTarget*/, const OUString& /*rData*/) {} + +void XMLImport::setDocumentLocator(const uno::Reference<xml::sax::XLocator>& /*xLocator*/) {} + +void XMLImport::HandlePageSpan(const librevenge::RVNGPropertyList& rPropertyList) +{ + OUString sMasterPageName; + OUString sLayoutName; + + if (rPropertyList["style:master-page-name"]) + sMasterPageName = OStringToOUString( + rPropertyList["style:master-page-name"]->getStr().cstr(), RTL_TEXTENCODING_UTF8); + else if (!GetIsInPageSpan()) + sMasterPageName = "Standard"; + + if (sMasterPageName.getLength()) + { + librevenge::RVNGPropertyList& rMasterPage = GetMasterStyles()[sMasterPageName]; + if (rMasterPage["style:page-layout-name"]) + { + sLayoutName = OStringToOUString(rMasterPage["style:page-layout-name"]->getStr().cstr(), + RTL_TEXTENCODING_UTF8); + } + } + + if (sLayoutName.getLength()) + { + librevenge::RVNGPropertyList& rPageLayout = GetPageLayouts()[sLayoutName]; + + if (GetIsInPageSpan()) + GetGenerator().closePageSpan(); + + GetGenerator().openPageSpan(rPageLayout); + mbIsInPageSpan = true; + } +} + +} // namespace writerperfect + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/xmlimp.hxx b/writerperfect/source/writer/exp/xmlimp.hxx new file mode 100644 index 000000000..fd2882371 --- /dev/null +++ b/writerperfect/source/writer/exp/xmlimp.hxx @@ -0,0 +1,149 @@ +/* -*- 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 <map> +#include <stack> +#include <vector> + +#include <librevenge/librevenge.h> + +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> + +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> +#include <tools/gen.hxx> + +#include "xmlictxt.hxx" + +namespace com::sun::star +{ +namespace beans +{ +struct PropertyValue; +} +namespace uno +{ +class XComponentContext; +} +namespace uri +{ +class XUriReferenceFactory; +} +} + +namespace writerperfect::exp +{ +class XMLImportContext; + +/// Contains info about a fixed-layout page. +struct FixedLayoutPage +{ + css::uno::Sequence<sal_Int8> aMetafile; + Size aCssPixels; + std::vector<OUString> aChapterNames; +}; + +/// States describing the result of a link -> popup conversion. +enum class PopupState +{ + /// Conversion did not happen yet. + NONE, + /// The relative link was converted to a popup. + Consumed, + /// The absolute link was not handled. + NotConsumed, + /// The relative link is invalid and should be ignored. + Ignore +}; + +/// ODT export feeds this class to make librevenge calls. +class XMLImport : public cppu::WeakImplHelper<css::xml::sax::XDocumentHandler> +{ + librevenge::RVNGTextInterface& mrGenerator; + std::stack<rtl::Reference<XMLImportContext>> maContexts; + std::map<OUString, librevenge::RVNGPropertyList> maAutomaticTextStyles; + std::map<OUString, librevenge::RVNGPropertyList> maTextStyles; + std::map<OUString, librevenge::RVNGPropertyList> maAutomaticParagraphStyles; + std::map<OUString, librevenge::RVNGPropertyList> maParagraphStyles; + std::map<OUString, librevenge::RVNGPropertyList> maAutomaticCellStyles; + std::map<OUString, librevenge::RVNGPropertyList> maCellStyles; + std::map<OUString, librevenge::RVNGPropertyList> maAutomaticColumnStyles; + std::map<OUString, librevenge::RVNGPropertyList> maColumnStyles; + std::map<OUString, librevenge::RVNGPropertyList> maAutomaticRowStyles; + std::map<OUString, librevenge::RVNGPropertyList> maRowStyles; + std::map<OUString, librevenge::RVNGPropertyList> maAutomaticTableStyles; + std::map<OUString, librevenge::RVNGPropertyList> maTableStyles; + std::map<OUString, librevenge::RVNGPropertyList> maAutomaticGraphicStyles; + std::map<OUString, librevenge::RVNGPropertyList> maGraphicStyles; + std::map<OUString, librevenge::RVNGPropertyList> maPageLayouts; + std::map<OUString, librevenge::RVNGPropertyList> maMasterStyles; + librevenge::RVNGPropertyListVector maCoverImages; + /// Author, date, etc -- overwrites what would be from the document out of the box. + librevenge::RVNGPropertyList maMetaData; + const css::uno::Reference<css::uno::XComponentContext>& mxContext; + css::uno::Reference<css::uri::XUriReferenceFactory> mxUriReferenceFactory; + OUString maMediaDir; + bool mbIsInPageSpan; + const std::vector<FixedLayoutPage>& mrPageMetafiles; + +public: + XMLImport(const css::uno::Reference<css::uno::XComponentContext>& xContext, + librevenge::RVNGTextInterface& rGenerator, const OUString& rURL, + const css::uno::Sequence<css::beans::PropertyValue>& rDescriptor, + const std::vector<FixedLayoutPage>& rPageMetafiles); + + rtl::Reference<XMLImportContext> + CreateContext(std::u16string_view rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs); + + librevenge::RVNGTextInterface& GetGenerator() const; + std::map<OUString, librevenge::RVNGPropertyList>& GetAutomaticTextStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetAutomaticParagraphStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetAutomaticCellStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetAutomaticColumnStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetAutomaticRowStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetAutomaticTableStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetAutomaticGraphicStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetTextStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetParagraphStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetCellStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetColumnStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetRowStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetTableStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetGraphicStyles(); + std::map<OUString, librevenge::RVNGPropertyList>& GetPageLayouts(); + std::map<OUString, librevenge::RVNGPropertyList>& GetMasterStyles(); + const librevenge::RVNGPropertyListVector& GetCoverImages() const; + const librevenge::RVNGPropertyList& GetMetaData() const; + PopupState FillPopupData(const OUString& rURL, librevenge::RVNGPropertyList& rPropList); + const std::vector<FixedLayoutPage>& GetPageMetafiles() const; + const css::uno::Reference<css::uno::XComponentContext>& GetComponentContext() const; + + bool GetIsInPageSpan() const { return mbIsInPageSpan; } + void HandlePageSpan(const librevenge::RVNGPropertyList& rPropertyList); + + // XDocumentHandler + void SAL_CALL startDocument() override; + void SAL_CALL endDocument() override; + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; + void SAL_CALL characters(const OUString& rChars) override; + void SAL_CALL ignorableWhitespace(const OUString& rWhitespaces) override; + void SAL_CALL processingInstruction(const OUString& rTarget, const OUString& rData) override; + void SAL_CALL + setDocumentLocator(const css::uno::Reference<css::xml::sax::XLocator>& xLocator) override; +}; + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/xmlmetai.cxx b/writerperfect/source/writer/exp/xmlmetai.cxx new file mode 100644 index 000000000..2e42f740f --- /dev/null +++ b/writerperfect/source/writer/exp/xmlmetai.cxx @@ -0,0 +1,285 @@ +/* -*- 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 "xmlmetai.hxx" + +#include "xmlimp.hxx" + +using namespace com::sun::star; + +namespace writerperfect::exp +{ +namespace +{ +/// Handler for <dc:title>. +class XMLDcTitleContext : public XMLImportContext +{ +public: + XMLDcTitleContext(XMLImport& rImport, XMLMetaDocumentContext& rMeta); + + void SAL_CALL characters(const OUString& rChars) override; + +private: + XMLMetaDocumentContext& mrMeta; +}; +} + +XMLDcTitleContext::XMLDcTitleContext(XMLImport& rImport, XMLMetaDocumentContext& rMeta) + : XMLImportContext(rImport) + , mrMeta(rMeta) +{ +} + +void XMLDcTitleContext::characters(const OUString& rChars) +{ + OString sCharU8 = OUStringToOString(rChars, RTL_TEXTENCODING_UTF8); + if (!mrMeta.GetPropertyList()["dc:title"]) + mrMeta.GetPropertyList().insert("dc:title", librevenge::RVNGString(sCharU8.getStr())); +} + +namespace +{ +/// Handler for <dc:language>. +class XMLDcLanguageContext : public XMLImportContext +{ +public: + XMLDcLanguageContext(XMLImport& rImport, XMLMetaDocumentContext& rMeta); + + void SAL_CALL characters(const OUString& rChars) override; + +private: + XMLMetaDocumentContext& mrMeta; +}; +} + +XMLDcLanguageContext::XMLDcLanguageContext(XMLImport& rImport, XMLMetaDocumentContext& rMeta) + : XMLImportContext(rImport) + , mrMeta(rMeta) +{ +} + +void XMLDcLanguageContext::characters(const OUString& rChars) +{ + OString sCharU8 = OUStringToOString(rChars, RTL_TEXTENCODING_UTF8); + if (!mrMeta.GetPropertyList()["dc:language"]) + mrMeta.GetPropertyList().insert("dc:language", librevenge::RVNGString(sCharU8.getStr())); +} + +namespace +{ +/// Handler for <dc:date>. +class XMLDcDateContext : public XMLImportContext +{ +public: + XMLDcDateContext(XMLImport& rImport, XMLMetaDocumentContext& rMeta); + + void SAL_CALL characters(const OUString& rChars) override; + +private: + XMLMetaDocumentContext& mrMeta; +}; +} + +XMLDcDateContext::XMLDcDateContext(XMLImport& rImport, XMLMetaDocumentContext& rMeta) + : XMLImportContext(rImport) + , mrMeta(rMeta) +{ +} + +void XMLDcDateContext::characters(const OUString& rChars) +{ + OString sCharU8 = OUStringToOString(rChars, RTL_TEXTENCODING_UTF8); + if (!mrMeta.GetPropertyList()["dc:date"]) + mrMeta.GetPropertyList().insert("dc:date", librevenge::RVNGString(sCharU8.getStr())); +} + +namespace +{ +/// Handler for <meta:generator>. +class XMLMetaGeneratorContext : public XMLImportContext +{ +public: + XMLMetaGeneratorContext(XMLImport& rImport, XMLMetaDocumentContext& rMeta); + + void SAL_CALL characters(const OUString& rChars) override; + +private: + XMLMetaDocumentContext& mrMeta; +}; +} + +XMLMetaGeneratorContext::XMLMetaGeneratorContext(XMLImport& rImport, XMLMetaDocumentContext& rMeta) + : XMLImportContext(rImport) + , mrMeta(rMeta) +{ +} + +void XMLMetaGeneratorContext::characters(const OUString& rChars) +{ + OString sCharU8 = OUStringToOString(rChars, RTL_TEXTENCODING_UTF8); + mrMeta.GetPropertyList().insert("meta:generator", librevenge::RVNGString(sCharU8.getStr())); +} + +namespace +{ +/// Handler for <meta:initial-creator>. +class XMLMetaInitialCreatorContext : public XMLImportContext +{ +public: + XMLMetaInitialCreatorContext(XMLImport& rImport, XMLMetaDocumentContext& rMeta); + + void SAL_CALL characters(const OUString& rChars) override; + +private: + XMLMetaDocumentContext& mrMeta; +}; +} + +XMLMetaInitialCreatorContext::XMLMetaInitialCreatorContext(XMLImport& rImport, + XMLMetaDocumentContext& rMeta) + : XMLImportContext(rImport) + , mrMeta(rMeta) +{ +} + +void XMLMetaInitialCreatorContext::characters(const OUString& rChars) +{ + OString sCharU8 = OUStringToOString(rChars, RTL_TEXTENCODING_UTF8); + if (!mrMeta.GetPropertyList()["meta:initial-creator"]) + mrMeta.GetPropertyList().insert("meta:initial-creator", + librevenge::RVNGString(sCharU8.getStr())); +} + +XMLMetaDocumentContext::XMLMetaDocumentContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ + librevenge::RVNGPropertyList::Iter it(GetImport().GetMetaData()); + for (it.rewind(); it.next();) + m_aPropertyList.insert(it.key(), it()->clone()); + m_aPropertyList.insert("librevenge:cover-images", GetImport().GetCoverImages()); +} + +rtl::Reference<XMLImportContext> XMLMetaDocumentContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "dc:title") + return new XMLDcTitleContext(GetImport(), *this); + if (rName == "dc:language") + return new XMLDcLanguageContext(GetImport(), *this); + if (rName == "dc:date") + return new XMLDcDateContext(GetImport(), *this); + if (rName == "meta:generator") + return new XMLMetaGeneratorContext(GetImport(), *this); + if (rName == "meta:initial-creator") + return new XMLMetaInitialCreatorContext(GetImport(), *this); + return nullptr; +} + +void XMLMetaDocumentContext::endElement(const OUString& /*rName*/) +{ + GetImport().GetGenerator().setDocumentMetaData(m_aPropertyList); +} + +XMPParser::XMPParser(librevenge::RVNGPropertyList& rMetaData) + : mrMetaData(rMetaData) +{ +} + +XMPParser::~XMPParser() = default; + +void XMPParser::startDocument() {} + +void XMPParser::endDocument() +{ + if (!mrMetaData["dc:identifier"] && !m_aIdentifier.isEmpty()) + mrMetaData.insert("dc:identifier", m_aIdentifier.toUtf8().getStr()); + if (!mrMetaData["dc:title"] && !m_aTitle.isEmpty()) + mrMetaData.insert("dc:title", m_aTitle.toUtf8().getStr()); + if (!mrMetaData["meta:initial-creator"] && !m_aCreator.isEmpty()) + mrMetaData.insert("meta:initial-creator", m_aCreator.toUtf8().getStr()); + if (!mrMetaData["dc:language"] && !m_aLanguage.isEmpty()) + mrMetaData.insert("dc:language", m_aLanguage.toUtf8().getStr()); + if (!mrMetaData["dc:date"] && !m_aDate.isEmpty()) + mrMetaData.insert("dc:date", m_aDate.toUtf8().getStr()); +} + +void XMPParser::startElement(const OUString& rName, + const uno::Reference<xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "dc:identifier") + m_bInIdentifier = true; + else if (rName == "dc:title") + m_bInTitle = true; + else if (rName == "dc:creator") + m_bInCreator = true; + else if (rName == "dc:language") + m_bInLanguage = true; + else if (rName == "dc:date") + m_bInDate = true; + else if (rName == "rdf:li") + { + if (m_bInTitle) + m_bInTitleItem = true; + else if (m_bInCreator) + m_bInCreatorItem = true; + else if (m_bInLanguage) + m_bInLanguageItem = true; + else if (m_bInDate) + m_bInDateItem = true; + } +} + +void XMPParser::endElement(const OUString& rName) +{ + if (rName == "dc:identifier") + m_bInIdentifier = false; + else if (rName == "dc:title") + m_bInTitle = false; + else if (rName == "dc:creator") + m_bInCreator = false; + else if (rName == "dc:language") + m_bInLanguage = false; + else if (rName == "dc:date") + m_bInDate = false; + else if (rName == "rdf:li") + { + if (m_bInTitle) + m_bInTitleItem = false; + else if (m_bInCreator) + m_bInCreatorItem = false; + else if (m_bInLanguage) + m_bInLanguageItem = false; + else if (m_bInDate) + m_bInDateItem = false; + } +} + +void XMPParser::characters(const OUString& rChars) +{ + if (m_bInIdentifier) + m_aIdentifier += rChars; + else if (m_bInTitleItem) + m_aTitle += rChars; + else if (m_bInCreatorItem) + m_aCreator += rChars; + else if (m_bInLanguageItem) + m_aLanguage += rChars; + else if (m_bInDateItem) + m_aDate += rChars; +} + +void XMPParser::ignorableWhitespace(const OUString& /*rWhitespace*/) {} + +void XMPParser::processingInstruction(const OUString& /*rTarget*/, const OUString& /*rData*/) {} + +void XMPParser::setDocumentLocator(const uno::Reference<xml::sax::XLocator>& /*xLocator*/) {} + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/xmlmetai.hxx b/writerperfect/source/writer/exp/xmlmetai.hxx new file mode 100644 index 000000000..f87ddff24 --- /dev/null +++ b/writerperfect/source/writer/exp/xmlmetai.hxx @@ -0,0 +1,83 @@ +/* -*- 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 <librevenge/librevenge.h> + +#include "xmlictxt.hxx" + +namespace writerperfect::exp +{ +/// Handler for <office:meta>. +class XMLMetaDocumentContext : public XMLImportContext +{ +public: + XMLMetaDocumentContext(XMLImport& rImport); + + librevenge::RVNGPropertyList& GetPropertyList() { return m_aPropertyList; } + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + void SAL_CALL endElement(const OUString& rName) override; + +private: + librevenge::RVNGPropertyList m_aPropertyList; +}; + +/// Parses an XMP file. +class XMPParser : public cppu::WeakImplHelper<css::xml::sax::XDocumentHandler> +{ +public: + explicit XMPParser(librevenge::RVNGPropertyList& rMetaData); + ~XMPParser() override; + + // XDocumentHandler + void SAL_CALL startDocument() override; + + void SAL_CALL endDocument() override; + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + void SAL_CALL endElement(const OUString& rName) override; + + void SAL_CALL characters(const OUString& rChars) override; + + void SAL_CALL ignorableWhitespace(const OUString& aWhitespaces) override; + + void SAL_CALL processingInstruction(const OUString& aTarget, const OUString& aData) override; + + void SAL_CALL + setDocumentLocator(const css::uno::Reference<css::xml::sax::XLocator>& xLocator) override; + +private: + librevenge::RVNGPropertyList& mrMetaData; + bool m_bInIdentifier = false; + OUString m_aIdentifier; + bool m_bInTitle = false; + bool m_bInTitleItem = false; + OUString m_aTitle; + bool m_bInCreator = false; + bool m_bInCreatorItem = false; + OUString m_aCreator; + bool m_bInLanguage = false; + bool m_bInLanguageItem = false; + OUString m_aLanguage; + bool m_bInDate = false; + bool m_bInDateItem = false; + OUString m_aDate; +}; + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/xmltbli.cxx b/writerperfect/source/writer/exp/xmltbli.cxx new file mode 100644 index 000000000..0e5818f0b --- /dev/null +++ b/writerperfect/source/writer/exp/xmltbli.cxx @@ -0,0 +1,254 @@ +/* -*- 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 "xmltbli.hxx" + +#include "txtparai.hxx" +#include "xmlimp.hxx" +#include "xmltext.hxx" + +#include <sal/log.hxx> + +using namespace com::sun::star; + +namespace writerperfect::exp +{ +namespace +{ +/// Handler for <table:table-row>. +class XMLTableRowContext : public XMLImportContext +{ +public: + XMLTableRowContext(XMLImport& rImport); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + int GetColumn() const; + void SetColumn(int nColumn); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; + +private: + int m_nColumn = 0; +}; + +/// Handler for <table:cell>. +class XMLTableCellContext : public XMLImportContext +{ +public: + XMLTableCellContext(XMLImport& rImport, XMLTableRowContext& rRow); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; + +private: + XMLTableRowContext& m_rRow; +}; +} + +XMLTableCellContext::XMLTableCellContext(XMLImport& rImport, XMLTableRowContext& rRow) + : XMLImportContext(rImport) + , m_rRow(rRow) +{ +} + +rtl::Reference<XMLImportContext> XMLTableCellContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + return CreateTextChildContext(GetImport(), rName); +} + +void XMLTableCellContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + librevenge::RVNGPropertyList aPropertyList; + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + const OUString& rAttributeValue = xAttribs->getValueByIndex(i); + + if (rAttributeName == "table:style-name") + FillStyles(rAttributeValue, GetImport().GetAutomaticCellStyles(), + GetImport().GetCellStyles(), aPropertyList); + else + { + OString sName = OUStringToOString(rAttributeName, RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(rAttributeValue, RTL_TEXTENCODING_UTF8); + aPropertyList.insert(sName.getStr(), sValue.getStr()); + } + } + aPropertyList.insert("librevenge:column", m_rRow.GetColumn()); + GetImport().GetGenerator().openTableCell(aPropertyList); + m_rRow.SetColumn(m_rRow.GetColumn() + 1); +} + +void XMLTableCellContext::endElement(const OUString& /*rName*/) +{ + GetImport().GetGenerator().closeTableCell(); +} + +namespace +{ +/// Handler for <table:table-column>. +class XMLTableColumnContext : public XMLImportContext +{ +public: + XMLTableColumnContext(XMLImport& rImport, librevenge::RVNGPropertyListVector& rColumns); + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + +private: + librevenge::RVNGPropertyListVector& m_rColumns; +}; +} + +XMLTableColumnContext::XMLTableColumnContext(XMLImport& rImport, + librevenge::RVNGPropertyListVector& rColumns) + : XMLImportContext(rImport) + , m_rColumns(rColumns) +{ +} + +void XMLTableColumnContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + librevenge::RVNGPropertyList aPropertyList; + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + const OUString& rAttributeValue = xAttribs->getValueByIndex(i); + + if (rAttributeName == "table:style-name") + FillStyles(rAttributeValue, GetImport().GetAutomaticColumnStyles(), + GetImport().GetColumnStyles(), aPropertyList); + } + m_rColumns.append(aPropertyList); +} + +XMLTableRowContext::XMLTableRowContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +rtl::Reference<XMLImportContext> XMLTableRowContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "table:table-cell") + return new XMLTableCellContext(GetImport(), *this); + if (rName == "table:covered-table-cell") + { + ++m_nColumn; + GetImport().GetGenerator().insertCoveredTableCell(librevenge::RVNGPropertyList()); + } + else + SAL_WARN("writerperfect", "XMLTableRowContext::CreateChildContext: unhandled " << rName); + return nullptr; +} + +void XMLTableRowContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + librevenge::RVNGPropertyList aPropertyList; + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + const OUString& rAttributeValue = xAttribs->getValueByIndex(i); + + if (rAttributeName == "table:style-name") + FillStyles(rAttributeValue, GetImport().GetAutomaticRowStyles(), + GetImport().GetRowStyles(), aPropertyList); + } + GetImport().GetGenerator().openTableRow(aPropertyList); +} + +void XMLTableRowContext::endElement(const OUString& /*rName*/) +{ + GetImport().GetGenerator().closeTableRow(); +} + +int XMLTableRowContext::GetColumn() const { return m_nColumn; } + +void XMLTableRowContext::SetColumn(int nColumn) { m_nColumn = nColumn; } + +XMLTableContext::XMLTableContext(XMLImport& rImport, bool bTopLevel) + : XMLImportContext(rImport) + , m_bTopLevel(bTopLevel) +{ +} + +rtl::Reference<XMLImportContext> XMLTableContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + if (rName == "table:table-column") + // Make sure columns are parsed before we open the table. + return new XMLTableColumnContext(GetImport(), m_aColumns); + + if (!m_bTableOpened) + { + if (!m_aColumns.empty()) + m_aPropertyList.insert("librevenge:table-columns", m_aColumns); + GetImport().GetGenerator().openTable(m_aPropertyList); + m_bTableOpened = true; + } + + if (rName == "table:table-row") + return new XMLTableRowContext(GetImport()); + + SAL_WARN("writerperfect", "XMLTableContext::CreateChildContext: unhandled " << rName); + + return nullptr; +} + +void XMLTableContext::startElement( + const OUString& /*rName*/, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) +{ + for (sal_Int16 i = 0; i < xAttribs->getLength(); ++i) + { + const OUString& rAttributeName = xAttribs->getNameByIndex(i); + const OUString& rAttributeValue = xAttribs->getValueByIndex(i); + + if (rAttributeName == "table:style-name") + { + FillStyles(rAttributeValue, GetImport().GetAutomaticTableStyles(), + GetImport().GetTableStyles(), m_aPropertyList); + if (m_bTopLevel) + GetImport().HandlePageSpan(m_aPropertyList); + } + else + { + OString sName = OUStringToOString(rAttributeName, RTL_TEXTENCODING_UTF8); + OString sValue = OUStringToOString(rAttributeValue, RTL_TEXTENCODING_UTF8); + m_aPropertyList.insert(sName.getStr(), sValue.getStr()); + } + } +} + +void XMLTableContext::endElement(const OUString& /*rName*/) +{ + if (m_bTableOpened) + { + GetImport().GetGenerator().closeTable(); + } +} + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/xmltbli.hxx b/writerperfect/source/writer/exp/xmltbli.hxx new file mode 100644 index 000000000..c1b33f498 --- /dev/null +++ b/writerperfect/source/writer/exp/xmltbli.hxx @@ -0,0 +1,44 @@ +/* -*- 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 <librevenge/librevenge.h> + +#include "xmlictxt.hxx" + +namespace writerperfect::exp +{ +/// Handler for <table:table>. +class XMLTableContext : public XMLImportContext +{ +public: + XMLTableContext(XMLImport& rImport, bool bTopLevel = false); + + rtl::Reference<XMLImportContext> + CreateChildContext(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + + void SAL_CALL + startElement(const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override; + void SAL_CALL endElement(const OUString& rName) override; + +private: + bool m_bTableOpened = false; + /// If the context is a direct child of XMLBodyContentContext. + /// Only direct child of XMLBodyContentContext has to handle page span. + bool m_bTopLevel; + librevenge::RVNGPropertyList m_aPropertyList; + librevenge::RVNGPropertyListVector m_aColumns; +}; + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/xmltext.cxx b/writerperfect/source/writer/exp/xmltext.cxx new file mode 100644 index 000000000..933cf43e1 --- /dev/null +++ b/writerperfect/source/writer/exp/xmltext.cxx @@ -0,0 +1,55 @@ +/* -*- 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 "xmltext.hxx" + +#include "txtparai.hxx" +#include "xmltbli.hxx" +#include "XMLSectionContext.hxx" +#include "XMLTextListContext.hxx" +#include "xmlimp.hxx" + +using namespace com::sun::star; + +namespace writerperfect::exp +{ +XMLBodyContentContext::XMLBodyContentContext(XMLImport& rImport) + : XMLImportContext(rImport) +{ +} + +void XMLBodyContentContext::endElement(const OUString& /*rName*/) +{ + if (GetImport().GetIsInPageSpan()) + GetImport().GetGenerator().closePageSpan(); +} + +rtl::Reference<XMLImportContext> XMLBodyContentContext::CreateChildContext( + const OUString& rName, const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) +{ + return CreateTextChildContext(GetImport(), rName, true); +} + +rtl::Reference<XMLImportContext> CreateTextChildContext(XMLImport& rImport, + std::u16string_view rName, bool bTopLevel) +{ + if (rName == u"text:p" || rName == u"text:h") + return new XMLParaContext(rImport, bTopLevel); + if (rName == u"text:section") + return new XMLSectionContext(rImport); + if (rName == u"table:table") + return new XMLTableContext(rImport, bTopLevel); + if (rName == u"text:list") + return new XMLTextListContext(rImport); + return nullptr; +} + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/exp/xmltext.hxx b/writerperfect/source/writer/exp/xmltext.hxx new file mode 100644 index 000000000..50188acfe --- /dev/null +++ b/writerperfect/source/writer/exp/xmltext.hxx @@ -0,0 +1,34 @@ +/* -*- 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 "xmlictxt.hxx" + +namespace writerperfect::exp +{ +/// Handler for <office:text>. +class XMLBodyContentContext : public XMLImportContext +{ +public: + XMLBodyContentContext(XMLImport& rImport); + + rtl::Reference<XMLImportContext> CreateChildContext( + const OUString& rName, + const css::uno::Reference<css::xml::sax::XAttributeList>& /*xAttribs*/) override; + void SAL_CALL endElement(const OUString& rName) override; +}; + +/// Context factory for body text, section, table cell, etc. +rtl::Reference<XMLImportContext> +CreateTextChildContext(XMLImport& rImport, std::u16string_view rName, bool bTopLevel = false); + +} // namespace writerperfect::exp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/writer/wpftwriter.component b/writerperfect/source/writer/wpftwriter.component new file mode 100644 index 000000000..176d48750 --- /dev/null +++ b/writerperfect/source/writer/wpftwriter.component @@ -0,0 +1,72 @@ +<?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.Writer.AbiWordImportFilter" + constructor="com_sun_star_comp_Writer_AbiWordImportFilter_get_implementation"> + <service name="com.sun.star.document.ImportFilter"/> + <service name="com.sun.star.document.ExtendedTypeDetection"/> + <optional/> + </implementation> + <implementation name="com.sun.star.comp.Writer.MSWorksImportFilter" + constructor="com_sun_star_comp_Writer_MSWorksImportFilter_get_implementation"> + <service name="com.sun.star.document.ImportFilter"/> + <service name="com.sun.star.document.ExtendedTypeDetection"/> + <optional/> + </implementation> + <implementation name="com.sun.star.comp.Writer.MWAWImportFilter" + constructor="com_sun_star_comp_Writer_MWAWImportFilter_get_implementation"> + <service name="com.sun.star.document.ImportFilter"/> + <service name="com.sun.star.document.ExtendedTypeDetection"/> + <optional/> + </implementation> + <implementation name="com.sun.star.comp.Writer.WordPerfectImportFilter" + constructor="com_sun_star_comp_Writer_WordPerfectImportFilter_get_implementation"> + <service name="com.sun.star.document.ExtendedTypeDetection"/> + <service name="com.sun.star.document.ImportFilter"/> + <optional/> + </implementation> + <implementation name="org.libreoffice.comp.Writer.EBookImportFilter" + constructor="org_libreoffice_comp_Writer_EBookImportFilter_get_implementation"> + <service name="com.sun.star.document.ExtendedTypeDetection"/> + <service name="com.sun.star.document.ImportFilter"/> + <optional/> + </implementation> + <implementation name="org.libreoffice.comp.Writer.PagesImportFilter" + constructor="org_libreoffice_comp_Writer_PagesImportFilter_get_implementation"> + <service name="com.sun.star.document.ExtendedTypeDetection"/> + <service name="com.sun.star.document.ImportFilter"/> + <optional/> + </implementation> + <implementation name="org.libreoffice.comp.Writer.StarOfficeWriterImportFilter" + constructor="org_libreoffice_comp_Writer_StarOfficeWriterImportFilter_get_implementation"> + <service name="com.sun.star.document.ImportFilter"/> + <service name="com.sun.star.document.ExtendedTypeDetection"/> + </implementation> + <implementation name="com.sun.star.comp.Writer.EPUBExportFilter" + constructor="com_sun_star_comp_Writer_EPUBExportFilter_get_implementation"> + <service name="com.sun.star.document.ExportFilter"/> + <optional/> + </implementation> + <implementation name="com.sun.star.comp.Writer.EPUBExportUIComponent" + constructor="com_sun_star_comp_Writer_EPUBExportUIComponent_get_implementation"> + <service name="com.sun.star.ui.dialogs.FilterOptionsDialog"/> + <optional/> + </implementation> +</component> diff --git a/writerperfect/source/writer/wpftwriter.component.extended b/writerperfect/source/writer/wpftwriter.component.extended new file mode 100644 index 000000000..577d05e96 --- /dev/null +++ b/writerperfect/source/writer/wpftwriter.component.extended @@ -0,0 +1,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/. + +com.sun.star.comp.Writer.EPUBExportFilter +com.sun.star.comp.Writer.EPUBExportUIComponent diff --git a/writerperfect/source/writer/wpftwriter.component.extended2 b/writerperfect/source/writer/wpftwriter.component.extended2 new file mode 100644 index 000000000..91cd0e046 --- /dev/null +++ b/writerperfect/source/writer/wpftwriter.component.extended2 @@ -0,0 +1,12 @@ +# 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/. + +com.sun.star.comp.Writer.AbiWordImportFilter +com.sun.star.comp.Writer.MSWorksImportFilter +com.sun.star.comp.Writer.MWAWImportFilter +com.sun.star.comp.Writer.WordPerfectImportFilter +org.libreoffice.comp.Writer.EBookImportFilter +org.libreoffice.comp.Writer.PagesImportFilter |