1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
25 #include <IDocumentStylePoolAccess.hxx>
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
;
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*/)
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
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());
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
),
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(
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
);
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
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
);
179 pTextNode
->ChgFormatColl(pDelNd
->GetTextColl());
180 pTextNode
->JoinPrev();
187 extern "C" SAL_DLLPUBLIC_EXPORT Reader
* ImportRTF() { return new SwRTFReader
; }
189 extern "C" SAL_DLLPUBLIC_EXPORT
bool TestImportRTF(SvStream
& rStream
)
193 SfxObjectShellLock
xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL
));
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
),
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(
209 uno::Any(uno::Reference
<io::XStream
>(new utl::OStreamWrapper(rStream
))) } }));
213 xFilter
->filter(aDescriptor
);
222 extern "C" SAL_DLLPUBLIC_EXPORT
bool TestPDFExportRTF(SvStream
& rStream
)
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\"")
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
),
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
) } }));
271 ret
= xFilter
->filter(aArgs
);
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)
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
),
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);
330 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */