1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
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"
51 #include <qvarlengtharray.h>
52 #include <qtextcodec.h>
54 #include <qcoreapplication.h>
55 #include <qmetaobject.h>
57 #include "qtexthtmlparser_p.h"
60 #include <qfileinfo.h>
63 #include "private/qdataurl_p.h"
65 #include "qtextdocument_p.h"
66 #include <private/qabstracttextdocumentlayout_p.h>
67 #include "qpagedpaintdevice.h"
68 #include "private/qpagedpaintdevice_p.h"
74 Q_CORE_EXPORT unsigned int qt_int_sqrt(unsigned int n);
77 Returns true if the string \a text is likely to be rich text;
78 otherwise returns false.
80 This function uses a fast and therefore simple heuristic. It
81 mainly checks whether there is something that looks like a tag
82 before the first line break. Although the result may be correct
83 for common cases, there is no guarantee.
85 This function is defined in the \c <QTextDocument> header file.
87 bool Qt::mightBeRichText(const QString& text)
93 while (start < text.length() && text.at(start).isSpace())
96 // skip a leading <?xml ... ?> as for example with xhtml
97 if (text.mid(start, 5) == QLatin1String("<?xml")) {
98 while (start < text.length()) {
99 if (text.at(start) == QLatin1Char('?')
100 && start + 2 < text.length()
101 && text.at(start + 1) == QLatin1Char('>')) {
108 while (start < text.length() && text.at(start).isSpace())
112 if (text.mid(start, 5).toLower() == QLatin1String("<!doc"))
115 while (open < text.length() && text.at(open) != QLatin1Char('<')
116 && text.at(open) != QLatin1Char('\n')) {
117 if (text.at(open) == QLatin1Char('&') && text.mid(open+1,3) == QLatin1String("lt;"))
118 return true; // support desperate attempt of user to see <...>
121 if (open < text.length() && text.at(open) == QLatin1Char('<')) {
122 const int close = text.indexOf(QLatin1Char('>'), open);
125 for (int i = open+1; i < close; ++i) {
126 if (text[i].isDigit() || text[i].isLetter())
128 else if (!tag.isEmpty() && text[i].isSpace())
130 else if (!tag.isEmpty() && text[i] == QLatin1Char('/') && i + 1 == close)
132 else if (!text[i].isSpace() && (!tag.isEmpty() || text[i] != QLatin1Char('!')))
133 return false; // that's not a tag
135 #ifndef QT_NO_TEXTHTMLPARSER
136 return QTextHtmlParser::lookupElement(tag.toLower()) != -1;
139 #endif // QT_NO_TEXTHTMLPARSER
146 \fn QString Qt::convertFromPlainText(const QString &plain, WhiteSpaceMode mode)
148 Converts the plain text string \a plain to an HTML-formatted
149 paragraph while preserving most of its look.
151 \a mode defines how whitespace is handled.
153 This function is defined in the \c <QTextDocument> header file.
155 \sa escape(), mightBeRichText()
157 QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
161 rich += QLatin1String("<p>");
162 for (int i = 0; i < plain.length(); ++i) {
163 if (plain[i] == QLatin1Char('\n')){
165 while (i+1 < plain.length() && plain[i+1] == QLatin1Char('\n')) {
170 rich += QLatin1String("<br>\n");
172 rich += QLatin1String("</p>\n");
174 rich += QLatin1String("<br>\n");
175 rich += QLatin1String("<p>");
179 if (mode == Qt::WhiteSpacePre && plain[i] == QLatin1Char('\t')){
180 rich += QChar(0x00a0U);
183 rich += QChar(0x00a0U);
187 else if (mode == Qt::WhiteSpacePre && plain[i].isSpace())
188 rich += QChar(0x00a0U);
189 else if (plain[i] == QLatin1Char('<'))
190 rich += QLatin1String("<");
191 else if (plain[i] == QLatin1Char('>'))
192 rich += QLatin1String(">");
193 else if (plain[i] == QLatin1Char('&'))
194 rich += QLatin1String("&");
201 rich += QLatin1String("</p>");
205 #ifndef QT_NO_TEXTCODEC
209 This function is defined in the \c <QTextDocument> header file.
211 QTextCodec *Qt::codecForHtml(const QByteArray &ba)
213 return QTextCodec::codecForHtml(ba);
222 \brief The QTextDocument class holds formatted text that can be
223 viewed and edited using a QTextEdit.
225 \ingroup richtext-processing
228 QTextDocument is a container for structured rich text documents, providing
229 support for styled text and various types of document elements, such as
230 lists, tables, frames, and images.
231 They can be created for use in a QTextEdit, or used independently.
233 Each document element is described by an associated format object. Each
234 format object is treated as a unique object by QTextDocuments, and can be
235 passed to objectForFormat() to obtain the document element that it is
238 A QTextDocument can be edited programmatically using a QTextCursor, and
239 its contents can be examined by traversing the document structure. The
240 entire document structure is stored as a hierarchy of document elements
241 beneath the root frame, found with the rootFrame() function. Alternatively,
242 if you just want to iterate over the textual contents of the document you
243 can use begin(), end(), and findBlock() to retrieve text blocks that you
244 can examine and iterate over.
246 The layout of a document is determined by the documentLayout();
247 you can create your own QAbstractTextDocumentLayout subclass and
248 set it using setDocumentLayout() if you want to use your own
249 layout logic. The document's title and other meta-information can be
250 obtained by calling the metaInformation() function. For documents that
251 are exposed to users through the QTextEdit class, the document title
252 is also available via the QTextEdit::documentTitle() function.
254 The toPlainText() and toHtml() convenience functions allow you to retrieve the
255 contents of the document as plain text and HTML.
256 The document's text can be searched using the find() functions.
258 Undo/redo of operations performed on the document can be controlled using
259 the setUndoRedoEnabled() function. The undo/redo system can be controlled
260 by an editor widget through the undo() and redo() slots; the document also
261 provides contentsChanged(), undoAvailable(), and redoAvailable() signals
262 that inform connected editor widgets about the state of the undo/redo
263 system. The following are the undo/redo operations of a QTextDocument:
266 \li Insertion or removal of characters. A sequence of insertions or removals
267 within the same text block are regarded as a single undo/redo operation.
268 \li Insertion or removal of text blocks. Sequences of insertion or removals
269 in a single operation (e.g., by selecting and then deleting text) are
270 regarded as a single undo/redo operation.
271 \li Text character format changes.
272 \li Text block format changes.
273 \li Text block group format changes.
276 \sa QTextCursor, QTextEdit, {Rich Text Processing}, {Text Object Example}
280 \property QTextDocument::defaultFont
281 \brief the default font used to display the document's text
285 \property QTextDocument::defaultTextOption
286 \brief the default text option will be set on all \l{QTextLayout}s in the document.
288 When \l{QTextBlock}s are created, the defaultTextOption is set on their
289 QTextLayout. This allows setting global properties for the document such as the
290 default word wrap mode.
294 Constructs an empty QTextDocument with the given \a parent.
296 QTextDocument::QTextDocument(QObject *parent)
297 : QObject(*new QTextDocumentPrivate, parent)
304 Constructs a QTextDocument containing the plain (unformatted) \a text
305 specified, and with the given \a parent.
307 QTextDocument::QTextDocument(const QString &text, QObject *parent)
308 : QObject(*new QTextDocumentPrivate, parent)
312 QTextCursor(this).insertText(text);
318 QTextDocument::QTextDocument(QTextDocumentPrivate &dd, QObject *parent)
319 : QObject(dd, parent)
326 Destroys the document.
328 QTextDocument::~QTextDocument()
334 Creates a new QTextDocument that is a copy of this text document. \a
335 parent is the parent of the returned text document.
337 QTextDocument *QTextDocument::clone(QObject *parent) const
339 Q_D(const QTextDocument);
340 QTextDocument *doc = new QTextDocument(parent);
341 QTextCursor(doc).insertFragment(QTextDocumentFragment(this));
342 doc->rootFrame()->setFrameFormat(rootFrame()->frameFormat());
343 QTextDocumentPrivate *priv = doc->d_func();
344 priv->title = d->title;
346 priv->pageSize = d->pageSize;
347 priv->indentWidth = d->indentWidth;
348 priv->defaultTextOption = d->defaultTextOption;
349 priv->setDefaultFont(d->defaultFont());
350 priv->resources = d->resources;
351 priv->cachedResources.clear();
352 #ifndef QT_NO_CSSPARSER
353 priv->defaultStyleSheet = d->defaultStyleSheet;
354 priv->parsedDefaultStyleSheet = d->parsedDefaultStyleSheet;
360 Returns true if the document is empty; otherwise returns false.
362 bool QTextDocument::isEmpty() const
364 Q_D(const QTextDocument);
365 /* because if we're empty we still have one single paragraph as
366 * one single fragment */
367 return d->length() <= 1;
373 void QTextDocument::clear()
377 d->resources.clear();
383 Undoes the last editing operation on the document if undo is
384 available. The provided \a cursor is positioned at the end of the
385 location where the edition operation was undone.
387 See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
388 documentation for details.
390 \sa undoAvailable(), isUndoRedoEnabled()
392 void QTextDocument::undo(QTextCursor *cursor)
395 const int pos = d->undoRedo(true);
396 if (cursor && pos >= 0) {
397 *cursor = QTextCursor(this);
398 cursor->setPosition(pos);
404 Redoes the last editing operation on the document if \l{QTextDocument::isRedoAvailable()}{redo is available}.
406 The provided \a cursor is positioned at the end of the location where
407 the edition operation was redone.
409 void QTextDocument::redo(QTextCursor *cursor)
412 const int pos = d->undoRedo(false);
413 if (cursor && pos >= 0) {
414 *cursor = QTextCursor(this);
415 cursor->setPosition(pos);
419 /*! \enum QTextDocument::Stacks
421 \value UndoStack The undo stack.
422 \value RedoStack The redo stack.
423 \value UndoAndRedoStacks Both the undo and redo stacks.
428 Clears the stacks specified by \a stacksToClear.
430 This method clears any commands on the undo stack, the redo stack,
431 or both (the default). If commands are cleared, the appropriate
432 signals are emitted, QTextDocument::undoAvailable() or
433 QTextDocument::redoAvailable().
435 \sa QTextDocument::undoAvailable(), QTextDocument::redoAvailable()
437 void QTextDocument::clearUndoRedoStacks(Stacks stacksToClear)
440 d->clearUndoRedoStacks(stacksToClear, true);
447 void QTextDocument::undo()
455 Redoes the last editing operation on the document if \l{QTextDocument::isRedoAvailable()}{redo is available}.
457 void QTextDocument::redo()
466 Appends a custom undo \a item to the undo stack.
468 void QTextDocument::appendUndoItem(QAbstractUndoItem *item)
471 d->appendUndoItem(item);
475 \property QTextDocument::undoRedoEnabled
476 \brief whether undo/redo are enabled for this document
478 This defaults to true. If disabled, the undo stack is cleared and
479 no items will be added to it.
481 void QTextDocument::setUndoRedoEnabled(bool enable)
484 d->enableUndoRedo(enable);
487 bool QTextDocument::isUndoRedoEnabled() const
489 Q_D(const QTextDocument);
490 return d->isUndoRedoEnabled();
494 \property QTextDocument::maximumBlockCount
496 \brief Specifies the limit for blocks in the document.
498 Specifies the maximum number of blocks the document may have. If there are
499 more blocks in the document that specified with this property blocks are removed
500 from the beginning of the document.
502 A negative or zero value specifies that the document may contain an unlimited
505 The default value is 0.
507 Note that setting this property will apply the limit immediately to the document
510 Setting this property also disables the undo redo history.
512 This property is undefined in documents with tables or frames.
514 int QTextDocument::maximumBlockCount() const
516 Q_D(const QTextDocument);
517 return d->maximumBlockCount;
520 void QTextDocument::setMaximumBlockCount(int maximum)
523 d->maximumBlockCount = maximum;
524 d->ensureMaximumBlockCount();
525 setUndoRedoEnabled(false);
531 The default text option is used on all QTextLayout objects in the document.
532 This allows setting global properties for the document such as the default
535 QTextOption QTextDocument::defaultTextOption() const
537 Q_D(const QTextDocument);
538 return d->defaultTextOption;
544 Sets the default text option.
546 void QTextDocument::setDefaultTextOption(const QTextOption &option)
549 d->defaultTextOption = option;
551 d->lout->documentChanged(0, 0, d->length());
557 The default cursor movement style is used by all QTextCursor objects
558 created from the document. The default is Qt::LogicalMoveStyle.
560 Qt::CursorMoveStyle QTextDocument::defaultCursorMoveStyle() const
562 Q_D(const QTextDocument);
563 return d->defaultCursorMoveStyle;
569 Sets the default cursor movement style to the given \a style.
571 void QTextDocument::setDefaultCursorMoveStyle(Qt::CursorMoveStyle style)
574 d->defaultCursorMoveStyle = style;
578 \fn void QTextDocument::markContentsDirty(int position, int length)
580 Marks the contents specified by the given \a position and \a length
581 as "dirty", informing the document that it needs to be laid out
584 void QTextDocument::markContentsDirty(int from, int length)
587 d->documentChange(from, length);
588 if (!d->inContentsChange) {
590 d->lout->documentChanged(d->docChangeFrom, d->docChangeOldLength, d->docChangeLength);
591 d->docChangeFrom = -1;
597 \property QTextDocument::useDesignMetrics
599 \brief whether the document uses design metrics of fonts to improve the accuracy of text layout
601 If this property is set to true, the layout will use design metrics.
602 Otherwise, the metrics of the paint device as set on
603 QAbstractTextDocumentLayout::setPaintDevice() will be used.
605 Using design metrics makes a layout have a width that is no longer dependent on hinting
606 and pixel-rounding. This means that WYSIWYG text layout becomes possible because the width
607 scales much more linearly based on paintdevice metrics than it would otherwise.
609 By default, this property is false.
612 void QTextDocument::setUseDesignMetrics(bool b)
615 if (b == d->defaultTextOption.useDesignMetrics())
617 d->defaultTextOption.setUseDesignMetrics(b);
619 d->lout->documentChanged(0, 0, d->length());
622 bool QTextDocument::useDesignMetrics() const
624 Q_D(const QTextDocument);
625 return d->defaultTextOption.useDesignMetrics();
631 Draws the content of the document with painter \a p, clipped to \a rect.
632 If \a rect is a null rectangle (default) then the document is painted unclipped.
634 void QTextDocument::drawContents(QPainter *p, const QRectF &rect)
637 QAbstractTextDocumentLayout::PaintContext ctx;
638 if (rect.isValid()) {
639 p->setClipRect(rect);
642 documentLayout()->draw(p, ctx);
647 \property QTextDocument::textWidth
650 The text width specifies the preferred width for text in the document. If
651 the text (or content in general) is wider than the specified with it is broken
652 into multiple lines and grows vertically. If the text cannot be broken into multiple
653 lines to fit into the specified text width it will be larger and the size() and the
654 idealWidth() property will reflect that.
656 If the text width is set to -1 then the text will not be broken into multiple lines
657 unless it is enforced through an explicit line break or a new paragraph.
659 The default value is -1.
661 Setting the text width will also set the page height to -1, causing the document to
662 grow or shrink vertically in a continuous way. If you want the document layout to break
663 the text into multiple pages then you have to set the pageSize property instead.
665 \sa size(), idealWidth(), pageSize()
667 void QTextDocument::setTextWidth(qreal width)
670 QSizeF sz = d->pageSize;
676 qreal QTextDocument::textWidth() const
678 Q_D(const QTextDocument);
679 return d->pageSize.width();
685 Returns the ideal width of the text document. The ideal width is the actually used width
686 of the document without optional alignments taken into account. It is always <= size().width().
688 \sa adjustSize(), textWidth
690 qreal QTextDocument::idealWidth() const
692 if (QTextDocumentLayout *lout = qobject_cast<QTextDocumentLayout *>(documentLayout()))
693 return lout->idealWidth();
698 \property QTextDocument::documentMargin
701 The margin around the document. The default is 4.
703 qreal QTextDocument::documentMargin() const
705 Q_D(const QTextDocument);
706 return d->documentMargin;
709 void QTextDocument::setDocumentMargin(qreal margin)
712 if (d->documentMargin != margin) {
713 d->documentMargin = margin;
715 QTextFrame* root = rootFrame();
716 QTextFrameFormat format = root->frameFormat();
717 format.setMargin(margin);
718 root->setFrameFormat(format);
721 d->lout->documentChanged(0, 0, d->length());
727 \property QTextDocument::indentWidth
730 Returns the width used for text list and text block indenting.
732 The indent properties of QTextListFormat and QTextBlockFormat specify
733 multiples of this value. The default indent width is 40.
735 qreal QTextDocument::indentWidth() const
737 Q_D(const QTextDocument);
738 return d->indentWidth;
745 Sets the \a width used for text list and text block indenting.
747 The indent properties of QTextListFormat and QTextBlockFormat specify
748 multiples of this value. The default indent width is 40 .
752 void QTextDocument::setIndentWidth(qreal width)
755 if (d->indentWidth != width) {
756 d->indentWidth = width;
758 d->lout->documentChanged(0, 0, d->length());
768 Adjusts the document to a reasonable size.
770 \sa idealWidth(), textWidth, size
772 void QTextDocument::adjustSize()
774 // Pull this private function in from qglobal.cpp
775 QFont f = defaultFont();
777 int mw = fm.width(QLatin1Char('x')) * 80;
780 QSizeF size = documentLayout()->documentSize();
781 if (size.width() != 0) {
782 w = qt_int_sqrt((uint)(5 * size.height() * size.width() / 3));
783 setTextWidth(qMin(w, mw));
785 size = documentLayout()->documentSize();
786 if (w*3 < 5*size.height()) {
787 w = qt_int_sqrt((uint)(2 * size.height() * size.width()));
788 setTextWidth(qMin(w, mw));
791 setTextWidth(idealWidth());
795 \property QTextDocument::size
798 Returns the actual size of the document.
799 This is equivalent to documentLayout()->documentSize();
801 The size of the document can be changed either by setting
802 a text width or setting an entire page size.
804 Note that the width is always >= pageSize().width().
806 By default, for a newly-created, empty document, this property contains
807 a configuration-dependent size.
809 \sa setTextWidth(), setPageSize(), idealWidth()
811 QSizeF QTextDocument::size() const
813 return documentLayout()->documentSize();
817 \property QTextDocument::blockCount
820 Returns the number of text blocks in the document.
822 The value of this property is undefined in documents with tables or frames.
824 By default, if defined, this property contains a value of 1.
825 \sa lineCount(), characterCount()
827 int QTextDocument::blockCount() const
829 Q_D(const QTextDocument);
830 return d->blockMap().numNodes();
837 Returns the number of lines of this document (if the layout supports
838 this). Otherwise, this is identical to the number of blocks.
840 \sa blockCount(), characterCount()
842 int QTextDocument::lineCount() const
844 Q_D(const QTextDocument);
845 return d->blockMap().length(2);
851 Returns the number of characters of this document.
853 \sa blockCount(), characterAt()
855 int QTextDocument::characterCount() const
857 Q_D(const QTextDocument);
864 Returns the character at position \a pos, or a null character if the
865 position is out of range.
869 QChar QTextDocument::characterAt(int pos) const
871 Q_D(const QTextDocument);
872 if (pos < 0 || pos >= d->length())
874 QTextDocumentPrivate::FragmentIterator fragIt = d->find(pos);
875 const QTextFragmentData * const frag = fragIt.value();
876 const int offsetInFragment = qMax(0, pos - fragIt.position());
877 return d->text.at(frag->stringPosition + offsetInFragment);
882 \property QTextDocument::defaultStyleSheet
885 The default style sheet is applied to all newly HTML formatted text that is
886 inserted into the document, for example using setHtml() or QTextCursor::insertHtml().
888 The style sheet needs to be compliant to CSS 2.1 syntax.
890 \b{Note:} Changing the default style sheet does not have any effect to the existing content
893 \sa {Supported HTML Subset}
896 #ifndef QT_NO_CSSPARSER
897 void QTextDocument::setDefaultStyleSheet(const QString &sheet)
900 d->defaultStyleSheet = sheet;
901 QCss::Parser parser(sheet);
902 d->parsedDefaultStyleSheet = QCss::StyleSheet();
903 d->parsedDefaultStyleSheet.origin = QCss::StyleSheetOrigin_UserAgent;
904 parser.parse(&d->parsedDefaultStyleSheet);
907 QString QTextDocument::defaultStyleSheet() const
909 Q_D(const QTextDocument);
910 return d->defaultStyleSheet;
912 #endif // QT_NO_CSSPARSER
915 \fn void QTextDocument::contentsChanged()
917 This signal is emitted whenever the document's content changes; for
918 example, when text is inserted or deleted, or when formatting is applied.
924 \fn void QTextDocument::contentsChange(int position, int charsRemoved, int charsAdded)
926 This signal is emitted whenever the document's content changes; for
927 example, when text is inserted or deleted, or when formatting is applied.
929 Information is provided about the \a position of the character in the
930 document where the change occurred, the number of characters removed
931 (\a charsRemoved), and the number of characters added (\a charsAdded).
933 The signal is emitted before the document's layout manager is notified
934 about the change. This hook allows you to implement syntax highlighting
937 \sa QAbstractTextDocumentLayout::documentChanged(), contentsChanged()
942 \fn QTextDocument::undoAvailable(bool available);
944 This signal is emitted whenever undo operations become available
945 (\a available is true) or unavailable (\a available is false).
947 See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
948 documentation for details.
950 \sa undo(), isUndoRedoEnabled()
954 \fn QTextDocument::redoAvailable(bool available);
956 This signal is emitted whenever redo operations become available
957 (\a available is true) or unavailable (\a available is false).
961 \fn QTextDocument::cursorPositionChanged(const QTextCursor &cursor);
963 This signal is emitted whenever the position of a cursor changed
964 due to an editing operation. The cursor that changed is passed in
965 \a cursor. If you need a signal when the cursor is moved with the
966 arrow keys you can use the \l{QTextEdit::}{cursorPositionChanged()} signal in
971 \fn QTextDocument::blockCountChanged(int newBlockCount);
974 This signal is emitted when the total number of text blocks in the
975 document changes. The value passed in \a newBlockCount is the new
980 \fn QTextDocument::documentLayoutChanged();
983 This signal is emitted when a new document layout is set.
985 \sa setDocumentLayout()
991 Returns true if undo is available; otherwise returns false.
993 \sa isRedoAvailable(), availableUndoSteps()
995 bool QTextDocument::isUndoAvailable() const
997 Q_D(const QTextDocument);
998 return d->isUndoAvailable();
1002 Returns true if redo is available; otherwise returns false.
1004 \sa isUndoAvailable(), availableRedoSteps()
1006 bool QTextDocument::isRedoAvailable() const
1008 Q_D(const QTextDocument);
1009 return d->isRedoAvailable();
1014 Returns the number of available undo steps.
1016 \sa isUndoAvailable()
1018 int QTextDocument::availableUndoSteps() const
1020 Q_D(const QTextDocument);
1021 return d->availableUndoSteps();
1026 Returns the number of available redo steps.
1028 \sa isRedoAvailable()
1030 int QTextDocument::availableRedoSteps() const
1032 Q_D(const QTextDocument);
1033 return d->availableRedoSteps();
1038 Returns the document's revision (if undo is enabled).
1040 The revision is guaranteed to increase when a document that is not
1043 \sa QTextBlock::revision(), isModified()
1045 int QTextDocument::revision() const
1047 Q_D(const QTextDocument);
1054 Sets the document to use the given \a layout. The previous layout
1057 \sa documentLayoutChanged()
1059 void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *layout)
1062 d->setLayout(layout);
1066 Returns the document layout for this document.
1068 QAbstractTextDocumentLayout *QTextDocument::documentLayout() const
1070 Q_D(const QTextDocument);
1072 QTextDocument *that = const_cast<QTextDocument *>(this);
1073 that->d_func()->setLayout(new QTextDocumentLayout(that));
1080 Returns meta information about the document of the type specified by
1083 \sa setMetaInformation()
1085 QString QTextDocument::metaInformation(MetaInformation info) const
1087 Q_D(const QTextDocument);
1098 Sets the document's meta information of the type specified by \a info
1099 to the given \a string.
1101 \sa metaInformation()
1103 void QTextDocument::setMetaInformation(MetaInformation info, const QString &string)
1117 Returns the plain text contained in the document. If you want
1118 formatting information use a QTextCursor instead.
1122 QString QTextDocument::toPlainText() const
1124 Q_D(const QTextDocument);
1125 QString txt = d->plainText();
1127 QChar *uc = txt.data();
1128 QChar *e = uc + txt.size();
1130 for (; uc != e; ++uc) {
1131 switch (uc->unicode()) {
1132 case 0xfdd0: // QTextBeginningOfFrame
1133 case 0xfdd1: // QTextEndOfFrame
1134 case QChar::ParagraphSeparator:
1135 case QChar::LineSeparator:
1136 *uc = QLatin1Char('\n');
1139 *uc = QLatin1Char(' ');
1149 Replaces the entire contents of the document with the given plain
1154 void QTextDocument::setPlainText(const QString &text)
1157 bool previousState = d->isUndoRedoEnabled();
1158 d->enableUndoRedo(false);
1159 d->beginEditBlock();
1161 QTextCursor(this).insertText(text);
1163 d->enableUndoRedo(previousState);
1167 Replaces the entire contents of the document with the given
1168 HTML-formatted text in the \a html string.
1170 The HTML formatting is respected as much as possible; for example,
1171 "<b>bold</b> text" will produce text where the first word has a font
1172 weight that gives it a bold appearance: "\b{bold} text".
1174 \note It is the responsibility of the caller to make sure that the
1175 text is correctly decoded when a QString containing HTML is created
1176 and passed to setHtml().
1178 \sa setPlainText(), {Supported HTML Subset}
1181 #ifndef QT_NO_TEXTHTMLPARSER
1183 void QTextDocument::setHtml(const QString &html)
1186 bool previousState = d->isUndoRedoEnabled();
1187 d->enableUndoRedo(false);
1188 d->beginEditBlock();
1190 QTextHtmlImporter(this, html, QTextHtmlImporter::ImportToDocument).import();
1192 d->enableUndoRedo(previousState);
1195 #endif // QT_NO_TEXTHTMLPARSER
1198 \enum QTextDocument::FindFlag
1200 This enum describes the options available to QTextDocument's find function. The options
1201 can be OR-ed together from the following list:
1203 \value FindBackward Search backwards instead of forwards.
1204 \value FindCaseSensitively By default find works case insensitive. Specifying this option
1205 changes the behaviour to a case sensitive find operation.
1206 \value FindWholeWords Makes find match only complete words.
1210 \enum QTextDocument::MetaInformation
1212 This enum describes the different types of meta information that can be
1213 added to a document.
1215 \value DocumentTitle The title of the document.
1216 \value DocumentUrl The url of the document. The loadResource() function uses
1217 this url as the base when loading relative resources.
1219 \sa metaInformation(), setMetaInformation()
1223 \fn QTextCursor QTextDocument::find(const QString &subString, int position, FindFlags options) const
1227 Finds the next occurrence of the string, \a subString, in the document.
1228 The search starts at the given \a position, and proceeds forwards
1229 through the document unless specified otherwise in the search options.
1230 The \a options control the type of search performed.
1232 Returns a cursor with the match selected if \a subString
1233 was found; otherwise returns a null cursor.
1235 If the \a position is 0 (the default) the search begins from the beginning
1236 of the document; otherwise it begins at the specified position.
1238 QTextCursor QTextDocument::find(const QString &subString, int from, FindFlags options) const
1240 QRegExp expr(subString);
1241 expr.setPatternSyntax(QRegExp::FixedString);
1242 expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
1244 return find(expr, from, options);
1248 \fn QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &cursor, FindFlags options) const
1250 Finds the next occurrence of the string, \a subString, in the document.
1251 The search starts at the position of the given \a cursor, and proceeds
1252 forwards through the document unless specified otherwise in the search
1253 options. The \a options control the type of search performed.
1255 Returns a cursor with the match selected if \a subString was found; otherwise
1256 returns a null cursor.
1258 If the given \a cursor has a selection, the search begins after the
1259 selection; otherwise it begins at the cursor's position.
1261 By default the search is case-sensitive, and can match text anywhere in the
1264 QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &from, FindFlags options) const
1267 if (!from.isNull()) {
1268 if (options & QTextDocument::FindBackward)
1269 pos = from.selectionStart();
1271 pos = from.selectionEnd();
1273 QRegExp expr(subString);
1274 expr.setPatternSyntax(QRegExp::FixedString);
1275 expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
1277 return find(expr, pos, options);
1281 static bool findInBlock(const QTextBlock &block, const QRegExp &expression, int offset,
1282 QTextDocument::FindFlags options, QTextCursor &cursor)
1284 QRegExp expr(expression);
1285 QString text = block.text();
1286 text.replace(QChar::Nbsp, QLatin1Char(' '));
1289 while (offset >=0 && offset <= text.length()) {
1290 idx = (options & QTextDocument::FindBackward) ?
1291 expr.lastIndexIn(text, offset) : expr.indexIn(text, offset);
1295 if (options & QTextDocument::FindWholeWords) {
1296 const int start = idx;
1297 const int end = start + expr.matchedLength();
1298 if ((start != 0 && text.at(start - 1).isLetterOrNumber())
1299 || (end != text.length() && text.at(end).isLetterOrNumber())) {
1300 //if this is not a whole word, continue the search in the string
1301 offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
1306 //we have a hit, return the cursor for that.
1311 cursor = QTextCursor(block.docHandle(), block.position() + idx);
1312 cursor.setPosition(cursor.position() + expr.matchedLength(), QTextCursor::KeepAnchor);
1317 \fn QTextCursor QTextDocument::find(const QRegExp & expr, int position, FindFlags options) const
1321 Finds the next occurrence, matching the regular expression, \a expr, in the document.
1322 The search starts at the given \a position, and proceeds forwards
1323 through the document unless specified otherwise in the search options.
1324 The \a options control the type of search performed. The FindCaseSensitively
1325 option is ignored for this overload, use QRegExp::caseSensitivity instead.
1327 Returns a cursor with the match selected if a match was found; otherwise
1328 returns a null cursor.
1330 If the \a position is 0 (the default) the search begins from the beginning
1331 of the document; otherwise it begins at the specified position.
1333 QTextCursor QTextDocument::find(const QRegExp & expr, int from, FindFlags options) const
1335 Q_D(const QTextDocument);
1338 return QTextCursor();
1341 //the cursor is positioned between characters, so for a backward search
1342 //do not include the character given in the position.
1343 if (options & FindBackward) {
1346 return QTextCursor();
1350 QTextBlock block = d->blocksFind(pos);
1352 if (!(options & FindBackward)) {
1353 int blockOffset = qMax(0, pos - block.position());
1354 while (block.isValid()) {
1355 if (findInBlock(block, expr, blockOffset, options, cursor))
1358 block = block.next();
1361 int blockOffset = pos - block.position();
1362 while (block.isValid()) {
1363 if (findInBlock(block, expr, blockOffset, options, cursor))
1365 block = block.previous();
1366 blockOffset = block.length() - 1;
1370 return QTextCursor();
1374 \fn QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &cursor, FindFlags options) const
1376 Finds the next occurrence, matching the regular expression, \a expr, in the document.
1377 The search starts at the position of the given \a cursor, and proceeds
1378 forwards through the document unless specified otherwise in the search
1379 options. The \a options control the type of search performed. The FindCaseSensitively
1380 option is ignored for this overload, use QRegExp::caseSensitivity instead.
1382 Returns a cursor with the match selected if a match was found; otherwise
1383 returns a null cursor.
1385 If the given \a cursor has a selection, the search begins after the
1386 selection; otherwise it begins at the cursor's position.
1388 By default the search is case-sensitive, and can match text anywhere in the
1391 QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &from, FindFlags options) const
1394 if (!from.isNull()) {
1395 if (options & QTextDocument::FindBackward)
1396 pos = from.selectionStart();
1398 pos = from.selectionEnd();
1400 return find(expr, pos, options);
1405 \fn QTextObject *QTextDocument::createObject(const QTextFormat &format)
1407 Creates and returns a new document object (a QTextObject), based
1408 on the given \a format.
1410 QTextObjects will always get created through this method, so you
1411 must reimplement it if you use custom text objects inside your document.
1413 QTextObject *QTextDocument::createObject(const QTextFormat &f)
1415 QTextObject *obj = 0;
1416 if (f.isListFormat())
1417 obj = new QTextList(this);
1418 else if (f.isTableFormat())
1419 obj = new QTextTable(this);
1420 else if (f.isFrameFormat())
1421 obj = new QTextFrame(this);
1429 Returns the frame that contains the text cursor position \a pos.
1431 QTextFrame *QTextDocument::frameAt(int pos) const
1433 Q_D(const QTextDocument);
1434 return d->frameAt(pos);
1438 Returns the document's root frame.
1440 QTextFrame *QTextDocument::rootFrame() const
1442 Q_D(const QTextDocument);
1443 return d->rootFrame();
1447 Returns the text object associated with the given \a objectIndex.
1449 QTextObject *QTextDocument::object(int objectIndex) const
1451 Q_D(const QTextDocument);
1452 return d->objectForIndex(objectIndex);
1456 Returns the text object associated with the format \a f.
1458 QTextObject *QTextDocument::objectForFormat(const QTextFormat &f) const
1460 Q_D(const QTextDocument);
1461 return d->objectForFormat(f);
1466 Returns the text block that contains the \a{pos}-th character.
1468 QTextBlock QTextDocument::findBlock(int pos) const
1470 Q_D(const QTextDocument);
1471 return QTextBlock(docHandle(), d->blockMap().findNode(pos));
1476 Returns the text block with the specified \a blockNumber.
1478 \sa QTextBlock::blockNumber()
1480 QTextBlock QTextDocument::findBlockByNumber(int blockNumber) const
1482 Q_D(const QTextDocument);
1483 return QTextBlock(docHandle(), d->blockMap().findNode(blockNumber, 1));
1488 Returns the text block that contains the specified \a lineNumber.
1490 \sa QTextBlock::firstLineNumber()
1492 QTextBlock QTextDocument::findBlockByLineNumber(int lineNumber) const
1494 Q_D(const QTextDocument);
1495 return QTextBlock(docHandle(), d->blockMap().findNode(lineNumber, 2));
1499 Returns the document's first text block.
1503 QTextBlock QTextDocument::begin() const
1505 Q_D(const QTextDocument);
1506 return QTextBlock(docHandle(), d->blockMap().begin().n);
1510 This function returns a block to test for the end of the document
1511 while iterating over it.
1513 \snippet textdocumentendsnippet.cpp 0
1515 The block returned is invalid and represents the block after the
1516 last block in the document. You can use lastBlock() to retrieve the
1517 last valid block of the document.
1521 QTextBlock QTextDocument::end() const
1523 return QTextBlock(docHandle(), 0);
1528 Returns the document's first text block.
1530 QTextBlock QTextDocument::firstBlock() const
1532 Q_D(const QTextDocument);
1533 return QTextBlock(docHandle(), d->blockMap().begin().n);
1538 Returns the document's last (valid) text block.
1540 QTextBlock QTextDocument::lastBlock() const
1542 Q_D(const QTextDocument);
1543 return QTextBlock(docHandle(), d->blockMap().last().n);
1547 \property QTextDocument::pageSize
1548 \brief the page size that should be used for laying out the document
1550 By default, for a newly-created, empty document, this property contains
1553 \sa modificationChanged()
1556 void QTextDocument::setPageSize(const QSizeF &size)
1561 d->lout->documentChanged(0, 0, d->length());
1564 QSizeF QTextDocument::pageSize() const
1566 Q_D(const QTextDocument);
1571 returns the number of pages in this document.
1573 int QTextDocument::pageCount() const
1575 return documentLayout()->pageCount();
1579 Sets the default \a font to use in the document layout.
1581 void QTextDocument::setDefaultFont(const QFont &font)
1584 d->setDefaultFont(font);
1586 d->lout->documentChanged(0, 0, d->length());
1590 Returns the default font to be used in the document layout.
1592 QFont QTextDocument::defaultFont() const
1594 Q_D(const QTextDocument);
1595 return d->defaultFont();
1599 \fn QTextDocument::modificationChanged(bool changed)
1601 This signal is emitted whenever the content of the document
1602 changes in a way that affects the modification state. If \a
1603 changed is true, the document has been modified; otherwise it is
1606 For example, calling setModified(false) on a document and then
1607 inserting text causes the signal to get emitted. If you undo that
1608 operation, causing the document to return to its original
1609 unmodified state, the signal will get emitted again.
1613 \property QTextDocument::modified
1614 \brief whether the document has been modified by the user
1616 By default, this property is false.
1618 \sa modificationChanged()
1621 bool QTextDocument::isModified() const
1623 return docHandle()->isModified();
1626 void QTextDocument::setModified(bool m)
1628 docHandle()->setModified(m);
1631 #ifndef QT_NO_PRINTER
1632 static void printPage(int index, QPainter *painter, const QTextDocument *doc, const QRectF &body, const QPointF &pageNumberPos)
1635 painter->translate(body.left(), body.top() - (index - 1) * body.height());
1636 QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
1638 QAbstractTextDocumentLayout *layout = doc->documentLayout();
1639 QAbstractTextDocumentLayout::PaintContext ctx;
1641 painter->setClipRect(view);
1644 // don't use the system palette text as default text color, on HP/UX
1645 // for example that's white, and white text on white paper doesn't
1647 ctx.palette.setColor(QPalette::Text, Qt::black);
1649 layout->draw(painter, ctx);
1651 if (!pageNumberPos.isNull()) {
1652 painter->setClipping(false);
1653 painter->setFont(QFont(doc->defaultFont()));
1654 const QString pageString = QString::number(index);
1656 painter->drawText(qRound(pageNumberPos.x() - painter->fontMetrics().width(pageString)),
1657 qRound(pageNumberPos.y() + view.top()),
1665 Prints the document to the given \a device. The QPageablePaintDevice must be
1666 set up before being used with this function.
1668 This is only a convenience method to print the whole document to the printer.
1670 If the document is already paginated through a specified height in the pageSize()
1671 property it is printed as-is.
1673 If the document is not paginated, like for example a document used in a QTextEdit,
1674 then a temporary copy of the document is created and the copy is broken into
1675 multiple pages according to the size of the paint device's paperRect(). By default
1676 a 2 cm margin is set around the document contents. In addition the current page
1677 number is printed at the bottom of each page.
1679 \sa QTextEdit::print()
1682 void QTextDocument::print(QPagedPaintDevice *printer) const
1684 Q_D(const QTextDocument);
1689 bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
1690 && d->pageSize.height() != INT_MAX;
1692 QPagedPaintDevicePrivate *pd = QPagedPaintDevicePrivate::get(printer);
1694 // ### set page size to paginated size?
1695 QPagedPaintDevice::Margins m = printer->margins();
1696 if (!documentPaginated && m.left == 0. && m.right == 0. && m.top == 0. && m.bottom == 0.) {
1697 m.left = m.right = m.top = m.bottom = 2.;
1698 printer->setMargins(m);
1700 // ### use the margins correctly
1702 QPainter p(printer);
1704 // Check that there is a valid device to print to.
1708 const QTextDocument *doc = this;
1709 QScopedPointer<QTextDocument> clonedDoc;
1710 (void)doc->documentLayout(); // make sure that there is a layout
1712 QRectF body = QRectF(QPointF(0, 0), d->pageSize);
1713 QPointF pageNumberPos;
1715 if (documentPaginated) {
1716 qreal sourceDpiX = qt_defaultDpi();
1717 qreal sourceDpiY = sourceDpiX;
1719 QPaintDevice *dev = doc->documentLayout()->paintDevice();
1721 sourceDpiX = dev->logicalDpiX();
1722 sourceDpiY = dev->logicalDpiY();
1725 const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
1726 const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
1729 p.scale(dpiScaleX, dpiScaleY);
1731 QSizeF scaledPageSize = d->pageSize;
1732 scaledPageSize.rwidth() *= dpiScaleX;
1733 scaledPageSize.rheight() *= dpiScaleY;
1735 const QSizeF printerPageSize(printer->width(), printer->height());
1738 p.scale(printerPageSize.width() / scaledPageSize.width(),
1739 printerPageSize.height() / scaledPageSize.height());
1741 doc = clone(const_cast<QTextDocument *>(this));
1742 clonedDoc.reset(const_cast<QTextDocument *>(doc));
1744 for (QTextBlock srcBlock = firstBlock(), dstBlock = clonedDoc->firstBlock();
1745 srcBlock.isValid() && dstBlock.isValid();
1746 srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
1747 dstBlock.layout()->setAdditionalFormats(srcBlock.layout()->additionalFormats());
1750 QAbstractTextDocumentLayout *layout = doc->documentLayout();
1751 layout->setPaintDevice(p.device());
1753 // copy the custom object handlers
1754 layout->d_func()->handlers = documentLayout()->d_func()->handlers;
1756 int dpiy = p.device()->logicalDpiY();
1757 int margin = (int) ((2/2.54)*dpiy); // 2 cm margins
1758 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
1759 fmt.setMargin(margin);
1760 doc->rootFrame()->setFrameFormat(fmt);
1762 body = QRectF(0, 0, printer->width(), printer->height());
1763 pageNumberPos = QPointF(body.width() - margin,
1764 body.height() - margin
1765 + QFontMetrics(doc->defaultFont(), p.device()).ascent()
1767 clonedDoc->setPageSize(body.size());
1770 int fromPage = pd->fromPage;
1771 int toPage = pd->toPage;
1772 bool ascending = true;
1774 if (fromPage == 0 && toPage == 0) {
1776 toPage = doc->pageCount();
1779 fromPage = qMax(1, fromPage);
1780 toPage = qMin(doc->pageCount(), toPage);
1782 if (toPage < fromPage) {
1783 // if the user entered a page range outside the actual number
1784 // of printable pages, just return
1788 // if (printer->pageOrder() == QPrinter::LastPageFirst) {
1789 // int tmp = fromPage;
1790 // fromPage = toPage;
1792 // ascending = false;
1795 int page = fromPage;
1797 printPage(page, &p, doc, body, pageNumberPos);
1807 if (!printer->newPage())
1814 \enum QTextDocument::ResourceType
1816 This enum describes the types of resources that can be loaded by
1817 QTextDocument's loadResource() function.
1819 \value HtmlResource The resource contains HTML.
1820 \value ImageResource The resource contains image data.
1821 Currently supported data types are QVariant::Pixmap and
1822 QVariant::Image. If the corresponding variant is of type
1823 QVariant::ByteArray then Qt attempts to load the image using
1824 QImage::loadFromData. QVariant::Icon is currently not supported.
1825 The icon needs to be converted to one of the supported types first,
1826 for example using QIcon::pixmap.
1827 \value StyleSheetResource The resource contains CSS.
1828 \value UserResource The first available value for user defined
1835 Returns data of the specified \a type from the resource with the
1838 This function is called by the rich text engine to request data that isn't
1839 directly stored by QTextDocument, but still associated with it. For example,
1840 images are referenced indirectly by the name attribute of a QTextImageFormat
1843 Resources are cached internally in the document. If a resource can
1844 not be found in the cache, loadResource is called to try to load
1845 the resource. loadResource should then use addResource to add the
1846 resource to the cache.
1848 \sa QTextDocument::ResourceType
1850 QVariant QTextDocument::resource(int type, const QUrl &name) const
1852 Q_D(const QTextDocument);
1853 QVariant r = d->resources.value(name);
1855 r = d->cachedResources.value(name);
1857 r = const_cast<QTextDocument *>(this)->loadResource(type, name);
1863 Adds the resource \a resource to the resource cache, using \a
1864 type and \a name as identifiers. \a type should be a value from
1865 QTextDocument::ResourceType.
1867 For example, you can add an image as a resource in order to reference it
1868 from within the document:
1870 \snippet textdocument-resources/main.cpp Adding a resource
1872 The image can be inserted into the document using the QTextCursor API:
1874 \snippet textdocument-resources/main.cpp Inserting an image with a cursor
1876 Alternatively, you can insert images using the HTML \c img tag:
1878 \snippet textdocument-resources/main.cpp Inserting an image using HTML
1880 void QTextDocument::addResource(int type, const QUrl &name, const QVariant &resource)
1884 d->resources.insert(name, resource);
1888 Loads data of the specified \a type from the resource with the
1891 This function is called by the rich text engine to request data that isn't
1892 directly stored by QTextDocument, but still associated with it. For example,
1893 images are referenced indirectly by the name attribute of a QTextImageFormat
1896 When called by Qt, \a type is one of the values of
1897 QTextDocument::ResourceType.
1899 If the QTextDocument is a child object of a QObject that has an invokable
1900 loadResource method such as QTextEdit, QTextBrowser
1901 or a QTextDocument itself then the default implementation tries
1902 to retrieve the data from the parent.
1904 QVariant QTextDocument::loadResource(int type, const QUrl &name)
1909 QObject *p = parent();
1911 const QMetaObject *me = p->metaObject();
1912 int index = me->indexOfMethod("loadResource(int,QUrl)");
1914 QMetaMethod loader = me->method(index);
1915 loader.invoke(p, Q_RETURN_ARG(QVariant, r), Q_ARG(int, type), Q_ARG(QUrl, name));
1919 // handle data: URLs
1920 if (r.isNull() && name.scheme().compare(QLatin1String("data"), Qt::CaseInsensitive) == 0) {
1923 if (qDecodeDataUrl(name, mimetype, payload))
1927 // if resource was not loaded try to load it here
1928 if (!qobject_cast<QTextDocument *>(p) && r.isNull() && name.isRelative()) {
1929 QUrl currentURL = d->url;
1930 QUrl resourceUrl = name;
1932 // For the second case QUrl can merge "#someanchor" with "foo.html"
1933 // correctly to "foo.html#someanchor"
1934 if (!(currentURL.isRelative()
1935 || (currentURL.scheme() == QLatin1String("file")
1936 && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
1937 || (name.hasFragment() && name.path().isEmpty())) {
1938 resourceUrl = currentURL.resolved(name);
1940 // this is our last resort when current url and new url are both relative
1941 // we try to resolve against the current working directory in the local
1943 QFileInfo fi(currentURL.toLocalFile());
1946 QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
1947 } else if (currentURL.isEmpty()) {
1948 resourceUrl.setScheme(QLatin1String("file"));
1952 QString s = resourceUrl.toLocalFile();
1954 if (!s.isEmpty() && f.open(QFile::ReadOnly)) {
1961 if (type == ImageResource && r.type() == QVariant::ByteArray) {
1962 if (qApp->thread() != QThread::currentThread()) {
1963 // must use images in non-GUI threads
1965 image.loadFromData(r.toByteArray());
1966 if (!image.isNull())
1970 pm.loadFromData(r.toByteArray());
1975 d->cachedResources.insert(name, r);
1980 static QTextFormat formatDifference(const QTextFormat &from, const QTextFormat &to)
1982 QTextFormat diff = to;
1984 const QMap<int, QVariant> props = to.properties();
1985 for (QMap<int, QVariant>::ConstIterator it = props.begin(), end = props.end();
1987 if (it.value() == from.property(it.key()))
1988 diff.clearProperty(it.key());
1993 QTextHtmlExporter::QTextHtmlExporter(const QTextDocument *_doc)
1994 : doc(_doc), fragmentMarkers(false)
1996 const QFont defaultFont = doc->defaultFont();
1997 defaultCharFormat.setFont(defaultFont);
1998 // don't export those for the default font since we cannot turn them off with CSS
1999 defaultCharFormat.clearProperty(QTextFormat::FontUnderline);
2000 defaultCharFormat.clearProperty(QTextFormat::FontOverline);
2001 defaultCharFormat.clearProperty(QTextFormat::FontStrikeOut);
2002 defaultCharFormat.clearProperty(QTextFormat::TextUnderlineStyle);
2006 Returns the document in HTML format. The conversion may not be
2007 perfect, especially for complex documents, due to the limitations
2010 QString QTextHtmlExporter::toHtml(const QByteArray &encoding, ExportMode mode)
2012 html = QLatin1String("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
2013 "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
2014 "<html><head><meta name=\"qrichtext\" content=\"1\" />");
2015 html.reserve(doc->docHandle()->length());
2017 fragmentMarkers = (mode == ExportFragment);
2019 if (!encoding.isEmpty())
2020 html += QString::fromLatin1("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\" />").arg(QString::fromLatin1(encoding));
2022 QString title = doc->metaInformation(QTextDocument::DocumentTitle);
2023 if (!title.isEmpty())
2024 html += QString::fromLatin1("<title>") + title + QString::fromLatin1("</title>");
2025 html += QLatin1String("<style type=\"text/css\">\n");
2026 html += QLatin1String("p, li { white-space: pre-wrap; }\n");
2027 html += QLatin1String("</style>");
2028 html += QLatin1String("</head><body");
2030 if (mode == ExportEntireDocument) {
2031 html += QLatin1String(" style=\"");
2033 emitFontFamily(defaultCharFormat.fontFamily());
2035 if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) {
2036 html += QLatin1String(" font-size:");
2037 html += QString::number(defaultCharFormat.fontPointSize());
2038 html += QLatin1String("pt;");
2039 } else if (defaultCharFormat.hasProperty(QTextFormat::FontPixelSize)) {
2040 html += QLatin1String(" font-size:");
2041 html += QString::number(defaultCharFormat.intProperty(QTextFormat::FontPixelSize));
2042 html += QLatin1String("px;");
2045 html += QLatin1String(" font-weight:");
2046 html += QString::number(defaultCharFormat.fontWeight() * 8);
2047 html += QLatin1Char(';');
2049 html += QLatin1String(" font-style:");
2050 html += (defaultCharFormat.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2051 html += QLatin1Char(';');
2053 // do not set text-decoration on the default font since those values are /always/ propagated
2054 // and cannot be turned off with CSS
2056 html += QLatin1Char('\"');
2058 const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2059 emitBackgroundAttribute(fmt);
2062 defaultCharFormat = QTextCharFormat();
2064 html += QLatin1Char('>');
2066 QTextFrameFormat rootFmt = doc->rootFrame()->frameFormat();
2067 rootFmt.clearProperty(QTextFormat::BackgroundBrush);
2069 QTextFrameFormat defaultFmt;
2070 defaultFmt.setMargin(doc->documentMargin());
2072 if (rootFmt == defaultFmt)
2073 emitFrame(doc->rootFrame()->begin());
2075 emitTextFrame(doc->rootFrame());
2077 html += QLatin1String("</body></html>");
2081 void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &value)
2083 html += QLatin1Char(' ');
2084 html += QLatin1String(attribute);
2085 html += QLatin1String("=\"");
2086 html += value.toHtmlEscaped();
2087 html += QLatin1Char('"');
2090 bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
2092 bool attributesEmitted = false;
2095 const QString family = format.fontFamily();
2096 if (!family.isEmpty() && family != defaultCharFormat.fontFamily()) {
2097 emitFontFamily(family);
2098 attributesEmitted = true;
2102 if (format.hasProperty(QTextFormat::FontPointSize)
2103 && format.fontPointSize() != defaultCharFormat.fontPointSize()) {
2104 html += QLatin1String(" font-size:");
2105 html += QString::number(format.fontPointSize());
2106 html += QLatin1String("pt;");
2107 attributesEmitted = true;
2108 } else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) {
2109 static const char * const sizeNames[] = {
2110 "small", "medium", "large", "x-large", "xx-large"
2112 const char *name = 0;
2113 const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1;
2114 if (idx >= 0 && idx <= 4) {
2115 name = sizeNames[idx];
2118 html += QLatin1String(" font-size:");
2119 html += QLatin1String(name);
2120 html += QLatin1Char(';');
2121 attributesEmitted = true;
2123 } else if (format.hasProperty(QTextFormat::FontPixelSize)) {
2124 html += QLatin1String(" font-size:");
2125 html += QString::number(format.intProperty(QTextFormat::FontPixelSize));
2126 html += QLatin1String("px;");
2129 if (format.hasProperty(QTextFormat::FontWeight)
2130 && format.fontWeight() != defaultCharFormat.fontWeight()) {
2131 html += QLatin1String(" font-weight:");
2132 html += QString::number(format.fontWeight() * 8);
2133 html += QLatin1Char(';');
2134 attributesEmitted = true;
2137 if (format.hasProperty(QTextFormat::FontItalic)
2138 && format.fontItalic() != defaultCharFormat.fontItalic()) {
2139 html += QLatin1String(" font-style:");
2140 html += (format.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2141 html += QLatin1Char(';');
2142 attributesEmitted = true;
2145 QLatin1String decorationTag(" text-decoration:");
2146 html += decorationTag;
2147 bool hasDecoration = false;
2148 bool atLeastOneDecorationSet = false;
2150 if ((format.hasProperty(QTextFormat::FontUnderline) || format.hasProperty(QTextFormat::TextUnderlineStyle))
2151 && format.fontUnderline() != defaultCharFormat.fontUnderline()) {
2152 hasDecoration = true;
2153 if (format.fontUnderline()) {
2154 html += QLatin1String(" underline");
2155 atLeastOneDecorationSet = true;
2159 if (format.hasProperty(QTextFormat::FontOverline)
2160 && format.fontOverline() != defaultCharFormat.fontOverline()) {
2161 hasDecoration = true;
2162 if (format.fontOverline()) {
2163 html += QLatin1String(" overline");
2164 atLeastOneDecorationSet = true;
2168 if (format.hasProperty(QTextFormat::FontStrikeOut)
2169 && format.fontStrikeOut() != defaultCharFormat.fontStrikeOut()) {
2170 hasDecoration = true;
2171 if (format.fontStrikeOut()) {
2172 html += QLatin1String(" line-through");
2173 atLeastOneDecorationSet = true;
2177 if (hasDecoration) {
2178 if (!atLeastOneDecorationSet)
2179 html += QLatin1String("none");
2180 html += QLatin1Char(';');
2181 attributesEmitted = true;
2183 html.chop(qstrlen(decorationTag.latin1()));
2186 if (format.foreground() != defaultCharFormat.foreground()
2187 && format.foreground().style() != Qt::NoBrush) {
2188 html += QLatin1String(" color:");
2189 html += format.foreground().color().name();
2190 html += QLatin1Char(';');
2191 attributesEmitted = true;
2194 if (format.background() != defaultCharFormat.background()
2195 && format.background().style() == Qt::SolidPattern) {
2196 html += QLatin1String(" background-color:");
2197 html += format.background().color().name();
2198 html += QLatin1Char(';');
2199 attributesEmitted = true;
2202 if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()
2203 && format.verticalAlignment() != QTextCharFormat::AlignNormal)
2205 html += QLatin1String(" vertical-align:");
2207 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2208 if (valign == QTextCharFormat::AlignSubScript)
2209 html += QLatin1String("sub");
2210 else if (valign == QTextCharFormat::AlignSuperScript)
2211 html += QLatin1String("super");
2212 else if (valign == QTextCharFormat::AlignMiddle)
2213 html += QLatin1String("middle");
2214 else if (valign == QTextCharFormat::AlignTop)
2215 html += QLatin1String("top");
2216 else if (valign == QTextCharFormat::AlignBottom)
2217 html += QLatin1String("bottom");
2219 html += QLatin1Char(';');
2220 attributesEmitted = true;
2223 if (format.fontCapitalization() != QFont::MixedCase) {
2224 const QFont::Capitalization caps = format.fontCapitalization();
2225 if (caps == QFont::AllUppercase)
2226 html += QLatin1String(" text-transform:uppercase;");
2227 else if (caps == QFont::AllLowercase)
2228 html += QLatin1String(" text-transform:lowercase;");
2229 else if (caps == QFont::SmallCaps)
2230 html += QLatin1String(" font-variant:small-caps;");
2231 attributesEmitted = true;
2234 if (format.fontWordSpacing() != 0.0) {
2235 html += QLatin1String(" word-spacing:");
2236 html += QString::number(format.fontWordSpacing());
2237 html += QLatin1String("px;");
2238 attributesEmitted = true;
2241 return attributesEmitted;
2244 void QTextHtmlExporter::emitTextLength(const char *attribute, const QTextLength &length)
2246 if (length.type() == QTextLength::VariableLength) // default
2249 html += QLatin1Char(' ');
2250 html += QLatin1String(attribute);
2251 html += QLatin1String("=\"");
2252 html += QString::number(length.rawValue());
2254 if (length.type() == QTextLength::PercentageLength)
2255 html += QLatin1String("%\"");
2257 html += QLatin1Char('\"');
2260 void QTextHtmlExporter::emitAlignment(Qt::Alignment align)
2262 if (align & Qt::AlignLeft)
2264 else if (align & Qt::AlignRight)
2265 html += QLatin1String(" align=\"right\"");
2266 else if (align & Qt::AlignHCenter)
2267 html += QLatin1String(" align=\"center\"");
2268 else if (align & Qt::AlignJustify)
2269 html += QLatin1String(" align=\"justify\"");
2272 void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
2274 if (pos == QTextFrameFormat::InFlow)
2277 if (mode == EmitStyleTag)
2278 html += QLatin1String(" style=\"float:");
2280 html += QLatin1String(" float:");
2282 if (pos == QTextFrameFormat::FloatLeft)
2283 html += QLatin1String(" left;");
2284 else if (pos == QTextFrameFormat::FloatRight)
2285 html += QLatin1String(" right;");
2287 Q_ASSERT_X(0, "QTextHtmlExporter::emitFloatStyle()", "pos should be a valid enum type");
2289 if (mode == EmitStyleTag)
2290 html += QLatin1Char('\"');
2293 void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style)
2295 Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
2297 html += QLatin1String(" border-style:");
2300 case QTextFrameFormat::BorderStyle_None:
2301 html += QLatin1String("none");
2303 case QTextFrameFormat::BorderStyle_Dotted:
2304 html += QLatin1String("dotted");
2306 case QTextFrameFormat::BorderStyle_Dashed:
2307 html += QLatin1String("dashed");
2309 case QTextFrameFormat::BorderStyle_Solid:
2310 html += QLatin1String("solid");
2312 case QTextFrameFormat::BorderStyle_Double:
2313 html += QLatin1String("double");
2315 case QTextFrameFormat::BorderStyle_DotDash:
2316 html += QLatin1String("dot-dash");
2318 case QTextFrameFormat::BorderStyle_DotDotDash:
2319 html += QLatin1String("dot-dot-dash");
2321 case QTextFrameFormat::BorderStyle_Groove:
2322 html += QLatin1String("groove");
2324 case QTextFrameFormat::BorderStyle_Ridge:
2325 html += QLatin1String("ridge");
2327 case QTextFrameFormat::BorderStyle_Inset:
2328 html += QLatin1String("inset");
2330 case QTextFrameFormat::BorderStyle_Outset:
2331 html += QLatin1String("outset");
2338 html += QLatin1Char(';');
2341 void QTextHtmlExporter::emitPageBreakPolicy(QTextFormat::PageBreakFlags policy)
2343 if (policy & QTextFormat::PageBreak_AlwaysBefore)
2344 html += QLatin1String(" page-break-before:always;");
2346 if (policy & QTextFormat::PageBreak_AlwaysAfter)
2347 html += QLatin1String(" page-break-after:always;");
2350 void QTextHtmlExporter::emitFontFamily(const QString &family)
2352 html += QLatin1String(" font-family:");
2354 QLatin1String quote("\'");
2355 if (family.contains(QLatin1Char('\'')))
2356 quote = QLatin1String(""");
2359 html += family.toHtmlEscaped();
2361 html += QLatin1Char(';');
2364 void QTextHtmlExporter::emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right)
2366 html += QLatin1String(" margin-top:");
2368 html += QLatin1String("px;");
2370 html += QLatin1String(" margin-bottom:");
2372 html += QLatin1String("px;");
2374 html += QLatin1String(" margin-left:");
2376 html += QLatin1String("px;");
2378 html += QLatin1String(" margin-right:");
2380 html += QLatin1String("px;");
2383 void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
2385 const QTextCharFormat format = fragment.charFormat();
2387 bool closeAnchor = false;
2389 if (format.isAnchor()) {
2390 const QString name = format.anchorName();
2391 if (!name.isEmpty()) {
2392 html += QLatin1String("<a name=\"");
2393 html += name.toHtmlEscaped();
2394 html += QLatin1String("\"></a>");
2396 const QString href = format.anchorHref();
2397 if (!href.isEmpty()) {
2398 html += QLatin1String("<a href=\"");
2399 html += href.toHtmlEscaped();
2400 html += QLatin1String("\">");
2405 QString txt = fragment.text();
2406 const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
2407 const bool isImage = isObject && format.isImageFormat();
2409 QLatin1String styleTag("<span style=\"");
2412 bool attributesEmitted = false;
2414 attributesEmitted = emitCharFormatStyle(format);
2415 if (attributesEmitted)
2416 html += QLatin1String("\">");
2418 html.chop(qstrlen(styleTag.latin1()));
2421 for (int i = 0; isImage && i < txt.length(); ++i) {
2422 QTextImageFormat imgFmt = format.toImageFormat();
2424 html += QLatin1String("<img");
2426 if (imgFmt.hasProperty(QTextFormat::ImageName))
2427 emitAttribute("src", imgFmt.name());
2429 if (imgFmt.hasProperty(QTextFormat::ImageWidth))
2430 emitAttribute("width", QString::number(imgFmt.width()));
2432 if (imgFmt.hasProperty(QTextFormat::ImageHeight))
2433 emitAttribute("height", QString::number(imgFmt.height()));
2435 if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
2436 html += QLatin1String(" style=\"vertical-align: middle;\"");
2437 else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
2438 html += QLatin1String(" style=\"vertical-align: top;\"");
2440 if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
2441 emitFloatStyle(imageFrame->frameFormat().position());
2443 html += QLatin1String(" />");
2446 Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
2448 txt = txt.toHtmlEscaped();
2450 // split for [\n{LineSeparator}]
2451 QString forcedLineBreakRegExp = QString::fromLatin1("[\\na]");
2452 forcedLineBreakRegExp[3] = QChar::LineSeparator;
2454 const QStringList lines = txt.split(QRegExp(forcedLineBreakRegExp));
2455 for (int i = 0; i < lines.count(); ++i) {
2457 html += QLatin1String("<br />"); // space on purpose for compatibility with Netscape, Lynx & Co.
2458 html += lines.at(i);
2462 if (attributesEmitted)
2463 html += QLatin1String("</span>");
2466 html += QLatin1String("</a>");
2469 static bool isOrderedList(int style)
2471 return style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
2472 || style == QTextListFormat::ListUpperAlpha
2473 || style == QTextListFormat::ListUpperRoman
2474 || style == QTextListFormat::ListLowerRoman
2478 void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
2480 QTextBlockFormat format = block.blockFormat();
2481 emitAlignment(format.alignment());
2483 // assume default to not bloat the html too much
2484 // html += QLatin1String(" dir='ltr'");
2485 if (block.textDirection() == Qt::RightToLeft)
2486 html += QLatin1String(" dir='rtl'");
2488 QLatin1String style(" style=\"");
2491 const bool emptyBlock = block.begin().atEnd();
2493 html += QLatin1String("-qt-paragraph-type:empty;");
2496 emitMargins(QString::number(format.topMargin()),
2497 QString::number(format.bottomMargin()),
2498 QString::number(format.leftMargin()),
2499 QString::number(format.rightMargin()));
2501 html += QLatin1String(" -qt-block-indent:");
2502 html += QString::number(format.indent());
2503 html += QLatin1Char(';');
2505 html += QLatin1String(" text-indent:");
2506 html += QString::number(format.textIndent());
2507 html += QLatin1String("px;");
2509 if (block.userState() != -1) {
2510 html += QLatin1String(" -qt-user-state:");
2511 html += QString::number(block.userState());
2512 html += QLatin1Char(';');
2515 emitPageBreakPolicy(format.pageBreakPolicy());
2517 QTextCharFormat diff;
2518 if (emptyBlock) { // only print character properties when we don't expect them to be repeated by actual text in the parag
2519 const QTextCharFormat blockCharFmt = block.charFormat();
2520 diff = formatDifference(defaultCharFormat, blockCharFmt).toCharFormat();
2523 diff.clearProperty(QTextFormat::BackgroundBrush);
2524 if (format.hasProperty(QTextFormat::BackgroundBrush)) {
2525 QBrush bg = format.background();
2526 if (bg.style() != Qt::NoBrush)
2527 diff.setProperty(QTextFormat::BackgroundBrush, format.property(QTextFormat::BackgroundBrush));
2530 if (!diff.properties().isEmpty())
2531 emitCharFormatStyle(diff);
2533 html += QLatin1Char('"');
2537 void QTextHtmlExporter::emitBlock(const QTextBlock &block)
2539 if (block.begin().atEnd()) {
2540 // ### HACK, remove once QTextFrame::Iterator is fixed
2541 int p = block.position();
2544 QTextDocumentPrivate::FragmentIterator frag = doc->docHandle()->find(p);
2545 QChar ch = doc->docHandle()->buffer().at(frag->stringPosition);
2546 if (ch == QTextBeginningOfFrame
2547 || ch == QTextEndOfFrame)
2551 html += QLatin1Char('\n');
2553 // save and later restore, in case we 'change' the default format by
2554 // emitting block char format information
2555 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2557 QTextList *list = block.textList();
2559 if (list->itemNumber(block) == 0) { // first item? emit <ul> or appropriate
2560 const QTextListFormat format = list->format();
2561 const int style = format.style();
2563 case QTextListFormat::ListDecimal: html += QLatin1String("<ol"); break;
2564 case QTextListFormat::ListDisc: html += QLatin1String("<ul"); break;
2565 case QTextListFormat::ListCircle: html += QLatin1String("<ul type=\"circle\""); break;
2566 case QTextListFormat::ListSquare: html += QLatin1String("<ul type=\"square\""); break;
2567 case QTextListFormat::ListLowerAlpha: html += QLatin1String("<ol type=\"a\""); break;
2568 case QTextListFormat::ListUpperAlpha: html += QLatin1String("<ol type=\"A\""); break;
2569 case QTextListFormat::ListLowerRoman: html += QLatin1String("<ol type=\"i\""); break;
2570 case QTextListFormat::ListUpperRoman: html += QLatin1String("<ol type=\"I\""); break;
2571 default: html += QLatin1String("<ul"); // ### should not happen
2574 QString styleString = QString::fromLatin1("margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;");
2576 if (format.hasProperty(QTextFormat::ListIndent)) {
2577 styleString += QLatin1String(" -qt-list-indent: ");
2578 styleString += QString::number(format.indent());
2579 styleString += QLatin1Char(';');
2582 if (format.hasProperty(QTextFormat::ListNumberPrefix)) {
2583 QString numberPrefix = format.numberPrefix();
2584 numberPrefix.replace(QLatin1Char('"'), QLatin1String("\\22"));
2585 numberPrefix.replace(QLatin1Char('\''), QLatin1String("\\27")); // FIXME: There's a problem in the CSS parser the prevents this from being correctly restored
2586 styleString += QLatin1String(" -qt-list-number-prefix: ");
2587 styleString += QLatin1Char('\'');
2588 styleString += numberPrefix;
2589 styleString += QLatin1Char('\'');
2590 styleString += QLatin1Char(';');
2593 if (format.hasProperty(QTextFormat::ListNumberSuffix)) {
2594 if (format.numberSuffix() != QLatin1String(".")) { // this is our default
2595 QString numberSuffix = format.numberSuffix();
2596 numberSuffix.replace(QLatin1Char('"'), QLatin1String("\\22"));
2597 numberSuffix.replace(QLatin1Char('\''), QLatin1String("\\27")); // see above
2598 styleString += QLatin1String(" -qt-list-number-suffix: ");
2599 styleString += QLatin1Char('\'');
2600 styleString += numberSuffix;
2601 styleString += QLatin1Char('\'');
2602 styleString += QLatin1Char(';');
2606 html += QLatin1String(" style=\"");
2607 html += styleString;
2608 html += QLatin1String("\">");
2611 html += QLatin1String("<li");
2613 const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
2614 if (!blockFmt.properties().isEmpty()) {
2615 html += QLatin1String(" style=\"");
2616 emitCharFormatStyle(blockFmt);
2617 html += QLatin1Char('\"');
2619 defaultCharFormat.merge(block.charFormat());
2623 const QTextBlockFormat blockFormat = block.blockFormat();
2624 if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
2625 html += QLatin1String("<hr");
2627 QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
2628 if (width.type() != QTextLength::VariableLength)
2629 emitTextLength("width", width);
2631 html += QLatin1Char(' ');
2633 html += QLatin1String("/>");
2637 const bool pre = blockFormat.nonBreakableLines();
2640 html += QLatin1Char('>');
2641 html += QLatin1String("<pre");
2643 html += QLatin1String("<p");
2646 emitBlockAttributes(block);
2648 html += QLatin1Char('>');
2649 if (block.begin().atEnd())
2650 html += QLatin1String("<br />");
2652 QTextBlock::Iterator it = block.begin();
2653 if (fragmentMarkers && !it.atEnd() && block == doc->begin())
2654 html += QLatin1String("<!--StartFragment-->");
2656 for (; !it.atEnd(); ++it)
2657 emitFragment(it.fragment());
2659 if (fragmentMarkers && block.position() + block.length() == doc->docHandle()->length())
2660 html += QLatin1String("<!--EndFragment-->");
2663 html += QLatin1String("</pre>");
2665 html += QLatin1String("</li>");
2667 html += QLatin1String("</p>");
2670 if (list->itemNumber(block) == list->count() - 1) { // last item? close list
2671 if (isOrderedList(list->format().style()))
2672 html += QLatin1String("</ol>");
2674 html += QLatin1String("</ul>");
2678 defaultCharFormat = oldDefaultCharFormat;
2681 extern bool qHasPixmapTexture(const QBrush& brush);
2683 QString QTextHtmlExporter::findUrlForImage(const QTextDocument *doc, qint64 cacheKey, bool isPixmap)
2689 if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent()))
2690 return findUrlForImage(parent, cacheKey, isPixmap);
2692 if (doc && doc->docHandle()) {
2693 QTextDocumentPrivate *priv = doc->docHandle();
2694 QMap<QUrl, QVariant>::const_iterator it = priv->cachedResources.constBegin();
2695 for (; it != priv->cachedResources.constEnd(); ++it) {
2697 const QVariant &v = it.value();
2698 if (v.type() == QVariant::Image && !isPixmap) {
2699 if (qvariant_cast<QImage>(v).cacheKey() == cacheKey)
2703 if (v.type() == QVariant::Pixmap && isPixmap) {
2704 if (qvariant_cast<QPixmap>(v).cacheKey() == cacheKey)
2709 if (it != priv->cachedResources.constEnd())
2710 url = it.key().toString();
2716 void QTextDocumentPrivate::mergeCachedResources(const QTextDocumentPrivate *priv)
2721 cachedResources.unite(priv->cachedResources);
2724 void QTextHtmlExporter::emitBackgroundAttribute(const QTextFormat &format)
2726 if (format.hasProperty(QTextFormat::BackgroundImageUrl)) {
2727 QString url = format.property(QTextFormat::BackgroundImageUrl).toString();
2728 emitAttribute("background", url);
2730 const QBrush &brush = format.background();
2731 if (brush.style() == Qt::SolidPattern) {
2732 emitAttribute("bgcolor", brush.color().name());
2733 } else if (brush.style() == Qt::TexturePattern) {
2734 const bool isPixmap = qHasPixmapTexture(brush);
2735 const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
2737 const QString url = findUrlForImage(doc, cacheKey, isPixmap);
2740 emitAttribute("background", url);
2745 void QTextHtmlExporter::emitTable(const QTextTable *table)
2747 QTextTableFormat format = table->format();
2749 html += QLatin1String("\n<table");
2751 if (format.hasProperty(QTextFormat::FrameBorder))
2752 emitAttribute("border", QString::number(format.border()));
2754 emitFrameStyle(format, TableFrame);
2756 emitAlignment(format.alignment());
2757 emitTextLength("width", format.width());
2759 if (format.hasProperty(QTextFormat::TableCellSpacing))
2760 emitAttribute("cellspacing", QString::number(format.cellSpacing()));
2761 if (format.hasProperty(QTextFormat::TableCellPadding))
2762 emitAttribute("cellpadding", QString::number(format.cellPadding()));
2764 emitBackgroundAttribute(format);
2766 html += QLatin1Char('>');
2768 const int rows = table->rows();
2769 const int columns = table->columns();
2771 QVector<QTextLength> columnWidths = format.columnWidthConstraints();
2772 if (columnWidths.isEmpty()) {
2773 columnWidths.resize(columns);
2774 columnWidths.fill(QTextLength());
2776 Q_ASSERT(columnWidths.count() == columns);
2778 QVarLengthArray<bool> widthEmittedForColumn(columns);
2779 for (int i = 0; i < columns; ++i)
2780 widthEmittedForColumn[i] = false;
2782 const int headerRowCount = qMin(format.headerRowCount(), rows);
2783 if (headerRowCount > 0)
2784 html += QLatin1String("<thead>");
2786 for (int row = 0; row < rows; ++row) {
2787 html += QLatin1String("\n<tr>");
2789 for (int col = 0; col < columns; ++col) {
2790 const QTextTableCell cell = table->cellAt(row, col);
2793 if (cell.row() != row)
2796 if (cell.column() != col)
2799 html += QLatin1String("\n<td");
2801 if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
2802 emitTextLength("width", columnWidths.at(col));
2803 widthEmittedForColumn[col] = true;
2806 if (cell.columnSpan() > 1)
2807 emitAttribute("colspan", QString::number(cell.columnSpan()));
2809 if (cell.rowSpan() > 1)
2810 emitAttribute("rowspan", QString::number(cell.rowSpan()));
2812 const QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
2813 emitBackgroundAttribute(cellFormat);
2815 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2817 QTextCharFormat::VerticalAlignment valign = cellFormat.verticalAlignment();
2819 QString styleString;
2820 if (valign >= QTextCharFormat::AlignMiddle && valign <= QTextCharFormat::AlignBottom) {
2821 styleString += QLatin1String(" vertical-align:");
2823 case QTextCharFormat::AlignMiddle:
2824 styleString += QLatin1String("middle");
2826 case QTextCharFormat::AlignTop:
2827 styleString += QLatin1String("top");
2829 case QTextCharFormat::AlignBottom:
2830 styleString += QLatin1String("bottom");
2835 styleString += QLatin1Char(';');
2837 QTextCharFormat temp;
2838 temp.setVerticalAlignment(valign);
2839 defaultCharFormat.merge(temp);
2842 if (cellFormat.hasProperty(QTextFormat::TableCellLeftPadding))
2843 styleString += QLatin1String(" padding-left:") + QString::number(cellFormat.leftPadding()) + QLatin1Char(';');
2844 if (cellFormat.hasProperty(QTextFormat::TableCellRightPadding))
2845 styleString += QLatin1String(" padding-right:") + QString::number(cellFormat.rightPadding()) + QLatin1Char(';');
2846 if (cellFormat.hasProperty(QTextFormat::TableCellTopPadding))
2847 styleString += QLatin1String(" padding-top:") + QString::number(cellFormat.topPadding()) + QLatin1Char(';');
2848 if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
2849 styleString += QLatin1String(" padding-bottom:") + QString::number(cellFormat.bottomPadding()) + QLatin1Char(';');
2851 if (!styleString.isEmpty())
2852 html += QLatin1String(" style=\"") + styleString + QLatin1Char('\"');
2854 html += QLatin1Char('>');
2856 emitFrame(cell.begin());
2858 html += QLatin1String("</td>");
2860 defaultCharFormat = oldDefaultCharFormat;
2863 html += QLatin1String("</tr>");
2864 if (headerRowCount > 0 && row == headerRowCount - 1)
2865 html += QLatin1String("</thead>");
2868 html += QLatin1String("</table>");
2871 void QTextHtmlExporter::emitFrame(QTextFrame::Iterator frameIt)
2873 if (!frameIt.atEnd()) {
2874 QTextFrame::Iterator next = frameIt;
2877 && frameIt.currentFrame() == 0
2878 && frameIt.parentFrame() != doc->rootFrame()
2879 && frameIt.currentBlock().begin().atEnd())
2883 for (QTextFrame::Iterator it = frameIt;
2884 !it.atEnd(); ++it) {
2885 if (QTextFrame *f = it.currentFrame()) {
2886 if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
2891 } else if (it.currentBlock().isValid()) {
2892 emitBlock(it.currentBlock());
2897 void QTextHtmlExporter::emitTextFrame(const QTextFrame *f)
2899 FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
2901 html += QLatin1String("\n<table");
2902 QTextFrameFormat format = f->frameFormat();
2904 if (format.hasProperty(QTextFormat::FrameBorder))
2905 emitAttribute("border", QString::number(format.border()));
2907 emitFrameStyle(format, frameType);
2909 emitTextLength("width", format.width());
2910 emitTextLength("height", format.height());
2912 // root frame's bcolor goes in the <body> tag
2913 if (frameType != RootFrame)
2914 emitBackgroundAttribute(format);
2916 html += QLatin1Char('>');
2917 html += QLatin1String("\n<tr>\n<td style=\"border: none;\">");
2918 emitFrame(f->begin());
2919 html += QLatin1String("</td></tr></table>");
2922 void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType frameType)
2924 QLatin1String styleAttribute(" style=\"");
2925 html += styleAttribute;
2926 const int originalHtmlLength = html.length();
2928 if (frameType == TextFrame)
2929 html += QLatin1String("-qt-table-type: frame;");
2930 else if (frameType == RootFrame)
2931 html += QLatin1String("-qt-table-type: root;");
2933 const QTextFrameFormat defaultFormat;
2935 emitFloatStyle(format.position(), OmitStyleTag);
2936 emitPageBreakPolicy(format.pageBreakPolicy());
2938 if (format.borderBrush() != defaultFormat.borderBrush()) {
2939 html += QLatin1String(" border-color:");
2940 html += format.borderBrush().color().name();
2941 html += QLatin1Char(';');
2944 if (format.borderStyle() != defaultFormat.borderStyle())
2945 emitBorderStyle(format.borderStyle());
2947 if (format.hasProperty(QTextFormat::FrameMargin)
2948 || format.hasProperty(QTextFormat::FrameLeftMargin)
2949 || format.hasProperty(QTextFormat::FrameRightMargin)
2950 || format.hasProperty(QTextFormat::FrameTopMargin)
2951 || format.hasProperty(QTextFormat::FrameBottomMargin))
2952 emitMargins(QString::number(format.topMargin()),
2953 QString::number(format.bottomMargin()),
2954 QString::number(format.leftMargin()),
2955 QString::number(format.rightMargin()));
2957 if (html.length() == originalHtmlLength) // nothing emitted?
2958 html.chop(qstrlen(styleAttribute.latin1()));
2960 html += QLatin1Char('\"');
2964 Returns a string containing an HTML representation of the document.
2966 The \a encoding parameter specifies the value for the charset attribute
2967 in the html header. For example if 'utf-8' is specified then the
2968 beginning of the generated html will look like this:
2969 \snippet code/src_gui_text_qtextdocument.cpp 0
2971 If no encoding is specified then no such meta information is generated.
2973 If you later on convert the returned html string into a byte array for
2974 transmission over a network or when saving to disk you should specify
2975 the encoding you're going to use for the conversion to a byte array here.
2977 \sa {Supported HTML Subset}
2979 #ifndef QT_NO_TEXTHTMLPARSER
2980 QString QTextDocument::toHtml(const QByteArray &encoding) const
2982 return QTextHtmlExporter(this).toHtml(encoding);
2984 #endif // QT_NO_TEXTHTMLPARSER
2987 Returns a vector of text formats for all the formats used in the document.
2989 QVector<QTextFormat> QTextDocument::allFormats() const
2991 Q_D(const QTextDocument);
2992 return d->formatCollection()->formats;
2999 So that not all classes have to be friends of each other...
3001 QTextDocumentPrivate *QTextDocument::docHandle() const
3003 Q_D(const QTextDocument);
3004 return const_cast<QTextDocumentPrivate *>(d);
3009 \fn QTextDocument::undoCommandAdded()
3011 This signal is emitted every time a new level of undo is added to the QTextDocument.