diff options
| author | Felix (xq) Queißner <git@mq32.de> | 2020-06-23 00:35:41 +0200 |
|---|---|---|
| committer | Felix (xq) Queißner <git@mq32.de> | 2020-06-23 00:35:41 +0200 |
| commit | 0860a8ec32fd01b6fdea92e1e6a80de096e2e694 (patch) | |
| tree | b858ca1d88bad6050f51d9a413b48f74b1a21545 /src | |
| parent | 75ec461eeaa851cb5c53f4cfffc434e3e529ed1d (diff) | |
| download | kristall-0860a8ec32fd01b6fdea92e1e6a80de096e2e694.tar.gz | |
Starts to implement new markdown rendering
Diffstat (limited to 'src')
| -rw-r--r-- | src/browsertab.cpp | 13 | ||||
| -rw-r--r-- | src/kristall.pro | 4 | ||||
| -rw-r--r-- | src/renderers/geminirenderer.cpp | 62 | ||||
| -rw-r--r-- | src/renderers/markdownrenderer.cpp | 267 | ||||
| -rw-r--r-- | src/renderers/markdownrenderer.hpp | 29 | ||||
| -rw-r--r-- | src/renderers/textstyleinstance.cpp | 28 | ||||
| -rw-r--r-- | src/renderers/textstyleinstance.hpp | 22 |
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 |
