Compile with clang when C++11 support is enabled
[profile/ivi/qtbase.git] / src / gui / text / qtextdocument.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
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 #include <qmetaobject.h>
56
57 #include "qtexthtmlparser_p.h"
58 #include "qpainter.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/qabstracttextdocumentlayout_p.h>
67 #include "qpagedpaintdevice.h"
68 #include "private/qpagedpaintdevice_p.h"
69
70 #include <limits.h>
71
72 QT_BEGIN_NAMESPACE
73
74 Q_CORE_EXPORT unsigned int qt_int_sqrt(unsigned int n);
75
76 /*!
77     Returns true if the string \a text is likely to be rich text;
78     otherwise returns false.
79
80     This function uses a fast and therefore simple heuristic. It
81     mainly checks whether there is something that looks like a tag
82     before the first line break. Although the result may be correct
83     for common cases, there is no guarantee.
84
85     This function is defined in the \c <QTextDocument> header file.
86 */
87 bool Qt::mightBeRichText(const QString& text)
88 {
89     if (text.isEmpty())
90         return false;
91     int start = 0;
92
93     while (start < text.length() && text.at(start).isSpace())
94         ++start;
95
96     // skip a leading <?xml ... ?> as for example with xhtml
97     if (text.mid(start, 5) == QLatin1String("<?xml")) {
98         while (start < text.length()) {
99             if (text.at(start) == QLatin1Char('?')
100                 && start + 2 < text.length()
101                 && text.at(start + 1) == QLatin1Char('>')) {
102                 start += 2;
103                 break;
104             }
105             ++start;
106         }
107
108         while (start < text.length() && text.at(start).isSpace())
109             ++start;
110     }
111
112     if (text.mid(start, 5).toLower() == QLatin1String("<!doc"))
113         return true;
114     int open = start;
115     while (open < text.length() && text.at(open) != QLatin1Char('<')
116             && text.at(open) != QLatin1Char('\n')) {
117         if (text.at(open) == QLatin1Char('&') &&  text.mid(open+1,3) == QLatin1String("lt;"))
118             return true; // support desperate attempt of user to see <...>
119         ++open;
120     }
121     if (open < text.length() && text.at(open) == QLatin1Char('<')) {
122         const int close = text.indexOf(QLatin1Char('>'), open);
123         if (close > -1) {
124             QString tag;
125             for (int i = open+1; i < close; ++i) {
126                 if (text[i].isDigit() || text[i].isLetter())
127                     tag += text[i];
128                 else if (!tag.isEmpty() && text[i].isSpace())
129                     break;
130                 else if (!tag.isEmpty() && text[i] == QLatin1Char('/') && i + 1 == close)
131                     break;
132                 else if (!text[i].isSpace() && (!tag.isEmpty() || text[i] != QLatin1Char('!')))
133                     return false; // that's not a tag
134             }
135 #ifndef QT_NO_TEXTHTMLPARSER
136             return QTextHtmlParser::lookupElement(tag.toLower()) != -1;
137 #else
138             return false;
139 #endif // QT_NO_TEXTHTMLPARSER
140         }
141     }
142     return false;
143 }
144
145 /*!
146     \fn QString Qt::convertFromPlainText(const QString &plain, WhiteSpaceMode mode)
147
148     Converts the plain text string \a plain to an HTML-formatted
149     paragraph while preserving most of its look.
150
151     \a mode defines how whitespace is handled.
152
153     This function is defined in the \c <QTextDocument> header file.
154
155     \sa escape(), mightBeRichText()
156 */
157 QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
158 {
159     int col = 0;
160     QString rich;
161     rich += QLatin1String("<p>");
162     for (int i = 0; i < plain.length(); ++i) {
163         if (plain[i] == QLatin1Char('\n')){
164             int c = 1;
165             while (i+1 < plain.length() && plain[i+1] == QLatin1Char('\n')) {
166                 i++;
167                 c++;
168             }
169             if (c == 1)
170                 rich += QLatin1String("<br>\n");
171             else {
172                 rich += QLatin1String("</p>\n");
173                 while (--c > 1)
174                     rich += QLatin1String("<br>\n");
175                 rich += QLatin1String("<p>");
176             }
177             col = 0;
178         } else {
179             if (mode == Qt::WhiteSpacePre && plain[i] == QLatin1Char('\t')){
180                 rich += QChar(0x00a0U);
181                 ++col;
182                 while (col % 8) {
183                     rich += QChar(0x00a0U);
184                     ++col;
185                 }
186             }
187             else if (mode == Qt::WhiteSpacePre && plain[i].isSpace())
188                 rich += QChar(0x00a0U);
189             else if (plain[i] == QLatin1Char('<'))
190                 rich += QLatin1String("&lt;");
191             else if (plain[i] == QLatin1Char('>'))
192                 rich += QLatin1String("&gt;");
193             else if (plain[i] == QLatin1Char('&'))
194                 rich += QLatin1String("&amp;");
195             else
196                 rich += plain[i];
197             ++col;
198         }
199     }
200     if (col != 0)
201         rich += QLatin1String("</p>");
202     return rich;
203 }
204
205 #ifndef QT_NO_TEXTCODEC
206 /*!
207     \internal
208
209     This function is defined in the \c <QTextDocument> header file.
210 */
211 QTextCodec *Qt::codecForHtml(const QByteArray &ba)
212 {
213     return QTextCodec::codecForHtml(ba);
214 }
215 #endif
216
217 /*!
218     \class QTextDocument
219     \reentrant
220
221     \brief The QTextDocument class holds formatted text that can be
222     viewed and edited using a QTextEdit.
223
224     \ingroup richtext-processing
225
226
227     QTextDocument is a container for structured rich text documents, providing
228     support for styled text and various types of document elements, such as
229     lists, tables, frames, and images.
230     They can be created for use in a QTextEdit, or used independently.
231
232     Each document element is described by an associated format object. Each
233     format object is treated as a unique object by QTextDocuments, and can be
234     passed to objectForFormat() to obtain the document element that it is
235     applied to.
236
237     A QTextDocument can be edited programmatically using a QTextCursor, and
238     its contents can be examined by traversing the document structure. The
239     entire document structure is stored as a hierarchy of document elements
240     beneath the root frame, found with the rootFrame() function. Alternatively,
241     if you just want to iterate over the textual contents of the document you
242     can use begin(), end(), and findBlock() to retrieve text blocks that you
243     can examine and iterate over.
244
245     The layout of a document is determined by the documentLayout();
246     you can create your own QAbstractTextDocumentLayout subclass and
247     set it using setDocumentLayout() if you want to use your own
248     layout logic. The document's title and other meta-information can be
249     obtained by calling the metaInformation() function. For documents that
250     are exposed to users through the QTextEdit class, the document title
251     is also available via the QTextEdit::documentTitle() function.
252
253     The toPlainText() and toHtml() convenience functions allow you to retrieve the
254     contents of the document as plain text and HTML.
255     The document's text can be searched using the find() functions.
256
257     Undo/redo of operations performed on the document can be controlled using
258     the setUndoRedoEnabled() function. The undo/redo system can be controlled
259     by an editor widget through the undo() and redo() slots; the document also
260     provides contentsChanged(), undoAvailable(), and redoAvailable() signals
261     that inform connected editor widgets about the state of the undo/redo
262     system. The following are the undo/redo operations of a QTextDocument:
263
264     \list
265         \li Insertion or removal of characters. A sequence of insertions or removals
266            within the same text block are regarded as a single undo/redo operation.
267         \li Insertion or removal of text blocks. Sequences of insertion or removals
268            in a single operation (e.g., by selecting and then deleting text) are
269            regarded as a single undo/redo operation.
270         \li Text character format changes.
271         \li Text block format changes.
272         \li Text block group format changes.
273     \endlist
274
275     \sa QTextCursor, QTextEdit, {Rich Text Processing}, {Text Object Example}
276 */
277
278 /*!
279     \property QTextDocument::defaultFont
280     \brief the default font used to display the document's text
281 */
282
283 /*!
284     \property QTextDocument::defaultTextOption
285     \brief the default text option will be set on all \l{QTextLayout}s in the document.
286
287     When \l{QTextBlock}s are created, the defaultTextOption is set on their
288     QTextLayout. This allows setting global properties for the document such as the
289     default word wrap mode.
290  */
291
292 /*!
293     Constructs an empty QTextDocument with the given \a parent.
294 */
295 QTextDocument::QTextDocument(QObject *parent)
296     : QObject(*new QTextDocumentPrivate, parent)
297 {
298     Q_D(QTextDocument);
299     d->init();
300 }
301
302 /*!
303     Constructs a QTextDocument containing the plain (unformatted) \a text
304     specified, and with the given \a parent.
305 */
306 QTextDocument::QTextDocument(const QString &text, QObject *parent)
307     : QObject(*new QTextDocumentPrivate, parent)
308 {
309     Q_D(QTextDocument);
310     d->init();
311     QTextCursor(this).insertText(text);
312 }
313
314 /*!
315     \internal
316 */
317 QTextDocument::QTextDocument(QTextDocumentPrivate &dd, QObject *parent)
318     : QObject(dd, parent)
319 {
320     Q_D(QTextDocument);
321     d->init();
322 }
323
324 /*!
325     Destroys the document.
326 */
327 QTextDocument::~QTextDocument()
328 {
329 }
330
331
332 /*!
333   Creates a new QTextDocument that is a copy of this text document. \a
334   parent is the parent of the returned text document.
335 */
336 QTextDocument *QTextDocument::clone(QObject *parent) const
337 {
338     Q_D(const QTextDocument);
339     QTextDocument *doc = new QTextDocument(parent);
340     QTextCursor(doc).insertFragment(QTextDocumentFragment(this));
341     doc->rootFrame()->setFrameFormat(rootFrame()->frameFormat());
342     QTextDocumentPrivate *priv = doc->d_func();
343     priv->title = d->title;
344     priv->url = d->url;
345     priv->pageSize = d->pageSize;
346     priv->indentWidth = d->indentWidth;
347     priv->defaultTextOption = d->defaultTextOption;
348     priv->setDefaultFont(d->defaultFont());
349     priv->resources = d->resources;
350     priv->cachedResources.clear();
351 #ifndef QT_NO_CSSPARSER
352     priv->defaultStyleSheet = d->defaultStyleSheet;
353     priv->parsedDefaultStyleSheet = d->parsedDefaultStyleSheet;
354 #endif
355     return doc;
356 }
357
358 /*!
359     Returns true if the document is empty; otherwise returns false.
360 */
361 bool QTextDocument::isEmpty() const
362 {
363     Q_D(const QTextDocument);
364     /* because if we're empty we still have one single paragraph as
365      * one single fragment */
366     return d->length() <= 1;
367 }
368
369 /*!
370   Clears the document.
371 */
372 void QTextDocument::clear()
373 {
374     Q_D(QTextDocument);
375     d->clear();
376     d->resources.clear();
377 }
378
379 /*!
380     \since 4.2
381
382     Undoes the last editing operation on the document if undo is
383     available. The provided \a cursor is positioned at the end of the
384     location where the edition operation was undone.
385
386     See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
387     documentation for details.
388
389     \sa undoAvailable(), isUndoRedoEnabled()
390 */
391 void QTextDocument::undo(QTextCursor *cursor)
392 {
393     Q_D(QTextDocument);
394     const int pos = d->undoRedo(true);
395     if (cursor && pos >= 0) {
396         *cursor = QTextCursor(this);
397         cursor->setPosition(pos);
398     }
399 }
400
401 /*!
402     \since 4.2
403     Redoes the last editing operation on the document if \l{QTextDocument::isRedoAvailable()}{redo is available}.
404
405     The provided \a cursor is positioned at the end of the location where
406     the edition operation was redone.
407 */
408 void QTextDocument::redo(QTextCursor *cursor)
409 {
410     Q_D(QTextDocument);
411     const int pos = d->undoRedo(false);
412     if (cursor && pos >= 0) {
413         *cursor = QTextCursor(this);
414         cursor->setPosition(pos);
415     }
416 }
417
418 /*! \enum QTextDocument::Stacks
419   
420   \value UndoStack              The undo stack.
421   \value RedoStack              The redo stack.
422   \value UndoAndRedoStacks      Both the undo and redo stacks.
423 */
424         
425 /*!
426     \since 4.7
427     Clears the stacks specified by \a stacksToClear.
428
429     This method clears any commands on the undo stack, the redo stack,
430     or both (the default). If commands are cleared, the appropriate
431     signals are emitted, QTextDocument::undoAvailable() or
432     QTextDocument::redoAvailable().
433
434     \sa QTextDocument::undoAvailable(), QTextDocument::redoAvailable()
435 */
436 void QTextDocument::clearUndoRedoStacks(Stacks stacksToClear)
437 {
438     Q_D(QTextDocument);
439     d->clearUndoRedoStacks(stacksToClear, true);
440 }
441
442 /*!
443     \overload
444
445 */
446 void QTextDocument::undo()
447 {
448     Q_D(QTextDocument);
449     d->undoRedo(true);
450 }
451
452 /*!
453     \overload
454     Redoes the last editing operation on the document if \l{QTextDocument::isRedoAvailable()}{redo is available}.
455 */
456 void QTextDocument::redo()
457 {
458     Q_D(QTextDocument);
459     d->undoRedo(false);
460 }
461
462 /*!
463     \internal
464
465     Appends a custom undo \a item to the undo stack.
466 */
467 void QTextDocument::appendUndoItem(QAbstractUndoItem *item)
468 {
469     Q_D(QTextDocument);
470     d->appendUndoItem(item);
471 }
472
473 /*!
474     \property QTextDocument::undoRedoEnabled
475     \brief whether undo/redo are enabled for this document
476
477     This defaults to true. If disabled, the undo stack is cleared and
478     no items will be added to it.
479 */
480 void QTextDocument::setUndoRedoEnabled(bool enable)
481 {
482     Q_D(QTextDocument);
483     d->enableUndoRedo(enable);
484 }
485
486 bool QTextDocument::isUndoRedoEnabled() const
487 {
488     Q_D(const QTextDocument);
489     return d->isUndoRedoEnabled();
490 }
491
492 /*!
493     \property QTextDocument::maximumBlockCount
494     \since 4.2
495     \brief Specifies the limit for blocks in the document.
496
497     Specifies the maximum number of blocks the document may have. If there are
498     more blocks in the document that specified with this property blocks are removed
499     from the beginning of the document.
500
501     A negative or zero value specifies that the document may contain an unlimited
502     amount of blocks.
503
504     The default value is 0.
505
506     Note that setting this property will apply the limit immediately to the document
507     contents.
508
509     Setting this property also disables the undo redo history.
510
511     This property is undefined in documents with tables or frames.
512 */
513 int QTextDocument::maximumBlockCount() const
514 {
515     Q_D(const QTextDocument);
516     return d->maximumBlockCount;
517 }
518
519 void QTextDocument::setMaximumBlockCount(int maximum)
520 {
521     Q_D(QTextDocument);
522     d->maximumBlockCount = maximum;
523     d->ensureMaximumBlockCount();
524     setUndoRedoEnabled(false);
525 }
526
527 /*!
528     \since 4.3
529
530     The default text option is used on all QTextLayout objects in the document.
531     This allows setting global properties for the document such as the default
532     word wrap mode.
533 */
534 QTextOption QTextDocument::defaultTextOption() const
535 {
536     Q_D(const QTextDocument);
537     return d->defaultTextOption;
538 }
539
540 /*!
541     \since 4.3
542
543     Sets the default text option.
544 */
545 void QTextDocument::setDefaultTextOption(const QTextOption &option)
546 {
547     Q_D(QTextDocument);
548     d->defaultTextOption = option;
549     if (d->lout)
550         d->lout->documentChanged(0, 0, d->length());
551 }
552
553 /*!
554     \since 4.8
555
556     The default cursor movement style is used by all QTextCursor objects
557     created from the document. The default is Qt::LogicalMoveStyle.
558 */
559 Qt::CursorMoveStyle QTextDocument::defaultCursorMoveStyle() const
560 {
561     Q_D(const QTextDocument);
562     return d->defaultCursorMoveStyle;
563 }
564
565 /*!
566     \since 4.8
567
568     Sets the default cursor movement style to the given \a style.
569 */
570 void QTextDocument::setDefaultCursorMoveStyle(Qt::CursorMoveStyle style)
571 {
572     Q_D(QTextDocument);
573     d->defaultCursorMoveStyle = style;
574 }
575
576 /*!
577     \fn void QTextDocument::markContentsDirty(int position, int length)
578
579     Marks the contents specified by the given \a position and \a length
580     as "dirty", informing the document that it needs to be laid out
581     again.
582 */
583 void QTextDocument::markContentsDirty(int from, int length)
584 {
585     Q_D(QTextDocument);
586     d->documentChange(from, length);
587     if (!d->inContentsChange) {
588         if (d->lout) {
589             d->lout->documentChanged(d->docChangeFrom, d->docChangeOldLength, d->docChangeLength);
590             d->docChangeFrom = -1;
591         }
592     }
593 }
594
595 /*!
596     \property QTextDocument::useDesignMetrics
597     \since 4.1
598     \brief whether the document uses design metrics of fonts to improve the accuracy of text layout
599
600     If this property is set to true, the layout will use design metrics.
601     Otherwise, the metrics of the paint device as set on
602     QAbstractTextDocumentLayout::setPaintDevice() will be used.
603
604     Using design metrics makes a layout have a width that is no longer dependent on hinting
605     and pixel-rounding. This means that WYSIWYG text layout becomes possible because the width
606     scales much more linearly based on paintdevice metrics than it would otherwise.
607
608     By default, this property is false.
609 */
610
611 void QTextDocument::setUseDesignMetrics(bool b)
612 {
613     Q_D(QTextDocument);
614     if (b == d->defaultTextOption.useDesignMetrics())
615         return;
616     d->defaultTextOption.setUseDesignMetrics(b);
617     if (d->lout)
618         d->lout->documentChanged(0, 0, d->length());
619 }
620
621 bool QTextDocument::useDesignMetrics() const
622 {
623     Q_D(const QTextDocument);
624     return d->defaultTextOption.useDesignMetrics();
625 }
626
627 /*!
628     \since 4.2
629
630     Draws the content of the document with painter \a p, clipped to \a rect.
631     If \a rect is a null rectangle (default) then the document is painted unclipped.
632 */
633 void QTextDocument::drawContents(QPainter *p, const QRectF &rect)
634 {
635     p->save();
636     QAbstractTextDocumentLayout::PaintContext ctx;
637     if (rect.isValid()) {
638         p->setClipRect(rect);
639         ctx.clip = rect;
640     }
641     documentLayout()->draw(p, ctx);
642     p->restore();
643 }
644
645 /*!
646     \property QTextDocument::textWidth
647     \since 4.2
648
649     The text width specifies the preferred width for text in the document. If
650     the text (or content in general) is wider than the specified with it is broken
651     into multiple lines and grows vertically. If the text cannot be broken into multiple
652     lines to fit into the specified text width it will be larger and the size() and the
653     idealWidth() property will reflect that.
654
655     If the text width is set to -1 then the text will not be broken into multiple lines
656     unless it is enforced through an explicit line break or a new paragraph.
657
658     The default value is -1.
659
660     Setting the text width will also set the page height to -1, causing the document to
661     grow or shrink vertically in a continuous way. If you want the document layout to break
662     the text into multiple pages then you have to set the pageSize property instead.
663
664     \sa size(), idealWidth(), pageSize()
665 */
666 void QTextDocument::setTextWidth(qreal width)
667 {
668     Q_D(QTextDocument);
669     QSizeF sz = d->pageSize;
670     sz.setWidth(width);
671     sz.setHeight(-1);
672     setPageSize(sz);
673 }
674
675 qreal QTextDocument::textWidth() const
676 {
677     Q_D(const QTextDocument);
678     return d->pageSize.width();
679 }
680
681 /*!
682     \since 4.2
683
684     Returns the ideal width of the text document. The ideal width is the actually used width
685     of the document without optional alignments taken into account. It is always <= size().width().
686
687     \sa adjustSize(), textWidth
688 */
689 qreal QTextDocument::idealWidth() const
690 {
691     if (QTextDocumentLayout *lout = qobject_cast<QTextDocumentLayout *>(documentLayout()))
692         return lout->idealWidth();
693     return textWidth();
694 }
695
696 /*!
697     \property QTextDocument::documentMargin
698     \since 4.5
699
700      The margin around the document. The default is 4.
701 */
702 qreal QTextDocument::documentMargin() const
703 {
704     Q_D(const QTextDocument);
705     return d->documentMargin;
706 }
707
708 void QTextDocument::setDocumentMargin(qreal margin)
709 {
710     Q_D(QTextDocument);
711     if (d->documentMargin != margin) {
712         d->documentMargin = margin;
713
714         QTextFrame* root = rootFrame();
715         QTextFrameFormat format = root->frameFormat();
716         format.setMargin(margin);
717         root->setFrameFormat(format);
718
719         if (d->lout)
720             d->lout->documentChanged(0, 0, d->length());
721     }
722 }
723
724
725 /*!
726     \property QTextDocument::indentWidth
727     \since 4.4
728
729     Returns the width used for text list and text block indenting.
730
731     The indent properties of QTextListFormat and QTextBlockFormat specify
732     multiples of this value. The default indent width is 40.
733 */
734 qreal QTextDocument::indentWidth() const
735 {
736     Q_D(const QTextDocument);
737     return d->indentWidth;
738 }
739
740
741 /*!
742     \since 4.4
743
744     Sets the \a width used for text list and text block indenting.
745
746     The indent properties of QTextListFormat and QTextBlockFormat specify
747     multiples of this value. The default indent width is 40 .
748
749     \sa indentWidth()
750 */
751 void QTextDocument::setIndentWidth(qreal width)
752 {
753     Q_D(QTextDocument);
754     if (d->indentWidth != width) {
755         d->indentWidth = width;
756         if (d->lout)
757             d->lout->documentChanged(0, 0, d->length());
758     }
759 }
760
761
762
763
764 /*!
765     \since 4.2
766
767     Adjusts the document to a reasonable size.
768
769     \sa idealWidth(), textWidth, size
770 */
771 void QTextDocument::adjustSize()
772 {
773     // Pull this private function in from qglobal.cpp
774     QFont f = defaultFont();
775     QFontMetrics fm(f);
776     int mw =  fm.width(QLatin1Char('x')) * 80;
777     int w = mw;
778     setTextWidth(w);
779     QSizeF size = documentLayout()->documentSize();
780     if (size.width() != 0) {
781         w = qt_int_sqrt((uint)(5 * size.height() * size.width() / 3));
782         setTextWidth(qMin(w, mw));
783
784         size = documentLayout()->documentSize();
785         if (w*3 < 5*size.height()) {
786             w = qt_int_sqrt((uint)(2 * size.height() * size.width()));
787             setTextWidth(qMin(w, mw));
788         }
789     }
790     setTextWidth(idealWidth());
791 }
792
793 /*!
794     \property QTextDocument::size
795     \since 4.2
796
797     Returns the actual size of the document.
798     This is equivalent to documentLayout()->documentSize();
799
800     The size of the document can be changed either by setting
801     a text width or setting an entire page size.
802
803     Note that the width is always >= pageSize().width().
804
805     By default, for a newly-created, empty document, this property contains
806     a configuration-dependent size.
807
808     \sa setTextWidth(), setPageSize(), idealWidth()
809 */
810 QSizeF QTextDocument::size() const
811 {
812     return documentLayout()->documentSize();
813 }
814
815 /*!
816     \property QTextDocument::blockCount
817     \since 4.2
818
819     Returns the number of text blocks in the document.
820
821     The value of this property is undefined in documents with tables or frames.
822
823     By default, if defined, this property contains a value of 1.
824     \sa lineCount(), characterCount()
825 */
826 int QTextDocument::blockCount() const
827 {
828     Q_D(const QTextDocument);
829     return d->blockMap().numNodes();
830 }
831
832
833 /*!
834   \since 4.5
835
836   Returns the number of lines of this document (if the layout supports
837   this). Otherwise, this is identical to the number of blocks.
838
839   \sa blockCount(), characterCount()
840  */
841 int QTextDocument::lineCount() const
842 {
843     Q_D(const QTextDocument);
844     return d->blockMap().length(2);
845 }
846
847 /*!
848   \since 4.5
849
850   Returns the number of characters of this document.
851
852   \sa blockCount(), characterAt()
853  */
854 int QTextDocument::characterCount() const
855 {
856     Q_D(const QTextDocument);
857     return d->length();
858 }
859
860 /*!
861   \since 4.5
862
863   Returns the character at position \a pos, or a null character if the
864   position is out of range.
865
866   \sa characterCount()
867  */
868 QChar QTextDocument::characterAt(int pos) const
869 {
870     Q_D(const QTextDocument);
871     if (pos < 0 || pos >= d->length())
872         return QChar();
873     QTextDocumentPrivate::FragmentIterator fragIt = d->find(pos);
874     const QTextFragmentData * const frag = fragIt.value();
875     const int offsetInFragment = qMax(0, pos - fragIt.position());
876     return d->text.at(frag->stringPosition + offsetInFragment);
877 }
878
879
880 /*!
881     \property QTextDocument::defaultStyleSheet
882     \since 4.2
883
884     The default style sheet is applied to all newly HTML formatted text that is
885     inserted into the document, for example using setHtml() or QTextCursor::insertHtml().
886
887     The style sheet needs to be compliant to CSS 2.1 syntax.
888
889     \b{Note:} Changing the default style sheet does not have any effect to the existing content
890     of the document.
891
892     \sa {Supported HTML Subset}
893 */
894
895 #ifndef QT_NO_CSSPARSER
896 void QTextDocument::setDefaultStyleSheet(const QString &sheet)
897 {
898     Q_D(QTextDocument);
899     d->defaultStyleSheet = sheet;
900     QCss::Parser parser(sheet);
901     d->parsedDefaultStyleSheet = QCss::StyleSheet();
902     d->parsedDefaultStyleSheet.origin = QCss::StyleSheetOrigin_UserAgent;
903     parser.parse(&d->parsedDefaultStyleSheet);
904 }
905
906 QString QTextDocument::defaultStyleSheet() const
907 {
908     Q_D(const QTextDocument);
909     return d->defaultStyleSheet;
910 }
911 #endif // QT_NO_CSSPARSER
912
913 /*!
914     \fn void QTextDocument::contentsChanged()
915
916     This signal is emitted whenever the document's content changes; for
917     example, when text is inserted or deleted, or when formatting is applied.
918
919     \sa contentsChange()
920 */
921
922 /*!
923     \fn void QTextDocument::contentsChange(int position, int charsRemoved, int charsAdded)
924
925     This signal is emitted whenever the document's content changes; for
926     example, when text is inserted or deleted, or when formatting is applied.
927
928     Information is provided about the \a position of the character in the
929     document where the change occurred, the number of characters removed
930     (\a charsRemoved), and the number of characters added (\a charsAdded).
931
932     The signal is emitted before the document's layout manager is notified
933     about the change. This hook allows you to implement syntax highlighting
934     for the document.
935
936     \sa QAbstractTextDocumentLayout::documentChanged(), contentsChanged()
937 */
938
939
940 /*!
941     \fn QTextDocument::undoAvailable(bool available);
942
943     This signal is emitted whenever undo operations become available
944     (\a available is true) or unavailable (\a available is false).
945
946     See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
947     documentation for details.
948
949     \sa undo(), isUndoRedoEnabled()
950 */
951
952 /*!
953     \fn QTextDocument::redoAvailable(bool available);
954
955     This signal is emitted whenever redo operations become available
956     (\a available is true) or unavailable (\a available is false).
957 */
958
959 /*!
960     \fn QTextDocument::cursorPositionChanged(const QTextCursor &cursor);
961
962     This signal is emitted whenever the position of a cursor changed
963     due to an editing operation. The cursor that changed is passed in
964     \a cursor.  If you need a signal when the cursor is moved with the
965     arrow keys you can use the \l{QTextEdit::}{cursorPositionChanged()} signal in
966     QTextEdit.
967 */
968
969 /*!
970     \fn QTextDocument::blockCountChanged(int newBlockCount);
971     \since 4.3
972
973     This signal is emitted when the total number of text blocks in the
974     document changes. The value passed in \a newBlockCount is the new
975     total.
976 */
977
978 /*!
979     \fn QTextDocument::documentLayoutChanged();
980     \since 4.4
981
982     This signal is emitted when a new document layout is set.
983
984     \sa setDocumentLayout()
985
986 */
987
988
989 /*!
990     Returns true if undo is available; otherwise returns false.
991
992     \sa isRedoAvailable(), availableUndoSteps()
993 */
994 bool QTextDocument::isUndoAvailable() const
995 {
996     Q_D(const QTextDocument);
997     return d->isUndoAvailable();
998 }
999
1000 /*!
1001     Returns true if redo is available; otherwise returns false.
1002
1003     \sa isUndoAvailable(), availableRedoSteps()
1004 */
1005 bool QTextDocument::isRedoAvailable() const
1006 {
1007     Q_D(const QTextDocument);
1008     return d->isRedoAvailable();
1009 }
1010
1011 /*! \since 4.6
1012
1013     Returns the number of available undo steps.
1014
1015     \sa isUndoAvailable()
1016 */
1017 int QTextDocument::availableUndoSteps() const
1018 {
1019     Q_D(const QTextDocument);
1020     return d->availableUndoSteps();
1021 }
1022
1023 /*! \since 4.6
1024
1025     Returns the number of available redo steps.
1026
1027     \sa isRedoAvailable()
1028 */
1029 int QTextDocument::availableRedoSteps() const
1030 {
1031     Q_D(const QTextDocument);
1032     return d->availableRedoSteps();
1033 }
1034
1035 /*! \since 4.4
1036
1037     Returns the document's revision (if undo is enabled).
1038
1039     The revision is guaranteed to increase when a document that is not
1040     modified is edited.
1041
1042     \sa QTextBlock::revision(), isModified()
1043  */
1044 int QTextDocument::revision() const
1045 {
1046     Q_D(const QTextDocument);
1047     return d->revision;
1048 }
1049
1050
1051
1052 /*!
1053     Sets the document to use the given \a layout. The previous layout
1054     is deleted.
1055
1056     \sa documentLayoutChanged()
1057 */
1058 void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *layout)
1059 {
1060     Q_D(QTextDocument);
1061     d->setLayout(layout);
1062 }
1063
1064 /*!
1065     Returns the document layout for this document.
1066 */
1067 QAbstractTextDocumentLayout *QTextDocument::documentLayout() const
1068 {
1069     Q_D(const QTextDocument);
1070     if (!d->lout) {
1071         QTextDocument *that = const_cast<QTextDocument *>(this);
1072         that->d_func()->setLayout(new QTextDocumentLayout(that));
1073     }
1074     return d->lout;
1075 }
1076
1077
1078 /*!
1079     Returns meta information about the document of the type specified by
1080     \a info.
1081
1082     \sa setMetaInformation()
1083 */
1084 QString QTextDocument::metaInformation(MetaInformation info) const
1085 {
1086     Q_D(const QTextDocument);
1087     switch (info) {
1088     case DocumentTitle:
1089         return d->title;
1090     case DocumentUrl:
1091         return d->url;
1092     }
1093     return QString();
1094 }
1095
1096 /*!
1097     Sets the document's meta information of the type specified by \a info
1098     to the given \a string.
1099
1100     \sa metaInformation()
1101 */
1102 void QTextDocument::setMetaInformation(MetaInformation info, const QString &string)
1103 {
1104     Q_D(QTextDocument);
1105     switch (info) {
1106     case DocumentTitle:
1107         d->title = string;
1108         break;
1109     case DocumentUrl:
1110         d->url = string;
1111         break;
1112     }
1113 }
1114
1115 /*!
1116     Returns the plain text contained in the document. If you want
1117     formatting information use a QTextCursor instead.
1118
1119     \sa toHtml()
1120 */
1121 QString QTextDocument::toPlainText() const
1122 {
1123     Q_D(const QTextDocument);
1124     QString txt = d->plainText();
1125
1126     QChar *uc = txt.data();
1127     QChar *e = uc + txt.size();
1128
1129     for (; uc != e; ++uc) {
1130         switch (uc->unicode()) {
1131         case 0xfdd0: // QTextBeginningOfFrame
1132         case 0xfdd1: // QTextEndOfFrame
1133         case QChar::ParagraphSeparator:
1134         case QChar::LineSeparator:
1135             *uc = QLatin1Char('\n');
1136             break;
1137         case QChar::Nbsp:
1138             *uc = QLatin1Char(' ');
1139             break;
1140         default:
1141             ;
1142         }
1143     }
1144     return txt;
1145 }
1146
1147 /*!
1148     Replaces the entire contents of the document with the given plain
1149     \a text.
1150
1151     \sa setHtml()
1152 */
1153 void QTextDocument::setPlainText(const QString &text)
1154 {
1155     Q_D(QTextDocument);
1156     bool previousState = d->isUndoRedoEnabled();
1157     d->enableUndoRedo(false);
1158     d->beginEditBlock();
1159     d->clear();
1160     QTextCursor(this).insertText(text);
1161     d->endEditBlock();
1162     d->enableUndoRedo(previousState);
1163 }
1164
1165 /*!
1166     Replaces the entire contents of the document with the given
1167     HTML-formatted text in the \a html string.
1168
1169     The HTML formatting is respected as much as possible; for example,
1170     "<b>bold</b> text" will produce text where the first word has a font
1171     weight that gives it a bold appearance: "\b{bold} text".
1172
1173     \note It is the responsibility of the caller to make sure that the
1174     text is correctly decoded when a QString containing HTML is created
1175     and passed to setHtml().
1176
1177     \sa setPlainText(), {Supported HTML Subset}
1178 */
1179
1180 #ifndef QT_NO_TEXTHTMLPARSER
1181
1182 void QTextDocument::setHtml(const QString &html)
1183 {
1184     Q_D(QTextDocument);
1185     bool previousState = d->isUndoRedoEnabled();
1186     d->enableUndoRedo(false);
1187     d->beginEditBlock();
1188     d->clear();
1189     QTextHtmlImporter(this, html, QTextHtmlImporter::ImportToDocument).import();
1190     d->endEditBlock();
1191     d->enableUndoRedo(previousState);
1192 }
1193
1194 #endif // QT_NO_TEXTHTMLPARSER
1195
1196 /*!
1197     \enum QTextDocument::FindFlag
1198
1199     This enum describes the options available to QTextDocument's find function. The options
1200     can be OR-ed together from the following list:
1201
1202     \value FindBackward Search backwards instead of forwards.
1203     \value FindCaseSensitively By default find works case insensitive. Specifying this option
1204     changes the behaviour to a case sensitive find operation.
1205     \value FindWholeWords Makes find match only complete words.
1206 */
1207
1208 /*!
1209     \enum QTextDocument::MetaInformation
1210
1211     This enum describes the different types of meta information that can be
1212     added to a document.
1213
1214     \value DocumentTitle    The title of the document.
1215     \value DocumentUrl      The url of the document. The loadResource() function uses
1216                             this url as the base when loading relative resources.
1217
1218     \sa metaInformation(), setMetaInformation()
1219 */
1220
1221 /*!
1222     \fn QTextCursor QTextDocument::find(const QString &subString, int position, FindFlags options) const
1223
1224     \overload
1225
1226     Finds the next occurrence of the string, \a subString, in the document.
1227     The search starts at the given \a position, and proceeds forwards
1228     through the document unless specified otherwise in the search options.
1229     The \a options control the type of search performed.
1230
1231     Returns a cursor with the match selected if \a subString
1232     was found; otherwise returns a null cursor.
1233
1234     If the \a position is 0 (the default) the search begins from the beginning
1235     of the document; otherwise it begins at the specified position.
1236 */
1237 QTextCursor QTextDocument::find(const QString &subString, int from, FindFlags options) const
1238 {
1239     QRegExp expr(subString);
1240     expr.setPatternSyntax(QRegExp::FixedString);
1241     expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
1242
1243     return find(expr, from, options);
1244 }
1245
1246 /*!
1247     \fn QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &cursor, FindFlags options) const
1248
1249     Finds the next occurrence of the string, \a subString, in the document.
1250     The search starts at the position of the given \a cursor, and proceeds
1251     forwards through the document unless specified otherwise in the search
1252     options. The \a options control the type of search performed.
1253
1254     Returns a cursor with the match selected if \a subString was found; otherwise
1255     returns a null cursor.
1256
1257     If the given \a cursor has a selection, the search begins after the
1258     selection; otherwise it begins at the cursor's position.
1259
1260     By default the search is case-sensitive, and can match text anywhere in the
1261     document.
1262 */
1263 QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &from, FindFlags options) const
1264 {
1265     int pos = 0;
1266     if (!from.isNull()) {
1267         if (options & QTextDocument::FindBackward)
1268             pos = from.selectionStart();
1269         else
1270             pos = from.selectionEnd();
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, pos, options);
1277 }
1278
1279
1280 static bool findInBlock(const QTextBlock &block, const QRegExp &expression, int offset,
1281                         QTextDocument::FindFlags options, QTextCursor &cursor)
1282 {
1283     QRegExp expr(expression);
1284     QString text = block.text();
1285     text.replace(QChar::Nbsp, QLatin1Char(' '));
1286
1287     int idx = -1;
1288     while (offset >=0 && offset <= text.length()) {
1289         idx = (options & QTextDocument::FindBackward) ?
1290                expr.lastIndexIn(text, offset) : expr.indexIn(text, offset);
1291         if (idx == -1)
1292             return false;
1293
1294         if (options & QTextDocument::FindWholeWords) {
1295             const int start = idx;
1296             const int end = start + expr.matchedLength();
1297             if ((start != 0 && text.at(start - 1).isLetterOrNumber())
1298                 || (end != text.length() && text.at(end).isLetterOrNumber())) {
1299                 //if this is not a whole word, continue the search in the string
1300                 offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
1301                 idx = -1;
1302                 continue;
1303             }
1304         }
1305         //we have a hit, return the cursor for that.
1306         break;
1307     }
1308     if (idx == -1)
1309         return false;
1310     cursor = QTextCursor(block.docHandle(), block.position() + idx);
1311     cursor.setPosition(cursor.position() + expr.matchedLength(), QTextCursor::KeepAnchor);
1312     return true;
1313 }
1314
1315 /*!
1316     \fn QTextCursor QTextDocument::find(const QRegExp & expr, int position, FindFlags options) const
1317
1318     \overload
1319
1320     Finds the next occurrence, matching the regular expression, \a expr, in the document.
1321     The search starts at the given \a position, and proceeds forwards
1322     through the document unless specified otherwise in the search options.
1323     The \a options control the type of search performed. The FindCaseSensitively
1324     option is ignored for this overload, use QRegExp::caseSensitivity instead.
1325
1326     Returns a cursor with the match selected if a match was found; otherwise
1327     returns a null cursor.
1328
1329     If the \a position is 0 (the default) the search begins from the beginning
1330     of the document; otherwise it begins at the specified position.
1331 */
1332 QTextCursor QTextDocument::find(const QRegExp & expr, int from, FindFlags options) const
1333 {
1334     Q_D(const QTextDocument);
1335
1336     if (expr.isEmpty())
1337         return QTextCursor();
1338
1339     int pos = from;
1340     //the cursor is positioned between characters, so for a backward search
1341     //do not include the character given in the position.
1342     if (options & FindBackward) {
1343         --pos ;
1344         if(pos < 0)
1345             return QTextCursor();
1346     }
1347
1348     QTextCursor cursor;
1349     QTextBlock block = d->blocksFind(pos);
1350
1351     if (!(options & FindBackward)) {
1352        int blockOffset = qMax(0, pos - block.position());
1353         while (block.isValid()) {
1354             if (findInBlock(block, expr, blockOffset, options, cursor))
1355                 return cursor;
1356             blockOffset = 0;
1357             block = block.next();
1358         }
1359     } else {
1360         int blockOffset = pos - block.position();
1361         while (block.isValid()) {
1362             if (findInBlock(block, expr, blockOffset, options, cursor))
1363                 return cursor;
1364             block = block.previous();
1365             blockOffset = block.length() - 1;
1366         }
1367     }
1368
1369     return QTextCursor();
1370 }
1371
1372 /*!
1373     \fn QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &cursor, FindFlags options) const
1374
1375     Finds the next occurrence, matching the regular expression, \a expr, in the document.
1376     The search starts at the position of the given \a cursor, and proceeds
1377     forwards through the document unless specified otherwise in the search
1378     options. The \a options control the type of search performed. The FindCaseSensitively
1379     option is ignored for this overload, use QRegExp::caseSensitivity instead.
1380
1381     Returns a cursor with the match selected if a match was found; otherwise
1382     returns a null cursor.
1383
1384     If the given \a cursor has a selection, the search begins after the
1385     selection; otherwise it begins at the cursor's position.
1386
1387     By default the search is case-sensitive, and can match text anywhere in the
1388     document.
1389 */
1390 QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &from, FindFlags options) const
1391 {
1392     int pos = 0;
1393     if (!from.isNull()) {
1394         if (options & QTextDocument::FindBackward)
1395             pos = from.selectionStart();
1396         else
1397             pos = from.selectionEnd();
1398     }
1399     return find(expr, pos, options);
1400 }
1401
1402
1403 /*!
1404     \fn QTextObject *QTextDocument::createObject(const QTextFormat &format)
1405
1406     Creates and returns a new document object (a QTextObject), based
1407     on the given \a format.
1408
1409     QTextObjects will always get created through this method, so you
1410     must reimplement it if you use custom text objects inside your document.
1411 */
1412 QTextObject *QTextDocument::createObject(const QTextFormat &f)
1413 {
1414     QTextObject *obj = 0;
1415     if (f.isListFormat())
1416         obj = new QTextList(this);
1417     else if (f.isTableFormat())
1418         obj = new QTextTable(this);
1419     else if (f.isFrameFormat())
1420         obj = new QTextFrame(this);
1421
1422     return obj;
1423 }
1424
1425 /*!
1426     \internal
1427
1428     Returns the frame that contains the text cursor position \a pos.
1429 */
1430 QTextFrame *QTextDocument::frameAt(int pos) const
1431 {
1432     Q_D(const QTextDocument);
1433     return d->frameAt(pos);
1434 }
1435
1436 /*!
1437     Returns the document's root frame.
1438 */
1439 QTextFrame *QTextDocument::rootFrame() const
1440 {
1441     Q_D(const QTextDocument);
1442     return d->rootFrame();
1443 }
1444
1445 /*!
1446     Returns the text object associated with the given \a objectIndex.
1447 */
1448 QTextObject *QTextDocument::object(int objectIndex) const
1449 {
1450     Q_D(const QTextDocument);
1451     return d->objectForIndex(objectIndex);
1452 }
1453
1454 /*!
1455     Returns the text object associated with the format \a f.
1456 */
1457 QTextObject *QTextDocument::objectForFormat(const QTextFormat &f) const
1458 {
1459     Q_D(const QTextDocument);
1460     return d->objectForFormat(f);
1461 }
1462
1463
1464 /*!
1465     Returns the text block that contains the \a{pos}-th character.
1466 */
1467 QTextBlock QTextDocument::findBlock(int pos) const
1468 {
1469     Q_D(const QTextDocument);
1470     return QTextBlock(docHandle(), d->blockMap().findNode(pos));
1471 }
1472
1473 /*!
1474     \since 4.4
1475     Returns the text block with the specified \a blockNumber.
1476
1477     \sa QTextBlock::blockNumber()
1478 */
1479 QTextBlock QTextDocument::findBlockByNumber(int blockNumber) const
1480 {
1481     Q_D(const QTextDocument);
1482     return QTextBlock(docHandle(), d->blockMap().findNode(blockNumber, 1));
1483 }
1484
1485 /*!
1486     \since 4.5
1487     Returns the text block that contains the specified \a lineNumber.
1488
1489     \sa QTextBlock::firstLineNumber()
1490 */
1491 QTextBlock QTextDocument::findBlockByLineNumber(int lineNumber) const
1492 {
1493     Q_D(const QTextDocument);
1494     return QTextBlock(docHandle(), d->blockMap().findNode(lineNumber, 2));
1495 }
1496
1497 /*!
1498     Returns the document's first text block.
1499
1500     \sa firstBlock()
1501 */
1502 QTextBlock QTextDocument::begin() const
1503 {
1504     Q_D(const QTextDocument);
1505     return QTextBlock(docHandle(), d->blockMap().begin().n);
1506 }
1507
1508 /*!
1509     This function returns a block to test for the end of the document
1510     while iterating over it.
1511
1512     \snippet textdocumentendsnippet.cpp 0
1513
1514     The block returned is invalid and represents the block after the
1515     last block in the document. You can use lastBlock() to retrieve the
1516     last valid block of the document.
1517
1518     \sa lastBlock()
1519 */
1520 QTextBlock QTextDocument::end() const
1521 {
1522     return QTextBlock(docHandle(), 0);
1523 }
1524
1525 /*!
1526     \since 4.4
1527     Returns the document's first text block.
1528 */
1529 QTextBlock QTextDocument::firstBlock() const
1530 {
1531     Q_D(const QTextDocument);
1532     return QTextBlock(docHandle(), d->blockMap().begin().n);
1533 }
1534
1535 /*!
1536     \since 4.4
1537     Returns the document's last (valid) text block.
1538 */
1539 QTextBlock QTextDocument::lastBlock() const
1540 {
1541     Q_D(const QTextDocument);
1542     return QTextBlock(docHandle(), d->blockMap().last().n);
1543 }
1544
1545 /*!
1546     \property QTextDocument::pageSize
1547     \brief the page size that should be used for laying out the document
1548
1549     By default, for a newly-created, empty document, this property contains
1550     an undefined size.
1551
1552     \sa modificationChanged()
1553 */
1554
1555 void QTextDocument::setPageSize(const QSizeF &size)
1556 {
1557     Q_D(QTextDocument);
1558     d->pageSize = size;
1559     if (d->lout)
1560         d->lout->documentChanged(0, 0, d->length());
1561 }
1562
1563 QSizeF QTextDocument::pageSize() const
1564 {
1565     Q_D(const QTextDocument);
1566     return d->pageSize;
1567 }
1568
1569 /*!
1570   returns the number of pages in this document.
1571 */
1572 int QTextDocument::pageCount() const
1573 {
1574     return documentLayout()->pageCount();
1575 }
1576
1577 /*!
1578     Sets the default \a font to use in the document layout.
1579 */
1580 void QTextDocument::setDefaultFont(const QFont &font)
1581 {
1582     Q_D(QTextDocument);
1583     d->setDefaultFont(font);
1584     if (d->lout)
1585         d->lout->documentChanged(0, 0, d->length());
1586 }
1587
1588 /*!
1589     Returns the default font to be used in the document layout.
1590 */
1591 QFont QTextDocument::defaultFont() const
1592 {
1593     Q_D(const QTextDocument);
1594     return d->defaultFont();
1595 }
1596
1597 /*!
1598     \fn QTextDocument::modificationChanged(bool changed)
1599
1600     This signal is emitted whenever the content of the document
1601     changes in a way that affects the modification state. If \a
1602     changed is true, the document has been modified; otherwise it is
1603     false.
1604
1605     For example, calling setModified(false) on a document and then
1606     inserting text causes the signal to get emitted. If you undo that
1607     operation, causing the document to return to its original
1608     unmodified state, the signal will get emitted again.
1609 */
1610
1611 /*!
1612     \property QTextDocument::modified
1613     \brief whether the document has been modified by the user
1614
1615     By default, this property is false.
1616
1617     \sa modificationChanged()
1618 */
1619
1620 bool QTextDocument::isModified() const
1621 {
1622     return docHandle()->isModified();
1623 }
1624
1625 void QTextDocument::setModified(bool m)
1626 {
1627     docHandle()->setModified(m);
1628 }
1629
1630 #ifndef QT_NO_PRINTER
1631 static void printPage(int index, QPainter *painter, const QTextDocument *doc, const QRectF &body, const QPointF &pageNumberPos)
1632 {
1633     painter->save();
1634     painter->translate(body.left(), body.top() - (index - 1) * body.height());
1635     QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
1636
1637     QAbstractTextDocumentLayout *layout = doc->documentLayout();
1638     QAbstractTextDocumentLayout::PaintContext ctx;
1639
1640     painter->setClipRect(view);
1641     ctx.clip = view;
1642
1643     // don't use the system palette text as default text color, on HP/UX
1644     // for example that's white, and white text on white paper doesn't
1645     // look that nice
1646     ctx.palette.setColor(QPalette::Text, Qt::black);
1647
1648     layout->draw(painter, ctx);
1649
1650     if (!pageNumberPos.isNull()) {
1651         painter->setClipping(false);
1652         painter->setFont(QFont(doc->defaultFont()));
1653         const QString pageString = QString::number(index);
1654
1655         painter->drawText(qRound(pageNumberPos.x() - painter->fontMetrics().width(pageString)),
1656                           qRound(pageNumberPos.y() + view.top()),
1657                           pageString);
1658     }
1659
1660     painter->restore();
1661 }
1662
1663 /*!
1664     Prints the document to the given \a device. The QPageablePaintDevice must be
1665     set up before being used with this function.
1666
1667     This is only a convenience method to print the whole document to the printer.
1668
1669     If the document is already paginated through a specified height in the pageSize()
1670     property it is printed as-is.
1671
1672     If the document is not paginated, like for example a document used in a QTextEdit,
1673     then a temporary copy of the document is created and the copy is broken into
1674     multiple pages according to the size of the paint device's paperRect(). By default
1675     a 2 cm margin is set around the document contents. In addition the current page
1676     number is printed at the bottom of each page.
1677
1678     \sa QTextEdit::print()
1679 */
1680
1681 void QTextDocument::print(QPagedPaintDevice *printer) const
1682 {
1683     Q_D(const QTextDocument);
1684
1685     if (!printer)
1686         return;
1687
1688     bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
1689                              && d->pageSize.height() != INT_MAX;
1690
1691     QPagedPaintDevicePrivate *pd = QPagedPaintDevicePrivate::get(printer);
1692
1693     // ### set page size to paginated size?
1694     QPagedPaintDevice::Margins m = printer->margins();
1695     if (!documentPaginated && m.left == 0. && m.right == 0. && m.top == 0. && m.bottom == 0.) {
1696         m.left = m.right = m.top = m.bottom = 2.;
1697         printer->setMargins(m);
1698     }
1699     // ### use the margins correctly
1700
1701     QPainter p(printer);
1702
1703     // Check that there is a valid device to print to.
1704     if (!p.isActive())
1705         return;
1706
1707     const QTextDocument *doc = this;
1708     QScopedPointer<QTextDocument> clonedDoc;
1709     (void)doc->documentLayout(); // make sure that there is a layout
1710
1711     QRectF body = QRectF(QPointF(0, 0), d->pageSize);
1712     QPointF pageNumberPos;
1713
1714     if (documentPaginated) {
1715         qreal sourceDpiX = qt_defaultDpi();
1716         qreal sourceDpiY = sourceDpiX;
1717
1718         QPaintDevice *dev = doc->documentLayout()->paintDevice();
1719         if (dev) {
1720             sourceDpiX = dev->logicalDpiX();
1721             sourceDpiY = dev->logicalDpiY();
1722         }
1723
1724         const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
1725         const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
1726
1727         // scale to dpi
1728         p.scale(dpiScaleX, dpiScaleY);
1729
1730         QSizeF scaledPageSize = d->pageSize;
1731         scaledPageSize.rwidth() *= dpiScaleX;
1732         scaledPageSize.rheight() *= dpiScaleY;
1733
1734         const QSizeF printerPageSize(printer->width(), printer->height());
1735
1736         // scale to page
1737         p.scale(printerPageSize.width() / scaledPageSize.width(),
1738                 printerPageSize.height() / scaledPageSize.height());
1739     } else {
1740         doc = clone(const_cast<QTextDocument *>(this));
1741         clonedDoc.reset(const_cast<QTextDocument *>(doc));
1742
1743         for (QTextBlock srcBlock = firstBlock(), dstBlock = clonedDoc->firstBlock();
1744              srcBlock.isValid() && dstBlock.isValid();
1745              srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
1746             dstBlock.layout()->setAdditionalFormats(srcBlock.layout()->additionalFormats());
1747         }
1748
1749         QAbstractTextDocumentLayout *layout = doc->documentLayout();
1750         layout->setPaintDevice(p.device());
1751
1752         // copy the custom object handlers
1753         layout->d_func()->handlers = documentLayout()->d_func()->handlers;
1754
1755         int dpiy = p.device()->logicalDpiY();
1756         int margin = (int) ((2/2.54)*dpiy); // 2 cm margins
1757         QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
1758         fmt.setMargin(margin);
1759         doc->rootFrame()->setFrameFormat(fmt);
1760
1761         body = QRectF(0, 0, printer->width(), printer->height());
1762         pageNumberPos = QPointF(body.width() - margin,
1763                                 body.height() - margin
1764                                 + QFontMetrics(doc->defaultFont(), p.device()).ascent()
1765                                 + 5 * dpiy / 72.0);
1766         clonedDoc->setPageSize(body.size());
1767     }
1768
1769     int fromPage = pd->fromPage;
1770     int toPage = pd->toPage;
1771     bool ascending = true;
1772
1773     if (fromPage == 0 && toPage == 0) {
1774         fromPage = 1;
1775         toPage = doc->pageCount();
1776     }
1777     // paranoia check
1778     fromPage = qMax(1, fromPage);
1779     toPage = qMin(doc->pageCount(), toPage);
1780
1781     if (toPage < fromPage) {
1782         // if the user entered a page range outside the actual number
1783         // of printable pages, just return
1784         return;
1785     }
1786
1787 //    if (printer->pageOrder() == QPrinter::LastPageFirst) {
1788 //        int tmp = fromPage;
1789 //        fromPage = toPage;
1790 //        toPage = tmp;
1791 //        ascending = false;
1792 //    }
1793
1794     int page = fromPage;
1795     while (true) {
1796         printPage(page, &p, doc, body, pageNumberPos);
1797
1798         if (page == toPage)
1799             break;
1800
1801         if (ascending)
1802             ++page;
1803         else
1804             --page;
1805
1806         if (!printer->newPage())
1807             return;
1808     }
1809 }
1810 #endif
1811
1812 /*!
1813     \enum QTextDocument::ResourceType
1814
1815     This enum describes the types of resources that can be loaded by
1816     QTextDocument's loadResource() function.
1817
1818     \value HtmlResource  The resource contains HTML.
1819     \value ImageResource The resource contains image data.
1820                          Currently supported data types are QVariant::Pixmap and
1821                          QVariant::Image. If the corresponding variant is of type
1822                          QVariant::ByteArray then Qt attempts to load the image using
1823                          QImage::loadFromData. QVariant::Icon is currently not supported.
1824                          The icon needs to be converted to one of the supported types first,
1825                          for example using QIcon::pixmap.
1826     \value StyleSheetResource The resource contains CSS.
1827     \value UserResource  The first available value for user defined
1828                          resource types.
1829
1830     \sa loadResource()
1831 */
1832
1833 /*!
1834     Returns data of the specified \a type from the resource with the
1835     given \a name.
1836
1837     This function is called by the rich text engine to request data that isn't
1838     directly stored by QTextDocument, but still associated with it. For example,
1839     images are referenced indirectly by the name attribute of a QTextImageFormat
1840     object.
1841
1842     Resources are cached internally in the document. If a resource can
1843     not be found in the cache, loadResource is called to try to load
1844     the resource. loadResource should then use addResource to add the
1845     resource to the cache.
1846
1847     \sa QTextDocument::ResourceType
1848 */
1849 QVariant QTextDocument::resource(int type, const QUrl &name) const
1850 {
1851     Q_D(const QTextDocument);
1852     QVariant r = d->resources.value(name);
1853     if (!r.isValid()) {
1854         r = d->cachedResources.value(name);
1855         if (!r.isValid())
1856             r = const_cast<QTextDocument *>(this)->loadResource(type, name);
1857     }
1858     return r;
1859 }
1860
1861 /*!
1862     Adds the resource \a resource to the resource cache, using \a
1863     type and \a name as identifiers. \a type should be a value from
1864     QTextDocument::ResourceType.
1865
1866     For example, you can add an image as a resource in order to reference it
1867     from within the document:
1868
1869     \snippet textdocument-resources/main.cpp Adding a resource
1870
1871     The image can be inserted into the document using the QTextCursor API:
1872
1873     \snippet textdocument-resources/main.cpp Inserting an image with a cursor
1874
1875     Alternatively, you can insert images using the HTML \c img tag:
1876
1877     \snippet textdocument-resources/main.cpp Inserting an image using HTML
1878 */
1879 void QTextDocument::addResource(int type, const QUrl &name, const QVariant &resource)
1880 {
1881     Q_UNUSED(type);
1882     Q_D(QTextDocument);
1883     d->resources.insert(name, resource);
1884 }
1885
1886 /*!
1887     Loads data of the specified \a type from the resource with the
1888     given \a name.
1889
1890     This function is called by the rich text engine to request data that isn't
1891     directly stored by QTextDocument, but still associated with it. For example,
1892     images are referenced indirectly by the name attribute of a QTextImageFormat
1893     object.
1894
1895     When called by Qt, \a type is one of the values of
1896     QTextDocument::ResourceType.
1897
1898     If the QTextDocument is a child object of a QObject that has an invokable
1899     loadResource method such as QTextEdit, QTextBrowser
1900     or a QTextDocument itself then the default implementation tries
1901     to retrieve the data from the parent.
1902 */
1903 QVariant QTextDocument::loadResource(int type, const QUrl &name)
1904 {
1905     Q_D(QTextDocument);
1906     QVariant r;
1907
1908     QObject *p = parent();
1909     if (p) {
1910         const QMetaObject *me = p->metaObject();
1911         int index = me->indexOfMethod("loadResource(int,QUrl)");
1912         if (index >= 0) {
1913             QMetaMethod loader = me->method(index);
1914             loader.invoke(p, Q_RETURN_ARG(QVariant, r), Q_ARG(int, type), Q_ARG(QUrl, name));
1915         }
1916     }
1917
1918     // handle data: URLs
1919     if (r.isNull() && name.scheme().compare(QLatin1String("data"), Qt::CaseInsensitive) == 0) {
1920         QString mimetype;
1921         QByteArray payload;
1922         if (qDecodeDataUrl(name, mimetype, payload))
1923             r = payload;
1924     }
1925
1926     // if resource was not loaded try to load it here
1927     if (!qobject_cast<QTextDocument *>(p) && r.isNull() && name.isRelative()) {
1928         QUrl currentURL = d->url;
1929         QUrl resourceUrl = name;
1930
1931         // For the second case QUrl can merge "#someanchor" with "foo.html"
1932         // correctly to "foo.html#someanchor"
1933         if (!(currentURL.isRelative()
1934               || (currentURL.scheme() == QLatin1String("file")
1935                   && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
1936             || (name.hasFragment() && name.path().isEmpty())) {
1937             resourceUrl =  currentURL.resolved(name);
1938         } else {
1939             // this is our last resort when current url and new url are both relative
1940             // we try to resolve against the current working directory in the local
1941             // file system.
1942             QFileInfo fi(currentURL.toLocalFile());
1943             if (fi.exists()) {
1944                 resourceUrl =
1945                     QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
1946             } else if (currentURL.isEmpty()) {
1947                 resourceUrl.setScheme(QLatin1String("file"));
1948             }
1949         }
1950
1951         QString s = resourceUrl.toLocalFile();
1952         QFile f(s);
1953         if (!s.isEmpty() && f.open(QFile::ReadOnly)) {
1954             r = f.readAll();
1955             f.close();
1956         }
1957     }
1958
1959     if (!r.isNull()) {
1960         if (type == ImageResource && r.type() == QVariant::ByteArray) {
1961             if (qApp->thread() != QThread::currentThread()) {
1962                 // must use images in non-GUI threads
1963                 QImage image;
1964                 image.loadFromData(r.toByteArray());
1965                 if (!image.isNull())
1966                     r = image;
1967             } else {
1968                 QPixmap pm;
1969                 pm.loadFromData(r.toByteArray());
1970                 if (!pm.isNull())
1971                     r = pm;
1972             }
1973         }
1974         d->cachedResources.insert(name, r);
1975     }
1976     return r;
1977 }
1978
1979 static QTextFormat formatDifference(const QTextFormat &from, const QTextFormat &to)
1980 {
1981     QTextFormat diff = to;
1982
1983     const QMap<int, QVariant> props = to.properties();
1984     for (QMap<int, QVariant>::ConstIterator it = props.begin(), end = props.end();
1985          it != end; ++it)
1986         if (it.value() == from.property(it.key()))
1987             diff.clearProperty(it.key());
1988
1989     return diff;
1990 }
1991
1992 QTextHtmlExporter::QTextHtmlExporter(const QTextDocument *_doc)
1993     : doc(_doc), fragmentMarkers(false)
1994 {
1995     const QFont defaultFont = doc->defaultFont();
1996     defaultCharFormat.setFont(defaultFont);
1997     // don't export those for the default font since we cannot turn them off with CSS
1998     defaultCharFormat.clearProperty(QTextFormat::FontUnderline);
1999     defaultCharFormat.clearProperty(QTextFormat::FontOverline);
2000     defaultCharFormat.clearProperty(QTextFormat::FontStrikeOut);
2001     defaultCharFormat.clearProperty(QTextFormat::TextUnderlineStyle);
2002 }
2003
2004 /*!
2005     Returns the document in HTML format. The conversion may not be
2006     perfect, especially for complex documents, due to the limitations
2007     of HTML.
2008 */
2009 QString QTextHtmlExporter::toHtml(const QByteArray &encoding, ExportMode mode)
2010 {
2011     html = QLatin1String("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
2012             "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
2013             "<html><head><meta name=\"qrichtext\" content=\"1\" />");
2014     html.reserve(doc->docHandle()->length());
2015
2016     fragmentMarkers = (mode == ExportFragment);
2017
2018     if (!encoding.isEmpty())
2019         html += QString::fromLatin1("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\" />").arg(QString::fromLatin1(encoding));
2020
2021     QString title  = doc->metaInformation(QTextDocument::DocumentTitle);
2022     if (!title.isEmpty())
2023         html += QString::fromLatin1("<title>") + title + QString::fromLatin1("</title>");
2024     html += QLatin1String("<style type=\"text/css\">\n");
2025     html += QLatin1String("p, li { white-space: pre-wrap; }\n");
2026     html += QLatin1String("</style>");
2027     html += QLatin1String("</head><body");
2028
2029     if (mode == ExportEntireDocument) {
2030         html += QLatin1String(" style=\"");
2031
2032         emitFontFamily(defaultCharFormat.fontFamily());
2033
2034         if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) {
2035             html += QLatin1String(" font-size:");
2036             html += QString::number(defaultCharFormat.fontPointSize());
2037             html += QLatin1String("pt;");
2038         } else if (defaultCharFormat.hasProperty(QTextFormat::FontPixelSize)) {
2039             html += QLatin1String(" font-size:");
2040             html += QString::number(defaultCharFormat.intProperty(QTextFormat::FontPixelSize));
2041             html += QLatin1String("px;");
2042         }
2043
2044         html += QLatin1String(" font-weight:");
2045         html += QString::number(defaultCharFormat.fontWeight() * 8);
2046         html += QLatin1Char(';');
2047
2048         html += QLatin1String(" font-style:");
2049         html += (defaultCharFormat.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2050         html += QLatin1Char(';');
2051
2052         // do not set text-decoration on the default font since those values are /always/ propagated
2053         // and cannot be turned off with CSS
2054
2055         html += QLatin1Char('\"');
2056
2057         const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2058         emitBackgroundAttribute(fmt);
2059
2060     } else {
2061         defaultCharFormat = QTextCharFormat();
2062     }
2063     html += QLatin1Char('>');
2064
2065     QTextFrameFormat rootFmt = doc->rootFrame()->frameFormat();
2066     rootFmt.clearProperty(QTextFormat::BackgroundBrush);
2067
2068     QTextFrameFormat defaultFmt;
2069     defaultFmt.setMargin(doc->documentMargin());
2070
2071     if (rootFmt == defaultFmt)
2072         emitFrame(doc->rootFrame()->begin());
2073     else
2074         emitTextFrame(doc->rootFrame());
2075
2076     html += QLatin1String("</body></html>");
2077     return html;
2078 }
2079
2080 void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &value)
2081 {
2082     html += QLatin1Char(' ');
2083     html += QLatin1String(attribute);
2084     html += QLatin1String("=\"");
2085     html += value.toHtmlEscaped();
2086     html += QLatin1Char('"');
2087 }
2088
2089 bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
2090 {
2091     bool attributesEmitted = false;
2092
2093     {
2094         const QString family = format.fontFamily();
2095         if (!family.isEmpty() && family != defaultCharFormat.fontFamily()) {
2096             emitFontFamily(family);
2097             attributesEmitted = true;
2098         }
2099     }
2100
2101     if (format.hasProperty(QTextFormat::FontPointSize)
2102         && format.fontPointSize() != defaultCharFormat.fontPointSize()) {
2103         html += QLatin1String(" font-size:");
2104         html += QString::number(format.fontPointSize());
2105         html += QLatin1String("pt;");
2106         attributesEmitted = true;
2107     } else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) {
2108         static const char * const sizeNames[] = {
2109             "small", "medium", "large", "x-large", "xx-large"
2110         };
2111         const char *name = 0;
2112         const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1;
2113         if (idx >= 0 && idx <= 4) {
2114             name = sizeNames[idx];
2115         }
2116         if (name) {
2117             html += QLatin1String(" font-size:");
2118             html += QLatin1String(name);
2119             html += QLatin1Char(';');
2120             attributesEmitted = true;
2121         }
2122     } else if (format.hasProperty(QTextFormat::FontPixelSize)) {
2123         html += QLatin1String(" font-size:");
2124         html += QString::number(format.intProperty(QTextFormat::FontPixelSize));
2125         html += QLatin1String("px;");
2126     }
2127
2128     if (format.hasProperty(QTextFormat::FontWeight)
2129         && format.fontWeight() != defaultCharFormat.fontWeight()) {
2130         html += QLatin1String(" font-weight:");
2131         html += QString::number(format.fontWeight() * 8);
2132         html += QLatin1Char(';');
2133         attributesEmitted = true;
2134     }
2135
2136     if (format.hasProperty(QTextFormat::FontItalic)
2137         && format.fontItalic() != defaultCharFormat.fontItalic()) {
2138         html += QLatin1String(" font-style:");
2139         html += (format.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
2140         html += QLatin1Char(';');
2141         attributesEmitted = true;
2142     }
2143
2144     QLatin1String decorationTag(" text-decoration:");
2145     html += decorationTag;
2146     bool hasDecoration = false;
2147     bool atLeastOneDecorationSet = false;
2148
2149     if ((format.hasProperty(QTextFormat::FontUnderline) || format.hasProperty(QTextFormat::TextUnderlineStyle))
2150         && format.fontUnderline() != defaultCharFormat.fontUnderline()) {
2151         hasDecoration = true;
2152         if (format.fontUnderline()) {
2153             html += QLatin1String(" underline");
2154             atLeastOneDecorationSet = true;
2155         }
2156     }
2157
2158     if (format.hasProperty(QTextFormat::FontOverline)
2159         && format.fontOverline() != defaultCharFormat.fontOverline()) {
2160         hasDecoration = true;
2161         if (format.fontOverline()) {
2162             html += QLatin1String(" overline");
2163             atLeastOneDecorationSet = true;
2164         }
2165     }
2166
2167     if (format.hasProperty(QTextFormat::FontStrikeOut)
2168         && format.fontStrikeOut() != defaultCharFormat.fontStrikeOut()) {
2169         hasDecoration = true;
2170         if (format.fontStrikeOut()) {
2171             html += QLatin1String(" line-through");
2172             atLeastOneDecorationSet = true;
2173         }
2174     }
2175
2176     if (hasDecoration) {
2177         if (!atLeastOneDecorationSet)
2178             html += QLatin1String("none");
2179         html += QLatin1Char(';');
2180         attributesEmitted = true;
2181     } else {
2182         html.chop(qstrlen(decorationTag.latin1()));
2183     }
2184
2185     if (format.foreground() != defaultCharFormat.foreground()
2186         && format.foreground().style() != Qt::NoBrush) {
2187         html += QLatin1String(" color:");
2188         html += format.foreground().color().name();
2189         html += QLatin1Char(';');
2190         attributesEmitted = true;
2191     }
2192
2193     if (format.background() != defaultCharFormat.background()
2194         && format.background().style() == Qt::SolidPattern) {
2195         html += QLatin1String(" background-color:");
2196         html += format.background().color().name();
2197         html += QLatin1Char(';');
2198         attributesEmitted = true;
2199     }
2200
2201     if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()
2202         && format.verticalAlignment() != QTextCharFormat::AlignNormal)
2203     {
2204         html += QLatin1String(" vertical-align:");
2205
2206         QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2207         if (valign == QTextCharFormat::AlignSubScript)
2208             html += QLatin1String("sub");
2209         else if (valign == QTextCharFormat::AlignSuperScript)
2210             html += QLatin1String("super");
2211         else if (valign == QTextCharFormat::AlignMiddle)
2212             html += QLatin1String("middle");
2213         else if (valign == QTextCharFormat::AlignTop)
2214             html += QLatin1String("top");
2215         else if (valign == QTextCharFormat::AlignBottom)
2216             html += QLatin1String("bottom");
2217
2218         html += QLatin1Char(';');
2219         attributesEmitted = true;
2220     }
2221
2222     if (format.fontCapitalization() != QFont::MixedCase) {
2223         const QFont::Capitalization caps = format.fontCapitalization();
2224         if (caps == QFont::AllUppercase)
2225             html += QLatin1String(" text-transform:uppercase;");
2226         else if (caps == QFont::AllLowercase)
2227             html += QLatin1String(" text-transform:lowercase;");
2228         else if (caps == QFont::SmallCaps)
2229             html += QLatin1String(" font-variant:small-caps;");
2230         attributesEmitted = true;
2231     }
2232
2233     if (format.fontWordSpacing() != 0.0) {
2234         html += QLatin1String(" word-spacing:");
2235         html += QString::number(format.fontWordSpacing());
2236         html += QLatin1String("px;");
2237         attributesEmitted = true;
2238     }
2239
2240     return attributesEmitted;
2241 }
2242
2243 void QTextHtmlExporter::emitTextLength(const char *attribute, const QTextLength &length)
2244 {
2245     if (length.type() == QTextLength::VariableLength) // default
2246         return;
2247
2248     html += QLatin1Char(' ');
2249     html += QLatin1String(attribute);
2250     html += QLatin1String("=\"");
2251     html += QString::number(length.rawValue());
2252
2253     if (length.type() == QTextLength::PercentageLength)
2254         html += QLatin1String("%\"");
2255     else
2256         html += QLatin1Char('\"');
2257 }
2258
2259 void QTextHtmlExporter::emitAlignment(Qt::Alignment align)
2260 {
2261     if (align & Qt::AlignLeft)
2262         return;
2263     else if (align & Qt::AlignRight)
2264         html += QLatin1String(" align=\"right\"");
2265     else if (align & Qt::AlignHCenter)
2266         html += QLatin1String(" align=\"center\"");
2267     else if (align & Qt::AlignJustify)
2268         html += QLatin1String(" align=\"justify\"");
2269 }
2270
2271 void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
2272 {
2273     if (pos == QTextFrameFormat::InFlow)
2274         return;
2275
2276     if (mode == EmitStyleTag)
2277         html += QLatin1String(" style=\"float:");
2278     else
2279         html += QLatin1String(" float:");
2280
2281     if (pos == QTextFrameFormat::FloatLeft)
2282         html += QLatin1String(" left;");
2283     else if (pos == QTextFrameFormat::FloatRight)
2284         html += QLatin1String(" right;");
2285     else
2286         Q_ASSERT_X(0, "QTextHtmlExporter::emitFloatStyle()", "pos should be a valid enum type");
2287
2288     if (mode == EmitStyleTag)
2289         html += QLatin1Char('\"');
2290 }
2291
2292 void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style)
2293 {
2294     Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
2295
2296     html += QLatin1String(" border-style:");
2297
2298     switch (style) {
2299     case QTextFrameFormat::BorderStyle_None:
2300         html += QLatin1String("none");
2301         break;
2302     case QTextFrameFormat::BorderStyle_Dotted:
2303         html += QLatin1String("dotted");
2304         break;
2305     case QTextFrameFormat::BorderStyle_Dashed:
2306         html += QLatin1String("dashed");
2307         break;
2308     case QTextFrameFormat::BorderStyle_Solid:
2309         html += QLatin1String("solid");
2310         break;
2311     case QTextFrameFormat::BorderStyle_Double:
2312         html += QLatin1String("double");
2313         break;
2314     case QTextFrameFormat::BorderStyle_DotDash:
2315         html += QLatin1String("dot-dash");
2316         break;
2317     case QTextFrameFormat::BorderStyle_DotDotDash:
2318         html += QLatin1String("dot-dot-dash");
2319         break;
2320     case QTextFrameFormat::BorderStyle_Groove:
2321         html += QLatin1String("groove");
2322         break;
2323     case QTextFrameFormat::BorderStyle_Ridge:
2324         html += QLatin1String("ridge");
2325         break;
2326     case QTextFrameFormat::BorderStyle_Inset:
2327         html += QLatin1String("inset");
2328         break;
2329     case QTextFrameFormat::BorderStyle_Outset:
2330         html += QLatin1String("outset");
2331         break;
2332     default:
2333         Q_ASSERT(false);
2334         break;
2335     };
2336
2337     html += QLatin1Char(';');
2338 }
2339
2340 void QTextHtmlExporter::emitPageBreakPolicy(QTextFormat::PageBreakFlags policy)
2341 {
2342     if (policy & QTextFormat::PageBreak_AlwaysBefore)
2343         html += QLatin1String(" page-break-before:always;");
2344
2345     if (policy & QTextFormat::PageBreak_AlwaysAfter)
2346         html += QLatin1String(" page-break-after:always;");
2347 }
2348
2349 void QTextHtmlExporter::emitFontFamily(const QString &family)
2350 {
2351     html += QLatin1String(" font-family:");
2352
2353     QLatin1String quote("\'");
2354     if (family.contains(QLatin1Char('\'')))
2355         quote = QLatin1String("&quot;");
2356
2357     html += quote;
2358     html += family.toHtmlEscaped();
2359     html += quote;
2360     html += QLatin1Char(';');
2361 }
2362
2363 void QTextHtmlExporter::emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right)
2364 {
2365     html += QLatin1String(" margin-top:");
2366     html += top;
2367     html += QLatin1String("px;");
2368
2369     html += QLatin1String(" margin-bottom:");
2370     html += bottom;
2371     html += QLatin1String("px;");
2372
2373     html += QLatin1String(" margin-left:");
2374     html += left;
2375     html += QLatin1String("px;");
2376
2377     html += QLatin1String(" margin-right:");
2378     html += right;
2379     html += QLatin1String("px;");
2380 }
2381
2382 void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
2383 {
2384     const QTextCharFormat format = fragment.charFormat();
2385
2386     bool closeAnchor = false;
2387
2388     if (format.isAnchor()) {
2389         const QString name = format.anchorName();
2390         if (!name.isEmpty()) {
2391             html += QLatin1String("<a name=\"");
2392             html += name.toHtmlEscaped();
2393             html += QLatin1String("\"></a>");
2394         }
2395         const QString href = format.anchorHref();
2396         if (!href.isEmpty()) {
2397             html += QLatin1String("<a href=\"");
2398             html += href.toHtmlEscaped();
2399             html += QLatin1String("\">");
2400             closeAnchor = true;
2401         }
2402     }
2403
2404     QString txt = fragment.text();
2405     const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
2406     const bool isImage = isObject && format.isImageFormat();
2407
2408     QLatin1String styleTag("<span style=\"");
2409     html += styleTag;
2410
2411     bool attributesEmitted = false;
2412     if (!isImage)
2413         attributesEmitted = emitCharFormatStyle(format);
2414     if (attributesEmitted)
2415         html += QLatin1String("\">");
2416     else
2417         html.chop(qstrlen(styleTag.latin1()));
2418
2419     if (isObject) {
2420         for (int i = 0; isImage && i < txt.length(); ++i) {
2421             QTextImageFormat imgFmt = format.toImageFormat();
2422
2423             html += QLatin1String("<img");
2424
2425             if (imgFmt.hasProperty(QTextFormat::ImageName))
2426                 emitAttribute("src", imgFmt.name());
2427
2428             if (imgFmt.hasProperty(QTextFormat::ImageWidth))
2429                 emitAttribute("width", QString::number(imgFmt.width()));
2430
2431             if (imgFmt.hasProperty(QTextFormat::ImageHeight))
2432                 emitAttribute("height", QString::number(imgFmt.height()));
2433
2434             if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
2435                 html += QLatin1String(" style=\"vertical-align: middle;\"");
2436             else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
2437                 html += QLatin1String(" style=\"vertical-align: top;\"");
2438
2439             if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
2440                 emitFloatStyle(imageFrame->frameFormat().position());
2441
2442             html += QLatin1String(" />");
2443         }
2444     } else {
2445         Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
2446
2447         txt = txt.toHtmlEscaped();
2448
2449         // split for [\n{LineSeparator}]
2450         QString forcedLineBreakRegExp = QString::fromLatin1("[\\na]");
2451         forcedLineBreakRegExp[3] = QChar::LineSeparator;
2452
2453         const QStringList lines = txt.split(QRegExp(forcedLineBreakRegExp));
2454         for (int i = 0; i < lines.count(); ++i) {
2455             if (i > 0)
2456                 html += QLatin1String("<br />"); // space on purpose for compatibility with Netscape, Lynx & Co.
2457             html += lines.at(i);
2458         }
2459     }
2460
2461     if (attributesEmitted)
2462         html += QLatin1String("</span>");
2463
2464     if (closeAnchor)
2465         html += QLatin1String("</a>");
2466 }
2467
2468 static bool isOrderedList(int style)
2469 {
2470     return style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
2471            || style == QTextListFormat::ListUpperAlpha
2472            || style == QTextListFormat::ListUpperRoman
2473            || style == QTextListFormat::ListLowerRoman
2474            ;
2475 }
2476
2477 void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
2478 {
2479     QTextBlockFormat format = block.blockFormat();
2480     emitAlignment(format.alignment());
2481
2482     // assume default to not bloat the html too much
2483     // html += QLatin1String(" dir='ltr'");
2484     if (block.textDirection() == Qt::RightToLeft)
2485         html += QLatin1String(" dir='rtl'");
2486
2487     QLatin1String style(" style=\"");
2488     html += style;
2489
2490     const bool emptyBlock = block.begin().atEnd();
2491     if (emptyBlock) {
2492         html += QLatin1String("-qt-paragraph-type:empty;");
2493     }
2494
2495     emitMargins(QString::number(format.topMargin()),
2496                 QString::number(format.bottomMargin()),
2497                 QString::number(format.leftMargin()),
2498                 QString::number(format.rightMargin()));
2499
2500     html += QLatin1String(" -qt-block-indent:");
2501     html += QString::number(format.indent());
2502     html += QLatin1Char(';');
2503
2504     html += QLatin1String(" text-indent:");
2505     html += QString::number(format.textIndent());
2506     html += QLatin1String("px;");
2507
2508     if (block.userState() != -1) {
2509         html += QLatin1String(" -qt-user-state:");
2510         html += QString::number(block.userState());
2511         html += QLatin1Char(';');
2512     }
2513
2514     emitPageBreakPolicy(format.pageBreakPolicy());
2515
2516     QTextCharFormat diff;
2517     if (emptyBlock) { // only print character properties when we don't expect them to be repeated by actual text in the parag
2518         const QTextCharFormat blockCharFmt = block.charFormat();
2519         diff = formatDifference(defaultCharFormat, blockCharFmt).toCharFormat();
2520     }
2521
2522     diff.clearProperty(QTextFormat::BackgroundBrush);
2523     if (format.hasProperty(QTextFormat::BackgroundBrush)) {
2524         QBrush bg = format.background();
2525         if (bg.style() != Qt::NoBrush)
2526             diff.setProperty(QTextFormat::BackgroundBrush, format.property(QTextFormat::BackgroundBrush));
2527     }
2528
2529     if (!diff.properties().isEmpty())
2530         emitCharFormatStyle(diff);
2531
2532     html += QLatin1Char('"');
2533
2534 }
2535
2536 void QTextHtmlExporter::emitBlock(const QTextBlock &block)
2537 {
2538     if (block.begin().atEnd()) {
2539         // ### HACK, remove once QTextFrame::Iterator is fixed
2540         int p = block.position();
2541         if (p > 0)
2542             --p;
2543         QTextDocumentPrivate::FragmentIterator frag = doc->docHandle()->find(p);
2544         QChar ch = doc->docHandle()->buffer().at(frag->stringPosition);
2545         if (ch == QTextBeginningOfFrame
2546             || ch == QTextEndOfFrame)
2547             return;
2548     }
2549
2550     html += QLatin1Char('\n');
2551
2552     // save and later restore, in case we 'change' the default format by
2553     // emitting block char format information
2554     QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2555
2556     QTextList *list = block.textList();
2557     if (list) {
2558         if (list->itemNumber(block) == 0) { // first item? emit <ul> or appropriate
2559             const QTextListFormat format = list->format();
2560             const int style = format.style();
2561             switch (style) {
2562                 case QTextListFormat::ListDecimal: html += QLatin1String("<ol"); break;
2563                 case QTextListFormat::ListDisc: html += QLatin1String("<ul"); break;
2564                 case QTextListFormat::ListCircle: html += QLatin1String("<ul type=\"circle\""); break;
2565                 case QTextListFormat::ListSquare: html += QLatin1String("<ul type=\"square\""); break;
2566                 case QTextListFormat::ListLowerAlpha: html += QLatin1String("<ol type=\"a\""); break;
2567                 case QTextListFormat::ListUpperAlpha: html += QLatin1String("<ol type=\"A\""); break;
2568                 case QTextListFormat::ListLowerRoman: html += QLatin1String("<ol type=\"i\""); break;
2569                 case QTextListFormat::ListUpperRoman: html += QLatin1String("<ol type=\"I\""); break;
2570                 default: html += QLatin1String("<ul"); // ### should not happen
2571             }
2572
2573             QString styleString = QString::fromLatin1("margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;");
2574
2575             if (format.hasProperty(QTextFormat::ListIndent)) {
2576                 styleString += QLatin1String(" -qt-list-indent: ");
2577                 styleString += QString::number(format.indent());
2578                 styleString += QLatin1Char(';');
2579             }
2580
2581             if (format.hasProperty(QTextFormat::ListNumberPrefix)) {
2582                 QString numberPrefix = format.numberPrefix();
2583                 numberPrefix.replace(QLatin1Char('"'), QLatin1String("\\22"));
2584                 numberPrefix.replace(QLatin1Char('\''), QLatin1String("\\27")); // FIXME: There's a problem in the CSS parser the prevents this from being correctly restored
2585                 styleString += QLatin1String(" -qt-list-number-prefix: ");
2586                 styleString += QLatin1Char('\'');
2587                 styleString += numberPrefix;
2588                 styleString += QLatin1Char('\'');
2589                 styleString += QLatin1Char(';');
2590             }
2591
2592             if (format.hasProperty(QTextFormat::ListNumberSuffix)) {
2593                 if (format.numberSuffix() != QLatin1String(".")) { // this is our default
2594                     QString numberSuffix = format.numberSuffix();
2595                     numberSuffix.replace(QLatin1Char('"'), QLatin1String("\\22"));
2596                     numberSuffix.replace(QLatin1Char('\''), QLatin1String("\\27")); // see above
2597                     styleString += QLatin1String(" -qt-list-number-suffix: ");
2598                     styleString += QLatin1Char('\'');
2599                     styleString += numberSuffix;
2600                     styleString += QLatin1Char('\'');
2601                     styleString += QLatin1Char(';');
2602                 }
2603             }
2604
2605             html += QLatin1String(" style=\"");
2606             html += styleString;
2607             html += QLatin1String("\">");
2608         }
2609
2610         html += QLatin1String("<li");
2611
2612         const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
2613         if (!blockFmt.properties().isEmpty()) {
2614             html += QLatin1String(" style=\"");
2615             emitCharFormatStyle(blockFmt);
2616             html += QLatin1Char('\"');
2617
2618             defaultCharFormat.merge(block.charFormat());
2619         }
2620     }
2621
2622     const QTextBlockFormat blockFormat = block.blockFormat();
2623     if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
2624         html += QLatin1String("<hr");
2625
2626         QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
2627         if (width.type() != QTextLength::VariableLength)
2628             emitTextLength("width", width);
2629         else
2630             html += QLatin1Char(' ');
2631
2632         html += QLatin1String("/>");
2633         return;
2634     }
2635
2636     const bool pre = blockFormat.nonBreakableLines();
2637     if (pre) {
2638         if (list)
2639             html += QLatin1Char('>');
2640         html += QLatin1String("<pre");
2641     } else if (!list) {
2642         html += QLatin1String("<p");
2643     }
2644
2645     emitBlockAttributes(block);
2646
2647     html += QLatin1Char('>');
2648     if (block.begin().atEnd())
2649         html += QLatin1String("<br />");
2650
2651     QTextBlock::Iterator it = block.begin();
2652     if (fragmentMarkers && !it.atEnd() && block == doc->begin())
2653         html += QLatin1String("<!--StartFragment-->");
2654
2655     for (; !it.atEnd(); ++it)
2656         emitFragment(it.fragment());
2657
2658     if (fragmentMarkers && block.position() + block.length() == doc->docHandle()->length())
2659         html += QLatin1String("<!--EndFragment-->");
2660
2661     if (pre)
2662         html += QLatin1String("</pre>");
2663     else if (list)
2664         html += QLatin1String("</li>");
2665     else
2666         html += QLatin1String("</p>");
2667
2668     if (list) {
2669         if (list->itemNumber(block) == list->count() - 1) { // last item? close list
2670             if (isOrderedList(list->format().style()))
2671                 html += QLatin1String("</ol>");
2672             else
2673                 html += QLatin1String("</ul>");
2674         }
2675     }
2676
2677     defaultCharFormat = oldDefaultCharFormat;
2678 }
2679
2680 extern bool qHasPixmapTexture(const QBrush& brush);
2681
2682 QString QTextHtmlExporter::findUrlForImage(const QTextDocument *doc, qint64 cacheKey, bool isPixmap)
2683 {
2684     QString url;
2685     if (!doc)
2686         return url;
2687
2688     if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent()))
2689         return findUrlForImage(parent, cacheKey, isPixmap);
2690
2691     if (doc && doc->docHandle()) {
2692         QTextDocumentPrivate *priv = doc->docHandle();
2693         QMap<QUrl, QVariant>::const_iterator it = priv->cachedResources.constBegin();
2694         for (; it != priv->cachedResources.constEnd(); ++it) {
2695
2696             const QVariant &v = it.value();
2697             if (v.type() == QVariant::Image && !isPixmap) {
2698                 if (qvariant_cast<QImage>(v).cacheKey() == cacheKey)
2699                     break;
2700             }
2701
2702             if (v.type() == QVariant::Pixmap && isPixmap) {
2703                 if (qvariant_cast<QPixmap>(v).cacheKey() == cacheKey)
2704                     break;
2705             }
2706         }
2707
2708         if (it != priv->cachedResources.constEnd())
2709             url = it.key().toString();
2710     }
2711
2712     return url;
2713 }
2714
2715 void QTextDocumentPrivate::mergeCachedResources(const QTextDocumentPrivate *priv)
2716 {
2717     if (!priv)
2718         return;
2719
2720     cachedResources.unite(priv->cachedResources);
2721 }
2722
2723 void QTextHtmlExporter::emitBackgroundAttribute(const QTextFormat &format)
2724 {
2725     if (format.hasProperty(QTextFormat::BackgroundImageUrl)) {
2726         QString url = format.property(QTextFormat::BackgroundImageUrl).toString();
2727         emitAttribute("background", url);
2728     } else {
2729         const QBrush &brush = format.background();
2730         if (brush.style() == Qt::SolidPattern) {
2731             emitAttribute("bgcolor", brush.color().name());
2732         } else if (brush.style() == Qt::TexturePattern) {
2733             const bool isPixmap = qHasPixmapTexture(brush);
2734             const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
2735
2736             const QString url = findUrlForImage(doc, cacheKey, isPixmap);
2737
2738             if (!url.isEmpty())
2739                 emitAttribute("background", url);
2740         }
2741     }
2742 }
2743
2744 void QTextHtmlExporter::emitTable(const QTextTable *table)
2745 {
2746     QTextTableFormat format = table->format();
2747
2748     html += QLatin1String("\n<table");
2749
2750     if (format.hasProperty(QTextFormat::FrameBorder))
2751         emitAttribute("border", QString::number(format.border()));
2752
2753     emitFrameStyle(format, TableFrame);
2754
2755     emitAlignment(format.alignment());
2756     emitTextLength("width", format.width());
2757
2758     if (format.hasProperty(QTextFormat::TableCellSpacing))
2759         emitAttribute("cellspacing", QString::number(format.cellSpacing()));
2760     if (format.hasProperty(QTextFormat::TableCellPadding))
2761         emitAttribute("cellpadding", QString::number(format.cellPadding()));
2762
2763     emitBackgroundAttribute(format);
2764
2765     html += QLatin1Char('>');
2766
2767     const int rows = table->rows();
2768     const int columns = table->columns();
2769
2770     QVector<QTextLength> columnWidths = format.columnWidthConstraints();
2771     if (columnWidths.isEmpty()) {
2772         columnWidths.resize(columns);
2773         columnWidths.fill(QTextLength());
2774     }
2775     Q_ASSERT(columnWidths.count() == columns);
2776
2777     QVarLengthArray<bool> widthEmittedForColumn(columns);
2778     for (int i = 0; i < columns; ++i)
2779         widthEmittedForColumn[i] = false;
2780
2781     const int headerRowCount = qMin(format.headerRowCount(), rows);
2782     if (headerRowCount > 0)
2783         html += QLatin1String("<thead>");
2784
2785     for (int row = 0; row < rows; ++row) {
2786         html += QLatin1String("\n<tr>");
2787
2788         for (int col = 0; col < columns; ++col) {
2789             const QTextTableCell cell = table->cellAt(row, col);
2790
2791             // for col/rowspans
2792             if (cell.row() != row)
2793                 continue;
2794
2795             if (cell.column() != col)
2796                 continue;
2797
2798             html += QLatin1String("\n<td");
2799
2800             if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
2801                 emitTextLength("width", columnWidths.at(col));
2802                 widthEmittedForColumn[col] = true;
2803             }
2804
2805             if (cell.columnSpan() > 1)
2806                 emitAttribute("colspan", QString::number(cell.columnSpan()));
2807
2808             if (cell.rowSpan() > 1)
2809                 emitAttribute("rowspan", QString::number(cell.rowSpan()));
2810
2811             const QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
2812             emitBackgroundAttribute(cellFormat);
2813
2814             QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
2815
2816             QTextCharFormat::VerticalAlignment valign = cellFormat.verticalAlignment();
2817
2818             QString styleString;
2819             if (valign >= QTextCharFormat::AlignMiddle && valign <= QTextCharFormat::AlignBottom) {
2820                 styleString += QLatin1String(" vertical-align:");
2821                 switch (valign) {
2822                 case QTextCharFormat::AlignMiddle:
2823                     styleString += QLatin1String("middle");
2824                     break;
2825                 case QTextCharFormat::AlignTop:
2826                     styleString += QLatin1String("top");
2827                     break;
2828                 case QTextCharFormat::AlignBottom:
2829                     styleString += QLatin1String("bottom");
2830                     break;
2831                 default:
2832                     break;
2833                 }
2834                 styleString += QLatin1Char(';');
2835
2836                 QTextCharFormat temp;
2837                 temp.setVerticalAlignment(valign);
2838                 defaultCharFormat.merge(temp);
2839             }
2840
2841             if (cellFormat.hasProperty(QTextFormat::TableCellLeftPadding))
2842                 styleString += QLatin1String(" padding-left:") + QString::number(cellFormat.leftPadding()) + QLatin1Char(';');
2843             if (cellFormat.hasProperty(QTextFormat::TableCellRightPadding))
2844                 styleString += QLatin1String(" padding-right:") + QString::number(cellFormat.rightPadding()) + QLatin1Char(';');
2845             if (cellFormat.hasProperty(QTextFormat::TableCellTopPadding))
2846                 styleString += QLatin1String(" padding-top:") + QString::number(cellFormat.topPadding()) + QLatin1Char(';');
2847             if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
2848                 styleString += QLatin1String(" padding-bottom:") + QString::number(cellFormat.bottomPadding()) + QLatin1Char(';');
2849
2850             if (!styleString.isEmpty())
2851                 html += QLatin1String(" style=\"") + styleString + QLatin1Char('\"');
2852
2853             html += QLatin1Char('>');
2854
2855             emitFrame(cell.begin());
2856
2857             html += QLatin1String("</td>");
2858
2859             defaultCharFormat = oldDefaultCharFormat;
2860         }
2861
2862         html += QLatin1String("</tr>");
2863         if (headerRowCount > 0 && row == headerRowCount - 1)
2864             html += QLatin1String("</thead>");
2865     }
2866
2867     html += QLatin1String("</table>");
2868 }
2869
2870 void QTextHtmlExporter::emitFrame(QTextFrame::Iterator frameIt)
2871 {
2872     if (!frameIt.atEnd()) {
2873         QTextFrame::Iterator next = frameIt;
2874         ++next;
2875         if (next.atEnd()
2876             && frameIt.currentFrame() == 0
2877             && frameIt.parentFrame() != doc->rootFrame()
2878             && frameIt.currentBlock().begin().atEnd())
2879             return;
2880     }
2881
2882     for (QTextFrame::Iterator it = frameIt;
2883          !it.atEnd(); ++it) {
2884         if (QTextFrame *f = it.currentFrame()) {
2885             if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
2886                 emitTable(table);
2887             } else {
2888                 emitTextFrame(f);
2889             }
2890         } else if (it.currentBlock().isValid()) {
2891             emitBlock(it.currentBlock());
2892         }
2893     }
2894 }
2895
2896 void QTextHtmlExporter::emitTextFrame(const QTextFrame *f)
2897 {
2898     FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
2899
2900     html += QLatin1String("\n<table");
2901     QTextFrameFormat format = f->frameFormat();
2902
2903     if (format.hasProperty(QTextFormat::FrameBorder))
2904         emitAttribute("border", QString::number(format.border()));
2905
2906     emitFrameStyle(format, frameType);
2907
2908     emitTextLength("width", format.width());
2909     emitTextLength("height", format.height());
2910
2911     // root frame's bcolor goes in the <body> tag
2912     if (frameType != RootFrame)
2913         emitBackgroundAttribute(format);
2914
2915     html += QLatin1Char('>');
2916     html += QLatin1String("\n<tr>\n<td style=\"border: none;\">");
2917     emitFrame(f->begin());
2918     html += QLatin1String("</td></tr></table>");
2919 }
2920
2921 void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType frameType)
2922 {
2923     QLatin1String styleAttribute(" style=\"");
2924     html += styleAttribute;
2925     const int originalHtmlLength = html.length();
2926
2927     if (frameType == TextFrame)
2928         html += QLatin1String("-qt-table-type: frame;");
2929     else if (frameType == RootFrame)
2930         html += QLatin1String("-qt-table-type: root;");
2931
2932     const QTextFrameFormat defaultFormat;
2933
2934     emitFloatStyle(format.position(), OmitStyleTag);
2935     emitPageBreakPolicy(format.pageBreakPolicy());
2936
2937     if (format.borderBrush() != defaultFormat.borderBrush()) {
2938         html += QLatin1String(" border-color:");
2939         html += format.borderBrush().color().name();
2940         html += QLatin1Char(';');
2941     }
2942
2943     if (format.borderStyle() != defaultFormat.borderStyle())
2944         emitBorderStyle(format.borderStyle());
2945
2946     if (format.hasProperty(QTextFormat::FrameMargin)
2947         || format.hasProperty(QTextFormat::FrameLeftMargin)
2948         || format.hasProperty(QTextFormat::FrameRightMargin)
2949         || format.hasProperty(QTextFormat::FrameTopMargin)
2950         || format.hasProperty(QTextFormat::FrameBottomMargin))
2951         emitMargins(QString::number(format.topMargin()),
2952                     QString::number(format.bottomMargin()),
2953                     QString::number(format.leftMargin()),
2954                     QString::number(format.rightMargin()));
2955
2956     if (html.length() == originalHtmlLength) // nothing emitted?
2957         html.chop(qstrlen(styleAttribute.latin1()));
2958     else
2959         html += QLatin1Char('\"');
2960 }
2961
2962 /*!
2963     Returns a string containing an HTML representation of the document.
2964
2965     The \a encoding parameter specifies the value for the charset attribute
2966     in the html header. For example if 'utf-8' is specified then the
2967     beginning of the generated html will look like this:
2968     \snippet code/src_gui_text_qtextdocument.cpp 0
2969
2970     If no encoding is specified then no such meta information is generated.
2971
2972     If you later on convert the returned html string into a byte array for
2973     transmission over a network or when saving to disk you should specify
2974     the encoding you're going to use for the conversion to a byte array here.
2975
2976     \sa {Supported HTML Subset}
2977 */
2978 #ifndef QT_NO_TEXTHTMLPARSER
2979 QString QTextDocument::toHtml(const QByteArray &encoding) const
2980 {
2981     return QTextHtmlExporter(this).toHtml(encoding);
2982 }
2983 #endif // QT_NO_TEXTHTMLPARSER
2984
2985 /*!
2986     Returns a vector of text formats for all the formats used in the document.
2987 */
2988 QVector<QTextFormat> QTextDocument::allFormats() const
2989 {
2990     Q_D(const QTextDocument);
2991     return d->formatCollection()->formats;
2992 }
2993
2994
2995 /*!
2996   \internal
2997
2998   So that not all classes have to be friends of each other...
2999 */
3000 QTextDocumentPrivate *QTextDocument::docHandle() const
3001 {
3002     Q_D(const QTextDocument);
3003     return const_cast<QTextDocumentPrivate *>(d);
3004 }
3005
3006 /*!
3007     \since 4.4
3008     \fn QTextDocument::undoCommandAdded()
3009
3010     This signal is emitted  every time a new level of undo is added to the QTextDocument.
3011 */
3012
3013 QT_END_NAMESPACE