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 if (!href.isEmpty()) {
2971 html +=
"<a href=\""_L1;
2972 html += href.toHtmlEscaped();
2978 QString txt = fragment.text();
2979 const bool isObject = txt.contains(QChar::ObjectReplacementCharacter);
2980 const bool isImage = isObject && format.isImageFormat();
2982 const auto styleTag =
"<span style=\""_L1;
2985 bool attributesEmitted =
false;
2987 attributesEmitted = emitCharFormatStyle(format);
2988 if (attributesEmitted)
2991 html.chop(styleTag.size());
2994 for (
int i = 0; isImage && i < txt.size(); ++i) {
2995 QTextImageFormat imgFmt = format.toImageFormat();
3001 if (imgFmt.hasProperty(QTextFormat::ImageMaxWidth)) {
3002 auto length = imgFmt.lengthProperty(QTextFormat::ImageMaxWidth);
3003 maxWidthCss +=
"max-width:"_L1;
3004 if (length.type() == QTextLength::PercentageLength)
3005 maxWidthCss += QString::number(length.rawValue()) +
"%;"_L1;
3006 else if (length.type() == QTextLength::FixedLength)
3007 maxWidthCss += QString::number(length.rawValue()) +
"px;"_L1;
3010 if (imgFmt.hasProperty(QTextFormat::ImageName))
3011 emitAttribute(
"src", imgFmt.name());
3013 if (imgFmt.hasProperty(QTextFormat::ImageAltText))
3014 emitAttribute(
"alt", imgFmt.stringProperty(QTextFormat::ImageAltText));
3016 if (imgFmt.hasProperty(QTextFormat::ImageTitle))
3017 emitAttribute(
"title", imgFmt.stringProperty(QTextFormat::ImageTitle));
3019 if (imgFmt.hasProperty(QTextFormat::ImageWidth))
3020 emitAttribute(
"width", QString::number(imgFmt.width()));
3022 if (imgFmt.hasProperty(QTextFormat::ImageHeight))
3023 emitAttribute(
"height", QString::number(imgFmt.height()));
3025 if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
3026 html +=
" style=\"vertical-align: middle;"_L1 + maxWidthCss + u'\"';
3027 else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
3028 html +=
" style=\"vertical-align: top;"_L1 + maxWidthCss + u'\"';
3029 else if (!maxWidthCss.isEmpty())
3030 html +=
" style=\""_L1 + maxWidthCss + u'\"';
3032 if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
3033 emitFloatStyle(imageFrame->frameFormat().position());
3038 Q_ASSERT(!txt.contains(QChar::ObjectReplacementCharacter));
3040 txt = txt.toHtmlEscaped();
3044 txt.replace(u'\n',
"<br />"_L1);
3045 txt.replace(QChar::LineSeparator,
"<br />"_L1);
3049 if (attributesEmitted)
3050 html +=
"</span>"_L1;
3058 return style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha
3059 || style == QTextListFormat::ListUpperAlpha
3060 || style == QTextListFormat::ListUpperRoman
3061 || style == QTextListFormat::ListLowerRoman
3067 QTextBlockFormat format = block.blockFormat();
3068 emitAlignment(format.alignment());
3072 if (block.textDirection() == Qt::RightToLeft)
3073 html +=
" dir='rtl'"_L1;
3075 const auto style =
" style=\""_L1;
3078 const bool emptyBlock = block.begin().atEnd();
3080 html +=
"-qt-paragraph-type:empty;"_L1;
3083 emitMargins(QString::number(format.topMargin()),
3084 QString::number(format.bottomMargin()),
3085 QString::number(format.leftMargin()),
3086 QString::number(format.rightMargin()));
3088 html +=
" -qt-block-indent:"_L1;
3089 html += QString::number(format.indent());
3092 html +=
" text-indent:"_L1;
3093 html += QString::number(format.textIndent());
3096 if (block.userState() != -1) {
3097 html +=
" -qt-user-state:"_L1;
3098 html += QString::number(block.userState());
3102 if (format.lineHeightType() != QTextBlockFormat::SingleHeight) {
3103 html +=
" line-height:"_L1
3104 + QString::number(format.lineHeight());
3105 switch (format.lineHeightType()) {
3106 case QTextBlockFormat::ProportionalHeight:
3109 case QTextBlockFormat::FixedHeight:
3110 html +=
"; -qt-line-height-type: fixed;"_L1;
3112 case QTextBlockFormat::MinimumHeight:
3115 case QTextBlockFormat::LineDistanceHeight:
3116 html +=
"; -qt-line-height-type: line-distance;"_L1;
3124 emitPageBreakPolicy(format.pageBreakPolicy());
3126 QTextCharFormat diff;
3128 const QTextCharFormat blockCharFmt = block.charFormat();
3129 diff = formatDifference(defaultCharFormat, blockCharFmt).toCharFormat();
3132 diff.clearProperty(QTextFormat::BackgroundBrush);
3133 if (format.hasProperty(QTextFormat::BackgroundBrush)) {
3134 QBrush bg = format.background();
3135 if (bg.style() != Qt::NoBrush)
3136 diff.setProperty(QTextFormat::BackgroundBrush, format.property(QTextFormat::BackgroundBrush));
3139 if (!diff.properties().isEmpty())
3140 emitCharFormatStyle(diff);
3148 if (block.begin().atEnd()) {
3150 int p = block.position();
3154 QTextDocumentPrivate::FragmentIterator frag = QTextDocumentPrivate::get(doc)->find(p);
3155 QChar ch = QTextDocumentPrivate::get(doc)->buffer().at(frag->stringPosition);
3165 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
3167 QTextList *list = block.textList();
3169 if (list->itemNumber(block) == 0) {
3170 const QTextListFormat format = list->format();
3171 const int style = format.style();
3172 bool ordered =
false;
3174 case QTextListFormat::ListDisc: html +=
"<ul"_L1;
break;
3175 case QTextListFormat::ListCircle: html +=
"<ul type=\"circle\""_L1;
break;
3176 case QTextListFormat::ListSquare: html +=
"<ul type=\"square\""_L1;
break;
3177 case QTextListFormat::ListDecimal: html +=
"<ol"_L1; ordered =
true;
break;
3178 case QTextListFormat::ListLowerAlpha: html +=
"<ol type=\"a\""_L1; ordered =
true;
break;
3179 case QTextListFormat::ListUpperAlpha: html +=
"<ol type=\"A\""_L1; ordered =
true;
break;
3180 case QTextListFormat::ListLowerRoman: html +=
"<ol type=\"i\""_L1; ordered =
true;
break;
3181 case QTextListFormat::ListUpperRoman: html +=
"<ol type=\"I\""_L1; ordered =
true;
break;
3182 default: html +=
"<ul"_L1;
3185 if (ordered && format.start() != 1) {
3186 html +=
" start=\""_L1;
3187 html += QString::number(format.start());
3192 styleString +=
"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;"_L1;
3194 if (format.hasProperty(QTextFormat::ListIndent)) {
3195 styleString +=
" -qt-list-indent: "_L1;
3196 styleString += QString::number(format.indent());
3197 styleString += u';';
3200 if (format.hasProperty(QTextFormat::ListNumberPrefix)) {
3201 QString numberPrefix = format.numberPrefix();
3202 numberPrefix.replace(u'"',
"\\22"_L1);
3203 numberPrefix.replace(u'\'',
"\\27"_L1);
3204 styleString +=
" -qt-list-number-prefix: "_L1;
3205 styleString += u'\'';
3206 styleString += numberPrefix;
3207 styleString += u'\'';
3208 styleString += u';';
3211 if (format.hasProperty(QTextFormat::ListNumberSuffix)) {
3212 if (format.numberSuffix() !=
"."_L1) {
3213 QString numberSuffix = format.numberSuffix();
3214 numberSuffix.replace(u'"',
"\\22"_L1);
3215 numberSuffix.replace(u'\'',
"\\27"_L1);
3216 styleString +=
" -qt-list-number-suffix: "_L1;
3217 styleString += u'\'';
3218 styleString += numberSuffix;
3219 styleString += u'\'';
3220 styleString += u';';
3224 html +=
" style=\""_L1;
3225 html += styleString;
3231 const QTextCharFormat blockFmt = formatDifference(defaultCharFormat, block.charFormat()).toCharFormat();
3232 if (!blockFmt.properties().isEmpty()) {
3233 html +=
" style=\""_L1;
3234 emitCharFormatStyle(blockFmt);
3237 defaultCharFormat.merge(block.charFormat());
3239 if (block.blockFormat().hasProperty(QTextFormat::BlockMarker)) {
3240 switch (block.blockFormat().marker()) {
3241 case QTextBlockFormat::MarkerType::Checked:
3242 html +=
" class=\"checked\""_L1;
3244 case QTextBlockFormat::MarkerType::Unchecked:
3245 html +=
" class=\"unchecked\""_L1;
3247 case QTextBlockFormat::MarkerType::NoMarker:
3253 const QTextBlockFormat blockFormat = block.blockFormat();
3254 if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
3257 QTextLength width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth);
3258 if (width.type() != QTextLength::VariableLength)
3259 emitTextLength(
"width", width);
3262 if (blockFormat.hasProperty(QTextFormat::BackgroundBrush)) {
3263 html +=
"style=\""_L1;
3264 html +=
"background-color:"_L1;
3265 html += colorValue(qvariant_cast<QBrush>(blockFormat.property(QTextFormat::BackgroundBrush)).color());
3274 const bool pre = blockFormat.nonBreakableLines();
3280 int headingLevel = blockFormat.headingLevel();
3281 if (headingLevel > 0 && headingLevel <= 6)
3282 html +=
"<h"_L1 + QString::number(headingLevel);
3287 emitBlockAttributes(block);
3290 if (block.begin().atEnd())
3291 html +=
"<br />"_L1;
3293 QTextBlock::Iterator it = block.begin();
3294 if (fragmentMarkers && !it.atEnd() && block == doc->begin())
3295 html +=
"<!--StartFragment-->"_L1;
3297 for (; !it.atEnd(); ++it)
3298 emitFragment(it.fragment());
3300 if (fragmentMarkers && block.position() + block.length() == QTextDocumentPrivate::get(doc)->length())
3301 html +=
"<!--EndFragment-->"_L1;
3306 html +=
"</pre>"_L1;
3308 closeTags +=
"</li>"_L1;
3310 int headingLevel = blockFormat.headingLevel();
3311 if (headingLevel > 0 && headingLevel <= 6)
3312 html += QString::asprintf(
"</h%d>", headingLevel);
3318 if (list->itemNumber(block) == list->count() - 1) {
3319 if (isOrderedList(list->format().style()))
3320 closeTags +=
"</ol>"_L1;
3322 closeTags +=
"</ul>"_L1;
3324 const QTextBlock nextBlock = block.next();
3328 if (nextBlock.isValid() && nextBlock.textList() &&
3329 nextBlock.textList()->itemNumber(nextBlock) == 0 &&
3330 nextBlock.textList()->format().indent() > list->format().indent()) {
3332 if (!closingTags.isEmpty() && list->itemNumber(block) == list->count() - 1)
3333 lastTag = closingTags.takeLast();
3334 lastTag.prepend(closeTags);
3335 closingTags << lastTag;
3336 }
else if (list->itemNumber(block) == list->count() - 1) {
3340 if (!closingTags.isEmpty())
3341 html += closingTags.takeLast();
3347 defaultCharFormat = oldDefaultCharFormat;
3352QString
QTextHtmlExporter::findUrlForImage(
const QTextDocument *doc, qint64 cacheKey,
bool isPixmap)
3358 if (QTextDocument *parent = qobject_cast<QTextDocument *>(doc->parent()))
3359 return findUrlForImage(parent, cacheKey, isPixmap);
3361 const QTextDocumentPrivate *priv = QTextDocumentPrivate::get(doc);
3362 Q_ASSERT(priv !=
nullptr);
3364 QMap<QUrl, QVariant>::const_iterator it = priv->cachedResources.constBegin();
3365 for (; it != priv->cachedResources.constEnd(); ++it) {
3367 const QVariant &v = it.value();
3368 if (v.userType() == QMetaType::QImage && !isPixmap) {
3369 if (qvariant_cast<QImage>(v).cacheKey() == cacheKey)
3373 if (v.userType() == QMetaType::QPixmap && isPixmap) {
3374 if (qvariant_cast<QPixmap>(v).cacheKey() == cacheKey)
3379 if (it != priv->cachedResources.constEnd())
3380 url = it.key().toString();
3385void QTextDocumentPrivate::mergeCachedResources(
const QTextDocumentPrivate *priv)
3390 cachedResources.insert(priv->cachedResources);
3395 if (format.hasProperty(QTextFormat::BackgroundImageUrl)) {
3396 QString url = format.property(QTextFormat::BackgroundImageUrl).toString();
3397 emitAttribute(
"background", url);
3399 const QBrush &brush = format.background();
3400 if (brush.style() == Qt::SolidPattern) {
3401 emitAttribute(
"bgcolor", colorValue(brush.color()));
3402 }
else if (brush.style() == Qt::TexturePattern) {
3404 const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
3406 const QString url = findUrlForImage(doc, cacheKey, isPixmap);
3409 emitAttribute(
"background", url);
3416 QTextTableFormat format = table->format();
3418 html +=
"\n<table"_L1;
3420 if (format.hasProperty(QTextFormat::FrameBorder))
3421 emitAttribute(
"border", QString::number(format.border()));
3423 emitFrameStyle(format, TableFrame);
3425 emitAlignment(format.alignment());
3426 emitTextLength(
"width", format.width());
3428 if (format.hasProperty(QTextFormat::TableCellSpacing))
3429 emitAttribute(
"cellspacing", QString::number(format.cellSpacing()));
3430 if (format.hasProperty(QTextFormat::TableCellPadding))
3431 emitAttribute(
"cellpadding", QString::number(format.cellPadding()));
3433 emitBackgroundAttribute(format);
3437 const int rows = table->rows();
3438 const int columns = table->columns();
3440 QList<QTextLength> columnWidths = format.columnWidthConstraints();
3441 if (columnWidths.isEmpty()) {
3442 columnWidths.resize(columns);
3443 columnWidths.fill(QTextLength());
3445 Q_ASSERT(columnWidths.size() == columns);
3447 QVarLengthArray<
bool> widthEmittedForColumn(columns);
3448 for (
int i = 0; i < columns; ++i)
3449 widthEmittedForColumn[i] =
false;
3451 const int headerRowCount = qMin(format.headerRowCount(), rows);
3452 if (headerRowCount > 0)
3453 html +=
"<thead>"_L1;
3455 for (
int row = 0; row < rows; ++row) {
3456 html +=
"\n<tr>"_L1;
3458 for (
int col = 0; col < columns; ++col) {
3459 const QTextTableCell cell = table->cellAt(row, col);
3462 if (cell.row() != row)
3465 if (cell.column() != col)
3470 if (!widthEmittedForColumn[col] && cell.columnSpan() == 1) {
3471 emitTextLength(
"width", columnWidths.at(col));
3472 widthEmittedForColumn[col] =
true;
3475 if (cell.columnSpan() > 1)
3476 emitAttribute(
"colspan", QString::number(cell.columnSpan()));
3478 if (cell.rowSpan() > 1)
3479 emitAttribute(
"rowspan", QString::number(cell.rowSpan()));
3481 const QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
3482 emitBackgroundAttribute(cellFormat);
3484 QTextCharFormat oldDefaultCharFormat = defaultCharFormat;
3486 QTextCharFormat::VerticalAlignment valign = cellFormat.verticalAlignment();
3489 if (valign >= QTextCharFormat::AlignMiddle && valign <= QTextCharFormat::AlignBottom) {
3490 styleString +=
" vertical-align:"_L1;
3492 case QTextCharFormat::AlignMiddle:
3493 styleString +=
"middle"_L1;
3495 case QTextCharFormat::AlignTop:
3496 styleString +=
"top"_L1;
3498 case QTextCharFormat::AlignBottom:
3499 styleString +=
"bottom"_L1;
3504 styleString += u';';
3506 QTextCharFormat temp;
3507 temp.setVerticalAlignment(valign);
3508 defaultCharFormat.merge(temp);
3511 if (cellFormat.hasProperty(QTextFormat::TableCellLeftPadding))
3512 styleString +=
" padding-left:"_L1 + QString::number(cellFormat.leftPadding()) + u';';
3513 if (cellFormat.hasProperty(QTextFormat::TableCellRightPadding))
3514 styleString +=
" padding-right:"_L1 + QString::number(cellFormat.rightPadding()) + u';';
3515 if (cellFormat.hasProperty(QTextFormat::TableCellTopPadding))
3516 styleString +=
" padding-top:"_L1 + QString::number(cellFormat.topPadding()) + u';';
3517 if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding))
3518 styleString +=
" padding-bottom:"_L1 + QString::number(cellFormat.bottomPadding()) + u';';
3520 if (cellFormat.hasProperty(QTextFormat::TableCellTopBorder))
3521 styleString +=
" border-top:"_L1 + QString::number(cellFormat.topBorder()) +
"px;"_L1;
3522 if (cellFormat.hasProperty(QTextFormat::TableCellRightBorder))
3523 styleString +=
" border-right:"_L1 + QString::number(cellFormat.rightBorder()) +
"px;"_L1;
3524 if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorder))
3525 styleString +=
" border-bottom:"_L1 + QString::number(cellFormat.bottomBorder()) +
"px;"_L1;
3526 if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorder))
3527 styleString +=
" border-left:"_L1 + QString::number(cellFormat.leftBorder()) +
"px;"_L1;
3529 if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderBrush))
3530 styleString +=
" border-top-color:"_L1 + cellFormat.topBorderBrush().color().name() + u';';
3531 if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderBrush))
3532 styleString +=
" border-right-color:"_L1 + cellFormat.rightBorderBrush().color().name() + u';';
3533 if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderBrush))
3534 styleString +=
" border-bottom-color:"_L1 + cellFormat.bottomBorderBrush().color().name() + u';';
3535 if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderBrush))
3536 styleString +=
" border-left-color:"_L1 + cellFormat.leftBorderBrush().color().name() + u';';
3538 if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderStyle))
3539 styleString +=
" border-top-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.topBorderStyle()) + u';';
3540 if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderStyle))
3541 styleString +=
" border-right-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.rightBorderStyle()) + u';';
3542 if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderStyle))
3543 styleString +=
" border-bottom-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.bottomBorderStyle()) + u';';
3544 if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderStyle))
3545 styleString +=
" border-left-style:"_L1 + richtextBorderStyleToHtmlBorderStyle(cellFormat.leftBorderStyle()) + u';';
3547 if (!styleString.isEmpty())
3548 html +=
" style=\""_L1 + styleString + u'\"';
3552 emitFrame(cell.begin());
3556 defaultCharFormat = oldDefaultCharFormat;
3560 if (headerRowCount > 0 && row == headerRowCount - 1)
3561 html +=
"</thead>"_L1;
3564 html +=
"</table>"_L1;
3569 if (!frameIt.atEnd()) {
3570 QTextFrame::Iterator next = frameIt;
3573 && frameIt.currentFrame() ==
nullptr
3574 && frameIt.parentFrame() != doc->rootFrame()
3575 && frameIt.currentBlock().begin().atEnd())
3579 for (QTextFrame::Iterator it = frameIt;
3580 !it.atEnd(); ++it) {
3581 if (QTextFrame *f = it.currentFrame()) {
3582 if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
3587 }
else if (it.currentBlock().isValid()) {
3588 emitBlock(it.currentBlock());
3595 FrameType frameType = f->parentFrame() ? TextFrame : RootFrame;
3597 html +=
"\n<table"_L1;
3598 QTextFrameFormat format = f->frameFormat();
3600 if (format.hasProperty(QTextFormat::FrameBorder))
3601 emitAttribute(
"border", QString::number(format.border()));
3603 emitFrameStyle(format, frameType);
3605 emitTextLength(
"width", format.width());
3606 emitTextLength(
"height", format.height());
3609 if (frameType != RootFrame)
3610 emitBackgroundAttribute(format);
3613 html +=
"\n<tr>\n<td style=\"border: none;\">"_L1;
3614 emitFrame(f->begin());
3615 html +=
"</td></tr></table>"_L1;
3618void QTextHtmlExporter::emitFrameStyle(
const QTextFrameFormat &format, FrameType frameType)
3620 const auto styleAttribute =
" style=\""_L1;
3621 html += styleAttribute;
3622 const qsizetype originalHtmlLength = html.size();
3624 if (frameType == TextFrame)
3625 html +=
"-qt-table-type: frame;"_L1;
3626 else if (frameType == RootFrame)
3627 html +=
"-qt-table-type: root;"_L1;
3629 const QTextFrameFormat defaultFormat;
3631 emitFloatStyle(format.position(), OmitStyleTag);
3632 emitPageBreakPolicy(format.pageBreakPolicy());
3634 if (format.borderBrush() != defaultFormat.borderBrush()) {
3635 html +=
" border-color:"_L1;
3636 html += colorValue(format.borderBrush().color());
3640 if (format.borderStyle() != defaultFormat.borderStyle())
3641 emitBorderStyle(format.borderStyle());
3643 if (format.hasProperty(QTextFormat::FrameMargin)
3644 || format.hasProperty(QTextFormat::FrameLeftMargin)
3645 || format.hasProperty(QTextFormat::FrameRightMargin)
3646 || format.hasProperty(QTextFormat::FrameTopMargin)
3647 || format.hasProperty(QTextFormat::FrameBottomMargin))
3648 emitMargins(QString::number(format.topMargin()),
3649 QString::number(format.bottomMargin()),
3650 QString::number(format.leftMargin()),
3651 QString::number(format.rightMargin()));
3653 if (format.property(QTextFormat::TableBorderCollapse).toBool())
3654 html +=
" border-collapse:collapse;"_L1;
3656 if (html.size() == originalHtmlLength)
3657 html.chop(styleAttribute.size());
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672#ifndef QT_NO_TEXTHTMLPARSER
3673QString QTextDocument::toHtml()
const
3675 return QTextHtmlExporter(
this).toHtml();
3680
3681
3682
3683
3684
3685
3686#if QT_CONFIG(textmarkdownwriter)
3687QString QTextDocument::toMarkdown(QTextDocument::MarkdownFeatures features)
const
3690 QTextStream s(&ret);
3691 QTextMarkdownWriter w(s, features);
3692 if (w.writeAll(
this))
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
3735
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758#if QT_CONFIG(textmarkdownreader)
3759void QTextDocument::setMarkdown(
const QString &markdown, QTextDocument::MarkdownFeatures features)
3761 QTextMarkdownImporter(
this, features).import(markdown);
3766
3767
3768QList<QTextFormat> QTextDocument::allFormats()
const
3770 Q_D(
const QTextDocument);
3771 return d->formatCollection()->formats;
3775
3776
3777
3778
3779
3783#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