#include "markdown.hpp" #include "coders/commons.hpp" #include "graphics/core/Font.hpp" using namespace markdown; template static inline void emit( CharT c, FontStylesScheme& styles, std::basic_stringstream& ss ) { ss << c; styles.map.emplace_back(styles.palette.size() - 1); } template static inline void emit_md( CharT c, FontStylesScheme& styles, std::basic_stringstream& ss ) { ss << c; styles.map.emplace_back(0); } template static glm::vec4 parse_color(const std::basic_string_view& color_code) { if (color_code.size() != 8 || color_code[0] != '#') { return glm::vec4(1, 1, 1, 1); // default to white } auto hex_to_float = [](char high, char low) { int high_val = hexchar2int(high); int low_val = hexchar2int(low); if (high_val == -1 || low_val == -1) { return 1.0f; // default to max value on error } return (high_val * 16 + low_val) / 255.0f; }; return glm::vec4( hex_to_float(color_code[1], color_code[2]), hex_to_float(color_code[3], color_code[4]), hex_to_float(color_code[5], color_code[6]), 1 ); } template static inline void apply_color( const std::basic_string_view& color_code, FontStylesScheme& styles ) { FontStyle style = styles.palette.back(); style.color = parse_color(color_code); styles.palette.push_back(style); } template static inline void restyle( CharT c, FontStyle& style, FontStylesScheme& styles, std::basic_stringstream& ss, int& pos, bool eraseMarkdown ) { styles.palette.push_back(style); if (!eraseMarkdown) { emit_md(c, styles, ss); } pos++; } template Result process_markdown( std::basic_string_view source, bool eraseMarkdown ) { std::basic_stringstream ss; FontStylesScheme styles { // markdown default {{false, false, false, false, glm::vec4(1, 1, 1, 0.5f)}, {}}, {} }; FontStyle style; int pos = 0; while (pos < source.size()) { CharT first = source[pos]; if (first == '\\') { if (pos + 1 < source.size()) { CharT second = source[++pos]; switch (second) { case '*': case '_': case '~': if (!eraseMarkdown) { emit_md(first, styles, ss); } emit(second, styles, ss); pos++; continue; case '[': if (pos + 9 < source.size() && source[pos + 1] == '#' && source[pos + 8] == ']') { if (!eraseMarkdown) { emit_md(source[pos - 1], styles, ss); } for (int i = 0; i < 10; ++i) { emit(source[pos + i], styles, ss); } pos += 10; continue; } } pos--; } } else if (first == '[' && pos + 9 <= source.size() && source[pos + 1] == '#' && source[pos + 8] == ']') { std::basic_string_view color_code = source.substr(pos + 1, 8); apply_color(color_code, styles); if (!eraseMarkdown) { for (int i = 0; i < 9; ++i) { emit_md(source[pos + i], styles, ss); } } pos += 9; // Skip past the color code continue; } else if (first == '*') { if (pos + 1 < source.size() && source[pos + 1] == '*') { pos++; if (!eraseMarkdown) emit_md(first, styles, ss); style.bold = !style.bold; restyle(first, style, styles, ss, pos, eraseMarkdown); continue; } style.italic = !style.italic; restyle(first, style, styles, ss, pos, eraseMarkdown); continue; } else if (first == '_' && pos + 1 < source.size() && source[pos + 1] == '_') { pos++; if (!eraseMarkdown) emit_md(first, styles, ss); style.underline = !style.underline; restyle(first, style, styles, ss, pos, eraseMarkdown); continue; } else if (first == '~' && pos + 1 < source.size() && source[pos + 1] == '~') { pos++; if (!eraseMarkdown) emit_md(first, styles, ss); style.strikethrough = !style.strikethrough; restyle(first, style, styles, ss, pos, eraseMarkdown); continue; } if (first == '\n') { styles.palette.push_back(styles.palette.at(1)); } emit(first, styles, ss); pos++; } return {ss.str(), std::make_unique(std::move(styles))}; } Result markdown::process(std::string_view source, bool eraseMarkdown) { return process_markdown(source, eraseMarkdown); } Result markdown::process( std::wstring_view source, bool eraseMarkdown ) { return process_markdown(source, eraseMarkdown); }