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);
221 \brief The QTextDocument class holds formatted text that can be
222 viewed and edited using a QTextEdit.
224 \ingroup richtext-processing
227 QTextDocument is a container for structured rich text documents, providing
228 support for styled text and various types of document elements, such as
229 lists, tables, frames, and images.
230 They can be created for use in a QTextEdit, or used independently.
232 Each document element is described by an associated format object. Each
233 format object is treated as a unique object by QTextDocuments, and can be
234 passed to objectForFormat() to obtain the document element that it is
237 A QTextDocument can be edited programmatically using a QTextCursor, and
238 its contents can be examined by traversing the document structure. The
239 entire document structure is stored as a hierarchy of document elements
240 beneath the root frame, found with the rootFrame() function. Alternatively,
241 if you just want to iterate over the textual contents of the document you
242 can use begin(), end(), and findBlock() to retrieve text blocks that you
243 can examine and iterate over.
245 The layout of a document is determined by the documentLayout();
246 you can create your own QAbstractTextDocumentLayout subclass and
247 set it using setDocumentLayout() if you want to use your own
248 layout logic. The document's title and other meta-information can be
249 obtained by calling the metaInformation() function. For documents that
250 are exposed to users through the QTextEdit class, the document title
251 is also available via the QTextEdit::documentTitle() function.
253 The toPlainText() and toHtml() convenience functions allow you to retrieve the
254 contents of the document as plain text and HTML.
255 The document's text can be searched using the find() functions.
257 Undo/redo of operations performed on the document can be controlled using
258 the setUndoRedoEnabled() function. The undo/redo system can be controlled
259 by an editor widget through the undo() and redo() slots; the document also
260 provides contentsChanged(), undoAvailable(), and redoAvailable() signals
261 that inform connected editor widgets about the state of the undo/redo
262 system. The following are the undo/redo operations of a QTextDocument:
265 \li Insertion or removal of characters. A sequence of insertions or removals
266 within the same text block are regarded as a single undo/redo operation.
267 \li Insertion or removal of text blocks. Sequences of insertion or removals
268 in a single operation (e.g., by selecting and then deleting text) are
269 regarded as a single undo/redo operation.
270 \li Text character format changes.
271 \li Text block format changes.
272 \li Text block group format changes.
275 \sa QTextCursor, QTextEdit, {Rich Text Processing}, {Text Object Example}
279 \property QTextDocument::defaultFont
280 \brief the default font used to display the document's text
284 \property QTextDocument::defaultTextOption
285 \brief the default text option will be set on all \l{QTextLayout}s in the document.
287 When \l{QTextBlock}s are created, the defaultTextOption is set on their
288 QTextLayout. This allows setting global properties for the document such as the
289 default word wrap mode.
293 Constructs an empty QTextDocument with the given \a parent.
295 QTextDocument::QTextDocument(QObject *parent)
296 : QObject(*new QTextDocumentPrivate, parent)
303 Constructs a QTextDocument containing the plain (unformatted) \a text
304 specified, and with the given \a parent.
306 QTextDocument::QTextDocument(const QString &text, QObject *parent)
307 : QObject(*new QTextDocumentPrivate, parent)
311 QTextCursor(this).insertText(text);
317 QTextDocument::QTextDocument(QTextDocumentPrivate &dd, QObject *parent)
318 : QObject(dd, parent)
325 Destroys the document.
327 QTextDocument::~QTextDocument()
333 Creates a new QTextDocument that is a copy of this text document. \a
334 parent is the parent of the returned text document.
336 QTextDocument *QTextDocument::clone(QObject *parent) const
338 Q_D(const QTextDocument);
339 QTextDocument *doc = new QTextDocument(parent);
340 QTextCursor(doc).insertFragment(QTextDocumentFragment(this));
341 doc->rootFrame()->setFrameFormat(rootFrame()->frameFormat());
342 QTextDocumentPrivate *priv = doc->d_func();
343 priv->title = d->title;
345 priv->pageSize = d->pageSize;
346 priv->indentWidth = d->indentWidth;
347 priv->defaultTextOption = d->defaultTextOption;
348 priv->setDefaultFont(d->defaultFont());
349 priv->resources = d->resources;
350 priv->cachedResources.clear();
351 #ifndef QT_NO_CSSPARSER
352 priv->defaultStyleSheet = d->defaultStyleSheet;
353 priv->parsedDefaultStyleSheet = d->parsedDefaultStyleSheet;
359 Returns true if the document is empty; otherwise returns false.
361 bool QTextDocument::isEmpty() const
363 Q_D(const QTextDocument);
364 /* because if we're empty we still have one single paragraph as
365 * one single fragment */
366 return d->length() <= 1;
372 void QTextDocument::clear()
376 d->resources.clear();
382 Undoes the last editing operation on the document if undo is
383 available. The provided \a cursor is positioned at the end of the
384 location where the edition operation was undone.
386 See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
387 documentation for details.
389 \sa undoAvailable(), isUndoRedoEnabled()
391 void QTextDocument::undo(QTextCursor *cursor)
394 const int pos = d->undoRedo(true);
395 if (cursor && pos >= 0) {
396 *cursor = QTextCursor(this);
397 cursor->setPosition(pos);
403 Redoes the last editing operation on the document if \l{QTextDocument::isRedoAvailable()}{redo is available}.
405 The provided \a cursor is positioned at the end of the location where
406 the edition operation was redone.
408 void QTextDocument::redo(QTextCursor *cursor)
411 const int pos = d->undoRedo(false);
412 if (cursor && pos >= 0) {
413 *cursor = QTextCursor(this);
414 cursor->setPosition(pos);
418 /*! \enum QTextDocument::Stacks
420 \value UndoStack The undo stack.
421 \value RedoStack The redo stack.
422 \value UndoAndRedoStacks Both the undo and redo stacks.
427 Clears the stacks specified by \a stacksToClear.
429 This method clears any commands on the undo stack, the redo stack,
430 or both (the default). If commands are cleared, the appropriate
431 signals are emitted, QTextDocument::undoAvailable() or
432 QTextDocument::redoAvailable().
434 \sa QTextDocument::undoAvailable(), QTextDocument::redoAvailable()
436 void QTextDocument::clearUndoRedoStacks(Stacks stacksToClear)
439 d->clearUndoRedoStacks(stacksToClear, true);
446 void QTextDocument::undo()
454 Redoes the last editing operation on the document if \l{QTextDocument::isRedoAvailable()}{redo is available}.
456 void QTextDocument::redo()
465 Appends a custom undo \a item to the undo stack.
467 void QTextDocument::appendUndoItem(QAbstractUndoItem *item)
470 d->appendUndoItem(item);
474 \property QTextDocument::undoRedoEnabled
475 \brief whether undo/redo are enabled for this document
477 This defaults to true. If disabled, the undo stack is cleared and
478 no items will be added to it.
480 void QTextDocument::setUndoRedoEnabled(bool enable)
483 d->enableUndoRedo(enable);
486 bool QTextDocument::isUndoRedoEnabled() const
488 Q_D(const QTextDocument);
489 return d->isUndoRedoEnabled();
493 \property QTextDocument::maximumBlockCount
495 \brief Specifies the limit for blocks in the document.
497 Specifies the maximum number of blocks the document may have. If there are
498 more blocks in the document that specified with this property blocks are removed
499 from the beginning of the document.
501 A negative or zero value specifies that the document may contain an unlimited
504 The default value is 0.
506 Note that setting this property will apply the limit immediately to the document
509 Setting this property also disables the undo redo history.
511 This property is undefined in documents with tables or frames.
513 int QTextDocument::maximumBlockCount() const
515 Q_D(const QTextDocument);
516 return d->maximumBlockCount;
519 void QTextDocument::setMaximumBlockCount(int maximum)
522 d->maximumBlockCount = maximum;
523 d->ensureMaximumBlockCount();
524 setUndoRedoEnabled(false);
530 The default text option is used on all QTextLayout objects in the document.
531 This allows setting global properties for the document such as the default
534 QTextOption QTextDocument::defaultTextOption() const
536 Q_D(const QTextDocument);
537 return d->defaultTextOption;
543 Sets the default text option.
545 void QTextDocument::setDefaultTextOption(const QTextOption &option)
548 d->defaultTextOption = option;
550 d->lout->documentChanged(0, 0, d->length());
556 The default cursor movement style is used by all QTextCursor objects
557 created from the document. The default is Qt::LogicalMoveStyle.
559 Qt::CursorMoveStyle QTextDocument::defaultCursorMoveStyle() const
561 Q_D(const QTextDocument);
562 return d->defaultCursorMoveStyle;
568 Sets the default cursor movement style to the given \a style.
570 void QTextDocument::setDefaultCursorMoveStyle(Qt::CursorMoveStyle style)
573 d->defaultCursorMoveStyle = style;
577 \fn void QTextDocument::markContentsDirty(int position, int length)
579 Marks the contents specified by the given \a position and \a length
580 as "dirty", informing the document that it needs to be laid out
583 void QTextDocument::markContentsDirty(int from, int length)
586 d->documentChange(from, length);
587 if (!d->inContentsChange) {
589 d->lout->documentChanged(d->docChangeFrom, d->docChangeOldLength, d->docChangeLength);
590 d->docChangeFrom = -1;
596 \property QTextDocument::useDesignMetrics
598 \brief whether the document uses design metrics of fonts to improve the accuracy of text layout
600 If this property is set to true, the layout will use design metrics.
601 Otherwise, the metrics of the paint device as set on
602 QAbstractTextDocumentLayout::setPaintDevice() will be used.
604 Using design metrics makes a layout have a width that is no longer dependent on hinting
605 and pixel-rounding. This means that WYSIWYG text layout becomes possible because the width
606 scales much more linearly based on paintdevice metrics than it would otherwise.
608 By default, this property is false.
611 void QTextDocument::setUseDesignMetrics(bool b)
614 if (b == d->defaultTextOption.useDesignMetrics())
616 d->defaultTextOption.setUseDesignMetrics(b);
618 d->lout->documentChanged(0, 0, d->length());
621 bool QTextDocument::useDesignMetrics() const
623 Q_D(const QTextDocument);
624 return d->defaultTextOption.useDesignMetrics();
630 Draws the content of the document with painter \a p, clipped to \a rect.
631 If \a rect is a null rectangle (default) then the document is painted unclipped.
633 void QTextDocument::drawContents(QPainter *p, const QRectF &rect)
636 QAbstractTextDocumentLayout::PaintContext ctx;
637 if (rect.isValid()) {
638 p->setClipRect(rect);
641 documentLayout()->draw(p, ctx);
646 \property QTextDocument::textWidth
649 The text width specifies the preferred width for text in the document. If
650 the text (or content in general) is wider than the specified with it is broken
651 into multiple lines and grows vertically. If the text cannot be broken into multiple
652 lines to fit into the specified text width it will be larger and the size() and the
653 idealWidth() property will reflect that.
655 If the text width is set to -1 then the text will not be broken into multiple lines
656 unless it is enforced through an explicit line break or a new paragraph.
658 The default value is -1.
660 Setting the text width will also set the page height to -1, causing the document to
661 grow or shrink vertically in a continuous way. If you want the document layout to break
662 the text into multiple pages then you have to set the pageSize property instead.
664 \sa size(), idealWidth(), pageSize()
666 void QTextDocument::setTextWidth(qreal width)
669 QSizeF sz = d->pageSize;
675 qreal QTextDocument::textWidth() const
677 Q_D(const QTextDocument);
678 return d->pageSize.width();
684 Returns the ideal width of the text document. The ideal width is the actually used width
685 of the document without optional alignments taken into account. It is always <= size().width().
687 \sa adjustSize(), textWidth
689 qreal QTextDocument::idealWidth() const
691 if (QTextDocumentLayout *lout = qobject_cast<QTextDocumentLayout *>(documentLayout()))
692 return lout->idealWidth();
697 \property QTextDocument::documentMargin
700 The margin around the document. The default is 4.
702 qreal QTextDocument::documentMargin() const
704 Q_D(const QTextDocument);
705 return d->documentMargin;
708 void QTextDocument::setDocumentMargin(qreal margin)
711 if (d->documentMargin != margin) {
712 d->documentMargin = margin;
714 QTextFrame* root = rootFrame();
715 QTextFrameFormat format = root->frameFormat();
716 format.setMargin(margin);
717 root->setFrameFormat(format);
720 d->lout->documentChanged(0, 0, d->length());
726 \property QTextDocument::indentWidth
729 Returns the width used for text list and text block indenting.
731 The indent properties of QTextListFormat and QTextBlockFormat specify
732 multiples of this value. The default indent width is 40.
734 qreal QTextDocument::indentWidth() const
736 Q_D(const QTextDocument);
737 return d->indentWidth;
744 Sets the \a width used for text list and text block indenting.
746 The indent properties of QTextListFormat and QTextBlockFormat specify
747 multiples of this value. The default indent width is 40 .
751 void QTextDocument::setIndentWidth(qreal width)
754 if (d->indentWidth != width) {
755 d->indentWidth = width;
757 d->lout->documentChanged(0, 0, d->length());
767 Adjusts the document to a reasonable size.
769 \sa idealWidth(), textWidth, size
771 void QTextDocument::adjustSize()
773 // Pull this private function in from qglobal.cpp
774 QFont f = defaultFont();
776 int mw = fm.width(QLatin1Char('x')) * 80;
779 QSizeF size = documentLayout()->documentSize();
780 if (size.width() != 0) {
781 w = qt_int_sqrt((uint)(5 * size.height() * size.width() / 3));
782 setTextWidth(qMin(w, mw));
784 size = documentLayout()->documentSize();
785 if (w*3 < 5*size.height()) {
786 w = qt_int_sqrt((uint)(2 * size.height() * size.width()));
787 setTextWidth(qMin(w, mw));
790 setTextWidth(idealWidth());
794 \property QTextDocument::size
797 Returns the actual size of the document.
798 This is equivalent to documentLayout()->documentSize();
800 The size of the document can be changed either by setting
801 a text width or setting an entire page size.
803 Note that the width is always >= pageSize().width().
805 By default, for a newly-created, empty document, this property contains
806 a configuration-dependent size.
808 \sa setTextWidth(), setPageSize(), idealWidth()
810 QSizeF QTextDocument::size() const
812 return documentLayout()->documentSize();
816 \property QTextDocument::blockCount
819 Returns the number of text blocks in the document.
821 The value of this property is undefined in documents with tables or frames.
823 By default, if defined, this property contains a value of 1.
824 \sa lineCount(), characterCount()
826 int QTextDocument::blockCount() const
828 Q_D(const QTextDocument);
829 return d->blockMap().numNodes();
836 Returns the number of lines of this document (if the layout supports
837 this). Otherwise, this is identical to the number of blocks.
839 \sa blockCount(), characterCount()
841 int QTextDocument::lineCount() const
843 Q_D(const QTextDocument);
844 return d->blockMap().length(2);
850 Returns the number of characters of this document.
852 \sa blockCount(), characterAt()
854 int QTextDocument::characterCount() const
856 Q_D(const QTextDocument);
863 Returns the character at position \a pos, or a null character if the
864 position is out of range.
868 QChar QTextDocument::characterAt(int pos) const
870 Q_D(const QTextDocument);
871 if (pos < 0 || pos >= d->length())
873 QTextDocumentPrivate::FragmentIterator fragIt = d->find(pos);
874 const QTextFragmentData * const frag = fragIt.value();
875 const int offsetInFragment = qMax(0, pos - fragIt.position());
876 return d->text.at(frag->stringPosition + offsetInFragment);
881 \property QTextDocument::defaultStyleSheet
884 The default style sheet is applied to all newly HTML formatted text that is
885 inserted into the document, for example using setHtml() or QTextCursor::insertHtml().
887 The style sheet needs to be compliant to CSS 2.1 syntax.
889 \b{Note:} Changing the default style sheet does not have any effect to the existing content
892 \sa {Supported HTML Subset}
895 #ifndef QT_NO_CSSPARSER
896 void QTextDocument::setDefaultStyleSheet(const QString &sheet)
899 d->defaultStyleSheet = sheet;
900 QCss::Parser parser(sheet);
901 d->parsedDefaultStyleSheet = QCss::StyleSheet();
902 d->parsedDefaultStyleSheet.origin = QCss::StyleSheetOrigin_UserAgent;
903 parser.parse(&d->parsedDefaultStyleSheet);
906 QString QTextDocument::defaultStyleSheet() const
908 Q_D(const QTextDocument);
909 return d->defaultStyleSheet;
911 #endif // QT_NO_CSSPARSER
914 \fn void QTextDocument::contentsChanged()
916 This signal is emitted whenever the document's content changes; for
917 example, when text is inserted or deleted, or when formatting is applied.
923 \fn void QTextDocument::contentsChange(int position, int charsRemoved, int charsAdded)
925 This signal is emitted whenever the document's content changes; for
926 example, when text is inserted or deleted, or when formatting is applied.
928 Information is provided about the \a position of the character in the
929 document where the change occurred, the number of characters removed
930 (\a charsRemoved), and the number of characters added (\a charsAdded).
932 The signal is emitted before the document's layout manager is notified
933 about the change. This hook allows you to implement syntax highlighting
936 \sa QAbstractTextDocumentLayout::documentChanged(), contentsChanged()
941 \fn QTextDocument::undoAvailable(bool available);
943 This signal is emitted whenever undo operations become available
944 (\a available is true) or unavailable (\a available is false).
946 See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
947 documentation for details.
949 \sa undo(), isUndoRedoEnabled()
953 \fn QTextDocument::redoAvailable(bool available);
955 This signal is emitted whenever redo operations become available
956 (\a available is true) or unavailable (\a available is false).
960 \fn QTextDocument::cursorPositionChanged(const QTextCursor &cursor);
962 This signal is emitted whenever the position of a cursor changed
963 due to an editing operation. The cursor that changed is passed in
964 \a cursor. If you need a signal when the cursor is moved with the
965 arrow keys you can use the \l{QTextEdit::}{cursorPositionChanged()} signal in
970 \fn QTextDocument::blockCountChanged(int newBlockCount);
973 This signal is emitted when the total number of text blocks in the
974 document changes. The value passed in \a newBlockCount is the new
979 \fn QTextDocument::documentLayoutChanged();
982 This signal is emitted when a new document layout is set.
984 \sa setDocumentLayout()
990 Returns true if undo is available; otherwise returns false.
992 \sa isRedoAvailable(), availableUndoSteps()
994 bool QTextDocument::isUndoAvailable() const
996 Q_D(const QTextDocument);
997 return d->isUndoAvailable();
1001 Returns true if redo is available; otherwise returns false.
1003 \sa isUndoAvailable(), availableRedoSteps()
1005 bool QTextDocument::isRedoAvailable() const
1007 Q_D(const QTextDocument);
1008 return d->isRedoAvailable();
1013 Returns the number of available undo steps.
1015 \sa isUndoAvailable()
1017 int QTextDocument::availableUndoSteps() const
1019 Q_D(const QTextDocument);
1020 return d->availableUndoSteps();
1025 Returns the number of available redo steps.
1027 \sa isRedoAvailable()
1029 int QTextDocument::availableRedoSteps() const
1031 Q_D(const QTextDocument);
1032 return d->availableRedoSteps();
1037 Returns the document's revision (if undo is enabled).
1039 The revision is guaranteed to increase when a document that is not
1042 \sa QTextBlock::revision(), isModified()
1044 int QTextDocument::revision() const
1046 Q_D(const QTextDocument);
1053 Sets the document to use the given \a layout. The previous layout
1056 \sa documentLayoutChanged()
1058 void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *layout)
1061 d->setLayout(layout);
1065 Returns the document layout for this document.
1067 QAbstractTextDocumentLayout *QTextDocument::documentLayout() const
1069 Q_D(const QTextDocument);
1071 QTextDocument *that = const_cast<QTextDocument *>(this);
1072 that->d_func()->setLayout(new QTextDocumentLayout(that));
1079 Returns meta information about the document of the type specified by
1082 \sa setMetaInformation()
1084 QString QTextDocument::metaInformation(MetaInformation info) const
1086 Q_D(const QTextDocument);
1097 Sets the document's meta information of the type specified by \a info
1098 to the given \a string.
1100 \sa metaInformation()
1102 void QTextDocument::setMetaInformation(MetaInformation info, const QString &string)
1116 Returns the plain text contained in the document. If you want
1117 formatting information use a QTextCursor instead.
1121 QString QTextDocument::toPlainText() const
1123 Q_D(const QTextDocument);
1124 QString txt = d->plainText();
1126 QChar *uc = txt.data();
1127 QChar *e = uc + txt.size();
1129 for (; uc != e; ++uc) {
1130 switch (uc->unicode()) {
1131 case 0xfdd0: // QTextBeginningOfFrame
1132 case 0xfdd1: // QTextEndOfFrame
1133 case QChar::ParagraphSeparator:
1134 case QChar::LineSeparator:
1135 *uc = QLatin1Char('\n');
1138 *uc = QLatin1Char(' ');
1148 Replaces the entire contents of the document with the given plain
1153 void QTextDocument::setPlainText(const QString &text)
1156 bool previousState = d->isUndoRedoEnabled();
1157 d->enableUndoRedo(false);
1158 d->beginEditBlock();
1160 QTextCursor(this).insertText(text);
1162 d->enableUndoRedo(previousState);
1166 Replaces the entire contents of the document with the given
1167 HTML-formatted text in the \a html string.
1169 The HTML formatting is respected as much as possible; for example,
1170 "<b>bold</b> text" will produce text where the first word has a font
1171 weight that gives it a bold appearance: "\b{bold} text".
1173 \note It is the responsibility of the caller to make sure that the
1174 text is correctly decoded when a QString containing HTML is created
1175 and passed to setHtml().
1177 \sa setPlainText(), {Supported HTML Subset}
1180 #ifndef QT_NO_TEXTHTMLPARSER
1182 void QTextDocument::setHtml(const QString &html)
1185 bool previousState = d->isUndoRedoEnabled();
1186 d->enableUndoRedo(false);
1187 d->beginEditBlock();
1189 QTextHtmlImporter(this, html, QTextHtmlImporter::ImportToDocument).import();
1191 d->enableUndoRedo(previousState);
1194 #endif // QT_NO_TEXTHTMLPARSER
1197 \enum QTextDocument::FindFlag
1199 This enum describes the options available to QTextDocument's find function. The options
1200 can be OR-ed together from the following list:
1202 \value FindBackward Search backwards instead of forwards.
1203 \value FindCaseSensitively By default find works case insensitive. Specifying this option
1204 changes the behaviour to a case sensitive find operation.
1205 \value FindWholeWords Makes find match only complete words.
1209 \enum QTextDocument::MetaInformation
1211 This enum describes the different types of meta information that can be
1212 added to a document.
1214 \value DocumentTitle The title of the document.
1215 \value DocumentUrl The url of the document. The loadResource() function uses
1216 this url as the base when loading relative resources.
1218 \sa metaInformation(), setMetaInformation()
1222 \fn QTextCursor QTextDocument::find(const QString &subString, int position, FindFlags options) const
1226 Finds the next occurrence of the string, \a subString, in the document.
1227 The search starts at the given \a position, and proceeds forwards
1228 through the document unless specified otherwise in the search options.
1229 The \a options control the type of search performed.
1231 Returns a cursor with the match selected if \a subString
1232 was found; otherwise returns a null cursor.
1234 If the \a position is 0 (the default) the search begins from the beginning
1235 of the document; otherwise it begins at the specified position.
1237 QTextCursor QTextDocument::find(const QString &subString, int from, FindFlags options) const
1239 QRegExp expr(subString);
1240 expr.setPatternSyntax(QRegExp::FixedString);
1241 expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
1243 return find(expr, from, options);
1247 \fn QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &cursor, FindFlags options) const
1249 Finds the next occurrence of the string, \a subString, in the document.
1250 The search starts at the position of the given \a cursor, and proceeds
1251 forwards through the document unless specified otherwise in the search
1252 options. The \a options control the type of search performed.
1254 Returns a cursor with the match selected if \a subString was found; otherwise
1255 returns a null cursor.
1257 If the given \a cursor has a selection, the search begins after the
1258 selection; otherwise it begins at the cursor's position.
1260 By default the search is case-sensitive, and can match text anywhere in the
1263 QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &from, FindFlags options) const
1266 if (!from.isNull()) {
1267 if (options & QTextDocument::FindBackward)
1268 pos = from.selectionStart();
1270 pos = from.selectionEnd();
1272 QRegExp expr(subString);
1273 expr.setPatternSyntax(QRegExp::FixedString);
1274 expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
1276 return find(expr, pos, options);
1280 static bool findInBlock(const QTextBlock &block, const QRegExp &expression, int offset,
1281 QTextDocument::FindFlags options, QTextCursor &cursor)
1283 QRegExp expr(expression);
1284 QString text = block.text();
1285 text.replace(QChar::Nbsp, QLatin1Char(' '));
1288 while (offset >=0 && offset <= text.length()) {
1289 idx = (options & QTextDocument::FindBackward) ?
1290 expr.lastIndexIn(text, offset) : expr.indexIn(text, offset);
1294 if (options & QTextDocument::FindWholeWords) {
1295 const int start = idx;
1296 const int end = start + expr.matchedLength();
1297 if ((start != 0 && text.at(start - 1).isLetterOrNumber())
1298 || (end != text.length() && text.at(end).isLetterOrNumber())) {
1299 //if this is not a whole word, continue the search in the string
1300 offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
1305 //we have a hit, return the cursor for that.
1310 cursor = QTextCursor(block.docHandle(), block.position() + idx);
1311 cursor.setPosition(cursor.position() + expr.matchedLength(), QTextCursor::KeepAnchor);
1316 \fn QTextCursor QTextDocument::find(const QRegExp & expr, int position, FindFlags options) const
1320 Finds the next occurrence, matching the regular expression, \a expr, in the document.
1321 The search starts at the given \a position, and proceeds forwards
1322 through the document unless specified otherwise in the search options.
1323 The \a options control the type of search performed. The FindCaseSensitively
1324 option is ignored for this overload, use QRegExp::caseSensitivity instead.
1326 Returns a cursor with the match selected if a match was found; otherwise
1327 returns a null cursor.
1329 If the \a position is 0 (the default) the search begins from the beginning
1330 of the document; otherwise it begins at the specified position.
1332 QTextCursor QTextDocument::find(const QRegExp & expr, int from, FindFlags options) const
1334 Q_D(const QTextDocument);
1337 return QTextCursor();
1340 //the cursor is positioned between characters, so for a backward search
1341 //do not include the character given in the position.
1342 if (options & FindBackward) {
1345 return QTextCursor();
1349 QTextBlock block = d->blocksFind(pos);
1351 if (!(options & FindBackward)) {
1352 int blockOffset = qMax(0, pos - block.position());
1353 while (block.isValid()) {
1354 if (findInBlock(block, expr, blockOffset, options, cursor))
1357 block = block.next();
1360 int blockOffset = pos - block.position();
1361 while (block.isValid()) {
1362 if (findInBlock(block, expr, blockOffset, options, cursor))
1364 block = block.previous();
1365 blockOffset = block.length() - 1;
1369 return QTextCursor();
1373 \fn QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &cursor, FindFlags options) const
1375 Finds the next occurrence, matching the regular expression, \a expr, in the document.
1376 The search starts at the position of the given \a cursor, and proceeds
1377 forwards through the document unless specified otherwise in the search
1378 options. The \a options control the type of search performed. The FindCaseSensitively
1379 option is ignored for this overload, use QRegExp::caseSensitivity instead.
1381 Returns a cursor with the match selected if a match was found; otherwise
1382 returns a null cursor.
1384 If the given \a cursor has a selection, the search begins after the
1385 selection; otherwise it begins at the cursor's position.
1387 By default the search is case-sensitive, and can match text anywhere in the
1390 QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &from, FindFlags options) const
1393 if (!from.isNull()) {
1394 if (options & QTextDocument::FindBackward)
1395 pos = from.selectionStart();
1397 pos = from.selectionEnd();
1399 return find(expr, pos, options);
1404 \fn QTextObject *QTextDocument::createObject(const QTextFormat &format)
1406 Creates and returns a new document object (a QTextObject), based
1407 on the given \a format.
1409 QTextObjects will always get created through this method, so you
1410 must reimplement it if you use custom text objects inside your document.
1412 QTextObject *QTextDocument::createObject(const QTextFormat &f)
1414 QTextObject *obj = 0;
1415 if (f.isListFormat())
1416 obj = new QTextList(this);
1417 else if (f.isTableFormat())
1418 obj = new QTextTable(this);
1419 else if (f.isFrameFormat())
1420 obj = new QTextFrame(this);
1428 Returns the frame that contains the text cursor position \a pos.
1430 QTextFrame *QTextDocument::frameAt(int pos) const
1432 Q_D(const QTextDocument);
1433 return d->frameAt(pos);
1437 Returns the document's root frame.
1439 QTextFrame *QTextDocument::rootFrame() const
1441 Q_D(const QTextDocument);
1442 return d->rootFrame();
1446 Returns the text object associated with the given \a objectIndex.
1448 QTextObject *QTextDocument::object(int objectIndex) const
1450 Q_D(const QTextDocument);
1451 return d->objectForIndex(objectIndex);
1455 Returns the text object associated with the format \a f.
1457 QTextObject *QTextDocument::objectForFormat(const QTextFormat &f) const
1459 Q_D(const QTextDocument);
1460 return d->objectForFormat(f);
1465 Returns the text block that contains the \a{pos}-th character.
1467 QTextBlock QTextDocument::findBlock(int pos) const
1469 Q_D(const QTextDocument);
1470 return QTextBlock(docHandle(), d->blockMap().findNode(pos));
1475 Returns the text block with the specified \a blockNumber.
1477 \sa QTextBlock::blockNumber()
1479 QTextBlock QTextDocument::findBlockByNumber(int blockNumber) const
1481 Q_D(const QTextDocument);
1482 return QTextBlock(docHandle(), d->blockMap().findNode(blockNumber, 1));
1487 Returns the text block that contains the specified \a lineNumber.
1489 \sa QTextBlock::firstLineNumber()
1491 QTextBlock QTextDocument::findBlockByLineNumber(int lineNumber) const
1493 Q_D(const QTextDocument);
1494 return QTextBlock(docHandle(), d->blockMap().findNode(lineNumber, 2));
1498 Returns the document's first text block.
1502 QTextBlock QTextDocument::begin() const
1504 Q_D(const QTextDocument);
1505 return QTextBlock(docHandle(), d->blockMap().begin().n);
1509 This function returns a block to test for the end of the document
1510 while iterating over it.
1512 \snippet textdocumentendsnippet.cpp 0
1514 The block returned is invalid and represents the block after the
1515 last block in the document. You can use lastBlock() to retrieve the
1516 last valid block of the document.
1520 QTextBlock QTextDocument::end() const
1522 return QTextBlock(docHandle(), 0);
1527 Returns the document's first text block.
1529 QTextBlock QTextDocument::firstBlock() const
1531 Q_D(const QTextDocument);
1532 return QTextBlock(docHandle(), d->blockMap().begin().n);
1537 Returns the document's last (valid) text block.
1539 QTextBlock QTextDocument::lastBlock() const
1541 Q_D(const QTextDocument);
1542 return QTextBlock(docHandle(), d->blockMap().last().n);
1546 \property QTextDocument::pageSize
1547 \brief the page size that should be used for laying out the document
1549 By default, for a newly-created, empty document, this property contains
1552 \sa modificationChanged()
1555 void QTextDocument::setPageSize(const QSizeF &size)
1560 d->lout->documentChanged(0, 0, d->length());
1563 QSizeF QTextDocument::pageSize() const
1565 Q_D(const QTextDocument);
1570 returns the number of pages in this document.
1572 int QTextDocument::pageCount() const
1574 return documentLayout()->pageCount();
1578 Sets the default \a font to use in the document layout.
1580 void QTextDocument::setDefaultFont(const QFont &font)
1583 d->setDefaultFont(font);
1585 d->lout->documentChanged(0, 0, d->length());
1589 Returns the default font to be used in the document layout.
1591 QFont QTextDocument::defaultFont() const
1593 Q_D(const QTextDocument);
1594 return d->defaultFont();
1598 \fn QTextDocument::modificationChanged(bool changed)
1600 This signal is emitted whenever the content of the document
1601 changes in a way that affects the modification state. If \a
1602 changed is true, the document has been modified; otherwise it is
1605 For example, calling setModified(false) on a document and then
1606 inserting text causes the signal to get emitted. If you undo that
1607 operation, causing the document to return to its original
1608 unmodified state, the signal will get emitted again.
1612 \property QTextDocument::modified
1613 \brief whether the document has been modified by the user
1615 By default, this property is false.
1617 \sa modificationChanged()
1620 bool QTextDocument::isModified() const
1622 return docHandle()->isModified();
1625 void QTextDocument::setModified(bool m)
1627 docHandle()->setModified(m);
1630 #ifndef QT_NO_PRINTER
1631 static void printPage(int index, QPainter *painter, const QTextDocument *doc, const QRectF &body, const QPointF &pageNumberPos)
1634 painter->translate(body.left(), body.top() - (index - 1) * body.height());
1635 QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
1637 QAbstractTextDocumentLayout *layout = doc->documentLayout();
1638 QAbstractTextDocumentLayout::PaintContext ctx;
1640 painter->setClipRect(view);
1643 // don't use the system palette text as default text color, on HP/UX
1644 // for example that's white, and white text on white paper doesn't
1646 ctx.palette.setColor(QPalette::Text, Qt::black);
1648 layout->draw(painter, ctx);
1650 if (!pageNumberPos.isNull()) {
1651 painter->setClipping(false);
1652 painter->setFont(QFont(doc->defaultFont()));
1653 const QString pageString = QString::number(index);
1655 painter->drawText(qRound(pageNumberPos.x() - painter->fontMetrics().width(pageString)),
1656 qRound(pageNumberPos.y() + view.top()),
1664 Prints the document to the given \a device. The QPageablePaintDevice must be
1665 set up before being used with this function.
1667 This is only a convenience method to print the whole document to the printer.
1669 If the document is already paginated through a specified height in the pageSize()
1670 property it is printed as-is.
1672 If the document is not paginated, like for example a document used in a QTextEdit,
1673 then a temporary copy of the document is created and the copy is broken into
1674 multiple pages according to the size of the paint device's paperRect(). By default
1675 a 2 cm margin is set around the document contents. In addition the current page
1676 number is printed at the bottom of each page.
1678 \sa QTextEdit::print()
1681 void QTextDocument::print(QPagedPaintDevice *printer) const
1683 Q_D(const QTextDocument);
1688 bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
1689 && d->pageSize.height() != INT_MAX;
1691 QPagedPaintDevicePrivate *pd = QPagedPaintDevicePrivate::get(printer);
1693 // ### set page size to paginated size?
1694 QPagedPaintDevice::Margins m = printer->margins();
1695 if (!documentPaginated && m.left == 0. && m.right == 0. && m.top == 0. && m.bottom == 0.) {
1696 m.left = m.right = m.top = m.bottom = 2.;
1697 printer->setMargins(m);
1699 // ### use the margins correctly
1701 QPainter p(printer);
1703 // Check that there is a valid device to print to.
1707 const QTextDocument *doc = this;
1708 QScopedPointer<QTextDocument> clonedDoc;
1709 (void)doc->documentLayout(); // make sure that there is a layout
1711 QRectF body = QRectF(QPointF(0, 0), d->pageSize);
1712 QPointF pageNumberPos;
1714 if (documentPaginated) {
1715 qreal sourceDpiX = qt_defaultDpi();
1716 qreal sourceDpiY = sourceDpiX;
1718 QPaintDevice *dev = doc->documentLayout()->paintDevice();
1720 sourceDpiX = dev->logicalDpiX();
1721 sourceDpiY = dev->logicalDpiY();
1724 const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
1725 const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
1728 p.scale(dpiScaleX, dpiScaleY);
1730 QSizeF scaledPageSize = d->pageSize;
1731 scaledPageSize.rwidth() *= dpiScaleX;
1732 scaledPageSize.rheight() *= dpiScaleY;
1734 const QSizeF printerPageSize(printer->width(), printer->height());
1737 p.scale(printerPageSize.width() / scaledPageSize.width(),
1738 printerPageSize.height() / scaledPageSize.height());
1740 doc = clone(const_cast<QTextDocument *>(this));
1741 clonedDoc.reset(const_cast<QTextDocument *>(doc));
1743 for (QTextBlock srcBlock = firstBlock(), dstBlock = clonedDoc->firstBlock();
1744 srcBlock.isValid() && dstBlock.isValid();
1745 srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
1746 dstBlock.layout()->setAdditionalFormats(srcBlock.layout()->additionalFormats());
1749 QAbstractTextDocumentLayout *layout = doc->documentLayout();
1750 layout->setPaintDevice(p.device());
1752 // copy the custom object handlers
1753 layout->d_func()->handlers = documentLayout()->d_func()->handlers;
1755 int dpiy = p.device()->logicalDpiY();
1756 int margin = (int) ((2/2.54)*dpiy); // 2 cm margins
1757 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
1758 fmt.setMargin(margin);
1759 doc->rootFrame()->setFrameFormat(fmt);
1761 body = QRectF(0, 0, printer->width(), printer->height());
1762 pageNumberPos = QPointF(body.width() - margin,
1763 body.height() - margin
1764 + QFontMetrics(doc->defaultFont(), p.device()).ascent()
1766 clonedDoc->setPageSize(body.size());
1769 int fromPage = pd->fromPage;
1770 int toPage = pd->toPage;
1771 bool ascending = true;
1773 if (fromPage == 0 && toPage == 0) {
1775 toPage = doc->pageCount();
1778 fromPage = qMax(1, fromPage);
1779 toPage = qMin(doc->pageCount(), toPage);
1781 if (toPage < fromPage) {
1782 // if the user entered a page range outside the actual number
1783 // of printable pages, just return
1787 // if (printer->pageOrder() == QPrinter::LastPageFirst) {
1788 // int tmp = fromPage;
1789 // fromPage = toPage;
1791 // ascending = false;
1794 int page = fromPage;
1796 printPage(page, &p, doc, body, pageNumberPos);
1806 if (!printer->newPage())
1813 \enum QTextDocument::ResourceType
1815 This enum describes the types of resources that can be loaded by
1816 QTextDocument's loadResource() function.
1818 \value HtmlResource The resource contains HTML.
1819 \value ImageResource The resource contains image data.
1820 Currently supported data types are QVariant::Pixmap and
1821 QVariant::Image. If the corresponding variant is of type
1822 QVariant::ByteArray then Qt attempts to load the image using
1823 QImage::loadFromData. QVariant::Icon is currently not supported.
1824 The icon needs to be converted to one of the supported types first,
1825 for example using QIcon::pixmap.
1826 \value StyleSheetResource The resource contains CSS.
1827 \value UserResource The first available value for user defined
1834 Returns data of the specified \a type from the resource with the
1837 This function is called by the rich text engine to request data that isn't
1838 directly stored by QTextDocument, but still associated with it. For example,
1839 images are referenced indirectly by the name attribute of a QTextImageFormat
1842 Resources are cached internally in the document. If a resource can
1843 not be found in the cache, loadResource is called to try to load
1844 the resource. loadResource should then use addResource to add the
1845 resource to the cache.
1847 \sa QTextDocument::ResourceType
1849 QVariant QTextDocument::resource(int type, const QUrl &name) const
1851 Q_D(const QTextDocument);
1852 QVariant r = d->resources.value(name);
1854 r = d->cachedResources.value(name);
1856 r = const_cast<QTextDocument *>(this)->loadResource(type, name);
1862 Adds the resource \a resource to the resource cache, using \a
1863 type and \a name as identifiers. \a type should be a value from
1864 QTextDocument::ResourceType.
1866 For example, you can add an image as a resource in order to reference it
1867 from within the document:
1869 \snippet textdocument-resources/main.cpp Adding a resource
1871 The image can be inserted into the document using the QTextCursor API:
1873 \snippet textdocument-resources/main.cpp Inserting an image with a cursor
1875 Alternatively, you can insert images using the HTML \c img tag:
1877 \snippet textdocument-resources/main.cpp Inserting an image using HTML
1879 void QTextDocument::addResource(int type, const QUrl &name, const QVariant &resource)
1883 d->resources.insert(name, resource);
1887 Loads data of the specified \a type from the resource with the
1890 This function is called by the rich text engine to request data that isn't
1891 directly stored by QTextDocument, but still associated with it. For example,
1892 images are referenced indirectly by the name attribute of a QTextImageFormat
1895 When called by Qt, \a type is one of the values of
1896 QTextDocument::ResourceType.
1898 If the QTextDocument is a child object of a QObject that has an invokable
1899 loadResource method such as QTextEdit, QTextBrowser
1900 or a QTextDocument itself then the default implementation tries
1901 to retrieve the data from the parent.
1903 QVariant QTextDocument::loadResource(int type, const QUrl &name)
1908 QObject *p = parent();
1910 const QMetaObject *me = p->metaObject();
1911 int index = me->indexOfMethod("loadResource(int,QUrl)");
1913 QMetaMethod loader = me->method(index);
1914 loader.invoke(p, Q_RETURN_ARG(QVariant, r), Q_ARG(int, type), Q_ARG(QUrl, name));
1918 // handle data: URLs
1919 if (r.isNull() && name.scheme().compare(QLatin1String("data"), Qt::CaseInsensitive) == 0) {
1922 if (qDecodeDataUrl(name, mimetype, payload))
1926 // if resource was not loaded try to load it here
1927 if (!qobject_cast<QTextDocument *>(p) && r.isNull() && name.isRelative()) {
1928 QUrl currentURL = d->url;
1929 QUrl resourceUrl = name;
1931 // For the second case QUrl can merge "#someanchor" with "foo.html"
1932 // correctly to "foo.html#someanchor"
1933 if (!(currentURL.isRelative()
1934 || (currentURL.scheme() == QLatin1String("file")
1935 && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
1936 || (name.hasFragment() && name.path().isEmpty())) {
1937 resourceUrl = currentURL.resolved(name);
1939 // this is our last resort when current url and new url are both relative
1940 // we try to resolve against the current working directory in the local
1942 QFileInfo fi(currentURL.toLocalFile());
1945 QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
1946 } else if (currentURL.isEmpty()) {
1947 resourceUrl.setScheme(QLatin1String("file"));
1951 QString s = resourceUrl.toLocalFile();
1953 if (!s.isEmpty() && f.open(QFile::ReadOnly)) {
1960 if (type == ImageResource && r.type() == QVariant::ByteArray) {
1961 if (qApp->thread() != QThread::currentThread()) {
1962 // must use images in non-GUI threads
1964 image.loadFromData(r.toByteArray());
1965 if (!image.isNull())
1969 pm.loadFromData(r.toByteArray());
1974 d->cachedResources.insert(name, r);
1979 static QTextFormat formatDifference(const QTextFormat &from, const QTextFormat &to)
1981 QTextFormat diff = to;
1983 const QMap<int, QVariant> props = to.properties();
1984 for (QMap<int, QVariant>::ConstIterator it = props.begin(), end = props.end();
1986 if (it.value() == from.property(it.key()))
1987 diff.clearProperty(it.key());
1992 QTextHtmlExporter::QTextHtmlExporter(const QTextDocument *_doc)
1993 : doc(_doc), fragmentMarkers(false)
1995 const QFont defaultFont = doc->defaultFont();
1996 defaultCharFormat.setFont(defaultFont);
1997 // don't export those for the default font since we cannot turn them off with CSS
1998 defaultCharFormat.clearProperty(QTextFormat::FontUnderline);
1999 defaultCharFormat.clearProperty(QTextFormat::FontOverline);
2000 defaultCharFormat.clearProperty(QTextFormat::FontStrikeOut);
2001 defaultCharFormat.clearProperty(QTextFormat::TextUnderlineStyle);
2005 Returns the document in HTML format. The conversion may not be
2006 perfect, especially for complex documents, due to the limitations
2009 QString QTextHtmlExporter::toHtml(const QByteArray &encoding, ExportMode mode)
2011 html = QLatin1String("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
2012 "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
2013 "<html><head><meta name=\"qrichtext\" content=\"1\" />");
2014 html.reserve(doc->docHandle()->length());
2016 fragmentMarkers = (mode == ExportFragment);
2018 if (!encoding.isEmpty())
2019 html += QString::fromLatin1("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\" />").arg(QString::fromLatin1(encoding));
2021 QString title = doc->metaInformation(QTextDocument::DocumentTitle);
2022 if (!title.isEmpty())
2023 html += QString::fromLatin1("<title>") + title + QString::fromLatin1("</title>");
2024 html += QLatin1String("<style type=\"text/css\">\n");
2025 html += QLatin1String("p, li { white-space: pre-wrap; }\n");
2026 html += QLatin1String("</style>");
2027 html += QLatin1String("</head><body");
2029 if (mode == ExportEntireDocument) {
2030 html += QLatin1String(" style=\"");
2032 emitFontFamily(defaultCharFormat.fontFamily());
2034 if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) {
2035 html += QLatin1String(" font-size:");
2036 html += QString::number(defaultCharFormat.fontPointSize());
2037 html += QLatin1String("pt;");
2038 } else if (defaultCharFormat.hasProperty(QTextFormat::FontPixelSize)) {
2039 html += QLatin1String(" font-size:");
2040 html += QString::number(defaultCharFormat.intProperty(QTextFormat::FontPixelSize));
2041 html += QLatin1String("px;");
2044 html += QLatin1String(" font-weight:");
2045 html += QString::number(defaultCharFormat.fontWeight() * 8);
2046 html += QLatin1Char(';');
2048 html += QLatin1String(" font-style:");
2049 html += (defaultCharFormat.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2050 html += QLatin1Char(';');
2052 // do not set text-decoration on the default font since those values are /always/ propagated
2053 // and cannot be turned off with CSS
2055 html += QLatin1Char('\"');
2057 const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2058 emitBackgroundAttribute(fmt);
2061 defaultCharFormat = QTextCharFormat();
2063 html += QLatin1Char('>');
2065 QTextFrameFormat rootFmt = doc->rootFrame()->frameFormat();
2066 rootFmt.clearProperty(QTextFormat::BackgroundBrush);
2068 QTextFrameFormat defaultFmt;
2069 defaultFmt.setMargin(doc->documentMargin());
2071 if (rootFmt == defaultFmt)
2072 emitFrame(doc->rootFrame()->begin());
2074 emitTextFrame(doc->rootFrame());
2076 html += QLatin1String("</body></html>");
2080 void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &value)
2082 html += QLatin1Char(' ');
2083 html += QLatin1String(attribute);
2084 html += QLatin1String("=\"");
2085 html += value.toHtmlEscaped();
2086 html += QLatin1Char('"');
2089 bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
2091 bool attributesEmitted = false;
2094 const QString family = format.fontFamily();
2095 if (!family.isEmpty() && family != defaultCharFormat.fontFamily()) {
2096 emitFontFamily(family);
2097 attributesEmitted = true;
2101 if (format.hasProperty(QTextFormat::FontPointSize)
2102 && format.fontPointSize() != defaultCharFormat.fontPointSize()) {
2103 html += QLatin1String(" font-size:");
2104 html += QString::number(format.fontPointSize());
2105 html += QLatin1String("pt;");
2106 attributesEmitted = true;
2107 } else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) {
2108 static const char * const sizeNames[] = {
2109 "small", "medium", "large", "x-large", "xx-large"
2111 const char *name = 0;
2112 const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1;
2113 if (idx >= 0 && idx <= 4) {
2114 name = sizeNames[idx];
2117 html += QLatin1String(" font-size:");
2118 html += QLatin1String(name);
2119 html += QLatin1Char(';');
2120 attributesEmitted = true;
2122 } else if (format.hasProperty(QTextFormat::FontPixelSize)) {
2123 html += QLatin1String(" font-size:");
2124 html += QString::number(format.intProperty(QTextFormat::FontPixelSize));
2125 html += QLatin1String("px;");
2128 if (format.hasProperty(QTextFormat::FontWeight)
2129 && format.fontWeight() != defaultCharFormat.fontWeight()) {
2130 html += QLatin1String(" font-weight:");
2131 html += QString::number(format.fontWeight() * 8);
2132 html += QLatin1Char(';');
2133 attributesEmitted = true;
2136 if (format.hasProperty(QTextFormat::FontItalic)
2137 && format.fontItalic() != defaultCharFormat.fontItalic()) {
2138 html += QLatin1String(" font-style:");
2139 html += (format.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2140 html += QLatin1Char(';');
2141 attributesEmitted = true;
2144 QLatin1String decorationTag(" text-decoration:");
2145 html += decorationTag;
2146 bool hasDecoration = false;
2147 bool atLeastOneDecorationSet = false;
2149 if ((format.hasProperty(QTextFormat::FontUnderline) || format.hasProperty(QTextFormat::TextUnderlineStyle))
2150 && format.fontUnderline() != defaultCharFormat.fontUnderline()) {
2151 hasDecoration = true;
2152 if (format.fontUnderline()) {
2153 html += QLatin1String(" underline");
2154 atLeastOneDecorationSet = true;
2158 if (format.hasProperty(QTextFormat::FontOverline)
2159 && format.fontOverline() != defaultCharFormat.fontOverline()) {
2160 hasDecoration = true;
2161 if (format.fontOverline()) {
2162 html += QLatin1String(" overline");
2163 atLeastOneDecorationSet = true;
2167 if (format.hasProperty(QTextFormat::FontStrikeOut)
2168 && format.fontStrikeOut() != defaultCharFormat.fontStrikeOut()) {
2169 hasDecoration = true;
2170 if (format.fontStrikeOut()) {
2171 html += QLatin1String(" line-through");
2172 atLeastOneDecorationSet = true;
2176 if (hasDecoration) {
2177 if (!atLeastOneDecorationSet)
2178 html += QLatin1String("none");
2179 html += QLatin1Char(';');
2180 attributesEmitted = true;
2182 html.chop(qstrlen(decorationTag.latin1()));
2185 if (format.foreground() != defaultCharFormat.foreground()
2186 && format.foreground().style() != Qt::NoBrush) {
2187 html += QLatin1String(" color:");
2188 html += format.foreground().color().name();
2189 html += QLatin1Char(';');
2190 attributesEmitted = true;
2193 if (format.background() != defaultCharFormat.background()
2194 && format.background().style() == Qt::SolidPattern) {
2195 html += QLatin1String(" background-color:");
2196 html += format.background().color().name();
2197 html += QLatin1Char(';');
2198 attributesEmitted = true;
2201 if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()
2202 && format.verticalAlignment() != QTextCharFormat::AlignNormal)
2204 html += QLatin1String(" vertical-align:");
2206 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2207 if (valign == QTextCharFormat::AlignSubScript)
2208 html += QLatin1String("sub");
2209 else if (valign == QTextCharFormat::AlignSuperScript)
2210 html += QLatin1String("super");
2211 else if (valign == QTextCharFormat::AlignMiddle)
2212 html += QLatin1String("middle");
2213 else if (valign == QTextCharFormat::AlignTop)
2214 html += QLatin1String("top");
2215 else if (valign == QTextCharFormat::AlignBottom)
2216 html += QLatin1String("bottom");
2218 html += QLatin1Char(';');
2219 attributesEmitted = true;
2222 if (format.fontCapitalization() != QFont::MixedCase) {
2223 const QFont::Capitalization caps = format.fontCapitalization();
2224 if (caps == QFont::AllUppercase)
2225 html += QLatin1String(" text-transform:uppercase;");
2226 else if (caps == QFont::AllLowercase)
2227 html += QLatin1String(" text-transform:lowercase;");
2228 else if (caps == QFont::SmallCaps)
2229 html += QLatin1String(" font-variant:small-caps;");
2230 attributesEmitted = true;
2233 if (format.fontWordSpacing() != 0.0) {
2234 html += QLatin1String(" word-spacing:");
2235 html += QString::number(format.fontWordSpacing());
2236 html += QLatin1String("px;");
2237 attributesEmitted = true;
2240 return attributesEmitted;
2243 void QTextHtmlExporter::emitTextLength(const char *attribute, const QTextLength &length)
2245 if (length.type() == QTextLength::VariableLength) // default
2248 html += QLatin1Char(' ');
2249 html += QLatin1String(attribute);
2250 html += QLatin1String("=\"");
2251 html += QString::number(length.rawValue());
2253 if (length.type() == QTextLength::PercentageLength)
2254 html += QLatin1String("%\"");
2256 html += QLatin1Char('\"');
2259 void QTextHtmlExporter::emitAlignment(Qt::Alignment align)
2261 if (align & Qt::AlignLeft)
2263 else if (align & Qt::AlignRight)
2264 html += QLatin1String(" align=\"right\"");
2265 else if (align & Qt::AlignHCenter)
2266 html += QLatin1String(" align=\"center\"");
2267 else if (align & Qt::AlignJustify)
2268 html += QLatin1String(" align=\"justify\"");
2271 void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
2273 if (pos == QTextFrameFormat::InFlow)
2276 if (mode == EmitStyleTag)
2277 html += QLatin1String(" style=\"float:");
2279 html += QLatin1String(" float:");
2281 if (pos == QTextFrameFormat::FloatLeft)
2282 html += QLatin1String(" left;");
2283 else if (pos == QTextFrameFormat::FloatRight)
2284 html += QLatin1String(" right;");
2286 Q_ASSERT_X(0, "QTextHtmlExporter::emitFloatStyle()", "pos should be a valid enum type");
2288 if (mode == EmitStyleTag)
2289 html += QLatin1Char('\"');
2292 void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style)
2294 Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
2296 html += QLatin1String(" border-style:");
2299 case QTextFrameFormat::BorderStyle_None:
2300 html += QLatin1String("none");
2302 case QTextFrameFormat::BorderStyle_Dotted:
2303 html += QLatin1String("dotted");
2305 case QTextFrameFormat::BorderStyle_Dashed:
2306 html += QLatin1String("dashed");
2308 case QTextFrameFormat::BorderStyle_Solid:
2309 html += QLatin1String("solid");
2311 case QTextFrameFormat::BorderStyle_Double:
2312 html += QLatin1String("double");
2314 case QTextFrameFormat::BorderStyle_DotDash:
2315 html += QLatin1String("dot-dash");
2317 case QTextFrameFormat::BorderStyle_DotDotDash:
2318 html += QLatin1String("dot-dot-dash");
2320 case QTextFrameFormat::BorderStyle_Groove:
2321 html += QLatin1String("groove");
2323 case QTextFrameFormat::BorderStyle_Ridge:
2324 html += QLatin1String("ridge");
2326 case QTextFrameFormat::BorderStyle_Inset:
2327 html += QLatin1String("inset");
2329 case QTextFrameFormat::BorderStyle_Outset:
2330 html += QLatin1String("outset");
2337 html += QLatin1Char(';');
2340 void QTextHtmlExporter::emitPageBreakPolicy(QTextFormat::PageBreakFlags policy)
2342 if (policy & QTextFormat::PageBreak_AlwaysBefore)
2343 html += QLatin1String(" page-break-before:always;");
2345 if (policy & QTextFormat::PageBreak_AlwaysAfter)
2346 html += QLatin1String(" page-break-after:always;");
2349 void QTextHtmlExporter::emitFontFamily(const QString &family)
2351 html += QLatin1String(" font-family:");
2353 QLatin1String quote("\'");
2354 if (family.contains(QLatin1Char('\'')))
2355 quote = QLatin1String(""");
2358 html += family.toHtmlEscaped();
2360 html += QLatin1Char(';');
2363 void QTextHtmlExporter::emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right)
2365 html += QLatin1String(" margin-top:");
2367 html += QLatin1String("px;");
2369 html += QLatin1String(" margin-bottom:");
2371 html += QLatin1String("px;");
2373 html += QLatin1String(" margin-left:");
2375 html += QLatin1String("px;");
2377 html += QLatin1String(" margin-right:");
2379 html += QLatin1String("px;");
2382 void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
2384 const QTextCharFormat format = fragment.charFormat();
2386 bool closeAnchor = false;
2388 if (format.isAnchor()) {
2389 const QString name = format.anchorName();
2390 if (!name.isEmpty()) {
2391 html += QLatin1String("<a name=\"");
2392 html += name.toHtmlEscaped();
2393 html += QLatin1String("\"></a>");
2395 const QString href = format.anchorHref();
2396 if (!href.isEmpty()) {
2397 html += QLatin1String("<a href=\"");
2398 html += href.toHtmlEscaped();
2399 html += QLatin1String("\">");
2404 QString txt = fragment.text();
2405 const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
2406 const bool isImage = isObject && format.isImageFormat();
2408 QLatin1String styleTag("<span style=\"");
2411 bool attributesEmitted = false;
2413 attributesEmitted = emitCharFormatStyle(format);
2414 if (attributesEmitted)
2415 html += QLatin1String("\">");
2417 html.chop(qstrlen(styleTag.latin1()));
2420 for (int i = 0; isImage && i < txt.length(); ++i) {
2421 QTextImageFormat imgFmt = format.toImageFormat();
2423 html += QLatin1String("<img");
2425 if (imgFmt.hasProperty(QTextFormat::ImageName))
2426 emitAttribute("src", imgFmt.name());
2428 if (imgFmt.hasProperty(QTextFormat::ImageWidth))
2429 emitAttribute("width", QString::number(imgFmt.width()));
2431 if (imgFmt.hasProperty(QTextFormat::ImageHeight))
2432 emitAttribute("height", QString::number(imgFmt.height()));
2434 if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
2435 html += QLatin1String(" style=\"vertical-align: middle;\"");
2436 else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
2437 html += QLatin1String(" style=\"vertical-align: top;\"");
2439 if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
2440 emitFloatStyle(imageFrame->frameFormat().position());
2442 html += QLatin1String(" />");
2445 Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
2447 txt = txt.toHtmlEscaped();
2449 // split for [\n{LineSeparator}]
2450 QString forcedLineBreakRegExp = QString::fromLatin1("[\\na]");
2451 forcedLineBreakRegExp[3] = QChar::LineSeparator;
2453 const QStringList lines = txt.split(QRegExp(forcedLineBreakRegExp));
2454 for (int i = 0; i < lines.count(); ++i) {
2456 html += QLatin1String("<br />"); // space on purpose for compatibility with Netscape, Lynx & Co.
2457 html += lines.at(i);
2461 if (attributesEmitted)
2462 html += QLatin1String("</span>");
2465 html += QLatin1String("</a>");
2468 static bool isOrderedList(int style)
2470 return style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
2471 || style == QTextListFormat::ListUpperAlpha
2472 || style == QTextListFormat::ListUpperRoman
2473 || style == QTextListFormat::ListLowerRoman
2477 void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
2479 QTextBlockFormat format = block.blockFormat();
2480 emitAlignment(format.alignment());
2482 // assume default to not bloat the html too much
2483 // html += QLatin1String(" dir='ltr'");
2484 if (block.textDirection() == Qt::RightToLeft)
2485 html += QLatin1String(" dir='rtl'");
2487 QLatin1String style(" style=\"");
2490 const bool emptyBlock = block.begin().atEnd();
2492 html += QLatin1String("-qt-paragraph-type:empty;");
2495 emitMargins(QString::number(format.topMargin()),
2496 QString::number(format.bottomMargin()),
2497 QString::number(format.leftMargin()),
2498 QString::number(format.rightMargin()));
2500 html += QLatin1String(" -qt-block-indent:");
2501 html += QString::number(format.indent());
2502 html += QLatin1Char(';');
2504 html += QLatin1String(" text-indent:");
2505 html += QString::number(format.textIndent());
2506 html += QLatin1String("px;");
2508 if (block.userState() != -1) {
2509 html += QLatin1String(" -qt-user-state:");
2510 html += QString::number(block.userState());
2511 html += QLatin1Char(';');
2514 emitPageBreakPolicy(format.pageBreakPolicy());
2516 QTextCharFormat diff;
2517 if (emptyBlock) { // only print character properties when we don't expect them to be repeated by actual text in the parag
2518 const QTextCharFormat blockCharFmt = block.charFormat();
2519 diff = formatDifference(defaultCharFormat, blockCharFmt).toCharFormat();
2522 diff.clearProperty(QTextFormat::BackgroundBrush);
2523 if (format.hasProperty(QTextFormat::BackgroundBrush)) {
2524 QBrush bg = format.background();
2525 if (bg.style() != Qt::NoBrush)
2526 diff.setProperty(QTextFormat::BackgroundBrush, format.property(QTextFormat::BackgroundBrush));
2529 if (!diff.properties().isEmpty())
2530 emitCharFormatStyle(diff);
2532 html += QLatin1Char('"');
2536 void QTextHtmlExporter::emitBlock(const QTextBlock &block)
2538 if (block.begin().atEnd()) {
2539 // ### HACK, remove once QTextFrame::Iterator is fixed
2540 int p = block.position();
2543 QTextDocumentPrivate::FragmentIterator frag = doc->docHandle()->find(p);
2544 QChar ch = doc->docHandle()->buffer().at(frag->stringPosition);
2545 if (ch == QTextBeginningOfFrame
2546 || ch == QTextEndOfFrame)
2550 html += QLatin1Char('\n');
2552 // save and later restore, in case we 'change' the default format by
2553 // emitting block char format information
2554 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2556 QTextList *list = block.textList();
2558 if (list->itemNumber(block) == 0) { // first item? emit <ul> or appropriate
2559 const QTextListFormat format = list->format();
2560 const int style = format.style();
2562 case QTextListFormat::ListDecimal: html += QLatin1String("<ol"); break;
2563 case QTextListFormat::ListDisc: html += QLatin1String("<ul"); break;
2564 case QTextListFormat::ListCircle: html += QLatin1String("<ul type=\"circle\""); break;
2565 case QTextListFormat::ListSquare: html += QLatin1String("<ul type=\"square\""); break;
2566 case QTextListFormat::ListLowerAlpha: html += QLatin1String("<ol type=\"a\""); break;
2567 case QTextListFormat::ListUpperAlpha: html += QLatin1String("<ol type=\"A\""); break;
2568 case QTextListFormat::ListLowerRoman: html += QLatin1String("<ol type=\"i\""); break;
2569 case QTextListFormat::ListUpperRoman: html += QLatin1String("<ol type=\"I\""); break;
2570 default: html += QLatin1String("<ul"); // ### should not happen
2573 QString styleString = QString::fromLatin1("margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;");
2575 if (format.hasProperty(QTextFormat::ListIndent)) {
2576 styleString += QLatin1String(" -qt-list-indent: ");
2577 styleString += QString::number(format.indent());
2578 styleString += QLatin1Char(';');
2581 if (format.hasProperty(QTextFormat::ListNumberPrefix)) {
2582 QString numberPrefix = format.numberPrefix();
2583 numberPrefix.replace(QLatin1Char('"'), QLatin1String("\\22"));
2584 numberPrefix.replace(QLatin1Char('\''), QLatin1String("\\27")); // FIXME: There's a problem in the CSS parser the prevents this from being correctly restored
2585 styleString += QLatin1String(" -qt-list-number-prefix: ");
2586 styleString += QLatin1Char('\'');
2587 styleString += numberPrefix;
2588 styleString += QLatin1Char('\'');
2589 styleString += QLatin1Char(';');
2592 if (format.hasProperty(QTextFormat::ListNumberSuffix)) {
2593 if (format.numberSuffix() != QLatin1String(".")) { // this is our default
2594 QString numberSuffix = format.numberSuffix();
2595 numberSuffix.replace(QLatin1Char('"'), QLatin1String("\\22"));
2596 numberSuffix.replace(QLatin1Char('\''), QLatin1String("\\27")); // see above
2597 styleString += QLatin1String(" -qt-list-number-suffix: ");
2598 styleString += QLatin1Char('\'');
2599 styleString += numberSuffix;
2600 styleString += QLatin1Char('\'');
2601 styleString += QLatin1Char(';');
2605 html += QLatin1String(" style=\"");
2606 html += styleString;
2607 html += QLatin1String("\">");
2610 html += QLatin1String("<li");
2612 const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
2613 if (!blockFmt.properties().isEmpty()) {
2614 html += QLatin1String(" style=\"");
2615 emitCharFormatStyle(blockFmt);
2616 html += QLatin1Char('\"');
2618 defaultCharFormat.merge(block.charFormat());
2622 const QTextBlockFormat blockFormat = block.blockFormat();
2623 if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
2624 html += QLatin1String("<hr");
2626 QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
2627 if (width.type() != QTextLength::VariableLength)
2628 emitTextLength("width", width);
2630 html += QLatin1Char(' ');
2632 html += QLatin1String("/>");
2636 const bool pre = blockFormat.nonBreakableLines();
2639 html += QLatin1Char('>');
2640 html += QLatin1String("<pre");
2642 html += QLatin1String("<p");
2645 emitBlockAttributes(block);
2647 html += QLatin1Char('>');
2648 if (block.begin().atEnd())
2649 html += QLatin1String("<br />");
2651 QTextBlock::Iterator it = block.begin();
2652 if (fragmentMarkers && !it.atEnd() && block == doc->begin())
2653 html += QLatin1String("<!--StartFragment-->");
2655 for (; !it.atEnd(); ++it)
2656 emitFragment(it.fragment());
2658 if (fragmentMarkers && block.position() + block.length() == doc->docHandle()->length())
2659 html += QLatin1String("<!--EndFragment-->");
2662 html += QLatin1String("</pre>");
2664 html += QLatin1String("</li>");
2666 html += QLatin1String("</p>");
2669 if (list->itemNumber(block) == list->count() - 1) { // last item? close list
2670 if (isOrderedList(list->format().style()))
2671 html += QLatin1String("</ol>");
2673 html += QLatin1String("</ul>");
2677 defaultCharFormat = oldDefaultCharFormat;
2680 extern bool qHasPixmapTexture(const QBrush& brush);
2682 QString QTextHtmlExporter::findUrlForImage(const QTextDocument *doc, qint64 cacheKey, bool isPixmap)
2688 if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent()))
2689 return findUrlForImage(parent, cacheKey, isPixmap);
2691 if (doc && doc->docHandle()) {
2692 QTextDocumentPrivate *priv = doc->docHandle();
2693 QMap<QUrl, QVariant>::const_iterator it = priv->cachedResources.constBegin();
2694 for (; it != priv->cachedResources.constEnd(); ++it) {
2696 const QVariant &v = it.value();
2697 if (v.type() == QVariant::Image && !isPixmap) {
2698 if (qvariant_cast<QImage>(v).cacheKey() == cacheKey)
2702 if (v.type() == QVariant::Pixmap && isPixmap) {
2703 if (qvariant_cast<QPixmap>(v).cacheKey() == cacheKey)
2708 if (it != priv->cachedResources.constEnd())
2709 url = it.key().toString();
2715 void QTextDocumentPrivate::mergeCachedResources(const QTextDocumentPrivate *priv)
2720 cachedResources.unite(priv->cachedResources);
2723 void QTextHtmlExporter::emitBackgroundAttribute(const QTextFormat &format)
2725 if (format.hasProperty(QTextFormat::BackgroundImageUrl)) {
2726 QString url = format.property(QTextFormat::BackgroundImageUrl).toString();
2727 emitAttribute("background", url);
2729 const QBrush &brush = format.background();
2730 if (brush.style() == Qt::SolidPattern) {
2731 emitAttribute("bgcolor", brush.color().name());
2732 } else if (brush.style() == Qt::TexturePattern) {
2733 const bool isPixmap = qHasPixmapTexture(brush);
2734 const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
2736 const QString url = findUrlForImage(doc, cacheKey, isPixmap);
2739 emitAttribute("background", url);
2744 void QTextHtmlExporter::emitTable(const QTextTable *table)
2746 QTextTableFormat format = table->format();
2748 html += QLatin1String("\n<table");
2750 if (format.hasProperty(QTextFormat::FrameBorder))
2751 emitAttribute("border", QString::number(format.border()));
2753 emitFrameStyle(format, TableFrame);
2755 emitAlignment(format.alignment());
2756 emitTextLength("width", format.width());
2758 if (format.hasProperty(QTextFormat::TableCellSpacing))
2759 emitAttribute("cellspacing", QString::number(format.cellSpacing()));
2760 if (format.hasProperty(QTextFormat::TableCellPadding))
2761 emitAttribute("cellpadding", QString::number(format.cellPadding()));
2763 emitBackgroundAttribute(format);
2765 html += QLatin1Char('>');
2767 const int rows = table->rows();
2768 const int columns = table->columns();
2770 QVector<QTextLength> columnWidths = format.columnWidthConstraints();
2771 if (columnWidths.isEmpty()) {
2772 columnWidths.resize(columns);
2773 columnWidths.fill(QTextLength());
2775 Q_ASSERT(columnWidths.count() == columns);
2777 QVarLengthArray<bool> widthEmittedForColumn(columns);
2778 for (int i = 0; i < columns; ++i)
2779 widthEmittedForColumn[i] = false;
2781 const int headerRowCount = qMin(format.headerRowCount(), rows);
2782 if (headerRowCount > 0)
2783 html += QLatin1String("<thead>");
2785 for (int row = 0; row < rows; ++row) {
2786 html += QLatin1String("\n<tr>");
2788 for (int col = 0; col < columns; ++col) {
2789 const QTextTableCell cell = table->cellAt(row, col);
2792 if (cell.row() != row)
2795 if (cell.column() != col)
2798 html += QLatin1String("\n<td");
2800 if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
2801 emitTextLength("width", columnWidths.at(col));
2802 widthEmittedForColumn[col] = true;
2805 if (cell.columnSpan() > 1)
2806 emitAttribute("colspan", QString::number(cell.columnSpan()));
2808 if (cell.rowSpan() > 1)
2809 emitAttribute("rowspan", QString::number(cell.rowSpan()));
2811 const QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
2812 emitBackgroundAttribute(cellFormat);
2814 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2816 QTextCharFormat::VerticalAlignment valign = cellFormat.verticalAlignment();
2818 QString styleString;
2819 if (valign >= QTextCharFormat::AlignMiddle && valign <= QTextCharFormat::AlignBottom) {
2820 styleString += QLatin1String(" vertical-align:");
2822 case QTextCharFormat::AlignMiddle:
2823 styleString += QLatin1String("middle");
2825 case QTextCharFormat::AlignTop:
2826 styleString += QLatin1String("top");
2828 case QTextCharFormat::AlignBottom:
2829 styleString += QLatin1String("bottom");
2834 styleString += QLatin1Char(';');
2836 QTextCharFormat temp;
2837 temp.setVerticalAlignment(valign);
2838 defaultCharFormat.merge(temp);
2841 if (cellFormat.hasProperty(QTextFormat::TableCellLeftPadding))
2842 styleString += QLatin1String(" padding-left:") + QString::number(cellFormat.leftPadding()) + QLatin1Char(';');
2843 if (cellFormat.hasProperty(QTextFormat::TableCellRightPadding))
2844 styleString += QLatin1String(" padding-right:") + QString::number(cellFormat.rightPadding()) + QLatin1Char(';');
2845 if (cellFormat.hasProperty(QTextFormat::TableCellTopPadding))
2846 styleString += QLatin1String(" padding-top:") + QString::number(cellFormat.topPadding()) + QLatin1Char(';');
2847 if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
2848 styleString += QLatin1String(" padding-bottom:") + QString::number(cellFormat.bottomPadding()) + QLatin1Char(';');
2850 if (!styleString.isEmpty())
2851 html += QLatin1String(" style=\"") + styleString + QLatin1Char('\"');
2853 html += QLatin1Char('>');
2855 emitFrame(cell.begin());
2857 html += QLatin1String("</td>");
2859 defaultCharFormat = oldDefaultCharFormat;
2862 html += QLatin1String("</tr>");
2863 if (headerRowCount > 0 && row == headerRowCount - 1)
2864 html += QLatin1String("</thead>");
2867 html += QLatin1String("</table>");
2870 void QTextHtmlExporter::emitFrame(QTextFrame::Iterator frameIt)
2872 if (!frameIt.atEnd()) {
2873 QTextFrame::Iterator next = frameIt;
2876 && frameIt.currentFrame() == 0
2877 && frameIt.parentFrame() != doc->rootFrame()
2878 && frameIt.currentBlock().begin().atEnd())
2882 for (QTextFrame::Iterator it = frameIt;
2883 !it.atEnd(); ++it) {
2884 if (QTextFrame *f = it.currentFrame()) {
2885 if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
2890 } else if (it.currentBlock().isValid()) {
2891 emitBlock(it.currentBlock());
2896 void QTextHtmlExporter::emitTextFrame(const QTextFrame *f)
2898 FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
2900 html += QLatin1String("\n<table");
2901 QTextFrameFormat format = f->frameFormat();
2903 if (format.hasProperty(QTextFormat::FrameBorder))
2904 emitAttribute("border", QString::number(format.border()));
2906 emitFrameStyle(format, frameType);
2908 emitTextLength("width", format.width());
2909 emitTextLength("height", format.height());
2911 // root frame's bcolor goes in the <body> tag
2912 if (frameType != RootFrame)
2913 emitBackgroundAttribute(format);
2915 html += QLatin1Char('>');
2916 html += QLatin1String("\n<tr>\n<td style=\"border: none;\">");
2917 emitFrame(f->begin());
2918 html += QLatin1String("</td></tr></table>");
2921 void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType frameType)
2923 QLatin1String styleAttribute(" style=\"");
2924 html += styleAttribute;
2925 const int originalHtmlLength = html.length();
2927 if (frameType == TextFrame)
2928 html += QLatin1String("-qt-table-type: frame;");
2929 else if (frameType == RootFrame)
2930 html += QLatin1String("-qt-table-type: root;");
2932 const QTextFrameFormat defaultFormat;
2934 emitFloatStyle(format.position(), OmitStyleTag);
2935 emitPageBreakPolicy(format.pageBreakPolicy());
2937 if (format.borderBrush() != defaultFormat.borderBrush()) {
2938 html += QLatin1String(" border-color:");
2939 html += format.borderBrush().color().name();
2940 html += QLatin1Char(';');
2943 if (format.borderStyle() != defaultFormat.borderStyle())
2944 emitBorderStyle(format.borderStyle());
2946 if (format.hasProperty(QTextFormat::FrameMargin)
2947 || format.hasProperty(QTextFormat::FrameLeftMargin)
2948 || format.hasProperty(QTextFormat::FrameRightMargin)
2949 || format.hasProperty(QTextFormat::FrameTopMargin)
2950 || format.hasProperty(QTextFormat::FrameBottomMargin))
2951 emitMargins(QString::number(format.topMargin()),
2952 QString::number(format.bottomMargin()),
2953 QString::number(format.leftMargin()),
2954 QString::number(format.rightMargin()));
2956 if (html.length() == originalHtmlLength) // nothing emitted?
2957 html.chop(qstrlen(styleAttribute.latin1()));
2959 html += QLatin1Char('\"');
2963 Returns a string containing an HTML representation of the document.
2965 The \a encoding parameter specifies the value for the charset attribute
2966 in the html header. For example if 'utf-8' is specified then the
2967 beginning of the generated html will look like this:
2968 \snippet code/src_gui_text_qtextdocument.cpp 0
2970 If no encoding is specified then no such meta information is generated.
2972 If you later on convert the returned html string into a byte array for
2973 transmission over a network or when saving to disk you should specify
2974 the encoding you're going to use for the conversion to a byte array here.
2976 \sa {Supported HTML Subset}
2978 #ifndef QT_NO_TEXTHTMLPARSER
2979 QString QTextDocument::toHtml(const QByteArray &encoding) const
2981 return QTextHtmlExporter(this).toHtml(encoding);
2983 #endif // QT_NO_TEXTHTMLPARSER
2986 Returns a vector of text formats for all the formats used in the document.
2988 QVector<QTextFormat> QTextDocument::allFormats() const
2990 Q_D(const QTextDocument);
2991 return d->formatCollection()->formats;
2998 So that not all classes have to be friends of each other...
3000 QTextDocumentPrivate *QTextDocument::docHandle() const
3002 Q_D(const QTextDocument);
3003 return const_cast<QTextDocumentPrivate *>(d);
3008 \fn QTextDocument::undoCommandAdded()
3010 This signal is emitted every time a new level of undo is added to the QTextDocument.