aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFelix (xq) Queißner <git@mq32.de>2020-06-23 00:35:41 +0200
committerFelix (xq) Queißner <git@mq32.de>2020-06-23 00:35:41 +0200
commit0860a8ec32fd01b6fdea92e1e6a80de096e2e694 (patch)
treeb858ca1d88bad6050f51d9a413b48f74b1a21545 /src
parent75ec461eeaa851cb5c53f4cfffc434e3e529ed1d (diff)
downloadkristall-0860a8ec32fd01b6fdea92e1e6a80de096e2e694.tar.gz
Starts to implement new markdown rendering
Diffstat (limited to 'src')
-rw-r--r--src/browsertab.cpp13
-rw-r--r--src/kristall.pro4
-rw-r--r--src/renderers/geminirenderer.cpp62
-rw-r--r--src/renderers/markdownrenderer.cpp267
-rw-r--r--src/renderers/markdownrenderer.hpp29
-rw-r--r--src/renderers/textstyleinstance.cpp28
-rw-r--r--src/renderers/textstyleinstance.hpp22
7 files changed, 373 insertions, 52 deletions
diff --git a/src/browsertab.cpp b/src/browsertab.cpp
index f73ac4b..ae7fd20 100644
--- a/src/browsertab.cpp
+++ b/src/browsertab.cpp
@@ -5,6 +5,7 @@
#include "renderers/gophermaprenderer.hpp"
#include "renderers/geminirenderer.hpp"
#include "renderers/plaintextrenderer.hpp"
+#include "renderers/markdownrenderer.hpp"
#include "mimeparser.hpp"
@@ -416,16 +417,14 @@ void BrowserTab::on_requestComplete(const QByteArray &ref_data, const QString &m
document->setDocumentMargin(doc_style.margin);
document->setHtml(QString::fromUtf8(data));
}
-#if defined(QT_FEATURE_textmarkdownreader)
else if (not plaintext_only and mime.is("text","markdown"))
{
- document = std::make_unique<QTextDocument>();
- document->setDefaultFont(doc_style.standard_font);
- document->setDefaultStyleSheet(doc_style.toStyleSheet());
- document->setDocumentMargin(doc_style.margin);
- document->setMarkdown(QString::fromUtf8(data));
+ document = MarkdownRenderer::render(
+ data,
+ this->current_location,
+ doc_style,
+ this->outline);
}
-#endif
else if (mime.is("text"))
{
document = PlainTextRenderer::render(data, doc_style);
diff --git a/src/kristall.pro b/src/kristall.pro
index c5a29f5..43316cc 100644
--- a/src/kristall.pro
+++ b/src/kristall.pro
@@ -67,6 +67,8 @@ SOURCES += \
ioutil.cpp \
main.cpp \
mainwindow.cpp \
+ renderers/markdownrenderer.cpp \
+ renderers/textstyleinstance.cpp \
widgets/mediaplayer.cpp \
mimeparser.cpp \
protocolhandler.cpp \
@@ -105,6 +107,8 @@ HEADERS += \
ioutil.hpp \
kristall.hpp \
mainwindow.hpp \
+ renderers/markdownrenderer.hpp \
+ renderers/textstyleinstance.hpp \
widgets/mediaplayer.hpp \
mimeparser.hpp \
protocolhandler.hpp \
diff --git a/src/renderers/geminirenderer.cpp b/src/renderers/geminirenderer.cpp
index ec00869..7066fa3 100644
--- a/src/renderers/geminirenderer.cpp
+++ b/src/renderers/geminirenderer.cpp
@@ -8,6 +8,8 @@
#include "kristall.hpp"
+#include "textstyleinstance.hpp"
+
static QByteArray trim_whitespace(QByteArray items)
{
int start = 0;
@@ -29,37 +31,7 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
DocumentStyle const & themed_style,
DocumentOutlineModel &outline)
{
- QTextCharFormat preformatted;
- preformatted.setFont(themed_style.preformatted_font);
- preformatted.setForeground(themed_style.preformatted_color);
-
- QTextCharFormat standard;
- standard.setFont(themed_style.standard_font);
- standard.setForeground(themed_style.standard_color);
-
- QTextCharFormat standard_link;
- standard_link.setFont(themed_style.standard_font);
- standard_link.setForeground(QBrush(themed_style.internal_link_color));
-
- QTextCharFormat external_link;
- external_link.setFont(themed_style.standard_font);
- external_link.setForeground(QBrush(themed_style.external_link_color));
-
- QTextCharFormat cross_protocol_link;
- cross_protocol_link.setFont(themed_style.standard_font);
- cross_protocol_link.setForeground(QBrush(themed_style.cross_scheme_link_color));
-
- QTextCharFormat standard_h1;
- standard_h1.setFont(themed_style.h1_font);
- standard_h1.setForeground(QBrush(themed_style.h1_color));
-
- QTextCharFormat standard_h2;
- standard_h2.setFont(themed_style.h2_font);
- standard_h2.setForeground(QBrush(themed_style.h2_color));
-
- QTextCharFormat standard_h3;
- standard_h3.setFont(themed_style.h3_font);
- standard_h3.setForeground(QBrush(themed_style.h3_color));
+ TextStyleInstance text_style { themed_style };
std::unique_ptr<GeminiDocument> result = std::make_unique<GeminiDocument>();
result->setDocumentMargin(themed_style.margin);
@@ -105,7 +77,7 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
else
{
cursor.setBlockFormat(preformatted_format);
- cursor.setCharFormat(preformatted);
+ cursor.setCharFormat(text_style.preformatted);
cursor.insertText(line + "\n");
}
}
@@ -125,7 +97,7 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
QString item = trim_whitespace(line.mid(1));
- cursor.insertText(item, standard);
+ cursor.insertText(item, text_style.standard);
continue;
}
else
@@ -146,7 +118,7 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
blockquote = true;
cursor.setBlockFormat(block_quote_format);
- cursor.insertText(trim_whitespace(line.mid(1)) + "\n", standard);
+ cursor.insertText(trim_whitespace(line.mid(1)) + "\n", text_style.standard);
continue;
}
@@ -163,7 +135,7 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
auto heading = trim_whitespace(line.mid(3));
auto id = unique_anchor_name();
- auto fmt = standard_h3;
+ auto fmt = text_style.standard_h3;
fmt.setAnchor(true);
fmt.setAnchorNames(QStringList { id });
@@ -175,7 +147,7 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
auto heading = trim_whitespace(line.mid(2));
auto id = unique_anchor_name();
- auto fmt = standard_h2;
+ auto fmt = text_style.standard_h2;
fmt.setAnchor(true);
fmt.setAnchorNames(QStringList { id });
@@ -187,7 +159,7 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
auto heading = trim_whitespace(line.mid(1));
auto id = unique_anchor_name();
- auto fmt = standard_h1;
+ auto fmt = text_style.standard_h1;
fmt.setAnchor(true);
fmt.setAnchorNames(QStringList { id });
@@ -227,18 +199,18 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
// qDebug() << link << title;
- auto fmt = standard_link;
+ auto fmt = text_style.standard_link;
QString prefix;
if (absolute_url.host() == root_url.host())
{
prefix = themed_style.internal_link_prefix;
- fmt = standard_link;
+ fmt = text_style.standard_link;
}
else
{
prefix = themed_style.external_link_prefix;
- fmt = external_link;
+ fmt = text_style.external_link;
}
QString suffix = "";
@@ -246,7 +218,7 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
{
if(absolute_url.scheme() != "kristall+ctrl") {
suffix = " [" + absolute_url.scheme().toUpper() + "]";
- fmt = cross_protocol_link;
+ fmt = text_style.cross_protocol_link;
}
}
@@ -267,7 +239,7 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
bool rendering_bold = false;
bool rendering_underlined = false;
- QTextCharFormat fmt = standard;
+ QTextCharFormat fmt = text_style.standard;
QByteArray buffer;
@@ -283,7 +255,7 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
char c = line.at(i);
if(c == ' ') {
flush();
- fmt = standard;
+ fmt = text_style.standard;
buffer.append(' ');
rendering_bold = false;
rendering_underlined = false;
@@ -320,10 +292,10 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
flush();
- cursor.insertText("\n", standard);
+ cursor.insertText("\n", text_style.standard);
}
else {
- cursor.insertText(line + "\n", standard);
+ cursor.insertText(line + "\n", text_style.standard);
}
}
}
diff --git a/src/renderers/markdownrenderer.cpp b/src/renderers/markdownrenderer.cpp
new file mode 100644
index 0000000..1c53a2f
--- /dev/null
+++ b/src/renderers/markdownrenderer.cpp
@@ -0,0 +1,267 @@
+#include "markdownrenderer.hpp"
+
+#include "textstyleinstance.hpp"
+
+#include <cmark.h>
+#include <node.h>
+#include <cassert>
+
+#include <QDebug>
+#include <QTextCursor>
+
+//static char const *nodeToStr(cmark_node_type type)
+//{
+// switch (type)
+// {
+// case CMARK_NODE_DOCUMENT:
+// return "CMARK_NODE_DOCUMENT";
+// case CMARK_NODE_BLOCK_QUOTE:
+// return "CMARK_NODE_BLOCK_QUOTE";
+// case CMARK_NODE_LIST:
+// return "CMARK_NODE_LIST";
+// case CMARK_NODE_ITEM:
+// return "CMARK_NODE_ITEM";
+// case CMARK_NODE_CODE_BLOCK:
+// return "CMARK_NODE_CODE_BLOCK";
+// case CMARK_NODE_HTML_BLOCK:
+// return "CMARK_NODE_HTML_BLOCK";
+// case CMARK_NODE_CUSTOM_BLOCK:
+// return "CMARK_NODE_CUSTOM_BLOCK";
+// case CMARK_NODE_PARAGRAPH:
+// return "CMARK_NODE_PARAGRAPH";
+// case CMARK_NODE_HEADING:
+// return "CMARK_NODE_HEADING";
+// case CMARK_NODE_THEMATIC_BREAK:
+// return "CMARK_NODE_THEMATIC_BREAK";
+// case CMARK_NODE_TEXT:
+// return "CMARK_NODE_TEXT";
+// case CMARK_NODE_SOFTBREAK:
+// return "CMARK_NODE_SOFTBREAK";
+// case CMARK_NODE_LINEBREAK:
+// return "CMARK_NODE_LINEBREAK";
+// case CMARK_NODE_CODE:
+// return "CMARK_NODE_CODE";
+// case CMARK_NODE_HTML_INLINE:
+// return "CMARK_NODE_HTML_INLINE";
+// case CMARK_NODE_CUSTOM_INLINE:
+// return "CMARK_NODE_CUSTOM_INLINE";
+// case CMARK_NODE_EMPH:
+// return "CMARK_NODE_EMPH";
+// case CMARK_NODE_STRONG:
+// return "CMARK_NODE_STRONG";
+// case CMARK_NODE_LINK:
+// return "CMARK_NODE_LINK";
+// case CMARK_NODE_IMAGE:
+// return "CMARK_NODE_IMAGE";
+// }
+// return "UNKNOWN";
+//};
+
+struct RenderState
+{
+ QTextCursor cursor;
+
+ QUrl root_url;
+ DocumentStyle const *style;
+ DocumentOutlineModel *outline;
+ TextStyleInstance text_style;
+};
+
+static void renderNode(RenderState &state, cmark_node const &node);
+
+static void renderChildren(RenderState &state, cmark_node const & node)
+{
+ for (auto child = node.first_child; child != nullptr; child = child->next)
+ {
+ renderNode(state, *child);
+ }
+}
+
+/* Leaf Nodes
+* * CMARK_NODE_HTML_BLOCK
+* * CMARK_NODE_THEMATIC_BREAK
+* * CMARK_NODE_CODE_BLOCK
+* * CMARK_NODE_TEXT
+* * CMARK_NODE_SOFTBREAK
+* * CMARK_NODE_LINEBREAK
+* * CMARK_NODE_CODE
+* * CMARK_NODE_HTML_INLINE
+*/
+
+static QString extractNodeText(cmark_node const &node)
+{
+ return QString::fromUtf8((char const*)node.data, node.len);
+}
+
+static void renderNode(RenderState &state, cmark_node const & node)
+{
+ auto & cursor = state.cursor;
+
+ switch (node.type)
+ {
+ case CMARK_NODE_DOCUMENT:
+ {
+ renderChildren(state, node);
+ break;
+ }
+
+ case CMARK_NODE_BLOCK_QUOTE:
+ {
+ qDebug() << "CMARK_NODE_BLOCK_QUOTE";
+ break;
+ }
+ case CMARK_NODE_LIST:
+ {
+ auto fmt = cursor.blockFormat();
+ cursor.insertList(QTextListFormat::ListDisc);
+ renderChildren(state, node);
+ cursor.setBlockFormat(fmt);
+ break;
+ }
+ case CMARK_NODE_ITEM:
+ {
+ renderChildren(state, node);
+ break;
+ }
+ case CMARK_NODE_CODE_BLOCK:
+ {
+ qDebug() << "CMARK_NODE_CODE_BLOCK";
+ break;
+ }
+ case CMARK_NODE_HTML_BLOCK:
+ {
+ qDebug() << "CMARK_NODE_HTML_BLOCK";
+ break;
+ }
+ case CMARK_NODE_CUSTOM_BLOCK:
+ {
+ qDebug() << "CMARK_NODE_CUSTOM_BLOCK";
+ break;
+ }
+ case CMARK_NODE_PARAGRAPH:
+ {
+ cursor.insertBlock();
+ renderChildren(state, node);
+ break;
+ }
+ case CMARK_NODE_HEADING:
+ {
+ cursor.insertBlock();
+ switch(node.as.heading.level) {
+ case 1: cursor.setCharFormat(state.text_style.standard_h1); break;
+ case 2: cursor.setCharFormat(state.text_style.standard_h2); break;
+ case 3: cursor.setCharFormat(state.text_style.standard_h3); break;
+ case 4: cursor.setCharFormat(state.text_style.standard_h3); break;
+ case 5: cursor.setCharFormat(state.text_style.standard_h3); break;
+ case 6: cursor.setCharFormat(state.text_style.standard_h3); break;
+
+ default: qDebug() << "heading" << node.as.heading.level; break;
+ }
+
+ switch(node.as.heading.level) {
+ case 1: state.outline->appendH1("Unknown H1", QString { }); break;
+ case 2: state.outline->appendH2("Unknown H2", QString { }); break;
+ case 3: state.outline->appendH3("Unknown H3", QString { }); break;
+ }
+
+ renderChildren(state, node);
+ cursor.setCharFormat(state.text_style.standard);
+ break;
+ }
+ case CMARK_NODE_THEMATIC_BREAK:
+ {
+ qDebug() << "CMARK_NODE_THEMATIC_BREAK";
+ break;
+ }
+ case CMARK_NODE_TEXT:
+ {
+ cursor.insertText(extractNodeText(node));
+ break;
+ }
+ case CMARK_NODE_SOFTBREAK:
+ {
+ qDebug() << "CMARK_NODE_SOFTBREAK";
+ break;
+ }
+ case CMARK_NODE_LINEBREAK:
+ {
+ qDebug() << "CMARK_NODE_LINEBREAK";
+ break;
+ }
+ case CMARK_NODE_CODE:
+ {
+ qDebug() << "CMARK_NODE_CODE";
+ break;
+ }
+ case CMARK_NODE_HTML_INLINE:
+ {
+ qDebug() << "CMARK_NODE_HTML_INLINE";
+ break;
+ }
+ case CMARK_NODE_CUSTOM_INLINE:
+ {
+ qDebug() << "CMARK_NODE_CUSTOM_INLINE";
+ break;
+ }
+ case CMARK_NODE_EMPH:
+ {
+ qDebug() << "CMARK_NODE_EMPH";
+ break;
+ }
+ case CMARK_NODE_STRONG:
+ {
+ qDebug() << "CMARK_NODE_STRONG";
+ break;
+ }
+ case CMARK_NODE_LINK:
+ {
+ // TODO: Implementing linking
+ // cursor.insertText(QString::fromUtf8((char*)node.as.link.title));
+ // qDebug() << "CMARK_NODE_LINK" << (char*)node.as.link.url;
+ renderChildren(state, node);
+ break;
+ }
+ case CMARK_NODE_IMAGE:
+ {
+ qDebug() << "CMARK_NODE_IMAGE";
+ break;
+ }
+ }
+}
+
+std::unique_ptr<QTextDocument> MarkdownRenderer::render(
+ QByteArray const &input,
+ QUrl const &root_url,
+ DocumentStyle const &style,
+ DocumentOutlineModel &outline)
+{
+
+ std::unique_ptr<cmark_node, decltype(&cmark_node_free)> md_root{
+ cmark_parse_document(input.data(), input.size(), 0),
+ &cmark_node_free,
+ };
+ if (not md_root)
+ return nullptr;
+
+ qDebug() << md_root.get();
+
+ auto doc = std::make_unique<QTextDocument>();
+ doc->setDocumentMargin(style.margin);
+ doc->setIndentWidth(20);
+
+ outline.beginBuild();
+
+ RenderState state = {
+ QTextCursor { doc.get() },
+ root_url,
+ &style,
+ &outline,
+ TextStyleInstance { style },
+ };
+
+ renderNode(state, *md_root);
+
+ outline.endBuild();
+
+ return doc;
+}
diff --git a/src/renderers/markdownrenderer.hpp b/src/renderers/markdownrenderer.hpp
new file mode 100644
index 0000000..7959378
--- /dev/null
+++ b/src/renderers/markdownrenderer.hpp
@@ -0,0 +1,29 @@
+#ifndef MARKDOWNRENDERER_HPP
+#define MARKDOWNRENDERER_HPP
+
+
+#include "documentstyle.hpp"
+#include "documentoutlinemodel.hpp"
+
+#include <memory>
+#include <QTextDocument>
+
+struct MarkdownRenderer
+{
+ MarkdownRenderer() = delete;
+
+
+ //! Renders the given byte sequence into a GeminiDocument.
+ //! @param input The utf8 encoded input string
+ //! @param root_url The url that is used to resolve relative links
+ //! @param style The style which is used to render the document
+ //! @param outline The extracted outline from the document
+ static std::unique_ptr<QTextDocument> render(
+ QByteArray const & input,
+ QUrl const & root_url,
+ DocumentStyle const & style,
+ DocumentOutlineModel & outline
+ );
+};
+
+#endif // MARKDOWNRENDERER_HPP
diff --git a/src/renderers/textstyleinstance.cpp b/src/renderers/textstyleinstance.cpp
new file mode 100644
index 0000000..e7295b0
--- /dev/null
+++ b/src/renderers/textstyleinstance.cpp
@@ -0,0 +1,28 @@
+#include "textstyleinstance.hpp"
+
+TextStyleInstance::TextStyleInstance(DocumentStyle const & themed_style)
+{
+ preformatted.setFont(themed_style.preformatted_font);
+ preformatted.setForeground(themed_style.preformatted_color);
+
+ standard.setFont(themed_style.standard_font);
+ standard.setForeground(themed_style.standard_color);
+
+ standard_link.setFont(themed_style.standard_font);
+ standard_link.setForeground(QBrush(themed_style.internal_link_color));
+
+ external_link.setFont(themed_style.standard_font);
+ external_link.setForeground(QBrush(themed_style.external_link_color));
+
+ cross_protocol_link.setFont(themed_style.standard_font);
+ cross_protocol_link.setForeground(QBrush(themed_style.cross_scheme_link_color));
+
+ standard_h1.setFont(themed_style.h1_font);
+ standard_h1.setForeground(QBrush(themed_style.h1_color));
+
+ standard_h2.setFont(themed_style.h2_font);
+ standard_h2.setForeground(QBrush(themed_style.h2_color));
+
+ standard_h3.setFont(themed_style.h3_font);
+ standard_h3.setForeground(QBrush(themed_style.h3_color));
+}
diff --git a/src/renderers/textstyleinstance.hpp b/src/renderers/textstyleinstance.hpp
new file mode 100644
index 0000000..9a1b4bb
--- /dev/null
+++ b/src/renderers/textstyleinstance.hpp
@@ -0,0 +1,22 @@
+#ifndef TEXTSTYLEINSTANCE_HPP
+#define TEXTSTYLEINSTANCE_HPP
+
+#include <QTextCharFormat>
+
+#include "documentstyle.hpp"
+
+struct TextStyleInstance
+{
+ QTextCharFormat preformatted;
+ QTextCharFormat standard;
+ QTextCharFormat standard_link;
+ QTextCharFormat external_link;
+ QTextCharFormat cross_protocol_link;
+ QTextCharFormat standard_h1;
+ QTextCharFormat standard_h2;
+ QTextCharFormat standard_h3;
+
+ explicit TextStyleInstance(DocumentStyle const & style);
+};
+
+#endif // TEXTSTYLEINSTANCE_HPP