Add Marathi autocorrect
[LibreOffice.git] / sw / source / filter / rtf / swparrtf.cxx
blobae7eac7c53e74df8b0ac0ac571e2cb69c65dad33
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <poolfmt.hxx>
21 #include <shellio.hxx>
22 #include <ndtxt.hxx>
23 #include <doc.hxx>
24 #include <docsh.hxx>
25 #include <IDocumentStylePoolAccess.hxx>
26 #include <swdll.hxx>
27 #include <swerror.h>
29 #include <unotextrange.hxx>
31 #include <unotools/fcm.hxx>
32 #include <unotools/streamwrap.hxx>
33 #include <unotools/tempfile.hxx>
34 #include <comphelper/processfactory.hxx>
35 #include <comphelper/propertysequence.hxx>
36 #include <comphelper/diagnose_ex.hxx>
38 #include <com/sun/star/document/XFilter.hpp>
39 #include <com/sun/star/document/XExporter.hpp>
40 #include <com/sun/star/document/XImporter.hpp>
41 #include <com/sun/star/frame/Desktop.hpp>
42 #include <com/sun/star/frame/XLoadable.hpp>
43 #include <com/sun/star/frame/XModel.hpp>
44 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
45 #include <com/sun/star/text/XTextDocument.hpp>
46 #include <com/sun/star/util/XCloseable.hpp>
48 using namespace ::com::sun::star;
50 namespace
52 /// Glue class to call RtfImport as an internal filter, needed by copy&paste support.
53 class SwRTFReader : public Reader
55 ErrCodeMsg Read(SwDoc& rDoc, const OUString& rBaseURL, SwPaM& rPam,
56 const OUString& rFileName) override;
60 ErrCodeMsg SwRTFReader::Read(SwDoc& rDoc, const OUString& /*rBaseURL*/, SwPaM& rPam,
61 const OUString& /*rFileName*/)
63 if (!m_pStream)
64 return ERR_SWG_READ_ERROR;
66 // We want to work in an empty paragraph.
67 // Step 1: XTextRange will be updated when content is inserted, so we know
68 // the end position.
69 const rtl::Reference<SwXTextRange> xInsertPosition
70 = SwXTextRange::CreateXTextRange(rDoc, *rPam.GetPoint(), nullptr);
71 auto pSttNdIdx = std::make_shared<SwNodeIndex>(rDoc.GetNodes());
72 const SwPosition* pPos = rPam.GetPoint();
74 // Step 2: Split once and remember the node that has been split.
75 rDoc.getIDocumentContentOperations().SplitNode(*pPos, false);
76 *pSttNdIdx = pPos->GetNodeIndex() - 1;
78 // Step 3: Split again.
79 rDoc.getIDocumentContentOperations().SplitNode(*pPos, false);
80 auto pSttNdIdx2 = std::make_shared<SwNodeIndex>(rDoc.GetNodes());
81 *pSttNdIdx2 = pPos->GetNodeIndex();
83 // Step 4: Insert all content into the new node
84 rPam.Move(fnMoveBackward);
85 rDoc.SetTextFormatColl(
86 rPam, rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false));
88 auto ret = ERRCODE_NONE;
89 SwDocShell* pDocShell(rDoc.GetDocShell());
90 if (!pDocShell)
91 return ret;
93 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(
94 comphelper::getProcessServiceFactory());
95 uno::Reference<uno::XInterface> xInterface(
96 xMultiServiceFactory->createInstance(u"com.sun.star.comp.Writer.RtfFilter"_ustr),
97 uno::UNO_SET_THROW);
99 uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW);
100 uno::Reference<lang::XComponent> xDstDoc(pDocShell->GetModel(), uno::UNO_QUERY_THROW);
101 xImporter->setTargetDocument(xDstDoc);
103 const rtl::Reference<SwXTextRange> xInsertTextRange
104 = SwXTextRange::CreateXTextRange(rDoc, *rPam.GetPoint(), nullptr);
106 uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW);
107 uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence(
108 { { "InputStream",
109 uno::Any(uno::Reference<io::XStream>(new utl::OStreamWrapper(*m_pStream))) },
110 { "InsertMode", uno::Any(true) },
111 { "TextInsertModeRange",
112 uno::Any(uno::Reference<text::XTextRange>(xInsertTextRange)) } }));
115 xFilter->filter(aDescriptor);
117 catch (uno::Exception const&)
119 TOOLS_WARN_EXCEPTION("sw.rtf", "SwRTFReader::Read()");
120 ret = ERR_SWG_READ_ERROR;
123 // Clean up the fake paragraphs.
124 SwUnoInternalPaM aPam(rDoc);
125 ::sw::XTextRangeToSwPaM(aPam, xInsertPosition);
126 if (pSttNdIdx->GetIndex())
128 // If we are in insert mode, join the split node that is in front
129 // of the new content with the first new node. Or in other words:
130 // Revert the first split node.
131 SwTextNode* pTextNode = pSttNdIdx->GetNode().GetTextNode();
132 SwNodeIndex aNxtIdx(*pSttNdIdx);
133 if (pTextNode && pTextNode->CanJoinNext(&aNxtIdx)
134 && pSttNdIdx->GetIndex() + 1 == aNxtIdx.GetIndex())
136 // If the PaM points to the first new node, move the PaM to the
137 // end of the previous node.
138 if (aPam.GetPoint()->GetNode() == aNxtIdx.GetNode())
140 aPam.GetPoint()->Assign(*pTextNode, pTextNode->GetText().getLength());
142 // If the first new node isn't empty, convert the node's text
143 // attributes into hints. Otherwise, set the new node's
144 // paragraph style at the previous (empty) node.
145 SwTextNode* pDelNd = aNxtIdx.GetNode().GetTextNode();
146 if (pTextNode->GetText().getLength())
147 pDelNd->FormatToTextAttr(pTextNode);
148 else
150 pTextNode->ChgFormatColl(pDelNd->GetTextColl());
151 if (!pDelNd->GetNoCondAttr(RES_PARATR_LIST_ID, /*bInParents=*/false))
153 // Lists would need manual merging, but copy paragraph direct formatting
154 // otherwise.
155 pDelNd->CopyCollFormat(*pTextNode);
158 pTextNode->JoinNext();
162 if (pSttNdIdx2->GetIndex())
164 // If we are in insert mode, join the split node that is after
165 // the new content with the last new node. Or in other words:
166 // Revert the second split node.
167 SwTextNode* pTextNode = pSttNdIdx2->GetNode().GetTextNode();
168 SwNodeIndex aPrevIdx(*pSttNdIdx2);
169 if (pTextNode && pTextNode->CanJoinPrev(&aPrevIdx)
170 && pSttNdIdx2->GetIndex() - 1 == aPrevIdx.GetIndex())
172 // If the last new node isn't empty, convert the node's text
173 // attributes into hints. Otherwise, set the new node's
174 // paragraph style at the next (empty) node.
175 SwTextNode* pDelNd = aPrevIdx.GetNode().GetTextNode();
176 if (pTextNode->GetText().getLength())
177 pDelNd->FormatToTextAttr(pTextNode);
178 else
179 pTextNode->ChgFormatColl(pDelNd->GetTextColl());
180 pTextNode->JoinPrev();
184 return ret;
187 extern "C" SAL_DLLPUBLIC_EXPORT Reader* ImportRTF() { return new SwRTFReader; }
189 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportRTF(SvStream& rStream)
191 SwGlobals::ensure();
193 SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL));
194 xDocSh->DoInitNew();
196 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(
197 comphelper::getProcessServiceFactory());
198 uno::Reference<uno::XInterface> xInterface(
199 xMultiServiceFactory->createInstance(u"com.sun.star.comp.Writer.RtfFilter"_ustr),
200 uno::UNO_SET_THROW);
202 uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW);
203 uno::Reference<lang::XComponent> xDstDoc(xDocSh->GetModel(), uno::UNO_QUERY_THROW);
204 xImporter->setTargetDocument(xDstDoc);
206 uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW);
207 uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence(
208 { { "InputStream",
209 uno::Any(uno::Reference<io::XStream>(new utl::OStreamWrapper(rStream))) } }));
210 bool bRet = true;
213 xFilter->filter(aDescriptor);
215 catch (...)
217 bRet = false;
219 return bRet;
222 extern "C" SAL_DLLPUBLIC_EXPORT bool TestPDFExportRTF(SvStream& rStream)
224 #if 0
225 //TODO: probably end up needing one of these too
226 // do the same sort of check as FilterDetect::detect
227 OString const str(read_uInt8s_ToOString(rStream, 4000));
228 rStream.Seek(STREAM_SEEK_TO_BEGIN);
229 OUString resultString(str.getStr(), str.getLength(), RTL_TEXTENCODING_ASCII_US,
230 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT
231 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT
232 | RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT);
233 if (!resultString.startsWith("<?xml")
234 || resultString.indexOf("office:mimetype=\"application/vnd.oasis.opendocument.text\"")
235 == -1)
236 return false;
237 #endif
239 uno::Reference<css::frame::XDesktop2> xDesktop
240 = css::frame::Desktop::create(comphelper::getProcessComponentContext());
241 uno::Reference<css::frame::XFrame> xTargetFrame = xDesktop->findFrame(u"_blank"_ustr, 0);
243 const uno::Reference<uno::XComponentContext>& xContext(
244 comphelper::getProcessComponentContext());
245 uno::Reference<css::frame::XModel2> xModel(
246 xContext->getServiceManager()->createInstanceWithContext(
247 u"com.sun.star.text.TextDocument"_ustr, xContext),
248 uno::UNO_QUERY_THROW);
250 uno::Reference<css::frame::XLoadable> xModelLoad(xModel, uno::UNO_QUERY_THROW);
251 xModelLoad->initNew();
253 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(
254 comphelper::getProcessServiceFactory());
255 uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream));
257 uno::Reference<uno::XInterface> xInterface(
258 xMultiServiceFactory->createInstance(u"com.sun.star.comp.Writer.RtfFilter"_ustr),
259 uno::UNO_SET_THROW);
261 uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW);
262 xImporter->setTargetDocument(xModel);
264 uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW);
265 uno::Sequence<beans::PropertyValue> aArgs(
266 comphelper::InitPropertySequence({ { "InputStream", uno::Any(xStream) } }));
268 bool ret = true;
271 ret = xFilter->filter(aArgs);
273 catch (...)
275 ret = false;
278 if (ret)
280 uno::Reference<text::XTextDocument> xTextDocument(xModel, uno::UNO_QUERY);
281 uno::Reference<text::XText> xText(xTextDocument->getText());
282 uno::Reference<container::XEnumerationAccess> xParaAccess(xText, uno::UNO_QUERY);
283 uno::Reference<container::XEnumeration> xParaEnum(xParaAccess->createEnumeration());
284 while (xParaEnum->hasMoreElements())
286 uno::Reference<text::XTextRange> xPara(xParaEnum->nextElement(), uno::UNO_QUERY);
287 // discourage very long paragraphs for fuzzing performance
288 if (xPara && xPara->getString().getLength() > 15000)
290 ret = false;
291 break;
296 if (ret)
298 css::uno::Reference<css::frame::XController2> xController(
299 xModel->createDefaultViewController(xTargetFrame), uno::UNO_SET_THROW);
300 utl::ConnectFrameControllerModel(xTargetFrame, xController, xModel);
302 utl::TempFileFast aTempFile;
304 uno::Reference<document::XFilter> xPDFFilter(
305 xMultiServiceFactory->createInstance(u"com.sun.star.document.PDFFilter"_ustr),
306 uno::UNO_QUERY);
307 uno::Reference<document::XExporter> xExporter(xPDFFilter, uno::UNO_QUERY);
308 xExporter->setSourceDocument(xModel);
310 uno::Reference<io::XOutputStream> xOutputStream(
311 new utl::OStreamWrapper(*aTempFile.GetStream(StreamMode::READWRITE)));
313 // ofz#60533 fuzzer learned to use fo:font-size="842pt" which generate timeouts trying
314 // to export thousands of pages from minimal input size
315 uno::Sequence<beans::PropertyValue> aFilterData(
316 comphelper::InitPropertySequence({ { "PageRange", uno::Any(u"1-100"_ustr) } }));
317 uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence(
318 { { "FilterName", uno::Any(u"writer_pdf_Export"_ustr) },
319 { "OutputStream", uno::Any(xOutputStream) },
320 { "FilterData", uno::Any(aFilterData) } }));
321 xPDFFilter->filter(aDescriptor);
324 css::uno::Reference<css::util::XCloseable> xClose(xModel, css::uno::UNO_QUERY);
325 xClose->close(false);
327 return ret;
330 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */