src/gui/text/qtextdocument.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the QtGui module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include "qtextdocument.h"
00025 #include <qtextformat.h>
00026 #include "qtextdocumentlayout_p.h"
00027 #include "qtextdocumentfragment.h"
00028 #include "qtextdocumentfragment_p.h"
00029 #include "qtexttable.h"
00030 #include "qtextlist.h"
00031 #include <qdebug.h>
00032 #include <qregexp.h>
00033 #include <qvarlengtharray.h>
00034 #include <qtextcodec.h>
00035 #include "qtexthtmlparser_p.h"
00036 #include "qpainter.h"
00037 #include "qprinter.h"
00038 #include "qtextedit.h"
00039 #include "qtextcontrol_p.h"
00040 
00041 #include "qtextdocument_p.h"
00042 
00043 #include <limits.h>
00044 
00056 bool Qt::mightBeRichText(const QString& text)
00057 {
00058     if (text.isEmpty())
00059         return false;
00060     int start = 0;
00061 
00062     while (start < text.length() && text.at(start).isSpace())
00063         ++start;
00064 
00065     // skip a leading <?xml ... ?> as for example with xhtml
00066     if (text.mid(start, 5) == QLatin1String("<?xml")) {
00067         while (start < text.length()) {
00068             if (text.at(start) == QLatin1Char('?')
00069                 && start + 2 < text.length()
00070                 && text.at(start + 1) == QLatin1Char('>')) {
00071                 start += 2;
00072                 break;
00073             }
00074             ++start;
00075         }
00076 
00077         while (start < text.length() && text.at(start).isSpace())
00078             ++start;
00079     }
00080 
00081     if (text.mid(start, 5).toLower() == QLatin1String("<!doc"))
00082         return true;
00083     int open = start;
00084     while (open < text.length() && text.at(open) != QLatin1Char('<')
00085             && text.at(open) != QLatin1Char('\n')) {
00086         if (text.at(open) == QLatin1Char('&') &&  text.mid(open+1,3) == QLatin1String("lt;"))
00087             return true; // support desperate attempt of user to see <...>
00088         ++open;
00089     }
00090     if (open < text.length() && text.at(open) == QLatin1Char('<')) {
00091         const int close = text.indexOf(QLatin1Char('>'), open);
00092         if (close > -1) {
00093             QString tag;
00094             for (int i = open+1; i < close; ++i) {
00095                 if (text[i].isDigit() || text[i].isLetter())
00096                     tag += text[i];
00097                 else if (!tag.isEmpty() && text[i].isSpace())
00098                     break;
00099                 else if (!text[i].isSpace() && (!tag.isEmpty() || text[i] != QLatin1Char('!')))
00100                     return false; // that's not a tag
00101             }
00102             return QTextHtmlParser::lookupElement(tag.toLower()) != -1;
00103         }
00104     }
00105     return false;
00106 }
00107 
00125 QString Qt::escape(const QString& plain)
00126 {
00127     QString rich;
00128     rich.reserve(int(plain.length() * 1.1));
00129     for (int i = 0; i < plain.length(); ++i) {
00130         if (plain.at(i) == QLatin1Char('<'))
00131             rich += QLatin1String("&lt;");
00132         else if (plain.at(i) == QLatin1Char('>'))
00133             rich += QLatin1String("&gt;");
00134         else if (plain.at(i) == QLatin1Char('&'))
00135             rich += QLatin1String("&amp;");
00136         else
00137             rich += plain.at(i);
00138     }
00139     return rich;
00140 }
00141 
00154 QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
00155 {
00156     int col = 0;
00157     QString rich;
00158     rich += QLatin1String("<p>");
00159     for (int i = 0; i < plain.length(); ++i) {
00160         if (plain[i] == QLatin1Char('\n')){
00161             int c = 1;
00162             while (i+1 < plain.length() && plain[i+1] == QLatin1Char('\n')) {
00163                 i++;
00164                 c++;
00165             }
00166             if (c == 1)
00167                 rich += QLatin1String("<br>\n");
00168             else {
00169                 rich += QLatin1String("</p>\n");
00170                 while (--c > 1)
00171                     rich += QLatin1String("<br>\n");
00172                 rich += QLatin1String("<p>");
00173             }
00174             col = 0;
00175         } else {
00176             if (mode == Qt::WhiteSpacePre && plain[i] == QLatin1Char('\t')){
00177                 rich += QChar(0x00a0U);
00178                 ++col;
00179                 while (col % 8) {
00180                     rich += QChar(0x00a0U);
00181                     ++col;
00182                 }
00183             }
00184             else if (mode == Qt::WhiteSpacePre && plain[i].isSpace())
00185                 rich += QChar(0x00a0U);
00186             else if (plain[i] == QLatin1Char('<'))
00187                 rich += QLatin1String("&lt;");
00188             else if (plain[i] == QLatin1Char('>'))
00189                 rich += QLatin1String("&gt;");
00190             else if (plain[i] == QLatin1Char('&'))
00191                 rich += QLatin1String("&amp;");
00192             else
00193                 rich += plain[i];
00194             ++col;
00195         }
00196     }
00197     if (col != 0)
00198         rich += QLatin1String("</p>");
00199     return rich;
00200 }
00201 
00202 #ifndef QT_NO_TEXTCODEC
00203 
00208 QTextCodec *Qt::codecForHtml(const QByteArray &ba)
00209 {
00210     return QTextCodec::codecForHtml(ba);
00211 }
00212 #endif
00213 
00268 QTextDocument::QTextDocument(QObject *parent)
00269     : QObject(*new QTextDocumentPrivate, parent)
00270 {
00271     Q_D(QTextDocument);
00272     d->init();
00273 }
00274 
00279 QTextDocument::QTextDocument(const QString &text, QObject *parent)
00280     : QObject(*new QTextDocumentPrivate, parent)
00281 {
00282     Q_D(QTextDocument);
00283     d->init();
00284     QTextCursor(this).insertText(text);
00285 }
00286 
00290 QTextDocument::QTextDocument(QTextDocumentPrivate &dd, QObject *parent)
00291     : QObject(dd, parent)
00292 {
00293     Q_D(QTextDocument);
00294     d->init();
00295 }
00296 
00300 QTextDocument::~QTextDocument()
00301 {
00302 }
00303 
00304 
00309 QTextDocument *QTextDocument::clone(QObject *parent) const
00310 {
00311     Q_D(const QTextDocument);
00312     QTextDocument *doc = new QTextDocument(parent);
00313     QTextCursor(doc).insertFragment(QTextDocumentFragment(this));
00314     doc->d_func()->title = d->title;
00315     doc->d_func()->pageSize = d->pageSize;
00316     doc->d_func()->useDesignMetrics = d->useDesignMetrics;
00317     doc->d_func()->setDefaultFont(d->defaultFont());
00318     doc->d_func()->resources = d->resources;
00319     doc->d_func()->defaultStyleSheet = d->defaultStyleSheet;
00320     doc->d_func()->parsedDefaultStyleSheet = d->parsedDefaultStyleSheet;
00321     return doc;
00322 }
00323 
00327 bool QTextDocument::isEmpty() const
00328 {
00329     Q_D(const QTextDocument);
00330     /* because if we're empty we still have one single paragraph as
00331      * one single fragment */
00332     return d->length() <= 1;
00333 }
00334 
00338 void QTextDocument::clear()
00339 {
00340     Q_D(QTextDocument);
00341     d->clear();
00342     d->resources.clear();
00343 }
00344 
00353 void QTextDocument::undo(QTextCursor *cursor)
00354 {
00355     Q_D(QTextDocument);
00356     const int pos = d->undoRedo(true);
00357     if (cursor && pos >= 0) {
00358         *cursor = QTextCursor(this);
00359         cursor->setPosition(pos);
00360     }
00361 }
00362 
00371 void QTextDocument::redo(QTextCursor *cursor)
00372 {
00373     Q_D(QTextDocument);
00374     const int pos = d->undoRedo(false);
00375     if (cursor && pos >= 0) {
00376         *cursor = QTextCursor(this);
00377         cursor->setPosition(pos);
00378     }
00379 }
00380 
00386 void QTextDocument::undo()
00387 {
00388     Q_D(QTextDocument);
00389     d->undoRedo(true);
00390 }
00391 
00397 void QTextDocument::redo()
00398 {
00399     Q_D(QTextDocument);
00400     d->undoRedo(false);
00401 }
00402 
00408 void QTextDocument::appendUndoItem(QAbstractUndoItem *item)
00409 {
00410     Q_D(QTextDocument);
00411     d->appendUndoItem(item);
00412 }
00413 
00421 void QTextDocument::setUndoRedoEnabled(bool enable)
00422 {
00423     Q_D(QTextDocument);
00424     d->enableUndoRedo(enable);
00425 }
00426 
00427 bool QTextDocument::isUndoRedoEnabled() const
00428 {
00429     Q_D(const QTextDocument);
00430     return d->isUndoRedoEnabled();
00431 }
00432 
00452 int QTextDocument::maximumBlockCount() const
00453 {
00454     Q_D(const QTextDocument);
00455     return d->maximumBlockCount;
00456 }
00457 
00458 void QTextDocument::setMaximumBlockCount(int maximum)
00459 {
00460     Q_D(QTextDocument);
00461     d->maximumBlockCount = maximum;
00462     d->ensureMaximumBlockCount();
00463 }
00464 
00472 void QTextDocument::markContentsDirty(int from, int length)
00473 {
00474     Q_D(QTextDocument);
00475     if (!d->inContentsChange)
00476         d->beginEditBlock();
00477     d->documentChange(from, length);
00478     if (!d->inContentsChange)
00479         d->endEditBlock();
00480 }
00481 
00487 void QTextDocument::setUseDesignMetrics(bool b)
00488 {
00489     Q_D(QTextDocument);
00490     d->useDesignMetrics = b;
00491     if (d->lout)
00492         d->lout->documentChanged(0, 0, d->length());
00493 }
00494 
00495 bool QTextDocument::useDesignMetrics() const
00496 {
00497     Q_D(const QTextDocument);
00498     return d->useDesignMetrics;
00499 }
00500 
00507 void QTextDocument::drawContents(QPainter *p, const QRectF &rect)
00508 {
00509     p->save();
00510     QAbstractTextDocumentLayout::PaintContext ctx;
00511     if (rect.isValid()) {
00512         p->setClipRect(rect);
00513         ctx.clip = rect;
00514     }
00515     documentLayout()->draw(p, ctx);
00516     p->restore();
00517 }
00518 
00540 void QTextDocument::setTextWidth(qreal width)
00541 {
00542     Q_D(QTextDocument);
00543     QSizeF sz = d->pageSize;
00544     sz.setWidth(width);
00545     sz.setHeight(-1);
00546     setPageSize(sz);
00547 }
00548 
00549 qreal QTextDocument::textWidth() const
00550 {
00551     Q_D(const QTextDocument);
00552     return d->pageSize.width();
00553 }
00554 
00563 qreal QTextDocument::idealWidth() const
00564 {
00565 #ifndef QT_NO_PROPERTIES
00566     QAbstractTextDocumentLayout *lout = documentLayout();
00567     if (lout->metaObject()->indexOfProperty("idealWidth") == -1)
00568         return textWidth();
00569     return lout->property("idealWidth").toDouble();
00570 #else
00571     return textWidth();
00572 #endif
00573 }
00574 
00582 void QTextDocument::adjustSize()
00583 {
00584     // Pull this private function in from qglobal.cpp
00585     Q_CORE_EXPORT unsigned int qt_int_sqrt(unsigned int n);
00586 
00587     QFont f = defaultFont();
00588     QFontMetrics fm(f);
00589     int mw =  fm.width(QLatin1Char('x')) * 80;
00590     int w = mw;
00591     setPageSize(QSizeF(w, -1));
00592     QSizeF size = documentLayout()->documentSize();
00593     if (size.width() != 0) {
00594         w = qt_int_sqrt((uint)(5 * size.height() * size.width() / 3));
00595         setPageSize(QSizeF(qMin(w, mw), -1));
00596 
00597         size = documentLayout()->documentSize();
00598         if (w*3 < 5*size.height()) {
00599             w = qt_int_sqrt((uint)(2 * size.height() * size.width()));
00600             setPageSize(QSizeF(qMin(w, mw), -1));
00601         }
00602     }
00603 }
00604 
00619 QSizeF QTextDocument::size() const
00620 {
00621     return documentLayout()->documentSize();
00622 }
00623 
00632 int QTextDocument::blockCount() const
00633 {
00634     Q_D(const QTextDocument);
00635     return d->blockMap().numNodes();
00636 }
00637 
00653 void QTextDocument::setDefaultStyleSheet(const QString &sheet)
00654 {
00655     Q_D(QTextDocument);
00656     d->defaultStyleSheet = sheet;
00657     QCss::Parser parser(sheet);
00658     d->parsedDefaultStyleSheet = QCss::StyleSheet();
00659     parser.parse(&d->parsedDefaultStyleSheet);
00660 }
00661 
00662 QString QTextDocument::defaultStyleSheet() const
00663 {
00664     Q_D(const QTextDocument);
00665     return d->defaultStyleSheet;
00666 }
00667 
00722 bool QTextDocument::isUndoAvailable() const
00723 {
00724     Q_D(const QTextDocument);
00725     return d->isUndoAvailable();
00726 }
00727 
00731 bool QTextDocument::isRedoAvailable() const
00732 {
00733     Q_D(const QTextDocument);
00734     return d->isRedoAvailable();
00735 }
00736 
00745 void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *layout)
00746 {
00747     Q_D(QTextDocument);
00748     d->setLayout(layout);
00749 }
00750 
00754 QAbstractTextDocumentLayout *QTextDocument::documentLayout() const
00755 {
00756     Q_D(const QTextDocument);
00757     if (!d->lout) {
00758         QTextDocument *that = const_cast<QTextDocument *>(this);
00759         that->d_func()->setLayout(new QTextDocumentLayout(that));
00760     }
00761     return d->lout;
00762 }
00763 
00764 
00771 QString QTextDocument::metaInformation(MetaInformation info) const
00772 {
00773     if (info != DocumentTitle)
00774         return QString();
00775     Q_D(const QTextDocument);
00776     return d->title;
00777 }
00778 
00785 void QTextDocument::setMetaInformation(MetaInformation info, const QString &string)
00786 {
00787     if (info != DocumentTitle)
00788         return;
00789     Q_D(QTextDocument);
00790     d->title = string;
00791 }
00792 
00799 QString QTextDocument::toPlainText() const
00800 {
00801     Q_D(const QTextDocument);
00802     QString txt = d->plainText();
00803     txt.replace(QTextBeginningOfFrame, QLatin1Char('\n'));
00804     txt.replace(QTextEndOfFrame, QLatin1Char('\n'));
00805     txt.replace(QChar::ParagraphSeparator, QLatin1Char('\n'));
00806     txt.replace(QChar::LineSeparator, QLatin1Char('\n'));
00807     txt.replace(QChar::Nbsp, QLatin1Char(' '));
00808     return txt;
00809 }
00810 
00817 void QTextDocument::setPlainText(const QString &text)
00818 {
00819     Q_D(QTextDocument);
00820     setUndoRedoEnabled(false);
00821     d->clear();
00822     QTextCursor(this).insertText(text);
00823     setUndoRedoEnabled(true);
00824 }
00825 
00836 void QTextDocument::setHtml(const QString &html)
00837 {
00838     Q_D(QTextDocument);
00839     setUndoRedoEnabled(false);
00840     d->clear();
00841     QTextHtmlImporter(this, html).import();
00842     setUndoRedoEnabled(true);
00843 }
00844 
00884 QTextCursor QTextDocument::find(const QString &subString, int from, FindFlags options) const
00885 {
00886     QRegExp expr(subString);
00887     expr.setPatternSyntax(QRegExp::FixedString);
00888     expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
00889 
00890     return find(expr, from, options);
00891 }
00892 
00910 QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &from, FindFlags options) const
00911 {
00912     const int pos = (from.isNull() ? 0 : from.selectionEnd());
00913     QRegExp expr(subString);
00914     expr.setPatternSyntax(QRegExp::FixedString);
00915     expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
00916 
00917     return find(expr, pos, options);
00918 }
00919 
00920 
00921 static bool findInBlock(const QTextBlock &block, const QString &text, const QRegExp &expression, int offset,
00922                         QTextDocument::FindFlags options, QTextCursor &cursor)
00923 {
00924     const QRegExp expr(expression);
00925 
00926     int idx = -1;
00927     while (offset >=0 && offset <= text.length()) {
00928         idx = (options & QTextDocument::FindBackward) ?
00929                expr.lastIndexIn(text, offset) : expr.indexIn(text, offset);
00930         if (idx == -1)
00931             return false;
00932 
00933         if (options & QTextDocument::FindWholeWords) {
00934             const int start = idx;
00935             const int end = start + expr.matchedLength();
00936             if ((start != 0 && text.at(start - 1).isLetterOrNumber())
00937                 || (end != text.length() && text.at(end).isLetterOrNumber())) {
00938                 //if this is not a whole word, continue the search in the string
00939                 offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
00940                 continue;
00941             }
00942         }
00943         //we have a hit, return the cursor for that.
00944         break;
00945     }
00946     if (idx == -1)
00947         return false;
00948     cursor = QTextCursor(block.docHandle(), block.position() + idx);
00949     cursor.setPosition(cursor.position() + expr.matchedLength(), QTextCursor::KeepAnchor);
00950     return true;
00951 }
00952 
00970 QTextCursor QTextDocument::find(const QRegExp & expr, int from, FindFlags options) const
00971 {
00972     Q_D(const QTextDocument);
00973 
00974     if (expr.isEmpty())
00975         return QTextCursor();
00976 
00977     int pos = from;
00978     //the cursor is positioned between characters, so for a backward search
00979     //do not include the character given in the position.
00980     if (options & FindBackward) {
00981         --pos ;
00982         if(pos < 0)
00983             return QTextCursor();
00984     }
00985 
00986     QTextCursor cursor;
00987     QTextBlock block = d->blocksFind(pos);
00988 
00989     if (!(options & FindBackward)) {
00990        int blockOffset = qMax(0, pos - block.position());
00991         while (block.isValid()) {
00992             const QString blockText = block.text();
00993             if (findInBlock(block, blockText, expr, blockOffset, options, cursor))
00994                 return cursor;
00995             blockOffset = 0;
00996             block = block.next();
00997         }
00998     } else {
00999         int blockOffset = pos - block.position();
01000         while (block.isValid()) {
01001             const QString blockText = block.text();
01002             if (findInBlock(block, blockText, expr, blockOffset, options, cursor))
01003                 return cursor;
01004             block = block.previous();
01005             blockOffset = block.length() - 1;
01006         }
01007     }
01008 
01009     return QTextCursor();
01010 }
01011 
01030 QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &from, FindFlags options) const
01031 {
01032     const int pos = (from.isNull() ? 0 : from.selectionEnd());
01033     return find(expr, pos, options);
01034 }
01035 
01036 
01046 QTextObject *QTextDocument::createObject(const QTextFormat &f)
01047 {
01048     QTextObject *obj = 0;
01049     if (f.isListFormat())
01050         obj = new QTextList(this);
01051     else if (f.isTableFormat())
01052         obj = new QTextTable(this);
01053     else if (f.isFrameFormat())
01054         obj = new QTextFrame(this);
01055 
01056     return obj;
01057 }
01058 
01064 QTextFrame *QTextDocument::frameAt(int pos) const
01065 {
01066     Q_D(const QTextDocument);
01067     return d->frameAt(pos);
01068 }
01069 
01073 QTextFrame *QTextDocument::rootFrame() const
01074 {
01075     Q_D(const QTextDocument);
01076     return d->rootFrame();
01077 }
01078 
01082 QTextObject *QTextDocument::object(int objectIndex) const
01083 {
01084     Q_D(const QTextDocument);
01085     return d->objectForIndex(objectIndex);
01086 }
01087 
01091 QTextObject *QTextDocument::objectForFormat(const QTextFormat &f) const
01092 {
01093     Q_D(const QTextDocument);
01094     return d->objectForFormat(f);
01095 }
01096 
01097 
01101 QTextBlock QTextDocument::findBlock(int pos) const
01102 {
01103     Q_D(const QTextDocument);
01104     return QTextBlock(docHandle(), d->blockMap().findNode(pos));
01105 }
01106 
01110 QTextBlock QTextDocument::begin() const
01111 {
01112     Q_D(const QTextDocument);
01113     return QTextBlock(docHandle(), d->blockMap().begin().n);
01114 }
01115 
01119 QTextBlock QTextDocument::end() const
01120 {
01121     return QTextBlock(docHandle(), 0);
01122 }
01123 
01131 void QTextDocument::setPageSize(const QSizeF &size)
01132 {
01133     Q_D(QTextDocument);
01134     d->pageSize = size;
01135     if (d->lout)
01136         d->lout->documentChanged(0, 0, d->length());
01137 }
01138 
01139 QSizeF QTextDocument::pageSize() const
01140 {
01141     Q_D(const QTextDocument);
01142     return d->pageSize;
01143 }
01144 
01148 int QTextDocument::pageCount() const
01149 {
01150     return documentLayout()->pageCount();
01151 }
01152 
01156 void QTextDocument::setDefaultFont(const QFont &font)
01157 {
01158     Q_D(QTextDocument);
01159     d->setDefaultFont(font);
01160     if (d->lout)
01161         d->lout->documentChanged(0, 0, d->length());
01162 }
01163 
01167 QFont QTextDocument::defaultFont() const
01168 {
01169     Q_D(const QTextDocument);
01170     return d->defaultFont();
01171 }
01172 
01194 bool QTextDocument::isModified() const
01195 {
01196     return docHandle()->isModified();
01197 }
01198 
01199 void QTextDocument::setModified(bool m)
01200 {
01201     docHandle()->setModified(m);
01202 }
01203 
01204 #ifndef QT_NO_PRINTER
01205 static void printPage(int index, QPainter *painter, const QTextDocument *doc, const QRectF &body, const QPointF &pageNumberPos)
01206 {
01207     painter->save();
01208     painter->translate(body.left(), body.top() - (index - 1) * body.height());
01209     QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
01210 
01211     QAbstractTextDocumentLayout *layout = doc->documentLayout();
01212     QAbstractTextDocumentLayout::PaintContext ctx;
01213 
01214     painter->setClipRect(view);
01215     ctx.clip = view;
01216 
01217     // don't use the system palette text as default text color, on HP/UX
01218     // for example that's white, and white text on white paper doesn't
01219     // look that nice
01220     ctx.palette.setColor(QPalette::Text, Qt::black);
01221 
01222     layout->draw(painter, ctx);
01223 
01224     if (!pageNumberPos.isNull()) {
01225         painter->setClipping(false);
01226         painter->setFont(QFont(doc->defaultFont()));
01227         const QString pageString = QString::number(index);
01228 
01229         painter->drawText(qRound(pageNumberPos.x() - painter->fontMetrics().width(pageString)),
01230                           qRound(pageNumberPos.y() + view.top()),
01231                           pageString);
01232     }
01233 
01234     painter->restore();
01235 }
01236 
01254 void QTextDocument::print(QPrinter *printer) const
01255 {
01256     Q_D(const QTextDocument);
01257     QPainter p(printer);
01258 
01259     // Check that there is a valid device to print to.
01260     if (!p.isActive())
01261         return;
01262 
01263     const QTextDocument *doc = this;
01264     QTextDocument *clonedDoc = 0;
01265     (void)doc->documentLayout(); // make sure that there is a layout
01266 
01267     QRectF body = QRectF(QPointF(0, 0), d->pageSize);
01268     QPointF pageNumberPos;
01269 
01270     if (d->pageSize.isValid()
01271         && d->pageSize.height() != INT_MAX) {
01272         extern int qt_defaultDpi();
01273 
01274         qreal sourceDpiX = qt_defaultDpi();
01275         qreal sourceDpiY = sourceDpiX;
01276 
01277         QPaintDevice *dev = doc->documentLayout()->paintDevice();
01278         if (dev) {
01279             sourceDpiX = dev->logicalDpiX();
01280             sourceDpiY = dev->logicalDpiY();
01281         }
01282 
01283         const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
01284         const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
01285 
01286         // scale to dpi
01287         p.scale(dpiScaleX, dpiScaleY);
01288 
01289         QSizeF scaledPageSize = d->pageSize;
01290         scaledPageSize.rwidth() *= dpiScaleX;
01291         scaledPageSize.rheight() *= dpiScaleY;
01292 
01293         const QSizeF printerPageSize(printer->width(), printer->height());
01294 
01295         // scale to page
01296         p.scale(printerPageSize.width() / scaledPageSize.width(),
01297                 printerPageSize.height() / scaledPageSize.height());
01298     } else {
01299         doc = clone(const_cast<QTextDocument *>(this));
01300         clonedDoc = const_cast<QTextDocument *>(doc);
01301 
01302         QAbstractTextDocumentLayout *layout = doc->documentLayout();
01303         layout->setPaintDevice(p.device());
01304 
01305         const int dpiy = p.device()->logicalDpiY();
01306 
01307         const int margin = (int) ((2/2.54)*dpiy); // 2 cm margins
01308         QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
01309         fmt.setMargin(margin);
01310         doc->rootFrame()->setFrameFormat(fmt);
01311 
01312         body = QRectF(0, 0, p.device()->width(), p.device()->height());
01313         pageNumberPos = QPointF(body.width() - margin,
01314                                 body.height() - margin
01315                                 + QFontMetrics(doc->defaultFont(), p.device()).ascent()
01316                                 + 5 * p.device()->logicalDpiY() / 72);
01317 
01318         QFont font(doc->defaultFont());
01319         font.setPointSize(10); // we define 10pt to be a nice base size for printing
01320         clonedDoc->setDefaultFont(font);
01321         clonedDoc->setPageSize(body.size());
01322     }
01323 
01324     int docCopies;
01325     int pageCopies;
01326     if (printer->collateCopies() == true){
01327         docCopies = 1;
01328         pageCopies = printer->numCopies();
01329     } else {
01330         docCopies = printer->numCopies();
01331         pageCopies = 1;
01332     }
01333 
01334     int fromPage = printer->fromPage();
01335     int toPage = printer->toPage();
01336     bool ascending = true;
01337 
01338     if (fromPage == 0 && toPage == 0) {
01339         fromPage = 1;
01340         toPage = doc->pageCount();
01341     }
01342 
01343     if (printer->pageOrder() == QPrinter::LastPageFirst) {
01344         int tmp = fromPage;
01345         fromPage = toPage;
01346         toPage = tmp;
01347         ascending = false;
01348     }
01349 
01350     for (int i = 0; i < docCopies; ++i) {
01351 
01352         int page = fromPage;
01353         while (true) {
01354             for (int j = 0; j < pageCopies; ++j) {
01355                 if (printer->printerState() == QPrinter::Aborted
01356                     || printer->printerState() == QPrinter::Error)
01357                     goto UserCanceled;
01358                 printPage(page, &p, doc, body, pageNumberPos);
01359                 if (j < pageCopies - 1)
01360                     printer->newPage();
01361             }
01362 
01363             if (page == toPage)
01364                 break;
01365 
01366             if (ascending)
01367                 ++page;
01368             else
01369                 --page;
01370 
01371             printer->newPage();
01372         }
01373 
01374         if ( i < docCopies - 1)
01375             printer->newPage();
01376     }
01377 
01378 UserCanceled:
01379     delete clonedDoc;
01380 }
01381 #endif
01382 
01418 QVariant QTextDocument::resource(int type, const QUrl &name) const
01419 {
01420     Q_D(const QTextDocument);
01421     QVariant r = d->resources.value(name);
01422     if (!r.isValid()) {
01423         r = d->cachedResources.value(name);
01424         if (!r.isValid())
01425             r = const_cast<QTextDocument *>(this)->loadResource(type, name);
01426     }
01427     return r;
01428 }
01429 
01434 void QTextDocument::addResource(int type, const QUrl &name, const QVariant &resource)
01435 {
01436     Q_UNUSED(type);
01437     Q_D(QTextDocument);
01438     d->resources.insert(name, resource);
01439 }
01440 
01457 QVariant QTextDocument::loadResource(int type, const QUrl &name)
01458 {
01459     Q_D(QTextDocument);
01460     QVariant r;
01461     if (QTextDocument *doc = qobject_cast<QTextDocument *>(parent()))
01462         r = doc->loadResource(type, name);
01463 #ifndef QT_NO_TEXTEDIT
01464     else if (QTextEdit *edit = qobject_cast<QTextEdit *>(parent()))
01465         r = edit->loadResource(type, name);
01466 #endif
01467 #ifndef QT_NO_TEXTCONTROL
01468     else if (QTextControl *control = qobject_cast<QTextControl *>(parent()))
01469         r = control->loadResource(type, name);
01470 #endif
01471     if (!r.isNull()) {
01472         if (type == ImageResource && r.type() == QVariant::ByteArray) {
01473             QPixmap pm;
01474             pm.loadFromData(r.toByteArray());
01475             if (!pm.isNull())
01476                 r = pm;
01477         }
01478         d->cachedResources.insert(name, r);
01479     }
01480     return r;
01481 }
01482 
01483 static QTextFormat formatDifference(const QTextFormat &from, const QTextFormat &to)
01484 {
01485     QTextFormat diff = to;
01486 
01487     const QMap<int, QVariant> props = to.properties();
01488     for (QMap<int, QVariant>::ConstIterator it = props.begin(), end = props.end();
01489          it != end; ++it)
01490         if (it.value() == from.property(it.key()))
01491             diff.clearProperty(it.key());
01492 
01493     return diff;
01494 }
01495 
01496 QTextHtmlExporter::QTextHtmlExporter(const QTextDocument *_doc)
01497     : doc(_doc), fragmentMarkers(false)
01498 {
01499     const QFont defaultFont = doc->defaultFont();
01500     defaultCharFormat.setFont(defaultFont);
01501 }
01502 
01508 QString QTextHtmlExporter::toHtml(const QByteArray &encoding)
01509 {
01510     html = QLatin1String("<html><head><meta name=\"qrichtext\" content=\"1\" />");
01511     html.reserve(doc->docHandle()->length());
01512 
01513     if (!encoding.isEmpty())
01514         html += QString::fromLatin1("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%1\" />").arg(QString::fromAscii(encoding));
01515 
01516     QString title  = doc->metaInformation(QTextDocument::DocumentTitle);
01517     if (!title.isEmpty())
01518         html += QString::fromLatin1("<title>") + title + QString::fromLatin1("</title>");
01519     html += QLatin1String("<style type=\"text/css\">\n");
01520     html += QLatin1String("p, li { white-space: pre-wrap; }\n");
01521     html += QLatin1String("</style>");
01522     html += QLatin1String("</head><body style=\"");
01523 
01524     html += QLatin1String(" font-family:'");
01525     html += defaultCharFormat.fontFamily();
01526     html += QLatin1String("';");
01527 
01528     if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) {
01529         html += QLatin1String(" font-size:");
01530         html += QString::number(defaultCharFormat.fontPointSize());
01531         html += QLatin1String("pt;");
01532     }
01533 
01534     html += QLatin1String(" font-weight:");
01535     html += QString::number(defaultCharFormat.fontWeight() * 8);
01536     html += QLatin1Char(';');
01537 
01538     html += QLatin1String(" font-style:");
01539     html += (defaultCharFormat.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
01540     html += QLatin1Char(';');
01541 
01542     {
01543         html += QLatin1String(" text-decoration:");
01544         bool atLeastOneDecorationSet = false;
01545 
01546         if (defaultCharFormat.fontUnderline()) {
01547             html += QLatin1String(" underline");
01548             atLeastOneDecorationSet = true;
01549         }
01550 
01551         if (defaultCharFormat.fontOverline()) {
01552             html += QLatin1String(" overline");
01553             atLeastOneDecorationSet = true;
01554         }
01555 
01556         if (defaultCharFormat.fontStrikeOut()) {
01557             html += QLatin1String(" line-through");
01558             atLeastOneDecorationSet = true;
01559         }
01560 
01561         if (!atLeastOneDecorationSet)
01562             html += QLatin1String("none");
01563         html += QLatin1Char(';');
01564     }
01565     html += QLatin1Char('\"');
01566 
01567     const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
01568     QBrush bg = fmt.background();
01569     if (bg != Qt::NoBrush)
01570         emitAttribute("bgcolor", bg.color().name());
01571 
01572     html += QLatin1Char('>');
01573 
01574     emitFrame(doc->rootFrame()->begin());
01575     html += QLatin1String("</body></html>");
01576     return html;
01577 }
01578 
01579 void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &value)
01580 {
01581     html += QLatin1Char(' ');
01582     html += QLatin1String(attribute);
01583     html += QLatin1String("=\"");
01584     html += value;
01585     html += QLatin1Char('"');
01586 }
01587 
01588 bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
01589 {
01590     bool attributesEmitted = false;
01591 
01592     {
01593         const QString family = format.fontFamily();
01594         if (!family.isEmpty() && family != defaultCharFormat.fontFamily()) {
01595             html += QLatin1String(" font-family:'");
01596             html += family;
01597             html += QLatin1String("';");
01598             attributesEmitted = true;
01599         }
01600     }
01601 
01602     if (format.hasProperty(QTextFormat::FontPointSize)
01603         && format.fontPointSize() != defaultCharFormat.fontPointSize()) {
01604         html += QLatin1String(" font-size:");
01605         html += QString::number(format.fontPointSize());
01606         html += QLatin1String("pt;");
01607         attributesEmitted = true;
01608     } else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) {
01609         static const char * const sizeNames[] = {
01610             "small", "medium", "large", "x-large", "xx-large"
01611         };
01612         const char *name = 0;
01613         const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1;
01614         if (idx >= 0 && idx <= 4) {
01615             name = sizeNames[idx];
01616         }
01617         if (name) {
01618             html += QLatin1String(" font-size:");
01619             html += QLatin1String(name);
01620             html += QLatin1Char(';');
01621             attributesEmitted = true;
01622         }
01623     }
01624 
01625     if (format.fontWeight() != defaultCharFormat.fontWeight()) {
01626         html += QLatin1String(" font-weight:");
01627         html += QString::number(format.fontWeight() * 8);
01628         html += QLatin1Char(';');
01629         attributesEmitted = true;
01630     }
01631 
01632     if (format.fontItalic() != defaultCharFormat.fontItalic()) {
01633         html += QLatin1String(" font-style:");
01634         html += (format.fontItalic() ? QLatin1String("italic") : QLatin1String("normal"));
01635         html += QLatin1Char(';');
01636         attributesEmitted = true;
01637     }
01638 
01639     QLatin1String decorationTag(" text-decoration:");
01640     html += decorationTag;
01641     bool hasDecoration = false;
01642     bool atLeastOneDecorationSet = false;
01643 
01644     if (format.fontUnderline() != defaultCharFormat.fontUnderline()) {
01645         hasDecoration = true;
01646         if (format.fontUnderline()) {
01647             html += QLatin1String(" underline");
01648             atLeastOneDecorationSet = true;
01649         }
01650     }
01651 
01652     if (format.fontOverline() != defaultCharFormat.fontOverline()) {
01653         hasDecoration = true;
01654         if (format.fontOverline()) {
01655             html += QLatin1String(" overline");
01656             atLeastOneDecorationSet = true;
01657         }
01658     }
01659 
01660     if (format.fontStrikeOut() != defaultCharFormat.fontStrikeOut()) {
01661         hasDecoration = true;
01662         if (format.fontStrikeOut()) {
01663             html += QLatin1String(" line-through");
01664             atLeastOneDecorationSet = true;
01665         }
01666     }
01667 
01668     if (hasDecoration) {
01669         if (!atLeastOneDecorationSet)
01670             html += QLatin1String("none");
01671         html += QLatin1Char(';');
01672         attributesEmitted = true;
01673     } else {
01674         html.chop(qstrlen(decorationTag.latin1()));
01675     }
01676 
01677     if (format.foreground() != defaultCharFormat.foreground()
01678         && format.foreground().style() != Qt::NoBrush) {
01679         html += QLatin1String(" color:");
01680         html += format.foreground().color().name();
01681         html += QLatin1Char(';');
01682         attributesEmitted = true;
01683     }
01684 
01685     if (format.background() != defaultCharFormat.background()
01686         && format.background().style() != Qt::NoBrush) {
01687         html += QLatin1String(" background-color:");
01688         html += format.background().color().name();
01689         html += QLatin1Char(';');
01690         attributesEmitted = true;
01691     }
01692 
01693     if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()) {
01694         html += QLatin1String(" vertical-align:");
01695 
01696         QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
01697         if (valign == QTextCharFormat::AlignSubScript)
01698             html += QLatin1String("sub");
01699         else if (valign == QTextCharFormat::AlignSuperScript)
01700             html += QLatin1String("super");
01701 
01702         html += QLatin1Char(';');
01703         attributesEmitted = true;
01704     }
01705 
01706     return attributesEmitted;
01707 }
01708 
01709 void QTextHtmlExporter::emitTextLength(const char *attribute, const QTextLength &length)
01710 {
01711     if (length.type() == QTextLength::VariableLength) // default
01712         return;
01713 
01714     html += QLatin1Char(' ');
01715     html += QLatin1String(attribute);
01716     html += QLatin1String("=\"");
01717     html += QString::number(length.rawValue());
01718 
01719     if (length.type() == QTextLength::PercentageLength)
01720         html += QLatin1String("%\"");
01721     else
01722         html += QLatin1String("\"");
01723 }
01724 
01725 void QTextHtmlExporter::emitAlignment(Qt::Alignment align)
01726 {
01727     if (align & Qt::AlignLeft)
01728         return;
01729     else if (align & Qt::AlignRight)
01730         html += QLatin1String(" align=\"right\"");
01731     else if (align & Qt::AlignHCenter)
01732         html += QLatin1String(" align=\"center\"");
01733     else if (align & Qt::AlignJustify)
01734         html += QLatin1String(" align=\"justify\"");
01735 }
01736 
01737 void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
01738 {
01739     if (pos == QTextFrameFormat::InFlow)
01740         return;
01741 
01742     if (mode == EmitStyleTag)
01743         html += QLatin1String(" style=\"float:");
01744     else
01745         html += QLatin1String(" float:");
01746 
01747     if (pos == QTextFrameFormat::FloatLeft)
01748         html += QLatin1String(" left;");
01749     else if (pos == QTextFrameFormat::FloatRight)
01750         html += QLatin1String(" right;");
01751     else
01752         Q_ASSERT_X(0, "QTextHtmlExporter::emitFloatStyle()", "pos should be a valid enum type");
01753 
01754     if (mode == EmitStyleTag)
01755         html += QLatin1Char('\"');
01756 }
01757 
01758 void QTextHtmlExporter::emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right)
01759 {
01760     html += QLatin1String(" margin-top:");
01761     html += top;
01762     html += QLatin1String("px;");
01763 
01764     html += QLatin1String(" margin-bottom:");
01765     html += bottom;
01766     html += QLatin1String("px;");
01767 
01768     html += QLatin1String(" margin-left:");
01769     html += left;
01770     html += QLatin1String("px;");
01771 
01772     html += QLatin1String(" margin-right:");
01773     html += right;
01774     html += QLatin1String("px;");
01775 }
01776 
01777 void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
01778 {
01779     const QTextCharFormat format = fragment.charFormat();
01780 
01781     bool closeAnchor = false;
01782 
01783     if (format.isAnchor()) {
01784         const QString name = format.anchorName();
01785         if (!name.isEmpty()) {
01786             html += QLatin1String("<a name=\"");
01787             html += name;
01788             html += QLatin1String("\"></a>");
01789         }
01790         const QString href = format.anchorHref();
01791         if (!href.isEmpty()) {
01792             html += QLatin1String("<a href=\"");
01793             html += href;
01794             html += QLatin1String("\">");
01795             closeAnchor = true;
01796         }
01797     }
01798 
01799     QLatin1String styleTag("<span style=\"");
01800     html += styleTag;
01801 
01802     const bool attributesEmitted = emitCharFormatStyle(format);
01803     if (attributesEmitted)
01804         html += QLatin1String("\">");
01805     else
01806         html.chop(qstrlen(styleTag.latin1()));
01807 
01808     QString txt = fragment.text();
01809     if (txt.count() == 1 && txt.at(0) == QChar::ObjectReplacementCharacter) {
01810         if (format.isImageFormat()) {
01811             QTextImageFormat imgFmt = format.toImageFormat();
01812 
01813             html += QLatin1String("<img");
01814 
01815             if (imgFmt.hasProperty(QTextFormat::ImageName))
01816                 emitAttribute("src", imgFmt.name());
01817 
01818             if (imgFmt.hasProperty(QTextFormat::ImageWidth))
01819                 emitAttribute("width", QString::number(imgFmt.width()));
01820 
01821             if (imgFmt.hasProperty(QTextFormat::ImageHeight))
01822                 emitAttribute("height", QString::number(imgFmt.height()));
01823 
01824             if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
01825                 emitFloatStyle(imageFrame->frameFormat().position());
01826 
01827             html += QLatin1String(" />");
01828         }
01829     } else {
01830         Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
01831 
01832         txt = Qt::escape(txt);
01833 
01834         // split for [\n{LineSeparator}]
01835         QString forcedLineBreakRegExp = QString::fromLatin1("[\\na]");
01836         forcedLineBreakRegExp[3] = QChar::LineSeparator;
01837 
01838         const QStringList lines = txt.split(QRegExp(forcedLineBreakRegExp));
01839         for (int i = 0; i < lines.count(); ++i) {
01840             if (i > 0)
01841                 html += QLatin1String("<br />"); // space on purpose for compatibility with Netscape, Lynx & Co.
01842             html += lines.at(i);
01843         }
01844     }
01845 
01846     if (attributesEmitted)
01847         html += QLatin1String("</span>");
01848 
01849     if (closeAnchor)
01850         html += QLatin1String("</a>");
01851 }
01852 
01853 static bool isOrderedList(int style)
01854 {
01855     return style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
01856            || style == QTextListFormat::ListUpperAlpha;
01857 }
01858 
01859 void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
01860 {
01861     QTextBlockFormat format = block.blockFormat();
01862     emitAlignment(format.alignment());
01863 
01864     Qt::LayoutDirection dir = format.layoutDirection();
01865     if (dir == Qt::LeftToRight) {
01866         // assume default to not bloat the html too much
01867         // html += QLatin1String(" dir='ltr'");
01868     } else {
01869         html += QLatin1String(" dir='rtl'");
01870     }
01871 
01872     QLatin1String style(" style=\"");
01873     html += style;
01874 
01875     if (block.begin().atEnd()) {
01876         html += QLatin1String("-qt-paragraph-type:empty;");
01877     }
01878 
01879     emitMargins(QString::number(format.topMargin()),
01880                 QString::number(format.bottomMargin()),
01881                 QString::number(format.leftMargin()),
01882                 QString::number(format.rightMargin()));
01883 
01884     html += QLatin1String(" -qt-block-indent:");
01885     html += QString::number(format.indent());
01886     html += QLatin1Char(';');
01887 
01888     html += QLatin1String(" text-indent:");
01889     html += QString::number(format.indent());
01890     html += QLatin1String("px;");
01891 
01892     QTextCharFormat diff = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
01893     if (!diff.properties().isEmpty())
01894         emitCharFormatStyle(diff);
01895 
01896     html += QLatin1Char('"');
01897 
01898     QBrush bg = format.background();
01899     if (bg != Qt::NoBrush)
01900         emitAttribute("bgcolor", bg.color().name());
01901 }
01902 
01903 void QTextHtmlExporter::emitBlock(const QTextBlock &block)
01904 {
01905     if (block.begin().atEnd()) {
01906         // ### HACK, remove once QTextFrame::Iterator is fixed
01907         int p = block.position();
01908         if (p > 0)
01909             --p;
01910         QTextDocumentPrivate::FragmentIterator frag = doc->docHandle()->find(p);
01911         QChar ch = doc->docHandle()->buffer().at(frag->stringPosition);
01912         if (ch == QTextBeginningOfFrame
01913             || ch == QTextEndOfFrame)
01914             return;
01915     }
01916 
01917     html += QLatin1Char('\n');
01918 
01919     // save and later restore, in case we 'change' the default format by
01920     // emitting block char format information
01921     QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
01922 
01923     QTextList *list = block.textList();
01924     if (list) {
01925         if (list->itemNumber(block) == 0) { // first item? emit <ul> or appropriate
01926             const QTextListFormat format = list->format();
01927             const int style = format.style();
01928             switch (style) {
01929                 case QTextListFormat::ListDecimal: html += QLatin1String("<ol"); break;
01930                 case QTextListFormat::ListDisc: html += QLatin1String("<ul"); break;
01931                 case QTextListFormat::ListCircle: html += QLatin1String("<ul type=\"circle\""); break;
01932                 case QTextListFormat::ListSquare: html += QLatin1String("<ul type=\"square\""); break;
01933                 case QTextListFormat::ListLowerAlpha: html += QLatin1String("<ol type=\"a\""); break;
01934                 case QTextListFormat::ListUpperAlpha: html += QLatin1String("<ol type=\"A\""); break;
01935                 default: html += QLatin1String("<ul"); // ### should not happen
01936             }
01937 
01938             if (format.hasProperty(QTextFormat::ListIndent)) {
01939                 html += QLatin1String(" style=\"-qt-list-indent: ");
01940                 html += QString::number(format.indent());
01941                 html += QLatin1String(";\"");
01942             }
01943 
01944             html += QLatin1Char('>');
01945         }
01946 
01947         html += QLatin1String("<li");
01948 
01949         const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
01950         if (!blockFmt.properties().isEmpty()) {
01951             html += QLatin1String(" style=\"");
01952             emitCharFormatStyle(blockFmt);
01953             html += QLatin1Char('\"');
01954 
01955             defaultCharFormat.merge(block.charFormat());
01956         }
01957     }
01958 
01959     const QTextBlockFormat blockFormat = block.blockFormat();
01960     if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
01961         html += QLatin1String("<hr");
01962 
01963         QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
01964         if (width.type() != QTextLength::VariableLength)
01965             emitTextLength("width", width);
01966         else
01967             html += QLatin1Char(' ');
01968 
01969         html += QLatin1String("/>");
01970         return;
01971     }
01972 
01973     const bool pre = blockFormat.nonBreakableLines();
01974     if (pre) {
01975         if (list)
01976             html += QLatin1Char('>');
01977         html += QLatin1String("<pre");
01978     } else if (!list) {
01979         html += QLatin1String("<p");
01980     }
01981 
01982     emitBlockAttributes(block);
01983 
01984     html += QLatin1Char('>');
01985 
01986     const QTextCharFormat blockCharFmt = block.charFormat();
01987     const QTextCharFormat diff = formatDifference(defaultCharFormat, blockCharFmt).toCharFormat();
01988 
01989     defaultCharFormat.merge(blockCharFmt);
01990 
01991     QTextBlock::Iterator it = block.begin();
01992     if (fragmentMarkers && !it.atEnd() && block == doc->begin())
01993         html += QLatin1String("<!--StartFragment-->");
01994 
01995     for (; !it.atEnd(); ++it)
01996         emitFragment(it.fragment());
01997 
01998     if (fragmentMarkers && block.position() + block.length() == doc->docHandle()->length())
01999         html += QLatin1String("<!--EndFragment-->");
02000 
02001     if (pre)
02002         html += QLatin1String("</pre>");
02003     else if (list)
02004         html += QLatin1String("</li>");
02005     else
02006         html += QLatin1String("</p>");
02007 
02008     if (list) {
02009         if (list->itemNumber(block) == list->count() - 1) { // last item? close list
02010             if (isOrderedList(list->format().style()))
02011                 html += QLatin1String("</ol>");
02012             else
02013                 html += QLatin1String("</ul>");
02014         }
02015     }
02016 
02017     defaultCharFormat = oldDefaultCharFormat;
02018 }
02019 
02020 void QTextHtmlExporter::emitTable(const QTextTable *table)
02021 {
02022     QTextTableFormat format = table->format();
02023 
02024     html += QLatin1String("\n<table");
02025 
02026     if (format.hasProperty(QTextFormat::FrameBorder))
02027         emitAttribute("border", QString::number(format.border()));
02028 
02029     emitFloatStyle(format.position());
02030     emitAlignment(format.alignment());
02031     emitTextLength("width", format.width());
02032 
02033     if (format.hasProperty(QTextFormat::TableCellSpacing))
02034         emitAttribute("cellspacing", QString::number(format.cellSpacing()));
02035     if (format.hasProperty(QTextFormat::TableCellPadding))
02036         emitAttribute("cellpadding", QString::number(format.cellPadding()));
02037 
02038     QBrush bg = format.background();
02039     if (bg != Qt::NoBrush)
02040         emitAttribute("bgcolor", bg.color().name());
02041 
02042     html += QLatin1Char('>');
02043 
02044     const int rows = table->rows();
02045     const int columns = table->columns();
02046 
02047     QVector<QTextLength> columnWidths = format.columnWidthConstraints();
02048     if (columnWidths.isEmpty()) {
02049         columnWidths.resize(columns);
02050         columnWidths.fill(QTextLength());
02051     }
02052     Q_ASSERT(columnWidths.count() == columns);
02053 
02054     QVarLengthArray<bool> widthEmittedForColumn(columns);
02055     for (int i = 0; i < columns; ++i)
02056         widthEmittedForColumn[i] = false;
02057 
02058     const int headerRowCount = qMin(format.headerRowCount(), rows);
02059     if (headerRowCount > 0)
02060         html += QLatin1String("<thead>");
02061 
02062     for (int row = 0; row < rows; ++row) {
02063         html += QLatin1String("\n<tr>");
02064 
02065         for (int col = 0; col < columns; ++col) {
02066             const QTextTableCell cell = table->cellAt(row, col);
02067 
02068             // for col/rowspans
02069             if (cell.row() != row)
02070                 continue;
02071 
02072             if (cell.column() != col)
02073                 continue;
02074 
02075             html += QLatin1String("\n<td");
02076 
02077             if (!widthEmittedForColumn[col]) {
02078                 emitTextLength("width", columnWidths.at(col));
02079                 widthEmittedForColumn[col] = true;
02080             }
02081 
02082             if (cell.columnSpan() > 1)
02083                 emitAttribute("colspan", QString::number(cell.columnSpan()));
02084 
02085             if (cell.rowSpan() > 1)
02086                 emitAttribute("rowspan", QString::number(cell.rowSpan()));
02087 
02088             const QTextCharFormat cellFormat = cell.format();
02089             QBrush bg = cellFormat.background();
02090             if (bg != Qt::NoBrush)
02091                 emitAttribute("bgcolor", bg.color().name());
02092 
02093             html += QLatin1Char('>');
02094 
02095             emitFrame(cell.begin());
02096 
02097             html += QLatin1String("</td>");
02098         }
02099 
02100         html += QLatin1String("</tr>");
02101         if (headerRowCount > 0 && row == headerRowCount - 1)
02102             html += QLatin1String("</thead>");
02103     }
02104 
02105     html += QLatin1String("</table>");
02106 }
02107 
02108 void QTextHtmlExporter::emitFrame(QTextFrame::Iterator frameIt)
02109 {
02110     if (!frameIt.atEnd()) {
02111         QTextFrame::Iterator next = frameIt;
02112         ++next;
02113         if (next.atEnd()
02114             && frameIt.currentFrame() == 0
02115             && frameIt.parentFrame() != doc->rootFrame()
02116             && frameIt.currentBlock().begin().atEnd())
02117             return;
02118     }
02119 
02120     for (QTextFrame::Iterator it = frameIt;
02121          !it.atEnd(); ++it) {
02122         if (QTextFrame *f = it.currentFrame()) {
02123             if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
02124                 emitTable(table);
02125             } else {
02126                 html += QLatin1String("\n<table");
02127                 QTextFrameFormat format = f->frameFormat();
02128 
02129                 if (format.hasProperty(QTextFormat::FrameBorder))
02130                     emitAttribute("border", QString::number(format.border()));
02131 
02132                 html += QLatin1String(" style=\"-qt-table-type: frame;");
02133                 emitFloatStyle(format.position(), OmitStyleTag);
02134 
02135                 if (format.hasProperty(QTextFormat::FrameMargin)) {
02136                     const QString margin = QString::number(format.margin());
02137                     emitMargins(margin, margin, margin, margin);
02138                 }
02139 
02140                 html += QLatin1Char('\"');
02141 
02142                 emitTextLength("width", format.width());
02143                 emitTextLength("height", format.height());
02144 
02145                 QBrush bg = format.background();
02146                 if (bg != Qt::NoBrush)
02147                     emitAttribute("bgcolor", bg.color().name());
02148 
02149                 html += QLatin1Char('>');
02150                 html += QLatin1String("\n<tr>\n<td style=\"border: none;\">");
02151                 emitFrame(f->begin());
02152                 html += QLatin1String("</td></tr></table>");
02153             }
02154         } else if (it.currentBlock().isValid()) {
02155             emitBlock(it.currentBlock());
02156         }
02157     }
02158 }
02159 
02178 QString QTextDocument::toHtml(const QByteArray &encoding) const
02179 {
02180     return QTextHtmlExporter(this).toHtml(encoding);
02181 }
02182 
02186 QVector<QTextFormat> QTextDocument::allFormats() const
02187 {
02188     Q_D(const QTextDocument);
02189     return d->formatCollection()->formats;
02190 }
02191 
02192 
02198 QTextDocumentPrivate *QTextDocument::docHandle() const
02199 {
02200     Q_D(const QTextDocument);
02201     return const_cast<QTextDocumentPrivate *>(d);
02202 }
02203 

Generated on Thu Mar 15 11:56:23 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1