1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39#include "qtextdocumentwriter.h"
40
41#include <QtCore/qfile.h>
42#include <QtCore/qbytearray.h>
43#include <QtCore/qfileinfo.h>
44#include <QtCore/qtextstream.h>
45#include <QtCore/qdebug.h>
46#include "qtextdocument.h"
47#include "qtextdocumentfragment.h"
48
49#include "qtextdocumentfragment_p.h"
50#include "qtextodfwriter_p.h"
51#if QT_CONFIG(textmarkdownwriter)
52#include "qtextmarkdownwriter_p.h"
53#endif
54
55#include <algorithm>
56
57QT_BEGIN_NAMESPACE
58
59class QTextDocumentWriterPrivate
60{
61public:
62 QTextDocumentWriterPrivate(QTextDocumentWriter* qq);
63
64 // device
65 QByteArray format;
66 QIODevice *device;
67 bool deleteDevice;
68
69 QTextDocumentWriter *q;
70};
71
72/*!
73 \since 4.5
74 \class QTextDocumentWriter
75
76 \brief The QTextDocumentWriter class provides a format-independent interface for writing a QTextDocument to files or other devices.
77 \inmodule QtGui
78
79 \ingroup richtext-processing
80 \ingroup io
81
82 To write a document, construct a QTextDocumentWriter object with either a
83 file name or a device object, and specify the document format to be
84 written. You can construct a writer and set the format using setFormat()
85 later.
86
87 Call write() to write the document to the device. If the document is
88 successfully written, this function returns \c true. However, if an error
89 occurs when writing the document, it will return false.
90
91 Call supportedDocumentFormats() for a list of formats that
92 QTextDocumentWriter can write.
93
94 Since the capabilities of the supported output formats vary considerably,
95 the writer simply outputs the appropriate subset of objects for each format.
96 This typically includes the formatted text and images contained in a
97 document.
98*/
99
100/*!
101 \internal
102*/
103QTextDocumentWriterPrivate::QTextDocumentWriterPrivate(QTextDocumentWriter *qq)
104 : device(nullptr),
105 deleteDevice(false),
106 q(qq)
107{
108}
109
110/*!
111 Constructs an empty QTextDocumentWriter object. Before writing, you must
112 call setFormat() to set a document format, then setDevice() or
113 setFileName().
114*/
115QTextDocumentWriter::QTextDocumentWriter()
116 : d(new QTextDocumentWriterPrivate(this))
117{
118}
119
120/*!
121 Constructs a QTextDocumentWriter object to write to the given \a device
122 in the document format specified by \a format.
123*/
124QTextDocumentWriter::QTextDocumentWriter(QIODevice *device, const QByteArray &format)
125 : d(new QTextDocumentWriterPrivate(this))
126{
127 d->device = device;
128 d->format = format;
129}
130
131/*!
132 Constructs an QTextDocumentWriter object that will write to a file with
133 the name \a fileName, using the document format specified by \a format.
134 If \a format is not provided, QTextDocumentWriter will detect the document
135 format by inspecting the extension of \a fileName.
136*/
137QTextDocumentWriter::QTextDocumentWriter(const QString &fileName, const QByteArray &format)
138 : QTextDocumentWriter(new QFile(fileName), format)
139{
140 d->deleteDevice = true;
141}
142
143/*!
144 Destroys the QTextDocumentWriter object.
145*/
146QTextDocumentWriter::~QTextDocumentWriter()
147{
148 if (d->deleteDevice)
149 delete d->device;
150 delete d;
151}
152
153/*!
154 Sets the format used to write documents to the \a format specified.
155 \a format is a case insensitive text string. For example:
156
157 \snippet code/src_gui_text_qtextdocumentwriter.cpp 0
158
159 You can call supportedDocumentFormats() for the full list of formats
160 QTextDocumentWriter supports.
161
162 \sa format()
163*/
164void QTextDocumentWriter::setFormat (const QByteArray &format)
165{
166 d->format = format;
167}
168
169/*!
170 Returns the format used for writing documents.
171
172 \sa setFormat()
173*/
174QByteArray QTextDocumentWriter::format () const
175{
176 return d->format;
177}
178
179/*!
180 Sets the writer's device to the \a device specified. If a device has
181 already been set, the old device is removed but otherwise left
182 unchanged.
183
184 If the device is not already open, QTextDocumentWriter will attempt to
185 open the device in \l QIODevice::WriteOnly mode by calling open().
186
187 \note This will not work for certain devices, such as QProcess,
188 QTcpSocket and QUdpSocket, where some configuration is required before
189 the device can be opened.
190
191 \sa device(), setFileName()
192*/
193void QTextDocumentWriter::setDevice (QIODevice *device)
194{
195 if (d->device && d->deleteDevice)
196 delete d->device;
197
198 d->device = device;
199 d->deleteDevice = false;
200}
201
202/*!
203 Returns the device currently assigned, or \nullptr if no device
204 has been assigned.
205*/
206QIODevice *QTextDocumentWriter::device () const
207{
208 return d->device;
209}
210
211/*!
212 Sets the name of the file to be written to \a fileName. Internally,
213 QTextDocumentWriter will create a QFile and open it in \l
214 QIODevice::WriteOnly mode, and use this file when writing the document.
215
216 \sa fileName(), setDevice()
217*/
218void QTextDocumentWriter::setFileName (const QString &fileName)
219{
220 setDevice(new QFile(fileName));
221 d->deleteDevice = true;
222}
223
224/*!
225 If the currently assigned device is a QFile, or if setFileName()
226 has been called, this function returns the name of the file
227 to be written to. In all other cases, it returns an empty string.
228
229 \sa setFileName(), setDevice()
230*/
231QString QTextDocumentWriter::fileName () const
232{
233 QFile *file = qobject_cast<QFile *>(d->device);
234 return file ? file->fileName() : QString();
235}
236
237/*!
238 Writes the given \a document to the assigned device or file and
239 returns \c true if successful; otherwise returns \c false.
240*/
241bool QTextDocumentWriter::write(const QTextDocument *document)
242{
243 QByteArray suffix;
244
245 if (d->device && d->format.isEmpty()) {
246 // if there's no format, see if device is a file, and if so, find
247 // the file suffix
248 if (QFile *file = qobject_cast<QFile *>(d->device))
249 suffix = QFileInfo(file->fileName()).suffix().toLower().toLatin1();
250 }
251
252 QByteArray format = !d->format.isEmpty() ? d->format.toLower() : suffix;
253
254#ifndef QT_NO_TEXTODFWRITER
255 if (format == "odf" || format == "opendocumentformat" || format == "odt") {
256 QTextOdfWriter writer(*document, d->device);
257 return writer.writeAll();
258 }
259#endif // QT_NO_TEXTODFWRITER
260
261#if QT_CONFIG(textmarkdownwriter)
262 if (format == "md" || format == "mkd" || format == "markdown") {
263 if (!d->device->isWritable() && !d->device->open(QIODevice::WriteOnly)) {
264 qWarning("QTextDocumentWriter::write: the device can not be opened for writing");
265 return false;
266 }
267 QTextStream s(d->device);
268 QTextMarkdownWriter writer(s, QTextDocument::MarkdownDialectGitHub);
269 return writer.writeAll(document);
270 }
271#endif // textmarkdownwriter
272
273#ifndef QT_NO_TEXTHTMLPARSER
274 if (format == "html" || format == "htm") {
275 if (!d->device->isWritable() && ! d->device->open(QIODevice::WriteOnly)) {
276 qWarning("QTextDocumentWriter::write: the device cannot be opened for writing");
277 return false;
278 }
279 d->device->write(document->toHtml().toUtf8());
280 d->device->close();
281 return true;
282 }
283#endif
284 if (format == "txt" || format == "plaintext") {
285 if (!d->device->isWritable() && ! d->device->open(QIODevice::WriteOnly)) {
286 qWarning("QTextDocumentWriter::write: the device cannot be opened for writing");
287 return false;
288 }
289 d->device->write(document->toPlainText().toUtf8());
290 d->device->close();
291 return true;
292 }
293
294 return false;
295}
296
297/*!
298 Writes the document fragment specified by \a fragment to the assigned device
299 or file and returns \c true if successful; otherwise returns \c false.
300*/
301bool QTextDocumentWriter::write(const QTextDocumentFragment &fragment)
302{
303 if (fragment.d == nullptr)
304 return false; // invalid fragment.
305 QTextDocument *doc = fragment.d->doc;
306 if (doc)
307 return write(doc);
308 return false;
309}
310
311/*!
312 Returns the list of document formats supported by QTextDocumentWriter.
313
314 By default, Qt can write the following formats:
315
316 \table
317 \header \li Format \li Description
318 \row \li plaintext \li Plain text
319 \row \li HTML \li HyperText Markup Language
320 \row \li markdown \li Markdown (CommonMark or GitHub dialects)
321 \row \li ODF \li OpenDocument Format
322 \endtable
323
324 \sa setFormat()
325*/
326QList<QByteArray> QTextDocumentWriter::supportedDocumentFormats()
327{
328 QList<QByteArray> answer;
329 answer << "plaintext";
330
331#ifndef QT_NO_TEXTHTMLPARSER
332 answer << "HTML";
333#endif
334#ifndef QT_NO_TEXTODFWRITER
335 answer << "ODF";
336#endif // QT_NO_TEXTODFWRITER
337#if QT_CONFIG(textmarkdownwriter)
338 answer << "markdown";
339#endif
340
341 std::sort(answer.begin(), answer.end());
342 return answer;
343}
344
345QT_END_NAMESPACE
346