1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** 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>
56 #include "qtexthtmlparser_p.h"
60 #include <qfileinfo.h>
63 #include "private/qdataurl_p.h"
65 #include "qtextdocument_p.h"
66 #include <private/qprinter_p.h>
67 #include <private/qabstracttextdocumentlayout_p.h>
73 Q_CORE_EXPORT unsigned int qt_int_sqrt(unsigned int n);
76 Returns true if the string \a text is likely to be rich text;
77 otherwise returns false.
79 This function uses a fast and therefore simple heuristic. It
80 mainly checks whether there is something that looks like a tag
81 before the first line break. Although the result may be correct
82 for common cases, there is no guarantee.
84 This function is defined in the \c <QTextDocument> header file.
86 bool Qt::mightBeRichText(const QString& text)
92 while (start < text.length() && text.at(start).isSpace())
95 // skip a leading <?xml ... ?> as for example with xhtml
96 if (text.mid(start, 5) == QLatin1String("<?xml")) {
97 while (start < text.length()) {
98 if (text.at(start) == QLatin1Char('?')
99 && start + 2 < text.length()
100 && text.at(start + 1) == QLatin1Char('>')) {
107 while (start < text.length() && text.at(start).isSpace())
111 if (text.mid(start, 5).toLower() == QLatin1String("<!doc"))
114 while (open < text.length() && text.at(open) != QLatin1Char('<')
115 && text.at(open) != QLatin1Char('\n')) {
116 if (text.at(open) == QLatin1Char('&') && text.mid(open+1,3) == QLatin1String("lt;"))
117 return true; // support desperate attempt of user to see <...>
120 if (open < text.length() && text.at(open) == QLatin1Char('<')) {
121 const int close = text.indexOf(QLatin1Char('>'), open);
124 for (int i = open+1; i < close; ++i) {
125 if (text[i].isDigit() || text[i].isLetter())
127 else if (!tag.isEmpty() && text[i].isSpace())
129 else if (!tag.isEmpty() && text[i] == QLatin1Char('/') && i + 1 == close)
131 else if (!text[i].isSpace() && (!tag.isEmpty() || text[i] != QLatin1Char('!')))
132 return false; // that's not a tag
134 #ifndef QT_NO_TEXTHTMLPARSER
135 return QTextHtmlParser::lookupElement(tag.toLower()) != -1;
138 #endif // QT_NO_TEXTHTMLPARSER
145 Converts the plain text string \a plain to a HTML string with
146 HTML metacharacters \c{<}, \c{>}, \c{&}, and \c{"} replaced by HTML
151 \snippet doc/src/snippets/code/src_gui_text_qtextdocument.cpp 0
153 This function is defined in the \c <QTextDocument> header file.
155 \sa convertFromPlainText(), mightBeRichText()
157 QString Qt::escape(const QString& plain)
160 rich.reserve(int(plain.length() * 1.1));
161 for (int i = 0; i < plain.length(); ++i) {
162 if (plain.at(i) == QLatin1Char('<'))
163 rich += QLatin1String("<");
164 else if (plain.at(i) == QLatin1Char('>'))
165 rich += QLatin1String(">");
166 else if (plain.at(i) == QLatin1Char('&'))
167 rich += QLatin1String("&");
168 else if (plain.at(i) == QLatin1Char('"'))
169 rich += QLatin1String(""");
177 \fn QString Qt::convertFromPlainText(const QString &plain, WhiteSpaceMode mode)
179 Converts the plain text string \a plain to an HTML-formatted
180 paragraph while preserving most of its look.
182 \a mode defines how whitespace is handled.
184 This function is defined in the \c <QTextDocument> header file.
186 \sa escape(), mightBeRichText()
188 QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
192 rich += QLatin1String("<p>");
193 for (int i = 0; i < plain.length(); ++i) {
194 if (plain[i] == QLatin1Char('\n')){
196 while (i+1 < plain.length() && plain[i+1] == QLatin1Char('\n')) {
201 rich += QLatin1String("<br>\n");
203 rich += QLatin1String("</p>\n");
205 rich += QLatin1String("<br>\n");
206 rich += QLatin1String("<p>");
210 if (mode == Qt::WhiteSpacePre && plain[i] == QLatin1Char('\t')){
211 rich += QChar(0x00a0U);
214 rich += QChar(0x00a0U);
218 else if (mode == Qt::WhiteSpacePre && plain[i].isSpace())
219 rich += QChar(0x00a0U);
220 else if (plain[i] == QLatin1Char('<'))
221 rich += QLatin1String("<");
222 else if (plain[i] == QLatin1Char('>'))
223 rich += QLatin1String(">");
224 else if (plain[i] == QLatin1Char('&'))
225 rich += QLatin1String("&");
232 rich += QLatin1String("</p>");
236 #ifndef QT_NO_TEXTCODEC
240 This function is defined in the \c <QTextDocument> header file.
242 QTextCodec *Qt::codecForHtml(const QByteArray &ba)
244 return QTextCodec::codecForHtml(ba);
252 \brief The QTextDocument class holds formatted text that can be
253 viewed and edited using a QTextEdit.
255 \ingroup richtext-processing
258 QTextDocument is a container for structured rich text documents, providing
259 support for styled text and various types of document elements, such as
260 lists, tables, frames, and images.
261 They can be created for use in a QTextEdit, or used independently.
263 Each document element is described by an associated format object. Each
264 format object is treated as a unique object by QTextDocuments, and can be
265 passed to objectForFormat() to obtain the document element that it is
268 A QTextDocument can be edited programmatically using a QTextCursor, and
269 its contents can be examined by traversing the document structure. The
270 entire document structure is stored as a hierarchy of document elements
271 beneath the root frame, found with the rootFrame() function. Alternatively,
272 if you just want to iterate over the textual contents of the document you
273 can use begin(), end(), and findBlock() to retrieve text blocks that you
274 can examine and iterate over.
276 The layout of a document is determined by the documentLayout();
277 you can create your own QAbstractTextDocumentLayout subclass and
278 set it using setDocumentLayout() if you want to use your own
279 layout logic. The document's title and other meta-information can be
280 obtained by calling the metaInformation() function. For documents that
281 are exposed to users through the QTextEdit class, the document title
282 is also available via the QTextEdit::documentTitle() function.
284 The toPlainText() and toHtml() convenience functions allow you to retrieve the
285 contents of the document as plain text and HTML.
286 The document's text can be searched using the find() functions.
288 Undo/redo of operations performed on the document can be controlled using
289 the setUndoRedoEnabled() function. The undo/redo system can be controlled
290 by an editor widget through the undo() and redo() slots; the document also
291 provides contentsChanged(), undoAvailable(), and redoAvailable() signals
292 that inform connected editor widgets about the state of the undo/redo
293 system. The following are the undo/redo operations of a QTextDocument:
296 \o Insertion or removal of characters. A sequence of insertions or removals
297 within the same text block are regarded as a single undo/redo operation.
298 \o Insertion or removal of text blocks. Sequences of insertion or removals
299 in a single operation (e.g., by selecting and then deleting text) are
300 regarded as a single undo/redo operation.
301 \o Text character format changes.
302 \o Text block format changes.
303 \o Text block group format changes.
306 \sa QTextCursor, QTextEdit, \link richtext.html Rich Text Processing\endlink , {Text Object Example}
310 \property QTextDocument::defaultFont
311 \brief the default font used to display the document's text
315 \property QTextDocument::defaultTextOption
316 \brief the default text option will be set on all \l{QTextLayout}s in the document.
318 When \l{QTextBlock}s are created, the defaultTextOption is set on their
319 QTextLayout. This allows setting global properties for the document such as the
320 default word wrap mode.
324 Constructs an empty QTextDocument with the given \a parent.
326 QTextDocument::QTextDocument(QObject *parent)
327 : QObject(*new QTextDocumentPrivate, parent)
334 Constructs a QTextDocument containing the plain (unformatted) \a text
335 specified, and with the given \a parent.
337 QTextDocument::QTextDocument(const QString &text, QObject *parent)
338 : QObject(*new QTextDocumentPrivate, parent)
342 QTextCursor(this).insertText(text);
348 QTextDocument::QTextDocument(QTextDocumentPrivate &dd, QObject *parent)
349 : QObject(dd, parent)
356 Destroys the document.
358 QTextDocument::~QTextDocument()
364 Creates a new QTextDocument that is a copy of this text document. \a
365 parent is the parent of the returned text document.
367 QTextDocument *QTextDocument::clone(QObject *parent) const
369 Q_D(const QTextDocument);
370 QTextDocument *doc = new QTextDocument(parent);
371 QTextCursor(doc).insertFragment(QTextDocumentFragment(this));
372 doc->rootFrame()->setFrameFormat(rootFrame()->frameFormat());
373 QTextDocumentPrivate *priv = doc->d_func();
374 priv->title = d->title;
376 priv->pageSize = d->pageSize;
377 priv->indentWidth = d->indentWidth;
378 priv->defaultTextOption = d->defaultTextOption;
379 priv->setDefaultFont(d->defaultFont());
380 priv->resources = d->resources;
381 priv->cachedResources.clear();
382 #ifndef QT_NO_CSSPARSER
383 priv->defaultStyleSheet = d->defaultStyleSheet;
384 priv->parsedDefaultStyleSheet = d->parsedDefaultStyleSheet;
390 Returns true if the document is empty; otherwise returns false.
392 bool QTextDocument::isEmpty() const
394 Q_D(const QTextDocument);
395 /* because if we're empty we still have one single paragraph as
396 * one single fragment */
397 return d->length() <= 1;
403 void QTextDocument::clear()
407 d->resources.clear();
413 Undoes the last editing operation on the document if undo is
414 available. The provided \a cursor is positioned at the end of the
415 location where the edition operation was undone.
417 See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
418 documentation for details.
420 \sa undoAvailable(), isUndoRedoEnabled()
422 void QTextDocument::undo(QTextCursor *cursor)
425 const int pos = d->undoRedo(true);
426 if (cursor && pos >= 0) {
427 *cursor = QTextCursor(this);
428 cursor->setPosition(pos);
434 Redoes the last editing operation on the document if \link
435 QTextDocument::isRedoAvailable() redo is available\endlink.
437 The provided \a cursor is positioned at the end of the location where
438 the edition operation was redone.
440 void QTextDocument::redo(QTextCursor *cursor)
443 const int pos = d->undoRedo(false);
444 if (cursor && pos >= 0) {
445 *cursor = QTextCursor(this);
446 cursor->setPosition(pos);
450 /*! \enum QTextDocument::Stacks
452 \value UndoStack The undo stack.
453 \value RedoStack The redo stack.
454 \value UndoAndRedoStacks Both the undo and redo stacks.
459 Clears the stacks specified by \a stacksToClear.
461 This method clears any commands on the undo stack, the redo stack,
462 or both (the default). If commands are cleared, the appropriate
463 signals are emitted, QTextDocument::undoAvailable() or
464 QTextDocument::redoAvailable().
466 \sa QTextDocument::undoAvailable() QTextDocument::redoAvailable()
468 void QTextDocument::clearUndoRedoStacks(Stacks stacksToClear)
471 d->clearUndoRedoStacks(stacksToClear, true);
478 void QTextDocument::undo()
486 Redoes the last editing operation on the document if \link
487 QTextDocument::isRedoAvailable() redo is available\endlink.
489 void QTextDocument::redo()
498 Appends a custom undo \a item to the undo stack.
500 void QTextDocument::appendUndoItem(QAbstractUndoItem *item)
503 d->appendUndoItem(item);
507 \property QTextDocument::undoRedoEnabled
508 \brief whether undo/redo are enabled for this document
510 This defaults to true. If disabled, the undo stack is cleared and
511 no items will be added to it.
513 void QTextDocument::setUndoRedoEnabled(bool enable)
516 d->enableUndoRedo(enable);
519 bool QTextDocument::isUndoRedoEnabled() const
521 Q_D(const QTextDocument);
522 return d->isUndoRedoEnabled();
526 \property QTextDocument::maximumBlockCount
528 \brief Specifies the limit for blocks in the document.
530 Specifies the maximum number of blocks the document may have. If there are
531 more blocks in the document that specified with this property blocks are removed
532 from the beginning of the document.
534 A negative or zero value specifies that the document may contain an unlimited
537 The default value is 0.
539 Note that setting this property will apply the limit immediately to the document
542 Setting this property also disables the undo redo history.
544 This property is undefined in documents with tables or frames.
546 int QTextDocument::maximumBlockCount() const
548 Q_D(const QTextDocument);
549 return d->maximumBlockCount;
552 void QTextDocument::setMaximumBlockCount(int maximum)
555 d->maximumBlockCount = maximum;
556 d->ensureMaximumBlockCount();
557 setUndoRedoEnabled(false);
563 The default text option is used on all QTextLayout objects in the document.
564 This allows setting global properties for the document such as the default
567 QTextOption QTextDocument::defaultTextOption() const
569 Q_D(const QTextDocument);
570 return d->defaultTextOption;
576 Sets the default text option.
578 void QTextDocument::setDefaultTextOption(const QTextOption &option)
581 d->defaultTextOption = option;
583 d->lout->documentChanged(0, 0, d->length());
589 The default cursor movement style is used by all QTextCursor objects
590 created from the document. The default is Qt::LogicalMoveStyle.
592 Qt::CursorMoveStyle QTextDocument::defaultCursorMoveStyle() const
594 Q_D(const QTextDocument);
595 return d->defaultCursorMoveStyle;
601 Sets the default cursor movement style to the given \a style.
603 void QTextDocument::setDefaultCursorMoveStyle(Qt::CursorMoveStyle style)
606 d->defaultCursorMoveStyle = style;
610 \fn void QTextDocument::markContentsDirty(int position, int length)
612 Marks the contents specified by the given \a position and \a length
613 as "dirty", informing the document that it needs to be laid out
616 void QTextDocument::markContentsDirty(int from, int length)
619 d->documentChange(from, length);
620 if (!d->inContentsChange) {
622 d->lout->documentChanged(d->docChangeFrom, d->docChangeOldLength, d->docChangeLength);
623 d->docChangeFrom = -1;
629 \property QTextDocument::useDesignMetrics
631 \brief whether the document uses design metrics of fonts to improve the accuracy of text layout
633 If this property is set to true, the layout will use design metrics.
634 Otherwise, the metrics of the paint device as set on
635 QAbstractTextDocumentLayout::setPaintDevice() will be used.
637 Using design metrics makes a layout have a width that is no longer dependent on hinting
638 and pixel-rounding. This means that WYSIWYG text layout becomes possible because the width
639 scales much more linearly based on paintdevice metrics than it would otherwise.
641 By default, this property is false.
644 void QTextDocument::setUseDesignMetrics(bool b)
647 if (b == d->defaultTextOption.useDesignMetrics())
649 d->defaultTextOption.setUseDesignMetrics(b);
651 d->lout->documentChanged(0, 0, d->length());
654 bool QTextDocument::useDesignMetrics() const
656 Q_D(const QTextDocument);
657 return d->defaultTextOption.useDesignMetrics();
663 Draws the content of the document with painter \a p, clipped to \a rect.
664 If \a rect is a null rectangle (default) then the document is painted unclipped.
666 void QTextDocument::drawContents(QPainter *p, const QRectF &rect)
669 QAbstractTextDocumentLayout::PaintContext ctx;
670 if (rect.isValid()) {
671 p->setClipRect(rect);
674 documentLayout()->draw(p, ctx);
679 \property QTextDocument::textWidth
682 The text width specifies the preferred width for text in the document. If
683 the text (or content in general) is wider than the specified with it is broken
684 into multiple lines and grows vertically. If the text cannot be broken into multiple
685 lines to fit into the specified text width it will be larger and the size() and the
686 idealWidth() property will reflect that.
688 If the text width is set to -1 then the text will not be broken into multiple lines
689 unless it is enforced through an explicit line break or a new paragraph.
691 The default value is -1.
693 Setting the text width will also set the page height to -1, causing the document to
694 grow or shrink vertically in a continuous way. If you want the document layout to break
695 the text into multiple pages then you have to set the pageSize property instead.
697 \sa size(), idealWidth(), pageSize()
699 void QTextDocument::setTextWidth(qreal width)
702 QSizeF sz = d->pageSize;
708 qreal QTextDocument::textWidth() const
710 Q_D(const QTextDocument);
711 return d->pageSize.width();
717 Returns the ideal width of the text document. The ideal width is the actually used width
718 of the document without optional alignments taken into account. It is always <= size().width().
720 \sa adjustSize(), textWidth
722 qreal QTextDocument::idealWidth() const
724 if (QTextDocumentLayout *lout = qobject_cast<QTextDocumentLayout *>(documentLayout()))
725 return lout->idealWidth();
730 \property QTextDocument::documentMargin
733 The margin around the document. The default is 4.
735 qreal QTextDocument::documentMargin() const
737 Q_D(const QTextDocument);
738 return d->documentMargin;
741 void QTextDocument::setDocumentMargin(qreal margin)
744 if (d->documentMargin != margin) {
745 d->documentMargin = margin;
747 QTextFrame* root = rootFrame();
748 QTextFrameFormat format = root->frameFormat();
749 format.setMargin(margin);
750 root->setFrameFormat(format);
753 d->lout->documentChanged(0, 0, d->length());
759 \property QTextDocument::indentWidth
762 Returns the width used for text list and text block indenting.
764 The indent properties of QTextListFormat and QTextBlockFormat specify
765 multiples of this value. The default indent width is 40.
767 qreal QTextDocument::indentWidth() const
769 Q_D(const QTextDocument);
770 return d->indentWidth;
777 Sets the \a width used for text list and text block indenting.
779 The indent properties of QTextListFormat and QTextBlockFormat specify
780 multiples of this value. The default indent width is 40 .
784 void QTextDocument::setIndentWidth(qreal width)
787 if (d->indentWidth != width) {
788 d->indentWidth = width;
790 d->lout->documentChanged(0, 0, d->length());
800 Adjusts the document to a reasonable size.
802 \sa idealWidth(), textWidth, size
804 void QTextDocument::adjustSize()
806 // Pull this private function in from qglobal.cpp
807 QFont f = defaultFont();
809 int mw = fm.width(QLatin1Char('x')) * 80;
812 QSizeF size = documentLayout()->documentSize();
813 if (size.width() != 0) {
814 w = qt_int_sqrt((uint)(5 * size.height() * size.width() / 3));
815 setTextWidth(qMin(w, mw));
817 size = documentLayout()->documentSize();
818 if (w*3 < 5*size.height()) {
819 w = qt_int_sqrt((uint)(2 * size.height() * size.width()));
820 setTextWidth(qMin(w, mw));
823 setTextWidth(idealWidth());
827 \property QTextDocument::size
830 Returns the actual size of the document.
831 This is equivalent to documentLayout()->documentSize();
833 The size of the document can be changed either by setting
834 a text width or setting an entire page size.
836 Note that the width is always >= pageSize().width().
838 By default, for a newly-created, empty document, this property contains
839 a configuration-dependent size.
841 \sa setTextWidth(), setPageSize(), idealWidth()
843 QSizeF QTextDocument::size() const
845 return documentLayout()->documentSize();
849 \property QTextDocument::blockCount
852 Returns the number of text blocks in the document.
854 The value of this property is undefined in documents with tables or frames.
856 By default, if defined, this property contains a value of 1.
857 \sa lineCount(), characterCount()
859 int QTextDocument::blockCount() const
861 Q_D(const QTextDocument);
862 return d->blockMap().numNodes();
869 Returns the number of lines of this document (if the layout supports
870 this). Otherwise, this is identical to the number of blocks.
872 \sa blockCount(), characterCount()
874 int QTextDocument::lineCount() const
876 Q_D(const QTextDocument);
877 return d->blockMap().length(2);
883 Returns the number of characters of this document.
885 \sa blockCount(), characterAt()
887 int QTextDocument::characterCount() const
889 Q_D(const QTextDocument);
896 Returns the character at position \a pos, or a null character if the
897 position is out of range.
901 QChar QTextDocument::characterAt(int pos) const
903 Q_D(const QTextDocument);
904 if (pos < 0 || pos >= d->length())
906 QTextDocumentPrivate::FragmentIterator fragIt = d->find(pos);
907 const QTextFragmentData * const frag = fragIt.value();
908 const int offsetInFragment = qMax(0, pos - fragIt.position());
909 return d->text.at(frag->stringPosition + offsetInFragment);
914 \property QTextDocument::defaultStyleSheet
917 The default style sheet is applied to all newly HTML formatted text that is
918 inserted into the document, for example using setHtml() or QTextCursor::insertHtml().
920 The style sheet needs to be compliant to CSS 2.1 syntax.
922 \bold{Note:} Changing the default style sheet does not have any effect to the existing content
925 \sa {Supported HTML Subset}
928 #ifndef QT_NO_CSSPARSER
929 void QTextDocument::setDefaultStyleSheet(const QString &sheet)
932 d->defaultStyleSheet = sheet;
933 QCss::Parser parser(sheet);
934 d->parsedDefaultStyleSheet = QCss::StyleSheet();
935 d->parsedDefaultStyleSheet.origin = QCss::StyleSheetOrigin_UserAgent;
936 parser.parse(&d->parsedDefaultStyleSheet);
939 QString QTextDocument::defaultStyleSheet() const
941 Q_D(const QTextDocument);
942 return d->defaultStyleSheet;
944 #endif // QT_NO_CSSPARSER
947 \fn void QTextDocument::contentsChanged()
949 This signal is emitted whenever the document's content changes; for
950 example, when text is inserted or deleted, or when formatting is applied.
956 \fn void QTextDocument::contentsChange(int position, int charsRemoved, int charsAdded)
958 This signal is emitted whenever the document's content changes; for
959 example, when text is inserted or deleted, or when formatting is applied.
961 Information is provided about the \a position of the character in the
962 document where the change occurred, the number of characters removed
963 (\a charsRemoved), and the number of characters added (\a charsAdded).
965 The signal is emitted before the document's layout manager is notified
966 about the change. This hook allows you to implement syntax highlighting
969 \sa QAbstractTextDocumentLayout::documentChanged(), contentsChanged()
974 \fn QTextDocument::undoAvailable(bool available);
976 This signal is emitted whenever undo operations become available
977 (\a available is true) or unavailable (\a available is false).
979 See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
980 documentation for details.
982 \sa undo(), isUndoRedoEnabled()
986 \fn QTextDocument::redoAvailable(bool available);
988 This signal is emitted whenever redo operations become available
989 (\a available is true) or unavailable (\a available is false).
993 \fn QTextDocument::cursorPositionChanged(const QTextCursor &cursor);
995 This signal is emitted whenever the position of a cursor changed
996 due to an editing operation. The cursor that changed is passed in
997 \a cursor. If you need a signal when the cursor is moved with the
998 arrow keys you can use the \l{QTextEdit::}{cursorPositionChanged()} signal in
1003 \fn QTextDocument::blockCountChanged(int newBlockCount);
1006 This signal is emitted when the total number of text blocks in the
1007 document changes. The value passed in \a newBlockCount is the new
1012 \fn QTextDocument::documentLayoutChanged();
1015 This signal is emitted when a new document layout is set.
1017 \sa setDocumentLayout()
1023 Returns true if undo is available; otherwise returns false.
1025 \sa isRedoAvailable(), availableUndoSteps()
1027 bool QTextDocument::isUndoAvailable() const
1029 Q_D(const QTextDocument);
1030 return d->isUndoAvailable();
1034 Returns true if redo is available; otherwise returns false.
1036 \sa isUndoAvailable(), availableRedoSteps()
1038 bool QTextDocument::isRedoAvailable() const
1040 Q_D(const QTextDocument);
1041 return d->isRedoAvailable();
1046 Returns the number of available undo steps.
1048 \sa isUndoAvailable()
1050 int QTextDocument::availableUndoSteps() const
1052 Q_D(const QTextDocument);
1053 return d->availableUndoSteps();
1058 Returns the number of available redo steps.
1060 \sa isRedoAvailable()
1062 int QTextDocument::availableRedoSteps() const
1064 Q_D(const QTextDocument);
1065 return d->availableRedoSteps();
1070 Returns the document's revision (if undo is enabled).
1072 The revision is guaranteed to increase when a document that is not
1075 \sa QTextBlock::revision(), isModified()
1077 int QTextDocument::revision() const
1079 Q_D(const QTextDocument);
1086 Sets the document to use the given \a layout. The previous layout
1089 \sa documentLayoutChanged()
1091 void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *layout)
1094 d->setLayout(layout);
1098 Returns the document layout for this document.
1100 QAbstractTextDocumentLayout *QTextDocument::documentLayout() const
1102 Q_D(const QTextDocument);
1104 QTextDocument *that = const_cast<QTextDocument *>(this);
1105 that->d_func()->setLayout(new QTextDocumentLayout(that));
1112 Returns meta information about the document of the type specified by
1115 \sa setMetaInformation()
1117 QString QTextDocument::metaInformation(MetaInformation info) const
1119 Q_D(const QTextDocument);
1130 Sets the document's meta information of the type specified by \a info
1131 to the given \a string.
1133 \sa metaInformation()
1135 void QTextDocument::setMetaInformation(MetaInformation info, const QString &string)
1149 Returns the plain text contained in the document. If you want
1150 formatting information use a QTextCursor instead.
1154 QString QTextDocument::toPlainText() const
1156 Q_D(const QTextDocument);
1157 QString txt = d->plainText();
1159 QChar *uc = txt.data();
1160 QChar *e = uc + txt.size();
1162 for (; uc != e; ++uc) {
1163 switch (uc->unicode()) {
1164 case 0xfdd0: // QTextBeginningOfFrame
1165 case 0xfdd1: // QTextEndOfFrame
1166 case QChar::ParagraphSeparator:
1167 case QChar::LineSeparator:
1168 *uc = QLatin1Char('\n');
1171 *uc = QLatin1Char(' ');
1181 Replaces the entire contents of the document with the given plain
1186 void QTextDocument::setPlainText(const QString &text)
1189 bool previousState = d->isUndoRedoEnabled();
1190 d->enableUndoRedo(false);
1191 d->beginEditBlock();
1193 QTextCursor(this).insertText(text);
1195 d->enableUndoRedo(previousState);
1199 Replaces the entire contents of the document with the given
1200 HTML-formatted text in the \a html string.
1202 The HTML formatting is respected as much as possible; for example,
1203 "<b>bold</b> text" will produce text where the first word has a font
1204 weight that gives it a bold appearance: "\bold{bold} text".
1206 \note It is the responsibility of the caller to make sure that the
1207 text is correctly decoded when a QString containing HTML is created
1208 and passed to setHtml().
1210 \sa setPlainText(), {Supported HTML Subset}
1213 #ifndef QT_NO_TEXTHTMLPARSER
1215 void QTextDocument::setHtml(const QString &html)
1218 bool previousState = d->isUndoRedoEnabled();
1219 d->enableUndoRedo(false);
1220 d->beginEditBlock();
1222 QTextHtmlImporter(this, html, QTextHtmlImporter::ImportToDocument).import();
1224 d->enableUndoRedo(previousState);
1227 #endif // QT_NO_TEXTHTMLPARSER
1230 \enum QTextDocument::FindFlag
1232 This enum describes the options available to QTextDocument's find function. The options
1233 can be OR-ed together from the following list:
1235 \value FindBackward Search backwards instead of forwards.
1236 \value FindCaseSensitively By default find works case insensitive. Specifying this option
1237 changes the behaviour to a case sensitive find operation.
1238 \value FindWholeWords Makes find match only complete words.
1242 \enum QTextDocument::MetaInformation
1244 This enum describes the different types of meta information that can be
1245 added to a document.
1247 \value DocumentTitle The title of the document.
1248 \value DocumentUrl The url of the document. The loadResource() function uses
1249 this url as the base when loading relative resources.
1251 \sa metaInformation(), setMetaInformation()
1255 \fn QTextCursor QTextDocument::find(const QString &subString, int position, FindFlags options) const
1259 Finds the next occurrence of the string, \a subString, in the document.
1260 The search starts at the given \a position, and proceeds forwards
1261 through the document unless specified otherwise in the search options.
1262 The \a options control the type of search performed.
1264 Returns a cursor with the match selected if \a subString
1265 was found; otherwise returns a null cursor.
1267 If the \a position is 0 (the default) the search begins from the beginning
1268 of the document; otherwise it begins at the specified position.
1270 QTextCursor QTextDocument::find(const QString &subString, int from, FindFlags options) const
1272 QRegExp expr(subString);
1273 expr.setPatternSyntax(QRegExp::FixedString);
1274 expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
1276 return find(expr, from, options);
1280 \fn QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &cursor, FindFlags options) const
1282 Finds the next occurrence of the string, \a subString, in the document.
1283 The search starts at the position of the given \a cursor, and proceeds
1284 forwards through the document unless specified otherwise in the search
1285 options. The \a options control the type of search performed.
1287 Returns a cursor with the match selected if \a subString was found; otherwise
1288 returns a null cursor.
1290 If the given \a cursor has a selection, the search begins after the
1291 selection; otherwise it begins at the cursor's position.
1293 By default the search is case-sensitive, and can match text anywhere in the
1296 QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &from, FindFlags options) const
1299 if (!from.isNull()) {
1300 if (options & QTextDocument::FindBackward)
1301 pos = from.selectionStart();
1303 pos = from.selectionEnd();
1305 QRegExp expr(subString);
1306 expr.setPatternSyntax(QRegExp::FixedString);
1307 expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
1309 return find(expr, pos, options);
1313 static bool findInBlock(const QTextBlock &block, const QRegExp &expression, int offset,
1314 QTextDocument::FindFlags options, QTextCursor &cursor)
1316 const QRegExp expr(expression);
1317 QString text = block.text();
1318 text.replace(QChar::Nbsp, QLatin1Char(' '));
1321 while (offset >=0 && offset <= text.length()) {
1322 idx = (options & QTextDocument::FindBackward) ?
1323 expr.lastIndexIn(text, offset) : expr.indexIn(text, offset);
1327 if (options & QTextDocument::FindWholeWords) {
1328 const int start = idx;
1329 const int end = start + expr.matchedLength();
1330 if ((start != 0 && text.at(start - 1).isLetterOrNumber())
1331 || (end != text.length() && text.at(end).isLetterOrNumber())) {
1332 //if this is not a whole word, continue the search in the string
1333 offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
1338 //we have a hit, return the cursor for that.
1343 cursor = QTextCursor(block.docHandle(), block.position() + idx);
1344 cursor.setPosition(cursor.position() + expr.matchedLength(), QTextCursor::KeepAnchor);
1349 \fn QTextCursor QTextDocument::find(const QRegExp & expr, int position, FindFlags options) const
1353 Finds the next occurrence, matching the regular expression, \a expr, in the document.
1354 The search starts at the given \a position, and proceeds forwards
1355 through the document unless specified otherwise in the search options.
1356 The \a options control the type of search performed. The FindCaseSensitively
1357 option is ignored for this overload, use QRegExp::caseSensitivity instead.
1359 Returns a cursor with the match selected if a match was found; otherwise
1360 returns a null cursor.
1362 If the \a position is 0 (the default) the search begins from the beginning
1363 of the document; otherwise it begins at the specified position.
1365 QTextCursor QTextDocument::find(const QRegExp & expr, int from, FindFlags options) const
1367 Q_D(const QTextDocument);
1370 return QTextCursor();
1373 //the cursor is positioned between characters, so for a backward search
1374 //do not include the character given in the position.
1375 if (options & FindBackward) {
1378 return QTextCursor();
1382 QTextBlock block = d->blocksFind(pos);
1384 if (!(options & FindBackward)) {
1385 int blockOffset = qMax(0, pos - block.position());
1386 while (block.isValid()) {
1387 if (findInBlock(block, expr, blockOffset, options, cursor))
1390 block = block.next();
1393 int blockOffset = pos - block.position();
1394 while (block.isValid()) {
1395 if (findInBlock(block, expr, blockOffset, options, cursor))
1397 block = block.previous();
1398 blockOffset = block.length() - 1;
1402 return QTextCursor();
1406 \fn QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &cursor, FindFlags options) const
1408 Finds the next occurrence, matching the regular expression, \a expr, in the document.
1409 The search starts at the position of the given \a cursor, and proceeds
1410 forwards through the document unless specified otherwise in the search
1411 options. The \a options control the type of search performed. The FindCaseSensitively
1412 option is ignored for this overload, use QRegExp::caseSensitivity instead.
1414 Returns a cursor with the match selected if a match was found; otherwise
1415 returns a null cursor.
1417 If the given \a cursor has a selection, the search begins after the
1418 selection; otherwise it begins at the cursor's position.
1420 By default the search is case-sensitive, and can match text anywhere in the
1423 QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &from, FindFlags options) const
1426 if (!from.isNull()) {
1427 if (options & QTextDocument::FindBackward)
1428 pos = from.selectionStart();
1430 pos = from.selectionEnd();
1432 return find(expr, pos, options);
1437 \fn QTextObject *QTextDocument::createObject(const QTextFormat &format)
1439 Creates and returns a new document object (a QTextObject), based
1440 on the given \a format.
1442 QTextObjects will always get created through this method, so you
1443 must reimplement it if you use custom text objects inside your document.
1445 QTextObject *QTextDocument::createObject(const QTextFormat &f)
1447 QTextObject *obj = 0;
1448 if (f.isListFormat())
1449 obj = new QTextList(this);
1450 else if (f.isTableFormat())
1451 obj = new QTextTable(this);
1452 else if (f.isFrameFormat())
1453 obj = new QTextFrame(this);
1461 Returns the frame that contains the text cursor position \a pos.
1463 QTextFrame *QTextDocument::frameAt(int pos) const
1465 Q_D(const QTextDocument);
1466 return d->frameAt(pos);
1470 Returns the document's root frame.
1472 QTextFrame *QTextDocument::rootFrame() const
1474 Q_D(const QTextDocument);
1475 return d->rootFrame();
1479 Returns the text object associated with the given \a objectIndex.
1481 QTextObject *QTextDocument::object(int objectIndex) const
1483 Q_D(const QTextDocument);
1484 return d->objectForIndex(objectIndex);
1488 Returns the text object associated with the format \a f.
1490 QTextObject *QTextDocument::objectForFormat(const QTextFormat &f) const
1492 Q_D(const QTextDocument);
1493 return d->objectForFormat(f);
1498 Returns the text block that contains the \a{pos}-th character.
1500 QTextBlock QTextDocument::findBlock(int pos) const
1502 Q_D(const QTextDocument);
1503 return QTextBlock(docHandle(), d->blockMap().findNode(pos));
1508 Returns the text block with the specified \a blockNumber.
1510 \sa QTextBlock::blockNumber()
1512 QTextBlock QTextDocument::findBlockByNumber(int blockNumber) const
1514 Q_D(const QTextDocument);
1515 return QTextBlock(docHandle(), d->blockMap().findNode(blockNumber, 1));
1520 Returns the text block that contains the specified \a lineNumber.
1522 \sa QTextBlock::firstLineNumber()
1524 QTextBlock QTextDocument::findBlockByLineNumber(int lineNumber) const
1526 Q_D(const QTextDocument);
1527 return QTextBlock(docHandle(), d->blockMap().findNode(lineNumber, 2));
1531 Returns the document's first text block.
1535 QTextBlock QTextDocument::begin() const
1537 Q_D(const QTextDocument);
1538 return QTextBlock(docHandle(), d->blockMap().begin().n);
1542 This function returns a block to test for the end of the document
1543 while iterating over it.
1545 \snippet doc/src/snippets/textdocumentendsnippet.cpp 0
1547 The block returned is invalid and represents the block after the
1548 last block in the document. You can use lastBlock() to retrieve the
1549 last valid block of the document.
1553 QTextBlock QTextDocument::end() const
1555 return QTextBlock(docHandle(), 0);
1560 Returns the document's first text block.
1562 QTextBlock QTextDocument::firstBlock() const
1564 Q_D(const QTextDocument);
1565 return QTextBlock(docHandle(), d->blockMap().begin().n);
1570 Returns the document's last (valid) text block.
1572 QTextBlock QTextDocument::lastBlock() const
1574 Q_D(const QTextDocument);
1575 return QTextBlock(docHandle(), d->blockMap().last().n);
1579 \property QTextDocument::pageSize
1580 \brief the page size that should be used for laying out the document
1582 By default, for a newly-created, empty document, this property contains
1585 \sa modificationChanged()
1588 void QTextDocument::setPageSize(const QSizeF &size)
1593 d->lout->documentChanged(0, 0, d->length());
1596 QSizeF QTextDocument::pageSize() const
1598 Q_D(const QTextDocument);
1603 returns the number of pages in this document.
1605 int QTextDocument::pageCount() const
1607 return documentLayout()->pageCount();
1611 Sets the default \a font to use in the document layout.
1613 void QTextDocument::setDefaultFont(const QFont &font)
1616 d->setDefaultFont(font);
1618 d->lout->documentChanged(0, 0, d->length());
1622 Returns the default font to be used in the document layout.
1624 QFont QTextDocument::defaultFont() const
1626 Q_D(const QTextDocument);
1627 return d->defaultFont();
1631 \fn QTextDocument::modificationChanged(bool changed)
1633 This signal is emitted whenever the content of the document
1634 changes in a way that affects the modification state. If \a
1635 changed is true, the document has been modified; otherwise it is
1638 For example, calling setModified(false) on a document and then
1639 inserting text causes the signal to get emitted. If you undo that
1640 operation, causing the document to return to its original
1641 unmodified state, the signal will get emitted again.
1645 \property QTextDocument::modified
1646 \brief whether the document has been modified by the user
1648 By default, this property is false.
1650 \sa modificationChanged()
1653 bool QTextDocument::isModified() const
1655 return docHandle()->isModified();
1658 void QTextDocument::setModified(bool m)
1660 docHandle()->setModified(m);
1663 #ifndef QT_NO_PRINTER
1664 static void printPage(int index, QPainter *painter, const QTextDocument *doc, const QRectF &body, const QPointF &pageNumberPos)
1667 painter->translate(body.left(), body.top() - (index - 1) * body.height());
1668 QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
1670 QAbstractTextDocumentLayout *layout = doc->documentLayout();
1671 QAbstractTextDocumentLayout::PaintContext ctx;
1673 painter->setClipRect(view);
1676 // don't use the system palette text as default text color, on HP/UX
1677 // for example that's white, and white text on white paper doesn't
1679 ctx.palette.setColor(QPalette::Text, Qt::black);
1681 layout->draw(painter, ctx);
1683 if (!pageNumberPos.isNull()) {
1684 painter->setClipping(false);
1685 painter->setFont(QFont(doc->defaultFont()));
1686 const QString pageString = QString::number(index);
1688 painter->drawText(qRound(pageNumberPos.x() - painter->fontMetrics().width(pageString)),
1689 qRound(pageNumberPos.y() + view.top()),
1697 Prints the document to the given \a printer. The QPrinter must be
1698 set up before being used with this function.
1700 This is only a convenience method to print the whole document to the printer.
1702 If the document is already paginated through a specified height in the pageSize()
1703 property it is printed as-is.
1705 If the document is not paginated, like for example a document used in a QTextEdit,
1706 then a temporary copy of the document is created and the copy is broken into
1707 multiple pages according to the size of the QPrinter's paperRect(). By default
1708 a 2 cm margin is set around the document contents. In addition the current page
1709 number is printed at the bottom of each page.
1711 Note that QPrinter::Selection is not supported as print range with this function since
1712 the selection is a property of QTextCursor. If you have a QTextEdit associated with
1713 your QTextDocument then you can use QTextEdit's print() function because QTextEdit has
1714 access to the user's selection.
1716 \sa QTextEdit::print()
1719 void QTextDocument::print(QPrinter *printer) const
1721 Q_D(const QTextDocument);
1723 if (!printer || !printer->isValid())
1726 if (!d->title.isEmpty())
1727 printer->setDocName(d->title);
1729 bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
1730 && d->pageSize.height() != INT_MAX;
1732 if (!documentPaginated && !printer->fullPage() && !printer->d_func()->hasCustomPageMargins)
1733 printer->setPageMargins(23.53, 23.53, 23.53, 23.53, QPrinter::Millimeter);
1735 QPainter p(printer);
1737 // Check that there is a valid device to print to.
1741 const QTextDocument *doc = this;
1742 QScopedPointer<QTextDocument> clonedDoc;
1743 (void)doc->documentLayout(); // make sure that there is a layout
1745 QRectF body = QRectF(QPointF(0, 0), d->pageSize);
1746 QPointF pageNumberPos;
1748 if (documentPaginated) {
1749 qreal sourceDpiX = qt_defaultDpi();
1750 qreal sourceDpiY = sourceDpiX;
1752 QPaintDevice *dev = doc->documentLayout()->paintDevice();
1754 sourceDpiX = dev->logicalDpiX();
1755 sourceDpiY = dev->logicalDpiY();
1758 const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
1759 const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
1762 p.scale(dpiScaleX, dpiScaleY);
1764 QSizeF scaledPageSize = d->pageSize;
1765 scaledPageSize.rwidth() *= dpiScaleX;
1766 scaledPageSize.rheight() *= dpiScaleY;
1768 const QSizeF printerPageSize(printer->pageRect().size());
1771 p.scale(printerPageSize.width() / scaledPageSize.width(),
1772 printerPageSize.height() / scaledPageSize.height());
1774 doc = clone(const_cast<QTextDocument *>(this));
1775 clonedDoc.reset(const_cast<QTextDocument *>(doc));
1777 for (QTextBlock srcBlock = firstBlock(), dstBlock = clonedDoc->firstBlock();
1778 srcBlock.isValid() && dstBlock.isValid();
1779 srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
1780 dstBlock.layout()->setAdditionalFormats(srcBlock.layout()->additionalFormats());
1783 QAbstractTextDocumentLayout *layout = doc->documentLayout();
1784 layout->setPaintDevice(p.device());
1786 // copy the custom object handlers
1787 layout->d_func()->handlers = documentLayout()->d_func()->handlers;
1789 int dpiy = p.device()->logicalDpiY();
1791 if (printer->fullPage() && !printer->d_func()->hasCustomPageMargins) {
1792 // for compatibility
1793 margin = (int) ((2/2.54)*dpiy); // 2 cm margins
1794 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
1795 fmt.setMargin(margin);
1796 doc->rootFrame()->setFrameFormat(fmt);
1799 QRectF pageRect(printer->pageRect());
1800 body = QRectF(0, 0, pageRect.width(), pageRect.height());
1801 pageNumberPos = QPointF(body.width() - margin,
1802 body.height() - margin
1803 + QFontMetrics(doc->defaultFont(), p.device()).ascent()
1805 clonedDoc->setPageSize(body.size());
1810 if (printer->collateCopies() == true){
1812 pageCopies = printer->supportsMultipleCopies() ? 1 : printer->copyCount();
1814 docCopies = printer->supportsMultipleCopies() ? 1 : printer->copyCount();
1818 int fromPage = printer->fromPage();
1819 int toPage = printer->toPage();
1820 bool ascending = true;
1822 if (fromPage == 0 && toPage == 0) {
1824 toPage = doc->pageCount();
1827 fromPage = qMax(1, fromPage);
1828 toPage = qMin(doc->pageCount(), toPage);
1830 if (toPage < fromPage) {
1831 // if the user entered a page range outside the actual number
1832 // of printable pages, just return
1836 if (printer->pageOrder() == QPrinter::LastPageFirst) {
1843 for (int i = 0; i < docCopies; ++i) {
1845 int page = fromPage;
1847 for (int j = 0; j < pageCopies; ++j) {
1848 if (printer->printerState() == QPrinter::Aborted
1849 || printer->printerState() == QPrinter::Error)
1851 printPage(page, &p, doc, body, pageNumberPos);
1852 if (j < pageCopies - 1)
1867 if ( i < docCopies - 1)
1874 \enum QTextDocument::ResourceType
1876 This enum describes the types of resources that can be loaded by
1877 QTextDocument's loadResource() function.
1879 \value HtmlResource The resource contains HTML.
1880 \value ImageResource The resource contains image data.
1881 Currently supported data types are QVariant::Pixmap and
1882 QVariant::Image. If the corresponding variant is of type
1883 QVariant::ByteArray then Qt attempts to load the image using
1884 QImage::loadFromData. QVariant::Icon is currently not supported.
1885 The icon needs to be converted to one of the supported types first,
1886 for example using QIcon::pixmap.
1887 \value StyleSheetResource The resource contains CSS.
1888 \value UserResource The first available value for user defined
1895 Returns data of the specified \a type from the resource with the
1898 This function is called by the rich text engine to request data that isn't
1899 directly stored by QTextDocument, but still associated with it. For example,
1900 images are referenced indirectly by the name attribute of a QTextImageFormat
1903 Resources are cached internally in the document. If a resource can
1904 not be found in the cache, loadResource is called to try to load
1905 the resource. loadResource should then use addResource to add the
1906 resource to the cache.
1908 \sa QTextDocument::ResourceType
1910 QVariant QTextDocument::resource(int type, const QUrl &name) const
1912 Q_D(const QTextDocument);
1913 QVariant r = d->resources.value(name);
1915 r = d->cachedResources.value(name);
1917 r = const_cast<QTextDocument *>(this)->loadResource(type, name);
1923 Adds the resource \a resource to the resource cache, using \a
1924 type and \a name as identifiers. \a type should be a value from
1925 QTextDocument::ResourceType.
1927 For example, you can add an image as a resource in order to reference it
1928 from within the document:
1930 \snippet snippets/textdocument-resources/main.cpp Adding a resource
1932 The image can be inserted into the document using the QTextCursor API:
1934 \snippet snippets/textdocument-resources/main.cpp Inserting an image with a cursor
1936 Alternatively, you can insert images using the HTML \c img tag:
1938 \snippet snippets/textdocument-resources/main.cpp Inserting an image using HTML
1940 void QTextDocument::addResource(int type, const QUrl &name, const QVariant &resource)
1944 d->resources.insert(name, resource);
1948 Loads data of the specified \a type from the resource with the
1951 This function is called by the rich text engine to request data that isn't
1952 directly stored by QTextDocument, but still associated with it. For example,
1953 images are referenced indirectly by the name attribute of a QTextImageFormat
1956 When called by Qt, \a type is one of the values of
1957 QTextDocument::ResourceType.
1959 If the QTextDocument is a child object of a QTextEdit, QTextBrowser,
1960 or a QTextDocument itself then the default implementation tries
1961 to retrieve the data from the parent.
1963 QVariant QTextDocument::loadResource(int type, const QUrl &name)
1968 QTextDocument *doc = qobject_cast<QTextDocument *>(parent());
1970 r = doc->loadResource(type, name);
1973 // ### Qt5: reenable
1974 #ifndef QT_NO_TEXTEDIT
1975 else if (QTextEdit *edit = qobject_cast<QTextEdit *>(parent())) {
1976 QUrl resolvedName = edit->d_func()->resolveUrl(name);
1977 r = edit->loadResource(type, resolvedName);
1980 #ifndef QT_NO_TEXTCONTROL
1981 else if (QWidgetTextControl *control = qobject_cast<QWidgetTextControl *>(parent())) {
1982 r = control->loadResource(type, name);
1987 // handle data: URLs
1988 if (r.isNull() && name.scheme().compare(QLatin1String("data"), Qt::CaseInsensitive) == 0)
1989 r = qDecodeDataUrl(name).second;
1991 // if resource was not loaded try to load it here
1992 if (!doc && r.isNull() && name.isRelative()) {
1993 QUrl currentURL = d->url;
1994 QUrl resourceUrl = name;
1996 // For the second case QUrl can merge "#someanchor" with "foo.html"
1997 // correctly to "foo.html#someanchor"
1998 if (!(currentURL.isRelative()
1999 || (currentURL.scheme() == QLatin1String("file")
2000 && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
2001 || (name.hasFragment() && name.path().isEmpty())) {
2002 resourceUrl = currentURL.resolved(name);
2004 // this is our last resort when current url and new url are both relative
2005 // we try to resolve against the current working directory in the local
2007 QFileInfo fi(currentURL.toLocalFile());
2010 QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
2011 } else if (currentURL.isEmpty()) {
2012 resourceUrl.setScheme(QLatin1String("file"));
2016 QString s = resourceUrl.toLocalFile();
2018 if (!s.isEmpty() && f.open(QFile::ReadOnly)) {
2025 if (type == ImageResource && r.type() == QVariant::ByteArray) {
2026 if (qApp->thread() != QThread::currentThread()) {
2027 // must use images in non-GUI threads
2029 image.loadFromData(r.toByteArray());
2030 if (!image.isNull())
2034 pm.loadFromData(r.toByteArray());
2039 d->cachedResources.insert(name, r);
2044 static QTextFormat formatDifference(const QTextFormat &from, const QTextFormat &to)
2046 QTextFormat diff = to;
2048 const QMap<int, QVariant> props = to.properties();
2049 for (QMap<int, QVariant>::ConstIterator it = props.begin(), end = props.end();
2051 if (it.value() == from.property(it.key()))
2052 diff.clearProperty(it.key());
2057 QTextHtmlExporter::QTextHtmlExporter(const QTextDocument *_doc)
2058 : doc(_doc), fragmentMarkers(false)
2060 const QFont defaultFont = doc->defaultFont();
2061 defaultCharFormat.setFont(defaultFont);
2062 // don't export those for the default font since we cannot turn them off with CSS
2063 defaultCharFormat.clearProperty(QTextFormat::FontUnderline);
2064 defaultCharFormat.clearProperty(QTextFormat::FontOverline);
2065 defaultCharFormat.clearProperty(QTextFormat::FontStrikeOut);
2066 defaultCharFormat.clearProperty(QTextFormat::TextUnderlineStyle);
2070 Returns the document in HTML format. The conversion may not be
2071 perfect, especially for complex documents, due to the limitations
2074 QString QTextHtmlExporter::toHtml(const QByteArray &encoding, ExportMode mode)
2076 html = QLatin1String("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
2077 "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
2078 "<html><head><meta name=\"qrichtext\" content=\"1\" />");
2079 html.reserve(doc->docHandle()->length());
2081 fragmentMarkers = (mode == ExportFragment);
2083 if (!encoding.isEmpty())
2084 html += QString::fromLatin1("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\" />").arg(QString::fromAscii(encoding));
2086 QString title = doc->metaInformation(QTextDocument::DocumentTitle);
2087 if (!title.isEmpty())
2088 html += QString::fromLatin1("<title>") + title + QString::fromLatin1("</title>");
2089 html += QLatin1String("<style type=\"text/css\">\n");
2090 html += QLatin1String("p, li { white-space: pre-wrap; }\n");
2091 html += QLatin1String("</style>");
2092 html += QLatin1String("</head><body");
2094 if (mode == ExportEntireDocument) {
2095 html += QLatin1String(" style=\"");
2097 emitFontFamily(defaultCharFormat.fontFamily());
2099 if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) {
2100 html += QLatin1String(" font-size:");
2101 html += QString::number(defaultCharFormat.fontPointSize());
2102 html += QLatin1String("pt;");
2103 } else if (defaultCharFormat.hasProperty(QTextFormat::FontPixelSize)) {
2104 html += QLatin1String(" font-size:");
2105 html += QString::number(defaultCharFormat.intProperty(QTextFormat::FontPixelSize));
2106 html += QLatin1String("px;");
2109 html += QLatin1String(" font-weight:");
2110 html += QString::number(defaultCharFormat.fontWeight() * 8);
2111 html += QLatin1Char(';');
2113 html += QLatin1String(" font-style:");
2114 html += (defaultCharFormat.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2115 html += QLatin1Char(';');
2117 // do not set text-decoration on the default font since those values are /always/ propagated
2118 // and cannot be turned off with CSS
2120 html += QLatin1Char('\"');
2122 const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2123 emitBackgroundAttribute(fmt);
2126 defaultCharFormat = QTextCharFormat();
2128 html += QLatin1Char('>');
2130 QTextFrameFormat rootFmt = doc->rootFrame()->frameFormat();
2131 rootFmt.clearProperty(QTextFormat::BackgroundBrush);
2133 QTextFrameFormat defaultFmt;
2134 defaultFmt.setMargin(doc->documentMargin());
2136 if (rootFmt == defaultFmt)
2137 emitFrame(doc->rootFrame()->begin());
2139 emitTextFrame(doc->rootFrame());
2141 html += QLatin1String("</body></html>");
2145 void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &value)
2147 html += QLatin1Char(' ');
2148 html += QLatin1String(attribute);
2149 html += QLatin1String("=\"");
2150 html += Qt::escape(value);
2151 html += QLatin1Char('"');
2154 bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
2156 bool attributesEmitted = false;
2159 const QString family = format.fontFamily();
2160 if (!family.isEmpty() && family != defaultCharFormat.fontFamily()) {
2161 emitFontFamily(family);
2162 attributesEmitted = true;
2166 if (format.hasProperty(QTextFormat::FontPointSize)
2167 && format.fontPointSize() != defaultCharFormat.fontPointSize()) {
2168 html += QLatin1String(" font-size:");
2169 html += QString::number(format.fontPointSize());
2170 html += QLatin1String("pt;");
2171 attributesEmitted = true;
2172 } else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) {
2173 static const char * const sizeNames[] = {
2174 "small", "medium", "large", "x-large", "xx-large"
2176 const char *name = 0;
2177 const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1;
2178 if (idx >= 0 && idx <= 4) {
2179 name = sizeNames[idx];
2182 html += QLatin1String(" font-size:");
2183 html += QLatin1String(name);
2184 html += QLatin1Char(';');
2185 attributesEmitted = true;
2187 } else if (format.hasProperty(QTextFormat::FontPixelSize)) {
2188 html += QLatin1String(" font-size:");
2189 html += QString::number(format.intProperty(QTextFormat::FontPixelSize));
2190 html += QLatin1String("px;");
2193 if (format.hasProperty(QTextFormat::FontWeight)
2194 && format.fontWeight() != defaultCharFormat.fontWeight()) {
2195 html += QLatin1String(" font-weight:");
2196 html += QString::number(format.fontWeight() * 8);
2197 html += QLatin1Char(';');
2198 attributesEmitted = true;
2201 if (format.hasProperty(QTextFormat::FontItalic)
2202 && format.fontItalic() != defaultCharFormat.fontItalic()) {
2203 html += QLatin1String(" font-style:");
2204 html += (format.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2205 html += QLatin1Char(';');
2206 attributesEmitted = true;
2209 QLatin1String decorationTag(" text-decoration:");
2210 html += decorationTag;
2211 bool hasDecoration = false;
2212 bool atLeastOneDecorationSet = false;
2214 if ((format.hasProperty(QTextFormat::FontUnderline) || format.hasProperty(QTextFormat::TextUnderlineStyle))
2215 && format.fontUnderline() != defaultCharFormat.fontUnderline()) {
2216 hasDecoration = true;
2217 if (format.fontUnderline()) {
2218 html += QLatin1String(" underline");
2219 atLeastOneDecorationSet = true;
2223 if (format.hasProperty(QTextFormat::FontOverline)
2224 && format.fontOverline() != defaultCharFormat.fontOverline()) {
2225 hasDecoration = true;
2226 if (format.fontOverline()) {
2227 html += QLatin1String(" overline");
2228 atLeastOneDecorationSet = true;
2232 if (format.hasProperty(QTextFormat::FontStrikeOut)
2233 && format.fontStrikeOut() != defaultCharFormat.fontStrikeOut()) {
2234 hasDecoration = true;
2235 if (format.fontStrikeOut()) {
2236 html += QLatin1String(" line-through");
2237 atLeastOneDecorationSet = true;
2241 if (hasDecoration) {
2242 if (!atLeastOneDecorationSet)
2243 html += QLatin1String("none");
2244 html += QLatin1Char(';');
2245 attributesEmitted = true;
2247 html.chop(qstrlen(decorationTag.latin1()));
2250 if (format.foreground() != defaultCharFormat.foreground()
2251 && format.foreground().style() != Qt::NoBrush) {
2252 html += QLatin1String(" color:");
2253 html += format.foreground().color().name();
2254 html += QLatin1Char(';');
2255 attributesEmitted = true;
2258 if (format.background() != defaultCharFormat.background()
2259 && format.background().style() == Qt::SolidPattern) {
2260 html += QLatin1String(" background-color:");
2261 html += format.background().color().name();
2262 html += QLatin1Char(';');
2263 attributesEmitted = true;
2266 if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()
2267 && format.verticalAlignment() != QTextCharFormat::AlignNormal)
2269 html += QLatin1String(" vertical-align:");
2271 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2272 if (valign == QTextCharFormat::AlignSubScript)
2273 html += QLatin1String("sub");
2274 else if (valign == QTextCharFormat::AlignSuperScript)
2275 html += QLatin1String("super");
2276 else if (valign == QTextCharFormat::AlignMiddle)
2277 html += QLatin1String("middle");
2278 else if (valign == QTextCharFormat::AlignTop)
2279 html += QLatin1String("top");
2280 else if (valign == QTextCharFormat::AlignBottom)
2281 html += QLatin1String("bottom");
2283 html += QLatin1Char(';');
2284 attributesEmitted = true;
2287 if (format.fontCapitalization() != QFont::MixedCase) {
2288 const QFont::Capitalization caps = format.fontCapitalization();
2289 if (caps == QFont::AllUppercase)
2290 html += QLatin1String(" text-transform:uppercase;");
2291 else if (caps == QFont::AllLowercase)
2292 html += QLatin1String(" text-transform:lowercase;");
2293 else if (caps == QFont::SmallCaps)
2294 html += QLatin1String(" font-variant:small-caps;");
2295 attributesEmitted = true;
2298 if (format.fontWordSpacing() != 0.0) {
2299 html += QLatin1String(" word-spacing:");
2300 html += QString::number(format.fontWordSpacing());
2301 html += QLatin1String("px;");
2302 attributesEmitted = true;
2305 return attributesEmitted;
2308 void QTextHtmlExporter::emitTextLength(const char *attribute, const QTextLength &length)
2310 if (length.type() == QTextLength::VariableLength) // default
2313 html += QLatin1Char(' ');
2314 html += QLatin1String(attribute);
2315 html += QLatin1String("=\"");
2316 html += QString::number(length.rawValue());
2318 if (length.type() == QTextLength::PercentageLength)
2319 html += QLatin1String("%\"");
2321 html += QLatin1Char('\"');
2324 void QTextHtmlExporter::emitAlignment(Qt::Alignment align)
2326 if (align & Qt::AlignLeft)
2328 else if (align & Qt::AlignRight)
2329 html += QLatin1String(" align=\"right\"");
2330 else if (align & Qt::AlignHCenter)
2331 html += QLatin1String(" align=\"center\"");
2332 else if (align & Qt::AlignJustify)
2333 html += QLatin1String(" align=\"justify\"");
2336 void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
2338 if (pos == QTextFrameFormat::InFlow)
2341 if (mode == EmitStyleTag)
2342 html += QLatin1String(" style=\"float:");
2344 html += QLatin1String(" float:");
2346 if (pos == QTextFrameFormat::FloatLeft)
2347 html += QLatin1String(" left;");
2348 else if (pos == QTextFrameFormat::FloatRight)
2349 html += QLatin1String(" right;");
2351 Q_ASSERT_X(0, "QTextHtmlExporter::emitFloatStyle()", "pos should be a valid enum type");
2353 if (mode == EmitStyleTag)
2354 html += QLatin1Char('\"');
2357 void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style)
2359 Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
2361 html += QLatin1String(" border-style:");
2364 case QTextFrameFormat::BorderStyle_None:
2365 html += QLatin1String("none");
2367 case QTextFrameFormat::BorderStyle_Dotted:
2368 html += QLatin1String("dotted");
2370 case QTextFrameFormat::BorderStyle_Dashed:
2371 html += QLatin1String("dashed");
2373 case QTextFrameFormat::BorderStyle_Solid:
2374 html += QLatin1String("solid");
2376 case QTextFrameFormat::BorderStyle_Double:
2377 html += QLatin1String("double");
2379 case QTextFrameFormat::BorderStyle_DotDash:
2380 html += QLatin1String("dot-dash");
2382 case QTextFrameFormat::BorderStyle_DotDotDash:
2383 html += QLatin1String("dot-dot-dash");
2385 case QTextFrameFormat::BorderStyle_Groove:
2386 html += QLatin1String("groove");
2388 case QTextFrameFormat::BorderStyle_Ridge:
2389 html += QLatin1String("ridge");
2391 case QTextFrameFormat::BorderStyle_Inset:
2392 html += QLatin1String("inset");
2394 case QTextFrameFormat::BorderStyle_Outset:
2395 html += QLatin1String("outset");
2402 html += QLatin1Char(';');
2405 void QTextHtmlExporter::emitPageBreakPolicy(QTextFormat::PageBreakFlags policy)
2407 if (policy & QTextFormat::PageBreak_AlwaysBefore)
2408 html += QLatin1String(" page-break-before:always;");
2410 if (policy & QTextFormat::PageBreak_AlwaysAfter)
2411 html += QLatin1String(" page-break-after:always;");
2414 void QTextHtmlExporter::emitFontFamily(const QString &family)
2416 html += QLatin1String(" font-family:");
2418 QLatin1String quote("\'");
2419 if (family.contains(QLatin1Char('\'')))
2420 quote = QLatin1String(""");
2423 html += Qt::escape(family);
2425 html += QLatin1Char(';');
2428 void QTextHtmlExporter::emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right)
2430 html += QLatin1String(" margin-top:");
2432 html += QLatin1String("px;");
2434 html += QLatin1String(" margin-bottom:");
2436 html += QLatin1String("px;");
2438 html += QLatin1String(" margin-left:");
2440 html += QLatin1String("px;");
2442 html += QLatin1String(" margin-right:");
2444 html += QLatin1String("px;");
2447 void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
2449 const QTextCharFormat format = fragment.charFormat();
2451 bool closeAnchor = false;
2453 if (format.isAnchor()) {
2454 const QString name = format.anchorName();
2455 if (!name.isEmpty()) {
2456 html += QLatin1String("<a name=\"");
2457 html += Qt::escape(name);
2458 html += QLatin1String("\"></a>");
2460 const QString href = format.anchorHref();
2461 if (!href.isEmpty()) {
2462 html += QLatin1String("<a href=\"");
2463 html += Qt::escape(href);
2464 html += QLatin1String("\">");
2469 QString txt = fragment.text();
2470 const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
2471 const bool isImage = isObject && format.isImageFormat();
2473 QLatin1String styleTag("<span style=\"");
2476 bool attributesEmitted = false;
2478 attributesEmitted = emitCharFormatStyle(format);
2479 if (attributesEmitted)
2480 html += QLatin1String("\">");
2482 html.chop(qstrlen(styleTag.latin1()));
2485 for (int i = 0; isImage && i < txt.length(); ++i) {
2486 QTextImageFormat imgFmt = format.toImageFormat();
2488 html += QLatin1String("<img");
2490 if (imgFmt.hasProperty(QTextFormat::ImageName))
2491 emitAttribute("src", imgFmt.name());
2493 if (imgFmt.hasProperty(QTextFormat::ImageWidth))
2494 emitAttribute("width", QString::number(imgFmt.width()));
2496 if (imgFmt.hasProperty(QTextFormat::ImageHeight))
2497 emitAttribute("height", QString::number(imgFmt.height()));
2499 if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
2500 html += QLatin1String(" style=\"vertical-align: middle;\"");
2501 else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
2502 html += QLatin1String(" style=\"vertical-align: top;\"");
2504 if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
2505 emitFloatStyle(imageFrame->frameFormat().position());
2507 html += QLatin1String(" />");
2510 Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
2512 txt = Qt::escape(txt);
2514 // split for [\n{LineSeparator}]
2515 QString forcedLineBreakRegExp = QString::fromLatin1("[\\na]");
2516 forcedLineBreakRegExp[3] = QChar::LineSeparator;
2518 const QStringList lines = txt.split(QRegExp(forcedLineBreakRegExp));
2519 for (int i = 0; i < lines.count(); ++i) {
2521 html += QLatin1String("<br />"); // space on purpose for compatibility with Netscape, Lynx & Co.
2522 html += lines.at(i);
2526 if (attributesEmitted)
2527 html += QLatin1String("</span>");
2530 html += QLatin1String("</a>");
2533 static bool isOrderedList(int style)
2535 return style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
2536 || style == QTextListFormat::ListUpperAlpha
2537 || style == QTextListFormat::ListUpperRoman
2538 || style == QTextListFormat::ListLowerRoman
2542 void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
2544 QTextBlockFormat format = block.blockFormat();
2545 emitAlignment(format.alignment());
2547 // assume default to not bloat the html too much
2548 // html += QLatin1String(" dir='ltr'");
2549 if (block.textDirection() == Qt::RightToLeft)
2550 html += QLatin1String(" dir='rtl'");
2552 QLatin1String style(" style=\"");
2555 const bool emptyBlock = block.begin().atEnd();
2557 html += QLatin1String("-qt-paragraph-type:empty;");
2560 emitMargins(QString::number(format.topMargin()),
2561 QString::number(format.bottomMargin()),
2562 QString::number(format.leftMargin()),
2563 QString::number(format.rightMargin()));
2565 html += QLatin1String(" -qt-block-indent:");
2566 html += QString::number(format.indent());
2567 html += QLatin1Char(';');
2569 html += QLatin1String(" text-indent:");
2570 html += QString::number(format.textIndent());
2571 html += QLatin1String("px;");
2573 if (block.userState() != -1) {
2574 html += QLatin1String(" -qt-user-state:");
2575 html += QString::number(block.userState());
2576 html += QLatin1Char(';');
2579 emitPageBreakPolicy(format.pageBreakPolicy());
2581 QTextCharFormat diff;
2582 if (emptyBlock) { // only print character properties when we don't expect them to be repeated by actual text in the parag
2583 const QTextCharFormat blockCharFmt = block.charFormat();
2584 diff = formatDifference(defaultCharFormat, blockCharFmt).toCharFormat();
2587 diff.clearProperty(QTextFormat::BackgroundBrush);
2588 if (format.hasProperty(QTextFormat::BackgroundBrush)) {
2589 QBrush bg = format.background();
2590 if (bg.style() != Qt::NoBrush)
2591 diff.setProperty(QTextFormat::BackgroundBrush, format.property(QTextFormat::BackgroundBrush));
2594 if (!diff.properties().isEmpty())
2595 emitCharFormatStyle(diff);
2597 html += QLatin1Char('"');
2601 void QTextHtmlExporter::emitBlock(const QTextBlock &block)
2603 if (block.begin().atEnd()) {
2604 // ### HACK, remove once QTextFrame::Iterator is fixed
2605 int p = block.position();
2608 QTextDocumentPrivate::FragmentIterator frag = doc->docHandle()->find(p);
2609 QChar ch = doc->docHandle()->buffer().at(frag->stringPosition);
2610 if (ch == QTextBeginningOfFrame
2611 || ch == QTextEndOfFrame)
2615 html += QLatin1Char('\n');
2617 // save and later restore, in case we 'change' the default format by
2618 // emitting block char format information
2619 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2621 QTextList *list = block.textList();
2623 if (list->itemNumber(block) == 0) { // first item? emit <ul> or appropriate
2624 const QTextListFormat format = list->format();
2625 const int style = format.style();
2627 case QTextListFormat::ListDecimal: html += QLatin1String("<ol"); break;
2628 case QTextListFormat::ListDisc: html += QLatin1String("<ul"); break;
2629 case QTextListFormat::ListCircle: html += QLatin1String("<ul type=\"circle\""); break;
2630 case QTextListFormat::ListSquare: html += QLatin1String("<ul type=\"square\""); break;
2631 case QTextListFormat::ListLowerAlpha: html += QLatin1String("<ol type=\"a\""); break;
2632 case QTextListFormat::ListUpperAlpha: html += QLatin1String("<ol type=\"A\""); break;
2633 case QTextListFormat::ListLowerRoman: html += QLatin1String("<ol type=\"i\""); break;
2634 case QTextListFormat::ListUpperRoman: html += QLatin1String("<ol type=\"I\""); break;
2635 default: html += QLatin1String("<ul"); // ### should not happen
2638 QString styleString = QString::fromLatin1("margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;");
2640 if (format.hasProperty(QTextFormat::ListIndent)) {
2641 styleString += QLatin1String(" -qt-list-indent: ");
2642 styleString += QString::number(format.indent());
2643 styleString += QLatin1Char(';');
2646 if (format.hasProperty(QTextFormat::ListNumberPrefix)) {
2647 QString numberPrefix = format.numberPrefix();
2648 numberPrefix.replace(QLatin1Char('"'), QLatin1String("\\22"));
2649 numberPrefix.replace(QLatin1Char('\''), QLatin1String("\\27")); // FIXME: There's a problem in the CSS parser the prevents this from being correctly restored
2650 styleString += QLatin1String(" -qt-list-number-prefix: ");
2651 styleString += QLatin1Char('\'');
2652 styleString += numberPrefix;
2653 styleString += QLatin1Char('\'');
2654 styleString += QLatin1Char(';');
2657 if (format.hasProperty(QTextFormat::ListNumberSuffix)) {
2658 if (format.numberSuffix() != QLatin1String(".")) { // this is our default
2659 QString numberSuffix = format.numberSuffix();
2660 numberSuffix.replace(QLatin1Char('"'), QLatin1String("\\22"));
2661 numberSuffix.replace(QLatin1Char('\''), QLatin1String("\\27")); // see above
2662 styleString += QLatin1String(" -qt-list-number-suffix: ");
2663 styleString += QLatin1Char('\'');
2664 styleString += numberSuffix;
2665 styleString += QLatin1Char('\'');
2666 styleString += QLatin1Char(';');
2670 html += QLatin1String(" style=\"");
2671 html += styleString;
2672 html += QLatin1String("\">");
2675 html += QLatin1String("<li");
2677 const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
2678 if (!blockFmt.properties().isEmpty()) {
2679 html += QLatin1String(" style=\"");
2680 emitCharFormatStyle(blockFmt);
2681 html += QLatin1Char('\"');
2683 defaultCharFormat.merge(block.charFormat());
2687 const QTextBlockFormat blockFormat = block.blockFormat();
2688 if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
2689 html += QLatin1String("<hr");
2691 QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
2692 if (width.type() != QTextLength::VariableLength)
2693 emitTextLength("width", width);
2695 html += QLatin1Char(' ');
2697 html += QLatin1String("/>");
2701 const bool pre = blockFormat.nonBreakableLines();
2704 html += QLatin1Char('>');
2705 html += QLatin1String("<pre");
2707 html += QLatin1String("<p");
2710 emitBlockAttributes(block);
2712 html += QLatin1Char('>');
2713 if (block.begin().atEnd())
2714 html += QLatin1String("<br />");
2716 QTextBlock::Iterator it = block.begin();
2717 if (fragmentMarkers && !it.atEnd() && block == doc->begin())
2718 html += QLatin1String("<!--StartFragment-->");
2720 for (; !it.atEnd(); ++it)
2721 emitFragment(it.fragment());
2723 if (fragmentMarkers && block.position() + block.length() == doc->docHandle()->length())
2724 html += QLatin1String("<!--EndFragment-->");
2727 html += QLatin1String("</pre>");
2729 html += QLatin1String("</li>");
2731 html += QLatin1String("</p>");
2734 if (list->itemNumber(block) == list->count() - 1) { // last item? close list
2735 if (isOrderedList(list->format().style()))
2736 html += QLatin1String("</ol>");
2738 html += QLatin1String("</ul>");
2742 defaultCharFormat = oldDefaultCharFormat;
2745 extern bool qHasPixmapTexture(const QBrush& brush);
2747 QString QTextHtmlExporter::findUrlForImage(const QTextDocument *doc, qint64 cacheKey, bool isPixmap)
2753 if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent()))
2754 return findUrlForImage(parent, cacheKey, isPixmap);
2756 if (doc && doc->docHandle()) {
2757 QTextDocumentPrivate *priv = doc->docHandle();
2758 QMap<QUrl, QVariant>::const_iterator it = priv->cachedResources.constBegin();
2759 for (; it != priv->cachedResources.constEnd(); ++it) {
2761 const QVariant &v = it.value();
2762 if (v.type() == QVariant::Image && !isPixmap) {
2763 if (qvariant_cast<QImage>(v).cacheKey() == cacheKey)
2767 if (v.type() == QVariant::Pixmap && isPixmap) {
2768 if (qvariant_cast<QPixmap>(v).cacheKey() == cacheKey)
2773 if (it != priv->cachedResources.constEnd())
2774 url = it.key().toString();
2780 void QTextDocumentPrivate::mergeCachedResources(const QTextDocumentPrivate *priv)
2785 cachedResources.unite(priv->cachedResources);
2788 void QTextHtmlExporter::emitBackgroundAttribute(const QTextFormat &format)
2790 if (format.hasProperty(QTextFormat::BackgroundImageUrl)) {
2791 QString url = format.property(QTextFormat::BackgroundImageUrl).toString();
2792 emitAttribute("background", url);
2794 const QBrush &brush = format.background();
2795 if (brush.style() == Qt::SolidPattern) {
2796 emitAttribute("bgcolor", brush.color().name());
2797 } else if (brush.style() == Qt::TexturePattern) {
2798 const bool isPixmap = qHasPixmapTexture(brush);
2799 const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
2801 const QString url = findUrlForImage(doc, cacheKey, isPixmap);
2804 emitAttribute("background", url);
2809 void QTextHtmlExporter::emitTable(const QTextTable *table)
2811 QTextTableFormat format = table->format();
2813 html += QLatin1String("\n<table");
2815 if (format.hasProperty(QTextFormat::FrameBorder))
2816 emitAttribute("border", QString::number(format.border()));
2818 emitFrameStyle(format, TableFrame);
2820 emitAlignment(format.alignment());
2821 emitTextLength("width", format.width());
2823 if (format.hasProperty(QTextFormat::TableCellSpacing))
2824 emitAttribute("cellspacing", QString::number(format.cellSpacing()));
2825 if (format.hasProperty(QTextFormat::TableCellPadding))
2826 emitAttribute("cellpadding", QString::number(format.cellPadding()));
2828 emitBackgroundAttribute(format);
2830 html += QLatin1Char('>');
2832 const int rows = table->rows();
2833 const int columns = table->columns();
2835 QVector<QTextLength> columnWidths = format.columnWidthConstraints();
2836 if (columnWidths.isEmpty()) {
2837 columnWidths.resize(columns);
2838 columnWidths.fill(QTextLength());
2840 Q_ASSERT(columnWidths.count() == columns);
2842 QVarLengthArray<bool> widthEmittedForColumn(columns);
2843 for (int i = 0; i < columns; ++i)
2844 widthEmittedForColumn[i] = false;
2846 const int headerRowCount = qMin(format.headerRowCount(), rows);
2847 if (headerRowCount > 0)
2848 html += QLatin1String("<thead>");
2850 for (int row = 0; row < rows; ++row) {
2851 html += QLatin1String("\n<tr>");
2853 for (int col = 0; col < columns; ++col) {
2854 const QTextTableCell cell = table->cellAt(row, col);
2857 if (cell.row() != row)
2860 if (cell.column() != col)
2863 html += QLatin1String("\n<td");
2865 if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
2866 emitTextLength("width", columnWidths.at(col));
2867 widthEmittedForColumn[col] = true;
2870 if (cell.columnSpan() > 1)
2871 emitAttribute("colspan", QString::number(cell.columnSpan()));
2873 if (cell.rowSpan() > 1)
2874 emitAttribute("rowspan", QString::number(cell.rowSpan()));
2876 const QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
2877 emitBackgroundAttribute(cellFormat);
2879 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2881 QTextCharFormat::VerticalAlignment valign = cellFormat.verticalAlignment();
2883 QString styleString;
2884 if (valign >= QTextCharFormat::AlignMiddle && valign <= QTextCharFormat::AlignBottom) {
2885 styleString += QLatin1String(" vertical-align:");
2887 case QTextCharFormat::AlignMiddle:
2888 styleString += QLatin1String("middle");
2890 case QTextCharFormat::AlignTop:
2891 styleString += QLatin1String("top");
2893 case QTextCharFormat::AlignBottom:
2894 styleString += QLatin1String("bottom");
2899 styleString += QLatin1Char(';');
2901 QTextCharFormat temp;
2902 temp.setVerticalAlignment(valign);
2903 defaultCharFormat.merge(temp);
2906 if (cellFormat.hasProperty(QTextFormat::TableCellLeftPadding))
2907 styleString += QLatin1String(" padding-left:") + QString::number(cellFormat.leftPadding()) + QLatin1Char(';');
2908 if (cellFormat.hasProperty(QTextFormat::TableCellRightPadding))
2909 styleString += QLatin1String(" padding-right:") + QString::number(cellFormat.rightPadding()) + QLatin1Char(';');
2910 if (cellFormat.hasProperty(QTextFormat::TableCellTopPadding))
2911 styleString += QLatin1String(" padding-top:") + QString::number(cellFormat.topPadding()) + QLatin1Char(';');
2912 if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
2913 styleString += QLatin1String(" padding-bottom:") + QString::number(cellFormat.bottomPadding()) + QLatin1Char(';');
2915 if (!styleString.isEmpty())
2916 html += QLatin1String(" style=\"") + styleString + QLatin1Char('\"');
2918 html += QLatin1Char('>');
2920 emitFrame(cell.begin());
2922 html += QLatin1String("</td>");
2924 defaultCharFormat = oldDefaultCharFormat;
2927 html += QLatin1String("</tr>");
2928 if (headerRowCount > 0 && row == headerRowCount - 1)
2929 html += QLatin1String("</thead>");
2932 html += QLatin1String("</table>");
2935 void QTextHtmlExporter::emitFrame(QTextFrame::Iterator frameIt)
2937 if (!frameIt.atEnd()) {
2938 QTextFrame::Iterator next = frameIt;
2941 && frameIt.currentFrame() == 0
2942 && frameIt.parentFrame() != doc->rootFrame()
2943 && frameIt.currentBlock().begin().atEnd())
2947 for (QTextFrame::Iterator it = frameIt;
2948 !it.atEnd(); ++it) {
2949 if (QTextFrame *f = it.currentFrame()) {
2950 if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
2955 } else if (it.currentBlock().isValid()) {
2956 emitBlock(it.currentBlock());
2961 void QTextHtmlExporter::emitTextFrame(const QTextFrame *f)
2963 FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
2965 html += QLatin1String("\n<table");
2966 QTextFrameFormat format = f->frameFormat();
2968 if (format.hasProperty(QTextFormat::FrameBorder))
2969 emitAttribute("border", QString::number(format.border()));
2971 emitFrameStyle(format, frameType);
2973 emitTextLength("width", format.width());
2974 emitTextLength("height", format.height());
2976 // root frame's bcolor goes in the <body> tag
2977 if (frameType != RootFrame)
2978 emitBackgroundAttribute(format);
2980 html += QLatin1Char('>');
2981 html += QLatin1String("\n<tr>\n<td style=\"border: none;\">");
2982 emitFrame(f->begin());
2983 html += QLatin1String("</td></tr></table>");
2986 void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType frameType)
2988 QLatin1String styleAttribute(" style=\"");
2989 html += styleAttribute;
2990 const int originalHtmlLength = html.length();
2992 if (frameType == TextFrame)
2993 html += QLatin1String("-qt-table-type: frame;");
2994 else if (frameType == RootFrame)
2995 html += QLatin1String("-qt-table-type: root;");
2997 const QTextFrameFormat defaultFormat;
2999 emitFloatStyle(format.position(), OmitStyleTag);
3000 emitPageBreakPolicy(format.pageBreakPolicy());
3002 if (format.borderBrush() != defaultFormat.borderBrush()) {
3003 html += QLatin1String(" border-color:");
3004 html += format.borderBrush().color().name();
3005 html += QLatin1Char(';');
3008 if (format.borderStyle() != defaultFormat.borderStyle())
3009 emitBorderStyle(format.borderStyle());
3011 if (format.hasProperty(QTextFormat::FrameMargin)
3012 || format.hasProperty(QTextFormat::FrameLeftMargin)
3013 || format.hasProperty(QTextFormat::FrameRightMargin)
3014 || format.hasProperty(QTextFormat::FrameTopMargin)
3015 || format.hasProperty(QTextFormat::FrameBottomMargin))
3016 emitMargins(QString::number(format.topMargin()),
3017 QString::number(format.bottomMargin()),
3018 QString::number(format.leftMargin()),
3019 QString::number(format.rightMargin()));
3021 if (html.length() == originalHtmlLength) // nothing emitted?
3022 html.chop(qstrlen(styleAttribute.latin1()));
3024 html += QLatin1Char('\"');
3028 Returns a string containing an HTML representation of the document.
3030 The \a encoding parameter specifies the value for the charset attribute
3031 in the html header. For example if 'utf-8' is specified then the
3032 beginning of the generated html will look like this:
3033 \snippet doc/src/snippets/code/src_gui_text_qtextdocument.cpp 1
3035 If no encoding is specified then no such meta information is generated.
3037 If you later on convert the returned html string into a byte array for
3038 transmission over a network or when saving to disk you should specify
3039 the encoding you're going to use for the conversion to a byte array here.
3041 \sa {Supported HTML Subset}
3043 #ifndef QT_NO_TEXTHTMLPARSER
3044 QString QTextDocument::toHtml(const QByteArray &encoding) const
3046 return QTextHtmlExporter(this).toHtml(encoding);
3048 #endif // QT_NO_TEXTHTMLPARSER
3051 Returns a vector of text formats for all the formats used in the document.
3053 QVector<QTextFormat> QTextDocument::allFormats() const
3055 Q_D(const QTextDocument);
3056 return d->formatCollection()->formats;
3063 So that not all classes have to be friends of each other...
3065 QTextDocumentPrivate *QTextDocument::docHandle() const
3067 Q_D(const QTextDocument);
3068 return const_cast<QTextDocumentPrivate *>(d);
3073 \fn QTextDocument::undoCommandAdded()
3075 This signal is emitted every time a new level of undo is added to the QTextDocument.