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
60 QT_BEGIN_NAMESPACE
61
62 class QTextDocumentWriterPrivate
63 {
64 public:
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 */
QTextDocumentWriterPrivate(QTextDocumentWriter * qq)109 QTextDocumentWriterPrivate::QTextDocumentWriterPrivate(QTextDocumentWriter *qq)
110 : device(nullptr),
111 deleteDevice(false),
112 #if QT_CONFIG(textcodec)
113 codec(QTextCodec::codecForName("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 */
QTextDocumentWriter()124 QTextDocumentWriter::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 */
QTextDocumentWriter(QIODevice * device,const QByteArray & format)133 QTextDocumentWriter::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 */
QTextDocumentWriter(const QString & fileName,const QByteArray & format)146 QTextDocumentWriter::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 */
~QTextDocumentWriter()155 QTextDocumentWriter::~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 */
setFormat(const QByteArray & format)173 void 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 */
format() const183 QByteArray 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 */
setDevice(QIODevice * device)202 void 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 */
device() const215 QIODevice *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 */
setFileName(const QString & fileName)227 void 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 */
fileName() const240 QString QTextDocumentWriter::fileName () const
241 {
242 QFile *file = qobject_cast<QFile *>(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 */
write(const QTextDocument * document)250 bool QTextDocumentWriter::write(const QTextDocument *document)
251 {
252 QByteArray suffix;
253
254 if (d->device && d->format.isEmpty()) {
255 // if there's no format, see if device is a file, and if so, find
256 // the file suffix
257 if (QFile *file = qobject_cast<QFile *>(d->device))
258 suffix = QFileInfo(file->fileName()).suffix().toLower().toLatin1();
259 }
260
261 QByteArray format = !d->format.isEmpty() ? d->format.toLower() : suffix;
262
263 #ifndef QT_NO_TEXTODFWRITER
264 if (format == "odf" || format == "opendocumentformat" || format == "odt") {
265 QTextOdfWriter writer(*document, d->device);
266 #if QT_CONFIG(textcodec)
267 writer.setCodec(d->codec);
268 #endif
269 return writer.writeAll();
270 }
271 #endif // QT_NO_TEXTODFWRITER
272
273 #if QT_CONFIG(textmarkdownwriter)
274 if (format == "md" || format == "mkd" || format == "markdown") {
275 if (!d->device->isWritable() && !d->device->open(QIODevice::WriteOnly)) {
276 qWarning("QTextDocumentWriter::write: the device can not be opened for writing");
277 return false;
278 }
279 QTextStream s(d->device);
280 QTextMarkdownWriter writer(s, QTextDocument::MarkdownDialectGitHub);
281 return writer.writeAll(document);
282 }
283 #endif // textmarkdownwriter
284
285 #ifndef QT_NO_TEXTHTMLPARSER
286 if (format == "html" || format == "htm") {
287 if (!d->device->isWritable() && ! d->device->open(QIODevice::WriteOnly)) {
288 qWarning("QTextDocumentWriter::write: the device cannot be opened for writing");
289 return false;
290 }
291 QTextStream ts(d->device);
292 #if QT_CONFIG(textcodec)
293 ts.setCodec(d->codec);
294 ts << document->toHtml(d->codec->name());
295 #endif
296 d->device->close();
297 return true;
298 }
299 #endif
300 if (format == "txt" || format == "plaintext") {
301 if (!d->device->isWritable() && ! d->device->open(QIODevice::WriteOnly)) {
302 qWarning("QTextDocumentWriter::write: the device cannot be opened for writing");
303 return false;
304 }
305 QTextStream ts(d->device);
306 #if QT_CONFIG(textcodec)
307 ts.setCodec(d->codec);
308 #endif
309 ts << document->toPlainText();
310 d->device->close();
311 return true;
312 }
313
314 return false;
315 }
316
317 /*!
318 Writes the document fragment specified by \a fragment to the assigned device
319 or file and returns \c true if successful; otherwise returns \c false.
320 */
write(const QTextDocumentFragment & fragment)321 bool QTextDocumentWriter::write(const QTextDocumentFragment &fragment)
322 {
323 if (fragment.d == nullptr)
324 return false; // invalid fragment.
325 QTextDocument *doc = fragment.d->doc;
326 if (doc)
327 return write(doc);
328 return false;
329 }
330
331 /*!
332 Sets the codec for this stream to \a codec. The codec is used for
333 encoding any data that is written. By default, QTextDocumentWriter
334 uses UTF-8.
335 */
336
337 #if QT_CONFIG(textcodec)
setCodec(QTextCodec * codec)338 void QTextDocumentWriter::setCodec(QTextCodec *codec)
339 {
340 if (codec == nullptr)
341 codec = QTextCodec::codecForName("UTF-8");
342 Q_ASSERT(codec);
343 d->codec = codec;
344 }
345 #endif
346
347 /*!
348 Returns the codec that is currently assigned to the writer.
349 */
350 #if QT_CONFIG(textcodec)
codec() const351 QTextCodec *QTextDocumentWriter::codec() const
352 {
353 return d->codec;
354 }
355 #endif
356
357 /*!
358 Returns the list of document formats supported by QTextDocumentWriter.
359
360 By default, Qt can write the following formats:
361
362 \table
363 \header \li Format \li Description
364 \row \li plaintext \li Plain text
365 \row \li HTML \li HyperText Markup Language
366 \row \li markdown \li Markdown (CommonMark or GitHub dialects)
367 \row \li ODF \li OpenDocument Format
368 \endtable
369
370 \sa setFormat()
371 */
supportedDocumentFormats()372 QList<QByteArray> QTextDocumentWriter::supportedDocumentFormats()
373 {
374 QList<QByteArray> answer;
375 answer << "plaintext";
376
377 #ifndef QT_NO_TEXTHTMLPARSER
378 answer << "HTML";
379 #endif
380 #ifndef QT_NO_TEXTODFWRITER
381 answer << "ODF";
382 #endif // QT_NO_TEXTODFWRITER
383 #if QT_CONFIG(textmarkdownwriter)
384 answer << "markdown";
385 #endif
386
387 std::sort(answer.begin(), answer.end());
388 return answer;
389 }
390
391 QT_END_NAMESPACE
392