data/MailTextWriter.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "MailTextWriter.h" | ||
| 2 | |||
| 3 | #include "ComposeDocumentFormat.h" | ||
| 4 | |||
| 5 | #include <QTextBlock> | ||
| 6 | #include <QTextCharFormat> | ||
| 7 | #include <QTextDocument> | ||
| 8 | #include <QTextFrame> | ||
| 9 | #include <QTextImageFormat> | ||
| 10 | #include <QTextList> | ||
| 11 | #include <QTextTable> | ||
| 12 | #include <QUrl> | ||
| 13 | |||
| 14 | namespace { | ||
| 15 | |||
| 16 | using namespace ComposeDocumentFormat; | ||
| 17 | |||
| 18 | class Writer { | ||
| 19 | public: | ||
| 20 | 90 | explicit Writer(const MailTextWriter::Options &options) : m_options(options) {} | |
| 21 | |||
| 22 | 90 | QString run(const QTextDocument &document) { | |
| 23 | 90 | writeFrame(document.rootFrame()); | |
| 24 |
1/1✓ Branch 6 → 7 taken 90 times.
|
90 | QString out = m_lines.join(QLatin1Char('\n')); |
| 25 | 90 | return out; | |
| 26 | } | ||
| 27 | |||
| 28 | private: | ||
| 29 | const MailTextWriter::Options &m_options; | ||
| 30 | QStringList m_lines; | ||
| 31 | bool m_inSignature = false; | ||
| 32 | |||
| 33 | 90 | void writeFrame(QTextFrame *frame) { | |
| 34 |
4/4✓ Branch 2 → 3 taken 90 times.
✓ Branch 14 → 15 taken 1912 times.
✓ Branch 16 → 4 taken 1912 times.
✓ Branch 16 → 17 taken 90 times.
|
2002 | for (auto it = frame->begin(); !it.atEnd(); ++it) { |
| 35 |
2/2✓ Branch 5 → 6 taken 20 times.
✓ Branch 5 → 11 taken 1892 times.
|
1912 | if (QTextFrame *child = it.currentFrame()) { |
| 36 |
3/4✓ Branch 6 → 7 taken 20 times.
✓ Branch 7 → 8 taken 20 times.
✗ Branch 7 → 9 not taken.
✓ Branch 8 → 10 taken 20 times.
|
20 | if (auto *table = qobject_cast<QTextTable *>(child)) writeTable(table); |
| 37 | ✗ | else writeFrame(child); | |
| 38 | 20 | continue; | |
| 39 | 20 | } | |
| 40 |
2/2✓ Branch 11 → 12 taken 1892 times.
✓ Branch 12 → 13 taken 1892 times.
|
1892 | writeBlock(it.currentBlock()); |
| 41 | } | ||
| 42 | 90 | } | |
| 43 | |||
| 44 | 15 | QString imageText(const QTextImageFormat &image) const { | |
| 45 |
5/5✓ Branch 2 → 3 taken 15 times.
✓ Branch 3 → 4 taken 15 times.
✓ Branch 4 → 5 taken 12 times.
✓ Branch 4 → 6 taken 3 times.
✓ Branch 6 → 7 taken 3 times.
|
15 | QString alt = image.name().startsWith(ImageScheme) ? QString() : image.name(); |
| 46 |
4/4✓ Branch 8 → 9 taken 15 times.
✓ Branch 9 → 10 taken 12 times.
✓ Branch 9 → 14 taken 3 times.
✓ Branch 10 → 11 taken 12 times.
|
15 | if (image.hasProperty(ImageAltProperty)) alt = image.stringProperty(ImageAltProperty); |
| 47 |
1/1✓ Branch 15 → 16 taken 15 times.
|
30 | return m_options.imageMarker.arg(alt); |
| 48 | 15 | } | |
| 49 | |||
| 50 | 613 | static QString linkText(const QString &text, const QString &href) { | |
| 51 | 613 | QString shown = href; | |
| 52 |
4/4✓ Branch 4 → 5 taken 613 times.
✓ Branch 5 → 6 taken 3 times.
✓ Branch 5 → 10 taken 610 times.
✓ Branch 6 → 7 taken 3 times.
|
613 | if (shown.startsWith(QLatin1String("mailto:"), Qt::CaseInsensitive)) shown = shown.mid(7); |
| 53 |
5/6✓ Branch 11 → 12 taken 613 times.
✗ Branch 11 → 14 not taken.
✓ Branch 13 → 14 taken 3 times.
✓ Branch 13 → 15 taken 610 times.
✓ Branch 16 → 17 taken 3 times.
✓ Branch 16 → 18 taken 610 times.
|
613 | if (text == href || text == shown) return text; |
| 54 |
3/3✓ Branch 23 → 24 taken 610 times.
✓ Branch 24 → 25 taken 610 times.
✓ Branch 25 → 26 taken 610 times.
|
1830 | return text + QStringLiteral(" <") + href + QLatin1Char('>'); |
| 55 | 613 | } | |
| 56 | |||
| 57 | 1988 | QString inlineText(const QTextBlock &block) const { | |
| 58 | 1988 | QString out; | |
| 59 |
2/2✓ Branch 2 → 3 taken 1988 times.
✓ Branch 3 → 4 taken 1988 times.
|
1988 | const bool heading = block.blockFormat().headingLevel() > 0; |
| 60 |
4/4✓ Branch 5 → 6 taken 1988 times.
✓ Branch 108 → 109 taken 4944 times.
✓ Branch 110 → 7 taken 4944 times.
✓ Branch 110 → 111 taken 1988 times.
|
6932 | for (auto it = block.begin(); !it.atEnd(); ++it) { |
| 61 |
1/1✓ Branch 7 → 8 taken 4944 times.
|
4944 | const QTextFragment fragment = it.fragment(); |
| 62 |
1/2✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 4944 times.
|
4959 | if (!fragment.isValid()) continue; |
| 63 |
1/1✓ Branch 11 → 12 taken 4944 times.
|
4944 | const QTextCharFormat format = fragment.charFormat(); |
| 64 |
3/3✓ Branch 12 → 13 taken 4944 times.
✓ Branch 13 → 14 taken 15 times.
✓ Branch 13 → 26 taken 4929 times.
|
4944 | if (format.isImageFormat()) { |
| 65 |
6/6✓ Branch 15 → 16 taken 15 times.
✓ Branch 16 → 17 taken 15 times.
✓ Branch 17 → 18 taken 15 times.
✓ Branch 21 → 22 taken 30 times.
✓ Branch 24 → 15 taken 15 times.
✓ Branch 24 → 25 taken 15 times.
|
30 | for (int i = 0; i < fragment.text().size(); ++i) out += imageText(format.toImageFormat()); |
| 66 | 15 | continue; | |
| 67 | 15 | } | |
| 68 |
1/1✓ Branch 26 → 27 taken 4929 times.
|
4929 | QString text = fragment.text(); |
| 69 |
1/1✓ Branch 30 → 31 taken 4929 times.
|
4929 | text.replace(QChar(0x2028), QLatin1Char('\n')); |
| 70 |
1/1✓ Branch 34 → 35 taken 4929 times.
|
4929 | text.replace(QChar(0x00A0), QLatin1Char(' ')); |
| 71 |
1/1✓ Branch 36 → 37 taken 4929 times.
|
4929 | text.remove(QChar(0xFFFC)); |
| 72 |
1/2✗ Branch 38 → 39 not taken.
✓ Branch 38 → 40 taken 4929 times.
|
4929 | if (text.isEmpty()) continue; |
| 73 |
2/2✓ Branch 40 → 41 taken 4927 times.
✓ Branch 40 → 97 taken 2 times.
|
4929 | if (m_options.markup) { |
| 74 |
5/5✓ Branch 41 → 42 taken 4927 times.
✓ Branch 42 → 43 taken 5 times.
✓ Branch 42 → 53 taken 4922 times.
✓ Branch 47 → 48 taken 5 times.
✓ Branch 48 → 49 taken 5 times.
|
4927 | if (isInlineCode(format)) text = QLatin1Char('`') + text + QLatin1Char('`'); |
| 75 |
5/5✓ Branch 53 → 54 taken 4927 times.
✓ Branch 54 → 55 taken 604 times.
✓ Branch 54 → 65 taken 4323 times.
✓ Branch 59 → 60 taken 604 times.
✓ Branch 60 → 61 taken 604 times.
|
4927 | if (format.fontItalic()) text = QLatin1Char('_') + text + QLatin1Char('_'); |
| 76 |
9/9✓ Branch 65 → 66 taken 4927 times.
✓ Branch 66 → 67 taken 680 times.
✓ Branch 66 → 69 taken 4247 times.
✓ Branch 67 → 68 taken 677 times.
✓ Branch 67 → 69 taken 3 times.
✓ Branch 70 → 71 taken 677 times.
✓ Branch 70 → 81 taken 4250 times.
✓ Branch 75 → 76 taken 677 times.
✓ Branch 76 → 77 taken 677 times.
|
4927 | if (format.fontWeight() >= QFont::Bold && !heading) text = QLatin1Char('*') + text + QLatin1Char('*'); |
| 77 |
11/13✓ Branch 81 → 82 taken 4927 times.
✓ Branch 82 → 83 taken 616 times.
✓ Branch 82 → 87 taken 4311 times.
✓ Branch 83 → 84 taken 616 times.
✓ Branch 84 → 85 taken 616 times.
✓ Branch 85 → 86 taken 613 times.
✓ Branch 85 → 87 taken 3 times.
✓ Branch 88 → 89 taken 616 times.
✓ Branch 88 → 90 taken 4311 times.
✓ Branch 90 → 91 taken 613 times.
✓ Branch 90 → 97 taken 4314 times.
✗ Branch 154 → 155 not taken.
✗ Branch 154 → 156 not taken.
|
4927 | if (format.isAnchor() && isAllowedLink(format.anchorHref())) |
| 78 |
2/2✓ Branch 91 → 92 taken 613 times.
✓ Branch 92 → 93 taken 613 times.
|
613 | text = linkText(text, format.anchorHref()); |
| 79 | } | ||
| 80 |
1/1✓ Branch 97 → 98 taken 4929 times.
|
4929 | out += text; |
| 81 | 4944 | } | |
| 82 | 1988 | return out; | |
| 83 | ✗ | } | |
| 84 | |||
| 85 | 332 | static QString listPrefix(const QTextBlock &block, QTextList *list) { | |
| 86 |
2/2✓ Branch 2 → 3 taken 332 times.
✓ Branch 3 → 4 taken 332 times.
|
332 | const int level = qBound(1, list->format().indent(), MaxListLevel); |
| 87 |
1/1✓ Branch 8 → 9 taken 332 times.
|
332 | const QString indent(2 * (level - 1), QLatin1Char(' ')); |
| 88 |
4/4✓ Branch 9 → 10 taken 332 times.
✓ Branch 10 → 11 taken 332 times.
✓ Branch 12 → 13 taken 34 times.
✓ Branch 12 → 21 taken 298 times.
|
332 | switch (list->format().style()) { |
| 89 | 34 | case QTextListFormat::ListDisc: | |
| 90 | case QTextListFormat::ListCircle: | ||
| 91 | case QTextListFormat::ListSquare: | ||
| 92 |
1/1✓ Branch 16 → 17 taken 34 times.
|
34 | return indent + QStringLiteral("- "); |
| 93 | 298 | default: { | |
| 94 |
1/1✓ Branch 21 → 22 taken 298 times.
|
298 | QString marker = list->itemText(block); |
| 95 |
1/5✗ Branch 23 → 24 not taken.
✓ Branch 23 → 33 taken 298 times.
✗ Branch 26 → 27 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 28 → 29 not taken.
|
298 | if (marker.isEmpty()) marker = QString::number(list->itemNumber(block) + 1) + QLatin1Char('.'); |
| 96 |
2/2✓ Branch 35 → 36 taken 298 times.
✓ Branch 36 → 37 taken 298 times.
|
596 | return indent + marker + QLatin1Char(' '); |
| 97 | 298 | } | |
| 98 | } | ||
| 99 | 332 | } | |
| 100 | |||
| 101 | 1892 | void append(const QString &prefix, const QString &text) { | |
| 102 | // A Shift+Enter line break continues with the same quote prefix. | ||
| 103 |
1/1✓ Branch 5 → 6 taken 1892 times.
|
1892 | const QStringList lines = text.split(QLatin1Char('\n')); |
| 104 |
2/2✓ Branch 33 → 7 taken 1892 times.
✓ Branch 33 → 34 taken 1892 times.
|
3784 | for (int i = 0; i < lines.size(); ++i) { |
| 105 | 1892 | QString linePrefix = prefix; | |
| 106 |
3/13✗ Branch 8 → 9 not taken.
✓ Branch 8 → 15 taken 1892 times.
✗ Branch 9 → 10 not taken.
✗ Branch 12 → 13 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 15 not taken.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 1892 times.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 26 taken 1892 times.
✗ Branch 22 → 23 not taken.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
|
1892 | if (i > 0 && !prefix.trimmed().startsWith(QLatin1Char('>'))) linePrefix = QString(prefix.size(), QLatin1Char(' ')); |
| 107 |
2/2✓ Branch 27 → 28 taken 1892 times.
✓ Branch 28 → 29 taken 1892 times.
|
1892 | m_lines.append(linePrefix + lines.at(i)); |
| 108 | 1892 | } | |
| 109 | 1892 | } | |
| 110 | |||
| 111 | 1892 | void writeBlock(const QTextBlock &block) { | |
| 112 |
1/1✓ Branch 2 → 3 taken 1892 times.
|
1892 | const bool signature = region(block) == SignatureRegion; |
| 113 |
4/4✓ Branch 3 → 4 taken 79 times.
✓ Branch 3 → 16 taken 1813 times.
✓ Branch 4 → 5 taken 29 times.
✓ Branch 4 → 16 taken 50 times.
|
1892 | if (signature && !m_inSignature) { |
| 114 | // The separator goes once, before the signature's first line — | ||
| 115 | // unless the signature already starts with one. | ||
| 116 |
5/6✓ Branch 6 → 7 taken 29 times.
✓ Branch 7 → 8 taken 29 times.
✓ Branch 11 → 12 taken 29 times.
✗ Branch 11 → 16 not taken.
✓ Branch 12 → 13 taken 29 times.
✓ Branch 13 → 14 taken 29 times.
|
29 | if (block.text().trimmed() != QLatin1String("--")) m_lines.append(MailTextWriter::signatureSeparator()); |
| 117 | } | ||
| 118 | 1892 | m_inSignature = signature; | |
| 119 | 1892 | QString prefix; | |
| 120 |
1/1✓ Branch 16 → 17 taken 1892 times.
|
1892 | const int depth = qBound(0, quoteLevel(block), 8); |
| 121 |
3/3✓ Branch 22 → 23 taken 12 times.
✓ Branch 26 → 19 taken 12 times.
✓ Branch 26 → 27 taken 1892 times.
|
1904 | for (int i = 0; i < depth; ++i) prefix += QStringLiteral("> "); |
| 122 |
9/12✓ Branch 27 → 28 taken 1892 times.
✓ Branch 28 → 29 taken 3 times.
✓ Branch 28 → 33 taken 1889 times.
✓ Branch 29 → 30 taken 3 times.
✓ Branch 31 → 32 taken 3 times.
✗ Branch 31 → 33 not taken.
✓ Branch 34 → 35 taken 3 times.
✓ Branch 34 → 36 taken 1889 times.
✓ Branch 36 → 37 taken 3 times.
✓ Branch 36 → 44 taken 1889 times.
✗ Branch 86 → 87 not taken.
✗ Branch 86 → 88 not taken.
|
1892 | if (isHorizontalRule(block) && block.text().isEmpty()) { |
| 123 |
1/1✓ Branch 40 → 41 taken 3 times.
|
3 | append(prefix, QStringLiteral("-----")); |
| 124 | 3 | return; | |
| 125 | } | ||
| 126 |
3/3✓ Branch 44 → 45 taken 1889 times.
✓ Branch 45 → 46 taken 8 times.
✓ Branch 45 → 54 taken 1881 times.
|
1889 | if (isCodeBlock(block)) { |
| 127 |
1/1✓ Branch 46 → 47 taken 8 times.
|
8 | QString text = block.text(); |
| 128 |
1/1✓ Branch 50 → 51 taken 8 times.
|
8 | text.replace(QChar(0x2028), QLatin1Char('\n')); |
| 129 |
1/1✓ Branch 51 → 52 taken 8 times.
|
8 | append(prefix, text); |
| 130 | 8 | return; | |
| 131 | 8 | } | |
| 132 |
5/5✓ Branch 54 → 55 taken 1881 times.
✓ Branch 55 → 56 taken 332 times.
✓ Branch 55 → 60 taken 1549 times.
✓ Branch 56 → 57 taken 332 times.
✓ Branch 57 → 58 taken 332 times.
|
1881 | if (QTextList *list = block.textList()) prefix += listPrefix(block, list); |
| 133 |
2/2✓ Branch 60 → 61 taken 1881 times.
✓ Branch 61 → 62 taken 1881 times.
|
1881 | append(prefix, inlineText(block)); |
| 134 | 1892 | } | |
| 135 | |||
| 136 | 20 | void writeTable(QTextTable *table) { | |
| 137 |
2/2✓ Branch 64 → 3 taken 54 times.
✓ Branch 64 → 65 taken 20 times.
|
74 | for (int row = 0; row < table->rows(); ++row) { |
| 138 | 54 | QStringList cells; | |
| 139 |
3/3✓ Branch 51 → 52 taken 161 times.
✓ Branch 52 → 4 taken 107 times.
✓ Branch 52 → 53 taken 54 times.
|
161 | for (int column = 0; column < table->columns(); ++column) { |
| 140 |
1/1✓ Branch 4 → 5 taken 107 times.
|
107 | const QTextTableCell cell = table->cellAt(row, column); |
| 141 | // A merged cell's text once, where it starts; blocks by position (a | ||
| 142 | // nested frame's iterator did not end inside a merged cell). | ||
| 143 |
6/10✓ Branch 6 → 7 taken 107 times.
✗ Branch 6 → 11 not taken.
✓ Branch 7 → 8 taken 107 times.
✓ Branch 8 → 9 taken 107 times.
✗ Branch 8 → 11 not taken.
✓ Branch 9 → 10 taken 107 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 107 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 107 times.
|
107 | if (!cell.isValid() || cell.row() != row || cell.column() != column) continue; |
| 144 | 107 | QStringList parts; | |
| 145 |
3/3✓ Branch 15 → 16 taken 107 times.
✓ Branch 16 → 17 taken 107 times.
✓ Branch 17 → 18 taken 107 times.
|
107 | for (QTextBlock block = table->document()->findBlock(cell.firstPosition()); |
| 146 |
8/9✓ Branch 30 → 31 taken 214 times.
✓ Branch 31 → 32 taken 214 times.
✗ Branch 31 → 36 not taken.
✓ Branch 32 → 33 taken 214 times.
✓ Branch 33 → 34 taken 214 times.
✓ Branch 34 → 35 taken 107 times.
✓ Branch 34 → 36 taken 107 times.
✓ Branch 37 → 19 taken 107 times.
✓ Branch 37 → 38 taken 107 times.
|
214 | block.isValid() && block.position() <= cell.lastPosition(); block = block.next()) |
| 147 |
4/4✓ Branch 19 → 20 taken 107 times.
✓ Branch 24 → 25 taken 107 times.
✓ Branch 25 → 26 taken 107 times.
✓ Branch 27 → 28 taken 107 times.
|
107 | parts.append(inlineText(block).replace(QLatin1Char('\n'), QLatin1Char(' '))); |
| 148 |
2/2✓ Branch 40 → 41 taken 107 times.
✓ Branch 41 → 42 taken 107 times.
|
107 | cells.append(parts.join(QLatin1Char(' '))); |
| 149 | 107 | } | |
| 150 |
2/2✓ Branch 56 → 57 taken 54 times.
✓ Branch 57 → 58 taken 54 times.
|
108 | m_lines.append(cells.join(QStringLiteral(" | "))); |
| 151 | 54 | } | |
| 152 | 20 | } | |
| 153 | }; | ||
| 154 | |||
| 155 | } // namespace | ||
| 156 | |||
| 157 | 90 | QString MailTextWriter::write(const QTextDocument &document, const Options &options) { | |
| 158 | 90 | Writer writer(options); | |
| 159 |
1/1✓ Branch 3 → 4 taken 90 times.
|
180 | return writer.run(document); |
| 160 | 90 | } | |
| 161 |