source: trunk/src/gui/text/qtextdocument.cpp@ 228

Last change on this file since 228 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 89.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qtextdocument.h"
43#include <qtextformat.h>
44#include "qtextdocumentlayout_p.h"
45#include "qtextdocumentfragment.h"
46#include "qtextdocumentfragment_p.h"
47#include "qtexttable.h"
48#include "qtextlist.h"
49#include <qdebug.h>
50#include <qregexp.h>
51#include <qvarlengtharray.h>
52#include <qtextcodec.h>
53#include <qthread.h>
54#include "qtexthtmlparser_p.h"
55#include "qpainter.h"
56#include "qprinter.h"
57#include "qtextedit.h"
58#include <qfile.h>
59#include <qfileinfo.h>
60#include <qdir.h>
61#include <qapplication.h>
62#include "qtextcontrol_p.h"
63#include "private/qtextedit_p.h"
64
65#include "qtextdocument_p.h"
66#include <private/qprinter_p.h>
67
68#include <limits.h>
69
70QT_BEGIN_NAMESPACE
71
72Q_CORE_EXPORT unsigned int qt_int_sqrt(unsigned int n);
73
74/*!
75 Returns true if the string \a text is likely to be rich text;
76 otherwise returns false.
77
78 This function uses a fast and therefore simple heuristic. It
79 mainly checks whether there is something that looks like a tag
80 before the first line break. Although the result may be correct
81 for common cases, there is no guarantee.
82
83 This function is defined in the \c <QTextDocument> header file.
84*/
85bool Qt::mightBeRichText(const QString& text)
86{
87 if (text.isEmpty())
88 return false;
89 int start = 0;
90
91 while (start < text.length() && text.at(start).isSpace())
92 ++start;
93
94 // skip a leading <?xml ... ?> as for example with xhtml
95 if (text.mid(start, 5) == QLatin1String("<?xml")) {
96 while (start < text.length()) {
97 if (text.at(start) == QLatin1Char('?')
98 && start + 2 < text.length()
99 && text.at(start + 1) == QLatin1Char('>')) {
100 start += 2;
101 break;
102 }
103 ++start;
104 }
105
106 while (start < text.length() && text.at(start).isSpace())
107 ++start;
108 }
109
110 if (text.mid(start, 5).toLower() == QLatin1String("<!doc"))
111 return true;
112 int open = start;
113 while (open < text.length() && text.at(open) != QLatin1Char('<')
114 && text.at(open) != QLatin1Char('\n')) {
115 if (text.at(open) == QLatin1Char('&') && text.mid(open+1,3) == QLatin1String("lt;"))
116 return true; // support desperate attempt of user to see <...>
117 ++open;
118 }
119 if (open < text.length() && text.at(open) == QLatin1Char('<')) {
120 const int close = text.indexOf(QLatin1Char('>'), open);
121 if (close > -1) {
122 QString tag;
123 for (int i = open+1; i < close; ++i) {
124 if (text[i].isDigit() || text[i].isLetter())
125 tag += text[i];
126 else if (!tag.isEmpty() && text[i].isSpace())
127 break;
128 else if (!text[i].isSpace() && (!tag.isEmpty() || text[i] != QLatin1Char('!')))
129 return false; // that's not a tag
130 }
131#ifndef QT_NO_TEXTHTMLPARSER
132 return QTextHtmlParser::lookupElement(tag.toLower()) != -1;
133#else
134 return false;
135#endif // QT_NO_TEXTHTMLPARSER
136 }
137 }
138 return false;
139}
140
141/*!
142 Converts the plain text string \a plain to a HTML string with
143 HTML metacharacters \c{<}, \c{>}, and \c{&} replaced by HTML
144 entities.
145
146 Example:
147
148 \snippet doc/src/snippets/code/src_gui_text_qtextdocument.cpp 0
149
150 This function is defined in the \c <QTextDocument> header file.
151
152 \sa convertFromPlainText(), mightBeRichText()
153*/
154QString Qt::escape(const QString& plain)
155{
156 QString rich;
157 rich.reserve(int(plain.length() * 1.1));
158 for (int i = 0; i < plain.length(); ++i) {
159 if (plain.at(i) == QLatin1Char('<'))
160 rich += QLatin1String("&lt;");
161 else if (plain.at(i) == QLatin1Char('>'))
162 rich += QLatin1String("&gt;");
163 else if (plain.at(i) == QLatin1Char('&'))
164 rich += QLatin1String("&amp;");
165 else
166 rich += plain.at(i);
167 }
168 return rich;
169}
170
171/*!
172 \fn QString Qt::convertFromPlainText(const QString &plain, WhiteSpaceMode mode)
173
174 Converts the plain text string \a plain to an HTML-formatted
175 paragraph while preserving most of its look.
176
177 \a mode defines how whitespace is handled.
178
179 This function is defined in the \c <QTextDocument> header file.
180
181 \sa escape(), mightBeRichText()
182*/
183QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
184{
185 int col = 0;
186 QString rich;
187 rich += QLatin1String("<p>");
188 for (int i = 0; i < plain.length(); ++i) {
189 if (plain[i] == QLatin1Char('\n')){
190 int c = 1;
191 while (i+1 < plain.length() && plain[i+1] == QLatin1Char('\n')) {
192 i++;
193 c++;
194 }
195 if (c == 1)
196 rich += QLatin1String("<br>\n");
197 else {
198 rich += QLatin1String("</p>\n");
199 while (--c > 1)
200 rich += QLatin1String("<br>\n");
201 rich += QLatin1String("<p>");
202 }
203 col = 0;
204 } else {
205 if (mode == Qt::WhiteSpacePre && plain[i] == QLatin1Char('\t')){
206 rich += QChar(0x00a0U);
207 ++col;
208 while (col % 8) {
209 rich += QChar(0x00a0U);
210 ++col;
211 }
212 }
213 else if (mode == Qt::WhiteSpacePre && plain[i].isSpace())
214 rich += QChar(0x00a0U);
215 else if (plain[i] == QLatin1Char('<'))
216 rich += QLatin1String("&lt;");
217 else if (plain[i] == QLatin1Char('>'))
218 rich += QLatin1String("&gt;");
219 else if (plain[i] == QLatin1Char('&'))
220 rich += QLatin1String("&amp;");
221 else
222 rich += plain[i];
223 ++col;
224 }
225 }
226 if (col != 0)
227 rich += QLatin1String("</p>");
228 return rich;
229}
230
231#ifndef QT_NO_TEXTCODEC
232/*!
233 \internal
234
235 This function is defined in the \c <QTextDocument> header file.
236*/
237QTextCodec *Qt::codecForHtml(const QByteArray &ba)
238{
239 return QTextCodec::codecForHtml(ba);
240}
241#endif
242
243/*!
244 \class QTextDocument
245 \reentrant
246
247 \brief The QTextDocument class holds formatted text that can be
248 viewed and edited using a QTextEdit.
249
250 \ingroup text
251 \mainclass
252
253 QTextDocument is a container for structured rich text documents, providing
254 support for styled text and various types of document elements, such as
255 lists, tables, frames, and images.
256 They can be created for use in a QTextEdit, or used independently.
257
258 Each document element is described by an associated format object. Each
259 format object is treated as a unique object by QTextDocuments, and can be
260 passed to objectForFormat() to obtain the document element that it is
261 applied to.
262
263 A QTextDocument can be edited programmatically using a QTextCursor, and
264 its contents can be examined by traversing the document structure. The
265 entire document structure is stored as a hierarchy of document elements
266 beneath the root frame, found with the rootFrame() function. Alternatively,
267 if you just want to iterate over the textual contents of the document you
268 can use begin(), end(), and findBlock() to retrieve text blocks that you
269 can examine and iterate over.
270
271 The layout of a document is determined by the documentLayout();
272 you can create your own QAbstractTextDocumentLayout subclass and
273 set it using setDocumentLayout() if you want to use your own
274 layout logic. The document's title and other meta-information can be
275 obtained by calling the metaInformation() function. For documents that
276 are exposed to users through the QTextEdit class, the document title
277 is also available via the QTextEdit::documentTitle() function.
278
279 The toPlainText() and toHtml() convenience functions allow you to retrieve the
280 contents of the document as plain text and HTML.
281 The document's text can be searched using the find() functions.
282
283 Undo/redo of operations performed on the document can be controlled using
284 the setUndoRedoEnabled() function. The undo/redo system can be controlled
285 by an editor widget through the undo() and redo() slots; the document also
286 provides contentsChanged(), undoAvailable(), and redoAvailable() signals
287 that inform connected editor widgets about the state of the undo/redo
288 system.
289
290 \sa QTextCursor QTextEdit \link richtext.html Rich Text Processing\endlink
291*/
292
293/*!
294 \property QTextDocument::defaultFont
295 \brief the default font used to display the document's text
296*/
297
298/*!
299 \property QTextDocument::defaultTextOption
300 \brief the default text option will be set on all \l{QTextLayout}s in the document.
301
302 When \l{QTextBlock}s are created, the defaultTextOption is set on their
303 QTextLayout. This allows setting global properties for the document such as the
304 default word wrap mode.
305 */
306
307/*!
308 Constructs an empty QTextDocument with the given \a parent.
309*/
310QTextDocument::QTextDocument(QObject *parent)
311 : QObject(*new QTextDocumentPrivate, parent)
312{
313 Q_D(QTextDocument);
314 d->init();
315}
316
317/*!
318 Constructs a QTextDocument containing the plain (unformatted) \a text
319 specified, and with the given \a parent.
320*/
321QTextDocument::QTextDocument(const QString &text, QObject *parent)
322 : QObject(*new QTextDocumentPrivate, parent)
323{
324 Q_D(QTextDocument);
325 d->init();
326 QTextCursor(this).insertText(text);
327}
328
329/*!
330 \internal
331*/
332QTextDocument::QTextDocument(QTextDocumentPrivate &dd, QObject *parent)
333 : QObject(dd, parent)
334{
335 Q_D(QTextDocument);
336 d->init();
337}
338
339/*!
340 Destroys the document.
341*/
342QTextDocument::~QTextDocument()
343{
344}
345
346
347/*!
348 Creates a new QTextDocument that is a copy of this text document. \a
349 parent is the parent of the returned text document.
350*/
351QTextDocument *QTextDocument::clone(QObject *parent) const
352{
353 Q_D(const QTextDocument);
354 QTextDocument *doc = new QTextDocument(parent);
355 QTextCursor(doc).insertFragment(QTextDocumentFragment(this));
356 doc->rootFrame()->setFrameFormat(rootFrame()->frameFormat());
357 QTextDocumentPrivate *priv = doc->d_func();
358 priv->title = d->title;
359 priv->url = d->url;
360 priv->pageSize = d->pageSize;
361 priv->indentWidth = d->indentWidth;
362 priv->defaultTextOption = d->defaultTextOption;
363 priv->setDefaultFont(d->defaultFont());
364 priv->resources = d->resources;
365 priv->cachedResources.clear();
366#ifndef QT_NO_CSSPARSER
367 priv->defaultStyleSheet = d->defaultStyleSheet;
368 priv->parsedDefaultStyleSheet = d->parsedDefaultStyleSheet;
369#endif
370 return doc;
371}
372
373/*!
374 Returns true if the document is empty; otherwise returns false.
375*/
376bool QTextDocument::isEmpty() const
377{
378 Q_D(const QTextDocument);
379 /* because if we're empty we still have one single paragraph as
380 * one single fragment */
381 return d->length() <= 1;
382}
383
384/*!
385 Clears the document.
386*/
387void QTextDocument::clear()
388{
389 Q_D(QTextDocument);
390 d->clear();
391 d->resources.clear();
392}
393
394/*!
395 \since 4.2
396
397 Undoes the last editing operation on the document if undo is
398 available. The provided \a cursor is positioned at the end of the
399 location where the edition operation was undone.
400
401 See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
402 documentation for details.
403
404 \sa undoAvailable(), isUndoRedoEnabled()
405*/
406void QTextDocument::undo(QTextCursor *cursor)
407{
408 Q_D(QTextDocument);
409 const int pos = d->undoRedo(true);
410 if (cursor && pos >= 0) {
411 *cursor = QTextCursor(this);
412 cursor->setPosition(pos);
413 }
414}
415
416/*!
417 \since 4.2
418 Redoes the last editing operation on the document if \link
419 QTextDocument::isRedoAvailable() redo is available\endlink.
420
421 The provided \a cursor is positioned at the end of the location where
422 the edition operation was redone.
423*/
424void QTextDocument::redo(QTextCursor *cursor)
425{
426 Q_D(QTextDocument);
427 const int pos = d->undoRedo(false);
428 if (cursor && pos >= 0) {
429 *cursor = QTextCursor(this);
430 cursor->setPosition(pos);
431 }
432}
433
434/*!
435 \overload
436
437*/
438void QTextDocument::undo()
439{
440 Q_D(QTextDocument);
441 d->undoRedo(true);
442}
443
444/*!
445 \overload
446 Redoes the last editing operation on the document if \link
447 QTextDocument::isRedoAvailable() redo is available\endlink.
448*/
449void QTextDocument::redo()
450{
451 Q_D(QTextDocument);
452 d->undoRedo(false);
453}
454
455/*!
456 \internal
457
458 Appends a custom undo \a item to the undo stack.
459*/
460void QTextDocument::appendUndoItem(QAbstractUndoItem *item)
461{
462 Q_D(QTextDocument);
463 d->appendUndoItem(item);
464}
465
466/*!
467 \property QTextDocument::undoRedoEnabled
468 \brief whether undo/redo are enabled for this document
469
470 This defaults to true. If disabled, the undo stack is cleared and
471 no items will be added to it.
472*/
473void QTextDocument::setUndoRedoEnabled(bool enable)
474{
475 Q_D(QTextDocument);
476 d->enableUndoRedo(enable);
477}
478
479bool QTextDocument::isUndoRedoEnabled() const
480{
481 Q_D(const QTextDocument);
482 return d->isUndoRedoEnabled();
483}
484
485/*!
486 \property QTextDocument::maximumBlockCount
487 \since 4.2
488 \brief Specifies the limit for blocks in the document.
489
490 Specifies the maximum number of blocks the document may have. If there are
491 more blocks in the document that specified with this property blocks are removed
492 from the beginning of the document.
493
494 A negative or zero value specifies that the document may contain an unlimited
495 amount of blocks.
496
497 The default value is 0.
498
499 Note that setting this property will apply the limit immediately to the document
500 contents.
501
502 Setting this property also disables the undo redo history.
503
504 This property is undefined in documents with tables or frames.
505*/
506int QTextDocument::maximumBlockCount() const
507{
508 Q_D(const QTextDocument);
509 return d->maximumBlockCount;
510}
511
512void QTextDocument::setMaximumBlockCount(int maximum)
513{
514 Q_D(QTextDocument);
515 d->maximumBlockCount = maximum;
516 d->ensureMaximumBlockCount();
517 setUndoRedoEnabled(false);
518}
519
520/*!
521 \since 4.3
522
523 The default text option is used on all QTextLayout objects in the document.
524 This allows setting global properties for the document such as the default
525 word wrap mode.
526*/
527QTextOption QTextDocument::defaultTextOption() const
528{
529 Q_D(const QTextDocument);
530 return d->defaultTextOption;
531}
532
533/*!
534 \since 4.3
535
536 Sets the default text option.
537*/
538void QTextDocument::setDefaultTextOption(const QTextOption &option)
539{
540 Q_D(QTextDocument);
541 d->defaultTextOption = option;
542 if (d->lout)
543 d->lout->documentChanged(0, 0, d->length());
544}
545
546/*!
547 \fn void QTextDocument::markContentsDirty(int position, int length)
548
549 Marks the contents specified by the given \a position and \a length
550 as "dirty", informing the document that it needs to be laid out
551 again.
552*/
553void QTextDocument::markContentsDirty(int from, int length)
554{
555 Q_D(QTextDocument);
556 if (!d->inContentsChange)
557 d->beginEditBlock();
558 d->documentChange(from, length);
559 if (!d->inContentsChange)
560 d->endEditBlock();
561}
562
563/*!
564 \property QTextDocument::useDesignMetrics
565 \since 4.1
566 \brief whether the document uses design metrics of fonts to improve the accuracy of text layout
567
568 If this property is set to true, the layout will use design metrics.
569 Otherwise, the metrics of the paint device as set on
570 QAbstractTextDocumentLayout::setPaintDevice() will be used.
571
572 Using design metrics makes a layout have a width that is no longer dependent on hinting
573 and pixel-rounding. This means that WYSIWYG text layout becomes possible because the width
574 scales much more linearly based on paintdevice metrics than it would otherwise.
575
576 By default, this property is false.
577*/
578
579void QTextDocument::setUseDesignMetrics(bool b)
580{
581 Q_D(QTextDocument);
582 if (b == d->defaultTextOption.useDesignMetrics())
583 return;
584 d->defaultTextOption.setUseDesignMetrics(b);
585 if (d->lout)
586 d->lout->documentChanged(0, 0, d->length());
587}
588
589bool QTextDocument::useDesignMetrics() const
590{
591 Q_D(const QTextDocument);
592 return d->defaultTextOption.useDesignMetrics();
593}
594
595/*!
596 \since 4.2
597
598 Draws the content of the document with painter \a p, clipped to \a rect.
599 If \a rect is a null rectangle (default) then the document is painted unclipped.
600*/
601void QTextDocument::drawContents(QPainter *p, const QRectF &rect)
602{
603 p->save();
604 QAbstractTextDocumentLayout::PaintContext ctx;
605 if (rect.isValid()) {
606 p->setClipRect(rect);
607 ctx.clip = rect;
608 }
609 documentLayout()->draw(p, ctx);
610 p->restore();
611}
612
613/*!
614 \property QTextDocument::textWidth
615 \since 4.2
616
617 The text width specifies the preferred width for text in the document. If
618 the text (or content in general) is wider than the specified with it is broken
619 into multiple lines and grows vertically. If the text cannot be broken into multiple
620 lines to fit into the specified text width it will be larger and the size() and the
621 idealWidth() property will reflect that.
622
623 If the text width is set to -1 then the text will not be broken into multiple lines
624 unless it is enforced through an explicit line break or a new paragraph.
625
626 The default value is -1.
627
628 Setting the text width will also set the page height to -1, causing the document to
629 grow or shrink vertically in a continuous way. If you want the document layout to break
630 the text into multiple pages then you have to set the pageSize property instead.
631
632 \sa size(), idealWidth(), pageSize()
633*/
634void QTextDocument::setTextWidth(qreal width)
635{
636 Q_D(QTextDocument);
637 QSizeF sz = d->pageSize;
638 sz.setWidth(width);
639 sz.setHeight(-1);
640 setPageSize(sz);
641}
642
643qreal QTextDocument::textWidth() const
644{
645 Q_D(const QTextDocument);
646 return d->pageSize.width();
647}
648
649/*!
650 \since 4.2
651
652 Returns the ideal width of the text document. The ideal width is the actually used width
653 of the document without optional alignments taken into account. It is always <= size().width().
654
655 \sa adjustSize(), textWidth
656*/
657qreal QTextDocument::idealWidth() const
658{
659 if (QTextDocumentLayout *lout = qobject_cast<QTextDocumentLayout *>(documentLayout()))
660 return lout->idealWidth();
661 return textWidth();
662}
663
664/*!
665 \property QTextDocument::documentMargin
666 \since 4.5
667
668 The margin around the document. The default is 4.
669*/
670qreal QTextDocument::documentMargin() const
671{
672 Q_D(const QTextDocument);
673 return d->documentMargin;
674}
675
676void QTextDocument::setDocumentMargin(qreal margin)
677{
678 Q_D(QTextDocument);
679 if (d->documentMargin != margin) {
680 d->documentMargin = margin;
681
682 QTextFrame* root = rootFrame();
683 QTextFrameFormat format = root->frameFormat();
684 format.setMargin(margin);
685 root->setFrameFormat(format);
686
687 if (d->lout)
688 d->lout->documentChanged(0, 0, d->length());
689 }
690}
691
692
693/*!
694 \property QTextDocument::indentWidth
695 \since 4.4
696
697 Returns the width used for text list and text block indenting.
698
699 The indent properties of QTextListFormat and QTextBlockFormat specify
700 multiples of this value. The default indent width is 40.
701*/
702qreal QTextDocument::indentWidth() const
703{
704 Q_D(const QTextDocument);
705 return d->indentWidth;
706}
707
708
709/*!
710 \since 4.4
711
712 Sets the \a width used for text list and text block indenting.
713
714 The indent properties of QTextListFormat and QTextBlockFormat specify
715 multiples of this value. The default indent width is 40 .
716
717 \sa indentWidth()
718*/
719void QTextDocument::setIndentWidth(qreal width)
720{
721 Q_D(QTextDocument);
722 if (d->indentWidth != width) {
723 d->indentWidth = width;
724 if (d->lout)
725 d->lout->documentChanged(0, 0, d->length());
726 }
727}
728
729
730
731
732/*!
733 \since 4.2
734
735 Adjusts the document to a reasonable size.
736
737 \sa idealWidth(), textWidth, size
738*/
739void QTextDocument::adjustSize()
740{
741 // Pull this private function in from qglobal.cpp
742 QFont f = defaultFont();
743 QFontMetrics fm(f);
744 int mw = fm.width(QLatin1Char('x')) * 80;
745 int w = mw;
746 setTextWidth(w);
747 QSizeF size = documentLayout()->documentSize();
748 if (size.width() != 0) {
749 w = qt_int_sqrt((uint)(5 * size.height() * size.width() / 3));
750 setTextWidth(qMin(w, mw));
751
752 size = documentLayout()->documentSize();
753 if (w*3 < 5*size.height()) {
754 w = qt_int_sqrt((uint)(2 * size.height() * size.width()));
755 setTextWidth(qMin(w, mw));
756 }
757 }
758 setTextWidth(idealWidth());
759}
760
761/*!
762 \property QTextDocument::size
763 \since 4.2
764
765 Returns the actual size of the document.
766 This is equivalent to documentLayout()->documentSize();
767
768 The size of the document can be changed either by setting
769 a text width or setting an entire page size.
770
771 Note that the width is always >= pageSize().width().
772
773 By default, for a newly-created, empty document, this property contains
774 a configuration-dependent size.
775
776 \sa setTextWidth(), setPageSize(), idealWidth()
777*/
778QSizeF QTextDocument::size() const
779{
780 return documentLayout()->documentSize();
781}
782
783/*!
784 \property QTextDocument::blockCount
785 \since 4.2
786
787 Returns the number of text blocks in the document.
788
789 The value of this property is undefined in documents with tables or frames.
790
791 By default, if defined, this property contains a value of 1.
792 \sa lineCount(), characterCount()
793*/
794int QTextDocument::blockCount() const
795{
796 Q_D(const QTextDocument);
797 return d->blockMap().numNodes();
798}
799
800
801/*!
802 \since 4.5
803
804 Returns the number of lines of this document (if the layout supports
805 this). Otherwise, this is identical to the number of blocks.
806
807 \sa blockCount(), characterCount()
808 */
809int QTextDocument::lineCount() const
810{
811 Q_D(const QTextDocument);
812 return d->blockMap().length(2);
813}
814
815/*!
816 \since 4.5
817
818 Returns the number of characters of this document.
819
820 \sa blockCount(), characterAt()
821 */
822int QTextDocument::characterCount() const
823{
824 Q_D(const QTextDocument);
825 return d->length();
826}
827
828/*!
829 \since 4.5
830
831 Returns the character at position \a pos, or a null character if the
832 position is out of range.
833
834 \sa characterCount()
835 */
836QChar QTextDocument::characterAt(int pos) const
837{
838 Q_D(const QTextDocument);
839 if (pos < 0 || pos >= d->length())
840 return QChar();
841 QTextDocumentPrivate::FragmentIterator fragIt = d->find(pos);
842 const QTextFragmentData * const frag = fragIt.value();
843 const int offsetInFragment = qMax(0, pos - fragIt.position());
844 return d->text.at(frag->stringPosition + offsetInFragment);
845}
846
847
848/*!
849 \property QTextDocument::defaultStyleSheet
850 \since 4.2
851
852 The default style sheet is applied to all newly HTML formatted text that is
853 inserted into the document, for example using setHtml() or QTextCursor::insertHtml().
854
855 The style sheet needs to be compliant to CSS 2.1 syntax.
856
857 \bold{Note:} Changing the default style sheet does not have any effect to the existing content
858 of the document.
859
860 \sa {Supported HTML Subset}
861*/
862
863#ifndef QT_NO_CSSPARSER
864void QTextDocument::setDefaultStyleSheet(const QString &sheet)
865{
866 Q_D(QTextDocument);
867 d->defaultStyleSheet = sheet;
868 QCss::Parser parser(sheet);
869 d->parsedDefaultStyleSheet = QCss::StyleSheet();
870 d->parsedDefaultStyleSheet.origin = QCss::StyleSheetOrigin_UserAgent;
871 parser.parse(&d->parsedDefaultStyleSheet);
872}
873
874QString QTextDocument::defaultStyleSheet() const
875{
876 Q_D(const QTextDocument);
877 return d->defaultStyleSheet;
878}
879#endif // QT_NO_CSSPARSER
880
881/*!
882 \fn void QTextDocument::contentsChanged()
883
884 This signal is emitted whenever the document's content changes; for
885 example, when text is inserted or deleted, or when formatting is applied.
886
887 \sa contentsChange()
888*/
889
890/*!
891 \fn void QTextDocument::contentsChange(int position, int charsRemoved, int charsAdded)
892
893 This signal is emitted whenever the document's content changes; for
894 example, when text is inserted or deleted, or when formatting is applied.
895
896 Information is provided about the \a position of the character in the
897 document where the change occurred, the number of characters removed
898 (\a charsRemoved), and the number of characters added (\a charsAdded).
899
900 The signal is emitted before the document's layout manager is notified
901 about the change. This hook allows you to implement syntax highlighting
902 for the document.
903
904 \sa QAbstractTextDocumentLayout::documentChanged(), contentsChanged()
905*/
906
907
908/*!
909 \fn QTextDocument::undoAvailable(bool available);
910
911 This signal is emitted whenever undo operations become available
912 (\a available is true) or unavailable (\a available is false).
913
914 See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
915 documentation for details.
916
917 \sa undo(), isUndoRedoEnabled()
918*/
919
920/*!
921 \fn QTextDocument::redoAvailable(bool available);
922
923 This signal is emitted whenever redo operations become available
924 (\a available is true) or unavailable (\a available is false).
925*/
926
927/*!
928 \fn QTextDocument::cursorPositionChanged(const QTextCursor &cursor);
929
930 This signal is emitted whenever the position of a cursor changed
931 due to an editing operation. The cursor that changed is passed in
932 \a cursor. If you need a signal when the cursor is moved with the
933 arrow keys you can use the \l{QTextEdit::}{cursorPositionChanged()} signal in
934 QTextEdit.
935*/
936
937/*!
938 \fn QTextDocument::blockCountChanged(int newBlockCount);
939 \since 4.3
940
941 This signal is emitted when the total number of text blocks in the
942 document changes. The value passed in \a newBlockCount is the new
943 total.
944*/
945
946/*!
947 \fn QTextDocument::documentLayoutChanged();
948 \since 4.4
949
950 This signal is emitted when a new document layout is set.
951
952 \sa setDocumentLayout()
953
954*/
955
956
957/*!
958 Returns true if undo is available; otherwise returns false.
959*/
960bool QTextDocument::isUndoAvailable() const
961{
962 Q_D(const QTextDocument);
963 return d->isUndoAvailable();
964}
965
966/*!
967 Returns true if redo is available; otherwise returns false.
968*/
969bool QTextDocument::isRedoAvailable() const
970{
971 Q_D(const QTextDocument);
972 return d->isRedoAvailable();
973}
974
975
976/*! \since 4.4
977
978 Returns the document's revision (if undo is enabled).
979
980 The revision is guaranteed to increase when a document that is not
981 modified is edited.
982
983 \sa QTextBlock::revision(), isModified()
984 */
985int QTextDocument::revision() const
986{
987 Q_D(const QTextDocument);
988 return d->undoState;
989}
990
991
992
993/*!
994 Sets the document to use the given \a layout. The previous layout
995 is deleted.
996
997 \sa documentLayoutChanged()
998*/
999void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *layout)
1000{
1001 Q_D(QTextDocument);
1002 d->setLayout(layout);
1003}
1004
1005/*!
1006 Returns the document layout for this document.
1007*/
1008QAbstractTextDocumentLayout *QTextDocument::documentLayout() const
1009{
1010 Q_D(const QTextDocument);
1011 if (!d->lout) {
1012 QTextDocument *that = const_cast<QTextDocument *>(this);
1013 that->d_func()->setLayout(new QTextDocumentLayout(that));
1014 }
1015 return d->lout;
1016}
1017
1018
1019/*!
1020 Returns meta information about the document of the type specified by
1021 \a info.
1022
1023 \sa setMetaInformation()
1024*/
1025QString QTextDocument::metaInformation(MetaInformation info) const
1026{
1027 Q_D(const QTextDocument);
1028 switch (info) {
1029 case DocumentTitle:
1030 return d->title;
1031 case DocumentUrl:
1032 return d->url;
1033 }
1034 return QString();
1035}
1036
1037/*!
1038 Sets the document's meta information of the type specified by \a info
1039 to the given \a string.
1040
1041 \sa metaInformation()
1042*/
1043void QTextDocument::setMetaInformation(MetaInformation info, const QString &string)
1044{
1045 Q_D(QTextDocument);
1046 switch (info) {
1047 case DocumentTitle:
1048 d->title = string;
1049 break;
1050 case DocumentUrl:
1051 d->url = string;
1052 break;
1053 }
1054}
1055
1056/*!
1057 Returns the plain text contained in the document. If you want
1058 formatting information use a QTextCursor instead.
1059
1060 \sa toHtml()
1061*/
1062QString QTextDocument::toPlainText() const
1063{
1064 Q_D(const QTextDocument);
1065 QString txt = d->plainText();
1066
1067 QChar *uc = txt.data();
1068 QChar *e = uc + txt.size();
1069
1070 for (; uc != e; ++uc) {
1071 switch (uc->unicode()) {
1072 case 0xfdd0: // QTextBeginningOfFrame
1073 case 0xfdd1: // QTextEndOfFrame
1074 case QChar::ParagraphSeparator:
1075 case QChar::LineSeparator:
1076 *uc = QLatin1Char('\n');
1077 break;
1078 case QChar::Nbsp:
1079 *uc = QLatin1Char(' ');
1080 break;
1081 default:
1082 ;
1083 }
1084 }
1085 return txt;
1086}
1087
1088/*!
1089 Replaces the entire contents of the document with the given plain
1090 \a text.
1091
1092 \sa setHtml()
1093*/
1094void QTextDocument::setPlainText(const QString &text)
1095{
1096 Q_D(QTextDocument);
1097 bool previousState = d->isUndoRedoEnabled();
1098 d->enableUndoRedo(false);
1099 d->clear();
1100 QTextCursor(this).insertText(text);
1101 d->enableUndoRedo(previousState);
1102}
1103
1104/*!
1105 Replaces the entire contents of the document with the given
1106 HTML-formatted text in the \a html string.
1107
1108 The HTML formatting is respected as much as possible; for example,
1109 "<b>bold</b> text" will produce text where the first word has a font
1110 weight that gives it a bold appearance: "\bold{bold} text".
1111
1112 \note It is the responsibility of the caller to make sure that the
1113 text is correctly decoded when a QString containing HTML is created
1114 and passed to setHtml().
1115
1116 \sa setPlainText(), {Supported HTML Subset}
1117*/
1118
1119#ifndef QT_NO_TEXTHTMLPARSER
1120
1121void QTextDocument::setHtml(const QString &html)
1122{
1123 Q_D(QTextDocument);
1124 bool previousState = d->isUndoRedoEnabled();
1125 d->enableUndoRedo(false);
1126 d->clear();
1127 QTextHtmlImporter(this, html, QTextHtmlImporter::ImportToDocument).import();
1128 d->enableUndoRedo(previousState);
1129}
1130
1131#endif // QT_NO_TEXTHTMLPARSER
1132
1133/*!
1134 \enum QTextDocument::FindFlag
1135
1136 This enum describes the options available to QTextDocument's find function. The options
1137 can be OR-ed together from the following list:
1138
1139 \value FindBackward Search backwards instead of forwards.
1140 \value FindCaseSensitively By default find works case insensitive. Specifying this option
1141 changes the behaviour to a case sensitive find operation.
1142 \value FindWholeWords Makes find match only complete words.
1143*/
1144
1145/*!
1146 \enum QTextDocument::MetaInformation
1147
1148 This enum describes the different types of meta information that can be
1149 added to a document.
1150
1151 \value DocumentTitle The title of the document.
1152 \value DocumentUrl The url of the document. The loadResource() function uses
1153 this url as the base when loading relative resources.
1154
1155 \sa metaInformation(), setMetaInformation()
1156*/
1157
1158/*!
1159 \fn QTextCursor QTextDocument::find(const QString &subString, int position, FindFlags options) const
1160
1161 \overload
1162
1163 Finds the next occurrence of the string, \a subString, in the document.
1164 The search starts at the given \a position, and proceeds forwards
1165 through the document unless specified otherwise in the search options.
1166 The \a options control the type of search performed.
1167
1168 Returns a cursor with the match selected if \a subString
1169 was found; otherwise returns a null cursor.
1170
1171 If the \a position is 0 (the default) the search begins from the beginning
1172 of the document; otherwise it begins at the specified position.
1173*/
1174QTextCursor QTextDocument::find(const QString &subString, int from, FindFlags options) const
1175{
1176 QRegExp expr(subString);
1177 expr.setPatternSyntax(QRegExp::FixedString);
1178 expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
1179
1180 return find(expr, from, options);
1181}
1182
1183/*!
1184 \fn QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &cursor, FindFlags options) const
1185
1186 Finds the next occurrence of the string, \a subString, in the document.
1187 The search starts at the position of the given \a cursor, and proceeds
1188 forwards through the document unless specified otherwise in the search
1189 options. The \a options control the type of search performed.
1190
1191 Returns a cursor with the match selected if \a subString was found; otherwise
1192 returns a null cursor.
1193
1194 If the given \a cursor has a selection, the search begins after the
1195 selection; otherwise it begins at the cursor's position.
1196
1197 By default the search is case-sensitive, and can match text anywhere in the
1198 document.
1199*/
1200QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &from, FindFlags options) const
1201{
1202 int pos = 0;
1203 if (!from.isNull()) {
1204 if (options & QTextDocument::FindBackward)
1205 pos = from.selectionStart();
1206 else
1207 pos = from.selectionEnd();
1208 }
1209 QRegExp expr(subString);
1210 expr.setPatternSyntax(QRegExp::FixedString);
1211 expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
1212
1213 return find(expr, pos, options);
1214}
1215
1216
1217static bool findInBlock(const QTextBlock &block, const QRegExp &expression, int offset,
1218 QTextDocument::FindFlags options, QTextCursor &cursor)
1219{
1220 const QRegExp expr(expression);
1221 QString text = block.text();
1222 text.replace(QChar::Nbsp, QLatin1Char(' '));
1223
1224 int idx = -1;
1225 while (offset >=0 && offset <= text.length()) {
1226 idx = (options & QTextDocument::FindBackward) ?
1227 expr.lastIndexIn(text, offset) : expr.indexIn(text, offset);
1228 if (idx == -1)
1229 return false;
1230
1231 if (options & QTextDocument::FindWholeWords) {
1232 const int start = idx;
1233 const int end = start + expr.matchedLength();
1234 if ((start != 0 && text.at(start - 1).isLetterOrNumber())
1235 || (end != text.length() && text.at(end).isLetterOrNumber())) {
1236 //if this is not a whole word, continue the search in the string
1237 offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
1238 idx = -1;
1239 continue;
1240 }
1241 }
1242 //we have a hit, return the cursor for that.
1243 break;
1244 }
1245 if (idx == -1)
1246 return false;
1247 cursor = QTextCursor(block.docHandle(), block.position() + idx);
1248 cursor.setPosition(cursor.position() + expr.matchedLength(), QTextCursor::KeepAnchor);
1249 return true;
1250}
1251
1252/*!
1253 \fn QTextCursor QTextDocument::find(const QRegExp & expr, int position, FindFlags options) const
1254
1255 \overload
1256
1257 Finds the next occurrence, matching the regular expression, \a expr, in the document.
1258 The search starts at the given \a position, and proceeds forwards
1259 through the document unless specified otherwise in the search options.
1260 The \a options control the type of search performed. The FindCaseSensitively
1261 option is ignored for this overload, use QRegExp::caseSensitivity instead.
1262
1263 Returns a cursor with the match selected if a match was found; otherwise
1264 returns a null cursor.
1265
1266 If the \a position is 0 (the default) the search begins from the beginning
1267 of the document; otherwise it begins at the specified position.
1268*/
1269QTextCursor QTextDocument::find(const QRegExp & expr, int from, FindFlags options) const
1270{
1271 Q_D(const QTextDocument);
1272
1273 if (expr.isEmpty())
1274 return QTextCursor();
1275
1276 int pos = from;
1277 //the cursor is positioned between characters, so for a backward search
1278 //do not include the character given in the position.
1279 if (options & FindBackward) {
1280 --pos ;
1281 if(pos < 0)
1282 return QTextCursor();
1283 }
1284
1285 QTextCursor cursor;
1286 QTextBlock block = d->blocksFind(pos);
1287
1288 if (!(options & FindBackward)) {
1289 int blockOffset = qMax(0, pos - block.position());
1290 while (block.isValid()) {
1291 if (findInBlock(block, expr, blockOffset, options, cursor))
1292 return cursor;
1293 blockOffset = 0;
1294 block = block.next();
1295 }
1296 } else {
1297 int blockOffset = pos - block.position();
1298 while (block.isValid()) {
1299 if (findInBlock(block, expr, blockOffset, options, cursor))
1300 return cursor;
1301 block = block.previous();
1302 blockOffset = block.length() - 1;
1303 }
1304 }
1305
1306 return QTextCursor();
1307}
1308
1309/*!
1310 \fn QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &cursor, FindFlags options) const
1311
1312 Finds the next occurrence, matching the regular expression, \a expr, in the document.
1313 The search starts at the position of the given \a cursor, and proceeds
1314 forwards through the document unless specified otherwise in the search
1315 options. The \a options control the type of search performed. The FindCaseSensitively
1316 option is ignored for this overload, use QRegExp::caseSensitivity instead.
1317
1318 Returns a cursor with the match selected if a match was found; otherwise
1319 returns a null cursor.
1320
1321 If the given \a cursor has a selection, the search begins after the
1322 selection; otherwise it begins at the cursor's position.
1323
1324 By default the search is case-sensitive, and can match text anywhere in the
1325 document.
1326*/
1327QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &from, FindFlags options) const
1328{
1329 int pos = 0;
1330 if (!from.isNull()) {
1331 if (options & QTextDocument::FindBackward)
1332 pos = from.selectionStart();
1333 else
1334 pos = from.selectionEnd();
1335 }
1336 return find(expr, pos, options);
1337}
1338
1339
1340/*!
1341 \fn QTextObject *QTextDocument::createObject(const QTextFormat &format)
1342
1343 Creates and returns a new document object (a QTextObject), based
1344 on the given \a format.
1345
1346 QTextObjects will always get created through this method, so you
1347 must reimplement it if you use custom text objects inside your document.
1348*/
1349QTextObject *QTextDocument::createObject(const QTextFormat &f)
1350{
1351 QTextObject *obj = 0;
1352 if (f.isListFormat())
1353 obj = new QTextList(this);
1354 else if (f.isTableFormat())
1355 obj = new QTextTable(this);
1356 else if (f.isFrameFormat())
1357 obj = new QTextFrame(this);
1358
1359 return obj;
1360}
1361
1362/*!
1363 \internal
1364
1365 Returns the frame that contains the text cursor position \a pos.
1366*/
1367QTextFrame *QTextDocument::frameAt(int pos) const
1368{
1369 Q_D(const QTextDocument);
1370 return d->frameAt(pos);
1371}
1372
1373/*!
1374 Returns the document's root frame.
1375*/
1376QTextFrame *QTextDocument::rootFrame() const
1377{
1378 Q_D(const QTextDocument);
1379 return d->rootFrame();
1380}
1381
1382/*!
1383 Returns the text object associated with the given \a objectIndex.
1384*/
1385QTextObject *QTextDocument::object(int objectIndex) const
1386{
1387 Q_D(const QTextDocument);
1388 return d->objectForIndex(objectIndex);
1389}
1390
1391/*!
1392 Returns the text object associated with the format \a f.
1393*/
1394QTextObject *QTextDocument::objectForFormat(const QTextFormat &f) const
1395{
1396 Q_D(const QTextDocument);
1397 return d->objectForFormat(f);
1398}
1399
1400
1401/*!
1402 Returns the text block that contains the \a{pos}-th character.
1403*/
1404QTextBlock QTextDocument::findBlock(int pos) const
1405{
1406 Q_D(const QTextDocument);
1407 return QTextBlock(docHandle(), d->blockMap().findNode(pos));
1408}
1409
1410/*!
1411 \since 4.4
1412 Returns the text block with the specified \a blockNumber.
1413
1414 \sa QTextBlock::blockNumber()
1415*/
1416QTextBlock QTextDocument::findBlockByNumber(int blockNumber) const
1417{
1418 Q_D(const QTextDocument);
1419 return QTextBlock(docHandle(), d->blockMap().findNode(blockNumber, 1));
1420}
1421
1422/*!
1423 \since 4.5
1424 Returns the text block that contains the specified \a lineNumber.
1425
1426 \sa QTextBlock::firstLineNumber()
1427*/
1428QTextBlock QTextDocument::findBlockByLineNumber(int lineNumber) const
1429{
1430 Q_D(const QTextDocument);
1431 return QTextBlock(docHandle(), d->blockMap().findNode(lineNumber, 2));
1432}
1433
1434/*!
1435 Returns the document's first text block.
1436
1437 \sa firstBlock()
1438*/
1439QTextBlock QTextDocument::begin() const
1440{
1441 Q_D(const QTextDocument);
1442 return QTextBlock(docHandle(), d->blockMap().begin().n);
1443}
1444
1445/*!
1446 This function returns a block to test for the end of the document
1447 while iterating over it.
1448
1449 \snippet doc/src/snippets/textdocumentendsnippet.cpp 0
1450
1451 The block returned is invalid and represents the block after the
1452 last block in the document. You can use lastBlock() to retrieve the
1453 last valid block of the document.
1454
1455 \sa lastBlock()
1456*/
1457QTextBlock QTextDocument::end() const
1458{
1459 return QTextBlock(docHandle(), 0);
1460}
1461
1462/*!
1463 \since 4.4
1464 Returns the document's first text block.
1465*/
1466QTextBlock QTextDocument::firstBlock() const
1467{
1468 Q_D(const QTextDocument);
1469 return QTextBlock(docHandle(), d->blockMap().begin().n);
1470}
1471
1472/*!
1473 \since 4.4
1474 Returns the document's last (valid) text block.
1475*/
1476QTextBlock QTextDocument::lastBlock() const
1477{
1478 Q_D(const QTextDocument);
1479 return QTextBlock(docHandle(), d->blockMap().last().n);
1480}
1481
1482/*!
1483 \property QTextDocument::pageSize
1484 \brief the page size that should be used for laying out the document
1485
1486 By default, for a newly-created, empty document, this property contains
1487 an undefined size.
1488
1489 \sa modificationChanged()
1490*/
1491
1492void QTextDocument::setPageSize(const QSizeF &size)
1493{
1494 Q_D(QTextDocument);
1495 d->pageSize = size;
1496 if (d->lout)
1497 d->lout->documentChanged(0, 0, d->length());
1498}
1499
1500QSizeF QTextDocument::pageSize() const
1501{
1502 Q_D(const QTextDocument);
1503 return d->pageSize;
1504}
1505
1506/*!
1507 returns the number of pages in this document.
1508*/
1509int QTextDocument::pageCount() const
1510{
1511 return documentLayout()->pageCount();
1512}
1513
1514/*!
1515 Sets the default \a font to use in the document layout.
1516*/
1517void QTextDocument::setDefaultFont(const QFont &font)
1518{
1519 Q_D(QTextDocument);
1520 d->setDefaultFont(font);
1521 if (d->lout)
1522 d->lout->documentChanged(0, 0, d->length());
1523}
1524
1525/*!
1526 Returns the default font to be used in the document layout.
1527*/
1528QFont QTextDocument::defaultFont() const
1529{
1530 Q_D(const QTextDocument);
1531 return d->defaultFont();
1532}
1533
1534/*!
1535 \fn QTextDocument::modificationChanged(bool changed)
1536
1537 This signal is emitted whenever the content of the document
1538 changes in a way that affects the modification state. If \a
1539 changed is true, the document has been modified; otherwise it is
1540 false.
1541
1542 For example, calling setModified(false) on a document and then
1543 inserting text causes the signal to get emitted. If you undo that
1544 operation, causing the document to return to its original
1545 unmodified state, the signal will get emitted again.
1546*/
1547
1548/*!
1549 \property QTextDocument::modified
1550 \brief whether the document has been modified by the user
1551
1552 By default, this property is false.
1553
1554 \sa modificationChanged()
1555*/
1556
1557bool QTextDocument::isModified() const
1558{
1559 return docHandle()->isModified();
1560}
1561
1562void QTextDocument::setModified(bool m)
1563{
1564 docHandle()->setModified(m);
1565}
1566
1567#ifndef QT_NO_PRINTER
1568static void printPage(int index, QPainter *painter, const QTextDocument *doc, const QRectF &body, const QPointF &pageNumberPos)
1569{
1570 painter->save();
1571 painter->translate(body.left(), body.top() - (index - 1) * body.height());
1572 QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
1573
1574 QAbstractTextDocumentLayout *layout = doc->documentLayout();
1575 QAbstractTextDocumentLayout::PaintContext ctx;
1576
1577 painter->setClipRect(view);
1578 ctx.clip = view;
1579
1580 // don't use the system palette text as default text color, on HP/UX
1581 // for example that's white, and white text on white paper doesn't
1582 // look that nice
1583 ctx.palette.setColor(QPalette::Text, Qt::black);
1584
1585 layout->draw(painter, ctx);
1586
1587 if (!pageNumberPos.isNull()) {
1588 painter->setClipping(false);
1589 painter->setFont(QFont(doc->defaultFont()));
1590 const QString pageString = QString::number(index);
1591
1592 painter->drawText(qRound(pageNumberPos.x() - painter->fontMetrics().width(pageString)),
1593 qRound(pageNumberPos.y() + view.top()),
1594 pageString);
1595 }
1596
1597 painter->restore();
1598}
1599
1600extern int qt_defaultDpi();
1601
1602/*!
1603 Prints the document to the given \a printer. The QPrinter must be
1604 set up before being used with this function.
1605
1606 This is only a convenience method to print the whole document to the printer.
1607
1608 If the document is already paginated through a specified height in the pageSize()
1609 property it is printed as-is.
1610
1611 If the document is not paginated, like for example a document used in a QTextEdit,
1612 then a temporary copy of the document is created and the copy is broken into
1613 multiple pages according to the size of the QPrinter's paperRect(). By default
1614 a 2 cm margin is set around the document contents. In addition the current page
1615 number is printed at the bottom of each page.
1616
1617 Note that QPrinter::Selection is not supported as print range with this function since
1618 the selection is a property of QTextCursor. If you have a QTextEdit associated with
1619 your QTextDocument then you can use QTextEdit's print() function because QTextEdit has
1620 access to the user's selection.
1621
1622 \sa QTextEdit::print()
1623*/
1624
1625void QTextDocument::print(QPrinter *printer) const
1626{
1627 Q_D(const QTextDocument);
1628
1629 if (!printer || !printer->isValid())
1630 return;
1631
1632 if (!d->title.isEmpty())
1633 printer->setDocName(d->title);
1634
1635 bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
1636 && d->pageSize.height() != INT_MAX;
1637
1638 if (!documentPaginated && !printer->fullPage() && !printer->d_func()->hasCustomPageMargins)
1639 printer->setPageMargins(23.53, 23.53, 23.53, 23.53, QPrinter::Millimeter);
1640
1641 QPainter p(printer);
1642
1643 // Check that there is a valid device to print to.
1644 if (!p.isActive())
1645 return;
1646
1647 const QTextDocument *doc = this;
1648 QTextDocument *clonedDoc = 0;
1649 (void)doc->documentLayout(); // make sure that there is a layout
1650
1651 QRectF body = QRectF(QPointF(0, 0), d->pageSize);
1652 QPointF pageNumberPos;
1653
1654 if (documentPaginated) {
1655 qreal sourceDpiX = qt_defaultDpi();
1656 qreal sourceDpiY = sourceDpiX;
1657
1658 QPaintDevice *dev = doc->documentLayout()->paintDevice();
1659 if (dev) {
1660 sourceDpiX = dev->logicalDpiX();
1661 sourceDpiY = dev->logicalDpiY();
1662 }
1663
1664 const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
1665 const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
1666
1667 // scale to dpi
1668 p.scale(dpiScaleX, dpiScaleY);
1669
1670 QSizeF scaledPageSize = d->pageSize;
1671 scaledPageSize.rwidth() *= dpiScaleX;
1672 scaledPageSize.rheight() *= dpiScaleY;
1673
1674 const QSizeF printerPageSize(printer->pageRect().size());
1675
1676 // scale to page
1677 p.scale(printerPageSize.width() / scaledPageSize.width(),
1678 printerPageSize.height() / scaledPageSize.height());
1679 } else {
1680 doc = clone(const_cast<QTextDocument *>(this));
1681 clonedDoc = const_cast<QTextDocument *>(doc);
1682
1683 for (QTextBlock srcBlock = firstBlock(), dstBlock = clonedDoc->firstBlock();
1684 srcBlock.isValid() && dstBlock.isValid();
1685 srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
1686 dstBlock.layout()->setAdditionalFormats(srcBlock.layout()->additionalFormats());
1687 }
1688
1689 QAbstractTextDocumentLayout *layout = doc->documentLayout();
1690 layout->setPaintDevice(p.device());
1691
1692 int dpiy = p.device()->logicalDpiY();
1693 int margin = 0;
1694 if (printer->fullPage() && !printer->d_func()->hasCustomPageMargins) {
1695 // for compatibility
1696 margin = (int) ((2/2.54)*dpiy); // 2 cm margins
1697 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
1698 fmt.setMargin(margin);
1699 doc->rootFrame()->setFrameFormat(fmt);
1700 }
1701
1702 QRectF pageRect(printer->pageRect());
1703 body = QRectF(0, 0, pageRect.width(), pageRect.height());
1704 pageNumberPos = QPointF(body.width() - margin,
1705 body.height() - margin
1706 + QFontMetrics(doc->defaultFont(), p.device()).ascent()
1707 + 5 * dpiy / 72.0);
1708 clonedDoc->setPageSize(body.size());
1709 }
1710
1711 int docCopies;
1712 int pageCopies;
1713 if (printer->collateCopies() == true){
1714 docCopies = 1;
1715 pageCopies = printer->numCopies();
1716 } else {
1717 docCopies = printer->numCopies();
1718 pageCopies = 1;
1719 }
1720
1721 int fromPage = printer->fromPage();
1722 int toPage = printer->toPage();
1723 bool ascending = true;
1724
1725 if (fromPage == 0 && toPage == 0) {
1726 fromPage = 1;
1727 toPage = doc->pageCount();
1728 }
1729 // paranoia check
1730 fromPage = qMax(1, fromPage);
1731 toPage = qMin(doc->pageCount(), toPage);
1732
1733 if (printer->pageOrder() == QPrinter::LastPageFirst) {
1734 int tmp = fromPage;
1735 fromPage = toPage;
1736 toPage = tmp;
1737 ascending = false;
1738 }
1739
1740 for (int i = 0; i < docCopies; ++i) {
1741
1742 int page = fromPage;
1743 while (true) {
1744 for (int j = 0; j < pageCopies; ++j) {
1745 if (printer->printerState() == QPrinter::Aborted
1746 || printer->printerState() == QPrinter::Error)
1747 goto UserCanceled;
1748 printPage(page, &p, doc, body, pageNumberPos);
1749 if (j < pageCopies - 1)
1750 printer->newPage();
1751 }
1752
1753 if (page == toPage)
1754 break;
1755
1756 if (ascending)
1757 ++page;
1758 else
1759 --page;
1760
1761 printer->newPage();
1762 }
1763
1764 if ( i < docCopies - 1)
1765 printer->newPage();
1766 }
1767
1768UserCanceled:
1769 delete clonedDoc;
1770}
1771#endif
1772
1773/*!
1774 \enum QTextDocument::ResourceType
1775
1776 This enum describes the types of resources that can be loaded by
1777 QTextDocument's loadResource() function.
1778
1779 \value HtmlResource The resource contains HTML.
1780 \value ImageResource The resource contains image data.
1781 Currently supported data types are QVariant::Pixmap and
1782 QVariant::Image. If the corresponding variant is of type
1783 QVariant::ByteArray then Qt attempts to load the image using
1784 QImage::loadFromData. QVariant::Icon is currently not supported.
1785 The icon needs to be converted to one of the supported types first,
1786 for example using QIcon::pixmap.
1787 \value StyleSheetResource The resource contains CSS.
1788 \value UserResource The first available value for user defined
1789 resource types.
1790
1791 \sa loadResource()
1792*/
1793
1794/*!
1795 Returns data of the specified \a type from the resource with the
1796 given \a name.
1797
1798 This function is called by the rich text engine to request data that isn't
1799 directly stored by QTextDocument, but still associated with it. For example,
1800 images are referenced indirectly by the name attribute of a QTextImageFormat
1801 object.
1802
1803 Resources are cached internally in the document. If a resource can
1804 not be found in the cache, loadResource is called to try to load
1805 the resource. loadResource should then use addResource to add the
1806 resource to the cache.
1807
1808 \sa QTextDocument::ResourceType
1809*/
1810QVariant QTextDocument::resource(int type, const QUrl &name) const
1811{
1812 Q_D(const QTextDocument);
1813 QVariant r = d->resources.value(name);
1814 if (!r.isValid()) {
1815 r = d->cachedResources.value(name);
1816 if (!r.isValid())
1817 r = const_cast<QTextDocument *>(this)->loadResource(type, name);
1818 }
1819 return r;
1820}
1821
1822/*!
1823 Adds the resource \a resource to the resource cache, using \a
1824 type and \a name as identifiers. \a type should be a value from
1825 QTextDocument::ResourceType.
1826
1827 For example, you can add an image as a resource in order to reference it
1828 from within the document:
1829
1830 \snippet snippets/textdocument-resources/main.cpp Adding a resource
1831
1832 The image can be inserted into the document using the QTextCursor API:
1833
1834 \snippet snippets/textdocument-resources/main.cpp Inserting an image with a cursor
1835
1836 Alternatively, you can insert images using the HTML \c img tag:
1837
1838 \snippet snippets/textdocument-resources/main.cpp Inserting an image using HTML
1839*/
1840void QTextDocument::addResource(int type, const QUrl &name, const QVariant &resource)
1841{
1842 Q_UNUSED(type);
1843 Q_D(QTextDocument);
1844 d->resources.insert(name, resource);
1845}
1846
1847/*!
1848 Loads data of the specified \a type from the resource with the
1849 given \a name.
1850
1851 This function is called by the rich text engine to request data that isn't
1852 directly stored by QTextDocument, but still associated with it. For example,
1853 images are referenced indirectly by the name attribute of a QTextImageFormat
1854 object.
1855
1856 When called by Qt, \a type is one of the values of
1857 QTextDocument::ResourceType.
1858
1859 If the QTextDocument is a child object of a QTextEdit, QTextBrowser,
1860 or a QTextDocument itself then the default implementation tries
1861 to retrieve the data from the parent.
1862*/
1863QVariant QTextDocument::loadResource(int type, const QUrl &name)
1864{
1865 Q_D(QTextDocument);
1866 QVariant r;
1867
1868 QTextDocument *doc = qobject_cast<QTextDocument *>(parent());
1869 if (doc) {
1870 r = doc->loadResource(type, name);
1871 }
1872#ifndef QT_NO_TEXTEDIT
1873 else if (QTextEdit *edit = qobject_cast<QTextEdit *>(parent())) {
1874 QUrl resolvedName = edit->d_func()->resolveUrl(name);
1875 r = edit->loadResource(type, resolvedName);
1876 }
1877#endif
1878#ifndef QT_NO_TEXTCONTROL
1879 else if (QTextControl *control = qobject_cast<QTextControl *>(parent())) {
1880 r = control->loadResource(type, name);
1881 }
1882#endif
1883
1884 // if resource was not loaded try to load it here
1885 if (!doc && r.isNull() && name.isRelative()) {
1886 QUrl currentURL = d->url;
1887 QUrl resourceUrl = name;
1888
1889 // For the second case QUrl can merge "#someanchor" with "foo.html"
1890 // correctly to "foo.html#someanchor"
1891 if (!(currentURL.isRelative()
1892 || (currentURL.scheme() == QLatin1String("file")
1893 && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
1894 || (name.hasFragment() && name.path().isEmpty())) {
1895 resourceUrl = currentURL.resolved(name);
1896 } else {
1897 // this is our last resort when current url and new url are both relative
1898 // we try to resolve against the current working directory in the local
1899 // file system.
1900 QFileInfo fi(currentURL.toLocalFile());
1901 if (fi.exists()) {
1902 resourceUrl =
1903 QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
1904 }
1905 }
1906
1907 QString s = resourceUrl.toLocalFile();
1908 QFile f(s);
1909 if (!s.isEmpty() && f.open(QFile::ReadOnly)) {
1910 r = f.readAll();
1911 f.close();
1912 }
1913 }
1914
1915 if (!r.isNull()) {
1916 if (type == ImageResource && r.type() == QVariant::ByteArray) {
1917 if (qApp->thread() != QThread::currentThread()) {
1918 // must use images in non-GUI threads
1919 QImage image;
1920 image.loadFromData(r.toByteArray());
1921 if (!image.isNull())
1922 r = image;
1923 } else {
1924 QPixmap pm;
1925 pm.loadFromData(r.toByteArray());
1926 if (!pm.isNull())
1927 r = pm;
1928 }
1929 }
1930 d->cachedResources.insert(name, r);
1931 }
1932 return r;
1933}
1934
1935static QTextFormat formatDifference(const QTextFormat &from, const QTextFormat &to)
1936{
1937 QTextFormat diff = to;
1938
1939 const QMap<int, QVariant> props = to.properties();
1940 for (QMap<int, QVariant>::ConstIterator it = props.begin(), end = props.end();
1941 it != end; ++it)
1942 if (it.value() == from.property(it.key()))
1943 diff.clearProperty(it.key());
1944
1945 return diff;
1946}
1947
1948QTextHtmlExporter::QTextHtmlExporter(const QTextDocument *_doc)
1949 : doc(_doc), fragmentMarkers(false)
1950{
1951 const QFont defaultFont = doc->defaultFont();
1952 defaultCharFormat.setFont(defaultFont);
1953 // don't export those for the default font since we cannot turn them off with CSS
1954 defaultCharFormat.clearProperty(QTextFormat::FontUnderline);
1955 defaultCharFormat.clearProperty(QTextFormat::FontOverline);
1956 defaultCharFormat.clearProperty(QTextFormat::FontStrikeOut);
1957 defaultCharFormat.clearProperty(QTextFormat::TextUnderlineStyle);
1958}
1959
1960/*!
1961 Returns the document in HTML format. The conversion may not be
1962 perfect, especially for complex documents, due to the limitations
1963 of HTML.
1964*/
1965QString QTextHtmlExporter::toHtml(const QByteArray &encoding, ExportMode mode)
1966{
1967 html = QLatin1String("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
1968 "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
1969 "<html><head><meta name=\"qrichtext\" content=\"1\" />");
1970 html.reserve(doc->docHandle()->length());
1971
1972 fragmentMarkers = (mode == ExportFragment);
1973
1974 if (!encoding.isEmpty())
1975 html += QString::fromLatin1("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\" />").arg(QString::fromAscii(encoding));
1976
1977 QString title = doc->metaInformation(QTextDocument::DocumentTitle);
1978 if (!title.isEmpty())
1979 html += QString::fromLatin1("<title>") + title + QString::fromLatin1("</title>");
1980 html += QLatin1String("<style type=\"text/css\">\n");
1981 html += QLatin1String("p, li { white-space: pre-wrap; }\n");
1982 html += QLatin1String("</style>");
1983 html += QLatin1String("</head><body");
1984
1985 if (mode == ExportEntireDocument) {
1986 html += QLatin1String(" style=\"");
1987
1988 emitFontFamily(defaultCharFormat.fontFamily());
1989
1990 if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) {
1991 html += QLatin1String(" font-size:");
1992 html += QString::number(defaultCharFormat.fontPointSize());
1993 html += QLatin1String("pt;");
1994 }
1995
1996 html += QLatin1String(" font-weight:");
1997 html += QString::number(defaultCharFormat.fontWeight() * 8);
1998 html += QLatin1Char(';');
1999
2000 html += QLatin1String(" font-style:");
2001 html += (defaultCharFormat.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2002 html += QLatin1Char(';');
2003
2004 // do not set text-decoration on the default font since those values are /always/ propagated
2005 // and cannot be turned off with CSS
2006
2007 html += QLatin1Char('\"');
2008
2009 const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2010 emitBackgroundAttribute(fmt);
2011
2012 } else {
2013 defaultCharFormat = QTextCharFormat();
2014 }
2015 html += QLatin1Char('>');
2016
2017 QTextFrameFormat rootFmt = doc->rootFrame()->frameFormat();
2018 rootFmt.clearProperty(QTextFormat::BackgroundBrush);
2019
2020 QTextFrameFormat defaultFmt;
2021 defaultFmt.setMargin(doc->documentMargin());
2022
2023 if (rootFmt == defaultFmt)
2024 emitFrame(doc->rootFrame()->begin());
2025 else
2026 emitTextFrame(doc->rootFrame());
2027
2028 html += QLatin1String("</body></html>");
2029 return html;
2030}
2031
2032void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &value)
2033{
2034 html += QLatin1Char(' ');
2035 html += QLatin1String(attribute);
2036 html += QLatin1String("=\"");
2037 html += value;
2038 html += QLatin1Char('"');
2039}
2040
2041bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
2042{
2043 bool attributesEmitted = false;
2044
2045 {
2046 const QString family = format.fontFamily();
2047 if (!family.isEmpty() && family != defaultCharFormat.fontFamily()) {
2048 emitFontFamily(family);
2049 attributesEmitted = true;
2050 }
2051 }
2052
2053 if (format.hasProperty(QTextFormat::FontPointSize)
2054 && format.fontPointSize() != defaultCharFormat.fontPointSize()) {
2055 html += QLatin1String(" font-size:");
2056 html += QString::number(format.fontPointSize());
2057 html += QLatin1String("pt;");
2058 attributesEmitted = true;
2059 } else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) {
2060 static const char * const sizeNames[] = {
2061 "small", "medium", "large", "x-large", "xx-large"
2062 };
2063 const char *name = 0;
2064 const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1;
2065 if (idx >= 0 && idx <= 4) {
2066 name = sizeNames[idx];
2067 }
2068 if (name) {
2069 html += QLatin1String(" font-size:");
2070 html += QLatin1String(name);
2071 html += QLatin1Char(';');
2072 attributesEmitted = true;
2073 }
2074 }
2075
2076 if (format.hasProperty(QTextFormat::FontWeight)
2077 && format.fontWeight() != defaultCharFormat.fontWeight()) {
2078 html += QLatin1String(" font-weight:");
2079 html += QString::number(format.fontWeight() * 8);
2080 html += QLatin1Char(';');
2081 attributesEmitted = true;
2082 }
2083
2084 if (format.hasProperty(QTextFormat::FontItalic)
2085 && format.fontItalic() != defaultCharFormat.fontItalic()) {
2086 html += QLatin1String(" font-style:");
2087 html += (format.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2088 html += QLatin1Char(';');
2089 attributesEmitted = true;
2090 }
2091
2092 QLatin1String decorationTag(" text-decoration:");
2093 html += decorationTag;
2094 bool hasDecoration = false;
2095 bool atLeastOneDecorationSet = false;
2096
2097 if ((format.hasProperty(QTextFormat::FontUnderline) || format.hasProperty(QTextFormat::TextUnderlineStyle))
2098 && format.fontUnderline() != defaultCharFormat.fontUnderline()) {
2099 hasDecoration = true;
2100 if (format.fontUnderline()) {
2101 html += QLatin1String(" underline");
2102 atLeastOneDecorationSet = true;
2103 }
2104 }
2105
2106 if (format.hasProperty(QTextFormat::FontOverline)
2107 && format.fontOverline() != defaultCharFormat.fontOverline()) {
2108 hasDecoration = true;
2109 if (format.fontOverline()) {
2110 html += QLatin1String(" overline");
2111 atLeastOneDecorationSet = true;
2112 }
2113 }
2114
2115 if (format.hasProperty(QTextFormat::FontStrikeOut)
2116 && format.fontStrikeOut() != defaultCharFormat.fontStrikeOut()) {
2117 hasDecoration = true;
2118 if (format.fontStrikeOut()) {
2119 html += QLatin1String(" line-through");
2120 atLeastOneDecorationSet = true;
2121 }
2122 }
2123
2124 if (hasDecoration) {
2125 if (!atLeastOneDecorationSet)
2126 html += QLatin1String("none");
2127 html += QLatin1Char(';');
2128 attributesEmitted = true;
2129 } else {
2130 html.chop(qstrlen(decorationTag.latin1()));
2131 }
2132
2133 if (format.foreground() != defaultCharFormat.foreground()
2134 && format.foreground().style() != Qt::NoBrush) {
2135 html += QLatin1String(" color:");
2136 html += format.foreground().color().name();
2137 html += QLatin1Char(';');
2138 attributesEmitted = true;
2139 }
2140
2141 if (format.background() != defaultCharFormat.background()
2142 && format.background().style() == Qt::SolidPattern) {
2143 html += QLatin1String(" background-color:");
2144 html += format.background().color().name();
2145 html += QLatin1Char(';');
2146 attributesEmitted = true;
2147 }
2148
2149 if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()
2150 && format.verticalAlignment() != QTextCharFormat::AlignNormal)
2151 {
2152 html += QLatin1String(" vertical-align:");
2153
2154 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2155 if (valign == QTextCharFormat::AlignSubScript)
2156 html += QLatin1String("sub");
2157 else if (valign == QTextCharFormat::AlignSuperScript)
2158 html += QLatin1String("super");
2159 else if (valign == QTextCharFormat::AlignMiddle)
2160 html += QLatin1String("middle");
2161 else if (valign == QTextCharFormat::AlignTop)
2162 html += QLatin1String("top");
2163 else if (valign == QTextCharFormat::AlignBottom)
2164 html += QLatin1String("bottom");
2165
2166 html += QLatin1Char(';');
2167 attributesEmitted = true;
2168 }
2169
2170 if (format.fontCapitalization() != QFont::MixedCase) {
2171 const QFont::Capitalization caps = format.fontCapitalization();
2172 if (caps == QFont::AllUppercase)
2173 html += QLatin1String(" text-transform:uppercase;");
2174 else if (caps == QFont::AllLowercase)
2175 html += QLatin1String(" text-transform:lowercase;");
2176 else if (caps == QFont::SmallCaps)
2177 html += QLatin1String(" font-variant:small-caps;");
2178 attributesEmitted = true;
2179 }
2180
2181 if (format.fontWordSpacing() != 0.0) {
2182 html += QLatin1String(" word-spacing:");
2183 html += QString::number(format.fontWordSpacing());
2184 html += QLatin1String("px;");
2185 attributesEmitted = true;
2186 }
2187
2188 return attributesEmitted;
2189}
2190
2191void QTextHtmlExporter::emitTextLength(const char *attribute, const QTextLength &length)
2192{
2193 if (length.type() == QTextLength::VariableLength) // default
2194 return;
2195
2196 html += QLatin1Char(' ');
2197 html += QLatin1String(attribute);
2198 html += QLatin1String("=\"");
2199 html += QString::number(length.rawValue());
2200
2201 if (length.type() == QTextLength::PercentageLength)
2202 html += QLatin1String("%\"");
2203 else
2204 html += QLatin1String("\"");
2205}
2206
2207void QTextHtmlExporter::emitAlignment(Qt::Alignment align)
2208{
2209 if (align & Qt::AlignLeft)
2210 return;
2211 else if (align & Qt::AlignRight)
2212 html += QLatin1String(" align=\"right\"");
2213 else if (align & Qt::AlignHCenter)
2214 html += QLatin1String(" align=\"center\"");
2215 else if (align & Qt::AlignJustify)
2216 html += QLatin1String(" align=\"justify\"");
2217}
2218
2219void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
2220{
2221 if (pos == QTextFrameFormat::InFlow)
2222 return;
2223
2224 if (mode == EmitStyleTag)
2225 html += QLatin1String(" style=\"float:");
2226 else
2227 html += QLatin1String(" float:");
2228
2229 if (pos == QTextFrameFormat::FloatLeft)
2230 html += QLatin1String(" left;");
2231 else if (pos == QTextFrameFormat::FloatRight)
2232 html += QLatin1String(" right;");
2233 else
2234 Q_ASSERT_X(0, "QTextHtmlExporter::emitFloatStyle()", "pos should be a valid enum type");
2235
2236 if (mode == EmitStyleTag)
2237 html += QLatin1Char('\"');
2238}
2239
2240void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style)
2241{
2242 Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
2243
2244 html += QLatin1String(" border-style:");
2245
2246 switch (style) {
2247 case QTextFrameFormat::BorderStyle_None:
2248 html += QLatin1String("none");
2249 break;
2250 case QTextFrameFormat::BorderStyle_Dotted:
2251 html += QLatin1String("dotted");
2252 break;
2253 case QTextFrameFormat::BorderStyle_Dashed:
2254 html += QLatin1String("dashed");
2255 break;
2256 case QTextFrameFormat::BorderStyle_Solid:
2257 html += QLatin1String("solid");
2258 break;
2259 case QTextFrameFormat::BorderStyle_Double:
2260 html += QLatin1String("double");
2261 break;
2262 case QTextFrameFormat::BorderStyle_DotDash:
2263 html += QLatin1String("dot-dash");
2264 break;
2265 case QTextFrameFormat::BorderStyle_DotDotDash:
2266 html += QLatin1String("dot-dot-dash");
2267 break;
2268 case QTextFrameFormat::BorderStyle_Groove:
2269 html += QLatin1String("groove");
2270 break;
2271 case QTextFrameFormat::BorderStyle_Ridge:
2272 html += QLatin1String("ridge");
2273 break;
2274 case QTextFrameFormat::BorderStyle_Inset:
2275 html += QLatin1String("inset");
2276 break;
2277 case QTextFrameFormat::BorderStyle_Outset:
2278 html += QLatin1String("outset");
2279 break;
2280 default:
2281 Q_ASSERT(false);
2282 break;
2283 };
2284
2285 html += QLatin1Char(';');
2286}
2287
2288void QTextHtmlExporter::emitPageBreakPolicy(QTextFormat::PageBreakFlags policy)
2289{
2290 if (policy & QTextFormat::PageBreak_AlwaysBefore)
2291 html += QLatin1String(" page-break-before:always;");
2292
2293 if (policy & QTextFormat::PageBreak_AlwaysAfter)
2294 html += QLatin1String(" page-break-after:always;");
2295}
2296
2297void QTextHtmlExporter::emitFontFamily(const QString &family)
2298{
2299 html += QLatin1String(" font-family:");
2300
2301 QLatin1Char quote('\'');
2302 if (family.contains(quote))
2303 quote = QLatin1Char('\"');
2304
2305 html += quote;
2306 html += family;
2307 html += quote;
2308 html += QLatin1Char(';');
2309}
2310
2311void QTextHtmlExporter::emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right)
2312{
2313 html += QLatin1String(" margin-top:");
2314 html += top;
2315 html += QLatin1String("px;");
2316
2317 html += QLatin1String(" margin-bottom:");
2318 html += bottom;
2319 html += QLatin1String("px;");
2320
2321 html += QLatin1String(" margin-left:");
2322 html += left;
2323 html += QLatin1String("px;");
2324
2325 html += QLatin1String(" margin-right:");
2326 html += right;
2327 html += QLatin1String("px;");
2328}
2329
2330void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
2331{
2332 const QTextCharFormat format = fragment.charFormat();
2333
2334 bool closeAnchor = false;
2335
2336 if (format.isAnchor()) {
2337 const QString name = format.anchorName();
2338 if (!name.isEmpty()) {
2339 html += QLatin1String("<a name=\"");
2340 html += name;
2341 html += QLatin1String("\"></a>");
2342 }
2343 const QString href = format.anchorHref();
2344 if (!href.isEmpty()) {
2345 html += QLatin1String("<a href=\"");
2346 html += href;
2347 html += QLatin1String("\">");
2348 closeAnchor = true;
2349 }
2350 }
2351
2352 QString txt = fragment.text();
2353 const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
2354 const bool isImage = isObject && format.isImageFormat();
2355
2356 QLatin1String styleTag("<span style=\"");
2357 html += styleTag;
2358
2359 bool attributesEmitted = false;
2360 if (!isImage)
2361 attributesEmitted = emitCharFormatStyle(format);
2362 if (attributesEmitted)
2363 html += QLatin1String("\">");
2364 else
2365 html.chop(qstrlen(styleTag.latin1()));
2366
2367 if (isObject) {
2368 for (int i = 0; isImage && i < txt.length(); ++i) {
2369 QTextImageFormat imgFmt = format.toImageFormat();
2370
2371 html += QLatin1String("<img");
2372
2373 if (imgFmt.hasProperty(QTextFormat::ImageName))
2374 emitAttribute("src", imgFmt.name());
2375
2376 if (imgFmt.hasProperty(QTextFormat::ImageWidth))
2377 emitAttribute("width", QString::number(imgFmt.width()));
2378
2379 if (imgFmt.hasProperty(QTextFormat::ImageHeight))
2380 emitAttribute("height", QString::number(imgFmt.height()));
2381
2382 if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
2383 html += QLatin1String(" style=\"vertical-align: middle;\"");
2384 else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
2385 html += QLatin1String(" style=\"vertical-align: top;\"");
2386
2387 if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
2388 emitFloatStyle(imageFrame->frameFormat().position());
2389
2390 html += QLatin1String(" />");
2391 }
2392 } else {
2393 Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
2394
2395 txt = Qt::escape(txt);
2396
2397 // split for [\n{LineSeparator}]
2398 QString forcedLineBreakRegExp = QString::fromLatin1("[\\na]");
2399 forcedLineBreakRegExp[3] = QChar::LineSeparator;
2400
2401 const QStringList lines = txt.split(QRegExp(forcedLineBreakRegExp));
2402 for (int i = 0; i < lines.count(); ++i) {
2403 if (i > 0)
2404 html += QLatin1String("<br />"); // space on purpose for compatibility with Netscape, Lynx & Co.
2405 html += lines.at(i);
2406 }
2407 }
2408
2409 if (attributesEmitted)
2410 html += QLatin1String("</span>");
2411
2412 if (closeAnchor)
2413 html += QLatin1String("</a>");
2414}
2415
2416static bool isOrderedList(int style)
2417{
2418 return style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
2419 || style == QTextListFormat::ListUpperAlpha;
2420}
2421
2422void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
2423{
2424 QTextBlockFormat format = block.blockFormat();
2425 emitAlignment(format.alignment());
2426
2427 Qt::LayoutDirection dir = format.layoutDirection();
2428 if (dir == Qt::LeftToRight) {
2429 // assume default to not bloat the html too much
2430 // html += QLatin1String(" dir='ltr'");
2431 } else {
2432 html += QLatin1String(" dir='rtl'");
2433 }
2434
2435 QLatin1String style(" style=\"");
2436 html += style;
2437
2438 const bool emptyBlock = block.begin().atEnd();
2439 if (emptyBlock) {
2440 html += QLatin1String("-qt-paragraph-type:empty;");
2441 }
2442
2443 emitMargins(QString::number(format.topMargin()),
2444 QString::number(format.bottomMargin()),
2445 QString::number(format.leftMargin()),
2446 QString::number(format.rightMargin()));
2447
2448 html += QLatin1String(" -qt-block-indent:");
2449 html += QString::number(format.indent());
2450 html += QLatin1Char(';');
2451
2452 html += QLatin1String(" text-indent:");
2453 html += QString::number(format.textIndent());
2454 html += QLatin1String("px;");
2455
2456 if (block.userState() != -1) {
2457 html += QLatin1String(" -qt-user-state:");
2458 html += QString::number(block.userState());
2459 html += QLatin1Char(';');
2460 }
2461
2462 emitPageBreakPolicy(format.pageBreakPolicy());
2463
2464 QTextCharFormat diff;
2465 if (emptyBlock) { // only print character properties when we don't expect them to be repeated by actual text in the parag
2466 const QTextCharFormat blockCharFmt = block.charFormat();
2467 diff = formatDifference(defaultCharFormat, blockCharFmt).toCharFormat();
2468 }
2469
2470 diff.clearProperty(QTextFormat::BackgroundBrush);
2471 if (format.hasProperty(QTextFormat::BackgroundBrush)) {
2472 QBrush bg = format.background();
2473 if (bg.style() != Qt::NoBrush)
2474 diff.setProperty(QTextFormat::BackgroundBrush, format.property(QTextFormat::BackgroundBrush));
2475 }
2476
2477 if (!diff.properties().isEmpty())
2478 emitCharFormatStyle(diff);
2479
2480 html += QLatin1Char('"');
2481
2482}
2483
2484void QTextHtmlExporter::emitBlock(const QTextBlock &block)
2485{
2486 if (block.begin().atEnd()) {
2487 // ### HACK, remove once QTextFrame::Iterator is fixed
2488 int p = block.position();
2489 if (p > 0)
2490 --p;
2491 QTextDocumentPrivate::FragmentIterator frag = doc->docHandle()->find(p);
2492 QChar ch = doc->docHandle()->buffer().at(frag->stringPosition);
2493 if (ch == QTextBeginningOfFrame
2494 || ch == QTextEndOfFrame)
2495 return;
2496 }
2497
2498 html += QLatin1Char('\n');
2499
2500 // save and later restore, in case we 'change' the default format by
2501 // emitting block char format information
2502 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2503
2504 QTextList *list = block.textList();
2505 if (list) {
2506 if (list->itemNumber(block) == 0) { // first item? emit <ul> or appropriate
2507 const QTextListFormat format = list->format();
2508 const int style = format.style();
2509 switch (style) {
2510 case QTextListFormat::ListDecimal: html += QLatin1String("<ol"); break;
2511 case QTextListFormat::ListDisc: html += QLatin1String("<ul"); break;
2512 case QTextListFormat::ListCircle: html += QLatin1String("<ul type=\"circle\""); break;
2513 case QTextListFormat::ListSquare: html += QLatin1String("<ul type=\"square\""); break;
2514 case QTextListFormat::ListLowerAlpha: html += QLatin1String("<ol type=\"a\""); break;
2515 case QTextListFormat::ListUpperAlpha: html += QLatin1String("<ol type=\"A\""); break;
2516 default: html += QLatin1String("<ul"); // ### should not happen
2517 }
2518
2519 if (format.hasProperty(QTextFormat::ListIndent)) {
2520 html += QLatin1String(" style=\"-qt-list-indent: ");
2521 html += QString::number(format.indent());
2522 html += QLatin1String(";\"");
2523 }
2524
2525 html += QLatin1Char('>');
2526 }
2527
2528 html += QLatin1String("<li");
2529
2530 const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
2531 if (!blockFmt.properties().isEmpty()) {
2532 html += QLatin1String(" style=\"");
2533 emitCharFormatStyle(blockFmt);
2534 html += QLatin1Char('\"');
2535
2536 defaultCharFormat.merge(block.charFormat());
2537 }
2538 }
2539
2540 const QTextBlockFormat blockFormat = block.blockFormat();
2541 if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
2542 html += QLatin1String("<hr");
2543
2544 QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
2545 if (width.type() != QTextLength::VariableLength)
2546 emitTextLength("width", width);
2547 else
2548 html += QLatin1Char(' ');
2549
2550 html += QLatin1String("/>");
2551 return;
2552 }
2553
2554 const bool pre = blockFormat.nonBreakableLines();
2555 if (pre) {
2556 if (list)
2557 html += QLatin1Char('>');
2558 html += QLatin1String("<pre");
2559 } else if (!list) {
2560 html += QLatin1String("<p");
2561 }
2562
2563 emitBlockAttributes(block);
2564
2565 html += QLatin1Char('>');
2566
2567 QTextBlock::Iterator it = block.begin();
2568 if (fragmentMarkers && !it.atEnd() && block == doc->begin())
2569 html += QLatin1String("<!--StartFragment-->");
2570
2571 for (; !it.atEnd(); ++it)
2572 emitFragment(it.fragment());
2573
2574 if (fragmentMarkers && block.position() + block.length() == doc->docHandle()->length())
2575 html += QLatin1String("<!--EndFragment-->");
2576
2577 if (pre)
2578 html += QLatin1String("</pre>");
2579 else if (list)
2580 html += QLatin1String("</li>");
2581 else
2582 html += QLatin1String("</p>");
2583
2584 if (list) {
2585 if (list->itemNumber(block) == list->count() - 1) { // last item? close list
2586 if (isOrderedList(list->format().style()))
2587 html += QLatin1String("</ol>");
2588 else
2589 html += QLatin1String("</ul>");
2590 }
2591 }
2592
2593 defaultCharFormat = oldDefaultCharFormat;
2594}
2595
2596extern bool qHasPixmapTexture(const QBrush& brush);
2597
2598QString QTextHtmlExporter::findUrlForImage(const QTextDocument *doc, qint64 cacheKey, bool isPixmap)
2599{
2600 QString url;
2601 if (!doc)
2602 return url;
2603
2604 if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent()))
2605 return findUrlForImage(parent, cacheKey, isPixmap);
2606
2607 if (doc && doc->docHandle()) {
2608 QTextDocumentPrivate *priv = doc->docHandle();
2609 QMap<QUrl, QVariant>::const_iterator it = priv->cachedResources.constBegin();
2610 for (; it != priv->cachedResources.constEnd(); ++it) {
2611
2612 const QVariant &v = it.value();
2613 if (v.type() == QVariant::Image && !isPixmap) {
2614 if (qvariant_cast<QImage>(v).cacheKey() == cacheKey)
2615 break;
2616 }
2617
2618 if (v.type() == QVariant::Pixmap && isPixmap) {
2619 if (qvariant_cast<QPixmap>(v).cacheKey() == cacheKey)
2620 break;
2621 }
2622 }
2623
2624 if (it != priv->cachedResources.constEnd())
2625 url = it.key().toString();
2626 }
2627
2628 return url;
2629}
2630
2631void QTextDocumentPrivate::mergeCachedResources(const QTextDocumentPrivate *priv)
2632{
2633 if (!priv)
2634 return;
2635
2636 cachedResources.unite(priv->cachedResources);
2637}
2638
2639void QTextHtmlExporter::emitBackgroundAttribute(const QTextFormat &format)
2640{
2641 if (format.hasProperty(QTextFormat::BackgroundImageUrl)) {
2642 QString url = format.property(QTextFormat::BackgroundImageUrl).toString();
2643 emitAttribute("background", url);
2644 } else {
2645 const QBrush &brush = format.background();
2646 if (brush.style() == Qt::SolidPattern) {
2647 emitAttribute("bgcolor", brush.color().name());
2648 } else if (brush.style() == Qt::TexturePattern) {
2649 const bool isPixmap = qHasPixmapTexture(brush);
2650 const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
2651
2652 const QString url = findUrlForImage(doc, cacheKey, isPixmap);
2653
2654 if (!url.isEmpty())
2655 emitAttribute("background", url);
2656 }
2657 }
2658}
2659
2660void QTextHtmlExporter::emitTable(const QTextTable *table)
2661{
2662 QTextTableFormat format = table->format();
2663
2664 html += QLatin1String("\n<table");
2665
2666 if (format.hasProperty(QTextFormat::FrameBorder))
2667 emitAttribute("border", QString::number(format.border()));
2668
2669 emitFrameStyle(format, TableFrame);
2670
2671 emitAlignment(format.alignment());
2672 emitTextLength("width", format.width());
2673
2674 if (format.hasProperty(QTextFormat::TableCellSpacing))
2675 emitAttribute("cellspacing", QString::number(format.cellSpacing()));
2676 if (format.hasProperty(QTextFormat::TableCellPadding))
2677 emitAttribute("cellpadding", QString::number(format.cellPadding()));
2678
2679 emitBackgroundAttribute(format);
2680
2681 html += QLatin1Char('>');
2682
2683 const int rows = table->rows();
2684 const int columns = table->columns();
2685
2686 QVector<QTextLength> columnWidths = format.columnWidthConstraints();
2687 if (columnWidths.isEmpty()) {
2688 columnWidths.resize(columns);
2689 columnWidths.fill(QTextLength());
2690 }
2691 Q_ASSERT(columnWidths.count() == columns);
2692
2693 QVarLengthArray<bool> widthEmittedForColumn(columns);
2694 for (int i = 0; i < columns; ++i)
2695 widthEmittedForColumn[i] = false;
2696
2697 const int headerRowCount = qMin(format.headerRowCount(), rows);
2698 if (headerRowCount > 0)
2699 html += QLatin1String("<thead>");
2700
2701 for (int row = 0; row < rows; ++row) {
2702 html += QLatin1String("\n<tr>");
2703
2704 for (int col = 0; col < columns; ++col) {
2705 const QTextTableCell cell = table->cellAt(row, col);
2706
2707 // for col/rowspans
2708 if (cell.row() != row)
2709 continue;
2710
2711 if (cell.column() != col)
2712 continue;
2713
2714 html += QLatin1String("\n<td");
2715
2716 if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
2717 emitTextLength("width", columnWidths.at(col));
2718 widthEmittedForColumn[col] = true;
2719 }
2720
2721 if (cell.columnSpan() > 1)
2722 emitAttribute("colspan", QString::number(cell.columnSpan()));
2723
2724 if (cell.rowSpan() > 1)
2725 emitAttribute("rowspan", QString::number(cell.rowSpan()));
2726
2727 const QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
2728 emitBackgroundAttribute(cellFormat);
2729
2730 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2731
2732 QTextCharFormat::VerticalAlignment valign = cellFormat.verticalAlignment();
2733
2734 QString styleString;
2735 if (valign >= QTextCharFormat::AlignMiddle && valign <= QTextCharFormat::AlignBottom) {
2736 styleString += QLatin1String(" vertical-align:");
2737 switch (valign) {
2738 case QTextCharFormat::AlignMiddle:
2739 styleString += QLatin1String("middle");
2740 break;
2741 case QTextCharFormat::AlignTop:
2742 styleString += QLatin1String("top");
2743 break;
2744 case QTextCharFormat::AlignBottom:
2745 styleString += QLatin1String("bottom");
2746 break;
2747 default:
2748 break;
2749 }
2750 styleString += QLatin1Char(';');
2751
2752 QTextCharFormat temp;
2753 temp.setVerticalAlignment(valign);
2754 defaultCharFormat.merge(temp);
2755 }
2756
2757 if (cellFormat.hasProperty(QTextFormat::TableCellLeftPadding))
2758 styleString += QLatin1String(" padding-left:") + QString::number(cellFormat.leftPadding()) + QLatin1Char(';');
2759 if (cellFormat.hasProperty(QTextFormat::TableCellRightPadding))
2760 styleString += QLatin1String(" padding-right:") + QString::number(cellFormat.rightPadding()) + QLatin1Char(';');
2761 if (cellFormat.hasProperty(QTextFormat::TableCellTopPadding))
2762 styleString += QLatin1String(" padding-top:") + QString::number(cellFormat.topPadding()) + QLatin1Char(';');
2763 if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
2764 styleString += QLatin1String(" padding-bottom:") + QString::number(cellFormat.bottomPadding()) + QLatin1Char(';');
2765
2766 if (!styleString.isEmpty())
2767 html += QLatin1String(" style=\"") + styleString + QLatin1Char('\"');
2768
2769 html += QLatin1Char('>');
2770
2771 emitFrame(cell.begin());
2772
2773 html += QLatin1String("</td>");
2774
2775 defaultCharFormat = oldDefaultCharFormat;
2776 }
2777
2778 html += QLatin1String("</tr>");
2779 if (headerRowCount > 0 && row == headerRowCount - 1)
2780 html += QLatin1String("</thead>");
2781 }
2782
2783 html += QLatin1String("</table>");
2784}
2785
2786void QTextHtmlExporter::emitFrame(QTextFrame::Iterator frameIt)
2787{
2788 if (!frameIt.atEnd()) {
2789 QTextFrame::Iterator next = frameIt;
2790 ++next;
2791 if (next.atEnd()
2792 && frameIt.currentFrame() == 0
2793 && frameIt.parentFrame() != doc->rootFrame()
2794 && frameIt.currentBlock().begin().atEnd())
2795 return;
2796 }
2797
2798 for (QTextFrame::Iterator it = frameIt;
2799 !it.atEnd(); ++it) {
2800 if (QTextFrame *f = it.currentFrame()) {
2801 if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
2802 emitTable(table);
2803 } else {
2804 emitTextFrame(f);
2805 }
2806 } else if (it.currentBlock().isValid()) {
2807 emitBlock(it.currentBlock());
2808 }
2809 }
2810}
2811
2812void QTextHtmlExporter::emitTextFrame(const QTextFrame *f)
2813{
2814 FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
2815
2816 html += QLatin1String("\n<table");
2817 QTextFrameFormat format = f->frameFormat();
2818
2819 if (format.hasProperty(QTextFormat::FrameBorder))
2820 emitAttribute("border", QString::number(format.border()));
2821
2822 emitFrameStyle(format, frameType);
2823
2824 emitTextLength("width", format.width());
2825 emitTextLength("height", format.height());
2826
2827 // root frame's bcolor goes in the <body> tag
2828 if (frameType != RootFrame)
2829 emitBackgroundAttribute(format);
2830
2831 html += QLatin1Char('>');
2832 html += QLatin1String("\n<tr>\n<td style=\"border: none;\">");
2833 emitFrame(f->begin());
2834 html += QLatin1String("</td></tr></table>");
2835}
2836
2837void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType frameType)
2838{
2839 QLatin1String styleAttribute(" style=\"");
2840 html += styleAttribute;
2841 const int originalHtmlLength = html.length();
2842
2843 if (frameType == TextFrame)
2844 html += QLatin1String("-qt-table-type: frame;");
2845 else if (frameType == RootFrame)
2846 html += QLatin1String("-qt-table-type: root;");
2847
2848 const QTextFrameFormat defaultFormat;
2849
2850 emitFloatStyle(format.position(), OmitStyleTag);
2851 emitPageBreakPolicy(format.pageBreakPolicy());
2852
2853 if (format.borderBrush() != defaultFormat.borderBrush()) {
2854 html += QLatin1String(" border-color:");
2855 html += format.borderBrush().color().name();
2856 html += QLatin1Char(';');
2857 }
2858
2859 if (format.borderStyle() != defaultFormat.borderStyle())
2860 emitBorderStyle(format.borderStyle());
2861
2862 if (format.hasProperty(QTextFormat::FrameMargin)
2863 || format.hasProperty(QTextFormat::FrameLeftMargin)
2864 || format.hasProperty(QTextFormat::FrameRightMargin)
2865 || format.hasProperty(QTextFormat::FrameTopMargin)
2866 || format.hasProperty(QTextFormat::FrameBottomMargin))
2867 emitMargins(QString::number(format.topMargin()),
2868 QString::number(format.bottomMargin()),
2869 QString::number(format.leftMargin()),
2870 QString::number(format.rightMargin()));
2871
2872 if (html.length() == originalHtmlLength) // nothing emitted?
2873 html.chop(qstrlen(styleAttribute.latin1()));
2874 else
2875 html += QLatin1Char('\"');
2876}
2877
2878/*!
2879 Returns a string containing an HTML representation of the document.
2880
2881 The \a encoding parameter specifies the value for the charset attribute
2882 in the html header. For example if 'utf-8' is specified then the
2883 beginning of the generated html will look like this:
2884 \snippet doc/src/snippets/code/src_gui_text_qtextdocument.cpp 1
2885
2886 If no encoding is specified then no such meta information is generated.
2887
2888 If you later on convert the returned html string into a byte array for
2889 transmission over a network or when saving to disk you should specify
2890 the encoding you're going to use for the conversion to a byte array here.
2891
2892 \sa {Supported HTML Subset}
2893*/
2894#ifndef QT_NO_TEXTHTMLPARSER
2895QString QTextDocument::toHtml(const QByteArray &encoding) const
2896{
2897 return QTextHtmlExporter(this).toHtml(encoding);
2898}
2899#endif // QT_NO_TEXTHTMLPARSER
2900
2901/*!
2902 Returns a vector of text formats for all the formats used in the document.
2903*/
2904QVector<QTextFormat> QTextDocument::allFormats() const
2905{
2906 Q_D(const QTextDocument);
2907 return d->formatCollection()->formats;
2908}
2909
2910
2911/*!
2912 \internal
2913
2914 So that not all classes have to be friends of each other...
2915*/
2916QTextDocumentPrivate *QTextDocument::docHandle() const
2917{
2918 Q_D(const QTextDocument);
2919 return const_cast<QTextDocumentPrivate *>(d);
2920}
2921
2922/*!
2923 \since 4.4
2924 \fn QTextDocument::undoCommandAdded()
2925
2926 This signal is emitted every time a new level of undo is added to the QTextDocument.
2927*/
2928
2929QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.