[evolution] em-format: Format text/markdown with cmark library



commit 02e762d964e9f6131676d03309d034af2ce4d623
Author: Milan Crha <mcrha redhat com>
Date:   Thu Sep 23 18:05:12 2021 +0200

    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 ++
 src/em-format/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(+)
---
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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evolution-config.h"
+
+#include <cmark.h>
+#include <glib/gi18n-lib.h>
+
+#include <e-util/e-util.h>
+
+#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 <iframe> */
+                               string = "<style>body{ margin: 0; }</style>";
+
+                               g_output_stream_write_all (
+                                       stream, string, strlen (string),
+                                       NULL, cancellable, NULL);
+                       }
+
+                       string =
+                               "<div class=\"part-container "
+                               "-e-web-view-background-color -e-web-view-text-color\" "
+                               "style=\"border: none; padding: 8px; margin: 0;\">";
+
+                       g_output_stream_write_all (
+                               stream, string, strlen (string),
+                               NULL, cancellable, NULL);
+
+                       g_output_stream_write_all (stream, html, strlen (html), NULL, cancellable, NULL);
+                       g_free (html);
+
+                       string = "</div>\n";
+
+                       g_output_stream_write_all (
+                               stream, string, strlen (string),
+                               NULL, cancellable, NULL);
+
+                       if (context->mode == E_MAIL_FORMATTER_MODE_RAW) {
+                               string = "</body></html>";
+
+                               g_output_stream_write_all (
+                                       stream, string, strlen (string),
+                                       NULL, cancellable, NULL);
+                       }
+
+                       return TRUE;
+               }
+
+               return FALSE;
+
+       } else {
+               CamelFolder *folder;
+               const gchar *message_uid;
+               gchar *uri, *str;
+               const gchar *default_charset, *charset;
+
+               folder = e_mail_part_list_get_folder (context->part_list);
+               message_uid = e_mail_part_list_get_message_uid (context->part_list);
+               default_charset = e_mail_formatter_get_default_charset (formatter);
+               charset = e_mail_formatter_get_charset (formatter);
+
+               if (!default_charset)
+                       default_charset = "";
+               if (!charset)
+                       charset = "";
+
+               uri = e_mail_part_build_uri (
+                       folder, message_uid,
+                       "part_id", G_TYPE_STRING, e_mail_part_get_id (part),
+                       "mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_RAW,
+                       "formatter_default_charset", G_TYPE_STRING, default_charset,
+                       "formatter_charset", G_TYPE_STRING, charset,
+                       NULL);
+
+               str = g_strdup_printf (
+                       "<div class=\"part-container-nostyle\" >"
+                       "<iframe width=\"100%%\" height=\"10\""
+                       " id=\"%s.iframe\" name=\"%s\" "
+                       " frameborder=\"0\" src=\"%s\" "
+                       " class=\"-e-mail-formatter-frame-color %s"
+                       " -e-web-view-text-color\" >"
+                       "</iframe>"
+                       "</div>",
+                       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 <string.h>
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]