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#if QT_CONFIG(textcodec)
45#include <QtCore/qtextcodec.h>
46#endif
47#include <QtCore/qtextstream.h>
48#include <QtCore/qdebug.h>
49#include "qtextdocument.h"
50#include "qtextdocumentfragment.h"
51
52#include "qtextdocumentfragment_p.h"
53#include "qtextodfwriter_p.h"
54#if QT_CONFIG(textmarkdownwriter)
55#include "qtextmarkdownwriter_p.h"
56#endif
57
58#include <algorithm>
59
60QT_BEGIN_NAMESPACE
61
62class QTextDocumentWriterPrivate
63{
64public:
65 QTextDocumentWriterPrivate(QTextDocumentWriter* qq);
66
67 // device
68 QByteArray format;
69 QIODevice *device;
70 bool deleteDevice;
71#if QT_CONFIG(textcodec)
72 QTextCodec *codec;
73#endif
74
75 QTextDocumentWriter *q;
76};
77
78/*!
79 \since 4.5
80 \class QTextDocumentWriter
81
82 \brief The QTextDocumentWriter class provides a format-independent interface for writing a QTextDocument to files or other devices.
83 \inmodule QtGui
84
85 \ingroup richtext-processing
86 \ingroup io
87
88 To write a document, construct a QTextDocumentWriter object with either a
89 file name or a device object, and specify the document format to be
90 written. You can construct a writer and set the format using setFormat()
91 later.
92
93 Call write() to write the document to the device. If the document is
94 successfully written, this function returns \c true. However, if an error
95 occurs when writing the document, it will return false.
96
97 Call supportedDocumentFormats() for a list of formats that
98 QTextDocumentWriter can write.
99
100 Since the capabilities of the supported output formats vary considerably,
101 the writer simply outputs the appropriate subset of objects for each format.
102 This typically includes the formatted text and images contained in a
103 document.
104*/
105
106/*!
107 \internal
108*/
109QTextDocumentWriterPrivate::QTextDocumentWriterPrivate(QTextDocumentWriter *qq)
110 : device(nullptr),
111 deleteDevice(false),
112#if QT_CONFIG(textcodec)
113 codec(QTextCodec::codecForName(name: "utf-8")),
114#endif
115 q(qq)
116{
117}
118
119/*!
120 Constructs an empty QTextDocumentWriter object. Before writing, you must
121 call setFormat() to set a document format, then setDevice() or
122 setFileName().
123*/
124QTextDocumentWriter::QTextDocumentWriter()
125 : d(new QTextDocumentWriterPrivate(this))
126{
127}
128
129/*!
130 Constructs a QTextDocumentWriter object to write to the given \a device
131 in the document format specified by \a format.
132*/
133QTextDocumentWriter::QTextDocumentWriter(QIODevice *device, const QByteArray &format)
134 : d(new QTextDocumentWriterPrivate(this))
135{
136 d->device = device;
137 d->format = format;
138}
139
140/*!
141 Constructs an QTextDocumentWriter object that will write to a file with
142 the name \a fileName, using the document format specified by \a format.
143 If \a format is not provided, QTextDocumentWriter will detect the document
144 format by inspecting the extension of \a fileName.
145*/
146QTextDocumentWriter::QTextDocumentWriter(const QString &fileName, const QByteArray &format)
147 : QTextDocumentWriter(new QFile(fileName), format)
148{
149 d->deleteDevice = true;
150}
151
152/*!
153 Destroys the QTextDocumentWriter object.
154*/
155QTextDocumentWriter::~QTextDocumentWriter()
156{
157 if (d->deleteDevice)
158 delete d->device;
159 delete d;
160}
161
162/*!
163 Sets the format used to write documents to the \a format specified.
164 \a format is a case insensitive text string. For example:
165
166 \snippet code/src_gui_text_qtextdocumentwriter.cpp 0
167
168 You can call supportedDocumentFormats() for the full list of formats
169 QTextDocumentWriter supports.
170
171 \sa format()
172*/
173void QTextDocumentWriter::setFormat (const QByteArray &format)
174{
175 d->format = format;
176}
177
178/*!
179 Returns the format used for writing documents.
180
181 \sa setFormat()
182*/
183QByteArray QTextDocumentWriter::format () const
184{
185 return d->format;
186}
187
188/*!
189 Sets the writer's device to the \a device specified. If a device has
190 already been set, the old device is removed but otherwise left
191 unchanged.
192
193 If the device is not already open, QTextDocumentWriter will attempt to
194 open the device in \l QIODevice::WriteOnly mode by calling open().
195
196 \note This will not work for certain devices, such as QProcess,
197 QTcpSocket and QUdpSocket, where some configuration is required before
198 the device can be opened.
199
200 \sa device(), setFileName()
201*/
202void QTextDocumentWriter::setDevice (QIODevice *device)
203{
204 if (d->device && d->deleteDevice)
205 delete d->device;
206
207 d->device = device;
208 d->deleteDevice = false;
209}
210
211/*!
212 Returns the device currently assigned, or \nullptr if no device
213 has been assigned.
214*/
215QIODevice *QTextDocumentWriter::device () const
216{
217 return d->device;
218}
219
220/*!
221 Sets the name of the file to be written to \a fileName. Internally,
222 QTextDocumentWriter will create a QFile and open it in \l
223 QIODevice::WriteOnly mode, and use this file when writing the document.
224
225 \sa fileName(), setDevice()
226*/
227void QTextDocumentWriter::setFileName (const QString &fileName)
228{
229 setDevice(new QFile(fileName));
230 d->deleteDevice = true;
231}
232
233/*!
234 If the currently assigned device is a QFile, or if setFileName()
235 has been called, this function returns the name of the file
236 to be written to. In all other cases, it returns an empty string.
237
238 \sa setFileName(), setDevice()
239*/
240QString QTextDocumentWriter::fileName () const
241{
242 QFile *file = qobject_cast<QFile *>(object: d->device);
243 return file ? file->fileName() : QString();
244}
245
246/*!
247 Writes the given \a document to the assigned device or file and
248 returns \c true if successful; otherwise returns \c false.
249*/
250bool QTextDocumentWriter::write(const QTextDocument *document)
251{
252 if (!d->device)
253 return false;
254
255 QByteArray suffix;
256 if (d->format.isEmpty()) {
257 // if there's no format, see if device is a file, and if so, find
258 // the file suffix
259 if (QFile *file = qobject_cast<QFile *>(object: d->device))
260 suffix = QFileInfo(file->fileName()).suffix().toLower().toLatin1();
261 }
262
263 QByteArray format = !d->format.isEmpty() ? d->format.toLower() : suffix;
264
265#ifndef QT_NO_TEXTODFWRITER
266 if (format == "odf" || format == "opendocumentformat" || format == "odt") {
267 QTextOdfWriter writer(*document, d->device);
268#if QT_CONFIG(textcodec)
269 writer.setCodec(d->codec);
270#endif
271 return writer.writeAll();
272 }
273#endif // QT_NO_TEXTODFWRITER
274
275#if QT_CONFIG(textmarkdownwriter)
276 if (format == "md" || format == "mkd" || format == "markdown") {
277 if (!d->device->isWritable() && !d->device->open(mode: QIODevice::WriteOnly)) {
278 qWarning(msg: "QTextDocumentWriter::write: the device can not be opened for writing");
279 return false;
280 }
281 QTextStream s(d->device);
282 QTextMarkdownWriter writer(s, QTextDocument::MarkdownDialectGitHub);
283 return writer.writeAll(document);
284 }
285#endif // textmarkdownwriter
286
287#ifndef QT_NO_TEXTHTMLPARSER
288 if (format == "html" || format == "htm") {
289 if (!d->device->isWritable() && ! d->device->open(mode: QIODevice::WriteOnly)) {
290 qWarning(msg: "QTextDocumentWriter::write: the device cannot be opened for writing");
291 return false;
292 }
293 QTextStream ts(d->device);
294#if QT_CONFIG(textcodec)
295 ts.setCodec(d->codec);
296 ts << document->toHtml(encoding: d->codec->name());
297#endif
298 d->device->close();
299 return true;
300 }
301#endif
302 if (format == "txt" || format == "plaintext") {
303 if (!d->device->isWritable() && ! d->device->open(mode: QIODevice::WriteOnly)) {
304 qWarning(msg: "QTextDocumentWriter::write: the device cannot be opened for writing");
305 return false;
306 }
307 QTextStream ts(d->device);
308#if QT_CONFIG(textcodec)
309 ts.setCodec(d->codec);
310#endif
311 ts << document->toPlainText();
312 d->device->close();
313 return true;
314 }
315
316 return false;
317}
318
319/*!
320 Writes the document fragment specified by \a fragment to the assigned device
321 or file and returns \c true if successful; otherwise returns \c false.
322*/
323bool QTextDocumentWriter::write(const QTextDocumentFragment &fragment)
324{
325 if (fragment.d == nullptr)
326 return false; // invalid fragment.
327 QTextDocument *doc = fragment.d->doc;
328 if (doc)
329 return write(document: doc);
330 return false;
331}
332
333/*!
334 Sets the codec for this stream to \a codec. The codec is used for
335 encoding any data that is written. By default, QTextDocumentWriter
336 uses UTF-8.
337*/
338
339#if QT_CONFIG(textcodec)
340void QTextDocumentWriter::setCodec(QTextCodec *codec)
341{
342 if (codec == nullptr)
343 codec = QTextCodec::codecForName(name: "UTF-8");
344 Q_ASSERT(codec);
345 d->codec = codec;
346}
347#endif
348
349/*!
350 Returns the codec that is currently assigned to the writer.
351*/
352#if QT_CONFIG(textcodec)
353QTextCodec *QTextDocumentWriter::codec() const
354{
355 return d->codec;
356}
357#endif
358
359/*!
360 Returns the list of document formats supported by QTextDocumentWriter.
361
362 By default, Qt can write the following formats:
363
364 \table
365 \header \li Format \li Description
366 \row \li plaintext \li Plain text
367 \row \li HTML \li HyperText Markup Language
368 \row \li markdown \li Markdown (CommonMark or GitHub dialects)
369 \row \li ODF \li OpenDocument Format
370 \endtable
371
372 \sa setFormat()
373*/
374QList<QByteArray> QTextDocumentWriter::supportedDocumentFormats()
375{
376 QList<QByteArray> answer;
377 answer << "plaintext";
378
379#ifndef QT_NO_TEXTHTMLPARSER
380 answer << "HTML";
381#endif
382#ifndef QT_NO_TEXTODFWRITER
383 answer << "ODF";
384#endif // QT_NO_TEXTODFWRITER
385#if QT_CONFIG(textmarkdownwriter)
386 answer << "markdown";
387#endif
388
389 std::sort(first: answer.begin(), last: answer.end());
390 return answer;
391}
392
393QT_END_NAMESPACE
394

source code of qtbase/src/gui/text/qtextdocumentwriter.cpp