Export QTextImageHandler and add accessor for image
[profile/ivi/qtbase.git] / src / gui / text / qtextdocument.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
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.
17 **
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.
21 **
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.
29 **
30 ** Other Usage
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.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qtextdocument.h"
43 #include <qtextformat.h>
44 #include "qtextdocumentlayout_p.h"
45 #include "qtextdocumentfragment.h"
46 #include "qtextdocumentfragment_p.h"
47 #include "qtexttable.h"
48 #include "qtextlist.h"
49 #include <qdebug.h>
50 #include <qregexp.h>
51 #include <qvarlengtharray.h>
52 #include <qtextcodec.h>
53 #include <qthread.h>
54 #include <qcoreapplication.h>
55
56 #include "qtexthtmlparser_p.h"
57 #include "qpainter.h"
58 #include <qfile.h>
59 #include <qfileinfo.h>
60 #include <qdir.h>
61 #include "qfont_p.h"
62 #include "private/qdataurl_p.h"
63
64 #include "qtextdocument_p.h"
65 #include <private/qabstracttextdocumentlayout_p.h>
66 #include "qpagedpaintdevice.h"
67 #include "private/qpagedpaintdevice_p.h"
68
69 #include <limits.h>
70
71 QT_BEGIN_NAMESPACE
72
73 Q_CORE_EXPORT unsigned int qt_int_sqrt(unsigned int n);
74
75 /*!
76     Returns true if the string \a text is likely to be rich text;
77     otherwise returns false.
78
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.
83
84     This function is defined in the \c <QTextDocument> header file.
85 */
86 bool Qt::mightBeRichText(const QString& text)
87 {
88     if (text.isEmpty())
89         return false;
90     int start = 0;
91
92     while (start < text.length() && text.at(start).isSpace())
93         ++start;
94
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('>')) {
101                 start += 2;
102                 break;
103             }
104             ++start;
105         }
106
107         while (start < text.length() && text.at(start).isSpace())
108             ++start;
109     }
110
111     if (text.mid(start, 5).toLower() == QLatin1String("<!doc"))
112         return true;
113     int open = start;
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 <...>
118         ++open;
119     }
120     if (open < text.length() && text.at(open) == QLatin1Char('<')) {
121         const int close = text.indexOf(QLatin1Char('>'), open);
122         if (close > -1) {
123             QString tag;
124             for (int i = open+1; i < close; ++i) {
125                 if (text[i].isDigit() || text[i].isLetter())
126                     tag += text[i];
127                 else if (!tag.isEmpty() && text[i].isSpace())
128                     break;
129                 else if (!tag.isEmpty() && text[i] == QLatin1Char('/') && i + 1 == close)
130                     break;
131                 else if (!text[i].isSpace() && (!tag.isEmpty() || text[i] != QLatin1Char('!')))
132                     return false; // that's not a tag
133             }
134 #ifndef QT_NO_TEXTHTMLPARSER
135             return QTextHtmlParser::lookupElement(tag.toLower()) != -1;
136 #else
137             return false;
138 #endif // QT_NO_TEXTHTMLPARSER
139         }
140     }
141     return false;
142 }
143
144 /*!
145     Converts the plain text string \a plain to a HTML string with
146     HTML metacharacters \c{<}, \c{>}, \c{&}, and \c{"} replaced by HTML
147     entities.
148
149     Example:
150
151     \snippet doc/src/snippets/code/src_gui_text_qtextdocument.cpp 0
152
153     This function is defined in the \c <QTextDocument> header file.
154
155     \sa convertFromPlainText(), mightBeRichText()
156 */
157 QString Qt::escape(const QString& plain)
158 {
159     QString rich;
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("&lt;");
164         else if (plain.at(i) == QLatin1Char('>'))
165             rich += QLatin1String("&gt;");
166         else if (plain.at(i) == QLatin1Char('&'))
167             rich += QLatin1String("&amp;");
168         else if (plain.at(i) == QLatin1Char('"'))
169             rich += QLatin1String("&quot;");
170         else
171             rich += plain.at(i);
172     }
173     return rich;
174 }
175
176 /*!
177     \fn QString Qt::convertFromPlainText(const QString &plain, WhiteSpaceMode mode)
178
179     Converts the plain text string \a plain to an HTML-formatted
180     paragraph while preserving most of its look.
181
182     \a mode defines how whitespace is handled.
183
184     This function is defined in the \c <QTextDocument> header file.
185
186     \sa escape(), mightBeRichText()
187 */
188 QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
189 {
190     int col = 0;
191     QString rich;
192     rich += QLatin1String("<p>");
193     for (int i = 0; i < plain.length(); ++i) {
194         if (plain[i] == QLatin1Char('\n')){
195             int c = 1;
196             while (i+1 < plain.length() && plain[i+1] == QLatin1Char('\n')) {
197                 i++;
198                 c++;
199             }
200             if (c == 1)
201                 rich += QLatin1String("<br>\n");
202             else {
203                 rich += QLatin1String("</p>\n");
204                 while (--c > 1)
205                     rich += QLatin1String("<br>\n");
206                 rich += QLatin1String("<p>");
207             }
208             col = 0;
209         } else {
210             if (mode == Qt::WhiteSpacePre && plain[i] == QLatin1Char('\t')){
211                 rich += QChar(0x00a0U);
212                 ++col;
213                 while (col % 8) {
214                     rich += QChar(0x00a0U);
215                     ++col;
216                 }
217             }
218             else if (mode == Qt::WhiteSpacePre && plain[i].isSpace())
219                 rich += QChar(0x00a0U);
220             else if (plain[i] == QLatin1Char('<'))
221                 rich += QLatin1String("&lt;");
222             else if (plain[i] == QLatin1Char('>'))
223                 rich += QLatin1String("&gt;");
224             else if (plain[i] == QLatin1Char('&'))
225                 rich += QLatin1String("&amp;");
226             else
227                 rich += plain[i];
228             ++col;
229         }
230     }
231     if (col != 0)
232         rich += QLatin1String("</p>");
233     return rich;
234 }
235
236 #ifndef QT_NO_TEXTCODEC
237 /*!
238     \internal
239
240     This function is defined in the \c <QTextDocument> header file.
241 */
242 QTextCodec *Qt::codecForHtml(const QByteArray &ba)
243 {
244     return QTextCodec::codecForHtml(ba);
245 }
246 #endif
247
248 /*!
249     \class QTextDocument
250     \reentrant
251
252     \brief The QTextDocument class holds formatted text that can be
253     viewed and edited using a QTextEdit.
254
255     \ingroup richtext-processing
256
257
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.
262
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
266     applied to.
267
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.
275
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.
283
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.
287
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:
294
295     \list
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.
304     \endlist
305
306     \sa QTextCursor, QTextEdit, \link richtext.html Rich Text Processing\endlink , {Text Object Example}
307 */
308
309 /*!
310     \property QTextDocument::defaultFont
311     \brief the default font used to display the document's text
312 */
313
314 /*!
315     \property QTextDocument::defaultTextOption
316     \brief the default text option will be set on all \l{QTextLayout}s in the document.
317
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.
321  */
322
323 /*!
324     Constructs an empty QTextDocument with the given \a parent.
325 */
326 QTextDocument::QTextDocument(QObject *parent)
327     : QObject(*new QTextDocumentPrivate, parent)
328 {
329     Q_D(QTextDocument);
330     d->init();
331 }
332
333 /*!
334     Constructs a QTextDocument containing the plain (unformatted) \a text
335     specified, and with the given \a parent.
336 */
337 QTextDocument::QTextDocument(const QString &text, QObject *parent)
338     : QObject(*new QTextDocumentPrivate, parent)
339 {
340     Q_D(QTextDocument);
341     d->init();
342     QTextCursor(this).insertText(text);
343 }
344
345 /*!
346     \internal
347 */
348 QTextDocument::QTextDocument(QTextDocumentPrivate &dd, QObject *parent)
349     : QObject(dd, parent)
350 {
351     Q_D(QTextDocument);
352     d->init();
353 }
354
355 /*!
356     Destroys the document.
357 */
358 QTextDocument::~QTextDocument()
359 {
360 }
361
362
363 /*!
364   Creates a new QTextDocument that is a copy of this text document. \a
365   parent is the parent of the returned text document.
366 */
367 QTextDocument *QTextDocument::clone(QObject *parent) const
368 {
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;
375     priv->url = d->url;
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;
385 #endif
386     return doc;
387 }
388
389 /*!
390     Returns true if the document is empty; otherwise returns false.
391 */
392 bool QTextDocument::isEmpty() const
393 {
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;
398 }
399
400 /*!
401   Clears the document.
402 */
403 void QTextDocument::clear()
404 {
405     Q_D(QTextDocument);
406     d->clear();
407     d->resources.clear();
408 }
409
410 /*!
411     \since 4.2
412
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.
416
417     See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
418     documentation for details.
419
420     \sa undoAvailable(), isUndoRedoEnabled()
421 */
422 void QTextDocument::undo(QTextCursor *cursor)
423 {
424     Q_D(QTextDocument);
425     const int pos = d->undoRedo(true);
426     if (cursor && pos >= 0) {
427         *cursor = QTextCursor(this);
428         cursor->setPosition(pos);
429     }
430 }
431
432 /*!
433     \since 4.2
434     Redoes the last editing operation on the document if \link
435     QTextDocument::isRedoAvailable() redo is available\endlink.
436
437     The provided \a cursor is positioned at the end of the location where
438     the edition operation was redone.
439 */
440 void QTextDocument::redo(QTextCursor *cursor)
441 {
442     Q_D(QTextDocument);
443     const int pos = d->undoRedo(false);
444     if (cursor && pos >= 0) {
445         *cursor = QTextCursor(this);
446         cursor->setPosition(pos);
447     }
448 }
449
450 /*! \enum QTextDocument::Stacks
451   
452   \value UndoStack              The undo stack.
453   \value RedoStack              The redo stack.
454   \value UndoAndRedoStacks      Both the undo and redo stacks.
455 */
456         
457 /*!
458     \since 4.7
459     Clears the stacks specified by \a stacksToClear.
460
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().
465
466     \sa QTextDocument::undoAvailable() QTextDocument::redoAvailable()
467 */
468 void QTextDocument::clearUndoRedoStacks(Stacks stacksToClear)
469 {
470     Q_D(QTextDocument);
471     d->clearUndoRedoStacks(stacksToClear, true);
472 }
473
474 /*!
475     \overload
476
477 */
478 void QTextDocument::undo()
479 {
480     Q_D(QTextDocument);
481     d->undoRedo(true);
482 }
483
484 /*!
485     \overload
486     Redoes the last editing operation on the document if \link
487     QTextDocument::isRedoAvailable() redo is available\endlink.
488 */
489 void QTextDocument::redo()
490 {
491     Q_D(QTextDocument);
492     d->undoRedo(false);
493 }
494
495 /*!
496     \internal
497
498     Appends a custom undo \a item to the undo stack.
499 */
500 void QTextDocument::appendUndoItem(QAbstractUndoItem *item)
501 {
502     Q_D(QTextDocument);
503     d->appendUndoItem(item);
504 }
505
506 /*!
507     \property QTextDocument::undoRedoEnabled
508     \brief whether undo/redo are enabled for this document
509
510     This defaults to true. If disabled, the undo stack is cleared and
511     no items will be added to it.
512 */
513 void QTextDocument::setUndoRedoEnabled(bool enable)
514 {
515     Q_D(QTextDocument);
516     d->enableUndoRedo(enable);
517 }
518
519 bool QTextDocument::isUndoRedoEnabled() const
520 {
521     Q_D(const QTextDocument);
522     return d->isUndoRedoEnabled();
523 }
524
525 /*!
526     \property QTextDocument::maximumBlockCount
527     \since 4.2
528     \brief Specifies the limit for blocks in the document.
529
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.
533
534     A negative or zero value specifies that the document may contain an unlimited
535     amount of blocks.
536
537     The default value is 0.
538
539     Note that setting this property will apply the limit immediately to the document
540     contents.
541
542     Setting this property also disables the undo redo history.
543
544     This property is undefined in documents with tables or frames.
545 */
546 int QTextDocument::maximumBlockCount() const
547 {
548     Q_D(const QTextDocument);
549     return d->maximumBlockCount;
550 }
551
552 void QTextDocument::setMaximumBlockCount(int maximum)
553 {
554     Q_D(QTextDocument);
555     d->maximumBlockCount = maximum;
556     d->ensureMaximumBlockCount();
557     setUndoRedoEnabled(false);
558 }
559
560 /*!
561     \since 4.3
562
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
565     word wrap mode.
566 */
567 QTextOption QTextDocument::defaultTextOption() const
568 {
569     Q_D(const QTextDocument);
570     return d->defaultTextOption;
571 }
572
573 /*!
574     \since 4.3
575
576     Sets the default text option.
577 */
578 void QTextDocument::setDefaultTextOption(const QTextOption &option)
579 {
580     Q_D(QTextDocument);
581     d->defaultTextOption = option;
582     if (d->lout)
583         d->lout->documentChanged(0, 0, d->length());
584 }
585
586 /*!
587     \since 4.8
588
589     The default cursor movement style is used by all QTextCursor objects
590     created from the document. The default is Qt::LogicalMoveStyle.
591 */
592 Qt::CursorMoveStyle QTextDocument::defaultCursorMoveStyle() const
593 {
594     Q_D(const QTextDocument);
595     return d->defaultCursorMoveStyle;
596 }
597
598 /*!
599     \since 4.8
600
601     Sets the default cursor movement style to the given \a style.
602 */
603 void QTextDocument::setDefaultCursorMoveStyle(Qt::CursorMoveStyle style)
604 {
605     Q_D(QTextDocument);
606     d->defaultCursorMoveStyle = style;
607 }
608
609 /*!
610     \fn void QTextDocument::markContentsDirty(int position, int length)
611
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
614     again.
615 */
616 void QTextDocument::markContentsDirty(int from, int length)
617 {
618     Q_D(QTextDocument);
619     d->documentChange(from, length);
620     if (!d->inContentsChange) {
621         if (d->lout) {
622             d->lout->documentChanged(d->docChangeFrom, d->docChangeOldLength, d->docChangeLength);
623             d->docChangeFrom = -1;
624         }
625     }
626 }
627
628 /*!
629     \property QTextDocument::useDesignMetrics
630     \since 4.1
631     \brief whether the document uses design metrics of fonts to improve the accuracy of text layout
632
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.
636
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.
640
641     By default, this property is false.
642 */
643
644 void QTextDocument::setUseDesignMetrics(bool b)
645 {
646     Q_D(QTextDocument);
647     if (b == d->defaultTextOption.useDesignMetrics())
648         return;
649     d->defaultTextOption.setUseDesignMetrics(b);
650     if (d->lout)
651         d->lout->documentChanged(0, 0, d->length());
652 }
653
654 bool QTextDocument::useDesignMetrics() const
655 {
656     Q_D(const QTextDocument);
657     return d->defaultTextOption.useDesignMetrics();
658 }
659
660 /*!
661     \since 4.2
662
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.
665 */
666 void QTextDocument::drawContents(QPainter *p, const QRectF &rect)
667 {
668     p->save();
669     QAbstractTextDocumentLayout::PaintContext ctx;
670     if (rect.isValid()) {
671         p->setClipRect(rect);
672         ctx.clip = rect;
673     }
674     documentLayout()->draw(p, ctx);
675     p->restore();
676 }
677
678 /*!
679     \property QTextDocument::textWidth
680     \since 4.2
681
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.
687
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.
690
691     The default value is -1.
692
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.
696
697     \sa size(), idealWidth(), pageSize()
698 */
699 void QTextDocument::setTextWidth(qreal width)
700 {
701     Q_D(QTextDocument);
702     QSizeF sz = d->pageSize;
703     sz.setWidth(width);
704     sz.setHeight(-1);
705     setPageSize(sz);
706 }
707
708 qreal QTextDocument::textWidth() const
709 {
710     Q_D(const QTextDocument);
711     return d->pageSize.width();
712 }
713
714 /*!
715     \since 4.2
716
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().
719
720     \sa adjustSize(), textWidth
721 */
722 qreal QTextDocument::idealWidth() const
723 {
724     if (QTextDocumentLayout *lout = qobject_cast<QTextDocumentLayout *>(documentLayout()))
725         return lout->idealWidth();
726     return textWidth();
727 }
728
729 /*!
730     \property QTextDocument::documentMargin
731     \since 4.5
732
733      The margin around the document. The default is 4.
734 */
735 qreal QTextDocument::documentMargin() const
736 {
737     Q_D(const QTextDocument);
738     return d->documentMargin;
739 }
740
741 void QTextDocument::setDocumentMargin(qreal margin)
742 {
743     Q_D(QTextDocument);
744     if (d->documentMargin != margin) {
745         d->documentMargin = margin;
746
747         QTextFrame* root = rootFrame();
748         QTextFrameFormat format = root->frameFormat();
749         format.setMargin(margin);
750         root->setFrameFormat(format);
751
752         if (d->lout)
753             d->lout->documentChanged(0, 0, d->length());
754     }
755 }
756
757
758 /*!
759     \property QTextDocument::indentWidth
760     \since 4.4
761
762     Returns the width used for text list and text block indenting.
763
764     The indent properties of QTextListFormat and QTextBlockFormat specify
765     multiples of this value. The default indent width is 40.
766 */
767 qreal QTextDocument::indentWidth() const
768 {
769     Q_D(const QTextDocument);
770     return d->indentWidth;
771 }
772
773
774 /*!
775     \since 4.4
776
777     Sets the \a width used for text list and text block indenting.
778
779     The indent properties of QTextListFormat and QTextBlockFormat specify
780     multiples of this value. The default indent width is 40 .
781
782     \sa indentWidth()
783 */
784 void QTextDocument::setIndentWidth(qreal width)
785 {
786     Q_D(QTextDocument);
787     if (d->indentWidth != width) {
788         d->indentWidth = width;
789         if (d->lout)
790             d->lout->documentChanged(0, 0, d->length());
791     }
792 }
793
794
795
796
797 /*!
798     \since 4.2
799
800     Adjusts the document to a reasonable size.
801
802     \sa idealWidth(), textWidth, size
803 */
804 void QTextDocument::adjustSize()
805 {
806     // Pull this private function in from qglobal.cpp
807     QFont f = defaultFont();
808     QFontMetrics fm(f);
809     int mw =  fm.width(QLatin1Char('x')) * 80;
810     int w = mw;
811     setTextWidth(w);
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));
816
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));
821         }
822     }
823     setTextWidth(idealWidth());
824 }
825
826 /*!
827     \property QTextDocument::size
828     \since 4.2
829
830     Returns the actual size of the document.
831     This is equivalent to documentLayout()->documentSize();
832
833     The size of the document can be changed either by setting
834     a text width or setting an entire page size.
835
836     Note that the width is always >= pageSize().width().
837
838     By default, for a newly-created, empty document, this property contains
839     a configuration-dependent size.
840
841     \sa setTextWidth(), setPageSize(), idealWidth()
842 */
843 QSizeF QTextDocument::size() const
844 {
845     return documentLayout()->documentSize();
846 }
847
848 /*!
849     \property QTextDocument::blockCount
850     \since 4.2
851
852     Returns the number of text blocks in the document.
853
854     The value of this property is undefined in documents with tables or frames.
855
856     By default, if defined, this property contains a value of 1.
857     \sa lineCount(), characterCount()
858 */
859 int QTextDocument::blockCount() const
860 {
861     Q_D(const QTextDocument);
862     return d->blockMap().numNodes();
863 }
864
865
866 /*!
867   \since 4.5
868
869   Returns the number of lines of this document (if the layout supports
870   this). Otherwise, this is identical to the number of blocks.
871
872   \sa blockCount(), characterCount()
873  */
874 int QTextDocument::lineCount() const
875 {
876     Q_D(const QTextDocument);
877     return d->blockMap().length(2);
878 }
879
880 /*!
881   \since 4.5
882
883   Returns the number of characters of this document.
884
885   \sa blockCount(), characterAt()
886  */
887 int QTextDocument::characterCount() const
888 {
889     Q_D(const QTextDocument);
890     return d->length();
891 }
892
893 /*!
894   \since 4.5
895
896   Returns the character at position \a pos, or a null character if the
897   position is out of range.
898
899   \sa characterCount()
900  */
901 QChar QTextDocument::characterAt(int pos) const
902 {
903     Q_D(const QTextDocument);
904     if (pos < 0 || pos >= d->length())
905         return QChar();
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);
910 }
911
912
913 /*!
914     \property QTextDocument::defaultStyleSheet
915     \since 4.2
916
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().
919
920     The style sheet needs to be compliant to CSS 2.1 syntax.
921
922     \bold{Note:} Changing the default style sheet does not have any effect to the existing content
923     of the document.
924
925     \sa {Supported HTML Subset}
926 */
927
928 #ifndef QT_NO_CSSPARSER
929 void QTextDocument::setDefaultStyleSheet(const QString &sheet)
930 {
931     Q_D(QTextDocument);
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);
937 }
938
939 QString QTextDocument::defaultStyleSheet() const
940 {
941     Q_D(const QTextDocument);
942     return d->defaultStyleSheet;
943 }
944 #endif // QT_NO_CSSPARSER
945
946 /*!
947     \fn void QTextDocument::contentsChanged()
948
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.
951
952     \sa contentsChange()
953 */
954
955 /*!
956     \fn void QTextDocument::contentsChange(int position, int charsRemoved, int charsAdded)
957
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.
960
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).
964
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
967     for the document.
968
969     \sa QAbstractTextDocumentLayout::documentChanged(), contentsChanged()
970 */
971
972
973 /*!
974     \fn QTextDocument::undoAvailable(bool available);
975
976     This signal is emitted whenever undo operations become available
977     (\a available is true) or unavailable (\a available is false).
978
979     See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
980     documentation for details.
981
982     \sa undo(), isUndoRedoEnabled()
983 */
984
985 /*!
986     \fn QTextDocument::redoAvailable(bool available);
987
988     This signal is emitted whenever redo operations become available
989     (\a available is true) or unavailable (\a available is false).
990 */
991
992 /*!
993     \fn QTextDocument::cursorPositionChanged(const QTextCursor &cursor);
994
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
999     QTextEdit.
1000 */
1001
1002 /*!
1003     \fn QTextDocument::blockCountChanged(int newBlockCount);
1004     \since 4.3
1005
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
1008     total.
1009 */
1010
1011 /*!
1012     \fn QTextDocument::documentLayoutChanged();
1013     \since 4.4
1014
1015     This signal is emitted when a new document layout is set.
1016
1017     \sa setDocumentLayout()
1018
1019 */
1020
1021
1022 /*!
1023     Returns true if undo is available; otherwise returns false.
1024
1025     \sa isRedoAvailable(), availableUndoSteps()
1026 */
1027 bool QTextDocument::isUndoAvailable() const
1028 {
1029     Q_D(const QTextDocument);
1030     return d->isUndoAvailable();
1031 }
1032
1033 /*!
1034     Returns true if redo is available; otherwise returns false.
1035
1036     \sa isUndoAvailable(), availableRedoSteps()
1037 */
1038 bool QTextDocument::isRedoAvailable() const
1039 {
1040     Q_D(const QTextDocument);
1041     return d->isRedoAvailable();
1042 }
1043
1044 /*! \since 4.6
1045
1046     Returns the number of available undo steps.
1047
1048     \sa isUndoAvailable()
1049 */
1050 int QTextDocument::availableUndoSteps() const
1051 {
1052     Q_D(const QTextDocument);
1053     return d->availableUndoSteps();
1054 }
1055
1056 /*! \since 4.6
1057
1058     Returns the number of available redo steps.
1059
1060     \sa isRedoAvailable()
1061 */
1062 int QTextDocument::availableRedoSteps() const
1063 {
1064     Q_D(const QTextDocument);
1065     return d->availableRedoSteps();
1066 }
1067
1068 /*! \since 4.4
1069
1070     Returns the document's revision (if undo is enabled).
1071
1072     The revision is guaranteed to increase when a document that is not
1073     modified is edited.
1074
1075     \sa QTextBlock::revision(), isModified()
1076  */
1077 int QTextDocument::revision() const
1078 {
1079     Q_D(const QTextDocument);
1080     return d->revision;
1081 }
1082
1083
1084
1085 /*!
1086     Sets the document to use the given \a layout. The previous layout
1087     is deleted.
1088
1089     \sa documentLayoutChanged()
1090 */
1091 void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *layout)
1092 {
1093     Q_D(QTextDocument);
1094     d->setLayout(layout);
1095 }
1096
1097 /*!
1098     Returns the document layout for this document.
1099 */
1100 QAbstractTextDocumentLayout *QTextDocument::documentLayout() const
1101 {
1102     Q_D(const QTextDocument);
1103     if (!d->lout) {
1104         QTextDocument *that = const_cast<QTextDocument *>(this);
1105         that->d_func()->setLayout(new QTextDocumentLayout(that));
1106     }
1107     return d->lout;
1108 }
1109
1110
1111 /*!
1112     Returns meta information about the document of the type specified by
1113     \a info.
1114
1115     \sa setMetaInformation()
1116 */
1117 QString QTextDocument::metaInformation(MetaInformation info) const
1118 {
1119     Q_D(const QTextDocument);
1120     switch (info) {
1121     case DocumentTitle:
1122         return d->title;
1123     case DocumentUrl:
1124         return d->url;
1125     }
1126     return QString();
1127 }
1128
1129 /*!
1130     Sets the document's meta information of the type specified by \a info
1131     to the given \a string.
1132
1133     \sa metaInformation()
1134 */
1135 void QTextDocument::setMetaInformation(MetaInformation info, const QString &string)
1136 {
1137     Q_D(QTextDocument);
1138     switch (info) {
1139     case DocumentTitle:
1140         d->title = string;
1141         break;
1142     case DocumentUrl:
1143         d->url = string;
1144         break;
1145     }
1146 }
1147
1148 /*!
1149     Returns the plain text contained in the document. If you want
1150     formatting information use a QTextCursor instead.
1151
1152     \sa toHtml()
1153 */
1154 QString QTextDocument::toPlainText() const
1155 {
1156     Q_D(const QTextDocument);
1157     QString txt = d->plainText();
1158
1159     QChar *uc = txt.data();
1160     QChar *e = uc + txt.size();
1161
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');
1169             break;
1170         case QChar::Nbsp:
1171             *uc = QLatin1Char(' ');
1172             break;
1173         default:
1174             ;
1175         }
1176     }
1177     return txt;
1178 }
1179
1180 /*!
1181     Replaces the entire contents of the document with the given plain
1182     \a text.
1183
1184     \sa setHtml()
1185 */
1186 void QTextDocument::setPlainText(const QString &text)
1187 {
1188     Q_D(QTextDocument);
1189     bool previousState = d->isUndoRedoEnabled();
1190     d->enableUndoRedo(false);
1191     d->beginEditBlock();
1192     d->clear();
1193     QTextCursor(this).insertText(text);
1194     d->endEditBlock();
1195     d->enableUndoRedo(previousState);
1196 }
1197
1198 /*!
1199     Replaces the entire contents of the document with the given
1200     HTML-formatted text in the \a html string.
1201
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".
1205
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().
1209
1210     \sa setPlainText(), {Supported HTML Subset}
1211 */
1212
1213 #ifndef QT_NO_TEXTHTMLPARSER
1214
1215 void QTextDocument::setHtml(const QString &html)
1216 {
1217     Q_D(QTextDocument);
1218     bool previousState = d->isUndoRedoEnabled();
1219     d->enableUndoRedo(false);
1220     d->beginEditBlock();
1221     d->clear();
1222     QTextHtmlImporter(this, html, QTextHtmlImporter::ImportToDocument).import();
1223     d->endEditBlock();
1224     d->enableUndoRedo(previousState);
1225 }
1226
1227 #endif // QT_NO_TEXTHTMLPARSER
1228
1229 /*!
1230     \enum QTextDocument::FindFlag
1231
1232     This enum describes the options available to QTextDocument's find function. The options
1233     can be OR-ed together from the following list:
1234
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.
1239 */
1240
1241 /*!
1242     \enum QTextDocument::MetaInformation
1243
1244     This enum describes the different types of meta information that can be
1245     added to a document.
1246
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.
1250
1251     \sa metaInformation(), setMetaInformation()
1252 */
1253
1254 /*!
1255     \fn QTextCursor QTextDocument::find(const QString &subString, int position, FindFlags options) const
1256
1257     \overload
1258
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.
1263
1264     Returns a cursor with the match selected if \a subString
1265     was found; otherwise returns a null cursor.
1266
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.
1269 */
1270 QTextCursor QTextDocument::find(const QString &subString, int from, FindFlags options) const
1271 {
1272     QRegExp expr(subString);
1273     expr.setPatternSyntax(QRegExp::FixedString);
1274     expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
1275
1276     return find(expr, from, options);
1277 }
1278
1279 /*!
1280     \fn QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &cursor, FindFlags options) const
1281
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.
1286
1287     Returns a cursor with the match selected if \a subString was found; otherwise
1288     returns a null cursor.
1289
1290     If the given \a cursor has a selection, the search begins after the
1291     selection; otherwise it begins at the cursor's position.
1292
1293     By default the search is case-sensitive, and can match text anywhere in the
1294     document.
1295 */
1296 QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &from, FindFlags options) const
1297 {
1298     int pos = 0;
1299     if (!from.isNull()) {
1300         if (options & QTextDocument::FindBackward)
1301             pos = from.selectionStart();
1302         else
1303             pos = from.selectionEnd();
1304     }
1305     QRegExp expr(subString);
1306     expr.setPatternSyntax(QRegExp::FixedString);
1307     expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
1308
1309     return find(expr, pos, options);
1310 }
1311
1312
1313 static bool findInBlock(const QTextBlock &block, const QRegExp &expression, int offset,
1314                         QTextDocument::FindFlags options, QTextCursor &cursor)
1315 {
1316     const QRegExp expr(expression);
1317     QString text = block.text();
1318     text.replace(QChar::Nbsp, QLatin1Char(' '));
1319
1320     int idx = -1;
1321     while (offset >=0 && offset <= text.length()) {
1322         idx = (options & QTextDocument::FindBackward) ?
1323                expr.lastIndexIn(text, offset) : expr.indexIn(text, offset);
1324         if (idx == -1)
1325             return false;
1326
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;
1334                 idx = -1;
1335                 continue;
1336             }
1337         }
1338         //we have a hit, return the cursor for that.
1339         break;
1340     }
1341     if (idx == -1)
1342         return false;
1343     cursor = QTextCursor(block.docHandle(), block.position() + idx);
1344     cursor.setPosition(cursor.position() + expr.matchedLength(), QTextCursor::KeepAnchor);
1345     return true;
1346 }
1347
1348 /*!
1349     \fn QTextCursor QTextDocument::find(const QRegExp & expr, int position, FindFlags options) const
1350
1351     \overload
1352
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.
1358
1359     Returns a cursor with the match selected if a match was found; otherwise
1360     returns a null cursor.
1361
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.
1364 */
1365 QTextCursor QTextDocument::find(const QRegExp & expr, int from, FindFlags options) const
1366 {
1367     Q_D(const QTextDocument);
1368
1369     if (expr.isEmpty())
1370         return QTextCursor();
1371
1372     int pos = from;
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) {
1376         --pos ;
1377         if(pos < 0)
1378             return QTextCursor();
1379     }
1380
1381     QTextCursor cursor;
1382     QTextBlock block = d->blocksFind(pos);
1383
1384     if (!(options & FindBackward)) {
1385        int blockOffset = qMax(0, pos - block.position());
1386         while (block.isValid()) {
1387             if (findInBlock(block, expr, blockOffset, options, cursor))
1388                 return cursor;
1389             blockOffset = 0;
1390             block = block.next();
1391         }
1392     } else {
1393         int blockOffset = pos - block.position();
1394         while (block.isValid()) {
1395             if (findInBlock(block, expr, blockOffset, options, cursor))
1396                 return cursor;
1397             block = block.previous();
1398             blockOffset = block.length() - 1;
1399         }
1400     }
1401
1402     return QTextCursor();
1403 }
1404
1405 /*!
1406     \fn QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &cursor, FindFlags options) const
1407
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.
1413
1414     Returns a cursor with the match selected if a match was found; otherwise
1415     returns a null cursor.
1416
1417     If the given \a cursor has a selection, the search begins after the
1418     selection; otherwise it begins at the cursor's position.
1419
1420     By default the search is case-sensitive, and can match text anywhere in the
1421     document.
1422 */
1423 QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &from, FindFlags options) const
1424 {
1425     int pos = 0;
1426     if (!from.isNull()) {
1427         if (options & QTextDocument::FindBackward)
1428             pos = from.selectionStart();
1429         else
1430             pos = from.selectionEnd();
1431     }
1432     return find(expr, pos, options);
1433 }
1434
1435
1436 /*!
1437     \fn QTextObject *QTextDocument::createObject(const QTextFormat &format)
1438
1439     Creates and returns a new document object (a QTextObject), based
1440     on the given \a format.
1441
1442     QTextObjects will always get created through this method, so you
1443     must reimplement it if you use custom text objects inside your document.
1444 */
1445 QTextObject *QTextDocument::createObject(const QTextFormat &f)
1446 {
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);
1454
1455     return obj;
1456 }
1457
1458 /*!
1459     \internal
1460
1461     Returns the frame that contains the text cursor position \a pos.
1462 */
1463 QTextFrame *QTextDocument::frameAt(int pos) const
1464 {
1465     Q_D(const QTextDocument);
1466     return d->frameAt(pos);
1467 }
1468
1469 /*!
1470     Returns the document's root frame.
1471 */
1472 QTextFrame *QTextDocument::rootFrame() const
1473 {
1474     Q_D(const QTextDocument);
1475     return d->rootFrame();
1476 }
1477
1478 /*!
1479     Returns the text object associated with the given \a objectIndex.
1480 */
1481 QTextObject *QTextDocument::object(int objectIndex) const
1482 {
1483     Q_D(const QTextDocument);
1484     return d->objectForIndex(objectIndex);
1485 }
1486
1487 /*!
1488     Returns the text object associated with the format \a f.
1489 */
1490 QTextObject *QTextDocument::objectForFormat(const QTextFormat &f) const
1491 {
1492     Q_D(const QTextDocument);
1493     return d->objectForFormat(f);
1494 }
1495
1496
1497 /*!
1498     Returns the text block that contains the \a{pos}-th character.
1499 */
1500 QTextBlock QTextDocument::findBlock(int pos) const
1501 {
1502     Q_D(const QTextDocument);
1503     return QTextBlock(docHandle(), d->blockMap().findNode(pos));
1504 }
1505
1506 /*!
1507     \since 4.4
1508     Returns the text block with the specified \a blockNumber.
1509
1510     \sa QTextBlock::blockNumber()
1511 */
1512 QTextBlock QTextDocument::findBlockByNumber(int blockNumber) const
1513 {
1514     Q_D(const QTextDocument);
1515     return QTextBlock(docHandle(), d->blockMap().findNode(blockNumber, 1));
1516 }
1517
1518 /*!
1519     \since 4.5
1520     Returns the text block that contains the specified \a lineNumber.
1521
1522     \sa QTextBlock::firstLineNumber()
1523 */
1524 QTextBlock QTextDocument::findBlockByLineNumber(int lineNumber) const
1525 {
1526     Q_D(const QTextDocument);
1527     return QTextBlock(docHandle(), d->blockMap().findNode(lineNumber, 2));
1528 }
1529
1530 /*!
1531     Returns the document's first text block.
1532
1533     \sa firstBlock()
1534 */
1535 QTextBlock QTextDocument::begin() const
1536 {
1537     Q_D(const QTextDocument);
1538     return QTextBlock(docHandle(), d->blockMap().begin().n);
1539 }
1540
1541 /*!
1542     This function returns a block to test for the end of the document
1543     while iterating over it.
1544
1545     \snippet doc/src/snippets/textdocumentendsnippet.cpp 0
1546
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.
1550
1551     \sa lastBlock()
1552 */
1553 QTextBlock QTextDocument::end() const
1554 {
1555     return QTextBlock(docHandle(), 0);
1556 }
1557
1558 /*!
1559     \since 4.4
1560     Returns the document's first text block.
1561 */
1562 QTextBlock QTextDocument::firstBlock() const
1563 {
1564     Q_D(const QTextDocument);
1565     return QTextBlock(docHandle(), d->blockMap().begin().n);
1566 }
1567
1568 /*!
1569     \since 4.4
1570     Returns the document's last (valid) text block.
1571 */
1572 QTextBlock QTextDocument::lastBlock() const
1573 {
1574     Q_D(const QTextDocument);
1575     return QTextBlock(docHandle(), d->blockMap().last().n);
1576 }
1577
1578 /*!
1579     \property QTextDocument::pageSize
1580     \brief the page size that should be used for laying out the document
1581
1582     By default, for a newly-created, empty document, this property contains
1583     an undefined size.
1584
1585     \sa modificationChanged()
1586 */
1587
1588 void QTextDocument::setPageSize(const QSizeF &size)
1589 {
1590     Q_D(QTextDocument);
1591     d->pageSize = size;
1592     if (d->lout)
1593         d->lout->documentChanged(0, 0, d->length());
1594 }
1595
1596 QSizeF QTextDocument::pageSize() const
1597 {
1598     Q_D(const QTextDocument);
1599     return d->pageSize;
1600 }
1601
1602 /*!
1603   returns the number of pages in this document.
1604 */
1605 int QTextDocument::pageCount() const
1606 {
1607     return documentLayout()->pageCount();
1608 }
1609
1610 /*!
1611     Sets the default \a font to use in the document layout.
1612 */
1613 void QTextDocument::setDefaultFont(const QFont &font)
1614 {
1615     Q_D(QTextDocument);
1616     d->setDefaultFont(font);
1617     if (d->lout)
1618         d->lout->documentChanged(0, 0, d->length());
1619 }
1620
1621 /*!
1622     Returns the default font to be used in the document layout.
1623 */
1624 QFont QTextDocument::defaultFont() const
1625 {
1626     Q_D(const QTextDocument);
1627     return d->defaultFont();
1628 }
1629
1630 /*!
1631     \fn QTextDocument::modificationChanged(bool changed)
1632
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
1636     false.
1637
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.
1642 */
1643
1644 /*!
1645     \property QTextDocument::modified
1646     \brief whether the document has been modified by the user
1647
1648     By default, this property is false.
1649
1650     \sa modificationChanged()
1651 */
1652
1653 bool QTextDocument::isModified() const
1654 {
1655     return docHandle()->isModified();
1656 }
1657
1658 void QTextDocument::setModified(bool m)
1659 {
1660     docHandle()->setModified(m);
1661 }
1662
1663 #ifndef QT_NO_PRINTER
1664 static void printPage(int index, QPainter *painter, const QTextDocument *doc, const QRectF &body, const QPointF &pageNumberPos)
1665 {
1666     painter->save();
1667     painter->translate(body.left(), body.top() - (index - 1) * body.height());
1668     QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
1669
1670     QAbstractTextDocumentLayout *layout = doc->documentLayout();
1671     QAbstractTextDocumentLayout::PaintContext ctx;
1672
1673     painter->setClipRect(view);
1674     ctx.clip = view;
1675
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
1678     // look that nice
1679     ctx.palette.setColor(QPalette::Text, Qt::black);
1680
1681     layout->draw(painter, ctx);
1682
1683     if (!pageNumberPos.isNull()) {
1684         painter->setClipping(false);
1685         painter->setFont(QFont(doc->defaultFont()));
1686         const QString pageString = QString::number(index);
1687
1688         painter->drawText(qRound(pageNumberPos.x() - painter->fontMetrics().width(pageString)),
1689                           qRound(pageNumberPos.y() + view.top()),
1690                           pageString);
1691     }
1692
1693     painter->restore();
1694 }
1695
1696 /*!
1697     Prints the document to the given \a device. The QPageablePaintDevice must be
1698     set up before being used with this function.
1699
1700     This is only a convenience method to print the whole document to the printer.
1701
1702     If the document is already paginated through a specified height in the pageSize()
1703     property it is printed as-is.
1704
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.
1710
1711     \sa QTextEdit::print()
1712 */
1713
1714 void QTextDocument::print(QPagedPaintDevice *printer) const
1715 {
1716     Q_D(const QTextDocument);
1717
1718     if (!printer)
1719         return;
1720
1721     bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
1722                              && d->pageSize.height() != INT_MAX;
1723
1724     QPagedPaintDevicePrivate *pd = QPagedPaintDevicePrivate::get(printer);
1725
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);
1731     }
1732     // ### use the margins correctly
1733
1734     QPainter p(printer);
1735
1736     // Check that there is a valid device to print to.
1737     if (!p.isActive())
1738         return;
1739
1740     const QTextDocument *doc = this;
1741     QScopedPointer<QTextDocument> clonedDoc;
1742     (void)doc->documentLayout(); // make sure that there is a layout
1743
1744     QRectF body = QRectF(QPointF(0, 0), d->pageSize);
1745     QPointF pageNumberPos;
1746
1747     if (documentPaginated) {
1748         qreal sourceDpiX = qt_defaultDpi();
1749         qreal sourceDpiY = sourceDpiX;
1750
1751         QPaintDevice *dev = doc->documentLayout()->paintDevice();
1752         if (dev) {
1753             sourceDpiX = dev->logicalDpiX();
1754             sourceDpiY = dev->logicalDpiY();
1755         }
1756
1757         const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
1758         const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
1759
1760         // scale to dpi
1761         p.scale(dpiScaleX, dpiScaleY);
1762
1763         QSizeF scaledPageSize = d->pageSize;
1764         scaledPageSize.rwidth() *= dpiScaleX;
1765         scaledPageSize.rheight() *= dpiScaleY;
1766
1767         const QSizeF printerPageSize(printer->width(), printer->height());
1768
1769         // scale to page
1770         p.scale(printerPageSize.width() / scaledPageSize.width(),
1771                 printerPageSize.height() / scaledPageSize.height());
1772     } else {
1773         doc = clone(const_cast<QTextDocument *>(this));
1774         clonedDoc.reset(const_cast<QTextDocument *>(doc));
1775
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());
1780         }
1781
1782         QAbstractTextDocumentLayout *layout = doc->documentLayout();
1783         layout->setPaintDevice(p.device());
1784
1785         // copy the custom object handlers
1786         layout->d_func()->handlers = documentLayout()->d_func()->handlers;
1787
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);
1793
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()
1798                                 + 5 * dpiy / 72.0);
1799         clonedDoc->setPageSize(body.size());
1800     }
1801
1802     int fromPage = pd->fromPage;
1803     int toPage = pd->toPage;
1804     bool ascending = true;
1805
1806     if (fromPage == 0 && toPage == 0) {
1807         fromPage = 1;
1808         toPage = doc->pageCount();
1809     }
1810     // paranoia check
1811     fromPage = qMax(1, fromPage);
1812     toPage = qMin(doc->pageCount(), toPage);
1813
1814     if (toPage < fromPage) {
1815         // if the user entered a page range outside the actual number
1816         // of printable pages, just return
1817         return;
1818     }
1819
1820 //    if (printer->pageOrder() == QPrinter::LastPageFirst) {
1821 //        int tmp = fromPage;
1822 //        fromPage = toPage;
1823 //        toPage = tmp;
1824 //        ascending = false;
1825 //    }
1826
1827     int page = fromPage;
1828     while (true) {
1829         printPage(page, &p, doc, body, pageNumberPos);
1830
1831         if (page == toPage)
1832             break;
1833
1834         if (ascending)
1835             ++page;
1836         else
1837             --page;
1838
1839         if (!printer->newPage())
1840             return;
1841     }
1842 }
1843 #endif
1844
1845 /*!
1846     \enum QTextDocument::ResourceType
1847
1848     This enum describes the types of resources that can be loaded by
1849     QTextDocument's loadResource() function.
1850
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
1861                          resource types.
1862
1863     \sa loadResource()
1864 */
1865
1866 /*!
1867     Returns data of the specified \a type from the resource with the
1868     given \a name.
1869
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
1873     object.
1874
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.
1879
1880     \sa QTextDocument::ResourceType
1881 */
1882 QVariant QTextDocument::resource(int type, const QUrl &name) const
1883 {
1884     Q_D(const QTextDocument);
1885     QVariant r = d->resources.value(name);
1886     if (!r.isValid()) {
1887         r = d->cachedResources.value(name);
1888         if (!r.isValid())
1889             r = const_cast<QTextDocument *>(this)->loadResource(type, name);
1890     }
1891     return r;
1892 }
1893
1894 /*!
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.
1898
1899     For example, you can add an image as a resource in order to reference it
1900     from within the document:
1901
1902     \snippet snippets/textdocument-resources/main.cpp Adding a resource
1903
1904     The image can be inserted into the document using the QTextCursor API:
1905
1906     \snippet snippets/textdocument-resources/main.cpp Inserting an image with a cursor
1907
1908     Alternatively, you can insert images using the HTML \c img tag:
1909
1910     \snippet snippets/textdocument-resources/main.cpp Inserting an image using HTML
1911 */
1912 void QTextDocument::addResource(int type, const QUrl &name, const QVariant &resource)
1913 {
1914     Q_UNUSED(type);
1915     Q_D(QTextDocument);
1916     d->resources.insert(name, resource);
1917 }
1918
1919 /*!
1920     Loads data of the specified \a type from the resource with the
1921     given \a name.
1922
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
1926     object.
1927
1928     When called by Qt, \a type is one of the values of
1929     QTextDocument::ResourceType.
1930
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.
1934 */
1935 QVariant QTextDocument::loadResource(int type, const QUrl &name)
1936 {
1937     Q_D(QTextDocument);
1938     QVariant r;
1939
1940     QTextDocument *doc = qobject_cast<QTextDocument *>(parent());
1941     if (doc) {
1942         r = doc->loadResource(type, name);
1943     }
1944 #if 0
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);
1950     }
1951 #endif
1952 #ifndef QT_NO_TEXTCONTROL
1953     else if (QWidgetTextControl *control = qobject_cast<QWidgetTextControl *>(parent())) {
1954         r = control->loadResource(type, name);
1955     }
1956 #endif
1957 #endif
1958
1959     // handle data: URLs
1960     if (r.isNull() && name.scheme().compare(QLatin1String("data"), Qt::CaseInsensitive) == 0)
1961         r = qDecodeDataUrl(name).second;
1962
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;
1967
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);
1975         } else {
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
1978             // file system.
1979             QFileInfo fi(currentURL.toLocalFile());
1980             if (fi.exists()) {
1981                 resourceUrl =
1982                     QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
1983             } else if (currentURL.isEmpty()) {
1984                 resourceUrl.setScheme(QLatin1String("file"));
1985             }
1986         }
1987
1988         QString s = resourceUrl.toLocalFile();
1989         QFile f(s);
1990         if (!s.isEmpty() && f.open(QFile::ReadOnly)) {
1991             r = f.readAll();
1992             f.close();
1993         }
1994     }
1995
1996     if (!r.isNull()) {
1997         if (type == ImageResource && r.type() == QVariant::ByteArray) {
1998             if (qApp->thread() != QThread::currentThread()) {
1999                 // must use images in non-GUI threads
2000                 QImage image;
2001                 image.loadFromData(r.toByteArray());
2002                 if (!image.isNull())
2003                     r = image;
2004             } else {
2005                 QPixmap pm;
2006                 pm.loadFromData(r.toByteArray());
2007                 if (!pm.isNull())
2008                     r = pm;
2009             }
2010         }
2011         d->cachedResources.insert(name, r);
2012     }
2013     return r;
2014 }
2015
2016 static QTextFormat formatDifference(const QTextFormat &from, const QTextFormat &to)
2017 {
2018     QTextFormat diff = to;
2019
2020     const QMap<int, QVariant> props = to.properties();
2021     for (QMap<int, QVariant>::ConstIterator it = props.begin(), end = props.end();
2022          it != end; ++it)
2023         if (it.value() == from.property(it.key()))
2024             diff.clearProperty(it.key());
2025
2026     return diff;
2027 }
2028
2029 QTextHtmlExporter::QTextHtmlExporter(const QTextDocument *_doc)
2030     : doc(_doc), fragmentMarkers(false)
2031 {
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);
2039 }
2040
2041 /*!
2042     Returns the document in HTML format. The conversion may not be
2043     perfect, especially for complex documents, due to the limitations
2044     of HTML.
2045 */
2046 QString QTextHtmlExporter::toHtml(const QByteArray &encoding, ExportMode mode)
2047 {
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());
2052
2053     fragmentMarkers = (mode == ExportFragment);
2054
2055     if (!encoding.isEmpty())
2056         html += QString::fromLatin1("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\" />").arg(QString::fromAscii(encoding));
2057
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");
2065
2066     if (mode == ExportEntireDocument) {
2067         html += QLatin1String(" style=\"");
2068
2069         emitFontFamily(defaultCharFormat.fontFamily());
2070
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;");
2079         }
2080
2081         html += QLatin1String(" font-weight:");
2082         html += QString::number(defaultCharFormat.fontWeight() * 8);
2083         html += QLatin1Char(';');
2084
2085         html += QLatin1String(" font-style:");
2086         html += (defaultCharFormat.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2087         html += QLatin1Char(';');
2088
2089         // do not set text-decoration on the default font since those values are /always/ propagated
2090         // and cannot be turned off with CSS
2091
2092         html += QLatin1Char('\"');
2093
2094         const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2095         emitBackgroundAttribute(fmt);
2096
2097     } else {
2098         defaultCharFormat = QTextCharFormat();
2099     }
2100     html += QLatin1Char('>');
2101
2102     QTextFrameFormat rootFmt = doc->rootFrame()->frameFormat();
2103     rootFmt.clearProperty(QTextFormat::BackgroundBrush);
2104
2105     QTextFrameFormat defaultFmt;
2106     defaultFmt.setMargin(doc->documentMargin());
2107
2108     if (rootFmt == defaultFmt)
2109         emitFrame(doc->rootFrame()->begin());
2110     else
2111         emitTextFrame(doc->rootFrame());
2112
2113     html += QLatin1String("</body></html>");
2114     return html;
2115 }
2116
2117 void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &value)
2118 {
2119     html += QLatin1Char(' ');
2120     html += QLatin1String(attribute);
2121     html += QLatin1String("=\"");
2122     html += Qt::escape(value);
2123     html += QLatin1Char('"');
2124 }
2125
2126 bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
2127 {
2128     bool attributesEmitted = false;
2129
2130     {
2131         const QString family = format.fontFamily();
2132         if (!family.isEmpty() && family != defaultCharFormat.fontFamily()) {
2133             emitFontFamily(family);
2134             attributesEmitted = true;
2135         }
2136     }
2137
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"
2147         };
2148         const char *name = 0;
2149         const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1;
2150         if (idx >= 0 && idx <= 4) {
2151             name = sizeNames[idx];
2152         }
2153         if (name) {
2154             html += QLatin1String(" font-size:");
2155             html += QLatin1String(name);
2156             html += QLatin1Char(';');
2157             attributesEmitted = true;
2158         }
2159     } else if (format.hasProperty(QTextFormat::FontPixelSize)) {
2160         html += QLatin1String(" font-size:");
2161         html += QString::number(format.intProperty(QTextFormat::FontPixelSize));
2162         html += QLatin1String("px;");
2163     }
2164
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;
2171     }
2172
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;
2179     }
2180
2181     QLatin1String decorationTag(" text-decoration:");
2182     html += decorationTag;
2183     bool hasDecoration = false;
2184     bool atLeastOneDecorationSet = false;
2185
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;
2192         }
2193     }
2194
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;
2201         }
2202     }
2203
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;
2210         }
2211     }
2212
2213     if (hasDecoration) {
2214         if (!atLeastOneDecorationSet)
2215             html += QLatin1String("none");
2216         html += QLatin1Char(';');
2217         attributesEmitted = true;
2218     } else {
2219         html.chop(qstrlen(decorationTag.latin1()));
2220     }
2221
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;
2228     }
2229
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;
2236     }
2237
2238     if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()
2239         && format.verticalAlignment() != QTextCharFormat::AlignNormal)
2240     {
2241         html += QLatin1String(" vertical-align:");
2242
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");
2254
2255         html += QLatin1Char(';');
2256         attributesEmitted = true;
2257     }
2258
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;
2268     }
2269
2270     if (format.fontWordSpacing() != 0.0) {
2271         html += QLatin1String(" word-spacing:");
2272         html += QString::number(format.fontWordSpacing());
2273         html += QLatin1String("px;");
2274         attributesEmitted = true;
2275     }
2276
2277     return attributesEmitted;
2278 }
2279
2280 void QTextHtmlExporter::emitTextLength(const char *attribute, const QTextLength &length)
2281 {
2282     if (length.type() == QTextLength::VariableLength) // default
2283         return;
2284
2285     html += QLatin1Char(' ');
2286     html += QLatin1String(attribute);
2287     html += QLatin1String("=\"");
2288     html += QString::number(length.rawValue());
2289
2290     if (length.type() == QTextLength::PercentageLength)
2291         html += QLatin1String("%\"");
2292     else
2293         html += QLatin1Char('\"');
2294 }
2295
2296 void QTextHtmlExporter::emitAlignment(Qt::Alignment align)
2297 {
2298     if (align & Qt::AlignLeft)
2299         return;
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\"");
2306 }
2307
2308 void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
2309 {
2310     if (pos == QTextFrameFormat::InFlow)
2311         return;
2312
2313     if (mode == EmitStyleTag)
2314         html += QLatin1String(" style=\"float:");
2315     else
2316         html += QLatin1String(" float:");
2317
2318     if (pos == QTextFrameFormat::FloatLeft)
2319         html += QLatin1String(" left;");
2320     else if (pos == QTextFrameFormat::FloatRight)
2321         html += QLatin1String(" right;");
2322     else
2323         Q_ASSERT_X(0, "QTextHtmlExporter::emitFloatStyle()", "pos should be a valid enum type");
2324
2325     if (mode == EmitStyleTag)
2326         html += QLatin1Char('\"');
2327 }
2328
2329 void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style)
2330 {
2331     Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
2332
2333     html += QLatin1String(" border-style:");
2334
2335     switch (style) {
2336     case QTextFrameFormat::BorderStyle_None:
2337         html += QLatin1String("none");
2338         break;
2339     case QTextFrameFormat::BorderStyle_Dotted:
2340         html += QLatin1String("dotted");
2341         break;
2342     case QTextFrameFormat::BorderStyle_Dashed:
2343         html += QLatin1String("dashed");
2344         break;
2345     case QTextFrameFormat::BorderStyle_Solid:
2346         html += QLatin1String("solid");
2347         break;
2348     case QTextFrameFormat::BorderStyle_Double:
2349         html += QLatin1String("double");
2350         break;
2351     case QTextFrameFormat::BorderStyle_DotDash:
2352         html += QLatin1String("dot-dash");
2353         break;
2354     case QTextFrameFormat::BorderStyle_DotDotDash:
2355         html += QLatin1String("dot-dot-dash");
2356         break;
2357     case QTextFrameFormat::BorderStyle_Groove:
2358         html += QLatin1String("groove");
2359         break;
2360     case QTextFrameFormat::BorderStyle_Ridge:
2361         html += QLatin1String("ridge");
2362         break;
2363     case QTextFrameFormat::BorderStyle_Inset:
2364         html += QLatin1String("inset");
2365         break;
2366     case QTextFrameFormat::BorderStyle_Outset:
2367         html += QLatin1String("outset");
2368         break;
2369     default:
2370         Q_ASSERT(false);
2371         break;
2372     };
2373
2374     html += QLatin1Char(';');
2375 }
2376
2377 void QTextHtmlExporter::emitPageBreakPolicy(QTextFormat::PageBreakFlags policy)
2378 {
2379     if (policy & QTextFormat::PageBreak_AlwaysBefore)
2380         html += QLatin1String(" page-break-before:always;");
2381
2382     if (policy & QTextFormat::PageBreak_AlwaysAfter)
2383         html += QLatin1String(" page-break-after:always;");
2384 }
2385
2386 void QTextHtmlExporter::emitFontFamily(const QString &family)
2387 {
2388     html += QLatin1String(" font-family:");
2389
2390     QLatin1String quote("\'");
2391     if (family.contains(QLatin1Char('\'')))
2392         quote = QLatin1String("&quot;");
2393
2394     html += quote;
2395     html += Qt::escape(family);
2396     html += quote;
2397     html += QLatin1Char(';');
2398 }
2399
2400 void QTextHtmlExporter::emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right)
2401 {
2402     html += QLatin1String(" margin-top:");
2403     html += top;
2404     html += QLatin1String("px;");
2405
2406     html += QLatin1String(" margin-bottom:");
2407     html += bottom;
2408     html += QLatin1String("px;");
2409
2410     html += QLatin1String(" margin-left:");
2411     html += left;
2412     html += QLatin1String("px;");
2413
2414     html += QLatin1String(" margin-right:");
2415     html += right;
2416     html += QLatin1String("px;");
2417 }
2418
2419 void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
2420 {
2421     const QTextCharFormat format = fragment.charFormat();
2422
2423     bool closeAnchor = false;
2424
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>");
2431         }
2432         const QString href = format.anchorHref();
2433         if (!href.isEmpty()) {
2434             html += QLatin1String("<a href=\"");
2435             html += Qt::escape(href);
2436             html += QLatin1String("\">");
2437             closeAnchor = true;
2438         }
2439     }
2440
2441     QString txt = fragment.text();
2442     const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
2443     const bool isImage = isObject && format.isImageFormat();
2444
2445     QLatin1String styleTag("<span style=\"");
2446     html += styleTag;
2447
2448     bool attributesEmitted = false;
2449     if (!isImage)
2450         attributesEmitted = emitCharFormatStyle(format);
2451     if (attributesEmitted)
2452         html += QLatin1String("\">");
2453     else
2454         html.chop(qstrlen(styleTag.latin1()));
2455
2456     if (isObject) {
2457         for (int i = 0; isImage && i < txt.length(); ++i) {
2458             QTextImageFormat imgFmt = format.toImageFormat();
2459
2460             html += QLatin1String("<img");
2461
2462             if (imgFmt.hasProperty(QTextFormat::ImageName))
2463                 emitAttribute("src", imgFmt.name());
2464
2465             if (imgFmt.hasProperty(QTextFormat::ImageWidth))
2466                 emitAttribute("width", QString::number(imgFmt.width()));
2467
2468             if (imgFmt.hasProperty(QTextFormat::ImageHeight))
2469                 emitAttribute("height", QString::number(imgFmt.height()));
2470
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;\"");
2475
2476             if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
2477                 emitFloatStyle(imageFrame->frameFormat().position());
2478
2479             html += QLatin1String(" />");
2480         }
2481     } else {
2482         Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
2483
2484         txt = Qt::escape(txt);
2485
2486         // split for [\n{LineSeparator}]
2487         QString forcedLineBreakRegExp = QString::fromLatin1("[\\na]");
2488         forcedLineBreakRegExp[3] = QChar::LineSeparator;
2489
2490         const QStringList lines = txt.split(QRegExp(forcedLineBreakRegExp));
2491         for (int i = 0; i < lines.count(); ++i) {
2492             if (i > 0)
2493                 html += QLatin1String("<br />"); // space on purpose for compatibility with Netscape, Lynx & Co.
2494             html += lines.at(i);
2495         }
2496     }
2497
2498     if (attributesEmitted)
2499         html += QLatin1String("</span>");
2500
2501     if (closeAnchor)
2502         html += QLatin1String("</a>");
2503 }
2504
2505 static bool isOrderedList(int style)
2506 {
2507     return style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
2508            || style == QTextListFormat::ListUpperAlpha
2509            || style == QTextListFormat::ListUpperRoman
2510            || style == QTextListFormat::ListLowerRoman
2511            ;
2512 }
2513
2514 void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
2515 {
2516     QTextBlockFormat format = block.blockFormat();
2517     emitAlignment(format.alignment());
2518
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'");
2523
2524     QLatin1String style(" style=\"");
2525     html += style;
2526
2527     const bool emptyBlock = block.begin().atEnd();
2528     if (emptyBlock) {
2529         html += QLatin1String("-qt-paragraph-type:empty;");
2530     }
2531
2532     emitMargins(QString::number(format.topMargin()),
2533                 QString::number(format.bottomMargin()),
2534                 QString::number(format.leftMargin()),
2535                 QString::number(format.rightMargin()));
2536
2537     html += QLatin1String(" -qt-block-indent:");
2538     html += QString::number(format.indent());
2539     html += QLatin1Char(';');
2540
2541     html += QLatin1String(" text-indent:");
2542     html += QString::number(format.textIndent());
2543     html += QLatin1String("px;");
2544
2545     if (block.userState() != -1) {
2546         html += QLatin1String(" -qt-user-state:");
2547         html += QString::number(block.userState());
2548         html += QLatin1Char(';');
2549     }
2550
2551     emitPageBreakPolicy(format.pageBreakPolicy());
2552
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();
2557     }
2558
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));
2564     }
2565
2566     if (!diff.properties().isEmpty())
2567         emitCharFormatStyle(diff);
2568
2569     html += QLatin1Char('"');
2570
2571 }
2572
2573 void QTextHtmlExporter::emitBlock(const QTextBlock &block)
2574 {
2575     if (block.begin().atEnd()) {
2576         // ### HACK, remove once QTextFrame::Iterator is fixed
2577         int p = block.position();
2578         if (p > 0)
2579             --p;
2580         QTextDocumentPrivate::FragmentIterator frag = doc->docHandle()->find(p);
2581         QChar ch = doc->docHandle()->buffer().at(frag->stringPosition);
2582         if (ch == QTextBeginningOfFrame
2583             || ch == QTextEndOfFrame)
2584             return;
2585     }
2586
2587     html += QLatin1Char('\n');
2588
2589     // save and later restore, in case we 'change' the default format by
2590     // emitting block char format information
2591     QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2592
2593     QTextList *list = block.textList();
2594     if (list) {
2595         if (list->itemNumber(block) == 0) { // first item? emit <ul> or appropriate
2596             const QTextListFormat format = list->format();
2597             const int style = format.style();
2598             switch (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
2608             }
2609
2610             QString styleString = QString::fromLatin1("margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;");
2611
2612             if (format.hasProperty(QTextFormat::ListIndent)) {
2613                 styleString += QLatin1String(" -qt-list-indent: ");
2614                 styleString += QString::number(format.indent());
2615                 styleString += QLatin1Char(';');
2616             }
2617
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(';');
2627             }
2628
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(';');
2639                 }
2640             }
2641
2642             html += QLatin1String(" style=\"");
2643             html += styleString;
2644             html += QLatin1String("\">");
2645         }
2646
2647         html += QLatin1String("<li");
2648
2649         const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
2650         if (!blockFmt.properties().isEmpty()) {
2651             html += QLatin1String(" style=\"");
2652             emitCharFormatStyle(blockFmt);
2653             html += QLatin1Char('\"');
2654
2655             defaultCharFormat.merge(block.charFormat());
2656         }
2657     }
2658
2659     const QTextBlockFormat blockFormat = block.blockFormat();
2660     if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
2661         html += QLatin1String("<hr");
2662
2663         QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
2664         if (width.type() != QTextLength::VariableLength)
2665             emitTextLength("width", width);
2666         else
2667             html += QLatin1Char(' ');
2668
2669         html += QLatin1String("/>");
2670         return;
2671     }
2672
2673     const bool pre = blockFormat.nonBreakableLines();
2674     if (pre) {
2675         if (list)
2676             html += QLatin1Char('>');
2677         html += QLatin1String("<pre");
2678     } else if (!list) {
2679         html += QLatin1String("<p");
2680     }
2681
2682     emitBlockAttributes(block);
2683
2684     html += QLatin1Char('>');
2685     if (block.begin().atEnd())
2686         html += QLatin1String("<br />");
2687
2688     QTextBlock::Iterator it = block.begin();
2689     if (fragmentMarkers && !it.atEnd() && block == doc->begin())
2690         html += QLatin1String("<!--StartFragment-->");
2691
2692     for (; !it.atEnd(); ++it)
2693         emitFragment(it.fragment());
2694
2695     if (fragmentMarkers && block.position() + block.length() == doc->docHandle()->length())
2696         html += QLatin1String("<!--EndFragment-->");
2697
2698     if (pre)
2699         html += QLatin1String("</pre>");
2700     else if (list)
2701         html += QLatin1String("</li>");
2702     else
2703         html += QLatin1String("</p>");
2704
2705     if (list) {
2706         if (list->itemNumber(block) == list->count() - 1) { // last item? close list
2707             if (isOrderedList(list->format().style()))
2708                 html += QLatin1String("</ol>");
2709             else
2710                 html += QLatin1String("</ul>");
2711         }
2712     }
2713
2714     defaultCharFormat = oldDefaultCharFormat;
2715 }
2716
2717 extern bool qHasPixmapTexture(const QBrush& brush);
2718
2719 QString QTextHtmlExporter::findUrlForImage(const QTextDocument *doc, qint64 cacheKey, bool isPixmap)
2720 {
2721     QString url;
2722     if (!doc)
2723         return url;
2724
2725     if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent()))
2726         return findUrlForImage(parent, cacheKey, isPixmap);
2727
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) {
2732
2733             const QVariant &v = it.value();
2734             if (v.type() == QVariant::Image && !isPixmap) {
2735                 if (qvariant_cast<QImage>(v).cacheKey() == cacheKey)
2736                     break;
2737             }
2738
2739             if (v.type() == QVariant::Pixmap && isPixmap) {
2740                 if (qvariant_cast<QPixmap>(v).cacheKey() == cacheKey)
2741                     break;
2742             }
2743         }
2744
2745         if (it != priv->cachedResources.constEnd())
2746             url = it.key().toString();
2747     }
2748
2749     return url;
2750 }
2751
2752 void QTextDocumentPrivate::mergeCachedResources(const QTextDocumentPrivate *priv)
2753 {
2754     if (!priv)
2755         return;
2756
2757     cachedResources.unite(priv->cachedResources);
2758 }
2759
2760 void QTextHtmlExporter::emitBackgroundAttribute(const QTextFormat &format)
2761 {
2762     if (format.hasProperty(QTextFormat::BackgroundImageUrl)) {
2763         QString url = format.property(QTextFormat::BackgroundImageUrl).toString();
2764         emitAttribute("background", url);
2765     } else {
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();
2772
2773             const QString url = findUrlForImage(doc, cacheKey, isPixmap);
2774
2775             if (!url.isEmpty())
2776                 emitAttribute("background", url);
2777         }
2778     }
2779 }
2780
2781 void QTextHtmlExporter::emitTable(const QTextTable *table)
2782 {
2783     QTextTableFormat format = table->format();
2784
2785     html += QLatin1String("\n<table");
2786
2787     if (format.hasProperty(QTextFormat::FrameBorder))
2788         emitAttribute("border", QString::number(format.border()));
2789
2790     emitFrameStyle(format, TableFrame);
2791
2792     emitAlignment(format.alignment());
2793     emitTextLength("width", format.width());
2794
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()));
2799
2800     emitBackgroundAttribute(format);
2801
2802     html += QLatin1Char('>');
2803
2804     const int rows = table->rows();
2805     const int columns = table->columns();
2806
2807     QVector<QTextLength> columnWidths = format.columnWidthConstraints();
2808     if (columnWidths.isEmpty()) {
2809         columnWidths.resize(columns);
2810         columnWidths.fill(QTextLength());
2811     }
2812     Q_ASSERT(columnWidths.count() == columns);
2813
2814     QVarLengthArray<bool> widthEmittedForColumn(columns);
2815     for (int i = 0; i < columns; ++i)
2816         widthEmittedForColumn[i] = false;
2817
2818     const int headerRowCount = qMin(format.headerRowCount(), rows);
2819     if (headerRowCount > 0)
2820         html += QLatin1String("<thead>");
2821
2822     for (int row = 0; row < rows; ++row) {
2823         html += QLatin1String("\n<tr>");
2824
2825         for (int col = 0; col < columns; ++col) {
2826             const QTextTableCell cell = table->cellAt(row, col);
2827
2828             // for col/rowspans
2829             if (cell.row() != row)
2830                 continue;
2831
2832             if (cell.column() != col)
2833                 continue;
2834
2835             html += QLatin1String("\n<td");
2836
2837             if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
2838                 emitTextLength("width", columnWidths.at(col));
2839                 widthEmittedForColumn[col] = true;
2840             }
2841
2842             if (cell.columnSpan() > 1)
2843                 emitAttribute("colspan", QString::number(cell.columnSpan()));
2844
2845             if (cell.rowSpan() > 1)
2846                 emitAttribute("rowspan", QString::number(cell.rowSpan()));
2847
2848             const QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
2849             emitBackgroundAttribute(cellFormat);
2850
2851             QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2852
2853             QTextCharFormat::VerticalAlignment valign = cellFormat.verticalAlignment();
2854
2855             QString styleString;
2856             if (valign >= QTextCharFormat::AlignMiddle && valign <= QTextCharFormat::AlignBottom) {
2857                 styleString += QLatin1String(" vertical-align:");
2858                 switch (valign) {
2859                 case QTextCharFormat::AlignMiddle:
2860                     styleString += QLatin1String("middle");
2861                     break;
2862                 case QTextCharFormat::AlignTop:
2863                     styleString += QLatin1String("top");
2864                     break;
2865                 case QTextCharFormat::AlignBottom:
2866                     styleString += QLatin1String("bottom");
2867                     break;
2868                 default:
2869                     break;
2870                 }
2871                 styleString += QLatin1Char(';');
2872
2873                 QTextCharFormat temp;
2874                 temp.setVerticalAlignment(valign);
2875                 defaultCharFormat.merge(temp);
2876             }
2877
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(';');
2886
2887             if (!styleString.isEmpty())
2888                 html += QLatin1String(" style=\"") + styleString + QLatin1Char('\"');
2889
2890             html += QLatin1Char('>');
2891
2892             emitFrame(cell.begin());
2893
2894             html += QLatin1String("</td>");
2895
2896             defaultCharFormat = oldDefaultCharFormat;
2897         }
2898
2899         html += QLatin1String("</tr>");
2900         if (headerRowCount > 0 && row == headerRowCount - 1)
2901             html += QLatin1String("</thead>");
2902     }
2903
2904     html += QLatin1String("</table>");
2905 }
2906
2907 void QTextHtmlExporter::emitFrame(QTextFrame::Iterator frameIt)
2908 {
2909     if (!frameIt.atEnd()) {
2910         QTextFrame::Iterator next = frameIt;
2911         ++next;
2912         if (next.atEnd()
2913             && frameIt.currentFrame() == 0
2914             && frameIt.parentFrame() != doc->rootFrame()
2915             && frameIt.currentBlock().begin().atEnd())
2916             return;
2917     }
2918
2919     for (QTextFrame::Iterator it = frameIt;
2920          !it.atEnd(); ++it) {
2921         if (QTextFrame *f = it.currentFrame()) {
2922             if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
2923                 emitTable(table);
2924             } else {
2925                 emitTextFrame(f);
2926             }
2927         } else if (it.currentBlock().isValid()) {
2928             emitBlock(it.currentBlock());
2929         }
2930     }
2931 }
2932
2933 void QTextHtmlExporter::emitTextFrame(const QTextFrame *f)
2934 {
2935     FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
2936
2937     html += QLatin1String("\n<table");
2938     QTextFrameFormat format = f->frameFormat();
2939
2940     if (format.hasProperty(QTextFormat::FrameBorder))
2941         emitAttribute("border", QString::number(format.border()));
2942
2943     emitFrameStyle(format, frameType);
2944
2945     emitTextLength("width", format.width());
2946     emitTextLength("height", format.height());
2947
2948     // root frame's bcolor goes in the <body> tag
2949     if (frameType != RootFrame)
2950         emitBackgroundAttribute(format);
2951
2952     html += QLatin1Char('>');
2953     html += QLatin1String("\n<tr>\n<td style=\"border: none;\">");
2954     emitFrame(f->begin());
2955     html += QLatin1String("</td></tr></table>");
2956 }
2957
2958 void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType frameType)
2959 {
2960     QLatin1String styleAttribute(" style=\"");
2961     html += styleAttribute;
2962     const int originalHtmlLength = html.length();
2963
2964     if (frameType == TextFrame)
2965         html += QLatin1String("-qt-table-type: frame;");
2966     else if (frameType == RootFrame)
2967         html += QLatin1String("-qt-table-type: root;");
2968
2969     const QTextFrameFormat defaultFormat;
2970
2971     emitFloatStyle(format.position(), OmitStyleTag);
2972     emitPageBreakPolicy(format.pageBreakPolicy());
2973
2974     if (format.borderBrush() != defaultFormat.borderBrush()) {
2975         html += QLatin1String(" border-color:");
2976         html += format.borderBrush().color().name();
2977         html += QLatin1Char(';');
2978     }
2979
2980     if (format.borderStyle() != defaultFormat.borderStyle())
2981         emitBorderStyle(format.borderStyle());
2982
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()));
2992
2993     if (html.length() == originalHtmlLength) // nothing emitted?
2994         html.chop(qstrlen(styleAttribute.latin1()));
2995     else
2996         html += QLatin1Char('\"');
2997 }
2998
2999 /*!
3000     Returns a string containing an HTML representation of the document.
3001
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
3006
3007     If no encoding is specified then no such meta information is generated.
3008
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.
3012
3013     \sa {Supported HTML Subset}
3014 */
3015 #ifndef QT_NO_TEXTHTMLPARSER
3016 QString QTextDocument::toHtml(const QByteArray &encoding) const
3017 {
3018     return QTextHtmlExporter(this).toHtml(encoding);
3019 }
3020 #endif // QT_NO_TEXTHTMLPARSER
3021
3022 /*!
3023     Returns a vector of text formats for all the formats used in the document.
3024 */
3025 QVector<QTextFormat> QTextDocument::allFormats() const
3026 {
3027     Q_D(const QTextDocument);
3028     return d->formatCollection()->formats;
3029 }
3030
3031
3032 /*!
3033   \internal
3034
3035   So that not all classes have to be friends of each other...
3036 */
3037 QTextDocumentPrivate *QTextDocument::docHandle() const
3038 {
3039     Q_D(const QTextDocument);
3040     return const_cast<QTextDocumentPrivate *>(d);
3041 }
3042
3043 /*!
3044     \since 4.4
3045     \fn QTextDocument::undoCommandAdded()
3046
3047     This signal is emitted  every time a new level of undo is added to the QTextDocument.
3048 */
3049
3050 QT_END_NAMESPACE