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