Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qtextdocument.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qtextdocument.h"
5#include <qtextformat.h>
6#include "qtextcursor_p.h"
7#include "qtextdocument_p.h"
11#include "qtexttable.h"
12#include "qtextlist.h"
13#include <qdebug.h>
14#include <qloggingcategory.h>
15#if QT_CONFIG(regularexpression)
16#include <qregularexpression.h>
17#endif
18#include <qvarlengtharray.h>
19#include <qthread.h>
20#include <qcoreapplication.h>
21#include <qmetaobject.h>
22
23#include "qtexthtmlparser_p.h"
24#include "qpainter.h"
25#include <qfile.h>
26#include <qfileinfo.h>
27#include <qdir.h>
28#include "qfont_p.h"
29#include "private/qdataurl_p.h"
30
31#include "qtextdocument_p.h"
32#include <private/qabstracttextdocumentlayout_p.h>
33#include "qpagedpaintdevice.h"
34#include "private/qpagedpaintdevice_p.h"
35#if QT_CONFIG(textmarkdownreader)
36#include <private/qtextmarkdownimporter_p.h>
37#endif
38#if QT_CONFIG(textmarkdownwriter)
39#include <private/qtextmarkdownwriter_p.h>
40#endif
41
42#include <limits.h>
43
45
47
48using namespace Qt::StringLiterals;
49
50Q_CORE_EXPORT Q_DECL_CONST_FUNCTION unsigned int qt_int_sqrt(unsigned int n);
51
52namespace {
53 QTextDocument::ResourceProvider qt_defaultResourceProvider;
54};
55
71template <typename T>
73{
74 if (text.isEmpty())
75 return false;
76 qsizetype start = 0;
77
78 while (start < text.size() && QChar(text.at(start)).isSpace())
79 ++start;
80
81 // skip a leading <?xml ... ?> as for example with xhtml
82 if (text.mid(start, 5).compare("<?xml"_L1) == 0) {
83 while (start < text.size()) {
84 if (text.at(start) == u'?'
85 && start + 2 < text.size()
86 && text.at(start + 1) == u'>') {
87 start += 2;
88 break;
89 }
90 ++start;
91 }
92
93 while (start < text.size() && QChar(text.at(start)).isSpace())
94 ++start;
95 }
96
97 if (text.mid(start, 5).compare("<!doc"_L1, Qt::CaseInsensitive) == 0)
98 return true;
100 while (open < text.size() && text.at(open) != u'<'
101 && text.at(open) != u'\n') {
102 if (text.at(open) == u'&' && text.mid(open + 1, 3) == "lt;"_L1)
103 return true; // support desperate attempt of user to see <...>
104 ++open;
105 }
106 if (open < text.size() && text.at(open) == u'<') {
107 const qsizetype close = text.indexOf(u'>', open);
108 if (close > -1) {
109 QVarLengthArray<char16_t> tag;
110 for (qsizetype i = open + 1; i < close; ++i) {
111 const auto current = QChar(text[i]);
112 if (current.isDigit() || current.isLetter())
113 tag.append(current.toLower().unicode());
114 else if (!tag.isEmpty() && current.isSpace())
115 break;
116 else if (!tag.isEmpty() && current == u'/' && i + 1 == close)
117 break;
118 else if (!current.isSpace() && (!tag.isEmpty() || current != u'!'))
119 return false; // that's not a tag
120 }
121#ifndef QT_NO_TEXTHTMLPARSER
123#else
124 return false;
125#endif // QT_NO_TEXTHTMLPARSER
126 }
127 }
128 return false;
129}
130
135
137{
138 return text.visit([](auto text) { return mightBeRichTextImpl(text); });
139}
140
152{
153 qsizetype col = 0;
154 QString rich;
155 rich += "<p>"_L1;
156 for (qsizetype i = 0; i < plain.size(); ++i) {
157 if (plain[i] == u'\n'){
158 qsizetype c = 1;
159 while (i+1 < plain.size() && plain[i+1] == u'\n') {
160 i++;
161 c++;
162 }
163 if (c == 1)
164 rich += "<br>\n"_L1;
165 else {
166 rich += "</p>\n"_L1;
167 while (--c > 1)
168 rich += "<br>\n"_L1;
169 rich += "<p>"_L1;
170 }
171 col = 0;
172 } else {
173 if (mode == Qt::WhiteSpacePre && plain[i] == u'\t'){
174 rich += QChar::Nbsp;
175 ++col;
176 while (col % 8) {
177 rich += QChar::Nbsp;
178 ++col;
179 }
180 }
181 else if (mode == Qt::WhiteSpacePre && plain[i].isSpace())
182 rich += QChar::Nbsp;
183 else if (plain[i] == u'<')
184 rich += "&lt;"_L1;
185 else if (plain[i] == u'>')
186 rich += "&gt;"_L1;
187 else if (plain[i] == u'&')
188 rich += "&amp;"_L1;
189 else
190 rich += plain[i];
191 ++col;
192 }
193 }
194 if (col != 0)
195 rich += "</p>"_L1;
196 return rich;
197}
198
278 : QObject(*new QTextDocumentPrivate, parent)
279{
280 Q_D(QTextDocument);
281 d->init();
282}
283
289 : QObject(*new QTextDocumentPrivate, parent)
290{
291 Q_D(QTextDocument);
292 d->init();
294}
295
300 : QObject(dd, parent)
301{
302 Q_D(QTextDocument);
303 d->init();
304}
305
312
313
319{
320 Q_D(const QTextDocument);
322 if (isEmpty()) {
323 const QTextCursor thisCursor(const_cast<QTextDocument *>(this));
324
325 const auto blockFormat = thisCursor.blockFormat();
326 if (blockFormat.isValid() && !blockFormat.isEmpty())
327 QTextCursor(doc).setBlockFormat(blockFormat);
328
329 const auto blockCharFormat = thisCursor.blockCharFormat();
330 if (blockCharFormat.isValid() && !blockCharFormat.isEmpty())
331 QTextCursor(doc).setBlockCharFormat(blockCharFormat);
332 } else {
334 }
335 doc->rootFrame()->setFrameFormat(rootFrame()->frameFormat());
336 QTextDocumentPrivate *priv = doc->d_func();
337 priv->title = d->title;
338 priv->url = d->url;
339 priv->cssMedia = d->cssMedia;
340 priv->pageSize = d->pageSize;
341 priv->indentWidth = d->indentWidth;
342 priv->defaultTextOption = d->defaultTextOption;
343 priv->setDefaultFont(d->defaultFont());
344 priv->resources = d->resources;
345 priv->cachedResources.clear();
346 priv->resourceProvider = d->resourceProvider;
347#ifndef QT_NO_CSSPARSER
348 priv->defaultStyleSheet = d->defaultStyleSheet;
349 priv->parsedDefaultStyleSheet = d->parsedDefaultStyleSheet;
350#endif
351 return doc;
352}
353
358{
359 Q_D(const QTextDocument);
360 /* because if we're empty we still have one single paragraph as
361 * one single fragment */
362 return d->length() <= 1;
363}
364
369{
370 Q_D(QTextDocument);
371 d->clear();
372 d->resources.clear();
373}
374
388{
389 Q_D(QTextDocument);
390 const int pos = d->undoRedo(true);
391 if (cursor && pos >= 0) {
392 *cursor = QTextCursor(this);
393 cursor->setPosition(pos);
394 }
395}
396
405{
406 Q_D(QTextDocument);
407 const int pos = d->undoRedo(false);
408 if (cursor && pos >= 0) {
409 *cursor = QTextCursor(this);
410 cursor->setPosition(pos);
411 }
412}
413
433{
434 Q_D(QTextDocument);
435 d->clearUndoRedoStacks(stacksToClear, true);
436}
437
443{
444 Q_D(QTextDocument);
445 d->undoRedo(true);
446}
447
453{
454 Q_D(QTextDocument);
455 d->undoRedo(false);
456}
457
464{
465 Q_D(QTextDocument);
466 d->appendUndoItem(item);
467}
468
477{
478 Q_D(QTextDocument);
479 d->enableUndoRedo(enable);
480}
481
483{
484 Q_D(const QTextDocument);
485 return d->isUndoRedoEnabled();
486}
487
510{
511 Q_D(const QTextDocument);
512 return d->maximumBlockCount;
513}
514
516{
517 Q_D(QTextDocument);
518 d->maximumBlockCount = maximum;
519 d->ensureMaximumBlockCount();
520 setUndoRedoEnabled(false);
521}
522
531{
532 Q_D(const QTextDocument);
533 return d->defaultTextOption;
534}
535
542{
543 Q_D(QTextDocument);
544 d->defaultTextOption = option;
545 if (d->lout)
546 d->lout->documentChanged(0, 0, d->length());
547}
548
566{
567 Q_D(const QTextDocument);
568 return d->baseUrl;
569}
570
572{
573 Q_D(QTextDocument);
574 if (d->baseUrl != url) {
575 d->baseUrl = url;
576 if (d->lout)
577 d->lout->documentChanged(0, 0, d->length());
579 }
580}
581
589{
590 Q_D(const QTextDocument);
591 return d->defaultCursorMoveStyle;
592}
593
600{
601 Q_D(QTextDocument);
602 d->defaultCursorMoveStyle = style;
603}
604
613{
614 Q_D(QTextDocument);
615 d->documentChange(from, length);
616 if (!d->inContentsChange) {
617 if (d->lout) {
618 d->lout->documentChanged(d->docChangeFrom, d->docChangeOldLength, d->docChangeLength);
619 d->docChangeFrom = -1;
620 }
621 }
622}
623
641{
642 Q_D(QTextDocument);
643 if (b == d->defaultTextOption.useDesignMetrics())
644 return;
645 d->defaultTextOption.setUseDesignMetrics(b);
646 if (d->lout)
647 d->lout->documentChanged(0, 0, d->length());
648}
649
651{
652 Q_D(const QTextDocument);
653 return d->defaultTextOption.useDesignMetrics();
654}
655
675{
676 Q_D(QTextDocument);
677 if (d->layoutEnabled == b)
678 return;
679 d->layoutEnabled = b;
680 if (b && d->lout)
681 d->lout->documentChanged(0, 0, d->length());
682}
683
685{
686 Q_D(const QTextDocument);
687 return d->layoutEnabled;
688}
689
697{
698 p->save();
700 if (rect.isValid()) {
701 p->setClipRect(rect);
702 ctx.clip = rect;
703 }
705 p->restore();
706}
707
730{
731 Q_D(QTextDocument);
732 QSizeF sz = d->pageSize;
733
734 qCDebug(lcLayout) << "page size" << sz << "-> width" << width;
735 sz.setWidth(width);
736 sz.setHeight(-1);
737 setPageSize(sz);
738}
739
741{
742 Q_D(const QTextDocument);
743 return d->pageSize.width();
744}
745
755{
756 if (QTextDocumentLayout *lout = qobject_cast<QTextDocumentLayout *>(documentLayout()))
757 return lout->idealWidth();
758 return textWidth();
759}
760
768{
769 Q_D(const QTextDocument);
770 return d->documentMargin;
771}
772
774{
775 Q_D(QTextDocument);
776 if (d->documentMargin != margin) {
777 d->documentMargin = margin;
778
779 QTextFrame* root = rootFrame();
781 format.setMargin(margin);
782 root->setFrameFormat(format);
783
784 if (d->lout)
785 d->lout->documentChanged(0, 0, d->length());
786 }
787}
788
789
800{
801 Q_D(const QTextDocument);
802 return d->indentWidth;
803}
804
805
817{
818 Q_D(QTextDocument);
819 if (d->indentWidth != width) {
820 d->indentWidth = width;
821 if (d->lout)
822 d->lout->documentChanged(0, 0, d->length());
823 }
824}
825
826
827
828
837{
838 // Pull this private function in from qglobal.cpp
839 QFont f = defaultFont();
840 QFontMetrics fm(f);
841 int mw = fm.horizontalAdvance(u'x') * 80;
842 int w = mw;
845 if (size.width() != 0) {
846 w = qt_int_sqrt((uint)(5 * size.height() * size.width() / 3));
847 setTextWidth(qMin(w, mw));
848
850 if (w*3 < 5*size.height()) {
851 w = qt_int_sqrt((uint)(2 * size.height() * size.width()));
852 setTextWidth(qMin(w, mw));
853 }
854 }
856}
857
876{
877 return documentLayout()->documentSize();
878}
879
892{
893 Q_D(const QTextDocument);
894 return d->blockMap().numNodes();
895}
896
897
907{
908 Q_D(const QTextDocument);
909 return d->blockMap().length(2);
910}
911
923{
924 Q_D(const QTextDocument);
925 return d->length();
926}
927
937{
938 Q_D(const QTextDocument);
939 if (pos < 0 || pos >= d->length())
940 return QChar();
942 const QTextFragmentData * const frag = fragIt.value();
943 const int offsetInFragment = qMax(0, pos - fragIt.position());
944 return d->text.at(frag->stringPosition + offsetInFragment);
945}
946
947
963#ifndef QT_NO_CSSPARSER
965{
966 Q_D(QTextDocument);
967 d->defaultStyleSheet = sheet;
968 QCss::Parser parser(sheet);
969 d->parsedDefaultStyleSheet = QCss::StyleSheet();
970 d->parsedDefaultStyleSheet.origin = QCss::StyleSheetOrigin_UserAgent;
971 parser.parse(&d->parsedDefaultStyleSheet);
972}
973
975{
976 Q_D(const QTextDocument);
977 return d->defaultStyleSheet;
978}
979#endif // QT_NO_CSSPARSER
980
1063{
1064 Q_D(const QTextDocument);
1065 return d->isUndoAvailable();
1066}
1067
1074{
1075 Q_D(const QTextDocument);
1076 return d->isRedoAvailable();
1077}
1078
1086{
1087 Q_D(const QTextDocument);
1088 return d->availableUndoSteps();
1089}
1090
1098{
1099 Q_D(const QTextDocument);
1100 return d->availableRedoSteps();
1101}
1102
1113{
1114 Q_D(const QTextDocument);
1115 return d->revision;
1116}
1117
1118
1119
1131
1136{
1137 Q_D(const QTextDocument);
1138 if (!d->lout) {
1139 QTextDocument *that = const_cast<QTextDocument *>(this);
1140 that->d_func()->setLayout(new QTextDocumentLayout(that));
1141 }
1142 return d->lout;
1143}
1144
1145
1153{
1154 Q_D(const QTextDocument);
1155 switch (info) {
1156 case DocumentTitle:
1157 return d->title;
1158 case DocumentUrl:
1159 return d->url;
1160 case CssMedia:
1161 return d->cssMedia;
1162 case FrontMatter:
1163 return d->frontMatter;
1164 }
1165 return QString();
1166}
1167
1175{
1176 Q_D(QTextDocument);
1177 switch (info) {
1178 case DocumentTitle:
1179 d->title = string;
1180 break;
1181 case DocumentUrl:
1182 d->url = string;
1183 break;
1184 case CssMedia:
1185 d->cssMedia = string;
1186 break;
1187 case FrontMatter:
1188 d->frontMatter = string;
1189 break;
1190 }
1191}
1192
1202{
1203 Q_D(const QTextDocument);
1204 return d->plainText();
1205}
1206
1225{
1226 Q_D(const QTextDocument);
1227 QString txt = d->plainText();
1228
1229 constexpr char16_t delims[] = { 0xfdd0, 0xfdd1,
1230 QChar::ParagraphSeparator, QChar::LineSeparator, QChar::Nbsp };
1231
1232 const size_t pos = std::u16string_view(txt).find_first_of(
1233 std::u16string_view(delims, std::size(delims)));
1234 if (pos == std::u16string_view::npos)
1235 return txt;
1236
1237 QChar *uc = txt.data();
1238 QChar *const e = uc + txt.size();
1239
1240 for (uc += pos; uc != e; ++uc) {
1241 switch (uc->unicode()) {
1242 case 0xfdd0: // QTextBeginningOfFrame
1243 case 0xfdd1: // QTextEndOfFrame
1244 case QChar::ParagraphSeparator:
1245 case QChar::LineSeparator:
1246 *uc = u'\n';
1247 break;
1248 case QChar::Nbsp:
1249 *uc = u' ';
1250 break;
1251 default:
1252 ;
1253 }
1254 }
1255 return txt;
1256}
1257
1265{
1266 Q_D(QTextDocument);
1267 bool previousState = d->isUndoRedoEnabled();
1268 d->enableUndoRedo(false);
1269 d->beginEditBlock();
1270 d->clear();
1272 d->endEditBlock();
1273 d->enableUndoRedo(previousState);
1274}
1275
1295#ifndef QT_NO_TEXTHTMLPARSER
1296
1298{
1299 Q_D(QTextDocument);
1300 bool previousState = d->isUndoRedoEnabled();
1301 d->enableUndoRedo(false);
1302 d->beginEditBlock();
1303 d->clear();
1304 // ctor calls parse() to build up QTextHtmlParser::nodes list
1305 // then import() populates the QTextDocument from those
1307 d->endEditBlock();
1308 d->enableUndoRedo(previousState);
1309}
1310
1311#endif // QT_NO_TEXTHTMLPARSER
1312
1345static bool findInBlock(const QTextBlock &block, const QString &expression, int offset,
1346 QTextDocument::FindFlags options, QTextCursor *cursor)
1347{
1348 QString text = block.text();
1349 text.replace(QChar::Nbsp, u' ');
1351 int idx = -1;
1352
1353 while (offset >= 0 && offset <= text.size()) {
1354 idx = (options & QTextDocument::FindBackward) ?
1355 text.lastIndexOf(expression, offset, sensitivity) : text.indexOf(expression, offset, sensitivity);
1356 if (idx == -1)
1357 return false;
1358
1359 if (options & QTextDocument::FindWholeWords) {
1360 const int start = idx;
1361 const int end = start + expression.size();
1362 if ((start != 0 && text.at(start - 1).isLetterOrNumber())
1363 || (end != text.size() && text.at(end).isLetterOrNumber())) {
1364 //if this is not a whole word, continue the search in the string
1365 offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
1366 idx = -1;
1367 continue;
1368 }
1369 }
1370 //we have a hit, return the cursor for that.
1372 block.position() + idx);
1373 cursor->setPosition(cursor->position() + expression.size(), QTextCursor::KeepAnchor);
1374 return true;
1375 }
1376 return false;
1377}
1378
1395QTextCursor QTextDocument::find(const QString &subString, int from, FindFlags options) const
1396{
1397 Q_D(const QTextDocument);
1398
1399 if (subString.isEmpty())
1400 return QTextCursor();
1401
1402 int pos = from;
1403 //the cursor is positioned between characters, so for a backward search
1404 //do not include the character given in the position.
1405 if (options & FindBackward) {
1406 --pos ;
1407 if (pos < 0)
1408 return QTextCursor();
1409 }
1410
1412 QTextBlock block = d->blocksFind(pos);
1413 int blockOffset = pos - block.position();
1414
1415 if (!(options & FindBackward)) {
1416 while (block.isValid()) {
1417 if (findInBlock(block, subString, blockOffset, options, &cursor))
1418 return cursor;
1419 block = block.next();
1420 blockOffset = 0;
1421 }
1422 } else {
1423 if (blockOffset == block.length() - 1)
1424 --blockOffset; // make sure to skip end-of-paragraph character
1425 while (block.isValid()) {
1426 if (findInBlock(block, subString, blockOffset, options, &cursor))
1427 return cursor;
1428 block = block.previous();
1429 blockOffset = block.length() - 2;
1430 }
1431 }
1432
1433 return QTextCursor();
1434}
1435
1451QTextCursor QTextDocument::find(const QString &subString, const QTextCursor &cursor, FindFlags options) const
1452{
1453 int pos = 0;
1454 if (!cursor.isNull()) {
1455 if (options & QTextDocument::FindBackward)
1456 pos = cursor.selectionStart();
1457 else
1458 pos = cursor.selectionEnd();
1459 }
1460
1461 return find(subString, pos, options);
1462}
1463
1464#if QT_CONFIG(regularexpression)
1465static bool findInBlock(const QTextBlock &block, const QRegularExpression &expr, int offset,
1466 QTextDocument::FindFlags options, QTextCursor *cursor)
1467{
1468 QString text = block.text();
1469 text.replace(QChar::Nbsp, u' ');
1471 int idx = -1;
1472
1473 while (offset >= 0 && offset <= text.size()) {
1474 idx = (options & QTextDocument::FindBackward) ?
1475 text.lastIndexOf(expr, offset, &match) : text.indexOf(expr, offset, &match);
1476 if (idx == -1)
1477 return false;
1478
1479 if (options & QTextDocument::FindWholeWords) {
1480 const int start = idx;
1481 const int end = start + match.capturedLength();
1482 if ((start != 0 && text.at(start - 1).isLetterOrNumber())
1483 || (end != text.size() && text.at(end).isLetterOrNumber())) {
1484 //if this is not a whole word, continue the search in the string
1485 offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
1486 idx = -1;
1487 continue;
1488 }
1489 }
1490 //we have a hit, return the cursor for that.
1492 block.position() + idx);
1493 cursor->setPosition(cursor->position() + match.capturedLength(), QTextCursor::KeepAnchor);
1494 return true;
1495 }
1496 return false;
1497}
1498
1519QTextCursor QTextDocument::find(const QRegularExpression &expr, int from, FindFlags options) const
1520{
1521 Q_D(const QTextDocument);
1522
1523 if (!expr.isValid())
1524 return QTextCursor();
1525
1526 int pos = from;
1527 //the cursor is positioned between characters, so for a backward search
1528 //do not include the character given in the position.
1529 if (options & FindBackward) {
1530 --pos ;
1531 if (pos < 0)
1532 return QTextCursor();
1533 }
1534
1536 QTextBlock block = d->blocksFind(pos);
1537 int blockOffset = pos - block.position();
1538
1539 QRegularExpression expression(expr);
1540 if (!(options & QTextDocument::FindCaseSensitively))
1541 expression.setPatternOptions(expr.patternOptions() | QRegularExpression::CaseInsensitiveOption);
1542 else
1543 expression.setPatternOptions(expr.patternOptions() & ~QRegularExpression::CaseInsensitiveOption);
1544
1545 if (!(options & FindBackward)) {
1546 while (block.isValid()) {
1547 if (findInBlock(block, expression, blockOffset, options, &cursor))
1548 return cursor;
1549 block = block.next();
1550 blockOffset = 0;
1551 }
1552 } else {
1553 while (block.isValid()) {
1554 if (findInBlock(block, expression, blockOffset, options, &cursor))
1555 return cursor;
1556 block = block.previous();
1557 blockOffset = block.length() - 1;
1558 }
1559 }
1560
1561 return QTextCursor();
1562}
1563
1583QTextCursor QTextDocument::find(const QRegularExpression &expr, const QTextCursor &cursor, FindFlags options) const
1584{
1585 int pos = 0;
1586 if (!cursor.isNull()) {
1587 if (options & QTextDocument::FindBackward)
1588 pos = cursor.selectionStart();
1589 else
1590 pos = cursor.selectionEnd();
1591 }
1592 return find(expr, pos, options);
1593}
1594#endif // QT_CONFIG(regularexpression)
1595
1606{
1607 QTextObject *obj = nullptr;
1608 if (f.isListFormat())
1609 obj = new QTextList(this);
1610 else if (f.isTableFormat())
1611 obj = new QTextTable(this);
1612 else if (f.isFrameFormat())
1613 obj = new QTextFrame(this);
1614
1615 return obj;
1616}
1617
1624{
1625 Q_D(const QTextDocument);
1626 return d->frameAt(pos);
1627}
1628
1633{
1634 Q_D(const QTextDocument);
1635 return d->rootFrame();
1636}
1637
1641QTextObject *QTextDocument::object(int objectIndex) const
1642{
1643 Q_D(const QTextDocument);
1644 return d->objectForIndex(objectIndex);
1645}
1646
1651{
1652 Q_D(const QTextDocument);
1653 return d->objectForFormat(f);
1654}
1655
1656
1661{
1662 Q_D(const QTextDocument);
1663 return QTextBlock(const_cast<QTextDocumentPrivate *>(d), d->blockMap().findNode(pos));
1664}
1665
1673{
1674 Q_D(const QTextDocument);
1675 return QTextBlock(const_cast<QTextDocumentPrivate *>(d), d->blockMap().findNode(blockNumber, 1));
1676}
1677
1685{
1686 Q_D(const QTextDocument);
1687 return QTextBlock(const_cast<QTextDocumentPrivate *>(d), d->blockMap().findNode(lineNumber, 2));
1688}
1689
1696{
1697 Q_D(const QTextDocument);
1698 return QTextBlock(const_cast<QTextDocumentPrivate *>(d), d->blockMap().begin().n);
1699}
1700
1714{
1715 Q_D(const QTextDocument);
1716 return QTextBlock(const_cast<QTextDocumentPrivate *>(d), 0);
1717}
1718
1724{
1725 Q_D(const QTextDocument);
1726 return QTextBlock(const_cast<QTextDocumentPrivate *>(d), d->blockMap().begin().n);
1727}
1728
1734{
1735 Q_D(const QTextDocument);
1736 return QTextBlock(const_cast<QTextDocumentPrivate *>(d), d->blockMap().last().n);
1737}
1738
1754{
1755 Q_D(QTextDocument);
1756 d->pageSize = size;
1757 if (d->lout)
1758 d->lout->documentChanged(0, 0, d->length());
1759}
1760
1762{
1763 Q_D(const QTextDocument);
1764 return d->pageSize;
1765}
1766
1771{
1772 return documentLayout()->pageCount();
1773}
1774
1779{
1780 Q_D(QTextDocument);
1781 d->setDefaultFont(font);
1782 if (d->lout)
1783 d->lout->documentChanged(0, 0, d->length());
1784}
1785
1790{
1791 Q_D(const QTextDocument);
1792 return d->defaultFont();
1793}
1794
1805{
1806 Q_D(QTextDocument);
1807 d->formats.setSuperScriptBaseline(baseline);
1808}
1809
1819{
1820 Q_D(const QTextDocument);
1821 return d->formats.defaultTextFormat().superScriptBaseline();
1822}
1823
1834{
1835 Q_D(QTextDocument);
1836 d->formats.setSubScriptBaseline(baseline);
1837}
1838
1848{
1849 Q_D(const QTextDocument);
1850 return d->formats.defaultTextFormat().subScriptBaseline();
1851}
1852
1864{
1865 Q_D(QTextDocument);
1866 d->formats.setBaselineOffset(baseline);
1867}
1868
1879{
1880 Q_D(const QTextDocument);
1881 return d->formats.defaultTextFormat().baselineOffset();
1882}
1883
1908{
1909 Q_D(const QTextDocument);
1910 return d->isModified();
1911}
1912
1914{
1915 Q_D(QTextDocument);
1916 d->setModified(m);
1917}
1918
1919#ifndef QT_NO_PRINTER
1920static void printPage(int index, QPainter *painter, const QTextDocument *doc, const QRectF &body, const QPointF &pageNumberPos)
1921{
1922 painter->save();
1923 painter->translate(body.left(), body.top() - (index - 1) * body.height());
1924 QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
1925
1928
1930 ctx.clip = view;
1931
1932 // don't use the system palette text as default text color, on HP/UX
1933 // for example that's white, and white text on white paper doesn't
1934 // look that nice
1935 ctx.palette.setColor(QPalette::Text, Qt::black);
1936
1937 layout->draw(painter, ctx);
1938
1939 if (!pageNumberPos.isNull()) {
1940 painter->setClipping(false);
1941 painter->setFont(QFont(doc->defaultFont()));
1942 const QString pageString = QString::number(index);
1943
1944 painter->drawText(qRound(pageNumberPos.x() - painter->fontMetrics().horizontalAdvance(pageString)),
1945 qRound(pageNumberPos.y() + view.top()),
1946 pageString);
1947 }
1948
1949 painter->restore();
1950}
1951
1971{
1972 Q_D(const QTextDocument);
1973
1974 if (!printer)
1975 return;
1976
1977 bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
1978 && d->pageSize.height() != INT_MAX;
1979
1980 // ### set page size to paginated size?
1982 if (!documentPaginated && m.left() == 0. && m.right() == 0. && m.top() == 0. && m.bottom() == 0.) {
1983 m.setLeft(2);
1984 m.setRight(2);
1985 m.setTop(2);
1986 m.setBottom(2);
1988 }
1989 // ### use the margins correctly
1990
1991 QPainter p(printer);
1992
1993 // Check that there is a valid device to print to.
1994 if (!p.isActive())
1995 return;
1996
1997 const QTextDocument *doc = this;
1998 QScopedPointer<QTextDocument> clonedDoc;
1999 (void)doc->documentLayout(); // make sure that there is a layout
2000
2001 QRectF body = QRectF(QPointF(0, 0), d->pageSize);
2002 QPointF pageNumberPos;
2003
2004 qreal sourceDpiX = qt_defaultDpiX();
2005 qreal sourceDpiY = qt_defaultDpiY();
2006 const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
2007 const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
2008
2009 if (documentPaginated) {
2010
2011 QPaintDevice *dev = doc->documentLayout()->paintDevice();
2012 if (dev) {
2013 sourceDpiX = dev->logicalDpiX();
2014 sourceDpiY = dev->logicalDpiY();
2015 }
2016
2017 // scale to dpi
2018 p.scale(dpiScaleX, dpiScaleY);
2019
2020 QSizeF scaledPageSize = d->pageSize;
2021 scaledPageSize.rwidth() *= dpiScaleX;
2022 scaledPageSize.rheight() *= dpiScaleY;
2023
2024 const QSizeF printerPageSize(printer->width(), printer->height());
2025
2026 // scale to page
2027 p.scale(printerPageSize.width() / scaledPageSize.width(),
2028 printerPageSize.height() / scaledPageSize.height());
2029 } else {
2030 doc = clone(const_cast<QTextDocument *>(this));
2031 clonedDoc.reset(const_cast<QTextDocument *>(doc));
2032
2033 for (QTextBlock srcBlock = firstBlock(), dstBlock = clonedDoc->firstBlock();
2034 srcBlock.isValid() && dstBlock.isValid();
2035 srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
2036 dstBlock.layout()->setFormats(srcBlock.layout()->formats());
2037 }
2038
2040 layout->setPaintDevice(p.device());
2041
2042 // copy the custom object handlers
2043 layout->d_func()->handlers = documentLayout()->d_func()->handlers;
2044
2045 // 2 cm margins, scaled to device in QTextDocumentLayoutPrivate::layoutFrame
2046 const int horizontalMargin = int((2/2.54)*sourceDpiX);
2047 const int verticalMargin = int((2/2.54)*sourceDpiY);
2049 fmt.setLeftMargin(horizontalMargin);
2050 fmt.setRightMargin(horizontalMargin);
2051 fmt.setTopMargin(verticalMargin);
2052 fmt.setBottomMargin(verticalMargin);
2053 doc->rootFrame()->setFrameFormat(fmt);
2054
2055 // pageNumberPos must be in device coordinates, so scale to device here
2056 const int dpiy = p.device()->logicalDpiY();
2057 body = QRectF(0, 0, printer->width(), printer->height());
2058 pageNumberPos = QPointF(body.width() - horizontalMargin * dpiScaleX,
2059 body.height() - verticalMargin * dpiScaleY
2060 + QFontMetrics(doc->defaultFont(), p.device()).ascent()
2061 + 5 * dpiy / 72.0);
2062 clonedDoc->setPageSize(body.size());
2063 }
2064
2065 const QPageRanges pageRanges = printer->pageRanges();
2066 int fromPage = pageRanges.firstPage();
2067 int toPage = pageRanges.lastPage();
2068
2069 if (fromPage == 0 && toPage == 0) {
2070 fromPage = 1;
2071 toPage = doc->pageCount();
2072 }
2073 // paranoia check
2074 fromPage = qMax(1, fromPage);
2075 toPage = qMin(doc->pageCount(), toPage);
2076
2077 if (toPage < fromPage) {
2078 // if the user entered a page range outside the actual number
2079 // of printable pages, just return
2080 return;
2081 }
2082
2083// bool ascending = true;
2084// if (printer->pageOrder() == QPrinter::LastPageFirst) {
2085// int tmp = fromPage;
2086// fromPage = toPage;
2087// toPage = tmp;
2088// ascending = false;
2089// }
2090
2091 int page = fromPage;
2092 while (true) {
2093 if (pageRanges.isEmpty() || pageRanges.contains(page))
2094 printPage(page, &p, doc, body, pageNumberPos);
2095
2096 if (page == toPage)
2097 break;
2098 ++page;
2099 if (!printer->newPage())
2100 return;
2101 }
2102}
2103#endif
2104
2149{
2150 Q_D(const QTextDocument);
2151 const QUrl url = d->baseUrl.resolved(name);
2152 QVariant r = d->resources.value(url);
2153 if (!r.isValid()) {
2154 r = d->cachedResources.value(url);
2155 if (!r.isValid()) {
2156 r = const_cast<QTextDocument *>(this)->loadResource(type, url);
2157 if (!r.isValid()) {
2158 if (d->resourceProvider)
2159 r = d->resourceProvider(url);
2160 else if (auto defaultProvider = defaultResourceProvider())
2161 r = defaultProvider(url);
2162 }
2163 }
2164 }
2165 return r;
2166}
2167
2186void QTextDocument::addResource(int type, const QUrl &name, const QVariant &resource)
2187{
2188 Q_UNUSED(type);
2189 Q_D(QTextDocument);
2190 d->resources.insert(name, resource);
2191}
2192
2201{
2202 Q_D(const QTextDocument);
2203 return d->resourceProvider;
2204}
2205
2221{
2222 Q_D(QTextDocument);
2223 d->resourceProvider = provider;
2224}
2225
2237{
2238 qt_defaultResourceProvider = provider;
2239}
2240
2249{
2250 return qt_defaultResourceProvider;
2251}
2252
2273{
2274 Q_D(QTextDocument);
2275 QVariant r;
2276
2277 QObject *p = parent();
2278 if (p) {
2279 const QMetaObject *me = p->metaObject();
2280 int index = me->indexOfMethod("loadResource(int,QUrl)");
2281 if (index >= 0) {
2282 QMetaMethod loader = me->method(index);
2283 // don't invoke() via a queued connection: this function needs to return a value
2285 }
2286 }
2287
2288 // handle data: URLs
2289 if (r.isNull() && name.scheme().compare("data"_L1, Qt::CaseInsensitive) == 0) {
2290 QString mimetype;
2291 QByteArray payload;
2292 if (qDecodeDataUrl(name, mimetype, payload))
2293 r = payload;
2294 }
2295
2296 // if resource was not loaded try to load it here
2297 if (!qobject_cast<QTextDocument *>(p) && r.isNull()) {
2298 QUrl resourceUrl = name;
2299
2300 if (name.isRelative()) {
2301 QUrl currentURL = d->url;
2302 // For the second case QUrl can merge "#someanchor" with "foo.html"
2303 // correctly to "foo.html#someanchor"
2304 if (!(currentURL.isRelative()
2305 || (currentURL.scheme() == "file"_L1
2306 && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
2307 || (name.hasFragment() && name.path().isEmpty())) {
2308 resourceUrl = currentURL.resolved(name);
2309 } else {
2310 // this is our last resort when current url and new url are both relative
2311 // we try to resolve against the current working directory in the local
2312 // file system.
2313 QFileInfo fi(currentURL.toLocalFile());
2314 if (fi.exists()) {
2315 resourceUrl =
2316 QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
2317 } else if (currentURL.isEmpty()) {
2318 resourceUrl.setScheme("file"_L1);
2319 }
2320 }
2321 }
2322
2323 QString s = resourceUrl.toLocalFile();
2324 QFile f(s);
2325 if (!s.isEmpty() && f.open(QFile::ReadOnly)) {
2326 r = f.readAll();
2327 f.close();
2328 }
2329 }
2330
2331 if (!r.isNull()) {
2332 if (type == ImageResource && r.userType() == QMetaType::QByteArray) {
2333 if (qApp->thread() != QThread::currentThread()) {
2334 // must use images in non-GUI threads
2335 QImage image;
2336 image.loadFromData(r.toByteArray());
2337 if (!image.isNull())
2338 r = image;
2339 } else {
2340 QPixmap pm;
2341 pm.loadFromData(r.toByteArray());
2342 if (!pm.isNull())
2343 r = pm;
2344 }
2345 }
2346 d->cachedResources.insert(name, r);
2347 }
2348 return r;
2349}
2350
2352{
2353 QTextFormat diff = to;
2354
2355 const QMap<int, QVariant> props = to.properties();
2357 it != end; ++it)
2358 if (it.value() == from.property(it.key()))
2359 diff.clearProperty(it.key());
2360
2361 return diff;
2362}
2363
2365{
2367
2368 if (color.alpha() == 255) {
2369 result = color.name();
2370 } else if (color.alpha()) {
2371 QString alphaValue = QString::number(color.alphaF(), 'f', 6);
2372 while (alphaValue.size() > 1 && alphaValue.at(alphaValue.size() - 1) == u'0')
2373 alphaValue.chop(1);
2374 if (alphaValue.at(alphaValue.size() - 1) == u'.')
2375 alphaValue.chop(1);
2376 result = QString::fromLatin1("rgba(%1,%2,%3,%4)").arg(color.red())
2377 .arg(color.green())
2378 .arg(color.blue())
2379 .arg(alphaValue);
2380 } else {
2381 result = "transparent"_L1;
2382 }
2383
2384 return result;
2385}
2386
2388 : doc(_doc), fragmentMarkers(false)
2389{
2390 const QFont defaultFont = doc->defaultFont();
2391 defaultCharFormat.setFont(defaultFont);
2392}
2393
2395{
2396 return format.fontFamilies().toStringList();
2397}
2398
2405{
2406 html = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
2407 "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
2408 "<html><head><meta name=\"qrichtext\" content=\"1\" />"_L1;
2410
2411 fragmentMarkers = (mode == ExportFragment);
2412
2413 html += "<meta charset=\"utf-8\" />"_L1;
2414
2416 if (!title.isEmpty()) {
2417 html += "<title>"_L1;
2418 html += title;
2419 html += "</title>"_L1;
2420 }
2421 html += "<style type=\"text/css\">\n"_L1;
2422 html += "p, li { white-space: pre-wrap; }\n"_L1;
2423 html += "hr { height: 1px; border-width: 0; }\n"_L1;
2424 html += "li.unchecked::marker { content: \"\\2610\"; }\n"_L1;
2425 html += "li.checked::marker { content: \"\\2612\"; }\n"_L1;
2426 html += "</style>"_L1;
2427 html += "</head><body"_L1;
2428
2429 if (mode == ExportEntireDocument) {
2430 html += " style=\""_L1;
2431
2432 emitFontFamily(resolvedFontFamilies(defaultCharFormat));
2433
2434 if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) {
2435 html += " font-size:"_L1;
2436 html += QString::number(defaultCharFormat.fontPointSize());
2437 html += "pt;"_L1;
2438 } else if (defaultCharFormat.hasProperty(QTextFormat::FontPixelSize)) {
2439 html += " font-size:"_L1;
2440 html += QString::number(defaultCharFormat.intProperty(QTextFormat::FontPixelSize));
2441 html += "px;"_L1;
2442 }
2443
2444 html += " font-weight:"_L1;
2445 html += QString::number(defaultCharFormat.fontWeight());
2446 html += u';';
2447
2448 html += " font-style:"_L1;
2449 html += (defaultCharFormat.fontItalic() ? "italic"_L1 : "normal"_L1);
2450 html += u';';
2451
2452 const bool percentSpacing = (defaultCharFormat.fontLetterSpacingType() == QFont::PercentageSpacing);
2453 if (defaultCharFormat.hasProperty(QTextFormat::FontLetterSpacing) &&
2454 (!percentSpacing || defaultCharFormat.fontLetterSpacing() != 0.0)) {
2455 html += " letter-spacing:"_L1;
2456 qreal value = defaultCharFormat.fontLetterSpacing();
2457 if (percentSpacing) // Map to em (100% == 0em)
2458 value = (value / 100) - 1;
2459 html += QString::number(value);
2460 html += percentSpacing ? "em;"_L1 : "px;"_L1;
2461 }
2462
2463 if (defaultCharFormat.hasProperty(QTextFormat::FontWordSpacing) &&
2464 defaultCharFormat.fontWordSpacing() != 0.0) {
2465 html += " word-spacing:"_L1;
2466 html += QString::number(defaultCharFormat.fontWordSpacing());
2467 html += "px;"_L1;
2468 }
2469
2470 QString decorationTag(" text-decoration:"_L1);
2471 bool atLeastOneDecorationSet = false;
2472 if (defaultCharFormat.hasProperty(QTextFormat::FontUnderline) || defaultCharFormat.hasProperty(QTextFormat::TextUnderlineStyle)) {
2473 if (defaultCharFormat.fontUnderline()) {
2474 decorationTag += " underline"_L1;
2475 atLeastOneDecorationSet = true;
2476 }
2477 }
2478 if (defaultCharFormat.hasProperty(QTextFormat::FontOverline)) {
2479 if (defaultCharFormat.fontOverline()) {
2480 decorationTag += " overline"_L1;
2481 atLeastOneDecorationSet = true;
2482 }
2483 }
2484 if (defaultCharFormat.hasProperty(QTextFormat::FontStrikeOut)) {
2485 if (defaultCharFormat.fontStrikeOut()) {
2486 decorationTag += " line-through"_L1;
2487 atLeastOneDecorationSet = true;
2488 }
2489 }
2490 if (atLeastOneDecorationSet)
2491 html += decorationTag + u';';
2492
2493 html += u'\"';
2494
2495 const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2496 emitBackgroundAttribute(fmt);
2497
2498 } else {
2499 defaultCharFormat = QTextCharFormat();
2500 }
2501 html += u'>';
2502
2503 QTextFrameFormat rootFmt = doc->rootFrame()->frameFormat();
2505
2506 QTextFrameFormat defaultFmt;
2507 defaultFmt.setMargin(doc->documentMargin());
2508
2509 if (rootFmt == defaultFmt)
2510 emitFrame(doc->rootFrame()->begin());
2511 else
2512 emitTextFrame(doc->rootFrame());
2513
2514 html += "</body></html>"_L1;
2515 return html;
2516}
2517
2518void QTextHtmlExporter::emitAttribute(const char *attribute, const QString &value)
2519{
2520 html += u' ';
2522 html += "=\""_L1;
2523 html += value.toHtmlEscaped();
2524 html += u'"';
2525}
2526
2527bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
2528{
2529 bool attributesEmitted = false;
2530
2531 {
2532 const QStringList families = resolvedFontFamilies(format);
2533 if (!families.isEmpty() && families != resolvedFontFamilies(defaultCharFormat)) {
2534 emitFontFamily(families);
2535 attributesEmitted = true;
2536 }
2537 }
2538
2539 if (format.hasProperty(QTextFormat::FontPointSize)
2540 && format.fontPointSize() != defaultCharFormat.fontPointSize()) {
2541 html += " font-size:"_L1;
2542 html += QString::number(format.fontPointSize());
2543 html += "pt;"_L1;
2544 attributesEmitted = true;
2545 } else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) {
2546 static const char sizeNameData[] =
2547 "small" "\0"
2548 "medium" "\0"
2549 "xx-large" ;
2550 static const quint8 sizeNameOffsets[] = {
2551 0, // "small"
2552 sizeof("small"), // "medium"
2553 sizeof("small") + sizeof("medium") + 3, // "large" )
2554 sizeof("small") + sizeof("medium") + 1, // "x-large" )> compressed into "xx-large"
2555 sizeof("small") + sizeof("medium"), // "xx-large" )
2556 };
2557 const char *name = nullptr;
2558 const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1;
2559 if (idx >= 0 && idx <= 4) {
2560 name = sizeNameData + sizeNameOffsets[idx];
2561 }
2562 if (name) {
2563 html += " font-size:"_L1;
2564 html += QLatin1StringView(name);
2565 html += u';';
2566 attributesEmitted = true;
2567 }
2568 } else if (format.hasProperty(QTextFormat::FontPixelSize)
2570 != defaultCharFormat.property(QTextFormat::FontPixelSize)) {
2571 html += " font-size:"_L1;
2573 html += "px;"_L1;
2574 attributesEmitted = true;
2575 }
2576
2577 if (format.hasProperty(QTextFormat::FontWeight)
2578 && format.fontWeight() != defaultCharFormat.fontWeight()) {
2579 html += " font-weight:"_L1;
2580 html += QString::number(format.fontWeight());
2581 html += u';';
2582 attributesEmitted = true;
2583 }
2584
2585 if (format.hasProperty(QTextFormat::FontItalic)
2586 && format.fontItalic() != defaultCharFormat.fontItalic()) {
2587 html += " font-style:"_L1;
2588 html += (format.fontItalic() ? "italic"_L1 : "normal"_L1);
2589 html += u';';
2590 attributesEmitted = true;
2591 }
2592
2593 const auto decorationTag = " text-decoration:"_L1;
2594 html += decorationTag;
2595 bool hasDecoration = false;
2596 bool atLeastOneDecorationSet = false;
2597
2599 && format.fontUnderline() != defaultCharFormat.fontUnderline()) {
2600 hasDecoration = true;
2601 if (format.fontUnderline()) {
2602 html += " underline"_L1;
2603 atLeastOneDecorationSet = true;
2604 }
2605 }
2606
2607 if (format.hasProperty(QTextFormat::FontOverline)
2608 && format.fontOverline() != defaultCharFormat.fontOverline()) {
2609 hasDecoration = true;
2610 if (format.fontOverline()) {
2611 html += " overline"_L1;
2612 atLeastOneDecorationSet = true;
2613 }
2614 }
2615
2616 if (format.hasProperty(QTextFormat::FontStrikeOut)
2617 && format.fontStrikeOut() != defaultCharFormat.fontStrikeOut()) {
2618 hasDecoration = true;
2619 if (format.fontStrikeOut()) {
2620 html += " line-through"_L1;
2621 atLeastOneDecorationSet = true;
2622 }
2623 }
2624
2625 if (hasDecoration) {
2626 if (!atLeastOneDecorationSet)
2627 html += "none"_L1;
2628 html += u';';
2629 if (format.hasProperty(QTextFormat::TextUnderlineColor)) {
2630 html += " text-decoration-color:"_L1;
2631 html += colorValue(format.underlineColor());
2632 html += u';';
2633 }
2634 attributesEmitted = true;
2635 } else {
2636 html.chop(decorationTag.size());
2637 }
2638
2639 if (format.foreground() != defaultCharFormat.foreground()
2640 && format.foreground().style() != Qt::NoBrush) {
2641 QBrush brush = format.foreground();
2642 if (brush.style() == Qt::TexturePattern) {
2643 const bool isPixmap = qHasPixmapTexture(brush);
2644 const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
2645
2646 html += " -qt-fg-texture-cachekey:"_L1;
2647 html += QString::number(cacheKey);
2648 html += ";"_L1;
2649 } else if (brush.style() == Qt::LinearGradientPattern
2650 || brush.style() == Qt::RadialGradientPattern
2651 || brush.style() == Qt::ConicalGradientPattern) {
2652 const QGradient *gradient = brush.gradient();
2653 if (gradient->type() == QGradient::LinearGradient) {
2654 const QLinearGradient *linearGradient = static_cast<const QLinearGradient *>(brush.gradient());
2655
2656 html += " -qt-foreground: qlineargradient("_L1;
2657 html += "x1:"_L1 + QString::number(linearGradient->start().x()) + u',';
2658 html += "y1:"_L1 + QString::number(linearGradient->start().y()) + u',';
2659 html += "x2:"_L1 + QString::number(linearGradient->finalStop().x()) + u',';
2660 html += "y2:"_L1 + QString::number(linearGradient->finalStop().y()) + u',';
2661 } else if (gradient->type() == QGradient::RadialGradient) {
2662 const QRadialGradient *radialGradient = static_cast<const QRadialGradient *>(brush.gradient());
2663
2664 html += " -qt-foreground: qradialgradient("_L1;
2665 html += "cx:"_L1 + QString::number(radialGradient->center().x()) + u',';
2666 html += "cy:"_L1 + QString::number(radialGradient->center().y()) + u',';
2667 html += "fx:"_L1 + QString::number(radialGradient->focalPoint().x()) + u',';
2668 html += "fy:"_L1 + QString::number(radialGradient->focalPoint().y()) + u',';
2669 html += "radius:"_L1 + QString::number(radialGradient->radius()) + u',';
2670 } else {
2671 const QConicalGradient *conicalGradient = static_cast<const QConicalGradient *>(brush.gradient());
2672
2673 html += " -qt-foreground: qconicalgradient("_L1;
2674 html += "cx:"_L1 + QString::number(conicalGradient->center().x()) + u',';
2675 html += "cy:"_L1 + QString::number(conicalGradient->center().y()) + u',';
2676 html += "angle:"_L1 + QString::number(conicalGradient->angle()) + u',';
2677 }
2678
2679 const QStringList coordinateModes = { "logical"_L1, "stretchtodevice"_L1, "objectbounding"_L1, "object"_L1 };
2680 html += "coordinatemode:"_L1;
2681 html += coordinateModes.at(int(gradient->coordinateMode()));
2682 html += u',';
2683
2684 const QStringList spreads = { "pad"_L1, "reflect"_L1, "repeat"_L1 };
2685 html += "spread:"_L1;
2686 html += spreads.at(int(gradient->spread()));
2687
2688 for (const QGradientStop &stop : gradient->stops()) {
2689 html += ",stop:"_L1;
2690 html += QString::number(stop.first);
2691 html += u' ';
2692 html += colorValue(stop.second);
2693 }
2694
2695 html += ");"_L1;
2696 } else {
2697 html += " color:"_L1;
2698 html += colorValue(brush.color());
2699 html += u';';
2700 }
2701 attributesEmitted = true;
2702 }
2703
2704 if (format.background() != defaultCharFormat.background()
2705 && format.background().style() == Qt::SolidPattern) {
2706 html += " background-color:"_L1;
2707 html += colorValue(format.background().color());
2708 html += u';';
2709 attributesEmitted = true;
2710 }
2711
2712 if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()
2713 && format.verticalAlignment() != QTextCharFormat::AlignNormal)
2714 {
2715 html += " vertical-align:"_L1;
2716
2717 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2718 if (valign == QTextCharFormat::AlignSubScript)
2719 html += "sub"_L1;
2720 else if (valign == QTextCharFormat::AlignSuperScript)
2721 html += "super"_L1;
2722 else if (valign == QTextCharFormat::AlignMiddle)
2723 html += "middle"_L1;
2724 else if (valign == QTextCharFormat::AlignTop)
2725 html += "top"_L1;
2726 else if (valign == QTextCharFormat::AlignBottom)
2727 html += "bottom"_L1;
2728
2729 html += u';';
2730 attributesEmitted = true;
2731 }
2732
2733 if (format.fontCapitalization() != QFont::MixedCase) {
2734 const QFont::Capitalization caps = format.fontCapitalization();
2735 if (caps == QFont::AllUppercase)
2736 html += " text-transform:uppercase;"_L1;
2737 else if (caps == QFont::AllLowercase)
2738 html += " text-transform:lowercase;"_L1;
2739 else if (caps == QFont::SmallCaps)
2740 html += " font-variant:small-caps;"_L1;
2741 attributesEmitted = true;
2742 }
2743
2744 if (format.fontWordSpacing() != 0.0) {
2745 html += " word-spacing:"_L1;
2746 html += QString::number(format.fontWordSpacing());
2747 html += "px;"_L1;
2748 attributesEmitted = true;
2749 }
2750
2751 if (format.hasProperty(QTextFormat::TextOutline)) {
2752 QPen outlinePen = format.textOutline();
2753 html += " -qt-stroke-color:"_L1;
2754 html += colorValue(outlinePen.color());
2755 html += u';';
2756
2757 html += " -qt-stroke-width:"_L1;
2758 html += QString::number(outlinePen.widthF());
2759 html += "px;"_L1;
2760
2761 html += " -qt-stroke-linecap:"_L1;
2762 if (outlinePen.capStyle() == Qt::SquareCap)
2763 html += "squarecap;"_L1;
2764 else if (outlinePen.capStyle() == Qt::FlatCap)
2765 html += "flatcap;"_L1;
2766 else if (outlinePen.capStyle() == Qt::RoundCap)
2767 html += "roundcap;"_L1;
2768
2769 html += " -qt-stroke-linejoin:"_L1;
2770 if (outlinePen.joinStyle() == Qt::MiterJoin)
2771 html += "miterjoin;"_L1;
2772 else if (outlinePen.joinStyle() == Qt::SvgMiterJoin)
2773 html += "svgmiterjoin;"_L1;
2774 else if (outlinePen.joinStyle() == Qt::BevelJoin)
2775 html += "beveljoin;"_L1;
2776 else if (outlinePen.joinStyle() == Qt::RoundJoin)
2777 html += "roundjoin;"_L1;
2778
2779 if (outlinePen.joinStyle() == Qt::MiterJoin ||
2780 outlinePen.joinStyle() == Qt::SvgMiterJoin) {
2781 html += " -qt-stroke-miterlimit:"_L1;
2782 html += QString::number(outlinePen.miterLimit());
2783 html += u';';
2784 }
2785
2786 if (outlinePen.style() == Qt::CustomDashLine && !outlinePen.dashPattern().empty()) {
2787 html += " -qt-stroke-dasharray:"_L1;
2788 QString dashArrayString;
2789 QList<qreal> dashes = outlinePen.dashPattern();
2790
2791 for (int i = 0; i < dashes.length() - 1; i++) {
2792 qreal dash = dashes[i];
2793 dashArrayString += QString::number(dash) + u',';
2794 }
2795
2796 dashArrayString += QString::number(dashes.last());
2797 html += dashArrayString;
2798 html += u';';
2799
2800 html += " -qt-stroke-dashoffset:"_L1;
2801 html += QString::number(outlinePen.dashOffset());
2802 html += u';';
2803 }
2804
2805 attributesEmitted = true;
2806 }
2807
2808 return attributesEmitted;
2809}
2810
2811void QTextHtmlExporter::emitTextLength(const char *attribute, const QTextLength &length)
2812{
2813 if (length.type() == QTextLength::VariableLength) // default
2814 return;
2815
2816 html += u' ';
2818 html += "=\""_L1;
2819 html += QString::number(length.rawValue());
2820
2822 html += "%\""_L1;
2823 else
2824 html += u'\"';
2825}
2826
2827void QTextHtmlExporter::emitAlignment(Qt::Alignment align)
2828{
2829 if (align & Qt::AlignLeft)
2830 return;
2831 else if (align & Qt::AlignRight)
2832 html += " align=\"right\""_L1;
2833 else if (align & Qt::AlignHCenter)
2834 html += " align=\"center\""_L1;
2835 else if (align & Qt::AlignJustify)
2836 html += " align=\"justify\""_L1;
2837}
2838
2839void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
2840{
2842 return;
2843
2844 if (mode == EmitStyleTag)
2845 html += " style=\"float:"_L1;
2846 else
2847 html += " float:"_L1;
2848
2850 html += " left;"_L1;
2852 html += " right;"_L1;
2853 else
2854 Q_ASSERT_X(0, "QTextHtmlExporter::emitFloatStyle()", "pos should be a valid enum type");
2855
2856 if (mode == EmitStyleTag)
2857 html += u'\"';
2858}
2859
2861{
2862 switch (style) {
2864 return "none"_L1;
2866 return "dotted"_L1;
2868 return "dashed"_L1;
2870 return "solid"_L1;
2872 return "double"_L1;
2874 return "dot-dash"_L1;
2876 return "dot-dot-dash"_L1;
2878 return "groove"_L1;
2880 return "ridge"_L1;
2882 return "inset"_L1;
2884 return "outset"_L1;
2885 default:
2886 Q_UNREACHABLE();
2887 };
2888 return ""_L1;
2889}
2890
2891void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style)
2892{
2894
2895 html += " border-style:"_L1;
2897 html += u';';
2898}
2899
2900void QTextHtmlExporter::emitPageBreakPolicy(QTextFormat::PageBreakFlags policy)
2901{
2903 html += " page-break-before:always;"_L1;
2904
2906 html += " page-break-after:always;"_L1;
2907}
2908
2909void QTextHtmlExporter::emitFontFamily(const QStringList &families)
2910{
2911 html += " font-family:"_L1;
2912
2913 bool first = true;
2914 for (const QString &family : families) {
2915 auto quote = "\'"_L1;
2916 if (family.contains(u'\''))
2917 quote = "&quot;"_L1;
2918
2919 if (!first)
2920 html += ","_L1;
2921 else
2922 first = false;
2923 html += quote;
2924 html += family.toHtmlEscaped();
2925 html += quote;
2926 }
2927 html += u';';
2928}
2929
2930void QTextHtmlExporter::emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right)
2931{
2932 html += " margin-top:"_L1;
2933 html += top;
2934 html += "px;"_L1;
2935
2936 html += " margin-bottom:"_L1;
2937 html += bottom;
2938 html += "px;"_L1;
2939
2940 html += " margin-left:"_L1;
2941 html += left;
2942 html += "px;"_L1;
2943
2944 html += " margin-right:"_L1;
2945 html += right;
2946 html += "px;"_L1;
2947}
2948
2949void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
2950{
2951 const QTextCharFormat format = fragment.charFormat();
2952
2953 bool closeAnchor = false;
2954
2955 if (format.isAnchor()) {
2956 const auto names = format.anchorNames();
2957 if (!names.isEmpty()) {
2958 html += "<a name=\""_L1;
2959 html += names.constFirst().toHtmlEscaped();
2960 html += "\"></a>"_L1;
2961 }
2962 const QString href = format.anchorHref();
2963 if (!href.isEmpty()) {
2964 html += "<a href=\""_L1;
2965 html += href.toHtmlEscaped();
2966 html += "\">"_L1;
2967 closeAnchor = true;
2968 }
2969 }
2970
2971 QString txt = fragment.text();
2972 const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
2973 const bool isImage = isObject && format.isImageFormat();
2974
2975 const auto styleTag = "<span style=\""_L1;
2976 html += styleTag;
2977
2978 bool attributesEmitted = false;
2979 if (!isImage)
2980 attributesEmitted = emitCharFormatStyle(format);
2981 if (attributesEmitted)
2982 html += "\">"_L1;
2983 else
2984 html.chop(styleTag.size());
2985
2986 if (isObject) {
2987 for (int i = 0; isImage && i < txt.size(); ++i) {
2989
2990 html += "<img"_L1;
2991
2992 QString maxWidthCss;
2993
2994 if (imgFmt.hasProperty(QTextFormat::ImageMaxWidth)) {
2995 auto length = imgFmt.lengthProperty(QTextFormat::ImageMaxWidth);
2996 maxWidthCss += "max-width:"_L1;
2998 maxWidthCss += QString::number(length.rawValue()) + "%;"_L1;
2999 else if (length.type() == QTextLength::FixedLength)
3000 maxWidthCss += QString::number(length.rawValue()) + "px;"_L1;
3001 }
3002
3003 if (imgFmt.hasProperty(QTextFormat::ImageName))
3004 emitAttribute("src", imgFmt.name());
3005
3006 if (imgFmt.hasProperty(QTextFormat::ImageAltText))
3007 emitAttribute("alt", imgFmt.stringProperty(QTextFormat::ImageAltText));
3008
3009 if (imgFmt.hasProperty(QTextFormat::ImageTitle))
3010 emitAttribute("title", imgFmt.stringProperty(QTextFormat::ImageTitle));
3011
3012 if (imgFmt.hasProperty(QTextFormat::ImageWidth))
3013 emitAttribute("width", QString::number(imgFmt.width()));
3014
3015 if (imgFmt.hasProperty(QTextFormat::ImageHeight))
3016 emitAttribute("height", QString::number(imgFmt.height()));
3017
3018 if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
3019 html += " style=\"vertical-align: middle;"_L1 + maxWidthCss + u'\"';
3020 else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
3021 html += " style=\"vertical-align: top;"_L1 + maxWidthCss + u'\"';
3022 else if (!maxWidthCss.isEmpty())
3023 html += " style=\""_L1 + maxWidthCss + u'\"';
3024
3025 if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
3026 emitFloatStyle(imageFrame->frameFormat().position());
3027
3028 html += " />"_L1;
3029 }
3030 } else {
3031 Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
3032
3033 txt = txt.toHtmlEscaped();
3034
3035 // split for [\n{LineSeparator}]
3036 // space in BR on purpose for compatibility with old-fashioned browsers
3037 txt.replace(u'\n', "<br />"_L1);
3038 txt.replace(QChar::LineSeparator, "<br />"_L1);
3039 html += txt;
3040 }
3041
3042 if (attributesEmitted)
3043 html += "</span>"_L1;
3044
3045 if (closeAnchor)
3046 html += "</a>"_L1;
3047}
3048
3049static bool isOrderedList(int style)
3050{
3055 ;
3056}
3057
3058void QTextHtmlExporter::emitBlockAttributes(const QTextBlock &block)
3059{
3061 emitAlignment(format.alignment());
3062
3063 // assume default to not bloat the html too much
3064 // html += " dir='ltr'"_L1;
3065 if (block.textDirection() == Qt::RightToLeft)
3066 html += " dir='rtl'"_L1;
3067
3068 const auto style = " style=\""_L1;
3069 html += style;
3070
3071 const bool emptyBlock = block.begin().atEnd();
3072 if (emptyBlock) {
3073 html += "-qt-paragraph-type:empty;"_L1;
3074 }
3075
3076 emitMargins(QString::number(format.topMargin()),
3077 QString::number(format.bottomMargin()),
3078 QString::number(format.leftMargin()),
3079 QString::number(format.rightMargin()));
3080
3081 html += " -qt-block-indent:"_L1;
3082 html += QString::number(format.indent());
3083 html += u';';
3084
3085 html += " text-indent:"_L1;
3086 html += QString::number(format.textIndent());
3087 html += "px;"_L1;
3088
3089 if (block.userState() != -1) {
3090 html += " -qt-user-state:"_L1;
3091 html += QString::number(block.userState());
3092 html += u';';
3093 }
3094
3095 if (format.lineHeightType() != QTextBlockFormat::SingleHeight) {
3096 html += " line-height:"_L1
3097 + QString::number(format.lineHeight());
3098 switch (format.lineHeightType()) {
3100 html += "%;"_L1;
3101 break;
3103 html += "; -qt-line-height-type: fixed;"_L1;
3104 break;
3106 html += "px;"_L1;
3107 break;
3109 html += "; -qt-line-height-type: line-distance;"_L1;
3110 break;
3111 default:
3112 html += ";"_L1;
3113 break; // Should never reach here
3114 }
3115 }
3116
3117 emitPageBreakPolicy(format.pageBreakPolicy());
3118
3119 QTextCharFormat diff;
3120 if (emptyBlock) { // only print character properties when we don't expect them to be repeated by actual text in the parag
3121 const QTextCharFormat blockCharFmt = block.charFormat();
3122 diff = formatDifference(defaultCharFormat, blockCharFmt).toCharFormat();
3123 }
3124
3126 if (format.hasProperty(QTextFormat::BackgroundBrush)) {
3127 QBrush bg = format.background();
3128 if (bg.style() != Qt::NoBrush)
3130 }
3131
3132 if (!diff.properties().isEmpty())
3133 emitCharFormatStyle(diff);
3134
3135 html += u'"';
3136
3137}
3138
3139void QTextHtmlExporter::emitBlock(const QTextBlock &block)
3140{
3141 if (block.begin().atEnd()) {
3142 // ### HACK, remove once QTextFrame::Iterator is fixed
3143 int p = block.position();
3144 if (p > 0)
3145 --p;
3146
3148 QChar ch = QTextDocumentPrivate::get(doc)->buffer().at(frag->stringPosition);
3150 || ch == QTextEndOfFrame)
3151 return;
3152 }
3153
3154 html += u'\n';
3155
3156 // save and later restore, in case we 'change' the default format by
3157 // emitting block char format information
3158 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
3159
3160 QTextList *list = block.textList();
3161 if (list) {
3162 if (list->itemNumber(block) == 0) { // first item? emit <ul> or appropriate
3163 const QTextListFormat format = list->format();
3164 const int style = format.style();
3165 bool ordered = false;
3166 switch (style) {
3167 case QTextListFormat::ListDisc: html += "<ul"_L1; break;
3168 case QTextListFormat::ListCircle: html += "<ul type=\"circle\""_L1; break;
3169 case QTextListFormat::ListSquare: html += "<ul type=\"square\""_L1; break;
3170 case QTextListFormat::ListDecimal: html += "<ol"_L1; ordered = true; break;
3171 case QTextListFormat::ListLowerAlpha: html += "<ol type=\"a\""_L1; ordered = true; break;
3172 case QTextListFormat::ListUpperAlpha: html += "<ol type=\"A\""_L1; ordered = true; break;
3173 case QTextListFormat::ListLowerRoman: html += "<ol type=\"i\""_L1; ordered = true; break;
3174 case QTextListFormat::ListUpperRoman: html += "<ol type=\"I\""_L1; ordered = true; break;
3175 default: html += "<ul"_L1; // ### should not happen
3176 }
3177
3178 if (ordered && format.start() != 1) {
3179 html += " start=\""_L1;
3180 html += QString::number(format.start());
3181 html += u'"';
3182 }
3183
3184 QString styleString;
3185 styleString += "margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;"_L1;
3186
3187 if (format.hasProperty(QTextFormat::ListIndent)) {
3188 styleString += " -qt-list-indent: "_L1;
3189 styleString += QString::number(format.indent());
3190 styleString += u';';
3191 }
3192
3193 if (format.hasProperty(QTextFormat::ListNumberPrefix)) {
3194 QString numberPrefix = format.numberPrefix();
3195 numberPrefix.replace(u'"', "\\22"_L1);
3196 numberPrefix.replace(u'\'', "\\27"_L1); // FIXME: There's a problem in the CSS parser the prevents this from being correctly restored
3197 styleString += " -qt-list-number-prefix: "_L1;
3198 styleString += u'\'';
3199 styleString += numberPrefix;
3200 styleString += u'\'';
3201 styleString += u';';
3202 }
3203
3204 if (format.hasProperty(QTextFormat::ListNumberSuffix)) {
3205 if (format.numberSuffix() != "."_L1) { // this is our default
3206 QString numberSuffix = format.numberSuffix();
3207 numberSuffix.replace(u'"', "\\22"_L1);
3208 numberSuffix.replace(u'\'', "\\27"_L1); // see above
3209 styleString += " -qt-list-number-suffix: "_L1;
3210 styleString += u'\'';
3211 styleString += numberSuffix;
3212 styleString += u'\'';
3213 styleString += u';';
3214 }
3215 }
3216
3217 html += " style=\""_L1;
3218 html += styleString;
3219 html += "\">\n"_L1;
3220 }
3221
3222 html += "<li"_L1;
3223
3224 const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
3225 if (!blockFmt.properties().isEmpty()) {
3226 html += " style=\""_L1;
3227 emitCharFormatStyle(blockFmt);
3228 html += u'\"';
3229
3230 defaultCharFormat.merge(block.charFormat());
3231 }
3233 switch (block.blockFormat().marker()) {
3235 html += " class=\"checked\""_L1;
3236 break;
3238 html += " class=\"unchecked\""_L1;
3239 break;
3241 break;
3242 }
3243 }
3244 }
3245
3246 const QTextBlockFormat blockFormat = block.blockFormat();
3248 html += "<hr"_L1;
3249
3251 if (width.type() != QTextLength::VariableLength)
3252 emitTextLength("width", width);
3253 html += u' ';
3254
3255 if (blockFormat.hasProperty(QTextFormat::BackgroundBrush)) {
3256 html += "style=\""_L1;
3257 html += "background-color:"_L1;
3258 html += colorValue(qvariant_cast<QBrush>(blockFormat.property(QTextFormat::BackgroundBrush)).color());
3259 html += u';';
3260 html += u'\"';
3261 }
3262
3263 html += "/>"_L1;
3264 return;
3265 }
3266
3267 const bool pre = blockFormat.nonBreakableLines();
3268 if (pre) {
3269 if (list)
3270 html += u'>';
3271 html += "<pre"_L1;
3272 } else if (!list) {
3273 int headingLevel = blockFormat.headingLevel();
3274 if (headingLevel > 0 && headingLevel <= 6)
3275 html += "<h"_L1 + QString::number(headingLevel);
3276 else
3277 html += "<p"_L1;
3278 }
3279
3280 emitBlockAttributes(block);
3281
3282 html += u'>';
3283 if (block.begin().atEnd())
3284 html += "<br />"_L1;
3285
3286 QTextBlock::Iterator it = block.begin();
3287 if (fragmentMarkers && !it.atEnd() && block == doc->begin())
3288 html += "<!--StartFragment-->"_L1;
3289
3290 for (; !it.atEnd(); ++it)
3291 emitFragment(it.fragment());
3292
3293 if (fragmentMarkers && block.position() + block.length() == QTextDocumentPrivate::get(doc)->length())
3294 html += "<!--EndFragment-->"_L1;
3295
3296 QString closeTags;
3297
3298 if (pre)
3299 html += "</pre>"_L1;
3300 else if (list)
3301 closeTags += "</li>"_L1;
3302 else {
3303 int headingLevel = blockFormat.headingLevel();
3304 if (headingLevel > 0 && headingLevel <= 6)
3305 html += QString::asprintf("</h%d>", headingLevel);
3306 else
3307 html += "</p>"_L1;
3308 }
3309
3310 if (list) {
3311 if (list->itemNumber(block) == list->count() - 1) { // last item? close list
3312 if (isOrderedList(list->format().style()))
3313 closeTags += "</ol>"_L1;
3314 else
3315 closeTags += "</ul>"_L1;
3316 }
3317 const QTextBlock nextBlock = block.next();
3318 // If the next block is the beginning of a new deeper nested list, then we don't
3319 // want to close the current list item just yet. This should be closed when this
3320 // item is fully finished
3321 if (nextBlock.isValid() && nextBlock.textList() &&
3322 nextBlock.textList()->itemNumber(nextBlock) == 0 &&
3323 nextBlock.textList()->format().indent() > list->format().indent()) {
3324 QString lastTag;
3325 if (!closingTags.isEmpty() && list->itemNumber(block) == list->count() - 1)
3326 lastTag = closingTags.takeLast();
3327 lastTag.prepend(closeTags);
3328 closingTags << lastTag;
3329 } else if (list->itemNumber(block) == list->count() - 1) {
3330 // If we are at the end of the list now then we can add in the closing tags for that
3331 // current block
3332 html += closeTags;
3333 if (!closingTags.isEmpty())
3334 html += closingTags.takeLast();
3335 } else {
3336 html += closeTags;
3337 }
3338 }
3339
3340 defaultCharFormat = oldDefaultCharFormat;
3341}
3342
3343extern bool qHasPixmapTexture(const QBrush& brush);
3344
3345QString QTextHtmlExporter::findUrlForImage(const QTextDocument *doc, qint64 cacheKey, bool isPixmap)
3346{
3347 QString url;
3348 if (!doc)
3349 return url;
3350
3351 if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent()))
3352 return findUrlForImage(parent, cacheKey, isPixmap);
3353
3355 Q_ASSERT(priv != nullptr);
3356
3357 QMap<QUrl, QVariant>::const_iterator it = priv->cachedResources.constBegin();
3358 for (; it != priv->cachedResources.constEnd(); ++it) {
3359
3360 const QVariant &v = it.value();
3361 if (v.userType() == QMetaType::QImage && !isPixmap) {
3362 if (qvariant_cast<QImage>(v).cacheKey() == cacheKey)
3363 break;
3364 }
3365
3366 if (v.userType() == QMetaType::QPixmap && isPixmap) {
3367 if (qvariant_cast<QPixmap>(v).cacheKey() == cacheKey)
3368 break;
3369 }
3370 }
3371
3372 if (it != priv->cachedResources.constEnd())
3373 url = it.key().toString();
3374
3375 return url;
3376}
3377
3379{
3380 if (!priv)
3381 return;
3382
3383 cachedResources.insert(priv->cachedResources);
3384}
3385
3386void QTextHtmlExporter::emitBackgroundAttribute(const QTextFormat &format)
3387{
3388 if (format.hasProperty(QTextFormat::BackgroundImageUrl)) {
3390 emitAttribute("background", url);
3391 } else {
3392 const QBrush &brush = format.background();
3393 if (brush.style() == Qt::SolidPattern) {
3394 emitAttribute("bgcolor", colorValue(brush.color()));
3395 } else if (brush.style() == Qt::TexturePattern) {
3396 const bool isPixmap = qHasPixmapTexture(brush);
3397 const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
3398
3399 const QString url = findUrlForImage(doc, cacheKey, isPixmap);
3400
3401 if (!url.isEmpty())
3402 emitAttribute("background", url);
3403 }
3404 }
3405}
3406
3407void QTextHtmlExporter::emitTable(const QTextTable *table)
3408{
3409 QTextTableFormat format = table->format();
3410
3411 html += "\n<table"_L1;
3412
3413 if (format.hasProperty(QTextFormat::FrameBorder))
3414 emitAttribute("border", QString::number(format.border()));
3415
3416 emitFrameStyle(format, TableFrame);
3417
3418 emitAlignment(format.alignment());
3419 emitTextLength("width", format.width());
3420
3421 if (format.hasProperty(QTextFormat::TableCellSpacing))
3422 emitAttribute("cellspacing", QString::number(format.cellSpacing()));
3423 if (format.hasProperty(QTextFormat::TableCellPadding))
3424 emitAttribute("cellpadding", QString::number(format.cellPadding()));
3425
3426 emitBackgroundAttribute(format);
3427
3428 html += u'>';
3429
3430 const int rows = table->rows();
3431 const int columns = table->columns();
3432
3433 QList<QTextLength> columnWidths = format.columnWidthConstraints();
3434 if (columnWidths.isEmpty()) {
3435 columnWidths.resize(columns);
3436 columnWidths.fill(QTextLength());
3437 }
3438 Q_ASSERT(columnWidths.size() == columns);
3439
3440 QVarLengthArray<bool> widthEmittedForColumn(columns);
3441 for (int i = 0; i < columns; ++i)
3442 widthEmittedForColumn[i] = false;
3443
3444 const int headerRowCount = qMin(format.headerRowCount(), rows);
3445 if (headerRowCount > 0)
3446 html += "<thead>"_L1;
3447
3448 for (int row = 0; row < rows; ++row) {
3449 html += "\n<tr>"_L1;
3450
3451 for (int col = 0; col < columns; ++col) {
3452 const QTextTableCell cell = table->cellAt(row, col);
3453
3454 // for col/rowspans
3455 if (cell.row() != row)
3456 continue;
3457
3458 if (cell.column() != col)
3459 continue;
3460
3461 html += "\n<td"_L1;
3462
3463 if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
3464 emitTextLength("width", columnWidths.at(col));
3465 widthEmittedForColumn[col] = true;
3466 }
3467
3468 if (cell.columnSpan() > 1)
3469 emitAttribute("colspan", QString::number(cell.columnSpan()));
3470
3471 if (cell.rowSpan() > 1)
3472 emitAttribute("rowspan", QString::number(cell.rowSpan()));
3473
3474 const QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
3475 emitBackgroundAttribute(cellFormat);
3476
3477 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
3478
3480
3481 QString styleString;
3483 styleString += " vertical-align:"_L1;
3484 switch (valign) {
3486 styleString += "middle"_L1;
3487 break;
3489 styleString += "top"_L1;
3490 break;
3492 styleString += "bottom"_L1;
3493 break;
3494 default:
3495 break;
3496 }
3497 styleString += u';';
3498
3499 QTextCharFormat temp;
3500 temp.setVerticalAlignment(valign);
3501 defaultCharFormat.merge(temp);
3502 }
3503
3504 if (cellFormat.hasProperty(QTextFormat::TableCellLeftPadding))
3505 styleString += " padding-left:"_L1 + QString::number(cellFormat.leftPadding()) + u';';
3506 if (cellFormat.hasProperty(QTextFormat::TableCellRightPadding))
3507 styleString += " padding-right:"_L1 + QString::number(cellFormat.rightPadding()) + u';';
3508 if (cellFormat.hasProperty(QTextFormat::TableCellTopPadding))
3509 styleString += " padding-top:"_L1 + QString::number(cellFormat.topPadding()) + u';';
3510 if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
3511 styleString += " padding-bottom:"_L1 + QString::number(cellFormat.bottomPadding()) + u';';
3512
3513 if (cellFormat.hasProperty(QTextFormat::TableCellTopBorder))
3514 styleString += " border-top:"_L1 + QString::number(cellFormat.topBorder()) + "px;"_L1;
3515 if (cellFormat.hasProperty(QTextFormat::TableCellRightBorder))
3516 styleString += " border-right:"_L1 + QString::number(cellFormat.rightBorder()) + "px;"_L1;
3517 if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorder))
3518 styleString += " border-bottom:"_L1 + QString::number(cellFormat.bottomBorder()) + "px;"_L1;
3519 if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorder))
3520 styleString += " border-left:"_L1 + QString::number(cellFormat.leftBorder()) + "px;"_L1;
3521
3522 if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderBrush))
3523 styleString += " border-top-color:"_L1 + cellFormat.topBorderBrush().color().name() + u';';
3524 if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderBrush))
3525 styleString += " border-right-color:"_L1 + cellFormat.rightBorderBrush().color().name() + u';';
3526 if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderBrush))
3527 styleString += " border-bottom-color:"_L1 + cellFormat.bottomBorderBrush().color().name() + u';';
3528 if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderBrush))
3529 styleString += " border-left-color:"_L1 + cellFormat.leftBorderBrush().color().name() + u';';
3530
3531 if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderStyle))
3532 styleString += " border-top-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.topBorderStyle()) + u';';
3533 if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderStyle))
3534 styleString += " border-right-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.rightBorderStyle()) + u';';
3535 if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderStyle))
3536 styleString += " border-bottom-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.bottomBorderStyle()) + u';';
3537 if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderStyle))
3538 styleString += " border-left-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.leftBorderStyle()) + u';';
3539
3540 if (!styleString.isEmpty())
3541 html += " style=\""_L1 + styleString + u'\"';
3542
3543 html += u'>';
3544
3545 emitFrame(cell.begin());
3546
3547 html += "</td>"_L1;
3548
3549 defaultCharFormat = oldDefaultCharFormat;
3550 }
3551
3552 html += "</tr>"_L1;
3553 if (headerRowCount > 0 && row == headerRowCount - 1)
3554 html += "</thead>"_L1;
3555 }
3556
3557 html += "</table>"_L1;
3558}
3559
3560void QTextHtmlExporter::emitFrame(const QTextFrame::Iterator &frameIt)
3561{
3562 if (!frameIt.atEnd()) {
3563 QTextFrame::Iterator next = frameIt;
3564 ++next;
3565 if (next.atEnd()
3566 && frameIt.currentFrame() == nullptr
3567 && frameIt.parentFrame() != doc->rootFrame()
3568 && frameIt.currentBlock().begin().atEnd())
3569 return;
3570 }
3571
3572 for (QTextFrame::Iterator it = frameIt;
3573 !it.atEnd(); ++it) {
3574 if (QTextFrame *f = it.currentFrame()) {
3575 if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
3576 emitTable(table);
3577 } else {
3578 emitTextFrame(f);
3579 }
3580 } else if (it.currentBlock().isValid()) {
3581 emitBlock(it.currentBlock());
3582 }
3583 }
3584}
3585
3586void QTextHtmlExporter::emitTextFrame(const QTextFrame *f)
3587{
3588 FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
3589
3590 html += "\n<table"_L1;
3591 QTextFrameFormat format = f->frameFormat();
3592
3593 if (format.hasProperty(QTextFormat::FrameBorder))
3594 emitAttribute("border", QString::number(format.border()));
3595
3596 emitFrameStyle(format, frameType);
3597
3598 emitTextLength("width", format.width());
3599 emitTextLength("height", format.height());
3600
3601 // root frame's bcolor goes in the <body> tag
3602 if (frameType != RootFrame)
3603 emitBackgroundAttribute(format);
3604
3605 html += u'>';
3606 html += "\n<tr>\n<td style=\"border: none;\">"_L1;
3607 emitFrame(f->begin());
3608 html += "</td></tr></table>"_L1;
3609}
3610
3611void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType frameType)
3612{
3613 const auto styleAttribute = " style=\""_L1;
3614 html += styleAttribute;
3615 const qsizetype originalHtmlLength = html.size();
3616
3617 if (frameType == TextFrame)
3618 html += "-qt-table-type: frame;"_L1;
3619 else if (frameType == RootFrame)
3620 html += "-qt-table-type: root;"_L1;
3621
3622 const QTextFrameFormat defaultFormat;
3623
3624 emitFloatStyle(format.position(), OmitStyleTag);
3625 emitPageBreakPolicy(format.pageBreakPolicy());
3626
3627 if (format.borderBrush() != defaultFormat.borderBrush()) {
3628 html += " border-color:"_L1;
3629 html += colorValue(format.borderBrush().color());
3630 html += u';';
3631 }
3632
3633 if (format.borderStyle() != defaultFormat.borderStyle())
3634 emitBorderStyle(format.borderStyle());
3635
3636 if (format.hasProperty(QTextFormat::FrameMargin)
3637 || format.hasProperty(QTextFormat::FrameLeftMargin)
3639 || format.hasProperty(QTextFormat::FrameTopMargin)
3640 || format.hasProperty(QTextFormat::FrameBottomMargin))
3641 emitMargins(QString::number(format.topMargin()),
3642 QString::number(format.bottomMargin()),
3643 QString::number(format.leftMargin()),
3644 QString::number(format.rightMargin()));
3645
3646 if (format.property(QTextFormat::TableBorderCollapse).toBool())
3647 html += " border-collapse:collapse;"_L1;
3648
3649 if (html.size() == originalHtmlLength) // nothing emitted?
3650 html.chop(styleAttribute.size());
3651 else
3652 html += u'\"';
3653}
3654
3665#ifndef QT_NO_TEXTHTMLPARSER
3667{
3668 return QTextHtmlExporter(this).toHtml();
3669}
3670#endif // QT_NO_TEXTHTMLPARSER
3671
3679#if QT_CONFIG(textmarkdownwriter)
3680QString QTextDocument::toMarkdown(QTextDocument::MarkdownFeatures features) const
3681{
3682 QString ret;
3683 QTextStream s(&ret);
3684 QTextMarkdownWriter w(s, features);
3685 if (w.writeAll(this))
3686 return ret;
3687 return QString();
3688}
3689#endif
3690
3721#if QT_CONFIG(textmarkdownreader)
3722void QTextDocument::setMarkdown(const QString &markdown, QTextDocument::MarkdownFeatures features)
3723{
3724 QTextMarkdownImporter(this, features).import(markdown);
3725}
3726#endif
3727
3731QList<QTextFormat> QTextDocument::allFormats() const
3732{
3733 Q_D(const QTextDocument);
3734 return d->formatCollection()->formats;
3735}
3736
3745
3746#include "moc_qtextdocument.cpp"
virtual int pageCount() const =0
Returns the number of pages contained in the layout.
virtual void draw(QPainter *painter, const PaintContext &context)=0
Draws the layout with the given painter using the given context.
QPaintDevice * paintDevice() const
Returns the paint device used to render the document's layout.
virtual QSizeF documentSize() const =0
Returns the total size of the document's layout.
\inmodule QtCore
\inmodule QtGui
Definition qbrush.h:30
Qt::BrushStyle style() const
Returns the brush style.
Definition qbrush.h:120
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
\inmodule QtGui
Definition qbrush.h:446
bool parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity=Qt::CaseSensitive)
static QChar separator()
Returns the native directory separator: "/" under Unix and "\\" under Windows.
Definition qdir.h:209
bool isAbsolute() const
Returns true if the file system entry's path is absolute, otherwise returns false (that is,...
Definition qfileinfo.h:120
\inmodule QtCore
Definition qfile.h:93
\reentrant \inmodule QtGui
int horizontalAdvance(const QString &, int len=-1) const
Returns the horizontal advance in pixels of the first len characters of text.
int ascent() const
Returns the ascent of the font.
\reentrant
Definition qfont.h:22
Capitalization
Definition qfont.h:97
@ AllLowercase
Definition qfont.h:100
@ AllUppercase
Definition qfont.h:99
@ MixedCase
Definition qfont.h:98
@ SmallCaps
Definition qfont.h:101
@ PercentageSpacing
Definition qfont.h:107
ConstIterator last() const
Iterator begin()
uint findNode(int k, uint field=0) const
\inmodule QtGui
Definition qbrush.h:135
Spread spread() const
Returns the spread method use by this gradient.
Definition qbrush.h:347
CoordinateMode coordinateMode() const
Definition qbrush.cpp:1672
Type type() const
Returns the type of gradient.
Definition qbrush.h:344
@ LinearGradient
Definition qbrush.h:139
@ RadialGradient
Definition qbrush.h:140
\inmodule QtGui
Definition qimage.h:37
\inmodule QtGui
Definition qbrush.h:394
qsizetype count() const noexcept
Definition qlist.h:398
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
\inmodule QtCore
Definition qmargins.h:270
constexpr void setLeft(qreal aleft) noexcept
Sets the left margin to aleft (which must be finite).
Definition qmargins.h:390
\inmodule QtCore
Definition qmetaobject.h:19
bool invoke(QObject *object, Qt::ConnectionType connectionType, QGenericReturnArgument returnValue, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument()) const
\obsolete [6.5] Please use the variadic overload of this function
\inmodule QtCore
Definition qobject.h:103
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
QMarginsF margins() const
Returns the margins of the page layout using the currently set units.
The QPageRanges class represents a collection of page ranges.
Definition qpageranges.h:21
bool isEmpty() const
Returns true if the ranges are empty; otherwise returns false.
int lastPage() const
Returns the index of the last page covered by the page ranges, or 0 if the page ranges are empty.
int firstPage() const
Returns the index of the first page covered by the page ranges, or 0 if the page ranges are empty.
bool contains(int pageNumber) const
Returns true if the ranges include the page pageNumber; otherwise returns false.
\inmodule QtGui
virtual bool newPage()=0
Starts a new page.
virtual bool setPageMargins(const QMarginsF &margins, QPageLayout::Unit units=QPageLayout::Millimeter)
QPageRanges pageRanges() const
QPageLayout pageLayout() const
int logicalDpiX() const
int logicalDpiY() const
int width() const
int height() const
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
void setClipping(bool enable)
Enables clipping if enable is true, or disables clipping if enable is false.
void setClipRect(const QRectF &, Qt::ClipOperation op=Qt::ReplaceClip)
Enables clipping, and sets the clip region to the given rectangle using the given clip operation.
void restore()
Restores the current painter state (pops a saved state off the stack).
QFontMetrics fontMetrics() const
Returns the font metrics for the painter if the painter is active.
void save()
Saves the current painter state (pushes the state onto a stack).
void setFont(const QFont &f)
Sets the painter's font to the given font.
void drawText(const QPointF &p, const QString &s)
Draws the given text with the currently defined text direction, beginning at the given position.
void translate(const QPointF &offset)
Translates the coordinate system by the given offset; i.e.
\inmodule QtGui
Definition qpen.h:28
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
bool isNull() const
Returns true if this is a null pixmap; otherwise returns false.
Definition qpixmap.cpp:456
bool loadFromData(const uchar *buf, uint len, const char *format=nullptr, Qt::ImageConversionFlags flags=Qt::AutoColor)
Loads a pixmap from the len first bytes of the given binary data.
Definition qpixmap.cpp:761
\inmodule QtCore\reentrant
Definition qpoint.h:217
\inmodule QtGui
Definition qbrush.h:412
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:732
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:729
constexpr qreal left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:497
constexpr QSizeF size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:735
constexpr qreal top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:498
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
bool isValid() const
Returns true if the regular expression is a valid regular expression (that is, it contains no syntax ...
PatternOptions patternOptions() const
Returns the pattern options for the regular expression.
iterator begin()
Definition qset.h:136
\inmodule QtCore
Definition qsize.h:208
constexpr qreal & rwidth() noexcept
Returns a reference to the width.
Definition qsize.h:356
constexpr void setHeight(qreal h) noexcept
Sets the height to the given finite height.
Definition qsize.h:341
constexpr void setWidth(qreal w) noexcept
Sets the width to the given finite width.
Definition qsize.h:338
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:296
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3824
void reserve(qsizetype size)
Ensures the string has space for at least size characters.
Definition qstring.h:1325
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6340
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
int compare(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.cpp:6664
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1369
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7263
QString toHtmlEscaped() const
int headingLevel() const
MarkerType marker() const
bool nonBreakableLines() const
Returns true if the lines in the paragraph are non-breakable; otherwise returns false.
bool atEnd() const
Returns true if the current item is the last item in the text block.
\reentrant
iterator begin() const
Returns a text block iterator pointing to the beginning of the text block.
int length() const
Returns the length of the block in characters.
QTextBlockFormat blockFormat() const
Returns the QTextBlockFormat that describes block-specific properties.
bool isValid() const
Returns true if this text block is valid; otherwise returns false.
QTextBlock next() const
Returns the text block in the document after this block, or an empty text block if this is the last o...
int userState() const
int position() const
Returns the index of the block's first character within the document.
QString text() const
Returns the block's contents as plain text.
Qt::LayoutDirection textDirection() const
QTextCharFormat charFormat() const
Returns the QTextCharFormat that describes the block's character format.
QTextList * textList() const
If the block represents a list item, returns the list that the item belongs to; otherwise returns \nu...
QTextBlock previous() const
Returns the text block in the document before this block, or an empty text block if this is the first...
qreal fontLetterSpacing() const
VerticalAlignment
This enum describes the ways that adjacent characters can be vertically aligned.
QStringList anchorNames() const
int fontWeight() const
Returns the text format's font weight.
qreal fontPointSize() const
Returns the font size used to display text in this format.
bool fontStrikeOut() const
Returns true if the text format's font is struck out (has a horizontal line drawn through it); otherw...
void setVerticalAlignment(VerticalAlignment alignment)
Sets the vertical alignment used for the characters with this format to the alignment specified.
qreal fontWordSpacing() const
bool fontOverline() const
Returns true if the text format's font is overlined; otherwise returns false.
VerticalAlignment verticalAlignment() const
Returns the vertical alignment used for characters with this format.
QFont::SpacingType fontLetterSpacingType() const
bool fontItalic() const
Returns true if the text format's font is italic; otherwise returns false.
bool fontUnderline() const
Returns true if the text format's font is underlined; otherwise returns false.
void setFont(const QFont &font, FontPropertiesInheritanceBehavior behavior=FontPropertiesAll)
static QTextCursor fromPosition(QTextDocumentPrivate *d, int pos)
\reentrant \inmodule QtGui
Definition qtextcursor.h:30
void setBlockCharFormat(const QTextCharFormat &format)
Sets the block char format of the current block (or all blocks that are contained in the selection) t...
void insertFragment(const QTextDocumentFragment &fragment)
Inserts the text fragment at the current position().
void setBlockFormat(const QTextBlockFormat &format)
Sets the block format of the current block (or all blocks that are contained in the selection) to for...
void insertText(const QString &text)
Inserts text at the current position, using the current character format.
const BlockMap & blockMap() const
static const QTextDocumentPrivate * get(const QTextDocument *document)
FragmentMap::ConstIterator FragmentIterator
void mergeCachedResources(const QTextDocumentPrivate *priv)
\reentrant \inmodule QtGui
void setModified(bool m=true)
QString defaultStyleSheet
QTextDocument * clone(QObject *parent=nullptr) const
Creates a new QTextDocument that is a copy of this text document.
void setDefaultCursorMoveStyle(Qt::CursorMoveStyle style)
void setSuperScriptBaseline(qreal baseline)
void setBaselineOffset(qreal baseline)
void setIndentWidth(qreal width)
QTextBlock findBlockByLineNumber(int blockNumber) const
int maximumBlockCount
Specifies the limit for blocks in the document.
void setHtml(const QString &html)
Replaces the entire contents of the document with the given HTML-formatted text in the html string.
bool isEmpty() const
Returns true if the document is empty; otherwise returns false.
QTextFrame * frameAt(int pos) const
MetaInformation
This enum describes the different types of meta information that can be added to a document.
qreal baselineOffset() const
qreal idealWidth() const
int lineCount() const
void setDefaultTextOption(const QTextOption &option)
int availableRedoSteps() const
Stacks
\value UndoStack The undo stack.
void setResourceProvider(const ResourceProvider &provider)
bool isLayoutEnabled() const
QList< QTextFormat > allFormats() const
Returns a list of text formats for all the formats used in the document.
bool isRedoAvailable() const
Returns true if redo is available; otherwise returns false.
QFont defaultFont
the default font used to display the document's text
QString toHtml() const
Returns a string containing an HTML representation of the document.
QTextBlock findBlockByNumber(int blockNumber) const
int availableUndoSteps() const
int blockCount
the number of text blocks in the document.
QAbstractTextDocumentLayout * documentLayout() const
Returns the document layout for this document.
QString toRawText() const
Returns the raw text contained in the document without any formatting information.
bool useDesignMetrics
whether the document uses design metrics of fonts to improve the accuracy of text layout
QSizeF size
the actual size of the document. This is equivalent to documentLayout()->documentSize();
QSizeF pageSize
the page size that should be used for laying out the document
void appendUndoItem(QAbstractUndoItem *)
bool isModified() const
QTextObject * objectForFormat(const QTextFormat &) const
Returns the text object associated with the format f.
virtual void clear()
Clears the document.
bool isUndoRedoEnabled() const
int revision() const
void baseUrlChanged(const QUrl &url)
int pageCount() const
returns the number of pages in this document.
QTextBlock begin() const
Returns the document's first text block.
bool isUndoAvailable() const
Returns true if undo is available; otherwise returns false.
void setLayoutEnabled(bool b)
QTextOption defaultTextOption() const
the default text option will be set on all \l{QTextLayout}s in the document.
void print(QPagedPaintDevice *printer) const
Prints the document to the given printer.
QVariant resource(int type, const QUrl &name) const
Returns data of the specified type from the resource with the given name.
void setDefaultFont(const QFont &font)
Sets the default font to use in the document layout.
QChar characterAt(int pos) const
void setPageSize(const QSizeF &size)
QTextDocument(QObject *parent=nullptr)
Constructs an empty QTextDocument with the given parent.
QTextDocument::ResourceProvider resourceProvider() const
void setDefaultStyleSheet(const QString &sheet)
void setUndoRedoEnabled(bool enable)
QUrl baseUrl
the base URL used to resolve relative resource URLs within the document.
QString toPlainText() const
Returns the plain text contained in the document.
virtual Q_INVOKABLE QVariant loadResource(int type, const QUrl &name)
Loads data of the specified type from the resource with the given name.
QTextObject * object(int objectIndex) const
Returns the text object associated with the given objectIndex.
void clearUndoRedoStacks(Stacks historyToClear=UndoAndRedoStacks)
QTextCursor find(const QString &subString, int from=0, FindFlags options=FindFlags()) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setDocumentMargin(qreal margin)
QTextBlock findBlock(int pos) const
Returns the text block that contains the {pos}-th character.
qreal subScriptBaseline() const
QTextBlock end() const
This function returns a block to test for the end of the document while iterating over it.
QString metaInformation(MetaInformation info) const
Returns meta information about the document of the type specified by info.
Qt::CursorMoveStyle defaultCursorMoveStyle() const
QTextBlock firstBlock() const
static void setDefaultResourceProvider(const ResourceProvider &provider)
void setPlainText(const QString &text)
Replaces the entire contents of the document with the given plain text.
void setMaximumBlockCount(int maximum)
QTextFrame * rootFrame() const
Returns the document's root frame.
void setDocumentLayout(QAbstractTextDocumentLayout *layout)
Sets the document to use the given layout.
void setMetaInformation(MetaInformation info, const QString &)
Sets the document's meta information of the type specified by info to the given string.
qreal superScriptBaseline() const
static QTextDocument::ResourceProvider defaultResourceProvider()
void setBaseUrl(const QUrl &url)
QTextBlock lastBlock() const
void addResource(int type, const QUrl &name, const QVariant &resource)
Adds the resource resource to the resource cache, using type and name as identifiers.
void setSubScriptBaseline(qreal baseline)
~QTextDocument()
Destroys the document.
void undo()
This is an overloaded member function, provided for convenience. It differs from the above function o...
virtual QTextObject * createObject(const QTextFormat &f)
Creates and returns a new document object (a QTextObject), based on the given format.
std::function< QVariant(const QUrl &)> ResourceProvider
void markContentsDirty(int from, int length)
Marks the contents specified by the given position and length as "dirty", informing the document that...
void setUseDesignMetrics(bool b)
void setTextWidth(qreal width)
void drawContents(QPainter *painter, const QRectF &rect=QRectF())
int characterCount() const
void redo()
This is an overloaded member function, provided for convenience. It differs from the above function o...
\reentrant
Definition qtextformat.h:90
QBrush background() const
Returns the brush used to paint the document's background.
@ TableCellBottomBorderStyle
@ TableCellTopBorderStyle
@ TableCellBottomBorderBrush
@ TableCellRightBorderStyle
@ BlockTrailingHorizontalRulerWidth
@ TableCellTopBorderBrush
@ TableCellRightBorderBrush
@ TableCellBottomPadding
@ TableCellRightPadding
@ TableCellBottomBorder
@ TableCellLeftBorderStyle
@ TableCellLeftBorderBrush
QTextLength lengthProperty(int propertyId) const
Returns the value of the property given by propertyId.
int intProperty(int propertyId) const
Returns the value of the property specified by propertyId.
void setProperty(int propertyId, const QVariant &value)
Sets the property specified by the propertyId to the given value.
QTextImageFormat toImageFormat() const
Returns this format as an image format.
QMap< int, QVariant > properties() const
Returns a map with all properties of this text format.
QTextTableCellFormat toTableCellFormat() const
bool hasProperty(int propertyId) const
Returns true if the text format has a property with the given propertyId; otherwise returns false.
void clearProperty(int propertyId)
Clears the value of the property given by propertyId.
void merge(const QTextFormat &other)
Merges the other format with this format; where there are conflicts the other format takes precedence...
@ PageBreak_AlwaysBefore
@ PageBreak_AlwaysAfter
QVariant property(int propertyId) const
Returns the property specified by the given propertyId.
QBrush foreground() const
Returns the brush used to render foreground details, such as text, frame outlines,...
\reentrant
QString text() const
Returns the text fragment's as plain text.
QTextCharFormat charFormat() const
Returns the text fragment's character format.
QBrush borderBrush() const
void setLeftMargin(qreal margin)
BorderStyle borderStyle() const
Position
This enum describes how a frame is located relative to the surrounding text.
void setMargin(qreal margin)
Sets the frame's margin in pixels.
bool atEnd() const
Returns true if the current item is the last item in the text frame.
\reentrant
Definition qtextobject.h:81
void setFrameFormat(const QTextFrameFormat &format)
Sets the frame's format.
QTextFrameFormat frameFormat() const
Returns the frame's format.
Definition qtextobject.h:89
iterator begin() const
Returns an iterator pointing to the first document element inside the frame.
QString toHtml(ExportMode mode=ExportEntireDocument)
Returns the document in HTML format.
QTextHtmlExporter(const QTextDocument *_doc)
static int lookupElement(QStringView element)
\reentrant
Definition qtextformat.h:45
\reentrant
Definition qtextlist.h:18
void import(const QString &markdown)
\reentrant
Definition qtextobject.h:25
\reentrant
Definition qtextoption.h:18
\inmodule QtCore
\reentrant
Definition qtexttable.h:19
QTextCharFormat format() const
Returns the cell's character format.
int columnSpan() const
Returns the number of columns this cell spans.
int row() const
Returns the number of the row in the table that contains this cell.
int rowSpan() const
Returns the number of rows this cell spans.
int column() const
Returns the number of the column in the table that contains this cell.
QTextFrame::iterator begin() const
Returns a frame iterator pointing to the beginning of the table's cell.
\reentrant
Definition qtexttable.h:63
static QThread * currentThread()
Definition qthread.cpp:1039
\inmodule QtCore
Definition qurl.h:94
static QUrl fromLocalFile(const QString &localfile)
Returns a QUrl representation of localFile, interpreted as a local file.
Definition qurl.cpp:3368
QString url(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2817
QUrl resolved(const QUrl &relative) const
Returns the result of the merge of this URL with relative.
Definition qurl.cpp:2725
bool isRelative() const
Returns true if the URL is relative; otherwise returns false.
Definition qurl.cpp:2800
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1896
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1991
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2831
QString toLocalFile() const
Returns the path of this URL formatted as a local file path.
Definition qurl.cpp:3425
\inmodule QtCore
Definition qvariant.h:65
T value() const &
Definition qvariant.h:516
EGLContext ctx
QString text
QCursor cursor
QSet< QString >::iterator it
rect
[4]
short next
Definition keywords.cpp:445
@ StyleSheetOrigin_UserAgent
Combined button and popup list for selecting options.
@ AlignRight
Definition qnamespace.h:146
@ AlignJustify
Definition qnamespace.h:149
@ AlignHCenter
Definition qnamespace.h:148
@ AlignLeft
Definition qnamespace.h:144
Q_GUI_EXPORT QString convertFromPlainText(const QString &plain, WhiteSpaceMode mode=WhiteSpacePre)
Converts the plain text string plain to an HTML-formatted paragraph while preserving most of its look...
@ RightToLeft
Q_GUI_EXPORT bool mightBeRichText(QAnyStringView)
Returns true if the string text is likely to be rich text; otherwise returns false.
@ black
Definition qnamespace.h:30
@ CustomDashLine
WhiteSpaceMode
Definition qnamespace.h:196
@ WhiteSpacePre
Definition qnamespace.h:198
@ SvgMiterJoin
@ BevelJoin
@ MiterJoin
@ RoundJoin
CaseSensitivity
@ CaseInsensitive
@ CaseSensitive
@ SolidPattern
@ RadialGradientPattern
@ TexturePattern
@ LinearGradientPattern
@ NoBrush
@ ConicalGradientPattern
CursorMoveStyle
@ DirectConnection
@ SquareCap
@ RoundCap
@ FlatCap
Definition brush.cpp:5
Definition image.cpp:4
bool Q_GUI_EXPORT qHasPixmapTexture(const QBrush &brush)
Definition qbrush.cpp:202
QPair< qreal, QColor > QGradientStop
Definition qbrush.h:131
#define Q_DECL_CONST_FUNCTION
#define qApp
AudioChannelLayoutTag tag
Q_CORE_EXPORT bool qDecodeDataUrl(const QUrl &uri, QString &mimeType, QByteArray &payload)
Definition qdataurl.cpp:18
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
EGLOutputLayerEXT EGLint attribute
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
Q_GUI_EXPORT int qt_defaultDpiX()
Definition qfont.cpp:110
Q_GUI_EXPORT int qt_defaultDpiY()
Definition qfont.cpp:125
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION unsigned int qt_int_sqrt(unsigned int n)
\inmodule QtCore \title Global Qt Declarations
Definition qglobal.cpp:98
static QByteArray cacheKey(Args &&...args)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
return ret
static const QMetaObjectPrivate * priv(const uint *data)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
#define Q_RETURN_ARG(Type, data)
Definition qobjectdefs.h:64
#define Q_ARG(Type, data)
Definition qobjectdefs.h:63
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLenum mode
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLdouble GLdouble GLdouble GLdouble top
GLdouble GLdouble right
GLfloat GLfloat f
GLint GLsizei width
GLuint color
[2]
GLint left
GLenum type
GLint GLint bottom
GLboolean enable
GLenum GLuint GLsizei const GLenum * props
GLuint start
GLenum GLuint GLintptr offset
GLuint name
GLint first
GLfloat n
GLint GLsizei GLsizei GLenum format
GLhandleARB obj
[2]
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLuint GLuint * names
GLenum GLenum GLsizei void * row
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLuint GLenum option
GLenum GLenum GLsizei void * table
GLsizei const GLchar *const * string
[0]
Definition qopenglext.h:694
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
static QString colorValue(QColor color)
static bool isOrderedList(int style)
static QLatin1StringView richtextBorderStyleToHtmlBorderStyle(QTextFrameFormat::BorderStyle style)
static QStringList resolvedFontFamilies(const QTextCharFormat &format)
static void printPage(int index, QPainter *painter, const QTextDocument *doc, const QRectF &body, const QPointF &pageNumberPos)
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION unsigned int qt_int_sqrt(unsigned int n)
\inmodule QtCore \title Global Qt Declarations
Definition qglobal.cpp:98
static bool mightBeRichTextImpl(T text)
static bool findInBlock(const QTextBlock &block, const QString &expression, int offset, QTextDocument::FindFlags options, QTextCursor *cursor)
static QTextFormat formatDifference(const QTextFormat &from, const QTextFormat &to)
bool qHasPixmapTexture(const QBrush &brush)
Definition qbrush.cpp:202
#define QTextBeginningOfFrame
#define QTextEndOfFrame
#define emit
#define Q_UNUSED(x)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
unsigned char quint8
Definition qtypes.h:46
static QString quote(const QString &str)
QVideoFrameFormat::PixelFormat fmt
QList< int > list
[14]
file open(QIODevice::ReadOnly)
QUrl url("example.com")
[constructor-url-reference]
QVBoxLayout * layout
QString title
[35]
QByteArray page
[45]
Text files * txt
QGraphicsItem * item
QPainter painter(this)
[7]
QSizePolicy policy
QHostInfo info
[0]
QQuickView * view
[0]
\inmodule QtCore
int indexOfMethod(const char *method) const
Finds method and returns its index; otherwise returns -1.
QMetaMethod method(int index) const
Returns the meta-data for the method with the given index.