summaryrefslogtreecommitdiffstats
path: root/sw/source/filter/rtf/swparrtf.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/filter/rtf/swparrtf.cxx')
-rw-r--r--sw/source/filter/rtf/swparrtf.cxx212
1 files changed, 212 insertions, 0 deletions
diff --git a/sw/source/filter/rtf/swparrtf.cxx b/sw/source/filter/rtf/swparrtf.cxx
new file mode 100644
index 0000000000..56dd365302
--- /dev/null
+++ b/sw/source/filter/rtf/swparrtf.cxx
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <poolfmt.hxx>
+#include <shellio.hxx>
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <swdll.hxx>
+#include <swerror.h>
+
+#include <unotextrange.hxx>
+
+#include <unotools/streamwrap.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XImporter.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Glue class to call RtfImport as an internal filter, needed by copy&paste support.
+class SwRTFReader : public Reader
+{
+ ErrCodeMsg Read(SwDoc& rDoc, const OUString& rBaseURL, SwPaM& rPam,
+ const OUString& rFileName) override;
+};
+}
+
+ErrCodeMsg SwRTFReader::Read(SwDoc& rDoc, const OUString& /*rBaseURL*/, SwPaM& rPam,
+ const OUString& /*rFileName*/)
+{
+ if (!m_pStream)
+ return ERR_SWG_READ_ERROR;
+
+ // We want to work in an empty paragraph.
+ // Step 1: XTextRange will be updated when content is inserted, so we know
+ // the end position.
+ const rtl::Reference<SwXTextRange> xInsertPosition
+ = SwXTextRange::CreateXTextRange(rDoc, *rPam.GetPoint(), nullptr);
+ auto pSttNdIdx = std::make_shared<SwNodeIndex>(rDoc.GetNodes());
+ const SwPosition* pPos = rPam.GetPoint();
+
+ // Step 2: Split once and remember the node that has been split.
+ rDoc.getIDocumentContentOperations().SplitNode(*pPos, false);
+ *pSttNdIdx = pPos->GetNodeIndex() - 1;
+
+ // Step 3: Split again.
+ rDoc.getIDocumentContentOperations().SplitNode(*pPos, false);
+ auto pSttNdIdx2 = std::make_shared<SwNodeIndex>(rDoc.GetNodes());
+ *pSttNdIdx2 = pPos->GetNodeIndex();
+
+ // Step 4: Insert all content into the new node
+ rPam.Move(fnMoveBackward);
+ rDoc.SetTextFormatColl(
+ rPam, rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false));
+
+ SwDocShell* pDocShell(rDoc.GetDocShell());
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(
+ comphelper::getProcessServiceFactory());
+ uno::Reference<uno::XInterface> xInterface(
+ xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.RtfFilter"),
+ uno::UNO_SET_THROW);
+
+ uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW);
+ uno::Reference<lang::XComponent> xDstDoc(pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ xImporter->setTargetDocument(xDstDoc);
+
+ const rtl::Reference<SwXTextRange> xInsertTextRange
+ = SwXTextRange::CreateXTextRange(rDoc, *rPam.GetPoint(), nullptr);
+
+ uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence(
+ { { "InputStream",
+ uno::Any(uno::Reference<io::XStream>(new utl::OStreamWrapper(*m_pStream))) },
+ { "InsertMode", uno::Any(true) },
+ { "TextInsertModeRange",
+ uno::Any(uno::Reference<text::XTextRange>(xInsertTextRange)) } }));
+ auto ret = ERRCODE_NONE;
+ try
+ {
+ xFilter->filter(aDescriptor);
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("sw.rtf", "SwRTFReader::Read()");
+ ret = ERR_SWG_READ_ERROR;
+ }
+
+ // Clean up the fake paragraphs.
+ SwUnoInternalPaM aPam(rDoc);
+ ::sw::XTextRangeToSwPaM(aPam, xInsertPosition);
+ if (pSttNdIdx->GetIndex())
+ {
+ // If we are in insert mode, join the split node that is in front
+ // of the new content with the first new node. Or in other words:
+ // Revert the first split node.
+ SwTextNode* pTextNode = pSttNdIdx->GetNode().GetTextNode();
+ SwNodeIndex aNxtIdx(*pSttNdIdx);
+ if (pTextNode && pTextNode->CanJoinNext(&aNxtIdx)
+ && pSttNdIdx->GetIndex() + 1 == aNxtIdx.GetIndex())
+ {
+ // If the PaM points to the first new node, move the PaM to the
+ // end of the previous node.
+ if (aPam.GetPoint()->GetNode() == aNxtIdx.GetNode())
+ {
+ aPam.GetPoint()->Assign(*pTextNode, pTextNode->GetText().getLength());
+ }
+ // If the first new node isn't empty, convert the node's text
+ // attributes into hints. Otherwise, set the new node's
+ // paragraph style at the previous (empty) node.
+ SwTextNode* pDelNd = aNxtIdx.GetNode().GetTextNode();
+ if (pTextNode->GetText().getLength())
+ pDelNd->FormatToTextAttr(pTextNode);
+ else
+ {
+ pTextNode->ChgFormatColl(pDelNd->GetTextColl());
+ if (!pDelNd->GetNoCondAttr(RES_PARATR_LIST_ID, /*bInParents=*/false))
+ {
+ // Lists would need manual merging, but copy paragraph direct formatting
+ // otherwise.
+ pDelNd->CopyCollFormat(*pTextNode);
+ }
+ }
+ pTextNode->JoinNext();
+ }
+ }
+
+ if (pSttNdIdx2->GetIndex())
+ {
+ // If we are in insert mode, join the split node that is after
+ // the new content with the last new node. Or in other words:
+ // Revert the second split node.
+ SwTextNode* pTextNode = pSttNdIdx2->GetNode().GetTextNode();
+ SwNodeIndex aPrevIdx(*pSttNdIdx2);
+ if (pTextNode && pTextNode->CanJoinPrev(&aPrevIdx)
+ && pSttNdIdx2->GetIndex() - 1 == aPrevIdx.GetIndex())
+ {
+ // If the last new node isn't empty, convert the node's text
+ // attributes into hints. Otherwise, set the new node's
+ // paragraph style at the next (empty) node.
+ SwTextNode* pDelNd = aPrevIdx.GetNode().GetTextNode();
+ if (pTextNode->GetText().getLength())
+ pDelNd->FormatToTextAttr(pTextNode);
+ else
+ pTextNode->ChgFormatColl(pDelNd->GetTextColl());
+ pTextNode->JoinPrev();
+ }
+ }
+
+ return ret;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT Reader* ImportRTF() { return new SwRTFReader; }
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportRTF(SvStream& rStream)
+{
+ SwGlobals::ensure();
+
+ SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL));
+ xDocSh->DoInitNew();
+
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(
+ comphelper::getProcessServiceFactory());
+ uno::Reference<uno::XInterface> xInterface(
+ xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.RtfFilter"),
+ uno::UNO_SET_THROW);
+
+ uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW);
+ uno::Reference<lang::XComponent> xDstDoc(xDocSh->GetModel(), uno::UNO_QUERY_THROW);
+ xImporter->setTargetDocument(xDstDoc);
+
+ uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence(
+ { { "InputStream",
+ uno::Any(uno::Reference<io::XStream>(new utl::OStreamWrapper(rStream))) } }));
+ bool bRet = true;
+ try
+ {
+ xFilter->filter(aDescriptor);
+ }
+ catch (...)
+ {
+ bRet = false;
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */