From 02e762d964e9f6131676d03309d034af2ce4d623 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Thu, 23 Sep 2021 18:05:12 +0200 Subject: [PATCH] em-format: Format text/markdown with cmark library Use the cmark library to format text/markdown into HTML and show it as such, instead of the raw markdown. The dependency on cmark is optional and can be turned off in the configure time. Related to https://gitlab.gnome.org/GNOME/evolution/-/issues/449 --- CMakeLists.txt | 13 ++ config.h.in | 3 + po/POTFILES.in | 1 + src/em-format/CMakeLists.txt | 9 + .../e-mail-formatter-text-markdown.c | 186 ++++++++++++++++++ src/em-format/e-mail-formatter.c | 8 + src/em-format/e-mail-parser-text-markdown.c | 88 +++++++++ src/em-format/e-mail-parser.c | 6 + 8 files changed, 314 insertions(+) create mode 100644 src/em-format/e-mail-formatter-text-markdown.c create mode 100644 src/em-format/e-mail-parser-text-markdown.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a5eb1278b..f2c757d045 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -705,6 +705,19 @@ if(WITH_GLADE_CATALOG) pkg_check_modules_for_option(WITH_GLADE_CATALOG "Glade 3 catalog files" GLADEUI gladeui-2.0>=${gladeui_minimum_version}) endif(WITH_GLADE_CATALOG) +# ****************************** +# cmark for CommonMarkdown +# ****************************** + +add_printable_option(ENABLE_MARKDOWN "Enable markdown support" ON) + +if(ENABLE_MARKDOWN) + pkg_check_modules_for_option(ENABLE_MARKDOWN "markdown support" MARKDOWN + libcmark + ) + set(HAVE_MARKDOWN ON) +endif(ENABLE_MARKDOWN) + # Generate the ${PROJECT_NAME}-config.h file CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/config.h.in ${CMAKE_BINARY_DIR}/${PROJECT_NAME}-config.h) diff --git a/config.h.in b/config.h.in index f2732f51ea..d1d13dac48 100644 --- a/config.h.in +++ b/config.h.in @@ -119,3 +119,6 @@ /* D-Bus service name of the evolution-source-registry, as provided by evolution-data-server-1.2.pc */ #define EDS_SOURCES_DBUS_SERVICE_NAME "@EDS_SOURCES_DBUS_SERVICE_NAME@" + +/* Define if markdown support is enabled */ +#cmakedefine HAVE_MARKDOWN 1 diff --git a/po/POTFILES.in b/po/POTFILES.in index 1eb44ae079..741155c552 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -141,6 +141,7 @@ src/em-format/e-mail-formatter-secure-button.c src/em-format/e-mail-formatter-source.c src/em-format/e-mail-formatter-text-enriched.c src/em-format/e-mail-formatter-text-html.c +src/em-format/e-mail-formatter-text-markdown.c src/em-format/e-mail-formatter-text-plain.c src/em-format/e-mail-formatter-utils.c src/em-format/e-mail-parser-application-mbox.c diff --git a/src/em-format/CMakeLists.txt b/src/em-format/CMakeLists.txt index 6e32d239de..9b215d4052 100644 --- a/src/em-format/CMakeLists.txt +++ b/src/em-format/CMakeLists.txt @@ -78,6 +78,13 @@ if(ENABLE_SMIME) ) endif(ENABLE_SMIME) +if(ENABLE_MARKDOWN) + list(APPEND SOURCES + e-mail-formatter-text-markdown.c + e-mail-parser-text-markdown.c + ) +endif(ENABLE_MARKDOWN) + set(HEADERS e-mail-extension-registry.h e-mail-formatter-extension.h @@ -130,12 +137,14 @@ target_include_directories(evolution-mail-formatter PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${EVOLUTION_DATA_SERVER_INCLUDE_DIRS} ${GNOME_PLATFORM_INCLUDE_DIRS} + ${MARKDOWN_INCLUDE_DIRS} ) target_link_libraries(evolution-mail-formatter ${DEPENDENCIES} ${EVOLUTION_DATA_SERVER_LDFLAGS} ${GNOME_PLATFORM_LDFLAGS} + ${MARKDOWN_LDFLAGS} ) install(TARGETS evolution-mail-formatter diff --git a/src/em-format/e-mail-formatter-text-markdown.c b/src/em-format/e-mail-formatter-text-markdown.c new file mode 100644 index 0000000000..015d41bc6a --- /dev/null +++ b/src/em-format/e-mail-formatter-text-markdown.c @@ -0,0 +1,186 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see . + * + */ + +#include "evolution-config.h" + +#include +#include + +#include + +#include "e-mail-formatter-extension.h" +#include "e-mail-inline-filter.h" +#include "e-mail-part-utils.h" + +typedef EMailFormatterExtension EMailFormatterTextMarkdown; +typedef EMailFormatterExtensionClass EMailFormatterTextMarkdownClass; + +GType e_mail_formatter_text_markdown_get_type (void); + +G_DEFINE_TYPE (EMailFormatterTextMarkdown, e_mail_formatter_text_markdown, E_TYPE_MAIL_FORMATTER_EXTENSION) + +static const gchar *formatter_mime_types[] = { + "text/markdown", + NULL +}; + +static gboolean +emfe_text_markdown_format (EMailFormatterExtension *extension, + EMailFormatter *formatter, + EMailFormatterContext *context, + EMailPart *part, + GOutputStream *stream, + GCancellable *cancellable) +{ + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + if ((context->mode == E_MAIL_FORMATTER_MODE_RAW) || + (context->mode == E_MAIL_FORMATTER_MODE_PRINTING)) { + CamelMimePart *mime_part; + CamelDataWrapper *dw; + GOutputStream *output_stream; + gchar *html; + + mime_part = e_mail_part_ref_mime_part (part); + dw = camel_medium_get_content (CAMEL_MEDIUM (mime_part)); + if (dw == NULL) { + g_object_unref (mime_part); + return FALSE; + } + + output_stream = g_memory_output_stream_new_resizable (); + + e_mail_formatter_format_text (formatter, part, output_stream, cancellable); + g_output_stream_flush (output_stream, cancellable, NULL); + + html = cmark_markdown_to_html ((const gchar *) g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (output_stream)), + g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (output_stream)), + CMARK_OPT_VALIDATE_UTF8); + g_object_unref (output_stream); + g_object_unref (mime_part); + + if (html) { + const gchar *string; + + if (context->mode == E_MAIL_FORMATTER_MODE_RAW) { + string = e_mail_formatter_get_sub_html_header (formatter); + + g_output_stream_write_all ( + stream, string, strlen (string), + NULL, cancellable, NULL); + + /* No need for body margins within " + "", + e_mail_part_get_id (part), + e_mail_part_get_id (part), + uri, + e_mail_part_get_frame_security_style (part)); + + g_output_stream_write_all ( + stream, str, strlen (str), + NULL, cancellable, NULL); + + g_free (str); + g_free (uri); + } + + return TRUE; +} + +static void +e_mail_formatter_text_markdown_class_init (EMailFormatterExtensionClass *class) +{ + class->display_name = _("Markdown Text"); + class->description = _("Format part as markdown text"); + class->mime_types = formatter_mime_types; + class->priority = G_PRIORITY_LOW; + class->format = emfe_text_markdown_format; +} + +static void +e_mail_formatter_text_markdown_init (EMailFormatterExtension *extension) +{ +} diff --git a/src/em-format/e-mail-formatter.c b/src/em-format/e-mail-formatter.c index 1933a690f1..53717f7371 100644 --- a/src/em-format/e-mail-formatter.c +++ b/src/em-format/e-mail-formatter.c @@ -15,6 +15,8 @@ * */ +#include "evolution-config.h" + #include "e-mail-formatter.h" #include @@ -72,6 +74,9 @@ GType e_mail_formatter_source_get_type (void); GType e_mail_formatter_text_enriched_get_type (void); GType e_mail_formatter_text_html_get_type (void); GType e_mail_formatter_text_plain_get_type (void); +#ifdef HAVE_MARKDOWN +GType e_mail_formatter_text_markdown_get_type (void); +#endif static gpointer e_mail_formatter_parent_class = 0; @@ -561,6 +566,9 @@ e_mail_formatter_base_init (EMailFormatterClass *class) g_type_ensure (e_mail_formatter_text_enriched_get_type ()); g_type_ensure (e_mail_formatter_text_html_get_type ()); g_type_ensure (e_mail_formatter_text_plain_get_type ()); +#ifdef HAVE_MARKDOWN + g_type_ensure (e_mail_formatter_text_markdown_get_type ()); +#endif class->extension_registry = g_object_new ( E_TYPE_MAIL_FORMATTER_EXTENSION_REGISTRY, NULL); diff --git a/src/em-format/e-mail-parser-text-markdown.c b/src/em-format/e-mail-parser-text-markdown.c new file mode 100644 index 0000000000..07b7c3a307 --- /dev/null +++ b/src/em-format/e-mail-parser-text-markdown.c @@ -0,0 +1,88 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see . + * + */ + +#include "evolution-config.h" + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserTextMarkdown; +typedef EMailParserExtensionClass EMailParserTextMarkdownClass; + +GType e_mail_parser_text_markdown_get_type (void); + +G_DEFINE_TYPE (EMailParserTextMarkdown, e_mail_parser_text_markdown, E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "text/markdown", + NULL +}; + +static gboolean +empe_text_markdown_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelContentType *type; + EMailPart *mail_part; + GQueue work_queue = G_QUEUE_INIT; + gint s_len = part_id->len; + gchar *mime_type; + gboolean is_attachment; + + if (!camel_medium_get_content ((CamelMedium *) part)) + return FALSE; + + is_attachment = e_mail_part_is_attachment (part); + + type = camel_mime_part_get_content_type (part); + if (!camel_content_type_is (type, "text", "markdown")) + return FALSE; + + g_string_append_printf (part_id, ".markdown_text.%d", 0); + + mail_part = e_mail_part_new (part, part_id->str); + + mime_type = camel_content_type_simple (type); + e_mail_part_set_mime_type (mail_part, mime_type); + g_free (mime_type); + + g_string_truncate (part_id, s_len); + + g_queue_push_tail (&work_queue, mail_part); + + if (is_attachment) + e_mail_parser_wrap_as_attachment (parser, part, part_id, &work_queue); + + e_queue_transfer (&work_queue, out_mail_parts); + + return TRUE; +} + +static void +e_mail_parser_text_markdown_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_text_markdown_parse; +} + +static void +e_mail_parser_text_markdown_init (EMailParserExtension *extension) +{ +} diff --git a/src/em-format/e-mail-parser.c b/src/em-format/e-mail-parser.c index c0e6a4c8cc..ad7c1d56ce 100644 --- a/src/em-format/e-mail-parser.c +++ b/src/em-format/e-mail-parser.c @@ -76,6 +76,9 @@ GType e_mail_parser_text_plain_get_type (void); #ifdef ENABLE_SMIME GType e_mail_parser_application_smime_get_type (void); #endif +#ifdef HAVE_MARKDOWN +GType e_mail_parser_text_markdown_get_type (void); +#endif static gpointer parent_class; @@ -323,6 +326,9 @@ e_mail_parser_base_init (EMailParserClass *class) #ifdef ENABLE_SMIME g_type_ensure (e_mail_parser_application_smime_get_type ()); #endif +#ifdef HAVE_MARKDOWN + g_type_ensure (e_mail_parser_text_markdown_get_type ()); +#endif class->extension_registry = g_object_new ( E_TYPE_MAIL_PARSER_EXTENSION_REGISTRY, NULL);