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

Last change on this file since 455 was 2, checked in by Dmitry A. Kuminov, 17 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 89.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department 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
68#include <limits.h>
69
70QT_BEGIN_NAMESPACE
71
72Q_CORE_EXPORT unsigned int qt_int_sqrt(unsigned int n);
73
74/*!
75 Returns true if the string \a text is likely to be rich text;
76 otherwise returns false.
77
78 This function uses a fast and therefore simple heuristic. It
79 mainly checks whether there is something that looks like a tag
80 before the first line break. Although the result may be correct
81 for common cases, there is no guarantee.
82
83 This function is defined in the \c <QTextDocument> header file.
84*/
85bool Qt::mightBeRichText(const QString& text)
86{
87 if (text.isEmpty())
88 return false;
89 int start = 0;
90
91 while (start < text.length() && text.at(start).isSpace())
92 ++start;
93
94 // skip a leading <?xml ... ?> as for example with xhtml
95 if (text.mid(start, 5) == QLatin1String("<?xml")) {
96 while (start < text.length()) {
97 if (text.at(start) == QLatin1Char('?')
98 && start + 2 < text.length()
99 && text.at(start + 1) == QLatin1Char('>')) {
100 start += 2;
101 break;
102 }
103 ++start;
104 }
105
106 while (start < text.length() && text.at(start).isSpace())
107 ++start;
108 }
109
110 if (text.mid(start, 5).toLower() == QLatin1String("<!doc"))
111 return true;
112 int open = start;
113 while (open < text.length() && text.at(open) != QLatin1Char('<')
114 && text.at(open) != QLatin1Char('\n')) {
115 if (text.at(open) == QLatin1Char('&') && text.mid(open+1,3) == QLatin1String("lt;"))
116 return true; // support desperate attempt of user to see <...>
117 ++open;
118 }
119 if (open < text.length() && text.at(open) == QLatin1Char('<')) {
120 const int close = text.indexOf(QLatin1Char('>'), open);
121 if (close > -1) {
122 QString tag;
123 for (int i = open+1; i < close; ++i) {
124 if (text[i].isDigit() || text[i].isLetter())
125 tag += text[i];
126 else if (!tag.isEmpty() && text[i].isSpace())
127 break;
128 else if (!text[i].isSpace() && (!tag.isEmpty() || text[i] != QLatin1Char('!')))
129 return false; // that's not a tag
130 }
131#ifndef QT_NO_TEXTHTMLPARSER
132 return QTextHtmlParser::lookupElement(tag.toLower()) != -1;
133#else
134 return false;
135#endif // QT_NO_TEXTHTMLPARSER
136 }
137 }
138 return false;
139}
140
141/*!
142 Converts the plain text string \a plain to a HTML string with
143 HTML metacharacters \c{<}, \c{>}, and \c{&} replaced by HTML
144 entities.
145
146 Example:
147
148 \snippet doc/src/snippets/code/src_gui_text_qtextdocument.cpp 0
149
150 This function is defined in the \c <QTextDocument> header file.
151
152 \sa convertFromPlainText(), mightBeRichText()
153*/
154QString Qt::escape(const QString& plain)
155{
156 QString rich;
157 rich.reserve(int(plain.length() * 1.1));
158 for (int i = 0; i < plain.length(); ++i) {
159 if (plain.at(i) == QLatin1Char('<'))
160 rich += QLatin1String("&lt;");
161 else if (plain.at(i) == QLatin1Char('>'))
162 rich += QLatin1String("&gt;");
163 else if (plain.at(i) == QLatin1Char('&'))
164 rich += QLatin1String("&amp;");
165 else
166 rich += plain.at(i);
167 }
168 return rich;
169}
170
171/*!
172 \fn QString Qt::convertFromPlainText(const QString &plain, WhiteSpaceMode mode)
173
174 Converts the plain text string \a plain to an HTML-formatted
175 paragraph while preserving most of its look.
176
177 \a mode defines how whitespace is handled.
178
179 This function is defined in the \c <QTextDocument> header file.
180
181 \sa escape(), mightBeRichText()
182*/
183QString Qt::convertFromPlainText(const QString &plain, Qt::WhiteSpaceMode mode)
184{
185 int col = 0;
186 QString rich;
187 rich += QLatin1String("<p>");
188 for (int i = 0; i < plain.length(); ++i) {
189 if (plain[i] == QLatin1Char('\n')){
190 int c = 1;
191 while (i+1 < plain.length() && plain[i+1] == QLatin1Char('\n')) {
192 i++;
193 c++;
194 }
195 if (c == 1)
196 rich += QLatin1String("<br>\n");
197 else {
198 rich += QLatin1String("</p>\n");
199 while (--c > 1)
200 rich += QLatin1String("<br>\n");
201 rich += QLatin1String("<p>");
202 }
203 col = 0;
204 } else {
205 if (mode == Qt::WhiteSpacePre && plain[i] == QLatin1Char('\t')){
206 rich += QChar(0x00a0U);
207 ++col;
208 while (col % 8) {
209 rich += QChar(0x00a0U);
210 ++col;
211 }
212 }
213 else if (mode == Qt::WhiteSpacePre && plain[i].isSpace())
214 rich += QChar(0x00a0U);
215 else if (plain[i] == QLatin1Char('<'))
216 rich += QLatin1String("&lt;");
217 else if (plain[i] == QLatin1Char('>'))
218 rich += QLatin1String("&gt;");
219 else if (plain[i] == QLatin1Char('&'))
220 rich += QLatin1String("&amp;");
221 else
222 rich += plain[i];
223 ++col;
224 }
225 }
226 if (col != 0)
227 rich += QLatin1String("</p>");
228 return rich;
229}
230
231#ifndef QT_NO_TEXTCODEC
232/*!
233 \internal
234
235 This function is defined in the \c <QTextDocument> header file.
236*/
237QTextCodec *Qt::codecForHtml(const QByteArray &ba)
238{
239 return QTextCodec::codecForHtml(ba);
240}
241#endif
242
243/*!
244 \class QTextDocument
245 \reentrant
246
247 \brief The QTextDocument class holds formatted text that can be
248 viewed and edited using a QTextEdit.
249
250 \ingroup text
251 \mainclass
252
253 QTextDocument is a container for structured rich text documents, providing
254 support for styled text and various types of document elements, such as
255 lists, tables, frames, and images.
256 They can be created for use in a QTextEdit, or used independently.
257
258 Each document element is described by an associated format object. Each
259 format object is treated as a unique object by QTextDocuments, and can be
260 passed to objectForFormat() to obtain the document element that it is
261 applied to.
262
263 A QTextDocument can be edited programmatically using a QTextCursor, and
264 its contents can be examined by traversing the document structure. The
265 entire document structure is stored as a hierarchy of document elements
266 beneath the root frame, found with the rootFrame() function. Alternatively,
267 if you just want to iterate over the textual contents of the document you
268 can use begin(), end(), and findBlock() to retrieve text blocks that you
269 can examine and iterate over.
270
271 The layout of a document is determined by the documentLayout();
272 you can create your own QAbstractTextDocumentLayout subclass and
273 set it using setDocumentLayout() if you want to use your own
274 layout logic. The document's title and other meta-information can be
275 obtained by calling the metaInformation() function. For documents that
276 are exposed to users through the QTextEdit class, the document title
277 is also available via the QTextEdit::documentTitle() function.
278
279 The toPlainText() and toHtml() convenience functions allow you to retrieve the
280 contents of the document as plain text and HTML.
281 The document's text can be searched using the find() functions.
282
283 Undo/redo of operations performed on the document can be controlled using
284 the setUndoRedoEnabled() function. The undo/redo system can be controlled
285 by an editor widget through the undo() and redo() slots; the document also
286 provides contentsChanged(), undoAvailable(), and redoAvailable() signals
287 that inform connected editor widgets about the state of the undo/redo
288 system.
289
290 \sa QTextCursor QTextEdit \link richtext.html Rich Text Processing\endlink
291*/
292
293/*!
294 \property QTextDocument::defaultFont
295 \brief the default font used to display the document's text
296*/
297
298/*!
299 \property QTextDocument::defaultTextOption
300 \brief the default text option will be set on all \l{QTextLayout}s in the document.
301
302 When \l{QTextBlock}s are created, the defaultTextOption is set on their
303 QTextLayout. This allows setting global properties for the document such as the
304 default word wrap mode.
305 */
306
307/*!
308 Constructs an empty QTextDocument with the given \a parent.
309*/
310QTextDocument::QTextDocument(QObject *parent)
311 : QObject(*new QTextDocumentPrivate, parent)
312{
313 Q_D(QTextDocument);
314 d->init();
315}
316
317/*!
318 Constructs a QTextDocument containing the plain (unformatted) \a text
319 specified, and with the given \a parent.
320*/
321QTextDocument::QTextDocument(const QString &text, QObject *parent)
322 : QObject(*new QTextDocumentPrivate, parent)
323{
324 Q_D(QTextDocument);
325 d->init();
326 QTextCursor(this).insertText(text);
327}
328
329/*!
330 \internal
331*/
332QTextDocument::QTextDocument(QTextDocumentPrivate &dd, QObject *parent)
333 : QObject(dd, parent)
334{
335 Q_D(QTextDocument);
336 d->init();
337}
338
339/*!
340 Destroys the document.
341*/
342QTextDocument::~QTextDocument()
343{
344}
345
346
347/*!
348 Creates a new QTextDocument that is a copy of this text document. \a
349 parent is the parent of the returned text document.
350*/
351QTextDocument *QTextDocument::clone(QObject *parent) const
352{
353 Q_D(const QTextDocument);
354 QTextDocument *doc = new QTextDocument(parent);
355 QTextCursor(doc).insertFragment(QTextDocumentFragment(this));
356 doc->rootFrame()->setFrameFormat(rootFrame()->frameFormat());
357 QTextDocumentPrivate *priv = doc->d_func();
358 priv->title = d->title;
359 priv->url = d->url;
360 priv->pageSize = d->pageSize;
361 priv->indentWidth = d->indentWidth;
362 priv->defaultTextOption = d->defaultTextOption;
363 priv->setDefaultFont(d->defaultFont());
364 priv->resources = d->resources;
365 priv->cachedResources.clear();
366#ifndef QT_NO_CSSPARSER
367 priv->defaultStyleSheet = d->defaultStyleSheet;
368 priv->parsedDefaultStyleSheet = d->parsedDefaultStyleSheet;
369#endif
370 return doc;
371}
372
373/*!
374 Returns true if the document is empty; otherwise returns false.
375*/
376bool QTextDocument::isEmpty() const
377{
378 Q_D(const QTextDocument);
379 /* because if we're empty we still have one single paragraph as
380 * one single fragment */
381 return d->length() <= 1;
382}
383
384/*!
385 Clears the document.
386*/
387void QTextDocument::clear()
388{
389 Q_D(QTextDocument);
390 d->clear();
391 d->resources.clear();
392}
393
394/*!
395 \since 4.2
396
397 Undoes the last editing operation on the document if undo is
398 available. The provided \a cursor is positioned at the end of the
399 location where the edition operation was undone.
400
401 See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
402 documentation for details.
403
404 \sa undoAvailable(), isUndoRedoEnabled()
405*/
406void QTextDocument::undo(QTextCursor *cursor)
407{
408 Q_D(QTextDocument);
409 const int pos = d->undoRedo(true);
410 if (cursor && pos >= 0) {
411 *cursor = QTextCursor(this);
412 cursor->setPosition(pos);
413 }
414}
415
416/*!
417 \since 4.2
418 Redoes the last editing operation on the document if \link
419 QTextDocument::isRedoAvailable() redo is available\endlink.
420
421 The provided \a cursor is positioned at the end of the location where
422 the edition operation was redone.
423*/
424void QTextDocument::redo(QTextCursor *cursor)
425{
426 Q_D(QTextDocument);
427 const int pos = d->undoRedo(false);
428 if (cursor && pos >= 0) {
429 *cursor = QTextCursor(this);
430 cursor->setPosition(pos);
431 }
432}
433
434/*!
435 \overload
436
437*/
438void QTextDocument::undo()
439{
440 Q_D(QTextDocument);
441 d->undoRedo(true);
442}
443
444/*!
445 \overload
446 Redoes the last editing operation on the document if \link
447 QTextDocument::isRedoAvailable() redo is available\endlink.
448*/
449void QTextDocument::redo()
450{
451 Q_D(QTextDocument);
452 d->undoRedo(false);
453}
454
455/*!
456 \internal
457
458 Appends a custom undo \a item to the undo stack.
459*/
460void QTextDocument::appendUndoItem(QAbstractUndoItem *item)
461{
462 Q_D(QTextDocument);
463 d->appendUndoItem(item);
464}
465
466/*!
467 \property QTextDocument::undoRedoEnabled
468 \brief whether undo/redo are enabled for this document
469
470 This defaults to true. If disabled, the undo stack is cleared and
471 no items will be added to it.
472*/
473void QTextDocument::setUndoRedoEnabled(bool enable)
474{
475 Q_D(QTextDocument);
476 d->enableUndoRedo(enable);
477}
478
479bool QTextDocument::isUndoRedoEnabled() const
480{
481 Q_D(const QTextDocument);
482 return d->isUndoRedoEnabled();
483}
484
485/*!
486 \property QTextDocument::maximumBlockCount
487 \since 4.2
488 \brief Specifies the limit for blocks in the document.
489
490 Specifies the maximum number of blocks the document may have. If there are
491 more blocks in the document that specified with this property blocks are removed
492 from the beginning of the document.
493
494 A negative or zero value specifies that the document may contain an unlimited
495 amount of blocks.
496
497 The default value is 0.
498
499 Note that setting this property will apply the limit immediately to the document
500 contents.
501
502 Setting this property also disables the undo redo history.
503
504 This property is undefined in documents with tables or frames.
505*/
506int QTextDocument::maximumBlockCount() const
507{
508 Q_D(const QTextDocument);
509 return d->maximumBlockCount;
510}
511
512void QTextDocument::setMaximumBlockCount(int maximum)
513{
514 Q_D(QTextDocument);
515 d->maximumBlockCount = maximum;
516 d->ensureMaximumBlockCount();
517 setUndoRedoEnabled(false);
518}
519
520/*!
521 \since 4.3
522
523 The default text option is used on all QTextLayout objects in the document.
524 This allows setting global properties for the document such as the default
525 word wrap mode.
526*/
527QTextOption QTextDocument::defaultTextOption() const
528{
529 Q_D(const QTextDocument);
530 return d->defaultTextOption;
531}
532
533/*!
534 \since 4.3
535
536 Sets the default text option.
537*/
538void QTextDocument::setDefaultTextOption(const QTextOption &option)
539{
540 Q_D(QTextDocument);
541 d->defaultTextOption = option;
542 if (d->lout)
543 d->lout->documentChanged(0, 0, d->length());
544}
545
546/*!
547 \fn void QTextDocument::markContentsDirty(int position, int length)
548
549 Marks the contents specified by the given \a position and \a length
550 as "dirty", informing the document that it needs to be laid out
551 again.
552*/
553void QTextDocument::markContentsDirty(int from, int length)
554{
555 Q_D(QTextDocument);
556 if (!d->inContentsChange)
557 d->beginEditBlock();
558 d->documentChange(from, length);
559 if (!d->inContentsChange)
560 d->endEditBlock();
561}
562
563/*!
564 \property QTextDocument::useDesignMetrics
565 \since 4.1
566 \brief whether the document uses design metrics of fonts to improve the accuracy of text layout
567
568 If this property is set to true, the layout will use design metrics.
569 Otherwise, the metrics of the paint device as set on
570 QAbstractTextDocumentLayout::setPaintDevice() will be used.
571
572 Using design metrics makes a layout have a width that is no longer dependent on hinting
573 and pixel-rounding. This means that WYSIWYG text layout becomes possible because the width
574 scales much more linearly based on paintdevice metrics than it would otherwise.
575
576 By default, this property is false.
577*/
578
579void QTextDocument::setUseDesignMetrics(bool b)
580{
581 Q_D(QTextDocument);
582 if (b == d->defaultTextOption.useDesignMetrics())
583 return;
584 d->defaultTextOption.setUseDesignMetrics(b);
585 if (d->lout)
586 d->lout->documentChanged(0, 0, d->length());
587}
588
589bool QTextDocument::useDesignMetrics() const
590{
591 Q_D(const QTextDocument);
592 return d->defaultTextOption.useDesignMetrics();
593}
594
595/*!
596 \since 4.2
597
598 Draws the content of the document with painter \a p, clipped to \a rect.
599 If \a rect is a null rectangle (default) then the document is painted unclipped.
600*/
601void QTextDocument::drawContents(QPainter *p, const QRectF &rect)
602{
603 p->save();
604 QAbstractTextDocumentLayout::PaintContext ctx;
605 if (rect.isValid()) {
606 p->setClipRect(rect);
607 ctx.clip = rect;
608 }
609 documentLayout()->draw(p, ctx);
610 p->restore();
611}
612
613/*!
614 \property QTextDocument::textWidth
615 \since 4.2
616
617 The text width specifies the preferred width for text in the document. If
618 the text (or content in general) is wider than the specified with it is broken
619 into multiple lines and grows vertically. If the text cannot be broken into multiple
620 lines to fit into the specified text width it will be larger and the size() and the
621 idealWidth() property will reflect that.
622
623 If the text width is set to -1 then the text will not be broken into multiple lines
624 unless it is enforced through an explicit line break or a new paragraph.
625
626 The default value is -1.
627
628 Setting the text width will also set the page height to -1, causing the document to
629 grow or shrink vertically in a continuous way. If you want the document layout to break
630 the text into multiple pages then you have to set the pageSize property instead.
631
632 \sa size(), idealWidth(), pageSize()
633*/
634void QTextDocument::setTextWidth(qreal width)
635{
636 Q_D(QTextDocument);
637 QSizeF sz = d->pageSize;
638 sz.setWidth(width);
639 sz.setHeight(-1);
640 setPageSize(sz);
641}
642
643qreal QTextDocument::textWidth() const
644{
645 Q_D(const QTextDocument);
646 return d->pageSize.width();
647}
648
649/*!
650 \since 4.2
651
652 Returns the ideal width of the text document. The ideal width is the actually used width
653 of the document without optional alignments taken into account. It is always <= size().width().
654
655 \sa adjustSize(), textWidth
656*/
657qreal QTextDocument::idealWidth() const
658{
659 if (QTextDocumentLayout *lout = qobject_cast<QTextDocumentLayout *>(documentLayout()))
660 return lout->idealWidth();
661 return textWidth();
662}
663
664/*!
665 \property QTextDocument::documentMargin
666 \since 4.5
667
668 The margin around the document. The default is 4.
669*/
670qreal QTextDocument::documentMargin() const
671{
672 Q_D(const QTextDocument);
673 return d->documentMargin;
674}
675
676void QTextDocument::setDocumentMargin(qreal margin)
677{
678 Q_D(QTextDocument);
679 if (d->documentMargin != margin) {
680 d->documentMargin = margin;
681
682 QTextFrame* root = rootFrame();
683 QTextFrameFormat format = root->frameFormat();
684 format.setMargin(margin);
685 root->setFrameFormat(format);
686
687 if (d->lout)
688 d->lout->documentChanged(0, 0, d->length());
689 }
690}
691
692
693/*!
694 \property QTextDocument::indentWidth
695 \since 4.4
696
697 Returns the width used for text list and text block indenting.
698
699 The indent properties of QTextListFormat and QTextBlockFormat specify
700 multiples of this value. The default indent width is 40.
701*/
702qreal QTextDocument::indentWidth() const
703{
704 Q_D(const QTextDocument);
705 return d->indentWidth;
706}
707
708
709/*!
710 \since 4.4
711
712 Sets the \a width used for text list and text block indenting.
713
714 The indent properties of QTextListFormat and QTextBlockFormat specify
715 multiples of this value. The default indent width is 40 .
716
717 \sa indentWidth()
718*/
719void QTextDocument::setIndentWidth(qreal width)
720{
721 Q_D(QTextDocument);
722 if (d->indentWidth != width) {
723 d->indentWidth = width;
724 if (d->lout)
725 d->lout->documentChanged(0, 0, d->length());
726 }
727}
728
729
730
731
732/*!
733 \since 4.2
734
735 Adjusts the document to a reasonable size.
736
737 \sa idealWidth(), textWidth, size
738*/
739void QTextDocument::adjustSize()
740{
741 // Pull this private function in from qglobal.cpp
742 QFont f = defaultFont();
743 QFontMetrics fm(f);
744 int mw = fm.width(QLatin1Char('x')) * 80;
745 int w = mw;
746 setTextWidth(w);
747 QSizeF size = documentLayout()->documentSize();
748 if (size.width() != 0) {
749 w = qt_int_sqrt((uint)(5 * size.height() * size.width() / 3));
750 setTextWidth(qMin(w, mw));
751
752 size = documentLayout()->documentSize();
753 if (w*3 < 5*size.height()) {
754 w = qt_int_sqrt((uint)(2 * size.height() * size.width()));
755 setTextWidth(qMin(w, mw));
756 }
757 }
758 setTextWidth(idealWidth());
759}
760
761/*!
762 \property QTextDocument::size
763 \since 4.2
764
765 Returns the actual size of the document.
766 This is equivalent to documentLayout()->documentSize();
767
768 The size of the document can be changed either by setting
769 a text width or setting an entire page size.
770
771 Note that the width is always >= pageSize().width().
772
773 By default, for a newly-created, empty document, this property contains
774 a configuration-dependent size.
775
776 \sa setTextWidth(), setPageSize(), idealWidth()
777*/
778QSizeF QTextDocument::size() const
779{
780 return documentLayout()->documentSize();
781}
782
783/*!
784 \property QTextDocument::blockCount
785 \since 4.2
786
787 Returns the number of text blocks in the document.
788
789 The value of this property is undefined in documents with tables or frames.
790
791 By default, if defined, this property contains a value of 1.
792 \sa lineCount(), characterCount()
793*/
794int QTextDocument::blockCount() const
795{
796 Q_D(const QTextDocument);
797 return d->blockMap().numNodes();
798}
799
800
801/*!
802 \since 4.5
803
804 Returns the number of lines of this document (if the layout supports
805 this). Otherwise, this is identical to the number of blocks.
806
807 \sa blockCount(), characterCount()
808 */
809int QTextDocument::lineCount() const
810{
811 Q_D(const QTextDocument);
812 return d->blockMap().length(2);
813}
814
815/*!
816 \since 4.5
817
818 Returns the number of characters of this document.
819
820 \sa blockCount(), characterAt()
821 */
822int QTextDocument::characterCount() const
823{
824 Q_D(const QTextDocument);
825 return d->length();
826}
827
828/*!
829 \since 4.5
830
831 Returns the character at position \a pos, or a null character if the
832 position is out of range.
833
834 \sa characterCount()
835 */
836QChar QTextDocument::characterAt(int pos) const
837{
838 Q_D(const QTextDocument);
839 if (pos < 0 || pos >= d->length())
840 return QChar();
841 QTextDocumentPrivate::FragmentIterator fragIt = d->find(pos);
842 const QTextFragmentData * const frag = fragIt.value();
843 const int offsetInFragment = qMax(0, pos - fragIt.position());
844 return d->text.at(frag->stringPosition + offsetInFragment);
845}
846
847
848/*!
849 \property QTextDocument::defaultStyleSheet
850 \since 4.2
851
852 The default style sheet is applied to all newly HTML formatted text that is
853 inserted into the document, for example using setHtml() or QTextCursor::insertHtml().
854
855 The style sheet needs to be compliant to CSS 2.1 syntax.
856
857 \bold{Note:} Changing the default style sheet does not have any effect to the existing content
858 of the document.
859
860 \sa {Supported HTML Subset}
861*/
862
863#ifndef QT_NO_CSSPARSER
864void QTextDocument::setDefaultStyleSheet(const QString &sheet)
865{
866 Q_D(QTextDocument);
867 d->defaultStyleSheet = sheet;
868 QCss::Parser parser(sheet);
869 d->parsedDefaultStyleSheet = QCss::StyleSheet();
870 d->parsedDefaultStyleSheet.origin = QCss::StyleSheetOrigin_UserAgent;
871 parser.parse(&d->parsedDefaultStyleSheet);
872}
873
874QString QTextDocument::defaultStyleSheet() const
875{
876 Q_D(const QTextDocument);
877 return d->defaultStyleSheet;
878}
879#endif // QT_NO_CSSPARSER
880
881/*!
882 \fn void QTextDocument::contentsChanged()
883
884 This signal is emitted whenever the document's content changes; for
885 example, when text is inserted or deleted, or when formatting is applied.
886
887 \sa contentsChange()
888*/
889
890/*!
891 \fn void QTextDocument::contentsChange(int position, int charsRemoved, int charsAdded)
892
893 This signal is emitted whenever the document's content changes; for
894 example, when text is inserted or deleted, or when formatting is applied.
895
896 Information is provided about the \a position of the character in the
897 document where the change occurred, the number of characters removed
898 (\a charsRemoved), and the number of characters added (\a charsAdded).
899
900 The signal is emitted before the document's layout manager is notified
901 about the change. This hook allows you to implement syntax highlighting
902 for the document.
903
904 \sa QAbstractTextDocumentLayout::documentChanged(), contentsChanged()
905*/
906
907
908/*!
909 \fn QTextDocument::undoAvailable(bool available);
910
911 This signal is emitted whenever undo operations become available
912 (\a available is true) or unavailable (\a available is false).
913
914 See the \l {Overview of Qt's Undo Framework}{Qt Undo Framework}
915 documentation for details.
916
917 \sa undo(), isUndoRedoEnabled()
918*/
919
920/*!
921 \fn QTextDocument::redoAvailable(bool available);
922
923 This signal is emitted whenever redo operations become available
924 (\a available is true) or unavailable (\a available is false).
925*/
926
927/*!
928 \fn QTextDocument::cursorPositionChanged(const QTextCursor &cursor);
929
930 This signal is emitted whenever the position of a cursor changed
931 due to an editing operation. The cursor that changed is passed in
932 \a cursor. If you need a signal when the cursor is moved with the
933 arrow keys you can use the \l{QTextEdit::}{cursorPositionChanged()} signal in
934 QTextEdit.
935*/
936
937/*!
938 \fn QTextDocument::blockCountChanged(int newBlockCount);
939 \since 4.3
940
941 This signal is emitted when the total number of text blocks in the
942 document changes. The value passed in \a newBlockCount is the new
943 total.
944*/
945
946/*!
947 \fn QTextDocument::documentLayoutChanged();
948 \since 4.4
949
950 This signal is emitted when a new document layout is set.
951
952 \sa setDocumentLayout()
953
954*/
955
956
957/*!
958 Returns true if undo is available; otherwise returns false.
959*/
960bool QTextDocument::isUndoAvailable() const
961{
962 Q_D(const QTextDocument);
963 return d->isUndoAvailable();
964}
965
966/*!
967 Returns true if redo is available; otherwise returns false.
968*/
969bool QTextDocument::isRedoAvailable() const
970{
971 Q_D(const QTextDocument);
972 return d->isRedoAvailable();
973}
974
975
976/*! \since 4.4
977
978 Returns the document's revision (if undo is enabled).
979
980 The revision is guaranteed to increase when a document that is not
981 modified is edited.
982
983 \sa QTextBlock::revision(), isModified()
984 */
985int QTextDocument::revision() const
986{
987 Q_D(const QTextDocument);
988 return d->undoState;
989}
990
991
992
993/*!
994 Sets the document to use the given \a layout. The previous layout
995 is deleted.
996
997 \sa documentLayoutChanged()
998*/
999void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *layout)
1000{
1001 Q_D(QTextDocument);
1002 d->setLayout(layout);
1003}
1004
1005/*!
1006 Returns the document layout for this document.
1007*/
1008QAbstractTextDocumentLayout *QTextDocument::documentLayout() const
1009{
1010 Q_D(const QTextDocument);
1011 if (!d->lout) {
1012 QTextDocument *that = const_cast<QTextDocument *>(this);
1013 that->d_func()->setLayout(new QTextDocumentLayout(that));
1014 }
1015 return d->lout;
1016}
1017
1018
1019/*!
1020 Returns meta information about the document of the type specified by
1021 \a info.
1022
1023 \sa setMetaInformation()
1024*/
1025QString QTextDocument::metaInformation(MetaInformation info) const
1026{
1027 Q_D(const QTextDocument);
1028 switch (info) {
1029 case DocumentTitle:
1030 return d->title;
1031 case DocumentUrl:
1032 return d->url;
1033 }
1034 return QString();
1035}
1036
1037/*!
1038 Sets the document's meta information of the type specified by \a info
1039 to the given \a string.
1040
1041 \sa metaInformation()
1042*/
1043void QTextDocument::setMetaInformation(MetaInformation info, const QString &string)
1044{
1045 Q_D(QTextDocument);
1046 switch (info) {
1047 case DocumentTitle:
1048 d->title = string;
1049 break;
1050 case DocumentUrl:
1051 d->url = string;
1052 break;
1053 }
1054}
1055
1056/*!
1057 Returns the plain text contained in the document. If you want
1058 formatting information use a QTextCursor instead.
1059
1060 \sa toHtml()
1061*/
1062QString QTextDocument::toPlainText() const
1063{
1064 Q_D(const QTextDocument);
1065 QString txt = d->plainText();
1066
1067 QChar *uc = txt.data();
1068 QChar *e = uc + txt.size();
1069
1070 for (; uc != e; ++uc) {
1071 switch (uc->unicode()) {
1072 case 0xfdd0: // QTextBeginningOfFrame
1073 case 0xfdd1: // QTextEndOfFrame
1074 case QChar::ParagraphSeparator:
1075 case QChar::LineSeparator:
1076 *uc = QLatin1Char('\n');
1077 break;
1078 case QChar::Nbsp:
1079 *uc = QLatin1Char(' ');
1080 break;
1081 default:
1082 ;
1083 }
1084 }
1085 return txt;
1086}
1087
1088/*!
1089 Replaces the entire contents of the document with the given plain
1090 \a text.
1091
1092 \sa setHtml()
1093*/
1094void QTextDocument::setPlainText(const QString &text)
1095{
1096 Q_D(QTextDocument);
1097 bool previousState = d->isUndoRedoEnabled();
1098 d->enableUndoRedo(false);
1099 d->clear();
1100 QTextCursor(this).insertText(text);
1101 d->enableUndoRedo(previousState);
1102}
1103
1104/*!
1105 Replaces the entire contents of the document with the given
1106 HTML-formatted text in the \a html string.
1107
1108 The HTML formatting is respected as much as possible; for example,
1109 "<b>bold</b> text" will produce text where the first word has a font
1110 weight that gives it a bold appearance: "\bold{bold} text".
1111
1112 \note It is the responsibility of the caller to make sure that the
1113 text is correctly decoded when a QString containing HTML is created
1114 and passed to setHtml().
1115
1116 \sa setPlainText(), {Supported HTML Subset}
1117*/
1118
1119#ifndef QT_NO_TEXTHTMLPARSER
1120
1121void QTextDocument::setHtml(const QString &html)
1122{
1123 Q_D(QTextDocument);
1124 bool previousState = d->isUndoRedoEnabled();
1125 d->enableUndoRedo(false);
1126 d->clear();
1127 QTextHtmlImporter(this, html, QTextHtmlImporter::ImportToDocument).import();
1128 d->enableUndoRedo(previousState);
1129}
1130
1131#endif // QT_NO_TEXTHTMLPARSER
1132
1133/*!
1134 \enum QTextDocument::FindFlag
1135
1136 This enum describes the options available to QTextDocument's find function. The options
1137 can be OR-ed together from the following list:
1138
1139 \value FindBackward Search backwards instead of forwards.
1140 \value FindCaseSensitively By default find works case insensitive. Specifying this option
1141 changes the behaviour to a case sensitive find operation.
1142 \value FindWholeWords Makes find match only complete words.
1143*/
1144
1145/*!
1146 \enum QTextDocument::MetaInformation
1147
1148 This enum describes the different types of meta information that can be
1149 added to a document.
1150
1151 \value DocumentTitle The title of the document.
1152 \value DocumentUrl The url of the document. The loadResource() function uses
1153 this url as the base when loading relative resources.
1154
1155 \sa metaInformation(), setMetaInformation()
1156*/
1157
1158/*!
1159 \fn QTextCursor QTextDocument::find(const QString &subString, int position, FindFlags options) const
1160
1161 \overload
1162
1163 Finds the next occurrence of the string, \a subString, in the document.
1164 The search starts at the given \a position, and proceeds forwards
1165 through the document unless specified otherwise in the search options.
1166 The \a options control the type of search performed.
1167
1168 Returns a cursor with the match selected if \a subString
1169 was found; otherwise returns a null cursor.
1170
1171 If the \a position is 0 (the default) the search begins from the beginning
1172 of the document; otherwise it begins at the specified position.
1173*/
1174QTextCursor QTextDocument::find(const QString &subString, int from, FindFlags options) const
1175{
1176 QRegExp expr(subString);
1177 expr.setPatternSyntax(QRegExp::FixedString);
1178 expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
1179
1180 return find(expr, from, options);
1181}
1182
1183/*!
1184 \fn QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &cursor, FindFlags options) const
1185
1186 Finds the next occurrence of the string, \a subString, in the document.
1187 The search starts at the position of the given \a cursor, and proceeds
1188 forwards through the document unless specified otherwise in the search
1189 options. The \a options control the type of search performed.
1190
1191 Returns a cursor with the match selected if \a subString was found; otherwise
1192 returns a null cursor.
1193
1194 If the given \a cursor has a selection, the search begins after the
1195 selection; otherwise it begins at the cursor's position.
1196
1197 By default the search is case-sensitive, and can match text anywhere in the
1198 document.
1199*/
1200QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &from, FindFlags options) const
1201{
1202 int pos = 0;
1203 if (!from.isNull()) {
1204 if (options & QTextDocument::FindBackward)
1205 pos = from.selectionStart();
1206 else
1207 pos = from.selectionEnd();
1208 }
1209 QRegExp expr(subString);
1210 expr.setPatternSyntax(QRegExp::FixedString);
1211 expr.setCaseSensitivity((options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
1212
1213 return find(expr, pos, options);
1214}
1215
1216
1217static bool findInBlock(const QTextBlock &block, const QRegExp &expression, int offset,
1218 QTextDocument::FindFlags options, QTextCursor &cursor)
1219{
1220 const QRegExp expr(expression);
1221 QString text = block.text();
1222 text.replace(QChar::Nbsp, QLatin1Char(' '));
1223
1224 int idx = -1;
1225 while (offset >=0 && offset <= text.length()) {
1226 idx = (options & QTextDocument::FindBackward) ?
1227 expr.lastIndexIn(text, offset) : expr.indexIn(text, offset);
1228 if (idx == -1)
1229 return false;
1230
1231 if (options & QTextDocument::FindWholeWords) {
1232 const int start = idx;
1233 const int end = start + expr.matchedLength();
1234 if ((start != 0 && text.at(start - 1).isLetterOrNumber())
1235 || (end != text.length() && text.at(end).isLetterOrNumber())) {
1236 //if this is not a whole word, continue the search in the string
1237 offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
1238 idx = -1;
1239 continue;
1240 }
1241 }
1242 //we have a hit, return the cursor for that.
1243 break;
1244 }
1245 if (idx == -1)
1246 return false;
1247 cursor = QTextCursor(block.docHandle(), block.position() + idx);
1248 cursor.setPosition(cursor.position() + expr.matchedLength(), QTextCursor::KeepAnchor);
1249 return true;
1250}
1251
1252/*!
1253 \fn QTextCursor QTextDocument::find(const QRegExp & expr, int position, FindFlags options) const
1254
1255 \overload
1256
1257 Finds the next occurrence, matching the regular expression, \a expr, in the document.
1258 The search starts at the given \a position, and proceeds forwards
1259 through the document unless specified otherwise in the search options.
1260 The \a options control the type of search performed. The FindCaseSensitively
1261 option is ignored for this overload, use QRegExp::caseSensitivity instead.
1262
1263 Returns a cursor with the match selected if a match was found; otherwise
1264 returns a null cursor.
1265
1266 If the \a position is 0 (the default) the search begins from the beginning
1267 of the document; otherwise it begins at the specified position.
1268*/
1269QTextCursor QTextDocument::find(const QRegExp & expr, int from, FindFlags options) const
1270{
1271 Q_D(const QTextDocument);
1272
1273 if (expr.isEmpty())
1274 return QTextCursor();
1275
1276 int pos = from;
1277 //the cursor is positioned between characters, so for a backward search
1278 //do not include the character given in the position.
1279 if (options & FindBackward) {
1280 --pos ;
1281 if(pos < 0)
1282 return QTextCursor();
1283 }
1284
1285 QTextCursor cursor;
1286 QTextBlock block = d->blocksFind(pos);
1287
1288 if (!(options & FindBackward)) {
1289 int blockOffset = qMax(0, pos - block.position());
1290 while (block.isValid()) {
1291 if (findInBlock(block, expr, blockOffset, options, cursor))
1292 return cursor;
1293 blockOffset = 0;
1294 block = block.next();
1295 }
1296 } else {
1297 int blockOffset = pos - block.position();
1298 while (block.isValid()) {
1299 if (findInBlock(block, expr, blockOffset, options, cursor))
1300 return cursor;
1301 block = block.previous();
1302 blockOffset = block.length() - 1;
1303 }
1304 }
1305
1306 return QTextCursor();
1307}
1308
1309/*!
1310 \fn QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &cursor, FindFlags options) const
1311
1312 Finds the next occurrence, matching the regular expression, \a expr, in the document.
1313 The search starts at the position of the given \a cursor, and proceeds
1314 forwards through the document unless specified otherwise in the search
1315 options. The \a options control the type of search performed. The FindCaseSensitively
1316 option is ignored for this overload, use QRegExp::caseSensitivity instead.
1317
1318 Returns a cursor with the match selected if a match was found; otherwise
1319 returns a null cursor.
1320
1321 If the given \a cursor has a selection, the search begins after the
1322 selection; otherwise it begins at the cursor's position.
1323
1324 By default the search is case-sensitive, and can match text anywhere in the
1325 document.
1326*/
1327QTextCursor QTextDocument::find(const QRegExp &expr, const QTextCursor &from, FindFlags options) const
1328{
1329 int pos = 0;
1330 if (!from.isNull()) {
1331 if (options & QTextDocument::FindBackward)
1332 pos = from.selectionStart();
1333 else
1334 pos = from.selectionEnd();
1335 }
1336 return find(expr, pos, options);
1337}
1338
1339
1340/*!
1341 \fn QTextObject *QTextDocument::createObject(const QTextFormat &format)
1342
1343 Creates and returns a new document object (a QTextObject), based
1344 on the given \a format.
1345
1346 QTextObjects will always get created through this method, so you
1347 must reimplement it if you use custom text objects inside your document.
1348*/
1349QTextObject *QTextDocument::createObject(const QTextFormat &f)
1350{
1351 QTextObject *obj = 0;
1352 if (f.isListFormat())
1353 obj = new QTextList(this);
1354 else if (f.isTableFormat())
1355 obj = new QTextTable(this);
1356 else if (f.isFrameFormat())
1357 obj = new QTextFrame(this);
1358
1359 return obj;
1360}
1361
1362/*!
1363 \internal
1364
1365 Returns the frame that contains the text cursor position \a pos.
1366*/
1367QTextFrame *QTextDocument::frameAt(int pos) const
1368{
1369 Q_D(const QTextDocument);
1370 return d->frameAt(pos);
1371}
1372
1373/*!
1374 Returns the document's root frame.
1375*/
1376QTextFrame *QTextDocument::rootFrame() const
1377{
1378 Q_D(const QTextDocument);
1379 return d->rootFrame();
1380}
1381
1382/*!
1383 Returns the text object associated with the given \a objectIndex.
1384*/
1385QTextObject *QTextDocument::object(int objectIndex) const
1386{
1387 Q_D(const QTextDocument);
1388 return d->objectForIndex(objectIndex);
1389}
1390
1391/*!
1392 Returns the text object associated with the format \a f.
1393*/
1394QTextObject *QTextDocument::objectForFormat(const QTextFormat &f) const
1395{
1396 Q_D(const QTextDocument);
1397 return d->objectForFormat(f);
1398}
1399
1400
1401/*!
1402 Returns the text block that contains the \a{pos}-th character.
1403*/
1404QTextBlock QTextDocument::findBlock(int pos) const
1405{
1406 Q_D(const QTextDocument);
1407 return QTextBlock(docHandle(), d->blockMap().findNode(pos));
1408}
1409
1410/*!
1411 \since 4.4
1412 Returns the text block with the specified \a blockNumber.
1413
1414 \sa QTextBlock::blockNumber()
1415*/
1416QTextBlock QTextDocument::findBlockByNumber(int blockNumber) const
1417{
1418 Q_D(const QTextDocument);
1419 return QTextBlock(docHandle(), d->blockMap().findNode(blockNumber, 1));
1420}
1421
1422/*!
1423 \since 4.5
1424 Returns the text block that contains the specified \a lineNumber.
1425
1426 \sa QTextBlock::firstLineNumber()
1427*/
1428QTextBlock QTextDocument::findBlockByLineNumber(int lineNumber) const
1429{
1430 Q_D(const QTextDocument);
1431 return QTextBlock(docHandle(), d->blockMap().findNode(lineNumber, 2));
1432}
1433
1434/*!
1435 Returns the document's first text block.
1436
1437 \sa firstBlock()
1438*/
1439QTextBlock QTextDocument::begin() const
1440{
1441 Q_D(const QTextDocument);
1442 return QTextBlock(docHandle(), d->blockMap().begin().n);
1443}
1444
1445/*!
1446 This function returns a block to test for the end of the document
1447 while iterating over it.
1448
1449 \snippet doc/src/snippets/textdocumentendsnippet.cpp 0
1450
1451 The block returned is invalid and represents the block after the
1452 last block in the document. You can use lastBlock() to retrieve the
1453 last valid block of the document.
1454
1455 \sa lastBlock()
1456*/
1457QTextBlock QTextDocument::end() const
1458{
1459 return QTextBlock(docHandle(), 0);
1460}
1461
1462/*!
1463 \since 4.4
1464 Returns the document's first text block.
1465*/
1466QTextBlock QTextDocument::firstBlock() const
1467{
1468 Q_D(const QTextDocument);
1469 return QTextBlock(docHandle(), d->blockMap().begin().n);
1470}
1471
1472/*!
1473 \since 4.4
1474 Returns the document's last (valid) text block.
1475*/
1476QTextBlock QTextDocument::lastBlock() const
1477{
1478 Q_D(const QTextDocument);
1479 return QTextBlock(docHandle(), d->blockMap().last().n);
1480}
1481
1482/*!
1483 \property QTextDocument::pageSize
1484 \brief the page size that should be used for laying out the document
1485
1486 By default, for a newly-created, empty document, this property contains
1487 an undefined size.
1488
1489 \sa modificationChanged()
1490*/
1491
1492void QTextDocument::setPageSize(const QSizeF &size)
1493{
1494 Q_D(QTextDocument);
1495 d->pageSize = size;
1496 if (d->lout)
1497 d->lout->documentChanged(0, 0, d->length());
1498}
1499
1500QSizeF QTextDocument::pageSize() const
1501{
1502 Q_D(const QTextDocument);
1503 return d->pageSize;
1504}
1505
1506/*!
1507 returns the number of pages in this document.
1508*/
1509int QTextDocument::pageCount() const
1510{
1511 return documentLayout()->pageCount();
1512}
1513
1514/*!
1515 Sets the default \a font to use in the document layout.
1516*/
1517void QTextDocument::setDefaultFont(const QFont &font)
1518{
1519 Q_D(QTextDocument);
1520 d->setDefaultFont(font);
1521 if (d->lout)
1522 d->lout->documentChanged(0, 0, d->length());
1523}
1524
1525/*!
1526 Returns the default font to be used in the document layout.
1527*/
1528QFont QTextDocument::defaultFont() const
1529{
1530 Q_D(const QTextDocument);
1531 return d->defaultFont();
1532}
1533
1534/*!
1535 \fn QTextDocument::modificationChanged(bool changed)
1536
1537 This signal is emitted whenever the content of the document
1538 changes in a way that affects the modification state. If \a
1539 changed is true, the document has been modified; otherwise it is
1540 false.
1541
1542 For example, calling setModified(false) on a document and then
1543 inserting text causes the signal to get emitted. If you undo that
1544 operation, causing the document to return to its original
1545 unmodified state, the signal will get emitted again.
1546*/
1547
1548/*!
1549 \property QTextDocument::modified
1550 \brief whether the document has been modified by the user
1551
1552 By default, this property is false.
1553
1554 \sa modificationChanged()
1555*/
1556
1557bool QTextDocument::isModified() const
1558{
1559 return docHandle()->isModified();
1560}
1561
1562void QTextDocument::setModified(bool m)
1563{
1564 docHandle()->setModified(m);
1565}
1566
1567#ifndef QT_NO_PRINTER
1568static void printPage(int index, QPainter *painter, const QTextDocument *doc, const QRectF &body, const QPointF &pageNumberPos)
1569{
1570 painter->save();
1571 painter->translate(body.left(), body.top() - (index - 1) * body.height());
1572 QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
1573
1574 QAbstractTextDocumentLayout *layout = doc->documentLayout();
1575 QAbstractTextDocumentLayout::PaintContext ctx;
1576
1577 painter->setClipRect(view);
1578 ctx.clip = view;
1579
1580 // don't use the system palette text as default text color, on HP/UX
1581 // for example that's white, and white text on white paper doesn't
1582 // look that nice
1583 ctx.palette.setColor(QPalette::Text, Qt::black);
1584
1585 layout->draw(painter, ctx);
1586
1587 if (!pageNumberPos.isNull()) {
1588 painter->setClipping(false);
1589 painter->setFont(QFont(doc->defaultFont()));
1590 const QString pageString = QString::number(index);
1591
1592 painter->drawText(qRound(pageNumberPos.x() - painter->fontMetrics().width(pageString)),
1593 qRound(pageNumberPos.y() + view.top()),
1594 pageString);
1595 }
1596
1597 painter->restore();
1598}
1599
1600extern int qt_defaultDpi();
1601
1602/*!
1603 Prints the document to the given \a printer. The QPrinter must be
1604 set up before being used with this function.
1605
1606 This is only a convenience method to print the whole document to the printer.
1607
1608 If the document is already paginated through a specified height in the pageSize()
1609 property it is printed as-is.
1610
1611 If the document is not paginated, like for example a document used in a QTextEdit,
1612 then a temporary copy of the document is created and the copy is broken into
1613 multiple pages according to the size of the QPrinter's paperRect(). By default
1614 a 2 cm margin is set around the document contents. In addition the current page
1615 number is printed at the bottom of each page.
1616
1617 Note that QPrinter::Selection is not supported as print range with this function since
1618 the selection is a property of QTextCursor. If you have a QTextEdit associated with
1619 your QTextDocument then you can use QTextEdit's print() function because QTextEdit has
1620 access to the user's selection.
1621
1622 \sa QTextEdit::print()
1623*/
1624
1625void QTextDocument::print(QPrinter *printer) const
1626{
1627 Q_D(const QTextDocument);
1628
1629 if (!printer || !printer->isValid())
1630 return;
1631
1632 if (!d->title.isEmpty())
1633 printer->setDocName(d->title);
1634
1635 bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
1636 && d->pageSize.height() != INT_MAX;
1637
1638 if (!documentPaginated && !printer->fullPage() && !printer->d_func()->hasCustomPageMargins)
1639 printer->setPageMargins(23.53, 23.53, 23.53, 23.53, QPrinter::Millimeter);
1640
1641 QPainter p(printer);
1642
1643 // Check that there is a valid device to print to.
1644 if (!p.isActive())
1645 return;
1646
1647 const QTextDocument *doc = this;
1648 QTextDocument *clonedDoc = 0;
1649 (void)doc->documentLayout(); // make sure that there is a layout
1650
1651 QRectF body = QRectF(QPointF(0, 0), d->pageSize);
1652 QPointF pageNumberPos;
1653
1654 if (documentPaginated) {
1655 qreal sourceDpiX = qt_defaultDpi();
1656 qreal sourceDpiY = sourceDpiX;
1657
1658 QPaintDevice *dev = doc->documentLayout()->paintDevice();
1659 if (dev) {
1660 sourceDpiX = dev->logicalDpiX();
1661 sourceDpiY = dev->logicalDpiY();
1662 }
1663
1664 const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
1665 const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
1666
1667 // scale to dpi
1668 p.scale(dpiScaleX, dpiScaleY);
1669
1670 QSizeF scaledPageSize = d->pageSize;
1671 scaledPageSize.rwidth() *= dpiScaleX;
1672 scaledPageSize.rheight() *= dpiScaleY;
1673
1674 const QSizeF printerPageSize(printer->pageRect().size());
1675
1676 // scale to page
1677 p.scale(printerPageSize.width() / scaledPageSize.width(),
1678 printerPageSize.height() / scaledPageSize.height());
1679 } else {
1680 doc = clone(const_cast<QTextDocument *>(this));
1681 clonedDoc = const_cast<QTextDocument *>(doc);
1682
1683 for (QTextBlock srcBlock = firstBlock(), dstBlock = clonedDoc->firstBlock();
1684 srcBlock.isValid() && dstBlock.isValid();
1685 srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
1686 dstBlock.layout()->setAdditionalFormats(srcBlock.layout()->additionalFormats());
1687 }
1688
1689 QAbstractTextDocumentLayout *layout = doc->documentLayout();