Merge remote branch 'gerrit/master' into refactor
[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 "qprinter.h"
59 #include <qfile.h>
60 #include <qfileinfo.h>
61 #include <qdir.h>
62 #include "qfont_p.h"
63 #include "private/qdataurl_p.h"
64
65 #include "qtextdocument_p.h"
66 #include <private/qprinter_p.h>
67 #include <private/qabstracttextdocumentlayout_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 printer. The QPrinter 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 QPrinter's paperRect(). By default
1708     a 2 cm margin is set around the document contents. In addition the current page
1709     number is printed at the bottom of each page.
1710
1711     Note that QPrinter::Selection is not supported as print range with this function since
1712     the selection is a property of QTextCursor. If you have a QTextEdit associated with
1713     your QTextDocument then you can use QTextEdit's print() function because QTextEdit has
1714     access to the user's selection.
1715
1716     \sa QTextEdit::print()
1717 */
1718
1719 void QTextDocument::print(QPrinter *printer) const
1720 {
1721     Q_D(const QTextDocument);
1722
1723     if (!printer || !printer->isValid())
1724         return;
1725
1726     if (!d->title.isEmpty())
1727         printer->setDocName(d->title);
1728
1729     bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
1730                              && d->pageSize.height() != INT_MAX;
1731
1732     if (!documentPaginated && !printer->fullPage() && !printer->d_func()->hasCustomPageMargins)
1733         printer->setPageMargins(23.53, 23.53, 23.53, 23.53, QPrinter::Millimeter);
1734
1735     QPainter p(printer);
1736
1737     // Check that there is a valid device to print to.
1738     if (!p.isActive())
1739         return;
1740
1741     const QTextDocument *doc = this;
1742     QScopedPointer<QTextDocument> clonedDoc;
1743     (void)doc->documentLayout(); // make sure that there is a layout
1744
1745     QRectF body = QRectF(QPointF(0, 0), d->pageSize);
1746     QPointF pageNumberPos;
1747
1748     if (documentPaginated) {
1749         qreal sourceDpiX = qt_defaultDpi();
1750         qreal sourceDpiY = sourceDpiX;
1751
1752         QPaintDevice *dev = doc->documentLayout()->paintDevice();
1753         if (dev) {
1754             sourceDpiX = dev->logicalDpiX();
1755             sourceDpiY = dev->logicalDpiY();
1756         }
1757
1758         const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
1759         const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
1760
1761         // scale to dpi
1762         p.scale(dpiScaleX, dpiScaleY);
1763
1764         QSizeF scaledPageSize = d->pageSize;
1765         scaledPageSize.rwidth() *= dpiScaleX;
1766         scaledPageSize.rheight() *= dpiScaleY;
1767
1768         const QSizeF printerPageSize(printer->pageRect().size());
1769
1770         // scale to page
1771         p.scale(printerPageSize.width() / scaledPageSize.width(),
1772                 printerPageSize.height() / scaledPageSize.height());
1773     } else {
1774         doc = clone(const_cast<QTextDocument *>(this));
1775         clonedDoc.reset(const_cast<QTextDocument *>(doc));
1776
1777         for (QTextBlock srcBlock = firstBlock(), dstBlock = clonedDoc->firstBlock();
1778              srcBlock.isValid() && dstBlock.isValid();
1779              srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
1780             dstBlock.layout()->setAdditionalFormats(srcBlock.layout()->additionalFormats());
1781         }
1782
1783         QAbstractTextDocumentLayout *layout = doc->documentLayout();
1784         layout->setPaintDevice(p.device());
1785
1786         // copy the custom object handlers
1787         layout->d_func()->handlers = documentLayout()->d_func()->handlers;
1788
1789         int dpiy = p.device()->logicalDpiY();
1790         int margin = 0;
1791         if (printer->fullPage() && !printer->d_func()->hasCustomPageMargins) {
1792             // for compatibility
1793             margin = (int) ((2/2.54)*dpiy); // 2 cm margins
1794             QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
1795             fmt.setMargin(margin);
1796             doc->rootFrame()->setFrameFormat(fmt);
1797         }
1798
1799         QRectF pageRect(printer->pageRect());
1800         body = QRectF(0, 0, pageRect.width(), pageRect.height());
1801         pageNumberPos = QPointF(body.width() - margin,
1802                                 body.height() - margin
1803                                 + QFontMetrics(doc->defaultFont(), p.device()).ascent()
1804                                 + 5 * dpiy / 72.0);
1805         clonedDoc->setPageSize(body.size());
1806     }
1807
1808     int docCopies;
1809     int pageCopies;
1810     if (printer->collateCopies() == true){
1811         docCopies = 1;
1812         pageCopies = printer->supportsMultipleCopies() ? 1 : printer->copyCount();
1813     } else {
1814         docCopies = printer->supportsMultipleCopies() ? 1 : printer->copyCount();
1815         pageCopies = 1;
1816     }
1817
1818     int fromPage = printer->fromPage();
1819     int toPage = printer->toPage();
1820     bool ascending = true;
1821
1822     if (fromPage == 0 && toPage == 0) {
1823         fromPage = 1;
1824         toPage = doc->pageCount();
1825     }
1826     // paranoia check
1827     fromPage = qMax(1, fromPage);
1828     toPage = qMin(doc->pageCount(), toPage);
1829
1830     if (toPage < fromPage) {
1831         // if the user entered a page range outside the actual number
1832         // of printable pages, just return
1833         return;
1834     }
1835
1836     if (printer->pageOrder() == QPrinter::LastPageFirst) {
1837         int tmp = fromPage;
1838         fromPage = toPage;
1839         toPage = tmp;
1840         ascending = false;
1841     }
1842
1843     for (int i = 0; i < docCopies; ++i) {
1844
1845         int page = fromPage;
1846         while (true) {
1847             for (int j = 0; j < pageCopies; ++j) {
1848                 if (printer->printerState() == QPrinter::Aborted
1849                     || printer->printerState() == QPrinter::Error)
1850                     return;
1851                 printPage(page, &p, doc, body, pageNumberPos);
1852                 if (j < pageCopies - 1)
1853                     printer->newPage();
1854             }
1855
1856             if (page == toPage)
1857                 break;
1858
1859             if (ascending)
1860                 ++page;
1861             else
1862                 --page;
1863
1864             printer->newPage();
1865         }
1866
1867         if ( i < docCopies - 1)
1868             printer->newPage();
1869     }
1870 }
1871 #endif
1872
1873 /*!
1874     \enum QTextDocument::ResourceType
1875
1876     This enum describes the types of resources that can be loaded by
1877     QTextDocument's loadResource() function.
1878
1879     \value HtmlResource  The resource contains HTML.
1880     \value ImageResource The resource contains image data.
1881                          Currently supported data types are QVariant::Pixmap and
1882                          QVariant::Image. If the corresponding variant is of type
1883                          QVariant::ByteArray then Qt attempts to load the image using
1884                          QImage::loadFromData. QVariant::Icon is currently not supported.
1885                          The icon needs to be converted to one of the supported types first,
1886                          for example using QIcon::pixmap.
1887     \value StyleSheetResource The resource contains CSS.
1888     \value UserResource  The first available value for user defined
1889                          resource types.
1890
1891     \sa loadResource()
1892 */
1893
1894 /*!
1895     Returns data of the specified \a type from the resource with the
1896     given \a name.
1897
1898     This function is called by the rich text engine to request data that isn't
1899     directly stored by QTextDocument, but still associated with it. For example,
1900     images are referenced indirectly by the name attribute of a QTextImageFormat
1901     object.
1902
1903     Resources are cached internally in the document. If a resource can
1904     not be found in the cache, loadResource is called to try to load
1905     the resource. loadResource should then use addResource to add the
1906     resource to the cache.
1907
1908     \sa QTextDocument::ResourceType
1909 */
1910 QVariant QTextDocument::resource(int type, const QUrl &name) const
1911 {
1912     Q_D(const QTextDocument);
1913     QVariant r = d->resources.value(name);
1914     if (!r.isValid()) {
1915         r = d->cachedResources.value(name);
1916         if (!r.isValid())
1917             r = const_cast<QTextDocument *>(this)->loadResource(type, name);
1918     }
1919     return r;
1920 }
1921
1922 /*!
1923     Adds the resource \a resource to the resource cache, using \a
1924     type and \a name as identifiers. \a type should be a value from
1925     QTextDocument::ResourceType.
1926
1927     For example, you can add an image as a resource in order to reference it
1928     from within the document:
1929
1930     \snippet snippets/textdocument-resources/main.cpp Adding a resource
1931
1932     The image can be inserted into the document using the QTextCursor API:
1933
1934     \snippet snippets/textdocument-resources/main.cpp Inserting an image with a cursor
1935
1936     Alternatively, you can insert images using the HTML \c img tag:
1937
1938     \snippet snippets/textdocument-resources/main.cpp Inserting an image using HTML
1939 */
1940 void QTextDocument::addResource(int type, const QUrl &name, const QVariant &resource)
1941 {
1942     Q_UNUSED(type);
1943     Q_D(QTextDocument);
1944     d->resources.insert(name, resource);
1945 }
1946
1947 /*!
1948     Loads data of the specified \a type from the resource with the
1949     given \a name.
1950
1951     This function is called by the rich text engine to request data that isn't
1952     directly stored by QTextDocument, but still associated with it. For example,
1953     images are referenced indirectly by the name attribute of a QTextImageFormat
1954     object.
1955
1956     When called by Qt, \a type is one of the values of
1957     QTextDocument::ResourceType.
1958
1959     If the QTextDocument is a child object of a QTextEdit, QTextBrowser,
1960     or a QTextDocument itself then the default implementation tries
1961     to retrieve the data from the parent.
1962 */
1963 QVariant QTextDocument::loadResource(int type, const QUrl &name)
1964 {
1965     Q_D(QTextDocument);
1966     QVariant r;
1967
1968     QTextDocument *doc = qobject_cast<QTextDocument *>(parent());
1969     if (doc) {
1970         r = doc->loadResource(type, name);
1971     }
1972 #if 0
1973     // ### Qt5: reenable
1974 #ifndef QT_NO_TEXTEDIT
1975     else if (QTextEdit *edit = qobject_cast<QTextEdit *>(parent())) {
1976         QUrl resolvedName = edit->d_func()->resolveUrl(name);
1977         r = edit->loadResource(type, resolvedName);
1978     }
1979 #endif
1980 #ifndef QT_NO_TEXTCONTROL
1981     else if (QWidgetTextControl *control = qobject_cast<QWidgetTextControl *>(parent())) {
1982         r = control->loadResource(type, name);
1983     }
1984 #endif
1985 #endif
1986
1987     // handle data: URLs
1988     if (r.isNull() && name.scheme().compare(QLatin1String("data"), Qt::CaseInsensitive) == 0)
1989         r = qDecodeDataUrl(name).second;
1990
1991     // if resource was not loaded try to load it here
1992     if (!doc && r.isNull() && name.isRelative()) {
1993         QUrl currentURL = d->url;
1994         QUrl resourceUrl = name;
1995
1996         // For the second case QUrl can merge "#someanchor" with "foo.html"
1997         // correctly to "foo.html#someanchor"
1998         if (!(currentURL.isRelative()
1999               || (currentURL.scheme() == QLatin1String("file")
2000                   && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
2001             || (name.hasFragment() && name.path().isEmpty())) {
2002             resourceUrl =  currentURL.resolved(name);
2003         } else {
2004             // this is our last resort when current url and new url are both relative
2005             // we try to resolve against the current working directory in the local
2006             // file system.
2007             QFileInfo fi(currentURL.toLocalFile());
2008             if (fi.exists()) {
2009                 resourceUrl =
2010                     QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
2011             } else if (currentURL.isEmpty()) {
2012                 resourceUrl.setScheme(QLatin1String("file"));
2013             }
2014         }
2015
2016         QString s = resourceUrl.toLocalFile();
2017         QFile f(s);
2018         if (!s.isEmpty() && f.open(QFile::ReadOnly)) {
2019             r = f.readAll();
2020             f.close();
2021         }
2022     }
2023
2024     if (!r.isNull()) {
2025         if (type == ImageResource && r.type() == QVariant::ByteArray) {
2026             if (qApp->thread() != QThread::currentThread()) {
2027                 // must use images in non-GUI threads
2028                 QImage image;
2029                 image.loadFromData(r.toByteArray());
2030                 if (!image.isNull())
2031                     r = image;
2032             } else {
2033                 QPixmap pm;
2034                 pm.loadFromData(r.toByteArray());
2035                 if (!pm.isNull())
2036                     r = pm;
2037             }
2038         }
2039         d->cachedResources.insert(name, r);
2040     }
2041     return r;
2042 }
2043
2044 static QTextFormat formatDifference(const QTextFormat &from, const QTextFormat &to)
2045 {
2046     QTextFormat diff = to;
2047
2048     const QMap<int, QVariant> props = to.properties();
2049     for (QMap<int, QVariant>::ConstIterator it = props.begin(), end = props.end();
2050          it != end; ++it)
2051         if (it.value() == from.property(it.key()))
2052             diff.clearProperty(it.key());
2053
2054     return diff;
2055 }
2056
2057 QTextHtmlExporter::QTextHtmlExporter(const QTextDocument *_doc)
2058     : doc(_doc), fragmentMarkers(false)
2059 {
2060     const QFont defaultFont = doc->defaultFont();
2061     defaultCharFormat.setFont(defaultFont);
2062     // don't export those for the default font since we cannot turn them off with CSS
2063     defaultCharFormat.clearProperty(QTextFormat::FontUnderline);
2064     defaultCharFormat.clearProperty(QTextFormat::FontOverline);
2065     defaultCharFormat.clearProperty(QTextFormat::FontStrikeOut);
2066     defaultCharFormat.clearProperty(QTextFormat::TextUnderlineStyle);
2067 }
2068
2069 /*!
2070     Returns the document in HTML format. The conversion may not be
2071     perfect, especially for complex documents, due to the limitations
2072     of HTML.
2073 */
2074 QString QTextHtmlExporter::toHtml(const QByteArray &encoding, ExportMode mode)
2075 {
2076     html = QLatin1String("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
2077             "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
2078             "<html><head><meta name=\"qrichtext\" content=\"1\" />");
2079     html.reserve(doc->docHandle()->length());
2080
2081     fragmentMarkers = (mode == ExportFragment);
2082
2083     if (!encoding.isEmpty())
2084         html += QString::fromLatin1("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\" />").arg(QString::fromAscii(encoding));
2085
2086     QString title  = doc->metaInformation(QTextDocument::DocumentTitle);
2087     if (!title.isEmpty())
2088         html += QString::fromLatin1("<title>") + title + QString::fromLatin1("</title>");
2089     html += QLatin1String("<style type=\"text/css\">\n");
2090     html += QLatin1String("p, li { white-space: pre-wrap; }\n");
2091     html += QLatin1String("</style>");
2092     html += QLatin1String("</head><body");
2093
2094     if (mode == ExportEntireDocument) {
2095         html += QLatin1String(" style=\"");
2096
2097         emitFontFamily(defaultCharFormat.fontFamily());
2098
2099         if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) {
2100             html += QLatin1String(" font-size:");
2101             html += QString::number(defaultCharFormat.fontPointSize());
2102             html += QLatin1String("pt;");
2103         } else if (defaultCharFormat.hasProperty(QTextFormat::FontPixelSize)) {
2104             html += QLatin1String(" font-size:");
2105             html += QString::number(defaultCharFormat.intProperty(QTextFormat::FontPixelSize));
2106             html += QLatin1String("px;");
2107         }
2108
2109         html += QLatin1String(" font-weight:");
2110         html += QString::number(defaultCharFormat.fontWeight() * 8);
2111         html += QLatin1Char(';');
2112
2113         html += QLatin1String(" font-style:");
2114         html += (defaultCharFormat.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2115         html += QLatin1Char(';');
2116
2117         // do not set text-decoration on the default font since those values are /always/ propagated
2118         // and cannot be turned off with CSS
2119
2120         html += QLatin1Char('\"');
2121
2122         const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2123         emitBackgroundAttribute(fmt);
2124
2125     } else {
2126         defaultCharFormat = QTextCharFormat();
2127     }
2128     html += QLatin1Char('>');
2129
2130     QTextFrameFormat rootFmt = doc->rootFrame()->frameFormat();
2131     rootFmt.clearProperty(QTextFormat::BackgroundBrush);
2132
2133     QTextFrameFormat defaultFmt;
2134     defaultFmt.setMargin(doc->documentMargin());
2135
2136     if (rootFmt == defaultFmt)
2137         emitFrame(doc->rootFrame()->begin());
2138     else
2139         emitTextFrame(doc->rootFrame());
2140
2141     html += QLatin1String("</body></html>");
2142     return html;
2143 }
2144
2145 void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &value)
2146 {
2147     html += QLatin1Char(' ');
2148     html += QLatin1String(attribute);
2149     html += QLatin1String("=\"");
2150     html += Qt::escape(value);
2151     html += QLatin1Char('"');
2152 }
2153
2154 bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
2155 {
2156     bool attributesEmitted = false;
2157
2158     {
2159         const QString family = format.fontFamily();
2160         if (!family.isEmpty() && family != defaultCharFormat.fontFamily()) {
2161             emitFontFamily(family);
2162             attributesEmitted = true;
2163         }
2164     }
2165
2166     if (format.hasProperty(QTextFormat::FontPointSize)
2167         && format.fontPointSize() != defaultCharFormat.fontPointSize()) {
2168         html += QLatin1String(" font-size:");
2169         html += QString::number(format.fontPointSize());
2170         html += QLatin1String("pt;");
2171         attributesEmitted = true;
2172     } else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) {
2173         static const char * const sizeNames[] = {
2174             "small", "medium", "large", "x-large", "xx-large"
2175         };
2176         const char *name = 0;
2177         const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1;
2178         if (idx >= 0 && idx <= 4) {
2179             name = sizeNames[idx];
2180         }
2181         if (name) {
2182             html += QLatin1String(" font-size:");
2183             html += QLatin1String(name);
2184             html += QLatin1Char(';');
2185             attributesEmitted = true;
2186         }
2187     } else if (format.hasProperty(QTextFormat::FontPixelSize)) {
2188         html += QLatin1String(" font-size:");
2189         html += QString::number(format.intProperty(QTextFormat::FontPixelSize));
2190         html += QLatin1String("px;");
2191     }
2192
2193     if (format.hasProperty(QTextFormat::FontWeight)
2194         && format.fontWeight() != defaultCharFormat.fontWeight()) {
2195         html += QLatin1String(" font-weight:");
2196         html += QString::number(format.fontWeight() * 8);
2197         html += QLatin1Char(';');
2198         attributesEmitted = true;
2199     }
2200
2201     if (format.hasProperty(QTextFormat::FontItalic)
2202         && format.fontItalic() != defaultCharFormat.fontItalic()) {
2203         html += QLatin1String(" font-style:");
2204         html += (format.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2205         html += QLatin1Char(';');
2206         attributesEmitted = true;
2207     }
2208
2209     QLatin1String decorationTag(" text-decoration:");
2210     html += decorationTag;
2211     bool hasDecoration = false;
2212     bool atLeastOneDecorationSet = false;
2213
2214     if ((format.hasProperty(QTextFormat::FontUnderline) || format.hasProperty(QTextFormat::TextUnderlineStyle))
2215         && format.fontUnderline() != defaultCharFormat.fontUnderline()) {
2216         hasDecoration = true;
2217         if (format.fontUnderline()) {
2218             html += QLatin1String(" underline");
2219             atLeastOneDecorationSet = true;
2220         }
2221     }
2222
2223     if (format.hasProperty(QTextFormat::FontOverline)
2224         && format.fontOverline() != defaultCharFormat.fontOverline()) {
2225         hasDecoration = true;
2226         if (format.fontOverline()) {
2227             html += QLatin1String(" overline");
2228             atLeastOneDecorationSet = true;
2229         }
2230     }
2231
2232     if (format.hasProperty(QTextFormat::FontStrikeOut)
2233         && format.fontStrikeOut() != defaultCharFormat.fontStrikeOut()) {
2234         hasDecoration = true;
2235         if (format.fontStrikeOut()) {
2236             html += QLatin1String(" line-through");
2237             atLeastOneDecorationSet = true;
2238         }
2239     }
2240
2241     if (hasDecoration) {
2242         if (!atLeastOneDecorationSet)
2243             html += QLatin1String("none");
2244         html += QLatin1Char(';');
2245         attributesEmitted = true;
2246     } else {
2247         html.chop(qstrlen(decorationTag.latin1()));
2248     }
2249
2250     if (format.foreground() != defaultCharFormat.foreground()
2251         && format.foreground().style() != Qt::NoBrush) {
2252         html += QLatin1String(" color:");
2253         html += format.foreground().color().name();
2254         html += QLatin1Char(';');
2255         attributesEmitted = true;
2256     }
2257
2258     if (format.background() != defaultCharFormat.background()
2259         && format.background().style() == Qt::SolidPattern) {
2260         html += QLatin1String(" background-color:");
2261         html += format.background().color().name();
2262         html += QLatin1Char(';');
2263         attributesEmitted = true;
2264     }
2265
2266     if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()
2267         && format.verticalAlignment() != QTextCharFormat::AlignNormal)
2268     {
2269         html += QLatin1String(" vertical-align:");
2270
2271         QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2272         if (valign == QTextCharFormat::AlignSubScript)
2273             html += QLatin1String("sub");
2274         else if (valign == QTextCharFormat::AlignSuperScript)
2275             html += QLatin1String("super");
2276         else if (valign == QTextCharFormat::AlignMiddle)
2277             html += QLatin1String("middle");
2278         else if (valign == QTextCharFormat::AlignTop)
2279             html += QLatin1String("top");
2280         else if (valign == QTextCharFormat::AlignBottom)
2281             html += QLatin1String("bottom");
2282
2283         html += QLatin1Char(';');
2284         attributesEmitted = true;
2285     }
2286
2287     if (format.fontCapitalization() != QFont::MixedCase) {
2288         const QFont::Capitalization caps = format.fontCapitalization();
2289         if (caps == QFont::AllUppercase)
2290             html += QLatin1String(" text-transform:uppercase;");
2291         else if (caps == QFont::AllLowercase)
2292             html += QLatin1String(" text-transform:lowercase;");
2293         else if (caps == QFont::SmallCaps)
2294             html += QLatin1String(" font-variant:small-caps;");
2295         attributesEmitted = true;
2296     }
2297
2298     if (format.fontWordSpacing() != 0.0) {
2299         html += QLatin1String(" word-spacing:");
2300         html += QString::number(format.fontWordSpacing());
2301         html += QLatin1String("px;");
2302         attributesEmitted = true;
2303     }
2304
2305     return attributesEmitted;
2306 }
2307
2308 void QTextHtmlExporter::emitTextLength(const char *attribute, const QTextLength &length)
2309 {
2310     if (length.type() == QTextLength::VariableLength) // default
2311         return;
2312
2313     html += QLatin1Char(' ');
2314     html += QLatin1String(attribute);
2315     html += QLatin1String("=\"");
2316     html += QString::number(length.rawValue());
2317
2318     if (length.type() == QTextLength::PercentageLength)
2319         html += QLatin1String("%\"");
2320     else
2321         html += QLatin1Char('\"');
2322 }
2323
2324 void QTextHtmlExporter::emitAlignment(Qt::Alignment align)
2325 {
2326     if (align & Qt::AlignLeft)
2327         return;
2328     else if (align & Qt::AlignRight)
2329         html += QLatin1String(" align=\"right\"");
2330     else if (align & Qt::AlignHCenter)
2331         html += QLatin1String(" align=\"center\"");
2332     else if (align & Qt::AlignJustify)
2333         html += QLatin1String(" align=\"justify\"");
2334 }
2335
2336 void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
2337 {
2338     if (pos == QTextFrameFormat::InFlow)
2339         return;
2340
2341     if (mode == EmitStyleTag)
2342         html += QLatin1String(" style=\"float:");
2343     else
2344         html += QLatin1String(" float:");
2345
2346     if (pos == QTextFrameFormat::FloatLeft)
2347         html += QLatin1String(" left;");
2348     else if (pos == QTextFrameFormat::FloatRight)
2349         html += QLatin1String(" right;");
2350     else
2351         Q_ASSERT_X(0, "QTextHtmlExporter::emitFloatStyle()", "pos should be a valid enum type");
2352
2353     if (mode == EmitStyleTag)
2354         html += QLatin1Char('\"');
2355 }
2356
2357 void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style)
2358 {
2359     Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
2360
2361     html += QLatin1String(" border-style:");
2362
2363     switch (style) {
2364     case QTextFrameFormat::BorderStyle_None:
2365         html += QLatin1String("none");
2366         break;
2367     case QTextFrameFormat::BorderStyle_Dotted:
2368         html += QLatin1String("dotted");
2369         break;
2370     case QTextFrameFormat::BorderStyle_Dashed:
2371         html += QLatin1String("dashed");
2372         break;
2373     case QTextFrameFormat::BorderStyle_Solid:
2374         html += QLatin1String("solid");
2375         break;
2376     case QTextFrameFormat::BorderStyle_Double:
2377         html += QLatin1String("double");
2378         break;
2379     case QTextFrameFormat::BorderStyle_DotDash:
2380         html += QLatin1String("dot-dash");
2381         break;
2382     case QTextFrameFormat::BorderStyle_DotDotDash:
2383         html += QLatin1String("dot-dot-dash");
2384         break;
2385     case QTextFrameFormat::BorderStyle_Groove:
2386         html += QLatin1String("groove");
2387         break;
2388     case QTextFrameFormat::BorderStyle_Ridge:
2389         html += QLatin1String("ridge");
2390         break;
2391     case QTextFrameFormat::BorderStyle_Inset:
2392         html += QLatin1String("inset");
2393         break;
2394     case QTextFrameFormat::BorderStyle_Outset:
2395         html += QLatin1String("outset");
2396         break;
2397     default:
2398         Q_ASSERT(false);
2399         break;
2400     };
2401
2402     html += QLatin1Char(';');
2403 }
2404
2405 void QTextHtmlExporter::emitPageBreakPolicy(QTextFormat::PageBreakFlags policy)
2406 {
2407     if (policy & QTextFormat::PageBreak_AlwaysBefore)
2408         html += QLatin1String(" page-break-before:always;");
2409
2410     if (policy & QTextFormat::PageBreak_AlwaysAfter)
2411         html += QLatin1String(" page-break-after:always;");
2412 }
2413
2414 void QTextHtmlExporter::emitFontFamily(const QString &family)
2415 {
2416     html += QLatin1String(" font-family:");
2417
2418     QLatin1String quote("\'");
2419     if (family.contains(QLatin1Char('\'')))
2420         quote = QLatin1String("&quot;");
2421
2422     html += quote;
2423     html += Qt::escape(family);
2424     html += quote;
2425     html += QLatin1Char(';');
2426 }
2427
2428 void QTextHtmlExporter::emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right)
2429 {
2430     html += QLatin1String(" margin-top:");
2431     html += top;
2432     html += QLatin1String("px;");
2433
2434     html += QLatin1String(" margin-bottom:");
2435     html += bottom;
2436     html += QLatin1String("px;");
2437
2438     html += QLatin1String(" margin-left:");
2439     html += left;
2440     html += QLatin1String("px;");
2441
2442     html += QLatin1String(" margin-right:");
2443     html += right;
2444     html += QLatin1String("px;");
2445 }
2446
2447 void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
2448 {
2449     const QTextCharFormat format = fragment.charFormat();
2450
2451     bool closeAnchor = false;
2452
2453     if (format.isAnchor()) {
2454         const QString name = format.anchorName();
2455         if (!name.isEmpty()) {
2456             html += QLatin1String("<a name=\"");
2457             html += Qt::escape(name);
2458             html += QLatin1String("\"></a>");
2459         }
2460         const QString href = format.anchorHref();
2461         if (!href.isEmpty()) {
2462             html += QLatin1String("<a href=\"");
2463             html += Qt::escape(href);
2464             html += QLatin1String("\">");
2465             closeAnchor = true;
2466         }
2467     }
2468
2469     QString txt = fragment.text();
2470     const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
2471     const bool isImage = isObject && format.isImageFormat();
2472
2473     QLatin1String styleTag("<span style=\"");
2474     html += styleTag;
2475
2476     bool attributesEmitted = false;
2477     if (!isImage)
2478         attributesEmitted = emitCharFormatStyle(format);
2479     if (attributesEmitted)
2480         html += QLatin1String("\">");
2481     else
2482         html.chop(qstrlen(styleTag.latin1()));
2483
2484     if (isObject) {
2485         for (int i = 0; isImage && i < txt.length(); ++i) {
2486             QTextImageFormat imgFmt = format.toImageFormat();
2487
2488             html += QLatin1String("<img");
2489
2490             if (imgFmt.hasProperty(QTextFormat::ImageName))
2491                 emitAttribute("src", imgFmt.name());
2492
2493             if (imgFmt.hasProperty(QTextFormat::ImageWidth))
2494                 emitAttribute("width", QString::number(imgFmt.width()));
2495
2496             if (imgFmt.hasProperty(QTextFormat::ImageHeight))
2497                 emitAttribute("height", QString::number(imgFmt.height()));
2498
2499             if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
2500                 html += QLatin1String(" style=\"vertical-align: middle;\"");
2501             else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
2502                 html += QLatin1String(" style=\"vertical-align: top;\"");
2503
2504             if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
2505                 emitFloatStyle(imageFrame->frameFormat().position());
2506
2507             html += QLatin1String(" />");
2508         }
2509     } else {
2510         Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
2511
2512         txt = Qt::escape(txt);
2513
2514         // split for [\n{LineSeparator}]
2515         QString forcedLineBreakRegExp = QString::fromLatin1("[\\na]");
2516         forcedLineBreakRegExp[3] = QChar::LineSeparator;
2517
2518         const QStringList lines = txt.split(QRegExp(forcedLineBreakRegExp));
2519         for (int i = 0; i < lines.count(); ++i) {
2520             if (i > 0)
2521                 html += QLatin1String("<br />"); // space on purpose for compatibility with Netscape, Lynx & Co.
2522             html += lines.at(i);
2523         }
2524     }
2525
2526     if (attributesEmitted)
2527         html += QLatin1String("</span>");
2528
2529     if (closeAnchor)
2530         html += QLatin1String("</a>");
2531 }
2532
2533 static bool isOrderedList(int style)
2534 {
2535     return style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
2536            || style == QTextListFormat::ListUpperAlpha
2537            || style == QTextListFormat::ListUpperRoman
2538            || style == QTextListFormat::ListLowerRoman
2539            ;
2540 }
2541
2542 void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
2543 {
2544     QTextBlockFormat format = block.blockFormat();
2545     emitAlignment(format.alignment());
2546
2547     // assume default to not bloat the html too much
2548     // html += QLatin1String(" dir='ltr'");
2549     if (block.textDirection() == Qt::RightToLeft)
2550         html += QLatin1String(" dir='rtl'");
2551
2552     QLatin1String style(" style=\"");
2553     html += style;
2554
2555     const bool emptyBlock = block.begin().atEnd();
2556     if (emptyBlock) {
2557         html += QLatin1String("-qt-paragraph-type:empty;");
2558     }
2559
2560     emitMargins(QString::number(format.topMargin()),
2561                 QString::number(format.bottomMargin()),
2562                 QString::number(format.leftMargin()),
2563                 QString::number(format.rightMargin()));
2564
2565     html += QLatin1String(" -qt-block-indent:");
2566     html += QString::number(format.indent());
2567     html += QLatin1Char(';');
2568
2569     html += QLatin1String(" text-indent:");
2570     html += QString::number(format.textIndent());
2571     html += QLatin1String("px;");
2572
2573     if (block.userState() != -1) {
2574         html += QLatin1String(" -qt-user-state:");
2575         html += QString::number(block.userState());
2576         html += QLatin1Char(';');
2577     }
2578
2579     emitPageBreakPolicy(format.pageBreakPolicy());
2580
2581     QTextCharFormat diff;
2582     if (emptyBlock) { // only print character properties when we don't expect them to be repeated by actual text in the parag
2583         const QTextCharFormat blockCharFmt = block.charFormat();
2584         diff = formatDifference(defaultCharFormat, blockCharFmt).toCharFormat();
2585     }
2586
2587     diff.clearProperty(QTextFormat::BackgroundBrush);
2588     if (format.hasProperty(QTextFormat::BackgroundBrush)) {
2589         QBrush bg = format.background();
2590         if (bg.style() != Qt::NoBrush)
2591             diff.setProperty(QTextFormat::BackgroundBrush, format.property(QTextFormat::BackgroundBrush));
2592     }
2593
2594     if (!diff.properties().isEmpty())
2595         emitCharFormatStyle(diff);
2596
2597     html += QLatin1Char('"');
2598
2599 }
2600
2601 void QTextHtmlExporter::emitBlock(const QTextBlock &block)
2602 {
2603     if (block.begin().atEnd()) {
2604         // ### HACK, remove once QTextFrame::Iterator is fixed
2605         int p = block.position();
2606         if (p > 0)
2607             --p;
2608         QTextDocumentPrivate::FragmentIterator frag = doc->docHandle()->find(p);
2609         QChar ch = doc->docHandle()->buffer().at(frag->stringPosition);
2610         if (ch == QTextBeginningOfFrame
2611             || ch == QTextEndOfFrame)
2612             return;
2613     }
2614
2615     html += QLatin1Char('\n');
2616
2617     // save and later restore, in case we 'change' the default format by
2618     // emitting block char format information
2619     QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2620
2621     QTextList *list = block.textList();
2622     if (list) {
2623         if (list->itemNumber(block) == 0) { // first item? emit <ul> or appropriate
2624             const QTextListFormat format = list->format();
2625             const int style = format.style();
2626             switch (style) {
2627                 case QTextListFormat::ListDecimal: html += QLatin1String("<ol"); break;
2628                 case QTextListFormat::ListDisc: html += QLatin1String("<ul"); break;
2629                 case QTextListFormat::ListCircle: html += QLatin1String("<ul type=\"circle\""); break;
2630                 case QTextListFormat::ListSquare: html += QLatin1String("<ul type=\"square\""); break;
2631                 case QTextListFormat::ListLowerAlpha: html += QLatin1String("<ol type=\"a\""); break;
2632                 case QTextListFormat::ListUpperAlpha: html += QLatin1String("<ol type=\"A\""); break;
2633                 case QTextListFormat::ListLowerRoman: html += QLatin1String("<ol type=\"i\""); break;
2634                 case QTextListFormat::ListUpperRoman: html += QLatin1String("<ol type=\"I\""); break;
2635                 default: html += QLatin1String("<ul"); // ### should not happen
2636             }
2637
2638             QString styleString = QString::fromLatin1("margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;");
2639
2640             if (format.hasProperty(QTextFormat::ListIndent)) {
2641                 styleString += QLatin1String(" -qt-list-indent: ");
2642                 styleString += QString::number(format.indent());
2643                 styleString += QLatin1Char(';');
2644             }
2645
2646             if (format.hasProperty(QTextFormat::ListNumberPrefix)) {
2647                 QString numberPrefix = format.numberPrefix();
2648                 numberPrefix.replace(QLatin1Char('"'), QLatin1String("\\22"));
2649                 numberPrefix.replace(QLatin1Char('\''), QLatin1String("\\27")); // FIXME: There's a problem in the CSS parser the prevents this from being correctly restored
2650                 styleString += QLatin1String(" -qt-list-number-prefix: ");
2651                 styleString += QLatin1Char('\'');
2652                 styleString += numberPrefix;
2653                 styleString += QLatin1Char('\'');
2654                 styleString += QLatin1Char(';');
2655             }
2656
2657             if (format.hasProperty(QTextFormat::ListNumberSuffix)) {
2658                 if (format.numberSuffix() != QLatin1String(".")) { // this is our default
2659                     QString numberSuffix = format.numberSuffix();
2660                     numberSuffix.replace(QLatin1Char('"'), QLatin1String("\\22"));
2661                     numberSuffix.replace(QLatin1Char('\''), QLatin1String("\\27")); // see above
2662                     styleString += QLatin1String(" -qt-list-number-suffix: ");
2663                     styleString += QLatin1Char('\'');
2664                     styleString += numberSuffix;
2665                     styleString += QLatin1Char('\'');
2666                     styleString += QLatin1Char(';');
2667                 }
2668             }
2669
2670             html += QLatin1String(" style=\"");
2671             html += styleString;
2672             html += QLatin1String("\">");
2673         }
2674
2675         html += QLatin1String("<li");
2676
2677         const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
2678         if (!blockFmt.properties().isEmpty()) {
2679             html += QLatin1String(" style=\"");
2680             emitCharFormatStyle(blockFmt);
2681             html += QLatin1Char('\"');
2682
2683             defaultCharFormat.merge(block.charFormat());
2684         }
2685     }
2686
2687     const QTextBlockFormat blockFormat = block.blockFormat();
2688     if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
2689         html += QLatin1String("<hr");
2690
2691         QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
2692         if (width.type() != QTextLength::VariableLength)
2693             emitTextLength("width", width);
2694         else
2695             html += QLatin1Char(' ');
2696
2697         html += QLatin1String("/>");
2698         return;
2699     }
2700
2701     const bool pre = blockFormat.nonBreakableLines();
2702     if (pre) {
2703         if (list)
2704             html += QLatin1Char('>');
2705         html += QLatin1String("<pre");
2706     } else if (!list) {
2707         html += QLatin1String("<p");
2708     }
2709
2710     emitBlockAttributes(block);
2711
2712     html += QLatin1Char('>');
2713     if (block.begin().atEnd())
2714         html += QLatin1String("<br />");
2715
2716     QTextBlock::Iterator it = block.begin();
2717     if (fragmentMarkers && !it.atEnd() && block == doc->begin())
2718         html += QLatin1String("<!--StartFragment-->");
2719
2720     for (; !it.atEnd(); ++it)
2721         emitFragment(it.fragment());
2722
2723     if (fragmentMarkers && block.position() + block.length() == doc->docHandle()->length())
2724         html += QLatin1String("<!--EndFragment-->");
2725
2726     if (pre)
2727         html += QLatin1String("</pre>");
2728     else if (list)
2729         html += QLatin1String("</li>");
2730     else
2731         html += QLatin1String("</p>");
2732
2733     if (list) {
2734         if (list->itemNumber(block) == list->count() - 1) { // last item? close list
2735             if (isOrderedList(list->format().style()))
2736                 html += QLatin1String("</ol>");
2737             else
2738                 html += QLatin1String("</ul>");
2739         }
2740     }
2741
2742     defaultCharFormat = oldDefaultCharFormat;
2743 }
2744
2745 extern bool qHasPixmapTexture(const QBrush& brush);
2746
2747 QString QTextHtmlExporter::findUrlForImage(const QTextDocument *doc, qint64 cacheKey, bool isPixmap)
2748 {
2749     QString url;
2750     if (!doc)
2751         return url;
2752
2753     if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent()))
2754         return findUrlForImage(parent, cacheKey, isPixmap);
2755
2756     if (doc && doc->docHandle()) {
2757         QTextDocumentPrivate *priv = doc->docHandle();
2758         QMap<QUrl, QVariant>::const_iterator it = priv->cachedResources.constBegin();
2759         for (; it != priv->cachedResources.constEnd(); ++it) {
2760
2761             const QVariant &v = it.value();
2762             if (v.type() == QVariant::Image && !isPixmap) {
2763                 if (qvariant_cast<QImage>(v).cacheKey() == cacheKey)
2764                     break;
2765             }
2766
2767             if (v.type() == QVariant::Pixmap && isPixmap) {
2768                 if (qvariant_cast<QPixmap>(v).cacheKey() == cacheKey)
2769                     break;
2770             }
2771         }
2772
2773         if (it != priv->cachedResources.constEnd())
2774             url = it.key().toString();
2775     }
2776
2777     return url;
2778 }
2779
2780 void QTextDocumentPrivate::mergeCachedResources(const QTextDocumentPrivate *priv)
2781 {
2782     if (!priv)
2783         return;
2784
2785     cachedResources.unite(priv->cachedResources);
2786 }
2787
2788 void QTextHtmlExporter::emitBackgroundAttribute(const QTextFormat &format)
2789 {
2790     if (format.hasProperty(QTextFormat::BackgroundImageUrl)) {
2791         QString url = format.property(QTextFormat::BackgroundImageUrl).toString();
2792         emitAttribute("background", url);
2793     } else {
2794         const QBrush &brush = format.background();
2795         if (brush.style() == Qt::SolidPattern) {
2796             emitAttribute("bgcolor", brush.color().name());
2797         } else if (brush.style() == Qt::TexturePattern) {
2798             const bool isPixmap = qHasPixmapTexture(brush);
2799             const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
2800
2801             const QString url = findUrlForImage(doc, cacheKey, isPixmap);
2802
2803             if (!url.isEmpty())
2804                 emitAttribute("background", url);
2805         }
2806     }
2807 }
2808
2809 void QTextHtmlExporter::emitTable(const QTextTable *table)
2810 {
2811     QTextTableFormat format = table->format();
2812
2813     html += QLatin1String("\n<table");
2814
2815     if (format.hasProperty(QTextFormat::FrameBorder))
2816         emitAttribute("border", QString::number(format.border()));
2817
2818     emitFrameStyle(format, TableFrame);
2819
2820     emitAlignment(format.alignment());
2821     emitTextLength("width", format.width());
2822
2823     if (format.hasProperty(QTextFormat::TableCellSpacing))
2824         emitAttribute("cellspacing", QString::number(format.cellSpacing()));
2825     if (format.hasProperty(QTextFormat::TableCellPadding))
2826         emitAttribute("cellpadding", QString::number(format.cellPadding()));
2827
2828     emitBackgroundAttribute(format);
2829
2830     html += QLatin1Char('>');
2831
2832     const int rows = table->rows();
2833     const int columns = table->columns();
2834
2835     QVector<QTextLength> columnWidths = format.columnWidthConstraints();
2836     if (columnWidths.isEmpty()) {
2837         columnWidths.resize(columns);
2838         columnWidths.fill(QTextLength());
2839     }
2840     Q_ASSERT(columnWidths.count() == columns);
2841
2842     QVarLengthArray<bool> widthEmittedForColumn(columns);
2843     for (int i = 0; i < columns; ++i)
2844         widthEmittedForColumn[i] = false;
2845
2846     const int headerRowCount = qMin(format.headerRowCount(), rows);
2847     if (headerRowCount > 0)
2848         html += QLatin1String("<thead>");
2849
2850     for (int row = 0; row < rows; ++row) {
2851         html += QLatin1String("\n<tr>");
2852
2853         for (int col = 0; col < columns; ++col) {
2854             const QTextTableCell cell = table->cellAt(row, col);
2855
2856             // for col/rowspans
2857             if (cell.row() != row)
2858                 continue;
2859
2860             if (cell.column() != col)
2861                 continue;
2862
2863             html += QLatin1String("\n<td");
2864
2865             if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
2866                 emitTextLength("width", columnWidths.at(col));
2867                 widthEmittedForColumn[col] = true;
2868             }
2869
2870             if (cell.columnSpan() > 1)
2871                 emitAttribute("colspan", QString::number(cell.columnSpan()));
2872
2873             if (cell.rowSpan() > 1)
2874                 emitAttribute("rowspan", QString::number(cell.rowSpan()));
2875
2876             const QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
2877             emitBackgroundAttribute(cellFormat);
2878
2879             QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2880
2881             QTextCharFormat::VerticalAlignment valign = cellFormat.verticalAlignment();
2882
2883             QString styleString;
2884             if (valign >= QTextCharFormat::AlignMiddle && valign <= QTextCharFormat::AlignBottom) {
2885                 styleString += QLatin1String(" vertical-align:");
2886                 switch (valign) {
2887                 case QTextCharFormat::AlignMiddle:
2888                     styleString += QLatin1String("middle");
2889                     break;
2890                 case QTextCharFormat::AlignTop:
2891                     styleString += QLatin1String("top");
2892                     break;
2893                 case QTextCharFormat::AlignBottom:
2894                     styleString += QLatin1String("bottom");
2895                     break;
2896                 default:
2897                     break;
2898                 }
2899                 styleString += QLatin1Char(';');
2900
2901                 QTextCharFormat temp;
2902                 temp.setVerticalAlignment(valign);
2903                 defaultCharFormat.merge(temp);
2904             }
2905
2906             if (cellFormat.hasProperty(QTextFormat::TableCellLeftPadding))
2907                 styleString += QLatin1String(" padding-left:") + QString::number(cellFormat.leftPadding()) + QLatin1Char(';');
2908             if (cellFormat.hasProperty(QTextFormat::TableCellRightPadding))
2909                 styleString += QLatin1String(" padding-right:") + QString::number(cellFormat.rightPadding()) + QLatin1Char(';');
2910             if (cellFormat.hasProperty(QTextFormat::TableCellTopPadding))
2911                 styleString += QLatin1String(" padding-top:") + QString::number(cellFormat.topPadding()) + QLatin1Char(';');
2912             if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
2913                 styleString += QLatin1String(" padding-bottom:") + QString::number(cellFormat.bottomPadding()) + QLatin1Char(';');
2914
2915             if (!styleString.isEmpty())
2916                 html += QLatin1String(" style=\"") + styleString + QLatin1Char('\"');
2917
2918             html += QLatin1Char('>');
2919
2920             emitFrame(cell.begin());
2921
2922             html += QLatin1String("</td>");
2923
2924             defaultCharFormat = oldDefaultCharFormat;
2925         }
2926
2927         html += QLatin1String("</tr>");
2928         if (headerRowCount > 0 && row == headerRowCount - 1)
2929             html += QLatin1String("</thead>");
2930     }
2931
2932     html += QLatin1String("</table>");
2933 }
2934
2935 void QTextHtmlExporter::emitFrame(QTextFrame::Iterator frameIt)
2936 {
2937     if (!frameIt.atEnd()) {
2938         QTextFrame::Iterator next = frameIt;
2939         ++next;
2940         if (next.atEnd()
2941             && frameIt.currentFrame() == 0
2942             && frameIt.parentFrame() != doc->rootFrame()
2943             && frameIt.currentBlock().begin().atEnd())
2944             return;
2945     }
2946
2947     for (QTextFrame::Iterator it = frameIt;
2948          !it.atEnd(); ++it) {
2949         if (QTextFrame *f = it.currentFrame()) {
2950             if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
2951                 emitTable(table);
2952             } else {
2953                 emitTextFrame(f);
2954             }
2955         } else if (it.currentBlock().isValid()) {
2956             emitBlock(it.currentBlock());
2957         }
2958     }
2959 }
2960
2961 void QTextHtmlExporter::emitTextFrame(const QTextFrame *f)
2962 {
2963     FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
2964
2965     html += QLatin1String("\n<table");
2966     QTextFrameFormat format = f->frameFormat();
2967
2968     if (format.hasProperty(QTextFormat::FrameBorder))
2969         emitAttribute("border", QString::number(format.border()));
2970
2971     emitFrameStyle(format, frameType);
2972
2973     emitTextLength("width", format.width());
2974     emitTextLength("height", format.height());
2975
2976     // root frame's bcolor goes in the <body> tag
2977     if (frameType != RootFrame)
2978         emitBackgroundAttribute(format);
2979
2980     html += QLatin1Char('>');
2981     html += QLatin1String("\n<tr>\n<td style=\"border: none;\">");
2982     emitFrame(f->begin());
2983     html += QLatin1String("</td></tr></table>");
2984 }
2985
2986 void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType frameType)
2987 {
2988     QLatin1String styleAttribute(" style=\"");
2989     html += styleAttribute;
2990     const int originalHtmlLength = html.length();
2991
2992     if (frameType == TextFrame)
2993         html += QLatin1String("-qt-table-type: frame;");
2994     else if (frameType == RootFrame)
2995         html += QLatin1String("-qt-table-type: root;");
2996
2997     const QTextFrameFormat defaultFormat;
2998
2999     emitFloatStyle(format.position(), OmitStyleTag);
3000     emitPageBreakPolicy(format.pageBreakPolicy());
3001
3002     if (format.borderBrush() != defaultFormat.borderBrush()) {
3003         html += QLatin1String(" border-color:");
3004         html += format.borderBrush().color().name();
3005         html += QLatin1Char(';');
3006     }
3007
3008     if (format.borderStyle() != defaultFormat.borderStyle())
3009         emitBorderStyle(format.borderStyle());
3010
3011     if (format.hasProperty(QTextFormat::FrameMargin)
3012         || format.hasProperty(QTextFormat::FrameLeftMargin)
3013         || format.hasProperty(QTextFormat::FrameRightMargin)
3014         || format.hasProperty(QTextFormat::FrameTopMargin)
3015         || format.hasProperty(QTextFormat::FrameBottomMargin))
3016         emitMargins(QString::number(format.topMargin()),
3017                     QString::number(format.bottomMargin()),
3018                     QString::number(format.leftMargin()),
3019                     QString::number(format.rightMargin()));
3020
3021     if (html.length() == originalHtmlLength) // nothing emitted?
3022         html.chop(qstrlen(styleAttribute.latin1()));
3023     else
3024         html += QLatin1Char('\"');
3025 }
3026
3027 /*!
3028     Returns a string containing an HTML representation of the document.
3029
3030     The \a encoding parameter specifies the value for the charset attribute
3031     in the html header. For example if 'utf-8' is specified then the
3032     beginning of the generated html will look like this:
3033     \snippet doc/src/snippets/code/src_gui_text_qtextdocument.cpp 1
3034
3035     If no encoding is specified then no such meta information is generated.
3036
3037     If you later on convert the returned html string into a byte array for
3038     transmission over a network or when saving to disk you should specify
3039     the encoding you're going to use for the conversion to a byte array here.
3040
3041     \sa {Supported HTML Subset}
3042 */
3043 #ifndef QT_NO_TEXTHTMLPARSER
3044 QString QTextDocument::toHtml(const QByteArray &encoding) const
3045 {
3046     return QTextHtmlExporter(this).toHtml(encoding);
3047 }
3048 #endif // QT_NO_TEXTHTMLPARSER
3049
3050 /*!
3051     Returns a vector of text formats for all the formats used in the document.
3052 */
3053 QVector<QTextFormat> QTextDocument::allFormats() const
3054 {
3055     Q_D(const QTextDocument);
3056     return d->formatCollection()->formats;
3057 }
3058
3059
3060 /*!
3061   \internal
3062
3063   So that not all classes have to be friends of each other...
3064 */
3065 QTextDocumentPrivate *QTextDocument::docHandle() const
3066 {
3067     Q_D(const QTextDocument);
3068     return const_cast<QTextDocumentPrivate *>(d);
3069 }
3070
3071 /*!
3072     \since 4.4
3073     \fn QTextDocument::undoCommandAdded()
3074
3075     This signal is emitted  every time a new level of undo is added to the QTextDocument.
3076 */
3077
3078 QT_END_NAMESPACE