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 const qsizetype first = open + 1;
111 qsizetype tagLength = 0;
112 for (qsizetype i = first; i < close; ++i) {
113 const auto current = QChar(text[i]);
114 if (current.isDigit() || current.isLetter())
116 else if (tagLength && current.isSpace())
118 else if (tagLength && current == u'/' && i + 1 == close)
120 else if (!current.isSpace() && (tagLength || current != u'!'))
123#ifndef QT_NO_TEXTHTMLPARSER
124 return QTextHtmlParser::lookupElement(QAnyStringView(text).mid(first, tagLength)) != -1;
135 return mightBeRichTextImpl(QLatin1StringView(QByteArrayView(text)));
140 return text.visit([](
auto text) {
return mightBeRichTextImpl(text); });
144
145
146
147
148
149
150
151
152
153QString
Qt::convertFromPlainText(
const QString &plain, Qt::WhiteSpaceMode mode)
158 for (qsizetype i = 0; i < plain.size(); ++i) {
159 if (plain[i] == u'\n'){
161 while (i+1 < plain.size() && plain[i+1] == u'\n') {
175 if (mode == Qt::WhiteSpacePre && plain[i] == u'\t'){
183 else if (mode == Qt::WhiteSpacePre && plain[i].isSpace())
185 else if (plain[i] == u'<')
187 else if (plain[i] == u'>')
189 else if (plain[i] == u'&')
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
260
263
264
265
268
269
270
271
272
273
274
277
278
279QTextDocument::QTextDocument(QObject *parent)
280 : QObject(*
new QTextDocumentPrivate, parent)
287
288
289
290QTextDocument::QTextDocument(
const QString &text, QObject *parent)
291 : QObject(*
new QTextDocumentPrivate, parent)
295 QTextCursor(
this).insertText(text);
299
300
301QTextDocument::QTextDocument(QTextDocumentPrivate &dd, QObject *parent)
302 : QObject(dd, parent)
309
310
311QTextDocument::~QTextDocument()
317
318
319
320QTextDocument *QTextDocument::clone(QObject *parent)
const
322 Q_D(
const QTextDocument);
323 QTextDocument *doc =
new QTextDocument(parent);
325 const QTextCursor thisCursor(
const_cast<QTextDocument *>(
this));
327 const auto blockFormat = thisCursor.blockFormat();
328 if (blockFormat.isValid() && !blockFormat.isEmpty())
329 QTextCursor(doc).setBlockFormat(blockFormat);
331 const auto blockCharFormat = thisCursor.blockCharFormat();
332 if (blockCharFormat.isValid() && !blockCharFormat.isEmpty())
333 QTextCursor(doc).setBlockCharFormat(blockCharFormat);
335 QTextCursor(doc).insertFragment(QTextDocumentFragment(
this));
337 doc->rootFrame()->setFrameFormat(rootFrame()->frameFormat());
338 QTextDocumentPrivate *priv = doc->d_func();
339 priv->title = d->title;
341 priv->cssMedia = d->cssMedia;
342 priv->pageSize = d->pageSize;
343 priv->indentWidth = d->indentWidth;
344 priv->defaultTextOption = d->defaultTextOption;
345 priv->setDefaultFont(d->defaultFont());
346 priv->resources = d->resources;
347 priv->cachedResources.clear();
348 priv->resourceProvider = d->resourceProvider;
349#ifndef QT_NO_CSSPARSER
350 priv->defaultStyleSheet = d->defaultStyleSheet;
351 priv->parsedDefaultStyleSheet = d->parsedDefaultStyleSheet;
357
358
359bool QTextDocument::isEmpty()
const
361 Q_D(
const QTextDocument);
363
364 return d->length() <= 1;
368
369
370void QTextDocument::clear()
374 d->resources.clear();
378
379
380
381
382
383
384
385
386
387
388
389void QTextDocument::undo(QTextCursor *cursor)
392 const int pos = d->undoRedo(
true);
393 if (cursor && pos >= 0) {
394 *cursor = QTextCursor(
this);
395 cursor->setPosition(pos);
400
401
402
403
404
405
406void QTextDocument::redo(QTextCursor *cursor)
409 const int pos = d->undoRedo(
false);
410 if (cursor && pos >= 0) {
411 *cursor = QTextCursor(
this);
412 cursor->setPosition(pos);
417
418
419
420
421
424
425
426
427
428
429
430
431
432
433
434void QTextDocument::clearUndoRedoStacks(Stacks stacksToClear)
437 d->clearUndoRedoStacks(stacksToClear,
true);
441
442
443
444void QTextDocument::undo()
451
452
453
454void QTextDocument::redo()
461
462
463
464
465void QTextDocument::appendUndoItem(QAbstractUndoItem *item)
468 d->appendUndoItem(item);
472
473
474
475
476
477
478void QTextDocument::setUndoRedoEnabled(
bool enable)
481 d->enableUndoRedo(enable);
484bool QTextDocument::isUndoRedoEnabled()
const
486 Q_D(
const QTextDocument);
487 return d->isUndoRedoEnabled();
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511int QTextDocument::maximumBlockCount()
const
513 Q_D(
const QTextDocument);
514 return d->maximumBlockCount;
517void QTextDocument::setMaximumBlockCount(
int maximum)
520 d->maximumBlockCount = maximum;
521 d->ensureMaximumBlockCount();
522 setUndoRedoEnabled(
false);
526
527
528
529
530
531
532QTextOption QTextDocument::defaultTextOption()
const
534 Q_D(
const QTextDocument);
535 return d->defaultTextOption;
539
540
541
542
543void QTextDocument::setDefaultTextOption(
const QTextOption &option)
546 d->defaultTextOption = option;
548 d->lout->documentChanged(0, 0, d->length());
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567QUrl QTextDocument::baseUrl()
const
569 Q_D(
const QTextDocument);
573void QTextDocument::setBaseUrl(
const QUrl &url)
576 if (d->baseUrl != url) {
579 d->lout->documentChanged(0, 0, d->length());
580 emit baseUrlChanged(url);
585
586
587
588
589
590Qt::CursorMoveStyle QTextDocument::defaultCursorMoveStyle()
const
592 Q_D(
const QTextDocument);
593 return d->defaultCursorMoveStyle;
597
598
599
600
601void QTextDocument::setDefaultCursorMoveStyle(Qt::CursorMoveStyle style)
604 d->defaultCursorMoveStyle = style;
608
609
610
611
612
613
614void QTextDocument::markContentsDirty(
int from,
int length)
617 d->documentChange(from, length);
618 if (!d->inContentsChange) {
620 d->lout->documentChanged(d->docChangeFrom, d->docChangeOldLength, d->docChangeLength);
621 d->docChangeFrom = -1;
627
628
629
630
631
632
633
634
635
636
637
638
639
640
642void QTextDocument::setUseDesignMetrics(
bool b)
645 if (b == d->defaultTextOption.useDesignMetrics())
647 d->defaultTextOption.setUseDesignMetrics(b);
649 d->lout->documentChanged(0, 0, d->length());
652bool QTextDocument::useDesignMetrics()
const
654 Q_D(
const QTextDocument);
655 return d->defaultTextOption.useDesignMetrics();
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
676void QTextDocument::setLayoutEnabled(
bool b)
679 if (d->layoutEnabled == b)
681 d->layoutEnabled = b;
683 d->lout->documentChanged(0, 0, d->length());
686bool QTextDocument::isLayoutEnabled()
const
688 Q_D(
const QTextDocument);
689 return d->layoutEnabled;
693
694
695
696
697
698void QTextDocument::drawContents(QPainter *p,
const QRectF &rect)
701 QAbstractTextDocumentLayout::PaintContext ctx;
702 if (rect.isValid()) {
703 p->setClipRect(rect);
706 documentLayout()->draw(p, ctx);
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731void QTextDocument::setTextWidth(qreal width)
734 QSizeF sz = d->pageSize;
736 qCDebug(lcLayout) <<
"page size" << sz <<
"-> width" << width;
742qreal QTextDocument::textWidth()
const
744 Q_D(
const QTextDocument);
745 return d->pageSize.width();
749
750
751
752
753
754
755
756qreal QTextDocument::idealWidth()
const
758 if (QTextDocumentLayout *lout = qobject_cast<QTextDocumentLayout *>(documentLayout()))
759 return lout->idealWidth();
764
765
766
767
768
769qreal QTextDocument::documentMargin()
const
771 Q_D(
const QTextDocument);
772 return d->documentMargin;
775void QTextDocument::setDocumentMargin(qreal margin)
778 if (d->documentMargin != margin) {
779 d->documentMargin = margin;
781 QTextFrame* root = rootFrame();
782 QTextFrameFormat format = root->frameFormat();
783 format.setMargin(margin);
784 root->setFrameFormat(format);
787 d->lout->documentChanged(0, 0, d->length());
793
794
795
796
797
798
799
800
801qreal QTextDocument::indentWidth()
const
803 Q_D(
const QTextDocument);
804 return d->indentWidth;
809
810
811
812
813
814
815
816
817
818void QTextDocument::setIndentWidth(qreal width)
821 if (d->indentWidth != width) {
822 d->indentWidth = width;
824 d->lout->documentChanged(0, 0, d->length());
832
833
834
835
836
837
838void QTextDocument::adjustSize()
841 QFont f = defaultFont();
843 int mw = fm.horizontalAdvance(u'x') * 80;
846 QSizeF size = documentLayout()->documentSize();
847 if (size.width() != 0) {
848 w = qt_int_sqrt((uint)(5 * size.height() * size.width() / 3));
849 setTextWidth(qMin(w, mw));
851 size = documentLayout()->documentSize();
852 if (w*3 < 5*size.height()) {
853 w = qt_int_sqrt((uint)(2 * size.height() * size.width()));
854 setTextWidth(qMin(w, mw));
857 setTextWidth(idealWidth());
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877QSizeF QTextDocument::size()
const
879 return documentLayout()->documentSize();
883
884
885
886
887
888
889
890
891
892
893int QTextDocument::blockCount()
const
895 Q_D(
const QTextDocument);
896 return d->blockMap().numNodes();
901
902
903
904
905
906
907
908int QTextDocument::lineCount()
const
910 Q_D(
const QTextDocument);
911 return d->blockMap().length(2);
915
916
917
918
919
920
921
922
923
924int QTextDocument::characterCount()
const
926 Q_D(
const QTextDocument);
931
932
933
934
935
936
937
938QChar QTextDocument::characterAt(
int pos)
const
940 Q_D(
const QTextDocument);
941 if (pos < 0 || pos >= d->length())
943 QTextDocumentPrivate::FragmentIterator fragIt = d->find(pos);
944 const QTextFragmentData *
const frag = fragIt.value();
945 const int offsetInFragment = qMax(0, pos - fragIt.position());
946 return d->text.at(frag->stringPosition + offsetInFragment);
951
952
953
954
955
956
957
958
959
960
961
962
963
965#ifndef QT_NO_CSSPARSER
966void QTextDocument::setDefaultStyleSheet(
const QString &sheet)
969 d->defaultStyleSheet = sheet;
970 QCss::Parser parser(sheet);
971 d->parsedDefaultStyleSheet = QCss::StyleSheet();
972 d->parsedDefaultStyleSheet.origin = QCss::StyleSheetOrigin_UserAgent;
973 parser.parse(&d->parsedDefaultStyleSheet);
976QString QTextDocument::defaultStyleSheet()
const
978 Q_D(
const QTextDocument);
979 return d->defaultStyleSheet;
984
985
986
987
988
989
990
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1023
1024
1025
1026
1027
1030
1031
1032
1033
1034
1035
1036
1037
1040
1041
1042
1043
1044
1045
1046
1049
1050
1051
1052
1053
1054
1055
1056
1060
1061
1062
1063
1064bool QTextDocument::isUndoAvailable()
const
1066 Q_D(
const QTextDocument);
1067 return d->isUndoAvailable();
1071
1072
1073
1074
1075bool QTextDocument::isRedoAvailable()
const
1077 Q_D(
const QTextDocument);
1078 return d->isRedoAvailable();
1082
1083
1084
1085
1086
1087int QTextDocument::availableUndoSteps()
const
1089 Q_D(
const QTextDocument);
1090 return d->availableUndoSteps();
1094
1095
1096
1097
1098
1099int QTextDocument::availableRedoSteps()
const
1101 Q_D(
const QTextDocument);
1102 return d->availableRedoSteps();
1106
1107
1108
1109
1110
1111
1112
1113
1114int QTextDocument::revision()
const
1116 Q_D(
const QTextDocument);
1123
1124
1125
1126
1127
1128void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *layout)
1131 d->setLayout(layout);
1135
1136
1137QAbstractTextDocumentLayout *QTextDocument::documentLayout()
const
1139 Q_D(
const QTextDocument);
1141 QTextDocument *that =
const_cast<QTextDocument *>(
this);
1142 that->d_func()->setLayout(
new QTextDocumentLayout(that));
1149
1150
1151
1152
1153
1154QString QTextDocument::metaInformation(MetaInformation info)
const
1156 Q_D(
const QTextDocument);
1165 return d->frontMatter;
1171
1172
1173
1174
1175
1176void QTextDocument::setMetaInformation(MetaInformation info,
const QString &string)
1187 d->cssMedia = string;
1190 d->frontMatter = string;
1196
1197
1198
1199
1200
1201
1202
1203QString QTextDocument::toRawText()
const
1205 Q_D(
const QTextDocument);
1206 return d->plainText();
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226QString QTextDocument::toPlainText()
const
1228 Q_D(
const QTextDocument);
1229 QString txt = d->plainText();
1231 constexpr char16_t delims[] = { 0xfdd0, 0xfdd1,
1232 QChar::ParagraphSeparator, QChar::LineSeparator, QChar::Nbsp };
1234 const size_t pos = std::u16string_view(txt).find_first_of(
1235 std::u16string_view(delims, std::size(delims)));
1236 if (pos == std::u16string_view::npos)
1239 QChar *uc = txt.data();
1240 QChar *
const e = uc + txt.size();
1242 for (uc += pos; uc != e; ++uc) {
1243 switch (uc->unicode()) {
1246 case QChar::ParagraphSeparator:
1247 case QChar::LineSeparator:
1261
1262
1263
1264
1265
1266void QTextDocument::setPlainText(
const QString &text)
1269 bool previousState = d->isUndoRedoEnabled();
1270 d->enableUndoRedo(
false);
1271 d->beginEditBlock();
1273 QTextCursor(
this).insertText(text);
1275 d->enableUndoRedo(previousState);
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1297#ifndef QT_NO_TEXTHTMLPARSER
1299void QTextDocument::setHtml(
const QString &html)
1302 bool previousState = d->isUndoRedoEnabled();
1303 d->enableUndoRedo(
false);
1304 d->beginEditBlock();
1308 QTextHtmlImporter(
this, html, QTextHtmlImporter::ImportToDocument).import();
1310 d->enableUndoRedo(previousState);
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1347static bool findInBlock(
const QTextBlock &block,
const QString &expression,
int offset,
1348 QTextDocument::FindFlags options, QTextCursor *cursor)
1351 text.replace(QChar::Nbsp, u' ');
1352 Qt::CaseSensitivity sensitivity = options & QTextDocument::FindCaseSensitively ? Qt::CaseSensitive : Qt::CaseInsensitive;
1355 while (offset >= 0 && offset <= text.size()) {
1356 idx = (options & QTextDocument::FindBackward) ?
1357 text.lastIndexOf(expression, offset, sensitivity) : text.indexOf(expression, offset, sensitivity);
1361 if (options & QTextDocument::FindWholeWords) {
1362 const int start = idx;
1363 const int end = start + expression.size();
1364 if ((start != 0 && text.at(start - 1).isLetterOrNumber())
1365 || (end != text.size() && text.at(end).isLetterOrNumber())) {
1367 offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
1373 *cursor = QTextCursorPrivate::fromPosition(
const_cast<QTextDocumentPrivate *>(QTextDocumentPrivate::get(block)),
1374 block.position() + idx);
1375 cursor->setPosition(cursor->position() + expression.size(), QTextCursor::KeepAnchor);
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397QTextCursor QTextDocument::find(
const QString &subString,
int from, FindFlags options)
const
1399 Q_D(
const QTextDocument);
1401 if (subString.isEmpty())
1402 return QTextCursor();
1407 if (options & FindBackward) {
1410 return QTextCursor();
1414 QTextBlock block = d->blocksFind(pos);
1415 int blockOffset = pos - block.position();
1417 if (!(options & FindBackward)) {
1418 while (block.isValid()) {
1419 if (findInBlock(block, subString, blockOffset, options, &cursor))
1421 block = block.next();
1425 if (blockOffset == block.length() - 1)
1427 while (block.isValid()) {
1428 if (findInBlock(block, subString, blockOffset, options, &cursor))
1430 block = block.previous();
1431 blockOffset = block.length() - 2;
1435 return QTextCursor();
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453QTextCursor QTextDocument::find(
const QString &subString,
const QTextCursor &cursor, FindFlags options)
const
1456 if (!cursor.isNull()) {
1457 if (options & QTextDocument::FindBackward)
1458 pos = cursor.selectionStart();
1460 pos = cursor.selectionEnd();
1463 return find(subString, pos, options);
1466#if QT_CONFIG(regularexpression)
1467static bool findInBlock(
const QTextBlock &block,
const QRegularExpression &expr,
int offset,
1468 QTextDocument::FindFlags options, QTextCursor *cursor)
1470 QString text = block.text();
1471 text.replace(QChar::Nbsp, u' ');
1472 QRegularExpressionMatch match;
1475 while (offset >= 0 && offset <= text.size()) {
1476 idx = (options & QTextDocument::FindBackward) ?
1477 text.lastIndexOf(expr, offset, &match) : text.indexOf(expr, offset, &match);
1481 if (options & QTextDocument::FindWholeWords) {
1482 const int start = idx;
1483 const int end = start + match.capturedLength();
1484 if ((start != 0 && text.at(start - 1).isLetterOrNumber())
1485 || (end != text.size() && text.at(end).isLetterOrNumber())) {
1487 offset = (options & QTextDocument::FindBackward) ? idx-1 : end+1;
1493 *cursor = QTextCursorPrivate::fromPosition(
const_cast<QTextDocumentPrivate *>(QTextDocumentPrivate::get(block)),
1494 block.position() + idx);
1495 cursor->setPosition(cursor->position() + match.capturedLength(), QTextCursor::KeepAnchor);
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521QTextCursor QTextDocument::find(
const QRegularExpression &expr,
int from, FindFlags options)
const
1523 Q_D(
const QTextDocument);
1525 if (!expr.isValid())
1526 return QTextCursor();
1531 if (options & FindBackward) {
1534 return QTextCursor();
1538 QTextBlock block = d->blocksFind(pos);
1539 int blockOffset = pos - block.position();
1541 QRegularExpression expression(expr);
1542 if (!(options & QTextDocument::FindCaseSensitively))
1543 expression.setPatternOptions(expr.patternOptions() | QRegularExpression::CaseInsensitiveOption);
1545 expression.setPatternOptions(expr.patternOptions() & ~QRegularExpression::CaseInsensitiveOption);
1547 if (!(options & FindBackward)) {
1548 while (block.isValid()) {
1549 if (findInBlock(block, expression, blockOffset, options, &cursor))
1551 block = block.next();
1555 while (block.isValid()) {
1556 if (findInBlock(block, expression, blockOffset, options, &cursor))
1558 block = block.previous();
1559 blockOffset = block.length() - 1;
1563 return QTextCursor();
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585QTextCursor QTextDocument::find(
const QRegularExpression &expr,
const QTextCursor &cursor, FindFlags options)
const
1588 if (!cursor.isNull()) {
1589 if (options & QTextDocument::FindBackward)
1590 pos = cursor.selectionStart();
1592 pos = cursor.selectionEnd();
1594 return find(expr, pos, options);
1599
1600
1601
1602
1603
1604
1605
1606
1607QTextObject *QTextDocument::createObject(
const QTextFormat &f)
1609 QTextObject *obj =
nullptr;
1610 if (f.isListFormat())
1611 obj =
new QTextList(
this);
1612 else if (f.isTableFormat())
1613 obj =
new QTextTable(
this);
1614 else if (f.isFrameFormat())
1615 obj =
new QTextFrame(
this);
1621
1622
1623
1624
1625QTextFrame *QTextDocument::frameAt(
int pos)
const
1627 Q_D(
const QTextDocument);
1628 return d->frameAt(pos);
1632
1633
1634QTextFrame *QTextDocument::rootFrame()
const
1636 Q_D(
const QTextDocument);
1637 return d->rootFrame();
1641
1642
1643QTextObject *QTextDocument::object(
int objectIndex)
const
1645 Q_D(
const QTextDocument);
1646 return d->objectForIndex(objectIndex);
1650
1651
1652QTextObject *QTextDocument::objectForFormat(
const QTextFormat &f)
const
1654 Q_D(
const QTextDocument);
1655 return d->objectForFormat(f);
1660
1661
1662QTextBlock QTextDocument::findBlock(
int pos)
const
1664 Q_D(
const QTextDocument);
1665 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), d->blockMap().findNode(pos));
1669
1670
1671
1672
1673
1674QTextBlock QTextDocument::findBlockByNumber(
int blockNumber)
const
1676 Q_D(
const QTextDocument);
1677 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), d->blockMap().findNode(blockNumber, 1));
1681
1682
1683
1684
1685
1686QTextBlock QTextDocument::findBlockByLineNumber(
int lineNumber)
const
1688 Q_D(
const QTextDocument);
1689 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), d->blockMap().findNode(lineNumber, 2));
1693
1694
1695
1696
1697QTextBlock QTextDocument::begin()
const
1699 Q_D(
const QTextDocument);
1700 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), d->blockMap().begin().n);
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715QTextBlock QTextDocument::end()
const
1717 Q_D(
const QTextDocument);
1718 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), 0);
1722
1723
1724
1725QTextBlock QTextDocument::firstBlock()
const
1727 Q_D(
const QTextDocument);
1728 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), d->blockMap().begin().n);
1732
1733
1734
1735QTextBlock QTextDocument::lastBlock()
const
1737 Q_D(
const QTextDocument);
1738 return QTextBlock(
const_cast<QTextDocumentPrivate *>(d), d->blockMap().last().n);
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1755void QTextDocument::setPageSize(
const QSizeF &size)
1760 d->lout->documentChanged(0, 0, d->length());
1763QSizeF QTextDocument::pageSize()
const
1765 Q_D(
const QTextDocument);
1770
1771
1772int QTextDocument::pageCount()
const
1774 return documentLayout()->pageCount();
1778
1779
1780void QTextDocument::setDefaultFont(
const QFont &font)
1783 d->setDefaultFont(font);
1785 d->lout->documentChanged(0, 0, d->length());
1789
1790
1791QFont QTextDocument::defaultFont()
const
1793 Q_D(
const QTextDocument);
1794 return d->defaultFont();
1798
1799
1800
1801
1802
1803
1804
1805
1806void QTextDocument::setSuperScriptBaseline(qreal baseline)
1809 d->formats.setSuperScriptBaseline(baseline);
1813
1814
1815
1816
1817
1818
1819
1820qreal QTextDocument::superScriptBaseline()
const
1822 Q_D(
const QTextDocument);
1823 return d->formats.defaultTextFormat().superScriptBaseline();
1827
1828
1829
1830
1831
1832
1833
1834
1835void QTextDocument::setSubScriptBaseline(qreal baseline)
1838 d->formats.setSubScriptBaseline(baseline);
1842
1843
1844
1845
1846
1847
1848
1849qreal QTextDocument::subScriptBaseline()
const
1851 Q_D(
const QTextDocument);
1852 return d->formats.defaultTextFormat().subScriptBaseline();
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865void QTextDocument::setBaselineOffset(qreal baseline)
1868 d->formats.setBaselineOffset(baseline);
1872
1873
1874
1875
1876
1877
1878
1879
1880qreal QTextDocument::baselineOffset()
const
1882 Q_D(
const QTextDocument);
1883 return d->formats.defaultTextFormat().baselineOffset();
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1901
1902
1903
1904
1905
1906
1907
1909bool QTextDocument::isModified()
const
1911 Q_D(
const QTextDocument);
1912 return d->isModified();
1915void QTextDocument::setModified(
bool m)
1921#ifndef QT_NO_PRINTER
1922static void printPage(
int index,
QPainter *painter,
const QTextDocument *doc,
const QRectF &body,
const QPointF &pageNumberPos)
1925 painter->translate(body.left(), body.top() - (index - 1) * body.height());
1926 QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
1928 QAbstractTextDocumentLayout *layout = doc->documentLayout();
1929 QAbstractTextDocumentLayout::PaintContext ctx;
1931 painter->setClipRect(view);
1937 ctx.palette.setColor(QPalette::Text, Qt::black);
1939 layout->draw(painter, ctx);
1941 if (!pageNumberPos.isNull()) {
1942 painter->setClipping(
false);
1943 painter->setFont(QFont(doc->defaultFont()));
1944 const QString pageString = QString::number(index);
1946 painter->drawText(qRound(pageNumberPos.x() - painter->fontMetrics().horizontalAdvance(pageString)),
1947 qRound(pageNumberPos.y() + view.top()),
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1972void QTextDocument::print(QPagedPaintDevice *printer)
const
1974 Q_D(
const QTextDocument);
1979 bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull()
1980 && d->pageSize.height() != qreal(INT_MAX);
1983 QMarginsF m = printer->pageLayout().margins(QPageLayout::Millimeter);
1984 if (!documentPaginated && m.left() == 0. && m.right() == 0. && m.top() == 0. && m.bottom() == 0.) {
1989 printer->setPageMargins(m, QPageLayout::Millimeter);
1993 QPainter p(printer);
1999 const QTextDocument *doc =
this;
2000 QScopedPointer<QTextDocument> clonedDoc;
2001 (
void)doc->documentLayout();
2003 QRectF body = QRectF(QPointF(0, 0), d->pageSize);
2004 QPointF pageNumberPos;
2006 qreal sourceDpiX = qt_defaultDpiX();
2007 qreal sourceDpiY = qt_defaultDpiY();
2008 const qreal dpiScaleX = qreal(printer->logicalDpiX()) / sourceDpiX;
2009 const qreal dpiScaleY = qreal(printer->logicalDpiY()) / sourceDpiY;
2011 if (documentPaginated) {
2013 QPaintDevice *dev = doc->documentLayout()->paintDevice();
2015 sourceDpiX = dev->logicalDpiX();
2016 sourceDpiY = dev->logicalDpiY();
2020 p.scale(dpiScaleX, dpiScaleY);
2022 QSizeF scaledPageSize = d->pageSize;
2023 scaledPageSize.rwidth() *= dpiScaleX;
2024 scaledPageSize.rheight() *= dpiScaleY;
2026 const QSizeF printerPageSize(printer->width(), printer->height());
2029 p.scale(printerPageSize.width() / scaledPageSize.width(),
2030 printerPageSize.height() / scaledPageSize.height());
2032 doc = clone(
const_cast<QTextDocument *>(
this));
2033 clonedDoc.reset(
const_cast<QTextDocument *>(doc));
2035 for (QTextBlock srcBlock = firstBlock(), dstBlock = clonedDoc->firstBlock();
2036 srcBlock.isValid() && dstBlock.isValid();
2037 srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
2038 dstBlock.layout()->setFormats(srcBlock.layout()->formats());
2041 QAbstractTextDocumentLayout *layout = doc->documentLayout();
2042 layout->setPaintDevice(p.device());
2045 layout->d_func()->handlers = documentLayout()->d_func()->handlers;
2048 const int horizontalMargin =
int((2/2.54)*sourceDpiX);
2049 const int verticalMargin =
int((2/2.54)*sourceDpiY);
2050 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2051 fmt.setLeftMargin(horizontalMargin);
2052 fmt.setRightMargin(horizontalMargin);
2053 fmt.setTopMargin(verticalMargin);
2054 fmt.setBottomMargin(verticalMargin);
2055 doc->rootFrame()->setFrameFormat(fmt);
2058 const int dpiy = p.device()->logicalDpiY();
2059 body = QRectF(0, 0, printer->width(), printer->height());
2060 pageNumberPos = QPointF(body.width() - horizontalMargin * dpiScaleX,
2061 body.height() - verticalMargin * dpiScaleY
2062 + QFontMetrics(doc->defaultFont(), p.device()).ascent()
2064 clonedDoc->setPageSize(body.size());
2067 const QPageRanges pageRanges = printer->pageRanges();
2068 int fromPage = pageRanges.firstPage();
2069 int toPage = pageRanges.lastPage();
2071 if (fromPage == 0 && toPage == 0) {
2073 toPage = doc->pageCount();
2076 fromPage = qMax(1, fromPage);
2077 toPage = qMin(doc->pageCount(), toPage);
2079 if (toPage < fromPage) {
2093 int page = fromPage;
2095 if (pageRanges.isEmpty() || pageRanges.contains(page))
2096 printPage(page, &p, doc, body, pageNumberPos);
2101 if (!printer->newPage())
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150QVariant QTextDocument::resource(
int type,
const QUrl &name)
const
2152 Q_D(
const QTextDocument);
2153 const QUrl url = d->baseUrl.resolved(name);
2154 QVariant r = d->resources.value(url);
2156 r = d->cachedResources.value(url);
2158 r =
const_cast<QTextDocument *>(
this)->loadResource(type, url);
2160 if (d->resourceProvider)
2161 r = d->resourceProvider(url);
2162 else if (
auto defaultProvider = defaultResourceProvider())
2163 r = defaultProvider(url);
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188void QTextDocument::addResource(
int type,
const QUrl &name,
const QVariant &resource)
2192 d->resources.insert(name, resource);
2196
2197
2198
2199
2200
2201
2202QTextDocument::ResourceProvider QTextDocument::resourceProvider()
const
2204 Q_D(
const QTextDocument);
2205 return d->resourceProvider;
2209
2210
2211
2212
2213
2216
2217
2218
2219
2220
2221
2222void QTextDocument::setResourceProvider(
const ResourceProvider &provider)
2225 d->resourceProvider = provider;
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238void QTextDocument::setDefaultResourceProvider(
const ResourceProvider &provider)
2240 qt_defaultResourceProvider = provider;
2244
2245
2246
2247
2248
2249
2250QTextDocument::ResourceProvider QTextDocument::defaultResourceProvider()
2252 return qt_defaultResourceProvider;
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274QVariant QTextDocument::loadResource(
int type,
const QUrl &name)
2279 QObject *p = parent();
2281 const QMetaObject *me = p->metaObject();
2282 int index = me->indexOfMethod(
"loadResource(int,QUrl)");
2284 QMetaMethod loader = me->method(index);
2286 loader.invoke(p, Qt::DirectConnection, Q_RETURN_ARG(QVariant, r), Q_ARG(
int, type), Q_ARG(QUrl, name));
2291 if (r.isNull() && name.scheme().compare(
"data"_L1, Qt::CaseInsensitive) == 0) {
2294 if (qDecodeDataUrl(name, mimetype, payload))
2299 if (!qobject_cast<QTextDocument *>(p) && r.isNull()) {
2300 QUrl resourceUrl = name;
2302 if (name.isRelative()) {
2303 const QUrl currentURL{d->url};
2306 if (!(currentURL.isRelative()
2307 || (currentURL.scheme() ==
"file"_L1
2308 && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
2309 || (name.hasFragment() && name.path().isEmpty())) {
2310 resourceUrl = currentURL.resolved(name);
2315 QFileInfo fi(currentURL.toLocalFile());
2318 QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name);
2319 }
else if (currentURL.isEmpty()) {
2320 resourceUrl.setScheme(
"file"_L1);
2325 QString s = resourceUrl.toLocalFile();
2327 if (!s.isEmpty() && f.open(QFile::ReadOnly)) {
2334 if (type == ImageResource && r.userType() == QMetaType::QByteArray) {
2335 if (!QThread::isMainThread()) {
2338 image.loadFromData(r.toByteArray());
2339 if (!image.isNull())
2343 pm.loadFromData(r.toByteArray());
2348 d->cachedResources.insert(name, r);
2355 QTextFormat diff = to;
2357 const QMap<
int, QVariant> props = to.properties();
2358 for (QMap<
int, QVariant>::ConstIterator it = props.begin(), end = props.end();
2360 if (it.value() == from.property(it.key()))
2361 diff.clearProperty(it.key());
2370 if (color.alpha() == 255) {
2371 result = color.name();
2372 }
else if (color.alpha()) {
2373 QString alphaValue = QString::number(color.alphaF(),
'f', 6);
2374 while (alphaValue.size() > 1 && alphaValue.at(alphaValue.size() - 1) == u'0')
2376 if (alphaValue.at(alphaValue.size() - 1) == u'.')
2378 result = QString::fromLatin1(
"rgba(%1,%2,%3,%4)").arg(color.red())
2383 result =
"transparent"_L1;
2390
2391
2392
2393
2395 : doc(_doc), fragmentMarkers(
false)
2397 const QFont defaultFont = doc->defaultFont();
2398 defaultCharFormat.setFont(defaultFont);
2403 return format.fontFamilies().toStringList();
2407
2408
2409
2410
2413 html =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
2414 "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
2415 "<html><head><meta name=\"qrichtext\" content=\"1\" />"_L1;
2416 html.reserve(QTextDocumentPrivate::get(doc)->length());
2420 html +=
"<meta charset=\"utf-8\" />"_L1;
2422 QString title = doc->metaInformation(QTextDocument::DocumentTitle);
2423 if (!title.isEmpty()) {
2424 html +=
"<title>"_L1;
2426 html +=
"</title>"_L1;
2428 html +=
"<style type=\"text/css\">\n"_L1;
2429 html +=
"p, li { white-space: pre-wrap; }\n"_L1;
2430 html +=
"hr { height: 1px; border-width: 0; }\n"_L1;
2431 html +=
"li.unchecked::marker { content: \"\\2610\"; }\n"_L1;
2432 html +=
"li.checked::marker { content: \"\\2612\"; }\n"_L1;
2433 html +=
"</style>"_L1;
2434 html +=
"</head><body"_L1;
2437 html +=
" style=\""_L1;
2439 emitFontFamily(resolvedFontFamilies(defaultCharFormat));
2441 if (defaultCharFormat.hasProperty(QTextFormat::FontPointSize)) {
2442 html +=
" font-size:"_L1;
2443 html += QString::number(defaultCharFormat.fontPointSize());
2445 }
else if (defaultCharFormat.hasProperty(QTextFormat::FontPixelSize)) {
2446 html +=
" font-size:"_L1;
2447 html += QString::number(defaultCharFormat.intProperty(QTextFormat::FontPixelSize));
2451 html +=
" font-weight:"_L1;
2452 html += QString::number(defaultCharFormat.fontWeight());
2455 html +=
" font-style:"_L1;
2456 html += (defaultCharFormat.fontItalic() ?
"italic"_L1 :
"normal"_L1);
2459 const bool percentSpacing = (defaultCharFormat.fontLetterSpacingType() == QFont::PercentageSpacing);
2460 if (defaultCharFormat.hasProperty(QTextFormat::FontLetterSpacing) &&
2461 (!percentSpacing || defaultCharFormat.fontLetterSpacing() != 0.0)) {
2462 html +=
" letter-spacing:"_L1;
2463 qreal value = defaultCharFormat.fontLetterSpacing();
2465 value = (value / 100) - 1;
2466 html += QString::number(value);
2467 html += percentSpacing ?
"em;"_L1 :
"px;"_L1;
2470 if (defaultCharFormat.hasProperty(QTextFormat::FontWordSpacing) &&
2471 defaultCharFormat.fontWordSpacing() != 0.0) {
2472 html +=
" word-spacing:"_L1;
2473 html += QString::number(defaultCharFormat.fontWordSpacing());
2477 QString decorationTag(
" text-decoration:"_L1);
2478 bool atLeastOneDecorationSet =
false;
2479 if (defaultCharFormat.hasProperty(QTextFormat::FontUnderline) || defaultCharFormat.hasProperty(QTextFormat::TextUnderlineStyle)) {
2480 if (defaultCharFormat.fontUnderline()) {
2481 decorationTag +=
" underline"_L1;
2482 atLeastOneDecorationSet =
true;
2485 if (defaultCharFormat.hasProperty(QTextFormat::FontOverline)) {
2486 if (defaultCharFormat.fontOverline()) {
2487 decorationTag +=
" overline"_L1;
2488 atLeastOneDecorationSet =
true;
2491 if (defaultCharFormat.hasProperty(QTextFormat::FontStrikeOut)) {
2492 if (defaultCharFormat.fontStrikeOut()) {
2493 decorationTag +=
" line-through"_L1;
2494 atLeastOneDecorationSet =
true;
2497 if (atLeastOneDecorationSet)
2498 html += decorationTag + u';';
2502 const QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2503 emitBackgroundAttribute(fmt);
2506 defaultCharFormat = QTextCharFormat();
2510 QTextFrameFormat rootFmt = doc->rootFrame()->frameFormat();
2511 rootFmt.clearProperty(QTextFormat::BackgroundBrush);
2513 QTextFrameFormat defaultFmt;
2514 defaultFmt.setMargin(doc->documentMargin());
2516 if (rootFmt == defaultFmt)
2517 emitFrame(doc->rootFrame()->begin());
2519 emitTextFrame(doc->rootFrame());
2521 html +=
"</body></html>"_L1;
2525void QTextHtmlExporter::emitAttribute(
const char *attribute,
const QString &value)
2528 html += QLatin1StringView(attribute);
2530 html += value.toHtmlEscaped();
2536 bool attributesEmitted =
false;
2539 const QStringList families = resolvedFontFamilies(format);
2540 if (!families.isEmpty() && families != resolvedFontFamilies(defaultCharFormat)) {
2541 emitFontFamily(families);
2542 attributesEmitted =
true;
2546 if (format.hasProperty(QTextFormat::FontPointSize)
2547 && format.fontPointSize() != defaultCharFormat.fontPointSize()) {
2548 html +=
" font-size:"_L1;
2549 html += QString::number(format.fontPointSize());
2551 attributesEmitted =
true;
2552 }
else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) {
2553 static const char sizeNameData[] =
2557 static const quint8 sizeNameOffsets[] = {
2560 sizeof(
"small") +
sizeof(
"medium") + 3,
2561 sizeof(
"small") +
sizeof(
"medium") + 1,
2562 sizeof(
"small") +
sizeof(
"medium"),
2564 const char *name =
nullptr;
2565 const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1;
2566 if (idx >= 0 && idx <= 4) {
2567 name = sizeNameData + sizeNameOffsets[idx];
2570 html +=
" font-size:"_L1;
2571 html += QLatin1StringView(name);
2573 attributesEmitted =
true;
2575 }
else if (format.hasProperty(QTextFormat::FontPixelSize)
2576 && format.property(QTextFormat::FontPixelSize)
2577 != defaultCharFormat.property(QTextFormat::FontPixelSize)) {
2578 html +=
" font-size:"_L1;
2579 html += QString::number(format.intProperty(QTextFormat::FontPixelSize));
2581 attributesEmitted =
true;
2584 if (format.hasProperty(QTextFormat::FontWeight)
2585 && format.fontWeight() != defaultCharFormat.fontWeight()) {
2586 html +=
" font-weight:"_L1;
2587 html += QString::number(format.fontWeight());
2589 attributesEmitted =
true;
2592 if (format.hasProperty(QTextFormat::FontItalic)
2593 && format.fontItalic() != defaultCharFormat.fontItalic()) {
2594 html +=
" font-style:"_L1;
2595 html += (format.fontItalic() ?
"italic"_L1 :
"normal"_L1);
2597 attributesEmitted =
true;
2600 const auto decorationTag =
" text-decoration:"_L1;
2601 html += decorationTag;
2602 bool hasDecoration =
false;
2603 bool atLeastOneDecorationSet =
false;
2605 if ((format.hasProperty(QTextFormat::FontUnderline) || format.hasProperty(QTextFormat::TextUnderlineStyle))
2606 && format.fontUnderline() != defaultCharFormat.fontUnderline()) {
2607 hasDecoration =
true;
2608 if (format.fontUnderline()) {
2609 html +=
" underline"_L1;
2610 atLeastOneDecorationSet =
true;
2614 if (format.hasProperty(QTextFormat::FontOverline)
2615 && format.fontOverline() != defaultCharFormat.fontOverline()) {
2616 hasDecoration =
true;
2617 if (format.fontOverline()) {
2618 html +=
" overline"_L1;
2619 atLeastOneDecorationSet =
true;
2623 if (format.hasProperty(QTextFormat::FontStrikeOut)
2624 && format.fontStrikeOut() != defaultCharFormat.fontStrikeOut()) {
2625 hasDecoration =
true;
2626 if (format.fontStrikeOut()) {
2627 html +=
" line-through"_L1;
2628 atLeastOneDecorationSet =
true;
2632 if (hasDecoration) {
2633 if (!atLeastOneDecorationSet)
2636 if (format.hasProperty(QTextFormat::TextUnderlineColor)) {
2637 html +=
" text-decoration-color:"_L1;
2638 html += colorValue(format.underlineColor());
2641 attributesEmitted =
true;
2643 html.chop(decorationTag.size());
2646 if (format.foreground() != defaultCharFormat.foreground()
2647 && format.foreground().style() != Qt::NoBrush) {
2648 QBrush brush = format.foreground();
2649 if (brush.style() == Qt::TexturePattern) {
2650 const bool isPixmap = qHasPixmapTexture(brush);
2651 const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
2653 html +=
" -qt-fg-texture-cachekey:"_L1;
2654 html += QString::number(cacheKey);
2656 }
else if (brush.style() == Qt::LinearGradientPattern
2657 || brush.style() == Qt::RadialGradientPattern
2658 || brush.style() == Qt::ConicalGradientPattern) {
2659 const QGradient *gradient = brush.gradient();
2660 if (gradient->type() == QGradient::LinearGradient) {
2661 const QLinearGradient *linearGradient =
static_cast<
const QLinearGradient *>(brush.gradient());
2663 html +=
" -qt-foreground: qlineargradient("_L1;
2664 html +=
"x1:"_L1 + QString::number(linearGradient->start().x()) + u',';
2665 html +=
"y1:"_L1 + QString::number(linearGradient->start().y()) + u',';
2666 html +=
"x2:"_L1 + QString::number(linearGradient->finalStop().x()) + u',';
2667 html +=
"y2:"_L1 + QString::number(linearGradient->finalStop().y()) + u',';
2668 }
else if (gradient->type() == QGradient::RadialGradient) {
2669 const QRadialGradient *radialGradient =
static_cast<
const QRadialGradient *>(brush.gradient());
2671 html +=
" -qt-foreground: qradialgradient("_L1;
2672 html +=
"cx:"_L1 + QString::number(radialGradient->center().x()) + u',';
2673 html +=
"cy:"_L1 + QString::number(radialGradient->center().y()) + u',';
2674 html +=
"fx:"_L1 + QString::number(radialGradient->focalPoint().x()) + u',';
2675 html +=
"fy:"_L1 + QString::number(radialGradient->focalPoint().y()) + u',';
2676 html +=
"radius:"_L1 + QString::number(radialGradient->radius()) + u',';
2678 const QConicalGradient *conicalGradient =
static_cast<
const QConicalGradient *>(brush.gradient());
2680 html +=
" -qt-foreground: qconicalgradient("_L1;
2681 html +=
"cx:"_L1 + QString::number(conicalGradient->center().x()) + u',';
2682 html +=
"cy:"_L1 + QString::number(conicalGradient->center().y()) + u',';
2683 html +=
"angle:"_L1 + QString::number(conicalGradient->angle()) + u',';
2686 const QStringList coordinateModes = {
"logical"_L1,
"stretchtodevice"_L1,
"objectbounding"_L1,
"object"_L1 };
2687 html +=
"coordinatemode:"_L1;
2688 html += coordinateModes.at(
int(gradient->coordinateMode()));
2691 const QStringList spreads = {
"pad"_L1,
"reflect"_L1,
"repeat"_L1 };
2692 html +=
"spread:"_L1;
2693 html += spreads.at(
int(gradient->spread()));
2695 for (
const QGradientStop &stop : gradient->stops()) {
2696 html +=
",stop:"_L1;
2697 html += QString::number(stop.first);
2699 html += colorValue(stop.second);
2704 html +=
" color:"_L1;
2705 html += colorValue(brush.color());
2708 attributesEmitted =
true;
2711 if (format.background() != defaultCharFormat.background()
2712 && format.background().style() == Qt::SolidPattern) {
2713 html +=
" background-color:"_L1;
2714 html += colorValue(format.background().color());
2716 attributesEmitted =
true;
2719 if (format.verticalAlignment() != defaultCharFormat.verticalAlignment()
2720 && format.verticalAlignment() != QTextCharFormat::AlignNormal)
2722 html +=
" vertical-align:"_L1;
2724 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2725 if (valign == QTextCharFormat::AlignSubScript)
2727 else if (valign == QTextCharFormat::AlignSuperScript)
2729 else if (valign == QTextCharFormat::AlignMiddle)
2730 html +=
"middle"_L1;
2731 else if (valign == QTextCharFormat::AlignTop)
2733 else if (valign == QTextCharFormat::AlignBottom)
2734 html +=
"bottom"_L1;
2737 attributesEmitted =
true;
2740 if (format.fontCapitalization() != QFont::MixedCase) {
2741 const QFont::Capitalization caps = format.fontCapitalization();
2742 if (caps == QFont::AllUppercase)
2743 html +=
" text-transform:uppercase;"_L1;
2744 else if (caps == QFont::AllLowercase)
2745 html +=
" text-transform:lowercase;"_L1;
2746 else if (caps == QFont::SmallCaps)
2747 html +=
" font-variant:small-caps;"_L1;
2748 attributesEmitted =
true;
2751 if (format.fontWordSpacing() != 0.0) {
2752 html +=
" word-spacing:"_L1;
2753 html += QString::number(format.fontWordSpacing());
2755 attributesEmitted =
true;
2758 if (format.hasProperty(QTextFormat::TextOutline)) {
2759 QPen outlinePen = format.textOutline();
2760 html +=
" -qt-stroke-color:"_L1;
2761 html += colorValue(outlinePen.color());
2764 html +=
" -qt-stroke-width:"_L1;
2765 html += QString::number(outlinePen.widthF());
2768 html +=
" -qt-stroke-linecap:"_L1;
2769 if (outlinePen.capStyle() == Qt::SquareCap)
2770 html +=
"squarecap;"_L1;
2771 else if (outlinePen.capStyle() == Qt::FlatCap)
2772 html +=
"flatcap;"_L1;
2773 else if (outlinePen.capStyle() == Qt::RoundCap)
2774 html +=
"roundcap;"_L1;
2776 html +=
" -qt-stroke-linejoin:"_L1;
2777 if (outlinePen.joinStyle() == Qt::MiterJoin)
2778 html +=
"miterjoin;"_L1;
2779 else if (outlinePen.joinStyle() == Qt::SvgMiterJoin)
2780 html +=
"svgmiterjoin;"_L1;
2781 else if (outlinePen.joinStyle() == Qt::BevelJoin)
2782 html +=
"beveljoin;"_L1;
2783 else if (outlinePen.joinStyle() == Qt::RoundJoin)
2784 html +=
"roundjoin;"_L1;
2786 if (outlinePen.joinStyle() == Qt::MiterJoin ||
2787 outlinePen.joinStyle() == Qt::SvgMiterJoin) {
2788 html +=
" -qt-stroke-miterlimit:"_L1;
2789 html += QString::number(outlinePen.miterLimit());
2793 if (outlinePen.style() == Qt::CustomDashLine && !outlinePen.dashPattern().empty()) {
2794 html +=
" -qt-stroke-dasharray:"_L1;
2796 QList<qreal> dashes = outlinePen.dashPattern();
2798 for (
int i = 0; i < dashes.length() - 1; i++) {
2799 qreal dash = dashes[i];
2800 dashArrayString += QString::number(dash) + u',';
2803 dashArrayString += QString::number(dashes.last());
2804 html += dashArrayString;
2807 html +=
" -qt-stroke-dashoffset:"_L1;
2808 html += QString::number(outlinePen.dashOffset());
2812 attributesEmitted =
true;
2815 return attributesEmitted;
2818void QTextHtmlExporter::emitTextLength(
const char *attribute,
const QTextLength &length)
2820 if (length.type() == QTextLength::VariableLength)
2824 html += QLatin1StringView(attribute);
2826 html += QString::number(length.rawValue());
2828 if (length.type() == QTextLength::PercentageLength)
2836 if (align & Qt::AlignLeft)
2838 else if (align & Qt::AlignRight)
2839 html +=
" align=\"right\""_L1;
2840 else if (align & Qt::AlignHCenter)
2841 html +=
" align=\"center\""_L1;
2842 else if (align & Qt::AlignJustify)
2843 html +=
" align=\"justify\""_L1;
2846void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode)
2848 if (pos == QTextFrameFormat::InFlow)
2851 if (mode == EmitStyleTag)
2852 html +=
" style=\"float:"_L1;
2854 html +=
" float:"_L1;
2856 if (pos == QTextFrameFormat::FloatLeft)
2857 html +=
" left;"_L1;
2858 else if (pos == QTextFrameFormat::FloatRight)
2859 html +=
" right;"_L1;
2861 Q_ASSERT_X(0,
"QTextHtmlExporter::emitFloatStyle()",
"pos should be a valid enum type");
2863 if (mode == EmitStyleTag)
2870 case QTextFrameFormat::BorderStyle_None:
2872 case QTextFrameFormat::BorderStyle_Dotted:
2874 case QTextFrameFormat::BorderStyle_Dashed:
2876 case QTextFrameFormat::BorderStyle_Solid:
2878 case QTextFrameFormat::BorderStyle_Double:
2880 case QTextFrameFormat::BorderStyle_DotDash:
2881 return "dot-dash"_L1;
2882 case QTextFrameFormat::BorderStyle_DotDotDash:
2883 return "dot-dot-dash"_L1;
2884 case QTextFrameFormat::BorderStyle_Groove:
2886 case QTextFrameFormat::BorderStyle_Ridge:
2888 case QTextFrameFormat::BorderStyle_Inset:
2890 case QTextFrameFormat::BorderStyle_Outset:
2900 Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset);
2902 html +=
" border-style:"_L1;
2903 html += richtextBorderStyleToHtmlBorderStyle(style);
2909 if (policy & QTextFormat::PageBreak_AlwaysBefore)
2910 html +=
" page-break-before:always;"_L1;
2912 if (policy & QTextFormat::PageBreak_AlwaysAfter)
2913 html +=
" page-break-after:always;"_L1;
2918 html +=
" font-family:"_L1;
2921 for (
const QString &family : families) {
2922 auto quote =
"\'"_L1;
2923 if (family.contains(u'\''))
2924 quote =
"""_L1;
2931 html += family.toHtmlEscaped();
2937void QTextHtmlExporter::emitMargins(
const QString &top,
const QString &bottom,
const QString &left,
const QString &right)
2939 html +=
" margin-top:"_L1;
2943 html +=
" margin-bottom:"_L1;
2947 html +=
" margin-left:"_L1;
2951 html +=
" margin-right:"_L1;
2958 const QTextCharFormat format = fragment.charFormat();
2960 bool closeAnchor =
false;
2962 if (format.isAnchor()) {
2963 const auto names = format.anchorNames();
2964 if (!names.isEmpty()) {
2965 html +=
"<a name=\""_L1;
2966 html += names.constFirst().toHtmlEscaped();
2967 html +=
"\"></a>"_L1;
2969 const QString href = format.anchorHref();
2970 const QString tooltip = format.toolTip();
2971 const bool hasHref = !href.isEmpty();
2972 const bool hasTooltip = !tooltip.isEmpty();
2973 if (hasHref || hasTooltip) {
2976 html +=
" href=\""_L1;
2977 html += href.toHtmlEscaped();
2981 html +=
" title=\""_L1;
2982 html += tooltip.toHtmlEscaped();
2990 QString txt = fragment.text();
2991 const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
2992 const bool isImage = isObject && format.isImageFormat();
2999 const QString spanToolTip = (!closeAnchor && !isImage) ? format.toolTip() : QString();
3001 const auto styleTag =
"<span style=\""_L1;
3004 bool attributesEmitted =
false;
3006 attributesEmitted = emitCharFormatStyle(format);
3008 bool spanEmitted = attributesEmitted;
3009 if (attributesEmitted)
3012 html.chop(styleTag.size());
3014 if (!spanToolTip.isEmpty()) {
3019 html +=
" title=\""_L1;
3020 html += spanToolTip.toHtmlEscaped();
3027 for (
int i = 0; isImage && i < txt.size(); ++i) {
3028 if (txt.at(i) != QChar::ObjectReplacementCharacter) {
3033 html += QString(txt.at(i)).toHtmlEscaped();
3036 QTextImageFormat imgFmt = format.toImageFormat();
3042 if (imgFmt.hasProperty(QTextFormat::ImageMaxWidth)) {
3043 auto length = imgFmt.lengthProperty(QTextFormat::ImageMaxWidth);
3044 maxWidthCss +=
"max-width:"_L1;
3045 if (length.type() == QTextLength::PercentageLength)
3046 maxWidthCss += QString::number(length.rawValue()) +
"%;"_L1;
3047 else if (length.type() == QTextLength::FixedLength)
3048 maxWidthCss += QString::number(length.rawValue()) +
"px;"_L1;
3051 if (imgFmt.hasProperty(QTextFormat::ImageName))
3052 emitAttribute(
"src", imgFmt.name());
3054 if (imgFmt.hasProperty(QTextFormat::ImageAltText))
3055 emitAttribute(
"alt", imgFmt.stringProperty(QTextFormat::ImageAltText));
3057 if (imgFmt.hasProperty(QTextFormat::ImageTitle))
3058 emitAttribute(
"title", imgFmt.stringProperty(QTextFormat::ImageTitle));
3060 if (imgFmt.hasProperty(QTextFormat::ImageWidth))
3061 emitAttribute(
"width", QString::number(imgFmt.width()));
3063 if (imgFmt.hasProperty(QTextFormat::ImageHeight))
3064 emitAttribute(
"height", QString::number(imgFmt.height()));
3066 if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
3067 html +=
" style=\"vertical-align: middle;"_L1 + maxWidthCss + u'\"';
3068 else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
3069 html +=
" style=\"vertical-align: top;"_L1 + maxWidthCss + u'\"';
3070 else if (!maxWidthCss.isEmpty())
3071 html +=
" style=\""_L1 + maxWidthCss + u'\"';
3073 if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
3074 emitFloatStyle(imageFrame->frameFormat().position());
3079 Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
3081 txt = txt.toHtmlEscaped();
3085 txt.replace(u'\n',
"<br />"_L1);
3086 txt.replace(QChar::LineSeparator,
"<br />"_L1);
3091 html +=
"</span>"_L1;
3099 return style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
3100 || style == QTextListFormat::ListUpperAlpha
3101 || style == QTextListFormat::ListUpperRoman
3102 || style == QTextListFormat::ListLowerRoman
3108 QTextBlockFormat format = block.blockFormat();
3109 emitAlignment(format.alignment());
3113 if (block.textDirection() == Qt::RightToLeft)
3114 html +=
" dir='rtl'"_L1;
3116 const auto style =
" style=\""_L1;
3119 const bool emptyBlock = block.begin().atEnd();
3121 html +=
"-qt-paragraph-type:empty;"_L1;
3124 emitMargins(QString::number(format.topMargin()),
3125 QString::number(format.bottomMargin()),
3126 QString::number(format.leftMargin()),
3127 QString::number(format.rightMargin()));
3129 html +=
" -qt-block-indent:"_L1;
3130 html += QString::number(format.indent());
3133 html +=
" text-indent:"_L1;
3134 html += QString::number(format.textIndent());
3137 if (block.userState() != -1) {
3138 html +=
" -qt-user-state:"_L1;
3139 html += QString::number(block.userState());
3143 if (format.lineHeightType() != QTextBlockFormat::SingleHeight) {
3144 html +=
" line-height:"_L1
3145 + QString::number(format.lineHeight());
3146 switch (format.lineHeightType()) {
3147 case QTextBlockFormat::ProportionalHeight:
3150 case QTextBlockFormat::FixedHeight:
3151 html +=
"; -qt-line-height-type: fixed;"_L1;
3153 case QTextBlockFormat::MinimumHeight:
3156 case QTextBlockFormat::LineDistanceHeight:
3157 html +=
"; -qt-line-height-type: line-distance;"_L1;
3165 emitPageBreakPolicy(format.pageBreakPolicy());
3167 QTextCharFormat diff;
3169 const QTextCharFormat blockCharFmt = block.charFormat();
3170 diff = formatDifference(defaultCharFormat, blockCharFmt).toCharFormat();
3173 diff.clearProperty(QTextFormat::BackgroundBrush);
3174 if (format.hasProperty(QTextFormat::BackgroundBrush)) {
3175 QBrush bg = format.background();
3176 if (bg.style() != Qt::NoBrush)
3177 diff.setProperty(QTextFormat::BackgroundBrush, format.property(QTextFormat::BackgroundBrush));
3180 if (!diff.properties().isEmpty())
3181 emitCharFormatStyle(diff);
3189 if (block.begin().atEnd()) {
3191 int p = block.position();
3195 QTextDocumentPrivate::FragmentIterator frag = QTextDocumentPrivate::get(doc)->find(p);
3196 QChar ch = QTextDocumentPrivate::get(doc)->buffer().at(frag->stringPosition);
3206 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
3208 QTextList *list = block.textList();
3210 if (list->itemNumber(block) == 0) {
3211 const QTextListFormat format = list->format();
3212 const int style = format.style();
3213 bool ordered =
false;
3215 case QTextListFormat::ListDisc: html +=
"<ul"_L1;
break;
3216 case QTextListFormat::ListCircle: html +=
"<ul type=\"circle\""_L1;
break;
3217 case QTextListFormat::ListSquare: html +=
"<ul type=\"square\""_L1;
break;
3218 case QTextListFormat::ListDecimal: html +=
"<ol"_L1; ordered =
true;
break;
3219 case QTextListFormat::ListLowerAlpha: html +=
"<ol type=\"a\""_L1; ordered =
true;
break;
3220 case QTextListFormat::ListUpperAlpha: html +=
"<ol type=\"A\""_L1; ordered =
true;
break;
3221 case QTextListFormat::ListLowerRoman: html +=
"<ol type=\"i\""_L1; ordered =
true;
break;
3222 case QTextListFormat::ListUpperRoman: html +=
"<ol type=\"I\""_L1; ordered =
true;
break;
3223 default: html +=
"<ul"_L1;
3226 if (ordered && format.start() != 1) {
3227 html +=
" start=\""_L1;
3228 html += QString::number(format.start());
3233 styleString +=
"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;"_L1;
3235 if (format.hasProperty(QTextFormat::ListIndent)) {
3236 styleString +=
" -qt-list-indent: "_L1;
3237 styleString += QString::number(format.indent());
3238 styleString += u';';
3241 if (format.hasProperty(QTextFormat::ListNumberPrefix)) {
3242 QString numberPrefix = format.numberPrefix();
3243 numberPrefix.replace(u'"',
"\\22"_L1);
3244 numberPrefix.replace(u'\'',
"\\27"_L1);
3245 styleString +=
" -qt-list-number-prefix: "_L1;
3246 styleString += u'\'';
3247 styleString += numberPrefix;
3248 styleString += u'\'';
3249 styleString += u';';
3252 if (format.hasProperty(QTextFormat::ListNumberSuffix)) {
3253 if (format.numberSuffix() !=
"."_L1) {
3254 QString numberSuffix = format.numberSuffix();
3255 numberSuffix.replace(u'"',
"\\22"_L1);
3256 numberSuffix.replace(u'\'',
"\\27"_L1);
3257 styleString +=
" -qt-list-number-suffix: "_L1;
3258 styleString += u'\'';
3259 styleString += numberSuffix;
3260 styleString += u'\'';
3261 styleString += u';';
3265 html +=
" style=\""_L1;
3266 html += styleString;
3272 const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
3273 if (!blockFmt.properties().isEmpty()) {
3274 html +=
" style=\""_L1;
3275 emitCharFormatStyle(blockFmt);
3278 defaultCharFormat.merge(block.charFormat());
3280 if (block.blockFormat().hasProperty(QTextFormat::BlockMarker)) {
3281 switch (block.blockFormat().marker()) {
3282 case QTextBlockFormat::MarkerType::Checked:
3283 html +=
" class=\"checked\""_L1;
3285 case QTextBlockFormat::MarkerType::Unchecked:
3286 html +=
" class=\"unchecked\""_L1;
3288 case QTextBlockFormat::MarkerType::NoMarker:
3294 const QTextBlockFormat blockFormat = block.blockFormat();
3295 if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
3298 QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
3299 if (width.type() != QTextLength::VariableLength)
3300 emitTextLength(
"width", width);
3303 if (blockFormat.hasProperty(QTextFormat::BackgroundBrush)) {
3304 html +=
"style=\""_L1;
3305 html +=
"background-color:"_L1;
3306 html += colorValue(qvariant_cast<QBrush>(blockFormat.property(QTextFormat::BackgroundBrush)).color());
3315 const bool pre = blockFormat.nonBreakableLines();
3321 int headingLevel = blockFormat.headingLevel();
3322 if (headingLevel > 0 && headingLevel <= 6)
3323 html +=
"<h"_L1 + QString::number(headingLevel);
3328 emitBlockAttributes(block);
3331 if (block.begin().atEnd())
3332 html +=
"<br />"_L1;
3334 QTextBlock::Iterator it = block.begin();
3335 if (fragmentMarkers && !it.atEnd() && block == doc->begin())
3336 html +=
"<!--StartFragment-->"_L1;
3338 for (; !it.atEnd(); ++it)
3339 emitFragment(it.fragment());
3341 if (fragmentMarkers && block.position() + block.length() == QTextDocumentPrivate::get(doc)->length())
3342 html +=
"<!--EndFragment-->"_L1;
3347 html +=
"</pre>"_L1;
3349 closeTags +=
"</li>"_L1;
3351 int headingLevel = blockFormat.headingLevel();
3352 if (headingLevel > 0 && headingLevel <= 6)
3353 html += QString::asprintf(
"</h%d>", headingLevel);
3359 if (list->itemNumber(block) == list->count() - 1) {
3360 if (isOrderedList(list->format().style()))
3361 closeTags +=
"</ol>"_L1;
3363 closeTags +=
"</ul>"_L1;
3365 const QTextBlock nextBlock = block.next();
3369 if (nextBlock.isValid() && nextBlock.textList() &&
3370 nextBlock.textList()->itemNumber(nextBlock) == 0 &&
3371 nextBlock.textList()->format().indent() > list->format().indent()) {
3373 if (!closingTags.isEmpty() && list->itemNumber(block) == list->count() - 1)
3374 lastTag = closingTags.takeLast();
3375 lastTag.prepend(closeTags);
3376 closingTags << lastTag;
3377 }
else if (list->itemNumber(block) == list->count() - 1) {
3381 if (!closingTags.isEmpty())
3382 html += closingTags.takeLast();
3388 defaultCharFormat = oldDefaultCharFormat;
3393QString
QTextHtmlExporter::findUrlForImage(
const QTextDocument *doc, qint64 cacheKey,
bool isPixmap)
3399 if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent()))
3400 return findUrlForImage(parent, cacheKey, isPixmap);
3402 const QTextDocumentPrivate *priv = QTextDocumentPrivate::get(doc);
3403 Q_ASSERT(priv !=
nullptr);
3405 QMap<QUrl, QVariant>::const_iterator it = priv->cachedResources.constBegin();
3406 for (; it != priv->cachedResources.constEnd(); ++it) {
3408 const QVariant &v = it.value();
3409 if (v.userType() == QMetaType::QImage && !isPixmap) {
3410 if (qvariant_cast<QImage>(v).cacheKey() == cacheKey)
3414 if (v.userType() == QMetaType::QPixmap && isPixmap) {
3415 if (qvariant_cast<QPixmap>(v).cacheKey() == cacheKey)
3420 if (it != priv->cachedResources.constEnd())
3421 url = it.key().toString();
3426void QTextDocumentPrivate::mergeCachedResources(
const QTextDocumentPrivate *priv)
3431 cachedResources.insert(priv->cachedResources);
3436 if (format.hasProperty(QTextFormat::BackgroundImageUrl)) {
3437 QString url = format.property(QTextFormat::BackgroundImageUrl).toString();
3438 emitAttribute(
"background", url);
3440 const QBrush &brush = format.background();
3441 if (brush.style() == Qt::SolidPattern) {
3442 emitAttribute(
"bgcolor", colorValue(brush.color()));
3443 }
else if (brush.style() == Qt::TexturePattern) {
3445 const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
3447 const QString url = findUrlForImage(doc, cacheKey, isPixmap);
3450 emitAttribute(
"background", url);
3457 QTextTableFormat format = table->format();
3459 html +=
"\n<table"_L1;
3461 if (format.hasProperty(QTextFormat::FrameBorder))
3462 emitAttribute(
"border", QString::number(format.border()));
3464 emitFrameStyle(format, TableFrame);
3466 emitAlignment(format.alignment());
3467 emitTextLength(
"width", format.width());
3469 if (format.hasProperty(QTextFormat::TableCellSpacing))
3470 emitAttribute(
"cellspacing", QString::number(format.cellSpacing()));
3471 if (format.hasProperty(QTextFormat::TableCellPadding))
3472 emitAttribute(
"cellpadding", QString::number(format.cellPadding()));
3474 emitBackgroundAttribute(format);
3478 const int rows = table->rows();
3479 const int columns = table->columns();
3481 QList<QTextLength> columnWidths = format.columnWidthConstraints();
3482 if (columnWidths.isEmpty()) {
3483 columnWidths.resize(columns);
3484 columnWidths.fill(QTextLength());
3486 Q_ASSERT(columnWidths.size() == columns);
3488 QVarLengthArray<
bool> widthEmittedForColumn(columns);
3489 for (
int i = 0; i < columns; ++i)
3490 widthEmittedForColumn[i] =
false;
3492 const int headerRowCount = qMin(format.headerRowCount(), rows);
3493 if (headerRowCount > 0)
3494 html +=
"<thead>"_L1;
3496 for (
int row = 0; row < rows; ++row) {
3497 html +=
"\n<tr>"_L1;
3499 for (
int col = 0; col < columns; ++col) {
3500 const QTextTableCell cell = table->cellAt(row, col);
3503 if (cell.row() != row)
3506 if (cell.column() != col)
3511 if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
3512 emitTextLength(
"width", columnWidths.at(col));
3513 widthEmittedForColumn[col] =
true;
3516 if (cell.columnSpan() > 1)
3517 emitAttribute(
"colspan", QString::number(cell.columnSpan()));
3519 if (cell.rowSpan() > 1)
3520 emitAttribute(
"rowspan", QString::number(cell.rowSpan()));
3522 const QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
3523 emitBackgroundAttribute(cellFormat);
3525 if (!cellFormat.toolTip().isEmpty())
3526 emitAttribute(
"title", cellFormat.toolTip());
3528 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
3530 QTextCharFormat::VerticalAlignment valign = cellFormat.verticalAlignment();
3533 if (valign >= QTextCharFormat::AlignMiddle && valign <= QTextCharFormat::AlignBottom) {
3534 styleString +=
" vertical-align:"_L1;
3536 case QTextCharFormat::AlignMiddle:
3537 styleString +=
"middle"_L1;
3539 case QTextCharFormat::AlignTop:
3540 styleString +=
"top"_L1;
3542 case QTextCharFormat::AlignBottom:
3543 styleString +=
"bottom"_L1;
3548 styleString += u';';
3550 QTextCharFormat temp;
3551 temp.setVerticalAlignment(valign);
3552 defaultCharFormat.merge(temp);
3555 if (cellFormat.hasProperty(QTextFormat::TableCellLeftPadding))
3556 styleString +=
" padding-left:"_L1 + QString::number(cellFormat.leftPadding()) + u';';
3557 if (cellFormat.hasProperty(QTextFormat::TableCellRightPadding))
3558 styleString +=
" padding-right:"_L1 + QString::number(cellFormat.rightPadding()) + u';';
3559 if (cellFormat.hasProperty(QTextFormat::TableCellTopPadding))
3560 styleString +=
" padding-top:"_L1 + QString::number(cellFormat.topPadding()) + u';';
3561 if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
3562 styleString +=
" padding-bottom:"_L1 + QString::number(cellFormat.bottomPadding()) + u';';
3564 if (cellFormat.hasProperty(QTextFormat::TableCellTopBorder))
3565 styleString +=
" border-top:"_L1 + QString::number(cellFormat.topBorder()) +
"px;"_L1;
3566 if (cellFormat.hasProperty(QTextFormat::TableCellRightBorder))
3567 styleString +=
" border-right:"_L1 + QString::number(cellFormat.rightBorder()) +
"px;"_L1;
3568 if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorder))
3569 styleString +=
" border-bottom:"_L1 + QString::number(cellFormat.bottomBorder()) +
"px;"_L1;
3570 if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorder))
3571 styleString +=
" border-left:"_L1 + QString::number(cellFormat.leftBorder()) +
"px;"_L1;
3573 if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderBrush))
3574 styleString +=
" border-top-color:"_L1 + cellFormat.topBorderBrush().color().name() + u';';
3575 if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderBrush))
3576 styleString +=
" border-right-color:"_L1 + cellFormat.rightBorderBrush().color().name() + u';';
3577 if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderBrush))
3578 styleString +=
" border-bottom-color:"_L1 + cellFormat.bottomBorderBrush().color().name() + u';';
3579 if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderBrush))
3580 styleString +=
" border-left-color:"_L1 + cellFormat.leftBorderBrush().color().name() + u';';
3582 if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderStyle))
3583 styleString +=
" border-top-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.topBorderStyle()) + u';';
3584 if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderStyle))
3585 styleString +=
" border-right-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.rightBorderStyle()) + u';';
3586 if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderStyle))
3587 styleString +=
" border-bottom-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.bottomBorderStyle()) + u';';
3588 if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderStyle))
3589 styleString +=
" border-left-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.leftBorderStyle()) + u';';
3591 if (!styleString.isEmpty())
3592 html +=
" style=\""_L1 + styleString + u'\"';
3596 emitFrame(cell.begin());
3600 defaultCharFormat = oldDefaultCharFormat;
3604 if (headerRowCount > 0 && row == headerRowCount - 1)
3605 html +=
"</thead>"_L1;
3608 html +=
"</table>"_L1;
3613 if (!frameIt.atEnd()) {
3614 QTextFrame::Iterator next = frameIt;
3617 && frameIt.currentFrame() ==
nullptr
3618 && frameIt.parentFrame() != doc->rootFrame()
3619 && frameIt.currentBlock().begin().atEnd())
3623 for (QTextFrame::Iterator it = frameIt;
3624 !it.atEnd(); ++it) {
3625 if (QTextFrame *f = it.currentFrame()) {
3626 if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
3631 }
else if (it.currentBlock().isValid()) {
3632 emitBlock(it.currentBlock());
3639 FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
3641 html +=
"\n<table"_L1;
3642 QTextFrameFormat format = f->frameFormat();
3644 if (format.hasProperty(QTextFormat::FrameBorder))
3645 emitAttribute(
"border", QString::number(format.border()));
3647 emitFrameStyle(format, frameType);
3649 emitTextLength(
"width", format.width());
3650 emitTextLength(
"height", format.height());
3653 if (frameType != RootFrame)
3654 emitBackgroundAttribute(format);
3657 html +=
"\n<tr>\n<td style=\"border: none;\">"_L1;
3658 emitFrame(f->begin());
3659 html +=
"</td></tr></table>"_L1;
3662void QTextHtmlExporter::emitFrameStyle(
const QTextFrameFormat &format, FrameType frameType)
3664 const auto styleAttribute =
" style=\""_L1;
3665 html += styleAttribute;
3666 const qsizetype originalHtmlLength = html.size();
3668 if (frameType == TextFrame)
3669 html +=
"-qt-table-type: frame;"_L1;
3670 else if (frameType == RootFrame)
3671 html +=
"-qt-table-type: root;"_L1;
3673 const QTextFrameFormat defaultFormat;
3675 emitFloatStyle(format.position(), OmitStyleTag);
3676 emitPageBreakPolicy(format.pageBreakPolicy());
3678 if (format.borderBrush() != defaultFormat.borderBrush()) {
3679 html +=
" border-color:"_L1;
3680 html += colorValue(format.borderBrush().color());
3684 if (format.borderStyle() != defaultFormat.borderStyle())
3685 emitBorderStyle(format.borderStyle());
3687 if (format.hasProperty(QTextFormat::FrameMargin)
3688 || format.hasProperty(QTextFormat::FrameLeftMargin)
3689 || format.hasProperty(QTextFormat::FrameRightMargin)
3690 || format.hasProperty(QTextFormat::FrameTopMargin)
3691 || format.hasProperty(QTextFormat::FrameBottomMargin))
3692 emitMargins(QString::number(format.topMargin()),
3693 QString::number(format.bottomMargin()),
3694 QString::number(format.leftMargin()),
3695 QString::number(format.rightMargin()));
3697 if (format.property(QTextFormat::TableBorderCollapse).toBool())
3698 html +=
" border-collapse:collapse;"_L1;
3700 if (html.size() == originalHtmlLength)
3701 html.chop(styleAttribute.size());
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716#ifndef QT_NO_TEXTHTMLPARSER
3717QString QTextDocument::toHtml()
const
3719 return QTextHtmlExporter(
this).toHtml();
3724
3725
3726
3727
3728
3729
3730#if QT_CONFIG(textmarkdownwriter)
3731QString QTextDocument::toMarkdown(QTextDocument::MarkdownFeatures features)
const
3734 QTextStream s(&ret);
3735 QTextMarkdownWriter w(s, features);
3736 if (w.writeAll(
this))
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802#if QT_CONFIG(textmarkdownreader)
3803void QTextDocument::setMarkdown(
const QString &markdown, QTextDocument::MarkdownFeatures features)
3805 QTextMarkdownImporter(
this, features).import(markdown);
3810
3811
3812QList<QTextFormat> QTextDocument::allFormats()
const
3814 Q_D(
const QTextDocument);
3815 return d->formatCollection()->formats;
3819
3820
3821
3822
3823
3827#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