5#include <qtextformat.h>
14#include <qloggingcategory.h>
15#if QT_CONFIG(regularexpression)
16#include <qregularexpression.h>
18#include <qvarlengtharray.h>
20#include <qcoreapplication.h>
21#include <qmetaobject.h>
29#include "private/qdataurl_p.h"
32#include <private/qabstracttextdocumentlayout_p.h>
34#include "private/qpagedpaintdevice_p.h"
35#if QT_CONFIG(textmarkdownreader)
36#include <private/qtextmarkdownimporter_p.h>
38#if QT_CONFIG(textmarkdownwriter)
39#include <private/qtextmarkdownwriter_p.h>
46using namespace Qt::StringLiterals;
48Q_CORE_EXPORT Q_DECL_CONST_FUNCTION
unsigned int qt_int_sqrt(
unsigned int n);
51 QTextDocument::ResourceProvider qt_defaultResourceProvider;
54QAbstractUndoItem::~QAbstractUndoItem()
58
59
60
61
62
63
64
65
66
67
68
69
70
71
79 while (start < text.size() && QChar(text.at(start)).isSpace())
83 if (text.mid(start, 5).compare(
"<?xml"_L1) == 0) {
84 while (start < text.size()) {
85 if (text.at(start) == u'?'
86 && start + 2 < text.size()
87 && text.at(start + 1) == u'>') {
94 while (start < text.size() && QChar(text.at(start)).isSpace())
98 if (text.mid(start, 5).compare(
"<!doc"_L1, Qt::CaseInsensitive) == 0)
100 qsizetype open = start;
101 while (open < text.size() && text.at(open) != u'<'
102 && text.at(open) != u'\n') {
103 if (text.at(open) == u'&' && text.mid(open + 1, 3) ==
"lt;"_L1)
107 if (open < text.size() && text.at(open) == u'<') {
108 const qsizetype close = text.indexOf(u'>', open);
110 QVarLengthArray<
char16_t> tag;
111 for (qsizetype i = open + 1; i < close; ++i) {
112 const auto current = QChar(text[i]);
113 if (current.isDigit() || current.isLetter())
114 tag.append(current.toLower().unicode());
115 else if (!tag.isEmpty() && current.isSpace())
117 else if (!tag.isEmpty() && current == u'/' && i + 1 == close)
119 else if (!current.isSpace() && (!tag.isEmpty() || current != u'!'))
122#ifndef QT_NO_TEXTHTMLPARSER
123 return QTextHtmlParser::lookupElement(tag) != -1;
134 return mightBeRichTextImpl(QLatin1StringView(QByteArrayView(text)));
139 return text.visit([](
auto text) {
return mightBeRichTextImpl(text); });
143
144
145
146
147
148
149
150
151
152QString
Qt::convertFromPlainText(
const QString &plain, Qt::WhiteSpaceMode mode)
157 for (qsizetype i = 0; i < plain.size(); ++i) {
158 if (plain[i] == u'\n'){
160 while (i+1 < plain.size() && plain[i+1] == u'\n') {
174 if (mode == Qt::WhiteSpacePre && plain[i] == u'\t'){
182 else if (mode == Qt::WhiteSpacePre && plain[i].isSpace())
184 else if (plain[i] == u'<')
186 else if (plain[i] == u'>')
188 else if (plain[i] == u'&')
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
262
263
264
267
268
269
270
271
272
273
276
277
278QTextDocument::QTextDocument(QObject *parent)
279 : QObject(*
new QTextDocumentPrivate, parent)
286
287
288
289QTextDocument::QTextDocument(
const QString &text, QObject *parent)
290 : QObject(*
new QTextDocumentPrivate, parent)
294 QTextCursor(
this).insertText(text);
298
299
300QTextDocument::QTextDocument(QTextDocumentPrivate &dd, QObject *parent)
301 : QObject(dd, parent)
308
309
310QTextDocument::~QTextDocument()
316
317
318
319QTextDocument *QTextDocument::clone(QObject *parent)
const
321 Q_D(
const QTextDocument);
322 QTextDocument *doc =
new QTextDocument(parent);
324 const QTextCursor thisCursor(
const_cast<QTextDocument *>(
this));
326 const auto blockFormat = thisCursor.blockFormat();
327 if (blockFormat.isValid() && !blockFormat.isEmpty())
328 QTextCursor(doc).setBlockFormat(blockFormat);
330 const auto blockCharFormat = thisCursor.blockCharFormat();
331 if (blockCharFormat.isValid() && !blockCharFormat.isEmpty())
332 QTextCursor(doc).setBlockCharFormat(blockCharFormat);
334 QTextCursor(doc).insertFragment(QTextDocumentFragment(
this));
336 doc->rootFrame()->setFrameFormat(rootFrame()->frameFormat());
337 QTextDocumentPrivate *priv = doc->d_func();
338 priv->title = d->title;
340 priv->cssMedia = d->cssMedia;
341 priv->pageSize = d->pageSize;
342 priv->indentWidth = d->indentWidth;
343 priv->defaultTextOption = d->defaultTextOption;
344 priv->setDefaultFont(d->defaultFont());
345 priv->resources = d->resources;
346 priv->cachedResources.clear();
347 priv->resourceProvider = d->resourceProvider;
348#ifndef QT_NO_CSSPARSER
349 priv->defaultStyleSheet = d->defaultStyleSheet;
350 priv->parsedDefaultStyleSheet = d->parsedDefaultStyleSheet;
356
357
358bool QTextDocument::isEmpty()
const
360 Q_D(
const QTextDocument);
362
363 return d->length() <= 1;
367
368
369void QTextDocument::clear()
373 d->resources.clear();
377
378
379
380
381
382
383
384
385
386
387
388void QTextDocument::undo(QTextCursor *cursor)
391 const int pos = d->undoRedo(
true);
392 if (cursor && pos >= 0) {
393 *cursor = QTextCursor(
this);
394 cursor->setPosition(pos);
399
400
401
402
403
404
405void QTextDocument::redo(QTextCursor *cursor)
408 const int pos = d->undoRedo(
false);
409 if (cursor && pos >= 0) {
410 *cursor = QTextCursor(
this);
411 cursor->setPosition(pos);
416
417
418
419
420
423
424
425
426
427
428
429
430
431
432
433void QTextDocument::clearUndoRedoStacks(Stacks stacksToClear)
436 d->clearUndoRedoStacks(stacksToClear,
true);
440
441
442
443void QTextDocument::undo()
450
451
452
453void QTextDocument::redo()
460
461
462
463
464void QTextDocument::appendUndoItem(QAbstractUndoItem *item)
467 d->appendUndoItem(item);
471
472
473
474
475
476
477void QTextDocument::setUndoRedoEnabled(
bool enable)
480 d->enableUndoRedo(enable);
483bool QTextDocument::isUndoRedoEnabled()
const
485 Q_D(
const QTextDocument);
486 return d->isUndoRedoEnabled();
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510int QTextDocument::maximumBlockCount()
const
512 Q_D(
const QTextDocument);
513 return d->maximumBlockCount;
516void QTextDocument::setMaximumBlockCount(
int maximum)
519 d->maximumBlockCount = maximum;
520 d->ensureMaximumBlockCount();
521 setUndoRedoEnabled(
false);
525
526
527
528
529
530
531QTextOption QTextDocument::defaultTextOption()
const
533 Q_D(
const QTextDocument);
534 return d->defaultTextOption;
538
539
540
541
542void QTextDocument::setDefaultTextOption(
const QTextOption &option)
545 d->defaultTextOption = option;
547 d->lout->documentChanged(0, 0, d->length());
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566QUrl QTextDocument::baseUrl()
const
568 Q_D(
const QTextDocument);
572void QTextDocument::setBaseUrl(
const QUrl &url)
575 if (d->baseUrl != url) {
578 d->lout->documentChanged(0, 0, d->length());
579 emit baseUrlChanged(url);
584
585
586
587
588
589Qt::CursorMoveStyle QTextDocument::defaultCursorMoveStyle()
const
591 Q_D(
const QTextDocument);
592 return d->defaultCursorMoveStyle;
596
597
598
599
600void QTextDocument::setDefaultCursorMoveStyle(Qt::CursorMoveStyle style)
603 d->defaultCursorMoveStyle = style;
607
608
609
610
611
612
613void QTextDocument::markContentsDirty(
int from,
int length)
616 d->documentChange(from, length);
617 if (!d->inContentsChange) {
619 d->lout->documentChanged(d->docChangeFrom, d->docChangeOldLength, d->docChangeLength);
620 d->docChangeFrom = -1;
626
627
628
629
630
631
632
633
634
635
636
637
638
639
641void QTextDocument::setUseDesignMetrics(
bool b)
644 if (b == d->defaultTextOption.useDesignMetrics())
646 d->defaultTextOption.setUseDesignMetrics(b);
648 d->lout->documentChanged(0, 0, d->length());
651bool QTextDocument::useDesignMetrics()
const
653 Q_D(
const QTextDocument);
654 return d->defaultTextOption.useDesignMetrics();
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
675void QTextDocument::setLayoutEnabled(
bool b)
678 if (d->layoutEnabled == b)
680 d->layoutEnabled = b;
682 d->lout->documentChanged(0, 0, d->length());
685bool QTextDocument::isLayoutEnabled()
const
687 Q_D(
const QTextDocument);
688 return d->layoutEnabled;
692
693
694
695
696
697void QTextDocument::drawContents(QPainter *p,
const QRectF &rect)
700 QAbstractTextDocumentLayout::PaintContext ctx;
701 if (rect.isValid()) {
702 p->setClipRect(rect);
705 documentLayout()->draw(p, ctx);
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730void QTextDocument::setTextWidth(qreal width)
733 QSizeF sz = d->pageSize;
735 qCDebug(lcLayout) <<
"page size" << sz <<
"-> width" << width;
741qreal QTextDocument::textWidth()
const
743 Q_D(
const QTextDocument);
744 return d->pageSize.width();
748
749
750
751
752
753
754
755qreal QTextDocument::idealWidth()
const
757 if (QTextDocumentLayout *lout = qobject_cast<QTextDocumentLayout *>(documentLayout()))
758 return lout->idealWidth();
763
764
765
766
767
768qreal QTextDocument::documentMargin()
const
770 Q_D(
const QTextDocument);
771 return d->documentMargin;
774void QTextDocument::setDocumentMargin(qreal margin)
777 if (d->documentMargin != margin) {
778 d->documentMargin = margin;
780 QTextFrame* root = rootFrame();
781 QTextFrameFormat format = root->frameFormat();
782 format.setMargin(margin);
783 root->setFrameFormat(format);
786 d->lout->documentChanged(0, 0, d->length());
792
793
794
795
796
797
798
799
800qreal QTextDocument::indentWidth()
const
802 Q_D(
const QTextDocument);
803 return d->indentWidth;
808
809
810
811
812
813
814
815
816
817void QTextDocument::setIndentWidth(qreal width)
820 if (d->indentWidth != width) {
821 d->indentWidth = width;
823 d->lout->documentChanged(0, 0, d->length());
831
832
833
834
835
836
837void QTextDocument::adjustSize()
840 QFont f = defaultFont();
842 int mw = fm.horizontalAdvance(u'x') * 80;
845 QSizeF size = documentLayout()->documentSize();
846 if (size.width() != 0) {
847 w = qt_int_sqrt((uint)(5 * size.height() * size.width() / 3));
848 setTextWidth(qMin(w, mw));
850 size = documentLayout()->documentSize();
851 if (w*3 < 5*size.height()) {
852 w = qt_int_sqrt((uint)(2 * size.height() * size.width()));
853 setTextWidth(qMin(w, mw));
856 setTextWidth(idealWidth());
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876QSizeF QTextDocument::size()
const
878 return documentLayout()->documentSize();
882
883
884
885
886
887
888
889
890
891
892int QTextDocument::blockCount()
const
894 Q_D(
const QTextDocument);
895 return d->blockMap().numNodes();
900
901
902
903
904
905
906
907int QTextDocument::lineCount()
const
909 Q_D(
const QTextDocument);
910 return d->blockMap().length(2);
914
915
916
917
918
919
920
921
922
923int QTextDocument::characterCount()
const
925 Q_D(
const QTextDocument);
930
931
932
933
934
935
936
937QChar QTextDocument::characterAt(
int pos)
const
939 Q_D(
const QTextDocument);
940 if (pos < 0 || pos >= d->length())
942 QTextDocumentPrivate::FragmentIterator fragIt = d->find(pos);
943 const QTextFragmentData *
const frag = fragIt.value();
944 const int offsetInFragment = qMax(0, pos - fragIt.position());
945 return d->text.at(frag->stringPosition + offsetInFragment);
950
951
952
953
954
955
956
957
958
959
960
961
962
964#ifndef QT_NO_CSSPARSER
965void QTextDocument::setDefaultStyleSheet(
const QString &sheet)
968 d->defaultStyleSheet = sheet;
969 QCss::Parser parser(sheet);
970 d->parsedDefaultStyleSheet = QCss::StyleSheet();
971 d->parsedDefaultStyleSheet.origin = QCss::StyleSheetOrigin_UserAgent;
972 parser.parse(&d->parsedDefaultStyleSheet);
975QString QTextDocument::defaultStyleSheet()
const
977 Q_D(
const QTextDocument);
978 return d->defaultStyleSheet;
983
984
985
986
987
988
989
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1022
1023
1024
1025
1026
1029
1030
1031
1032
1033
1034
1035
1036
1039
1040
1041
1042
1043
1044
1045
1048
1049
1050
1051
1052
1053
1054
1055
1059
1060
1061
1062
1063bool QTextDocument::isUndoAvailable()
const
1065 Q_D(
const QTextDocument);
1066 return d->isUndoAvailable();
1070
1071
1072
1073
1074bool QTextDocument::isRedoAvailable()
const
1076 Q_D(
const QTextDocument);
1077 return d->isRedoAvailable();
1081
1082
1083
1084
1085
1086int QTextDocument::availableUndoSteps()
const
1088 Q_D(
const QTextDocument);
1089 return d->availableUndoSteps();
1093
1094
1095
1096
1097
1098int QTextDocument::availableRedoSteps()
const
1100 Q_D(
const QTextDocument);
1101 return d->availableRedoSteps();
1105
1106
1107
1108
1109
1110
1111
1112
1113int QTextDocument::revision()
const
1115 Q_D(
const QTextDocument);
1122
1123
1124
1125
1126
1127void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *layout)
1130 d->setLayout(layout);
1134
1135
1136QAbstractTextDocumentLayout *QTextDocument::documentLayout()
const
1138 Q_D(
const QTextDocument);
1140 QTextDocument *that =
const_cast<QTextDocument *>(
this);
1141 that->d_func()->setLayout(
new QTextDocumentLayout(that));
1148
1149
1150
1151
1152
1153QString QTextDocument::metaInformation(MetaInformation info)
const
1155 Q_D(
const QTextDocument);
1164 return d->frontMatter;
1170
1171
1172
1173
1174
1175void QTextDocument::setMetaInformation(MetaInformation info,
const QString &string)
1186 d->cssMedia = string;
1189 d->frontMatter = string;
1195
1196
1197
1198
1199
1200
1201
1202QString QTextDocument::toRawText()
const
1204 Q_D(
const QTextDocument);
1205 return d->plainText();
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225QString QTextDocument::toPlainText()
const
1227 Q_D(
const QTextDocument);
1228 QString txt = d->plainText();
1230 constexpr char16_t delims[] = { 0xfdd0, 0xfdd1,
1231 QChar::ParagraphSeparator, QChar::LineSeparator, QChar::Nbsp };
1233 const size_t pos = std::u16string_view(txt).find_first_of(
1234 std::u16string_view(delims, std::size(delims)));
1235 if (pos == std::u16string_view::npos)
1238 QChar *uc = txt.data();
1239 QChar *
const e = uc + txt.size();
1241 for (uc += pos; uc != e; ++uc) {
1242 switch (uc->unicode()) {
1245 case QChar::ParagraphSeparator:
1246 case QChar::LineSeparator:
1260
1261
1262
1263
1264
1265void QTextDocument::setPlainText(
const QString &text)
1268 bool previousState = d->isUndoRedoEnabled();
1269 d->enableUndoRedo(
false);
1270 d->beginEditBlock();
1272 QTextCursor(
this).insertText(text);
1274 d->enableUndoRedo(previousState);
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1296#ifndef QT_NO_TEXTHTMLPARSER
1298void QTextDocument::setHtml(
const QString &html)
1301 bool previousState = d->isUndoRedoEnabled();
1302 d->enableUndoRedo(
false);
1303 d->beginEditBlock();
1307 QTextHtmlImporter(
this, html, QTextHtmlImporter::ImportToDocument).import();
1309 d->enableUndoRedo(previousState);
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1346static bool findInBlock(
const QTextBlock &block,
const QString &expression,
int offset,
1347 QTextDocument::FindFlags options, QTextCursor *cursor)
1350 text.replace(QChar::Nbsp, u' ');
1351 Qt::CaseSensitivity sensitivity = options & QTextDocument::FindCaseSensitively ? Qt::CaseSensitive : Qt::CaseInsensitive;
1354 while (offset >= 0 && offset <= text.size()) {
1355 idx = (options & QTextDocument::FindBackward) ?
1356 text.lastIndexOf(expression, offset, sensitivity) : text.indexOf(expression, offset, sensitivity);
1360 if (options & QTextDocument::FindWholeWords) {
1361 const int start = idx;
1362 const int end = start + expression.size();
1363 if ((start != 0 && text.at(start - 1).isLetterOrNumber())
1364 || (end != text.size() && text.at(end).isLetterOrNumber())) {
1366 offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
1372 *cursor = QTextCursorPrivate::fromPosition(
const_cast<QTextDocumentPrivate *>(QTextDocumentPrivate::get(block)),
1373 block.position() + idx);
1374 cursor->setPosition(cursor->position() + expression.size(), QTextCursor::KeepAnchor);
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396QTextCursor QTextDocument::find(
const QString &subString,
int from, FindFlags options)
const
1398 Q_D(
const QTextDocument);
1400 if (subString.isEmpty())
1401 return QTextCursor();
1406 if (options & FindBackward) {
1409 return QTextCursor();
1413 QTextBlock block = d->blocksFind(pos);
1414 int blockOffset = pos - block.position();
1416 if (!(options & FindBackward)) {
1417 while (block.isValid()) {
1418 if (findInBlock(block, subString, blockOffset, options, &cursor))
1420 block = block.next();
1424 if (blockOffset == block.length() - 1)
1426 while (block.isValid()) {
1427 if (findInBlock(block, subString, blockOffset, options, &cursor))
1429 block = block.previous();
1430 blockOffset = block.length() - 2;
1434 return QTextCursor();
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452QTextCursor QTextDocument::find(
const QString &subString,
const QTextCursor &cursor, FindFlags options)
const
1455 if (!cursor.isNull()) {
1456 if (options & QTextDocument::FindBackward)
1457 pos = cursor.selectionStart();
1459 pos = cursor.selectionEnd();
1462 return find(subString, pos, options);
1465#if QT_CONFIG(regularexpression)
1466static bool findInBlock(
const QTextBlock &block,
const QRegularExpression &expr,
int offset,
1467 QTextDocument::FindFlags options, QTextCursor *cursor)
1469 QString text = block.text();
1470 text.replace(QChar::Nbsp, u' ');
1471 QRegularExpressionMatch match;
1474 while (offset >= 0 && offset <= text.size()) {
1475 idx = (options & QTextDocument::FindBackward) ?
1476 text.lastIndexOf(expr, offset, &match) : text.indexOf(expr, offset, &match);
1480 if (options & QTextDocument::FindWholeWords) {
1481 const int start = idx;
1482 const int end = start + match.capturedLength();
1483 if ((start != 0 && text.at(start - 1).isLetterOrNumber())
1484 || (end != text.size() && text.at(end).isLetterOrNumber())) {
1486 offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
1492 *cursor = QTextCursorPrivate::fromPosition(
const_cast<QTextDocumentPrivate *>(QTextDocumentPrivate::get(block)),
1493 block.position() + idx);
1494 cursor->setPosition(cursor->position() + match.capturedLength(), QTextCursor::KeepAnchor);
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520QTextCursor QTextDocument::find(
const QRegularExpression &expr,
int from, FindFlags options)
const
1522 Q_D(
const QTextDocument);
1524 if (!expr.isValid())
1525 return QTextCursor();
1530 if (options & FindBackward) {
1533 return QTextCursor();
1537 QTextBlock block = d->blocksFind(pos);
1538 int blockOffset = pos - block.position();
1540 QRegularExpression expression(expr);
1541 if (!(options & QTextDocument::FindCaseSensitively))
1542 expression.setPatternOptions(expr.patternOptions() | QRegularExpression::CaseInsensitiveOption);
1544 expression.setPatternOptions(expr.patternOptions() & ~QRegularExpression::CaseInsensitiveOption);
1546 if (!(options & FindBackward)) {
1547 while (block.isValid()) {
1548 if (findInBlock(block, expression, blockOffset, options, &cursor))
1550 block = block.next();
1554 while (block.isValid()) {
1555 if (findInBlock(block, expression, blockOffset, options, &cursor))
1557 block = block.previous();
1558 blockOffset = block.length() - 1;
1562 return QTextCursor();
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584QTextCursor QTextDocument::find(
const QRegularExpression &expr,
const QTextCursor &cursor, FindFlags options)
const
1587 if (!cursor.isNull()) {
1588 if (options & QTextDocument::FindBackward)
1589 pos = cursor.selectionStart();
1591 pos = cursor.selectionEnd();
1593 return find(expr, pos, options);
1598
1599
1600
1601
1602
1603
1604
1605
1606QTextObject *QTextDocument::createObject(
const QTextFormat &f)
1608 QTextObject *obj =
nullptr;
1609 if (f.isListFormat())
1610 obj =
new QTextList(
this);
1611 else if (f.isTableFormat())
1612 obj =
new QTextTable(
this);
1613 else if (f.isFrameFormat())
1614 obj =
new QTextFrame(
this);
1620
1621
1622
1623
1624QTextFrame *QTextDocument::frameAt(
int pos)
const
1626 Q_D(
const QTextDocument);
1627 return d->frameAt(pos);
1631
1632
1633QTextFrame *QTextDocument::rootFrame()
const
1635 Q_D(
const QTextDocument);
1636 return d->rootFrame();
1640
1641
1642QTextObject *QTextDocument::object(
int objectIndex)
const
1644 Q_D(
const QTextDocument);
1645 return d->objectForIndex(objectIndex);
1649
1650
1651QTextObject *QTextDocument::objectForFormat(
const QTextFormat &f)
const
1653 Q_D(
const QTextDocument);
1654 return d->objectForFormat(f);
1659
1660
1661QTextBlock QTextDocument::findBlock(
int pos)
const
1663 Q_D(
const QTextDocument);
1664 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), d->blockMap().findNode(pos));
1668
1669
1670
1671
1672
1673QTextBlock QTextDocument::findBlockByNumber(
int blockNumber)
const
1675 Q_D(
const QTextDocument);
1676 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), d->blockMap().findNode(blockNumber, 1));
1680
1681
1682
1683
1684
1685QTextBlock QTextDocument::findBlockByLineNumber(
int lineNumber)
const
1687 Q_D(
const QTextDocument);
1688 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), d->blockMap().findNode(lineNumber, 2));
1692
1693
1694
1695
1696QTextBlock QTextDocument::begin()
const
1698 Q_D(
const QTextDocument);
1699 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), d->blockMap().begin().n);
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714QTextBlock QTextDocument::end()
const
1716 Q_D(
const QTextDocument);
1717 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), 0);
1721
1722
1723
1724QTextBlock QTextDocument::firstBlock()
const
1726 Q_D(
const QTextDocument);
1727 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), d->blockMap().begin().n);
1731
1732
1733
1734QTextBlock QTextDocument::lastBlock()
const
1736 Q_D(
const QTextDocument);
1737 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), d->blockMap().last().n);
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1754void QTextDocument::setPageSize(
const QSizeF &size)
1759 d->lout->documentChanged(0, 0, d->length());
1762QSizeF QTextDocument::pageSize()
const
1764 Q_D(
const QTextDocument);
1769
1770
1771int QTextDocument::pageCount()
const
1773 return documentLayout()->pageCount();
1777
1778
1779void QTextDocument::setDefaultFont(
const QFont &font)
1782 d->setDefaultFont(font);
1784 d->lout->documentChanged(0, 0, d->length());
1788
1789
1790QFont QTextDocument::defaultFont()
const
1792 Q_D(
const QTextDocument);
1793 return d->defaultFont();
1797
1798
1799
1800
1801
1802
1803
1804
1805void QTextDocument::setSuperScriptBaseline(qreal baseline)
1808 d->formats.setSuperScriptBaseline(baseline);
1812
1813
1814
1815
1816
1817
1818
1819qreal QTextDocument::superScriptBaseline()
const
1821 Q_D(
const QTextDocument);
1822 return d->formats.defaultTextFormat().superScriptBaseline();
1826
1827
1828
1829
1830
1831
1832
1833
1834void QTextDocument::setSubScriptBaseline(qreal baseline)
1837 d->formats.setSubScriptBaseline(baseline);
1841
1842
1843
1844
1845
1846
1847
1848qreal QTextDocument::subScriptBaseline()
const
1850 Q_D(
const QTextDocument);
1851 return d->formats.defaultTextFormat().subScriptBaseline();
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864void QTextDocument::setBaselineOffset(qreal baseline)
1867 d->formats.setBaselineOffset(baseline);
1871
1872
1873
1874
1875
1876
1877
1878
1879qreal QTextDocument::baselineOffset()
const
1881 Q_D(
const QTextDocument);
1882 return d->formats.defaultTextFormat().baselineOffset();
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1900
1901
1902
1903
1904
1905
1906
1908bool QTextDocument::isModified()
const
1910 Q_D(
const QTextDocument);
1911 return d->isModified();
1914void QTextDocument::setModified(
bool m)
1920#ifndef QT_NO_PRINTER
1921static void printPage(
int index,
QPainter *painter,
const QTextDocument *doc,
const QRectF &body,
const QPointF &pageNumberPos)
1924 painter->translate(body.left(), body.top() - (index - 1) * body.height());
1925 QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
1927 QAbstractTextDocumentLayout *layout = doc->documentLayout();
1928 QAbstractTextDocumentLayout::PaintContext ctx;
1930 painter->setClipRect(view);
1936 ctx.palette.setColor(QPalette::Text, Qt::black);
1938 layout->draw(painter, ctx);
1940 if (!pageNumberPos.isNull()) {
1941 painter->setClipping(
false);
1942 painter->setFont(QFont(doc->defaultFont()));
1943 const QString pageString = QString::number(index);
1945 painter->drawText(qRound(pageNumberPos.x() - painter->fontMetrics().horizontalAdvance(pageString)),
1946 qRound(pageNumberPos.y() + view.top()),
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1971void QTextDocument::print(QPagedPaintDevice *printer)
const
1973 Q_D(
const QTextDocument);
1978 bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
1979 && d->pageSize.height() != qreal(INT_MAX);
1982 QMarginsF m = printer->pageLayout().margins(QPageLayout::Millimeter);
1983 if (!documentPaginated && m.left() == 0. && m.right() == 0. && m.top() == 0. && m.bottom() == 0.) {
1988 printer->setPageMargins(m, QPageLayout::Millimeter);
1992 QPainter p(printer);
1998 const QTextDocument *doc =
this;
1999 QScopedPointer<QTextDocument> clonedDoc;
2000 (
void)doc->documentLayout();
2002 QRectF body = QRectF(QPointF(0, 0), d->pageSize);
2003 QPointF pageNumberPos;
2005 qreal sourceDpiX = qt_defaultDpiX();
2006 qreal sourceDpiY = qt_defaultDpiY();
2007 const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
2008 const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
2010 if (documentPaginated) {
2012 QPaintDevice *dev = doc->documentLayout()->paintDevice();
2014 sourceDpiX = dev->logicalDpiX();
2015 sourceDpiY = dev->logicalDpiY();
2019 p.scale(dpiScaleX, dpiScaleY);
2021 QSizeF scaledPageSize = d->pageSize;
2022 scaledPageSize.rwidth() *= dpiScaleX;
2023 scaledPageSize.rheight() *= dpiScaleY;
2025 const QSizeF printerPageSize(printer->width(), printer->height());
2028 p.scale(printerPageSize.width() / scaledPageSize.width(),
2029 printerPageSize.height() / scaledPageSize.height());
2031 doc = clone(
const_cast<QTextDocument *>(
this));
2032 clonedDoc.reset(
const_cast<QTextDocument *>(doc));
2034 for (QTextBlock srcBlock = firstBlock(), dstBlock = clonedDoc->firstBlock();
2035 srcBlock.isValid() && dstBlock.isValid();
2036 srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
2037 dstBlock.layout()->setFormats(srcBlock.layout()->formats());
2040 QAbstractTextDocumentLayout *layout = doc->documentLayout();
2041 layout->setPaintDevice(p.device());
2044 layout->d_func()->handlers = documentLayout()->d_func()->handlers;
2047 const int horizontalMargin =
int((2/2.54)*sourceDpiX);
2048 const int verticalMargin =
int((2/2.54)*sourceDpiY);
2049 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2050 fmt.setLeftMargin(horizontalMargin);
2051 fmt.setRightMargin(horizontalMargin);
2052 fmt.setTopMargin(verticalMargin);
2053 fmt.setBottomMargin(verticalMargin);
2054 doc->rootFrame()->setFrameFormat(fmt);
2057 const int dpiy = p.device()->logicalDpiY();
2058 body = QRectF(0, 0, printer->width(), printer->height());
2059 pageNumberPos = QPointF(body.width() - horizontalMargin * dpiScaleX,
2060 body.height() - verticalMargin * dpiScaleY
2061 + QFontMetrics(doc->defaultFont(), p.device()).ascent()
2063 clonedDoc->setPageSize(body.size());
2066 const QPageRanges pageRanges = printer->pageRanges();
2067 int fromPage = pageRanges.firstPage();
2068 int toPage = pageRanges.lastPage();
2070 if (fromPage == 0 && toPage == 0) {
2072 toPage = doc->pageCount();
2075 fromPage = qMax(1, fromPage);
2076 toPage = qMin(doc->pageCount(), toPage);
2078 if (toPage < fromPage) {
2092 int page = fromPage;
2094 if (pageRanges.isEmpty() || pageRanges.contains(page))
2095 printPage(page, &p, doc, body, pageNumberPos);
2100 if (!printer->newPage())
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149QVariant QTextDocument::resource(
int type,
const QUrl &name)
const
2151 Q_D(
const QTextDocument);
2152 const QUrl url = d->baseUrl.resolved(name);
2153 QVariant r = d->resources.value(url);
2155 r = d->cachedResources.value(url);
2157 r =
const_cast<QTextDocument *>(
this)->loadResource(type, url);
2159 if (d->resourceProvider)
2160 r = d->resourceProvider(url);
2161 else if (
auto defaultProvider = defaultResourceProvider())
2162 r = defaultProvider(url);
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187void QTextDocument::addResource(
int type,
const QUrl &name,
const QVariant &resource)
2191 d->resources.insert(name, resource);
2195
2196
2197
2198
2199
2200
2201QTextDocument::ResourceProvider QTextDocument::resourceProvider()
const
2203 Q_D(
const QTextDocument);
2204 return d->resourceProvider;
2208
2209
2210
2211
2212
2215
2216
2217
2218
2219
2220
2221void QTextDocument::setResourceProvider(
const ResourceProvider &provider)
2224 d->resourceProvider = provider;
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237void QTextDocument::setDefaultResourceProvider(
const ResourceProvider &provider)
2239 qt_defaultResourceProvider = provider;
2243
2244
2245
2246
2247
2248
2249QTextDocument::ResourceProvider QTextDocument::defaultResourceProvider()
2251 return qt_defaultResourceProvider;
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273QVariant QTextDocument::loadResource(
int type,
const QUrl &name)
2278 QObject *p = parent();
2280 const QMetaObject *me = p->metaObject();
2281 int index = me->indexOfMethod(
"loadResource(int,QUrl)");
2283 QMetaMethod loader = me->method(index);
2285 loader.invoke(p, Qt::DirectConnection, Q_RETURN_ARG(QVariant, r), Q_ARG(
int, type), Q_ARG(QUrl, name));
2290 if (r.isNull() && name.scheme().compare(
"data"_L1, Qt::CaseInsensitive) == 0) {
2293 if (qDecodeDataUrl(name, mimetype, payload))
2298 if (!qobject_cast<QTextDocument *>(p) && r.isNull()) {
2299 QUrl resourceUrl = name;
2301 if (name.isRelative()) {
2302 const QUrl currentURL{d->url};
2305 if (!(currentURL.isRelative()
2306 || (currentURL.scheme() ==
"file"_L1
2307 && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
2308 || (name.hasFragment() && name.path().isEmpty())) {
2309 resourceUrl = currentURL.resolved(name);
2314 QFileInfo fi(currentURL.toLocalFile());
2317 QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
2318 }
else if (currentURL.isEmpty()) {
2319 resourceUrl.setScheme(
"file"_L1);
2324 QString s = resourceUrl.toLocalFile();
2326 if (!s.isEmpty() && f.open(QFile::ReadOnly)) {
2333 if (type == ImageResource && r.userType() == QMetaType::QByteArray) {
2334 if (!QThread::isMainThread()) {
2337 image.loadFromData(r.toByteArray());
2338 if (!image.isNull())
2342 pm.loadFromData(r.toByteArray());
2347 d->cachedResources.insert(name, r);
2354 QTextFormat diff = to;
2356 const QMap<
int, QVariant> props = to.properties();
2357 for (QMap<
int, QVariant>::ConstIterator it = props.begin(), end = props.end();
2359 if (it.value() == from.property(it.key()))
2360 diff.clearProperty(it.key());
2369 if (color.alpha() == 255) {
2370 result = color.name();
2371 }
else if (color.alpha()) {
2372 QString alphaValue = QString::number(color.alphaF(),
'f', 6);
2373 while (alphaValue.size() > 1 && alphaValue.at(alphaValue.size() - 1) == u'0')
2375 if (alphaValue.at(alphaValue.size() - 1) == u'.')
2377 result = QString::fromLatin1(
"rgba(%1,%2,%3,%4)").arg(color.red())
2382 result =
"transparent"_L1;
2389
2390
2391
2392
2394 : doc(_doc), fragmentMarkers(
false)
2396 const QFont defaultFont = doc->defaultFont();
2397 defaultCharFormat.setFont(defaultFont);
2402 return format.fontFamilies().toStringList();
2406
2407
2408
2409
2412 html =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
2413 "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
2414 "<html><head><meta name=\"qrichtext\" content=\"1\" />"_L1;
2415 html.reserve(QTextDocumentPrivate::get(doc)->length());
2419 html +=
"<meta charset=\"utf-8\" />"_L1;
2421 QString title = doc->metaInformation(QTextDocument::DocumentTitle);
2422 if (!title.isEmpty()) {
2423 html +=
"<title>"_L1;
2425 html +=
"</title>"_L1;
2427 html +=
"<style type=\"text/css\">\n"_L1;
2428 html +=
"p, li { white-space: pre-wrap; }\n"_L1;
2429 html +=
"hr { height: 1px; border-width: 0; }\n"_L1;
2430 html +=
"li.unchecked::marker { content: \"\\2610\"; }\n"_L1;
2431 html +=
"li.checked::marker { content: \"\\2612\"; }\n"_L1;
2432 html +=
"</style>"_L1;
2433 html +=
"</head><body"_L1;
2436 html +=
" style=\""_L1;
2438 emitFontFamily(resolvedFontFamilies(defaultCharFormat));
2440 if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) {
2441 html +=
" font-size:"_L1;
2442 html += QString::number(defaultCharFormat.fontPointSize());
2444 }
else if (defaultCharFormat.hasProperty(QTextFormat::FontPixelSize)) {
2445 html +=
" font-size:"_L1;
2446 html += QString::number(defaultCharFormat.intProperty(QTextFormat::FontPixelSize));
2450 html +=
" font-weight:"_L1;
2451 html += QString::number(defaultCharFormat.fontWeight());
2454 html +=
" font-style:"_L1;
2455 html += (defaultCharFormat.fontItalic() ?
"italic"_L1 :
"normal"_L1);
2458 const bool percentSpacing = (defaultCharFormat.fontLetterSpacingType() == QFont::PercentageSpacing);
2459 if (defaultCharFormat.hasProperty(QTextFormat::FontLetterSpacing) &&
2460 (!percentSpacing || defaultCharFormat.fontLetterSpacing() != 0.0)) {
2461 html +=
" letter-spacing:"_L1;
2462 qreal value = defaultCharFormat.fontLetterSpacing();
2464 value = (value / 100) - 1;
2465 html += QString::number(value);
2466 html += percentSpacing ?
"em;"_L1 :
"px;"_L1;
2469 if (defaultCharFormat.hasProperty(QTextFormat::FontWordSpacing) &&
2470 defaultCharFormat.fontWordSpacing() != 0.0) {
2471 html +=
" word-spacing:"_L1;
2472 html += QString::number(defaultCharFormat.fontWordSpacing());
2476 QString decorationTag(
" text-decoration:"_L1);
2477 bool atLeastOneDecorationSet =
false;
2478 if (defaultCharFormat.hasProperty(QTextFormat::FontUnderline) || defaultCharFormat.hasProperty(QTextFormat::TextUnderlineStyle)) {
2479 if (defaultCharFormat.fontUnderline()) {
2480 decorationTag +=
" underline"_L1;
2481 atLeastOneDecorationSet =
true;
2484 if (defaultCharFormat.hasProperty(QTextFormat::FontOverline)) {
2485 if (defaultCharFormat.fontOverline()) {
2486 decorationTag +=
" overline"_L1;
2487 atLeastOneDecorationSet =
true;
2490 if (defaultCharFormat.hasProperty(QTextFormat::FontStrikeOut)) {
2491 if (defaultCharFormat.fontStrikeOut()) {
2492 decorationTag +=
" line-through"_L1;
2493 atLeastOneDecorationSet =
true;
2496 if (atLeastOneDecorationSet)
2497 html += decorationTag + u';';
2501 const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2502 emitBackgroundAttribute(fmt);
2505 defaultCharFormat = QTextCharFormat();
2509 QTextFrameFormat rootFmt = doc->rootFrame()->frameFormat();
2510 rootFmt.clearProperty(QTextFormat::BackgroundBrush);
2512 QTextFrameFormat defaultFmt;
2513 defaultFmt.setMargin(doc->documentMargin());
2515 if (rootFmt == defaultFmt)
2516 emitFrame(doc->rootFrame()->begin());
2518 emitTextFrame(doc->rootFrame());
2520 html +=
"</body></html>"_L1;
2524void QTextHtmlExporter::emitAttribute(
const char *attribute,
const QString &value)
2527 html += QLatin1StringView(attribute);
2529 html += value.toHtmlEscaped();
2535 bool attributesEmitted =
false;
2538 const QStringList families = resolvedFontFamilies(format);
2539 if (!families.isEmpty() && families != resolvedFontFamilies(defaultCharFormat)) {
2540 emitFontFamily(families);
2541 attributesEmitted =
true;
2545 if (format.hasProperty(QTextFormat::FontPointSize)
2546 && format.fontPointSize() != defaultCharFormat.fontPointSize()) {
2547 html +=
" font-size:"_L1;
2548 html += QString::number(format.fontPointSize());
2550 attributesEmitted =
true;
2551 }
else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) {
2552 static const char sizeNameData[] =
2556 static const quint8 sizeNameOffsets[] = {
2559 sizeof(
"small") +
sizeof(
"medium") + 3,
2560 sizeof(
"small") +
sizeof(
"medium") + 1,
2561 sizeof(
"small") +
sizeof(
"medium"),
2563 const char *name =
nullptr;
2564 const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1;
2565 if (idx >= 0 && idx <= 4) {
2566 name = sizeNameData + sizeNameOffsets[idx];
2569 html +=
" font-size:"_L1;
2570 html += QLatin1StringView(name);
2572 attributesEmitted =
true;
2574 }
else if (format.hasProperty(QTextFormat::FontPixelSize)
2575 && format.property(QTextFormat::FontPixelSize)
2576 != defaultCharFormat.property(QTextFormat::FontPixelSize)) {
2577 html +=
" font-size:"_L1;
2578 html += QString::number(format.intProperty(QTextFormat::FontPixelSize));
2580 attributesEmitted =
true;
2583 if (format.hasProperty(QTextFormat::FontWeight)
2584 && format.fontWeight() != defaultCharFormat.fontWeight()) {
2585 html +=
" font-weight:"_L1;
2586 html += QString::number(format.fontWeight());
2588 attributesEmitted =
true;
2591 if (format.hasProperty(QTextFormat::FontItalic)
2592 && format.fontItalic() != defaultCharFormat.fontItalic()) {
2593 html +=
" font-style:"_L1;
2594 html += (format.fontItalic() ?
"italic"_L1 :
"normal"_L1);
2596 attributesEmitted =
true;
2599 const auto decorationTag =
" text-decoration:"_L1;
2600 html += decorationTag;
2601 bool hasDecoration =
false;
2602 bool atLeastOneDecorationSet =
false;
2604 if ((format.hasProperty(QTextFormat::FontUnderline) || format.hasProperty(QTextFormat::TextUnderlineStyle))
2605 && format.fontUnderline() != defaultCharFormat.fontUnderline()) {
2606 hasDecoration =
true;
2607 if (format.fontUnderline()) {
2608 html +=
" underline"_L1;
2609 atLeastOneDecorationSet =
true;
2613 if (format.hasProperty(QTextFormat::FontOverline)
2614 && format.fontOverline() != defaultCharFormat.fontOverline()) {
2615 hasDecoration =
true;
2616 if (format.fontOverline()) {
2617 html +=
" overline"_L1;
2618 atLeastOneDecorationSet =
true;
2622 if (format.hasProperty(QTextFormat::FontStrikeOut)
2623 && format.fontStrikeOut() != defaultCharFormat.fontStrikeOut()) {
2624 hasDecoration =
true;
2625 if (format.fontStrikeOut()) {
2626 html +=
" line-through"_L1;
2627 atLeastOneDecorationSet =
true;
2631 if (hasDecoration) {
2632 if (!atLeastOneDecorationSet)
2635 if (format.hasProperty(QTextFormat::TextUnderlineColor)) {
2636 html +=
" text-decoration-color:"_L1;
2637 html += colorValue(format.underlineColor());
2640 attributesEmitted =
true;
2642 html.chop(decorationTag.size());
2645 if (format.foreground() != defaultCharFormat.foreground()
2646 && format.foreground().style() != Qt::NoBrush) {
2647 QBrush brush = format.foreground();
2648 if (brush.style() == Qt::TexturePattern) {
2649 const bool isPixmap = qHasPixmapTexture(brush);
2650 const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
2652 html +=
" -qt-fg-texture-cachekey:"_L1;
2653 html += QString::number(cacheKey);
2655 }
else if (brush.style() == Qt::LinearGradientPattern
2656 || brush.style() == Qt::RadialGradientPattern
2657 || brush.style() == Qt::ConicalGradientPattern) {
2658 const QGradient *gradient = brush.gradient();
2659 if (gradient->type() == QGradient::LinearGradient) {
2660 const QLinearGradient *linearGradient =
static_cast<
const QLinearGradient *>(brush.gradient());
2662 html +=
" -qt-foreground: qlineargradient("_L1;
2663 html +=
"x1:"_L1 + QString::number(linearGradient->start().x()) + u',';
2664 html +=
"y1:"_L1 + QString::number(linearGradient->start().y()) + u',';
2665 html +=
"x2:"_L1 + QString::number(linearGradient->finalStop().x()) + u',';
2666 html +=
"y2:"_L1 + QString::number(linearGradient->finalStop().y()) + u',';
2667 }
else if (gradient->type() == QGradient::RadialGradient) {
2668 const QRadialGradient *radialGradient =
static_cast<
const QRadialGradient *>(brush.gradient());
2670 html +=
" -qt-foreground: qradialgradient("_L1;
2671 html +=
"cx:"_L1 + QString::number(radialGradient->center().x()) + u',';
2672 html +=
"cy:"_L1 + QString::number(radialGradient->center().y()) + u',';
2673 html +=
"fx:"_L1 + QString::number(radialGradient->focalPoint().x()) + u',';
2674 html +=
"fy:"_L1 + QString::number(radialGradient->focalPoint().y()) + u',';
2675 html +=
"radius:"_L1 + QString::number(radialGradient->radius()) + u',';
2677 const QConicalGradient *conicalGradient =
static_cast<
const QConicalGradient *>(brush.gradient());
2679 html +=
" -qt-foreground: qconicalgradient("_L1;
2680 html +=
"cx:"_L1 + QString::number(conicalGradient->center().x()) + u',';
2681 html +=
"cy:"_L1 + QString::number(conicalGradient->center().y()) + u',';
2682 html +=
"angle:"_L1 + QString::number(conicalGradient->angle()) + u',';
2685 const QStringList coordinateModes = {
"logical"_L1,
"stretchtodevice"_L1,
"objectbounding"_L1,
"object"_L1 };
2686 html +=
"coordinatemode:"_L1;
2687 html += coordinateModes.at(
int(gradient->coordinateMode()));
2690 const QStringList spreads = {
"pad"_L1,
"reflect"_L1,
"repeat"_L1 };
2691 html +=
"spread:"_L1;
2692 html += spreads.at(
int(gradient->spread()));
2694 for (
const QGradientStop &stop : gradient->stops()) {
2695 html +=
",stop:"_L1;
2696 html += QString::number(stop.first);
2698 html += colorValue(stop.second);
2703 html +=
" color:"_L1;
2704 html += colorValue(brush.color());
2707 attributesEmitted =
true;
2710 if (format.background() != defaultCharFormat.background()
2711 && format.background().style() == Qt::SolidPattern) {
2712 html +=
" background-color:"_L1;
2713 html += colorValue(format.background().color());
2715 attributesEmitted =
true;
2718 if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()
2719 && format.verticalAlignment() != QTextCharFormat::AlignNormal)
2721 html +=
" vertical-align:"_L1;
2723 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2724 if (valign == QTextCharFormat::AlignSubScript)
2726 else if (valign == QTextCharFormat::AlignSuperScript)
2728 else if (valign == QTextCharFormat::AlignMiddle)
2729 html +=
"middle"_L1;
2730 else if (valign == QTextCharFormat::AlignTop)
2732 else if (valign == QTextCharFormat::AlignBottom)
2733 html +=
"bottom"_L1;
2736 attributesEmitted =
true;
2739 if (format.fontCapitalization() != QFont::MixedCase) {
2740 const QFont::Capitalization caps = format.fontCapitalization();
2741 if (caps == QFont::AllUppercase)
2742 html +=
" text-transform:uppercase;"_L1;
2743 else if (caps == QFont::AllLowercase)
2744 html +=
" text-transform:lowercase;"_L1;
2745 else if (caps == QFont::SmallCaps)
2746 html +=
" font-variant:small-caps;"_L1;
2747 attributesEmitted =
true;
2750 if (format.fontWordSpacing() != 0.0) {
2751 html +=
" word-spacing:"_L1;
2752 html += QString::number(format.fontWordSpacing());
2754 attributesEmitted =
true;
2757 if (format.hasProperty(QTextFormat::TextOutline)) {
2758 QPen outlinePen = format.textOutline();
2759 html +=
" -qt-stroke-color:"_L1;
2760 html += colorValue(outlinePen.color());
2763 html +=
" -qt-stroke-width:"_L1;
2764 html += QString::number(outlinePen.widthF());
2767 html +=
" -qt-stroke-linecap:"_L1;
2768 if (outlinePen.capStyle() == Qt::SquareCap)
2769 html +=
"squarecap;"_L1;
2770 else if (outlinePen.capStyle() == Qt::FlatCap)
2771 html +=
"flatcap;"_L1;
2772 else if (outlinePen.capStyle() == Qt::RoundCap)
2773 html +=
"roundcap;"_L1;
2775 html +=
" -qt-stroke-linejoin:"_L1;
2776 if (outlinePen.joinStyle() == Qt::MiterJoin)
2777 html +=
"miterjoin;"_L1;
2778 else if (outlinePen.joinStyle() == Qt::SvgMiterJoin)
2779 html +=
"svgmiterjoin;"_L1;
2780 else if (outlinePen.joinStyle() == Qt::BevelJoin)
2781 html +=
"beveljoin;"_L1;
2782 else if (outlinePen.joinStyle() == Qt::RoundJoin)
2783 html +=
"roundjoin;"_L1;
2785 if (outlinePen.joinStyle() == Qt::MiterJoin ||
2786 outlinePen.joinStyle() == Qt::SvgMiterJoin) {
2787 html +=
" -qt-stroke-miterlimit:"_L1;
2788 html += QString::number(outlinePen.miterLimit());
2792 if (outlinePen.style() == Qt::CustomDashLine && !outlinePen.dashPattern().empty()) {
2793 html +=
" -qt-stroke-dasharray:"_L1;
2795 QList<qreal> dashes = outlinePen.dashPattern();
2797 for (
int i = 0; i < dashes.length() - 1; i++) {
2798 qreal dash = dashes[i];
2799 dashArrayString += QString::number(dash) + u',';
2802 dashArrayString += QString::number(dashes.last());
2803 html += dashArrayString;
2806 html +=
" -qt-stroke-dashoffset:"_L1;
2807 html += QString::number(outlinePen.dashOffset());
2811 attributesEmitted =
true;
2814 return attributesEmitted;
2817void QTextHtmlExporter::emitTextLength(
const char *attribute,
const QTextLength &length)
2819 if (length.type() == QTextLength::VariableLength)
2823 html += QLatin1StringView(attribute);
2825 html += QString::number(length.rawValue());
2827 if (length.type() == QTextLength::PercentageLength)
2835 if (align & Qt::AlignLeft)
2837 else if (align & Qt::AlignRight)
2838 html +=
" align=\"right\""_L1;
2839 else if (align & Qt::AlignHCenter)
2840 html +=
" align=\"center\""_L1;
2841 else if (align & Qt::AlignJustify)
2842 html +=
" align=\"justify\""_L1;
2845void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
2847 if (pos == QTextFrameFormat::InFlow)
2850 if (mode == EmitStyleTag)
2851 html +=
" style=\"float:"_L1;
2853 html +=
" float:"_L1;
2855 if (pos == QTextFrameFormat::FloatLeft)
2856 html +=
" left;"_L1;
2857 else if (pos == QTextFrameFormat::FloatRight)
2858 html +=
" right;"_L1;
2860 Q_ASSERT_X(0,
"QTextHtmlExporter::emitFloatStyle()",
"pos should be a valid enum type");
2862 if (mode == EmitStyleTag)
2869 case QTextFrameFormat::BorderStyle_None:
2871 case QTextFrameFormat::BorderStyle_Dotted:
2873 case QTextFrameFormat::BorderStyle_Dashed:
2875 case QTextFrameFormat::BorderStyle_Solid:
2877 case QTextFrameFormat::BorderStyle_Double:
2879 case QTextFrameFormat::BorderStyle_DotDash:
2880 return "dot-dash"_L1;
2881 case QTextFrameFormat::BorderStyle_DotDotDash:
2882 return "dot-dot-dash"_L1;
2883 case QTextFrameFormat::BorderStyle_Groove:
2885 case QTextFrameFormat::BorderStyle_Ridge:
2887 case QTextFrameFormat::BorderStyle_Inset:
2889 case QTextFrameFormat::BorderStyle_Outset:
2899 Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
2901 html +=
" border-style:"_L1;
2902 html += richtextBorderStyleToHtmlBorderStyle(style);
2908 if (policy & QTextFormat::PageBreak_AlwaysBefore)
2909 html +=
" page-break-before:always;"_L1;
2911 if (policy & QTextFormat::PageBreak_AlwaysAfter)
2912 html +=
" page-break-after:always;"_L1;
2917 html +=
" font-family:"_L1;
2920 for (
const QString &family : families) {
2921 auto quote =
"\'"_L1;
2922 if (family.contains(u'\''))
2923 quote =
"""_L1;
2930 html += family.toHtmlEscaped();
2936void QTextHtmlExporter::emitMargins(
const QString &top,
const QString &bottom,
const QString &left,
const QString &right)
2938 html +=
" margin-top:"_L1;
2942 html +=
" margin-bottom:"_L1;
2946 html +=
" margin-left:"_L1;
2950 html +=
" margin-right:"_L1;
2957 const QTextCharFormat format = fragment.charFormat();
2959 bool closeAnchor =
false;
2961 if (format.isAnchor()) {
2962 const auto names = format.anchorNames();
2963 if (!names.isEmpty()) {
2964 html +=
"<a name=\""_L1;
2965 html += names.constFirst().toHtmlEscaped();
2966 html +=
"\"></a>"_L1;
2968 const QString href = format.anchorHref();
2969 if (!href.isEmpty()) {
2970 html +=
"<a href=\""_L1;
2971 html += href.toHtmlEscaped();
2977 QString txt = fragment.text();
2978 const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
2979 const bool isImage = isObject && format.isImageFormat();
2981 const auto styleTag =
"<span style=\""_L1;
2984 bool attributesEmitted =
false;
2986 attributesEmitted = emitCharFormatStyle(format);
2987 if (attributesEmitted)
2990 html.chop(styleTag.size());
2993 for (
int i = 0; isImage && i < txt.size(); ++i) {
2994 QTextImageFormat imgFmt = format.toImageFormat();
3000 if (imgFmt.hasProperty(QTextFormat::ImageMaxWidth)) {
3001 auto length = imgFmt.lengthProperty(QTextFormat::ImageMaxWidth);
3002 maxWidthCss +=
"max-width:"_L1;
3003 if (length.type() == QTextLength::PercentageLength)
3004 maxWidthCss += QString::number(length.rawValue()) +
"%;"_L1;
3005 else if (length.type() == QTextLength::FixedLength)
3006 maxWidthCss += QString::number(length.rawValue()) +
"px;"_L1;
3009 if (imgFmt.hasProperty(QTextFormat::ImageName))
3010 emitAttribute(
"src", imgFmt.name());
3012 if (imgFmt.hasProperty(QTextFormat::ImageAltText))
3013 emitAttribute(
"alt", imgFmt.stringProperty(QTextFormat::ImageAltText));
3015 if (imgFmt.hasProperty(QTextFormat::ImageTitle))
3016 emitAttribute(
"title", imgFmt.stringProperty(QTextFormat::ImageTitle));
3018 if (imgFmt.hasProperty(QTextFormat::ImageWidth))
3019 emitAttribute(
"width", QString::number(imgFmt.width()));
3021 if (imgFmt.hasProperty(QTextFormat::ImageHeight))
3022 emitAttribute(
"height", QString::number(imgFmt.height()));
3024 if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
3025 html +=
" style=\"vertical-align: middle;"_L1 + maxWidthCss + u'\"';
3026 else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
3027 html +=
" style=\"vertical-align: top;"_L1 + maxWidthCss + u'\"';
3028 else if (!maxWidthCss.isEmpty())
3029 html +=
" style=\""_L1 + maxWidthCss + u'\"';
3031 if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
3032 emitFloatStyle(imageFrame->frameFormat().position());
3037 Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
3039 txt = txt.toHtmlEscaped();
3043 txt.replace(u'\n',
"<br />"_L1);
3044 txt.replace(QChar::LineSeparator,
"<br />"_L1);
3048 if (attributesEmitted)
3049 html +=
"</span>"_L1;
3057 return style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
3058 || style == QTextListFormat::ListUpperAlpha
3059 || style == QTextListFormat::ListUpperRoman
3060 || style == QTextListFormat::ListLowerRoman
3066 QTextBlockFormat format = block.blockFormat();
3067 emitAlignment(format.alignment());
3071 if (block.textDirection() == Qt::RightToLeft)
3072 html +=
" dir='rtl'"_L1;
3074 const auto style =
" style=\""_L1;
3077 const bool emptyBlock = block.begin().atEnd();
3079 html +=
"-qt-paragraph-type:empty;"_L1;
3082 emitMargins(QString::number(format.topMargin()),
3083 QString::number(format.bottomMargin()),
3084 QString::number(format.leftMargin()),
3085 QString::number(format.rightMargin()));
3087 html +=
" -qt-block-indent:"_L1;
3088 html += QString::number(format.indent());
3091 html +=
" text-indent:"_L1;
3092 html += QString::number(format.textIndent());
3095 if (block.userState() != -1) {
3096 html +=
" -qt-user-state:"_L1;
3097 html += QString::number(block.userState());
3101 if (format.lineHeightType() != QTextBlockFormat::SingleHeight) {
3102 html +=
" line-height:"_L1
3103 + QString::number(format.lineHeight());
3104 switch (format.lineHeightType()) {
3105 case QTextBlockFormat::ProportionalHeight:
3108 case QTextBlockFormat::FixedHeight:
3109 html +=
"; -qt-line-height-type: fixed;"_L1;
3111 case QTextBlockFormat::MinimumHeight:
3114 case QTextBlockFormat::LineDistanceHeight:
3115 html +=
"; -qt-line-height-type: line-distance;"_L1;
3123 emitPageBreakPolicy(format.pageBreakPolicy());
3125 QTextCharFormat diff;
3127 const QTextCharFormat blockCharFmt = block.charFormat();
3128 diff = formatDifference(defaultCharFormat, blockCharFmt).toCharFormat();
3131 diff.clearProperty(QTextFormat::BackgroundBrush);
3132 if (format.hasProperty(QTextFormat::BackgroundBrush)) {
3133 QBrush bg = format.background();
3134 if (bg.style() != Qt::NoBrush)
3135 diff.setProperty(QTextFormat::BackgroundBrush, format.property(QTextFormat::BackgroundBrush));
3138 if (!diff.properties().isEmpty())
3139 emitCharFormatStyle(diff);
3147 if (block.begin().atEnd()) {
3149 int p = block.position();
3153 QTextDocumentPrivate::FragmentIterator frag = QTextDocumentPrivate::get(doc)->find(p);
3154 QChar ch = QTextDocumentPrivate::get(doc)->buffer().at(frag->stringPosition);
3164 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
3166 QTextList *list = block.textList();
3168 if (list->itemNumber(block) == 0) {
3169 const QTextListFormat format = list->format();
3170 const int style = format.style();
3171 bool ordered =
false;
3173 case QTextListFormat::ListDisc: html +=
"<ul"_L1;
break;
3174 case QTextListFormat::ListCircle: html +=
"<ul type=\"circle\""_L1;
break;
3175 case QTextListFormat::ListSquare: html +=
"<ul type=\"square\""_L1;
break;
3176 case QTextListFormat::ListDecimal: html +=
"<ol"_L1; ordered =
true;
break;
3177 case QTextListFormat::ListLowerAlpha: html +=
"<ol type=\"a\""_L1; ordered =
true;
break;
3178 case QTextListFormat::ListUpperAlpha: html +=
"<ol type=\"A\""_L1; ordered =
true;
break;
3179 case QTextListFormat::ListLowerRoman: html +=
"<ol type=\"i\""_L1; ordered =
true;
break;
3180 case QTextListFormat::ListUpperRoman: html +=
"<ol type=\"I\""_L1; ordered =
true;
break;
3181 default: html +=
"<ul"_L1;
3184 if (ordered && format.start() != 1) {
3185 html +=
" start=\""_L1;
3186 html += QString::number(format.start());
3191 styleString +=
"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;"_L1;
3193 if (format.hasProperty(QTextFormat::ListIndent)) {
3194 styleString +=
" -qt-list-indent: "_L1;
3195 styleString += QString::number(format.indent());
3196 styleString += u';';
3199 if (format.hasProperty(QTextFormat::ListNumberPrefix)) {
3200 QString numberPrefix = format.numberPrefix();
3201 numberPrefix.replace(u'"',
"\\22"_L1);
3202 numberPrefix.replace(u'\'',
"\\27"_L1);
3203 styleString +=
" -qt-list-number-prefix: "_L1;
3204 styleString += u'\'';
3205 styleString += numberPrefix;
3206 styleString += u'\'';
3207 styleString += u';';
3210 if (format.hasProperty(QTextFormat::ListNumberSuffix)) {
3211 if (format.numberSuffix() !=
"."_L1) {
3212 QString numberSuffix = format.numberSuffix();
3213 numberSuffix.replace(u'"',
"\\22"_L1);
3214 numberSuffix.replace(u'\'',
"\\27"_L1);
3215 styleString +=
" -qt-list-number-suffix: "_L1;
3216 styleString += u'\'';
3217 styleString += numberSuffix;
3218 styleString += u'\'';
3219 styleString += u';';
3223 html +=
" style=\""_L1;
3224 html += styleString;
3230 const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
3231 if (!blockFmt.properties().isEmpty()) {
3232 html +=
" style=\""_L1;
3233 emitCharFormatStyle(blockFmt);
3236 defaultCharFormat.merge(block.charFormat());
3238 if (block.blockFormat().hasProperty(QTextFormat::BlockMarker)) {
3239 switch (block.blockFormat().marker()) {
3240 case QTextBlockFormat::MarkerType::Checked:
3241 html +=
" class=\"checked\""_L1;
3243 case QTextBlockFormat::MarkerType::Unchecked:
3244 html +=
" class=\"unchecked\""_L1;
3246 case QTextBlockFormat::MarkerType::NoMarker:
3252 const QTextBlockFormat blockFormat = block.blockFormat();
3253 if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
3256 QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
3257 if (width.type() != QTextLength::VariableLength)
3258 emitTextLength(
"width", width);
3261 if (blockFormat.hasProperty(QTextFormat::BackgroundBrush)) {
3262 html +=
"style=\""_L1;
3263 html +=
"background-color:"_L1;
3264 html += colorValue(qvariant_cast<QBrush>(blockFormat.property(QTextFormat::BackgroundBrush)).color());
3273 const bool pre = blockFormat.nonBreakableLines();
3279 int headingLevel = blockFormat.headingLevel();
3280 if (headingLevel > 0 && headingLevel <= 6)
3281 html +=
"<h"_L1 + QString::number(headingLevel);
3286 emitBlockAttributes(block);
3289 if (block.begin().atEnd())
3290 html +=
"<br />"_L1;
3292 QTextBlock::Iterator it = block.begin();
3293 if (fragmentMarkers && !it.atEnd() && block == doc->begin())
3294 html +=
"<!--StartFragment-->"_L1;
3296 for (; !it.atEnd(); ++it)
3297 emitFragment(it.fragment());
3299 if (fragmentMarkers && block.position() + block.length() == QTextDocumentPrivate::get(doc)->length())
3300 html +=
"<!--EndFragment-->"_L1;
3305 html +=
"</pre>"_L1;
3307 closeTags +=
"</li>"_L1;
3309 int headingLevel = blockFormat.headingLevel();
3310 if (headingLevel > 0 && headingLevel <= 6)
3311 html += QString::asprintf(
"</h%d>", headingLevel);
3317 if (list->itemNumber(block) == list->count() - 1) {
3318 if (isOrderedList(list->format().style()))
3319 closeTags +=
"</ol>"_L1;
3321 closeTags +=
"</ul>"_L1;
3323 const QTextBlock nextBlock = block.next();
3327 if (nextBlock.isValid() && nextBlock.textList() &&
3328 nextBlock.textList()->itemNumber(nextBlock) == 0 &&
3329 nextBlock.textList()->format().indent() > list->format().indent()) {
3331 if (!closingTags.isEmpty() && list->itemNumber(block) == list->count() - 1)
3332 lastTag = closingTags.takeLast();
3333 lastTag.prepend(closeTags);
3334 closingTags << lastTag;
3335 }
else if (list->itemNumber(block) == list->count() - 1) {
3339 if (!closingTags.isEmpty())
3340 html += closingTags.takeLast();
3346 defaultCharFormat = oldDefaultCharFormat;
3351QString
QTextHtmlExporter::findUrlForImage(
const QTextDocument *doc, qint64 cacheKey,
bool isPixmap)
3357 if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent()))
3358 return findUrlForImage(parent, cacheKey, isPixmap);
3360 const QTextDocumentPrivate *priv = QTextDocumentPrivate::get(doc);
3361 Q_ASSERT(priv !=
nullptr);
3363 QMap<QUrl, QVariant>::const_iterator it = priv->cachedResources.constBegin();
3364 for (; it != priv->cachedResources.constEnd(); ++it) {
3366 const QVariant &v = it.value();
3367 if (v.userType() == QMetaType::QImage && !isPixmap) {
3368 if (qvariant_cast<QImage>(v).cacheKey() == cacheKey)
3372 if (v.userType() == QMetaType::QPixmap && isPixmap) {
3373 if (qvariant_cast<QPixmap>(v).cacheKey() == cacheKey)
3378 if (it != priv->cachedResources.constEnd())
3379 url = it.key().toString();
3384void QTextDocumentPrivate::mergeCachedResources(
const QTextDocumentPrivate *priv)
3389 cachedResources.insert(priv->cachedResources);
3394 if (format.hasProperty(QTextFormat::BackgroundImageUrl)) {
3395 QString url = format.property(QTextFormat::BackgroundImageUrl).toString();
3396 emitAttribute(
"background", url);
3398 const QBrush &brush = format.background();
3399 if (brush.style() == Qt::SolidPattern) {
3400 emitAttribute(
"bgcolor", colorValue(brush.color()));
3401 }
else if (brush.style() == Qt::TexturePattern) {
3403 const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
3405 const QString url = findUrlForImage(doc, cacheKey, isPixmap);
3408 emitAttribute(
"background", url);
3415 QTextTableFormat format = table->format();
3417 html +=
"\n<table"_L1;
3419 if (format.hasProperty(QTextFormat::FrameBorder))
3420 emitAttribute(
"border", QString::number(format.border()));
3422 emitFrameStyle(format, TableFrame);
3424 emitAlignment(format.alignment());
3425 emitTextLength(
"width", format.width());
3427 if (format.hasProperty(QTextFormat::TableCellSpacing))
3428 emitAttribute(
"cellspacing", QString::number(format.cellSpacing()));
3429 if (format.hasProperty(QTextFormat::TableCellPadding))
3430 emitAttribute(
"cellpadding", QString::number(format.cellPadding()));
3432 emitBackgroundAttribute(format);
3436 const int rows = table->rows();
3437 const int columns = table->columns();
3439 QList<QTextLength> columnWidths = format.columnWidthConstraints();
3440 if (columnWidths.isEmpty()) {
3441 columnWidths.resize(columns);
3442 columnWidths.fill(QTextLength());
3444 Q_ASSERT(columnWidths.size() == columns);
3446 QVarLengthArray<
bool> widthEmittedForColumn(columns);
3447 for (
int i = 0; i < columns; ++i)
3448 widthEmittedForColumn[i] =
false;
3450 const int headerRowCount = qMin(format.headerRowCount(), rows);
3451 if (headerRowCount > 0)
3452 html +=
"<thead>"_L1;
3454 for (
int row = 0; row < rows; ++row) {
3455 html +=
"\n<tr>"_L1;
3457 for (
int col = 0; col < columns; ++col) {
3458 const QTextTableCell cell = table->cellAt(row, col);
3461 if (cell.row() != row)
3464 if (cell.column() != col)
3469 if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
3470 emitTextLength(
"width", columnWidths.at(col));
3471 widthEmittedForColumn[col] =
true;
3474 if (cell.columnSpan() > 1)
3475 emitAttribute(
"colspan", QString::number(cell.columnSpan()));
3477 if (cell.rowSpan() > 1)
3478 emitAttribute(
"rowspan", QString::number(cell.rowSpan()));
3480 const QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
3481 emitBackgroundAttribute(cellFormat);
3483 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
3485 QTextCharFormat::VerticalAlignment valign = cellFormat.verticalAlignment();
3488 if (valign >= QTextCharFormat::AlignMiddle && valign <= QTextCharFormat::AlignBottom) {
3489 styleString +=
" vertical-align:"_L1;
3491 case QTextCharFormat::AlignMiddle:
3492 styleString +=
"middle"_L1;
3494 case QTextCharFormat::AlignTop:
3495 styleString +=
"top"_L1;
3497 case QTextCharFormat::AlignBottom:
3498 styleString +=
"bottom"_L1;
3503 styleString += u';';
3505 QTextCharFormat temp;
3506 temp.setVerticalAlignment(valign);
3507 defaultCharFormat.merge(temp);
3510 if (cellFormat.hasProperty(QTextFormat::TableCellLeftPadding))
3511 styleString +=
" padding-left:"_L1 + QString::number(cellFormat.leftPadding()) + u';';
3512 if (cellFormat.hasProperty(QTextFormat::TableCellRightPadding))
3513 styleString +=
" padding-right:"_L1 + QString::number(cellFormat.rightPadding()) + u';';
3514 if (cellFormat.hasProperty(QTextFormat::TableCellTopPadding))
3515 styleString +=
" padding-top:"_L1 + QString::number(cellFormat.topPadding()) + u';';
3516 if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
3517 styleString +=
" padding-bottom:"_L1 + QString::number(cellFormat.bottomPadding()) + u';';
3519 if (cellFormat.hasProperty(QTextFormat::TableCellTopBorder))
3520 styleString +=
" border-top:"_L1 + QString::number(cellFormat.topBorder()) +
"px;"_L1;
3521 if (cellFormat.hasProperty(QTextFormat::TableCellRightBorder))
3522 styleString +=
" border-right:"_L1 + QString::number(cellFormat.rightBorder()) +
"px;"_L1;
3523 if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorder))
3524 styleString +=
" border-bottom:"_L1 + QString::number(cellFormat.bottomBorder()) +
"px;"_L1;
3525 if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorder))
3526 styleString +=
" border-left:"_L1 + QString::number(cellFormat.leftBorder()) +
"px;"_L1;
3528 if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderBrush))
3529 styleString +=
" border-top-color:"_L1 + cellFormat.topBorderBrush().color().name() + u';';
3530 if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderBrush))
3531 styleString +=
" border-right-color:"_L1 + cellFormat.rightBorderBrush().color().name() + u';';
3532 if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderBrush))
3533 styleString +=
" border-bottom-color:"_L1 + cellFormat.bottomBorderBrush().color().name() + u';';
3534 if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderBrush))
3535 styleString +=
" border-left-color:"_L1 + cellFormat.leftBorderBrush().color().name() + u';';
3537 if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderStyle))
3538 styleString +=
" border-top-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.topBorderStyle()) + u';';
3539 if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderStyle))
3540 styleString +=
" border-right-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.rightBorderStyle()) + u';';
3541 if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderStyle))
3542 styleString +=
" border-bottom-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.bottomBorderStyle()) + u';';
3543 if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderStyle))
3544 styleString +=
" border-left-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.leftBorderStyle()) + u';';
3546 if (!styleString.isEmpty())
3547 html +=
" style=\""_L1 + styleString + u'\"';
3551 emitFrame(cell.begin());
3555 defaultCharFormat = oldDefaultCharFormat;
3559 if (headerRowCount > 0 && row == headerRowCount - 1)
3560 html +=
"</thead>"_L1;
3563 html +=
"</table>"_L1;
3568 if (!frameIt.atEnd()) {
3569 QTextFrame::Iterator next = frameIt;
3572 && frameIt.currentFrame() ==
nullptr
3573 && frameIt.parentFrame() != doc->rootFrame()
3574 && frameIt.currentBlock().begin().atEnd())
3578 for (QTextFrame::Iterator it = frameIt;
3579 !it.atEnd(); ++it) {
3580 if (QTextFrame *f = it.currentFrame()) {
3581 if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
3586 }
else if (it.currentBlock().isValid()) {
3587 emitBlock(it.currentBlock());
3594 FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
3596 html +=
"\n<table"_L1;
3597 QTextFrameFormat format = f->frameFormat();
3599 if (format.hasProperty(QTextFormat::FrameBorder))
3600 emitAttribute(
"border", QString::number(format.border()));
3602 emitFrameStyle(format, frameType);
3604 emitTextLength(
"width", format.width());
3605 emitTextLength(
"height", format.height());
3608 if (frameType != RootFrame)
3609 emitBackgroundAttribute(format);
3612 html +=
"\n<tr>\n<td style=\"border: none;\">"_L1;
3613 emitFrame(f->begin());
3614 html +=
"</td></tr></table>"_L1;
3617void QTextHtmlExporter::emitFrameStyle(
const QTextFrameFormat &format, FrameType frameType)
3619 const auto styleAttribute =
" style=\""_L1;
3620 html += styleAttribute;
3621 const qsizetype originalHtmlLength = html.size();
3623 if (frameType == TextFrame)
3624 html +=
"-qt-table-type: frame;"_L1;
3625 else if (frameType == RootFrame)
3626 html +=
"-qt-table-type: root;"_L1;
3628 const QTextFrameFormat defaultFormat;
3630 emitFloatStyle(format.position(), OmitStyleTag);
3631 emitPageBreakPolicy(format.pageBreakPolicy());
3633 if (format.borderBrush() != defaultFormat.borderBrush()) {
3634 html +=
" border-color:"_L1;
3635 html += colorValue(format.borderBrush().color());
3639 if (format.borderStyle() != defaultFormat.borderStyle())
3640 emitBorderStyle(format.borderStyle());
3642 if (format.hasProperty(QTextFormat::FrameMargin)
3643 || format.hasProperty(QTextFormat::FrameLeftMargin)
3644 || format.hasProperty(QTextFormat::FrameRightMargin)
3645 || format.hasProperty(QTextFormat::FrameTopMargin)
3646 || format.hasProperty(QTextFormat::FrameBottomMargin))
3647 emitMargins(QString::number(format.topMargin()),
3648 QString::number(format.bottomMargin()),
3649 QString::number(format.leftMargin()),
3650 QString::number(format.rightMargin()));
3652 if (format.property(QTextFormat::TableBorderCollapse).toBool())
3653 html +=
" border-collapse:collapse;"_L1;
3655 if (html.size() == originalHtmlLength)
3656 html.chop(styleAttribute.size());
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671#ifndef QT_NO_TEXTHTMLPARSER
3672QString QTextDocument::toHtml()
const
3674 return QTextHtmlExporter(
this).toHtml();
3679
3680
3681
3682
3683
3684
3685#if QT_CONFIG(textmarkdownwriter)
3686QString QTextDocument::toMarkdown(QTextDocument::MarkdownFeatures features)
const
3689 QTextStream s(&ret);
3690 QTextMarkdownWriter w(s, features);
3691 if (w.writeAll(
this))
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757#if QT_CONFIG(textmarkdownreader)
3758void QTextDocument::setMarkdown(
const QString &markdown, QTextDocument::MarkdownFeatures features)
3760 QTextMarkdownImporter(
this, features).import(markdown);
3765
3766
3767QList<QTextFormat> QTextDocument::allFormats()
const
3769 Q_D(
const QTextDocument);
3770 return d->formatCollection()->formats;
3774
3775
3776
3777
3778
3782#include "moc_qtextdocument.cpp"
\macro QT_RESTRICTED_CAST_FROM_ASCII
QString toHtml(ExportMode mode=ExportEntireDocument)
Returns the document in HTML format.
QTextHtmlExporter(const QTextDocument *_doc)
Combined button and popup list for selecting options.
Q_GUI_EXPORT bool mightBeRichText(QAnyStringView)
Returns true if the string text is likely to be rich text; otherwise returns false.
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION unsigned int qt_int_sqrt(unsigned int n)
\inmodule QtCore \title Global Qt Declarations
#define qCDebug(category,...)
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)
static bool mightBeRichTextImpl(T text)
static bool mightBeRichTextImpl(QUtf8StringView 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)
#define QTextBeginningOfFrame