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"
59 #include <qfileinfo.h>
62 #include "private/qdataurl_p.h"
64 #include "qtextdocument_p.h"
65 #include <private/qabstracttextdocumentlayout_p.h>
66 #include "qpagedpaintdevice.h"
67 #include "private/qpagedpaintdevice_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 device. The QPageablePaintDevice 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 paint device'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 \sa QTextEdit::print()
1714 void QTextDocument::print(QPagedPaintDevice *printer) const
1716 Q_D(const QTextDocument);
1721 bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
1722 && d->pageSize.height() != INT_MAX;
1724 QPagedPaintDevicePrivate *pd = QPagedPaintDevicePrivate::get(printer);
1726 // ### set page size to paginated size?
1727 QPagedPaintDevice::Margins m = printer->margins();
1728 if (!documentPaginated && m.left == 0. && m.right == 0. && m.top == 0. && m.bottom == 0.) {
1729 m.left = m.right = m.top = m.bottom = 2.;
1730 printer->setMargins(m);
1732 // ### use the margins correctly
1734 QPainter p(printer);
1736 // Check that there is a valid device to print to.
1740 const QTextDocument *doc = this;
1741 QScopedPointer<QTextDocument> clonedDoc;
1742 (void)doc->documentLayout(); // make sure that there is a layout
1744 QRectF body = QRectF(QPointF(0, 0), d->pageSize);
1745 QPointF pageNumberPos;
1747 if (documentPaginated) {
1748 qreal sourceDpiX = qt_defaultDpi();
1749 qreal sourceDpiY = sourceDpiX;
1751 QPaintDevice *dev = doc->documentLayout()->paintDevice();
1753 sourceDpiX = dev->logicalDpiX();
1754 sourceDpiY = dev->logicalDpiY();
1757 const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
1758 const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
1761 p.scale(dpiScaleX, dpiScaleY);
1763 QSizeF scaledPageSize = d->pageSize;
1764 scaledPageSize.rwidth() *= dpiScaleX;
1765 scaledPageSize.rheight() *= dpiScaleY;
1767 const QSizeF printerPageSize(printer->width(), printer->height());
1770 p.scale(printerPageSize.width() / scaledPageSize.width(),
1771 printerPageSize.height() / scaledPageSize.height());
1773 doc = clone(const_cast<QTextDocument *>(this));
1774 clonedDoc.reset(const_cast<QTextDocument *>(doc));
1776 for (QTextBlock srcBlock = firstBlock(), dstBlock = clonedDoc->firstBlock();
1777 srcBlock.isValid() && dstBlock.isValid();
1778 srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
1779 dstBlock.layout()->setAdditionalFormats(srcBlock.layout()->additionalFormats());
1782 QAbstractTextDocumentLayout *layout = doc->documentLayout();
1783 layout->setPaintDevice(p.device());
1785 // copy the custom object handlers
1786 layout->d_func()->handlers = documentLayout()->d_func()->handlers;
1788 int dpiy = p.device()->logicalDpiY();
1789 int margin = (int) ((2/2.54)*dpiy); // 2 cm margins
1790 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
1791 fmt.setMargin(margin);
1792 doc->rootFrame()->setFrameFormat(fmt);
1794 body = QRectF(0, 0, printer->width(), printer->height());
1795 pageNumberPos = QPointF(body.width() - margin,
1796 body.height() - margin
1797 + QFontMetrics(doc->defaultFont(), p.device()).ascent()
1799 clonedDoc->setPageSize(body.size());
1802 int fromPage = pd->fromPage;
1803 int toPage = pd->toPage;
1804 bool ascending = true;
1806 if (fromPage == 0 && toPage == 0) {
1808 toPage = doc->pageCount();
1811 fromPage = qMax(1, fromPage);
1812 toPage = qMin(doc->pageCount(), toPage);
1814 if (toPage < fromPage) {
1815 // if the user entered a page range outside the actual number
1816 // of printable pages, just return
1820 // if (printer->pageOrder() == QPrinter::LastPageFirst) {
1821 // int tmp = fromPage;
1822 // fromPage = toPage;
1824 // ascending = false;
1827 int page = fromPage;
1829 printPage(page, &p, doc, body, pageNumberPos);
1839 if (!printer->newPage())
1846 \enum QTextDocument::ResourceType
1848 This enum describes the types of resources that can be loaded by
1849 QTextDocument's loadResource() function.
1851 \value HtmlResource The resource contains HTML.
1852 \value ImageResource The resource contains image data.
1853 Currently supported data types are QVariant::Pixmap and
1854 QVariant::Image. If the corresponding variant is of type
1855 QVariant::ByteArray then Qt attempts to load the image using
1856 QImage::loadFromData. QVariant::Icon is currently not supported.
1857 The icon needs to be converted to one of the supported types first,
1858 for example using QIcon::pixmap.
1859 \value StyleSheetResource The resource contains CSS.
1860 \value UserResource The first available value for user defined
1867 Returns data of the specified \a type from the resource with the
1870 This function is called by the rich text engine to request data that isn't
1871 directly stored by QTextDocument, but still associated with it. For example,
1872 images are referenced indirectly by the name attribute of a QTextImageFormat
1875 Resources are cached internally in the document. If a resource can
1876 not be found in the cache, loadResource is called to try to load
1877 the resource. loadResource should then use addResource to add the
1878 resource to the cache.
1880 \sa QTextDocument::ResourceType
1882 QVariant QTextDocument::resource(int type, const QUrl &name) const
1884 Q_D(const QTextDocument);
1885 QVariant r = d->resources.value(name);
1887 r = d->cachedResources.value(name);
1889 r = const_cast<QTextDocument *>(this)->loadResource(type, name);
1895 Adds the resource \a resource to the resource cache, using \a
1896 type and \a name as identifiers. \a type should be a value from
1897 QTextDocument::ResourceType.
1899 For example, you can add an image as a resource in order to reference it
1900 from within the document:
1902 \snippet snippets/textdocument-resources/main.cpp Adding a resource
1904 The image can be inserted into the document using the QTextCursor API:
1906 \snippet snippets/textdocument-resources/main.cpp Inserting an image with a cursor
1908 Alternatively, you can insert images using the HTML \c img tag:
1910 \snippet snippets/textdocument-resources/main.cpp Inserting an image using HTML
1912 void QTextDocument::addResource(int type, const QUrl &name, const QVariant &resource)
1916 d->resources.insert(name, resource);
1920 Loads data of the specified \a type from the resource with the
1923 This function is called by the rich text engine to request data that isn't
1924 directly stored by QTextDocument, but still associated with it. For example,
1925 images are referenced indirectly by the name attribute of a QTextImageFormat
1928 When called by Qt, \a type is one of the values of
1929 QTextDocument::ResourceType.
1931 If the QTextDocument is a child object of a QTextEdit, QTextBrowser,
1932 or a QTextDocument itself then the default implementation tries
1933 to retrieve the data from the parent.
1935 QVariant QTextDocument::loadResource(int type, const QUrl &name)
1940 QTextDocument *doc = qobject_cast<QTextDocument *>(parent());
1942 r = doc->loadResource(type, name);
1945 // ### Qt5: reenable
1946 #ifndef QT_NO_TEXTEDIT
1947 else if (QTextEdit *edit = qobject_cast<QTextEdit *>(parent())) {
1948 QUrl resolvedName = edit->d_func()->resolveUrl(name);
1949 r = edit->loadResource(type, resolvedName);
1952 #ifndef QT_NO_TEXTCONTROL
1953 else if (QWidgetTextControl *control = qobject_cast<QWidgetTextControl *>(parent())) {
1954 r = control->loadResource(type, name);
1959 // handle data: URLs
1960 if (r.isNull() && name.scheme().compare(QLatin1String("data"), Qt::CaseInsensitive) == 0)
1961 r = qDecodeDataUrl(name).second;
1963 // if resource was not loaded try to load it here
1964 if (!doc && r.isNull() && name.isRelative()) {
1965 QUrl currentURL = d->url;
1966 QUrl resourceUrl = name;
1968 // For the second case QUrl can merge "#someanchor" with "foo.html"
1969 // correctly to "foo.html#someanchor"
1970 if (!(currentURL.isRelative()
1971 || (currentURL.scheme() == QLatin1String("file")
1972 && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
1973 || (name.hasFragment() && name.path().isEmpty())) {
1974 resourceUrl = currentURL.resolved(name);
1976 // this is our last resort when current url and new url are both relative
1977 // we try to resolve against the current working directory in the local
1979 QFileInfo fi(currentURL.toLocalFile());
1982 QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
1983 } else if (currentURL.isEmpty()) {
1984 resourceUrl.setScheme(QLatin1String("file"));
1988 QString s = resourceUrl.toLocalFile();
1990 if (!s.isEmpty() && f.open(QFile::ReadOnly)) {
1997 if (type == ImageResource && r.type() == QVariant::ByteArray) {
1998 if (qApp->thread() != QThread::currentThread()) {
1999 // must use images in non-GUI threads
2001 image.loadFromData(r.toByteArray());
2002 if (!image.isNull())
2006 pm.loadFromData(r.toByteArray());
2011 d->cachedResources.insert(name, r);
2016 static QTextFormat formatDifference(const QTextFormat &from, const QTextFormat &to)
2018 QTextFormat diff = to;
2020 const QMap<int, QVariant> props = to.properties();
2021 for (QMap<int, QVariant>::ConstIterator it = props.begin(), end = props.end();
2023 if (it.value() == from.property(it.key()))
2024 diff.clearProperty(it.key());
2029 QTextHtmlExporter::QTextHtmlExporter(const QTextDocument *_doc)
2030 : doc(_doc), fragmentMarkers(false)
2032 const QFont defaultFont = doc->defaultFont();
2033 defaultCharFormat.setFont(defaultFont);
2034 // don't export those for the default font since we cannot turn them off with CSS
2035 defaultCharFormat.clearProperty(QTextFormat::FontUnderline);
2036 defaultCharFormat.clearProperty(QTextFormat::FontOverline);
2037 defaultCharFormat.clearProperty(QTextFormat::FontStrikeOut);
2038 defaultCharFormat.clearProperty(QTextFormat::TextUnderlineStyle);
2042 Returns the document in HTML format. The conversion may not be
2043 perfect, especially for complex documents, due to the limitations
2046 QString QTextHtmlExporter::toHtml(const QByteArray &encoding, ExportMode mode)
2048 html = QLatin1String("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
2049 "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
2050 "<html><head><meta name=\"qrichtext\" content=\"1\" />");
2051 html.reserve(doc->docHandle()->length());
2053 fragmentMarkers = (mode == ExportFragment);
2055 if (!encoding.isEmpty())
2056 html += QString::fromLatin1("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\" />").arg(QString::fromAscii(encoding));
2058 QString title = doc->metaInformation(QTextDocument::DocumentTitle);
2059 if (!title.isEmpty())
2060 html += QString::fromLatin1("<title>") + title + QString::fromLatin1("</title>");
2061 html += QLatin1String("<style type=\"text/css\">\n");
2062 html += QLatin1String("p, li { white-space: pre-wrap; }\n");
2063 html += QLatin1String("</style>");
2064 html += QLatin1String("</head><body");
2066 if (mode == ExportEntireDocument) {
2067 html += QLatin1String(" style=\"");
2069 emitFontFamily(defaultCharFormat.fontFamily());
2071 if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) {
2072 html += QLatin1String(" font-size:");
2073 html += QString::number(defaultCharFormat.fontPointSize());
2074 html += QLatin1String("pt;");
2075 } else if (defaultCharFormat.hasProperty(QTextFormat::FontPixelSize)) {
2076 html += QLatin1String(" font-size:");
2077 html += QString::number(defaultCharFormat.intProperty(QTextFormat::FontPixelSize));
2078 html += QLatin1String("px;");
2081 html += QLatin1String(" font-weight:");
2082 html += QString::number(defaultCharFormat.fontWeight() * 8);
2083 html += QLatin1Char(';');
2085 html += QLatin1String(" font-style:");
2086 html += (defaultCharFormat.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2087 html += QLatin1Char(';');
2089 // do not set text-decoration on the default font since those values are /always/ propagated
2090 // and cannot be turned off with CSS
2092 html += QLatin1Char('\"');
2094 const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2095 emitBackgroundAttribute(fmt);
2098 defaultCharFormat = QTextCharFormat();
2100 html += QLatin1Char('>');
2102 QTextFrameFormat rootFmt = doc->rootFrame()->frameFormat();
2103 rootFmt.clearProperty(QTextFormat::BackgroundBrush);
2105 QTextFrameFormat defaultFmt;
2106 defaultFmt.setMargin(doc->documentMargin());
2108 if (rootFmt == defaultFmt)
2109 emitFrame(doc->rootFrame()->begin());
2111 emitTextFrame(doc->rootFrame());
2113 html += QLatin1String("</body></html>");
2117 void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &value)
2119 html += QLatin1Char(' ');
2120 html += QLatin1String(attribute);
2121 html += QLatin1String("=\"");
2122 html += Qt::escape(value);
2123 html += QLatin1Char('"');
2126 bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
2128 bool attributesEmitted = false;
2131 const QString family = format.fontFamily();
2132 if (!family.isEmpty() && family != defaultCharFormat.fontFamily()) {
2133 emitFontFamily(family);
2134 attributesEmitted = true;
2138 if (format.hasProperty(QTextFormat::FontPointSize)
2139 && format.fontPointSize() != defaultCharFormat.fontPointSize()) {
2140 html += QLatin1String(" font-size:");
2141 html += QString::number(format.fontPointSize());
2142 html += QLatin1String("pt;");
2143 attributesEmitted = true;
2144 } else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) {
2145 static const char * const sizeNames[] = {
2146 "small", "medium", "large", "x-large", "xx-large"
2148 const char *name = 0;
2149 const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1;
2150 if (idx >= 0 && idx <= 4) {
2151 name = sizeNames[idx];
2154 html += QLatin1String(" font-size:");
2155 html += QLatin1String(name);
2156 html += QLatin1Char(';');
2157 attributesEmitted = true;
2159 } else if (format.hasProperty(QTextFormat::FontPixelSize)) {
2160 html += QLatin1String(" font-size:");
2161 html += QString::number(format.intProperty(QTextFormat::FontPixelSize));
2162 html += QLatin1String("px;");
2165 if (format.hasProperty(QTextFormat::FontWeight)
2166 && format.fontWeight() != defaultCharFormat.fontWeight()) {
2167 html += QLatin1String(" font-weight:");
2168 html += QString::number(format.fontWeight() * 8);
2169 html += QLatin1Char(';');
2170 attributesEmitted = true;
2173 if (format.hasProperty(QTextFormat::FontItalic)
2174 && format.fontItalic() != defaultCharFormat.fontItalic()) {
2175 html += QLatin1String(" font-style:");
2176 html += (format.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2177 html += QLatin1Char(';');
2178 attributesEmitted = true;
2181 QLatin1String decorationTag(" text-decoration:");
2182 html += decorationTag;
2183 bool hasDecoration = false;
2184 bool atLeastOneDecorationSet = false;
2186 if ((format.hasProperty(QTextFormat::FontUnderline) || format.hasProperty(QTextFormat::TextUnderlineStyle))
2187 && format.fontUnderline() != defaultCharFormat.fontUnderline()) {
2188 hasDecoration = true;
2189 if (format.fontUnderline()) {
2190 html += QLatin1String(" underline");
2191 atLeastOneDecorationSet = true;
2195 if (format.hasProperty(QTextFormat::FontOverline)
2196 && format.fontOverline() != defaultCharFormat.fontOverline()) {
2197 hasDecoration = true;
2198 if (format.fontOverline()) {
2199 html += QLatin1String(" overline");
2200 atLeastOneDecorationSet = true;
2204 if (format.hasProperty(QTextFormat::FontStrikeOut)
2205 && format.fontStrikeOut() != defaultCharFormat.fontStrikeOut()) {
2206 hasDecoration = true;
2207 if (format.fontStrikeOut()) {
2208 html += QLatin1String(" line-through");
2209 atLeastOneDecorationSet = true;
2213 if (hasDecoration) {
2214 if (!atLeastOneDecorationSet)
2215 html += QLatin1String("none");
2216 html += QLatin1Char(';');
2217 attributesEmitted = true;
2219 html.chop(qstrlen(decorationTag.latin1()));
2222 if (format.foreground() != defaultCharFormat.foreground()
2223 && format.foreground().style() != Qt::NoBrush) {
2224 html += QLatin1String(" color:");
2225 html += format.foreground().color().name();
2226 html += QLatin1Char(';');
2227 attributesEmitted = true;
2230 if (format.background() != defaultCharFormat.background()
2231 && format.background().style() == Qt::SolidPattern) {
2232 html += QLatin1String(" background-color:");
2233 html += format.background().color().name();
2234 html += QLatin1Char(';');
2235 attributesEmitted = true;
2238 if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()
2239 && format.verticalAlignment() != QTextCharFormat::AlignNormal)
2241 html += QLatin1String(" vertical-align:");
2243 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2244 if (valign == QTextCharFormat::AlignSubScript)
2245 html += QLatin1String("sub");
2246 else if (valign == QTextCharFormat::AlignSuperScript)
2247 html += QLatin1String("super");
2248 else if (valign == QTextCharFormat::AlignMiddle)
2249 html += QLatin1String("middle");
2250 else if (valign == QTextCharFormat::AlignTop)
2251 html += QLatin1String("top");
2252 else if (valign == QTextCharFormat::AlignBottom)
2253 html += QLatin1String("bottom");
2255 html += QLatin1Char(';');
2256 attributesEmitted = true;
2259 if (format.fontCapitalization() != QFont::MixedCase) {
2260 const QFont::Capitalization caps = format.fontCapitalization();
2261 if (caps == QFont::AllUppercase)
2262 html += QLatin1String(" text-transform:uppercase;");
2263 else if (caps == QFont::AllLowercase)
2264 html += QLatin1String(" text-transform:lowercase;");
2265 else if (caps == QFont::SmallCaps)
2266 html += QLatin1String(" font-variant:small-caps;");
2267 attributesEmitted = true;
2270 if (format.fontWordSpacing() != 0.0) {
2271 html += QLatin1String(" word-spacing:");
2272 html += QString::number(format.fontWordSpacing());
2273 html += QLatin1String("px;");
2274 attributesEmitted = true;
2277 return attributesEmitted;
2280 void QTextHtmlExporter::emitTextLength(const char *attribute, const QTextLength &length)
2282 if (length.type() == QTextLength::VariableLength) // default
2285 html += QLatin1Char(' ');
2286 html += QLatin1String(attribute);
2287 html += QLatin1String("=\"");
2288 html += QString::number(length.rawValue());
2290 if (length.type() == QTextLength::PercentageLength)
2291 html += QLatin1String("%\"");
2293 html += QLatin1Char('\"');
2296 void QTextHtmlExporter::emitAlignment(Qt::Alignment align)
2298 if (align & Qt::AlignLeft)
2300 else if (align & Qt::AlignRight)
2301 html += QLatin1String(" align=\"right\"");
2302 else if (align & Qt::AlignHCenter)
2303 html += QLatin1String(" align=\"center\"");
2304 else if (align & Qt::AlignJustify)
2305 html += QLatin1String(" align=\"justify\"");
2308 void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
2310 if (pos == QTextFrameFormat::InFlow)
2313 if (mode == EmitStyleTag)
2314 html += QLatin1String(" style=\"float:");
2316 html += QLatin1String(" float:");
2318 if (pos == QTextFrameFormat::FloatLeft)
2319 html += QLatin1String(" left;");
2320 else if (pos == QTextFrameFormat::FloatRight)
2321 html += QLatin1String(" right;");
2323 Q_ASSERT_X(0, "QTextHtmlExporter::emitFloatStyle()", "pos should be a valid enum type");
2325 if (mode == EmitStyleTag)
2326 html += QLatin1Char('\"');
2329 void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style)
2331 Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
2333 html += QLatin1String(" border-style:");
2336 case QTextFrameFormat::BorderStyle_None:
2337 html += QLatin1String("none");
2339 case QTextFrameFormat::BorderStyle_Dotted:
2340 html += QLatin1String("dotted");
2342 case QTextFrameFormat::BorderStyle_Dashed:
2343 html += QLatin1String("dashed");
2345 case QTextFrameFormat::BorderStyle_Solid:
2346 html += QLatin1String("solid");
2348 case QTextFrameFormat::BorderStyle_Double:
2349 html += QLatin1String("double");
2351 case QTextFrameFormat::BorderStyle_DotDash:
2352 html += QLatin1String("dot-dash");
2354 case QTextFrameFormat::BorderStyle_DotDotDash:
2355 html += QLatin1String("dot-dot-dash");
2357 case QTextFrameFormat::BorderStyle_Groove:
2358 html += QLatin1String("groove");
2360 case QTextFrameFormat::BorderStyle_Ridge:
2361 html += QLatin1String("ridge");
2363 case QTextFrameFormat::BorderStyle_Inset:
2364 html += QLatin1String("inset");
2366 case QTextFrameFormat::BorderStyle_Outset:
2367 html += QLatin1String("outset");
2374 html += QLatin1Char(';');
2377 void QTextHtmlExporter::emitPageBreakPolicy(QTextFormat::PageBreakFlags policy)
2379 if (policy & QTextFormat::PageBreak_AlwaysBefore)
2380 html += QLatin1String(" page-break-before:always;");
2382 if (policy & QTextFormat::PageBreak_AlwaysAfter)
2383 html += QLatin1String(" page-break-after:always;");
2386 void QTextHtmlExporter::emitFontFamily(const QString &family)
2388 html += QLatin1String(" font-family:");
2390 QLatin1String quote("\'");
2391 if (family.contains(QLatin1Char('\'')))
2392 quote = QLatin1String(""");
2395 html += Qt::escape(family);
2397 html += QLatin1Char(';');
2400 void QTextHtmlExporter::emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right)
2402 html += QLatin1String(" margin-top:");
2404 html += QLatin1String("px;");
2406 html += QLatin1String(" margin-bottom:");
2408 html += QLatin1String("px;");
2410 html += QLatin1String(" margin-left:");
2412 html += QLatin1String("px;");
2414 html += QLatin1String(" margin-right:");
2416 html += QLatin1String("px;");
2419 void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
2421 const QTextCharFormat format = fragment.charFormat();
2423 bool closeAnchor = false;
2425 if (format.isAnchor()) {
2426 const QString name = format.anchorName();
2427 if (!name.isEmpty()) {
2428 html += QLatin1String("<a name=\"");
2429 html += Qt::escape(name);
2430 html += QLatin1String("\"></a>");
2432 const QString href = format.anchorHref();
2433 if (!href.isEmpty()) {
2434 html += QLatin1String("<a href=\"");
2435 html += Qt::escape(href);
2436 html += QLatin1String("\">");
2441 QString txt = fragment.text();
2442 const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
2443 const bool isImage = isObject && format.isImageFormat();
2445 QLatin1String styleTag("<span style=\"");
2448 bool attributesEmitted = false;
2450 attributesEmitted = emitCharFormatStyle(format);
2451 if (attributesEmitted)
2452 html += QLatin1String("\">");
2454 html.chop(qstrlen(styleTag.latin1()));
2457 for (int i = 0; isImage && i < txt.length(); ++i) {
2458 QTextImageFormat imgFmt = format.toImageFormat();
2460 html += QLatin1String("<img");
2462 if (imgFmt.hasProperty(QTextFormat::ImageName))
2463 emitAttribute("src", imgFmt.name());
2465 if (imgFmt.hasProperty(QTextFormat::ImageWidth))
2466 emitAttribute("width", QString::number(imgFmt.width()));
2468 if (imgFmt.hasProperty(QTextFormat::ImageHeight))
2469 emitAttribute("height", QString::number(imgFmt.height()));
2471 if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
2472 html += QLatin1String(" style=\"vertical-align: middle;\"");
2473 else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
2474 html += QLatin1String(" style=\"vertical-align: top;\"");
2476 if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
2477 emitFloatStyle(imageFrame->frameFormat().position());
2479 html += QLatin1String(" />");
2482 Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
2484 txt = Qt::escape(txt);
2486 // split for [\n{LineSeparator}]
2487 QString forcedLineBreakRegExp = QString::fromLatin1("[\\na]");
2488 forcedLineBreakRegExp[3] = QChar::LineSeparator;
2490 const QStringList lines = txt.split(QRegExp(forcedLineBreakRegExp));
2491 for (int i = 0; i < lines.count(); ++i) {
2493 html += QLatin1String("<br />"); // space on purpose for compatibility with Netscape, Lynx & Co.
2494 html += lines.at(i);
2498 if (attributesEmitted)
2499 html += QLatin1String("</span>");
2502 html += QLatin1String("</a>");
2505 static bool isOrderedList(int style)
2507 return style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
2508 || style == QTextListFormat::ListUpperAlpha
2509 || style == QTextListFormat::ListUpperRoman
2510 || style == QTextListFormat::ListLowerRoman
2514 void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
2516 QTextBlockFormat format = block.blockFormat();
2517 emitAlignment(format.alignment());
2519 // assume default to not bloat the html too much
2520 // html += QLatin1String(" dir='ltr'");
2521 if (block.textDirection() == Qt::RightToLeft)
2522 html += QLatin1String(" dir='rtl'");
2524 QLatin1String style(" style=\"");
2527 const bool emptyBlock = block.begin().atEnd();
2529 html += QLatin1String("-qt-paragraph-type:empty;");
2532 emitMargins(QString::number(format.topMargin()),
2533 QString::number(format.bottomMargin()),
2534 QString::number(format.leftMargin()),
2535 QString::number(format.rightMargin()));
2537 html += QLatin1String(" -qt-block-indent:");
2538 html += QString::number(format.indent());
2539 html += QLatin1Char(';');
2541 html += QLatin1String(" text-indent:");
2542 html += QString::number(format.textIndent());
2543 html += QLatin1String("px;");
2545 if (block.userState() != -1) {
2546 html += QLatin1String(" -qt-user-state:");
2547 html += QString::number(block.userState());
2548 html += QLatin1Char(';');
2551 emitPageBreakPolicy(format.pageBreakPolicy());
2553 QTextCharFormat diff;
2554 if (emptyBlock) { // only print character properties when we don't expect them to be repeated by actual text in the parag
2555 const QTextCharFormat blockCharFmt = block.charFormat();
2556 diff = formatDifference(defaultCharFormat, blockCharFmt).toCharFormat();
2559 diff.clearProperty(QTextFormat::BackgroundBrush);
2560 if (format.hasProperty(QTextFormat::BackgroundBrush)) {
2561 QBrush bg = format.background();
2562 if (bg.style() != Qt::NoBrush)
2563 diff.setProperty(QTextFormat::BackgroundBrush, format.property(QTextFormat::BackgroundBrush));
2566 if (!diff.properties().isEmpty())
2567 emitCharFormatStyle(diff);
2569 html += QLatin1Char('"');
2573 void QTextHtmlExporter::emitBlock(const QTextBlock &block)
2575 if (block.begin().atEnd()) {
2576 // ### HACK, remove once QTextFrame::Iterator is fixed
2577 int p = block.position();
2580 QTextDocumentPrivate::FragmentIterator frag = doc->docHandle()->find(p);
2581 QChar ch = doc->docHandle()->buffer().at(frag->stringPosition);
2582 if (ch == QTextBeginningOfFrame
2583 || ch == QTextEndOfFrame)
2587 html += QLatin1Char('\n');
2589 // save and later restore, in case we 'change' the default format by
2590 // emitting block char format information
2591 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2593 QTextList *list = block.textList();
2595 if (list->itemNumber(block) == 0) { // first item? emit <ul> or appropriate
2596 const QTextListFormat format = list->format();
2597 const int style = format.style();
2599 case QTextListFormat::ListDecimal: html += QLatin1String("<ol"); break;
2600 case QTextListFormat::ListDisc: html += QLatin1String("<ul"); break;
2601 case QTextListFormat::ListCircle: html += QLatin1String("<ul type=\"circle\""); break;
2602 case QTextListFormat::ListSquare: html += QLatin1String("<ul type=\"square\""); break;
2603 case QTextListFormat::ListLowerAlpha: html += QLatin1String("<ol type=\"a\""); break;
2604 case QTextListFormat::ListUpperAlpha: html += QLatin1String("<ol type=\"A\""); break;
2605 case QTextListFormat::ListLowerRoman: html += QLatin1String("<ol type=\"i\""); break;
2606 case QTextListFormat::ListUpperRoman: html += QLatin1String("<ol type=\"I\""); break;
2607 default: html += QLatin1String("<ul"); // ### should not happen
2610 QString styleString = QString::fromLatin1("margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;");
2612 if (format.hasProperty(QTextFormat::ListIndent)) {
2613 styleString += QLatin1String(" -qt-list-indent: ");
2614 styleString += QString::number(format.indent());
2615 styleString += QLatin1Char(';');
2618 if (format.hasProperty(QTextFormat::ListNumberPrefix)) {
2619 QString numberPrefix = format.numberPrefix();
2620 numberPrefix.replace(QLatin1Char('"'), QLatin1String("\\22"));
2621 numberPrefix.replace(QLatin1Char('\''), QLatin1String("\\27")); // FIXME: There's a problem in the CSS parser the prevents this from being correctly restored
2622 styleString += QLatin1String(" -qt-list-number-prefix: ");
2623 styleString += QLatin1Char('\'');
2624 styleString += numberPrefix;
2625 styleString += QLatin1Char('\'');
2626 styleString += QLatin1Char(';');
2629 if (format.hasProperty(QTextFormat::ListNumberSuffix)) {
2630 if (format.numberSuffix() != QLatin1String(".")) { // this is our default
2631 QString numberSuffix = format.numberSuffix();
2632 numberSuffix.replace(QLatin1Char('"'), QLatin1String("\\22"));
2633 numberSuffix.replace(QLatin1Char('\''), QLatin1String("\\27")); // see above
2634 styleString += QLatin1String(" -qt-list-number-suffix: ");
2635 styleString += QLatin1Char('\'');
2636 styleString += numberSuffix;
2637 styleString += QLatin1Char('\'');
2638 styleString += QLatin1Char(';');
2642 html += QLatin1String(" style=\"");
2643 html += styleString;
2644 html += QLatin1String("\">");
2647 html += QLatin1String("<li");
2649 const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
2650 if (!blockFmt.properties().isEmpty()) {
2651 html += QLatin1String(" style=\"");
2652 emitCharFormatStyle(blockFmt);
2653 html += QLatin1Char('\"');
2655 defaultCharFormat.merge(block.charFormat());
2659 const QTextBlockFormat blockFormat = block.blockFormat();
2660 if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
2661 html += QLatin1String("<hr");
2663 QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
2664 if (width.type() != QTextLength::VariableLength)
2665 emitTextLength("width", width);
2667 html += QLatin1Char(' ');
2669 html += QLatin1String("/>");
2673 const bool pre = blockFormat.nonBreakableLines();
2676 html += QLatin1Char('>');
2677 html += QLatin1String("<pre");
2679 html += QLatin1String("<p");
2682 emitBlockAttributes(block);
2684 html += QLatin1Char('>');
2685 if (block.begin().atEnd())
2686 html += QLatin1String("<br />");
2688 QTextBlock::Iterator it = block.begin();
2689 if (fragmentMarkers && !it.atEnd() && block == doc->begin())
2690 html += QLatin1String("<!--StartFragment-->");
2692 for (; !it.atEnd(); ++it)
2693 emitFragment(it.fragment());
2695 if (fragmentMarkers && block.position() + block.length() == doc->docHandle()->length())
2696 html += QLatin1String("<!--EndFragment-->");
2699 html += QLatin1String("</pre>");
2701 html += QLatin1String("</li>");
2703 html += QLatin1String("</p>");
2706 if (list->itemNumber(block) == list->count() - 1) { // last item? close list
2707 if (isOrderedList(list->format().style()))
2708 html += QLatin1String("</ol>");
2710 html += QLatin1String("</ul>");
2714 defaultCharFormat = oldDefaultCharFormat;
2717 extern bool qHasPixmapTexture(const QBrush& brush);
2719 QString QTextHtmlExporter::findUrlForImage(const QTextDocument *doc, qint64 cacheKey, bool isPixmap)
2725 if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent()))
2726 return findUrlForImage(parent, cacheKey, isPixmap);
2728 if (doc && doc->docHandle()) {
2729 QTextDocumentPrivate *priv = doc->docHandle();
2730 QMap<QUrl, QVariant>::const_iterator it = priv->cachedResources.constBegin();
2731 for (; it != priv->cachedResources.constEnd(); ++it) {
2733 const QVariant &v = it.value();
2734 if (v.type() == QVariant::Image && !isPixmap) {
2735 if (qvariant_cast<QImage>(v).cacheKey() == cacheKey)
2739 if (v.type() == QVariant::Pixmap && isPixmap) {
2740 if (qvariant_cast<QPixmap>(v).cacheKey() == cacheKey)
2745 if (it != priv->cachedResources.constEnd())
2746 url = it.key().toString();
2752 void QTextDocumentPrivate::mergeCachedResources(const QTextDocumentPrivate *priv)
2757 cachedResources.unite(priv->cachedResources);
2760 void QTextHtmlExporter::emitBackgroundAttribute(const QTextFormat &format)
2762 if (format.hasProperty(QTextFormat::BackgroundImageUrl)) {
2763 QString url = format.property(QTextFormat::BackgroundImageUrl).toString();
2764 emitAttribute("background", url);
2766 const QBrush &brush = format.background();
2767 if (brush.style() == Qt::SolidPattern) {
2768 emitAttribute("bgcolor", brush.color().name());
2769 } else if (brush.style() == Qt::TexturePattern) {
2770 const bool isPixmap = qHasPixmapTexture(brush);
2771 const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
2773 const QString url = findUrlForImage(doc, cacheKey, isPixmap);
2776 emitAttribute("background", url);
2781 void QTextHtmlExporter::emitTable(const QTextTable *table)
2783 QTextTableFormat format = table->format();
2785 html += QLatin1String("\n<table");
2787 if (format.hasProperty(QTextFormat::FrameBorder))
2788 emitAttribute("border", QString::number(format.border()));
2790 emitFrameStyle(format, TableFrame);
2792 emitAlignment(format.alignment());
2793 emitTextLength("width", format.width());
2795 if (format.hasProperty(QTextFormat::TableCellSpacing))
2796 emitAttribute("cellspacing", QString::number(format.cellSpacing()));
2797 if (format.hasProperty(QTextFormat::TableCellPadding))
2798 emitAttribute("cellpadding", QString::number(format.cellPadding()));
2800 emitBackgroundAttribute(format);
2802 html += QLatin1Char('>');
2804 const int rows = table->rows();
2805 const int columns = table->columns();
2807 QVector<QTextLength> columnWidths = format.columnWidthConstraints();
2808 if (columnWidths.isEmpty()) {
2809 columnWidths.resize(columns);
2810 columnWidths.fill(QTextLength());
2812 Q_ASSERT(columnWidths.count() == columns);
2814 QVarLengthArray<bool> widthEmittedForColumn(columns);
2815 for (int i = 0; i < columns; ++i)
2816 widthEmittedForColumn[i] = false;
2818 const int headerRowCount = qMin(format.headerRowCount(), rows);
2819 if (headerRowCount > 0)
2820 html += QLatin1String("<thead>");
2822 for (int row = 0; row < rows; ++row) {
2823 html += QLatin1String("\n<tr>");
2825 for (int col = 0; col < columns; ++col) {
2826 const QTextTableCell cell = table->cellAt(row, col);
2829 if (cell.row() != row)
2832 if (cell.column() != col)
2835 html += QLatin1String("\n<td");
2837 if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
2838 emitTextLength("width", columnWidths.at(col));
2839 widthEmittedForColumn[col] = true;
2842 if (cell.columnSpan() > 1)
2843 emitAttribute("colspan", QString::number(cell.columnSpan()));
2845 if (cell.rowSpan() > 1)
2846 emitAttribute("rowspan", QString::number(cell.rowSpan()));
2848 const QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
2849 emitBackgroundAttribute(cellFormat);
2851 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2853 QTextCharFormat::VerticalAlignment valign = cellFormat.verticalAlignment();
2855 QString styleString;
2856 if (valign >= QTextCharFormat::AlignMiddle && valign <= QTextCharFormat::AlignBottom) {
2857 styleString += QLatin1String(" vertical-align:");
2859 case QTextCharFormat::AlignMiddle:
2860 styleString += QLatin1String("middle");
2862 case QTextCharFormat::AlignTop:
2863 styleString += QLatin1String("top");
2865 case QTextCharFormat::AlignBottom:
2866 styleString += QLatin1String("bottom");
2871 styleString += QLatin1Char(';');
2873 QTextCharFormat temp;
2874 temp.setVerticalAlignment(valign);
2875 defaultCharFormat.merge(temp);
2878 if (cellFormat.hasProperty(QTextFormat::TableCellLeftPadding))
2879 styleString += QLatin1String(" padding-left:") + QString::number(cellFormat.leftPadding()) + QLatin1Char(';');
2880 if (cellFormat.hasProperty(QTextFormat::TableCellRightPadding))
2881 styleString += QLatin1String(" padding-right:") + QString::number(cellFormat.rightPadding()) + QLatin1Char(';');
2882 if (cellFormat.hasProperty(QTextFormat::TableCellTopPadding))
2883 styleString += QLatin1String(" padding-top:") + QString::number(cellFormat.topPadding()) + QLatin1Char(';');
2884 if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
2885 styleString += QLatin1String(" padding-bottom:") + QString::number(cellFormat.bottomPadding()) + QLatin1Char(';');
2887 if (!styleString.isEmpty())
2888 html += QLatin1String(" style=\"") + styleString + QLatin1Char('\"');
2890 html += QLatin1Char('>');
2892 emitFrame(cell.begin());
2894 html += QLatin1String("</td>");
2896 defaultCharFormat = oldDefaultCharFormat;
2899 html += QLatin1String("</tr>");
2900 if (headerRowCount > 0 && row == headerRowCount - 1)
2901 html += QLatin1String("</thead>");
2904 html += QLatin1String("</table>");
2907 void QTextHtmlExporter::emitFrame(QTextFrame::Iterator frameIt)
2909 if (!frameIt.atEnd()) {
2910 QTextFrame::Iterator next = frameIt;
2913 && frameIt.currentFrame() == 0
2914 && frameIt.parentFrame() != doc->rootFrame()
2915 && frameIt.currentBlock().begin().atEnd())
2919 for (QTextFrame::Iterator it = frameIt;
2920 !it.atEnd(); ++it) {
2921 if (QTextFrame *f = it.currentFrame()) {
2922 if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
2927 } else if (it.currentBlock().isValid()) {
2928 emitBlock(it.currentBlock());
2933 void QTextHtmlExporter::emitTextFrame(const QTextFrame *f)
2935 FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
2937 html += QLatin1String("\n<table");
2938 QTextFrameFormat format = f->frameFormat();
2940 if (format.hasProperty(QTextFormat::FrameBorder))
2941 emitAttribute("border", QString::number(format.border()));
2943 emitFrameStyle(format, frameType);
2945 emitTextLength("width", format.width());
2946 emitTextLength("height", format.height());
2948 // root frame's bcolor goes in the <body> tag
2949 if (frameType != RootFrame)
2950 emitBackgroundAttribute(format);
2952 html += QLatin1Char('>');
2953 html += QLatin1String("\n<tr>\n<td style=\"border: none;\">");
2954 emitFrame(f->begin());
2955 html += QLatin1String("</td></tr></table>");
2958 void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType frameType)
2960 QLatin1String styleAttribute(" style=\"");
2961 html += styleAttribute;
2962 const int originalHtmlLength = html.length();
2964 if (frameType == TextFrame)
2965 html += QLatin1String("-qt-table-type: frame;");
2966 else if (frameType == RootFrame)
2967 html += QLatin1String("-qt-table-type: root;");
2969 const QTextFrameFormat defaultFormat;
2971 emitFloatStyle(format.position(), OmitStyleTag);
2972 emitPageBreakPolicy(format.pageBreakPolicy());
2974 if (format.borderBrush() != defaultFormat.borderBrush()) {
2975 html += QLatin1String(" border-color:");
2976 html += format.borderBrush().color().name();
2977 html += QLatin1Char(';');
2980 if (format.borderStyle() != defaultFormat.borderStyle())
2981 emitBorderStyle(format.borderStyle());
2983 if (format.hasProperty(QTextFormat::FrameMargin)
2984 || format.hasProperty(QTextFormat::FrameLeftMargin)
2985 || format.hasProperty(QTextFormat::FrameRightMargin)
2986 || format.hasProperty(QTextFormat::FrameTopMargin)
2987 || format.hasProperty(QTextFormat::FrameBottomMargin))
2988 emitMargins(QString::number(format.topMargin()),
2989 QString::number(format.bottomMargin()),
2990 QString::number(format.leftMargin()),
2991 QString::number(format.rightMargin()));
2993 if (html.length() == originalHtmlLength) // nothing emitted?
2994 html.chop(qstrlen(styleAttribute.latin1()));
2996 html += QLatin1Char('\"');
3000 Returns a string containing an HTML representation of the document.
3002 The \a encoding parameter specifies the value for the charset attribute
3003 in the html header. For example if 'utf-8' is specified then the
3004 beginning of the generated html will look like this:
3005 \snippet doc/src/snippets/code/src_gui_text_qtextdocument.cpp 1
3007 If no encoding is specified then no such meta information is generated.
3009 If you later on convert the returned html string into a byte array for
3010 transmission over a network or when saving to disk you should specify
3011 the encoding you're going to use for the conversion to a byte array here.
3013 \sa {Supported HTML Subset}
3015 #ifndef QT_NO_TEXTHTMLPARSER
3016 QString QTextDocument::toHtml(const QByteArray &encoding) const
3018 return QTextHtmlExporter(this).toHtml(encoding);
3020 #endif // QT_NO_TEXTHTMLPARSER
3023 Returns a vector of text formats for all the formats used in the document.
3025 QVector<QTextFormat> QTextDocument::allFormats() const
3027 Q_D(const QTextDocument);
3028 return d->formatCollection()->formats;
3035 So that not all classes have to be friends of each other...
3037 QTextDocumentPrivate *QTextDocument::docHandle() const
3039 Q_D(const QTextDocument);
3040 return const_cast<QTextDocumentPrivate *>(d);
3045 \fn QTextDocument::undoCommandAdded()
3047 This signal is emitted every time a new level of undo is added to the QTextDocument.