File indexing completed on 2024-12-22 04:48:10
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2023 Louis Schul <schul9louis@gmail.com> 0004 */ 0005 0006 // CREDIT TO ORIGINAL IDEA: https://marked.js.org/ 0007 0008 #include "renderer.h" 0009 #include "logic/plugins/syntaxHighlight/highlightHelper.h" 0010 0011 #include <QRegularExpression> 0012 #include <QUrl> 0013 0014 QString Renderer::code(QString &code, const QString &lang, const bool highlight) 0015 { 0016 if (highlight) { 0017 code = HighlightHelper::getHighlightedString(code, lang); 0018 } 0019 0020 return QStringLiteral("<pre><code>") + (highlight ? code : escape(code, true)) + QStringLiteral("</code></pre>\n"); 0021 } 0022 0023 QString Renderer::blockquote(const QString "e) 0024 { 0025 return QStringLiteral("<blockquote>\n") + quote + QStringLiteral("</blockquote>\n"); 0026 } 0027 0028 QString Renderer::html(const QString &html) 0029 { 0030 return html; 0031 } 0032 0033 QString Renderer::heading(const QString &text, const QString &lvl, const QString &raw, const bool scrollTo) 0034 { 0035 static const QRegularExpression rawReg = QRegularExpression(QStringLiteral("[^\\w]+")); 0036 const QString id = scrollTo ? QStringLiteral("noteMapperScrollTo") : raw.toLower().replace(rawReg, QStringLiteral("-")); 0037 return QStringLiteral("<h") + lvl + QStringLiteral(" id=\"") + id + QStringLiteral("\">") + text + QStringLiteral("</h") + lvl + QStringLiteral(">\n"); 0038 } 0039 0040 QString Renderer::hr() 0041 { 0042 return QStringLiteral("<hr>\n"); 0043 } 0044 0045 QString Renderer::list(const QString &body, bool ordered, const QString &start) 0046 { 0047 const QString type = ordered ? QStringLiteral("ol") : QStringLiteral("ul"); 0048 QString startat; 0049 if (ordered && start != QString::number(1)) { 0050 startat = (QStringLiteral(" start=\"") + start + QStringLiteral("\"")); 0051 } else { 0052 startat = QLatin1String(); 0053 } 0054 0055 return QStringLiteral("<") + type + startat + QStringLiteral(">\n") + body + QStringLiteral("</") + type + QStringLiteral(">\n"); 0056 } 0057 0058 QString Renderer::listItem(const QString &text, const bool hasCheck) 0059 { 0060 const QString out = hasCheck ? QStringLiteral("<li class=\"hasCheck\"> <label class=\"form-control\">\n") + text + QStringLiteral("</label></li>\n") 0061 : QStringLiteral("<li>") + text + QStringLiteral("</li>\n"); 0062 return out; 0063 } 0064 0065 QString Renderer::checkbox(bool checked) 0066 { 0067 const QString checkedString = checked ? QStringLiteral("checked=\"\" ") : QLatin1String(); 0068 0069 return QStringLiteral("<input ") + checkedString + QStringLiteral("disabled=\"\" type=\"checkbox\">"); 0070 } 0071 0072 QString Renderer::paragraph(const QString &text) 0073 { 0074 return QStringLiteral("<p>") + text + QStringLiteral("</p>\n"); 0075 } 0076 0077 QString Renderer::table(const QString &header, QString &body) 0078 { 0079 if (!body.isEmpty()) 0080 body = QStringLiteral("<tbody>") + body + QStringLiteral("</tbody>"); 0081 0082 return QStringLiteral("<table>\n") + QStringLiteral("<thead>\n") + header + QStringLiteral("</thead>\n") + body + QStringLiteral("</table>\n"); 0083 } 0084 0085 QString Renderer::tableRow(const QString &content) 0086 { 0087 return QStringLiteral("<tr>\n") + content + QStringLiteral("</tr>\n"); 0088 } 0089 0090 QString Renderer::tableCell(const QString &content, const QVariantMap &flags) 0091 { 0092 const QString type = flags[QStringLiteral("header")].toBool() ? QStringLiteral("th") : QStringLiteral("td"); 0093 const QString align = flags[QStringLiteral("align")].toString(); 0094 QString tag; 0095 if (align.isEmpty()) { 0096 tag = QStringLiteral("<") + type + QStringLiteral(">"); 0097 } else { 0098 tag = QStringLiteral("<") + type + QStringLiteral(" style=\"text-align:") + align + QStringLiteral(";\">"); 0099 } 0100 return tag + content + QStringLiteral("</") + type + QStringLiteral(">\n"); 0101 } 0102 0103 QString Renderer::strong(const QString &text) 0104 { 0105 return QStringLiteral("<strong>") + text + QStringLiteral("</strong>"); 0106 } 0107 0108 QString Renderer::em(const QString &text) 0109 { 0110 return QStringLiteral("<em>") + text + QStringLiteral("</em>"); 0111 } 0112 0113 QString Renderer::codeSpan(const QString &text) 0114 { 0115 return QStringLiteral("<code>") + text + QStringLiteral("</code>"); 0116 } 0117 0118 QString Renderer::br() 0119 { 0120 return QStringLiteral("<br>"); 0121 } 0122 0123 QString Renderer::del(const QString &text) 0124 { 0125 return QStringLiteral("<del>") + text + QStringLiteral("</del>"); 0126 } 0127 0128 QString Renderer::mark(const QString &text) 0129 { 0130 return QStringLiteral("<mark>") + text + QStringLiteral("</mark>"); 0131 } 0132 0133 QString Renderer::wikilink(const QString &href, const QString &title, const QString &text) 0134 { 0135 const QString leading = QStringLiteral("<a href=\"") + href + QStringLiteral("\""); 0136 const QString ending = QStringLiteral(">") + text + QStringLiteral("</a>"); 0137 QString middle = QLatin1String(); 0138 if (!title.isEmpty()) { 0139 QStringLiteral(" title=\"") + title + QStringLiteral("\""); 0140 } 0141 return leading + middle + ending; 0142 } 0143 0144 QString Renderer::link(QString &href, const QString &title, const QString &text) 0145 { 0146 const QByteArray uri = QUrl::fromUserInput(href).toEncoded(); 0147 if (uri.isEmpty()) 0148 return text; 0149 0150 static const QRegularExpression uriPercentReg = QRegularExpression(QStringLiteral("%25")); 0151 href = QString::fromUtf8(uri).replace(uriPercentReg, QStringLiteral("%")); 0152 0153 const QString leading = QStringLiteral("<a href=\"") + escape(href, false) + QStringLiteral("\""); 0154 const QString ending = QStringLiteral(">") + text + QStringLiteral("</a>"); 0155 QString middle = QLatin1String(); 0156 if (!title.isEmpty()) { 0157 QStringLiteral(" title=\"") + title + QStringLiteral("\""); 0158 } 0159 0160 return leading + middle + ending; 0161 } 0162 0163 QString Renderer::image(const QString &href, const QString &title, const QString &text) 0164 { 0165 const QString leading = QStringLiteral("<img src=\"") + href + QStringLiteral("\" alt=\"") + text + QStringLiteral("\""); 0166 QString middle = QLatin1String(); 0167 if (!title.isEmpty()) { 0168 QStringLiteral(" title=\"") + title + QStringLiteral("\""); 0169 } 0170 static const QString ending = QStringLiteral(">"); 0171 0172 return leading + middle + ending; 0173 } 0174 0175 QString Renderer::text(const QString &text) 0176 { 0177 return text; 0178 } 0179 0180 QString Renderer::escape(QString &html, bool encode) 0181 { 0182 const QRegularExpression encodedReplacement = !encode ? QRegularExpression(QStringLiteral("&(?!#?\\w+;)")) : QRegularExpression(QStringLiteral("&")); 0183 0184 static const QRegularExpression leftBracketReg = QRegularExpression(QStringLiteral("<")); 0185 static const QRegularExpression rightBracketReg = QRegularExpression(QStringLiteral(">")); 0186 static const QRegularExpression quoteReg = QRegularExpression(QStringLiteral("\"")); 0187 static const QRegularExpression apostropheReg = QRegularExpression(QStringLiteral("'")); 0188 return html.replace(encodedReplacement, QStringLiteral("&")) 0189 .replace(leftBracketReg, QStringLiteral("<")) 0190 .replace(rightBracketReg, QStringLiteral(">")) 0191 .replace(quoteReg, QStringLiteral(""")) 0192 .replace(apostropheReg, QStringLiteral("'")); 0193 } 0194 0195 QString Renderer::unescape(const QString &html) 0196 { 0197 // explicitly match decimal, hex, and named HTML entities 0198 QString result = html; 0199 static const QRegularExpression regex(QStringLiteral("&(#(?:\\d+)|(?:#x[0-9A-Fa-f]+)|(?:\\w+));?")); 0200 QRegularExpressionMatchIterator i = regex.globalMatch(result); 0201 0202 QRegularExpressionMatch match; 0203 while (i.hasNext()) { 0204 match = i.next(); 0205 const QString entity = match.captured(1).toLower(); 0206 0207 if (entity == QStringLiteral("colon")) { 0208 result.replace(match.capturedStart(), match.capturedLength(), QStringLiteral(":")); 0209 continue; 0210 } 0211 if (entity.startsWith(QStringLiteral("#"))) { 0212 bool ok; 0213 // check for hexadecimal or numerical value 0214 const int charCode = (entity.at(1) == QChar::fromLatin1('x')) ? entity.mid(2).toInt(&ok, 16) : entity.mid(1).toInt(&ok); 0215 0216 result.replace(match.capturedStart(), match.capturedLength(), QChar(charCode)); 0217 continue; 0218 } 0219 result.replace(match.capturedStart(), match.capturedLength(), QLatin1String()); 0220 } 0221 0222 return result; 0223 }