source: trunk/src/gui/text/qtextdocument.cpp@ 624

Last change on this file since 624 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

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