From: Fletcher T. Penney Date: Fri, 3 Mar 2017 15:33:29 +0000 (-0500) Subject: ADDED: Basic ODF Support X-Git-Tag: 0.4.0-b^2~16 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3f93527ab78bf5adaa9bed331e91743c1dcb275a;p=multimarkdown ADDED: Basic ODF Support --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 49b5f1f..b11a4d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,6 +180,7 @@ set(src_files Sources/libMultiMarkdown/lexer.c Sources/libMultiMarkdown/memoir.c Sources/libMultiMarkdown/mmd.c + Sources/libMultiMarkdown/odf.c Sources/libMultiMarkdown/object_pool.c Sources/libMultiMarkdown/parser.c Sources/libMultiMarkdown/rng.c @@ -202,6 +203,7 @@ set(header_files Sources/libMultiMarkdown/include/libMultiMarkdown.h Sources/libMultiMarkdown/memoir.h Sources/libMultiMarkdown/mmd.h + Sources/libMultiMarkdown/odf.c Sources/libMultiMarkdown/object_pool.h Sources/libMultiMarkdown/scanners.h Sources/libMultiMarkdown/stack.h @@ -563,6 +565,8 @@ ADD_MMD_TEST(mmd-6-compat "-c" MMD6Tests htmlc) ADD_MMD_TEST(mmd-6-latex "-t latex" MMD6Tests tex) +ADD_MMD_TEST(mmd-6-odf "-t odf" MMD6Tests fodt) + ADD_MMD_TEST(pathologic "" ../build html) ADD_MMD_TEST(pathologic-compat "-c" ../build html) diff --git a/Sources/libMultiMarkdown/html.c b/Sources/libMultiMarkdown/html.c index 6c63fad..6aafb42 100644 --- a/Sources/libMultiMarkdown/html.c +++ b/Sources/libMultiMarkdown/html.c @@ -955,11 +955,18 @@ void mmd_export_token_html(DString * out, const char * source, token * t, size_t temp_char = url_accept(source, t->start + 1, t->len - 2, NULL, true); if (temp_char) { - if (scan_email(temp_char)) + print_const(""); mmd_print_string_html(out, temp_char, temp_bool); @@ -1263,10 +1270,10 @@ void mmd_export_token_html(DString * out, const char * source, token * t, size_t mmd_export_token_tree_html(out, source, t->child, offset, scratch); break; case PAREN_LEFT: - print_const("("); + print_char('('); break; case PAREN_RIGHT: - print_const(")"); + print_char(')'); break; case PIPE: print_token(t); diff --git a/Sources/libMultiMarkdown/latex.c b/Sources/libMultiMarkdown/latex.c index 9633609..b0e5875 100644 --- a/Sources/libMultiMarkdown/latex.c +++ b/Sources/libMultiMarkdown/latex.c @@ -110,9 +110,9 @@ void mmd_print_char_latex(DString * out, char c) { void mmd_print_string_latex(DString * out, const char * str) { - if (str == NULL) - return; - + if (str == NULL) + return; + while (*str != '\0') { mmd_print_char_latex(out, *str); str++; @@ -910,13 +910,15 @@ void mmd_export_token_latex(DString * out, const char * source, token * t, scrat temp_char = url_accept(source, t->start + 1, t->len - 2, NULL, true); if (temp_char) { + print_const("\\href{"); + if (scan_email(temp_char)) { - print_const("\\href{mailto:"); - print(temp_char); - } else { - print_const("\\href{"); - print(temp_char); + if (strncmp("mailto:", temp_char, 7) != 0) { + print_const("mailto:"); + } } + + print(temp_char); print_const("}{"); mmd_print_string_latex(out, temp_char); print_const("}"); @@ -1023,32 +1025,32 @@ void mmd_export_token_latex(DString * out, const char * source, token * t, scrat case PAIR_BRACKET_CITATION: parse_citation: temp_bool = true; // Track whether this is a 'not cited' - temp_token = t; // Remember whether we need to skip ahead - - if (scratch->extensions & EXT_NOTES) { - if (t->type == PAIR_BRACKET) { - // This is a locator for subsequent citation (e.g. `[foo][#bar]` - temp_char = text_inside_pair(source, t); - temp_char2 = label_from_string(temp_char); - - if (strcmp(temp_char2, "notcited") == 0) { - free(temp_char); - temp_char = strdup(""); - temp_bool = false; - } - - free(temp_char2); - - // Process the actual citation - t = t->next; - } else { - // This is just a citation (e.g. `[#foo]`) - temp_char = strdup(""); - } - - // See if we're a citep or cite - temp_char2 = clean_inside_pair(source, t, false); - + temp_token = t; // Remember whether we need to skip ahead + + if (scratch->extensions & EXT_NOTES) { + if (t->type == PAIR_BRACKET) { + // This is a locator for subsequent citation (e.g. `[foo][#bar]` + temp_char = text_inside_pair(source, t); + temp_char2 = label_from_string(temp_char); + + if (strcmp(temp_char2, "notcited") == 0) { + free(temp_char); + temp_char = strdup(""); + temp_bool = false; + } + + free(temp_char2); + + // Process the actual citation + t = t->next; + } else { + // This is just a citation (e.g. `[#foo]`) + temp_char = strdup(""); + } + + // See if we're a citep or cite + temp_char2 = clean_inside_pair(source, t, false); + citation_from_bracket(source, scratch, t, &temp_short); temp_note = stack_peek_index(scratch->used_citations, temp_short - 1); @@ -1057,11 +1059,11 @@ void mmd_export_token_latex(DString * out, const char * source, token * t, scrat // This is not a "not cited" if (temp_char[0] == '\0') { // No locator - if (temp_char2[strlen(temp_char2) - 1] == ';') { - print_const("\\citet"); - } else { - print_const("~\\citep"); - } + if (temp_char2[strlen(temp_char2) - 1] == ';') { + print_const("\\citet"); + } else { + print_const("~\\citep"); + } } else { // Locator present @@ -1074,11 +1076,11 @@ void mmd_export_token_latex(DString * out, const char * source, token * t, scrat memmove(temp_char3 + 1, temp_char3 + 3, strlen(temp_char3 - 3)); } - if (temp_char2[strlen(temp_char2) - 1] == ';') { - printf("\\citet[%s]", temp_char); - } else { - printf("~\\citep[%s]", temp_char); - } + if (temp_char2[strlen(temp_char2) - 1] == ';') { + printf("\\citet[%s]", temp_char); + } else { + printf("~\\citep[%s]", temp_char); + } } printf("{%s}", temp_note->label_text); @@ -1558,27 +1560,27 @@ void mmd_export_token_latex_tt(DString * out, const char * source, token * t, sc t->next->type = TEXT_EMPTY; case TEXT_EMPTY: break; - case SLASH: - print_const("\\slash "); - break; - case TEXT_BACKSLASH: - print_const("\\textbackslash{}"); - break; + case SLASH: + print_const("\\slash "); + break; + case TEXT_BACKSLASH: + print_const("\\textbackslash{}"); + break; case BRACE_DOUBLE_LEFT: print_const("\\{\\{"); break; case BRACE_DOUBLE_RIGHT: print_const("\\}\\}"); break; - case TEXT_BRACE_LEFT: + case TEXT_BRACE_LEFT: print_const("\\{"); break; - case TEXT_BRACE_RIGHT: + case TEXT_BRACE_RIGHT: print_const("\\}"); break; - case UL: - print_const("\\_"); - break; + case UL: + print_const("\\_"); + break; default: if (t->child) mmd_export_token_tree_latex_tt(out, source, t->child, scratch); diff --git a/Sources/libMultiMarkdown/odf.c b/Sources/libMultiMarkdown/odf.c new file mode 100644 index 0000000..8d4b0c6 --- /dev/null +++ b/Sources/libMultiMarkdown/odf.c @@ -0,0 +1,1031 @@ +/** + + MultiMarkdown -- Lightweight markup processor to produce HTML, LaTeX, and more. + + @file odf.c + + @brief Convert token tree to Flat OpenDocument (ODF/FODT) output + + + @author Fletcher T. Penney + @bug + +**/ + +/* + + Copyright © 2016 - 2017 Fletcher T. Penney. + + + The `MultiMarkdown 6` project is released under the MIT License.. + + GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project: + + https://github.com/fletcher/MultiMarkdown-4/ + + MMD 4 is released under both the MIT License and GPL. + + + CuTest is released under the zlib/libpng license. See CuTest.c for the + text of the license. + + + ## The MIT License ## + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +*/ + + +#include +#include +#include +#include + +#include "char.h" +#include "i18n.h" +#include "odf.h" +#include "parser.h" +#include "scanners.h" + +#define print(x) d_string_append(out, x) +#define print_const(x) d_string_append_c_array(out, x, sizeof(x) - 1) +#define print_char(x) d_string_append_c(out, x) +#define printf(...) d_string_append_printf(out, __VA_ARGS__) +#define print_token(t) d_string_append_c_array(out, &(source[t->start]), t->len) +#define print_localized(x) mmd_print_localized_char_odf(out, x, scratch) + + +void mmd_print_char_odf(DString * out, char c) { + switch (c) { + case '"': + print_const("""); + break; + case '&': + print_const("&"); + break; + case '<': + print_const("<"); + break; + case '>': + print_const(">"); + break; + default: + print_char(c); + break; + } +} + + +void mmd_print_string_odf(DString * out, const char * str) { + if (str == NULL) + return; + + while (*str != '\0') { + mmd_print_char_odf(out, *str); + str++; + } +} + + +void mmd_print_localized_char_odf(DString * out, unsigned short type, scratch_pad * scratch) { + switch (type) { + case DASH_N: + print_const("–"); + break; + case DASH_M: + print_const("—"); + break; + case ELLIPSIS: + print_const("…"); + break; + case APOSTROPHE: + print_const("’"); + break; + case QUOTE_LEFT_SINGLE: + switch (scratch->quotes_lang) { + case SWEDISH: + print( "’"); + break; + case FRENCH: + print_const("'"); + break; + case GERMAN: + print_const("‚"); + break; + case GERMANGUILL: + print_const("›"); + break; + default: + print_const("‘"); + } + break; + case QUOTE_RIGHT_SINGLE: + switch (scratch->quotes_lang) { + case GERMAN: + print_const("‘"); + break; + case GERMANGUILL: + print_const("‹"); + break; + default: + print_const("’"); + } + break; + case QUOTE_LEFT_DOUBLE: + switch (scratch->quotes_lang) { + case DUTCH: + case GERMAN: + print_const("„"); + break; + case GERMANGUILL: + print_const("»"); + break; + case FRENCH: + print_const("«"); + break; + case SWEDISH: + print( "”"); + break; + default: + print_const("“"); + } + break; + case QUOTE_RIGHT_DOUBLE: + switch (scratch->quotes_lang) { + case GERMAN: + print_const("“"); + break; + case GERMANGUILL: + print_const("«"); + break; + case FRENCH: + print_const("»"); + break; + case SWEDISH: + case DUTCH: + default: + print_const("”"); + } + break; + } +} + + +void mmd_export_link_odf(DString * out, const char * source, token * text, link * link, scratch_pad * scratch) { + attr * a = link->attributes; + + if (link->url) { + print_const("url); + print_const("\""); + } else + print_const("title && link->title[0] != '\0') { + print_const(" office:name=\""); + mmd_print_string_odf(out, link->title); + print_const("\""); + } + + while (a) { + print_const(" "); + print(a->key); + print_const("=\""); + print(a->value); + print_const("\""); + a = a->next; + } + + print_const(">"); + + // If we're printing contents of bracket as text, then ensure we include it all + if (text && text->child && text->child->len > 1) { + text->child->next->start--; + text->child->next->len++; + } + + mmd_export_token_tree_odf(out, source, text->child, scratch); + + print_const(""); +} + + +void mmd_export_image_odf(DString * out, const char * source, token * text, link * link, scratch_pad * scratch, bool is_figure) { + attr * a = link->attributes; + + // Compatibility mode doesn't allow figures + if (scratch->extensions & EXT_COMPATIBILITY) + is_figure = false; + + if (is_figure) { + // Remove wrapping

markers + d_string_erase(out, out->currentStringLength - 3, 3); + print_const("

\n"); + scratch->close_para = false; + } + + if (link->url) + printf("url); + else + print_const("\"");child); + print_const("\""); + } + + if (link->label && !(scratch->extensions & EXT_COMPATIBILITY)) { + // \todo: Need to decide on approach to id's + char * label = label_from_token(source, link->label); + printf(" id=\"%s\"", label); + free(label); + } + + if (link->title && link->title[0] != '\0') + printf(" title=\"%s\"", link->title); + + while (a) { + print_const(" "); + print(a->key); + print_const("=\""); + print(a->value); + print_const("\""); + a = a->next; + } + + print_const(" />"); + + if (is_figure) { + if (text) { + print_const("\n
"); + mmd_export_token_tree_odf(out, source, text->child, scratch); + print_const("
"); + } + print_const("\n
"); + } +} + + +void mmd_export_token_odf(DString * out, const char * source, token * t, scratch_pad * scratch) { + if (t == NULL) + return; + + short temp_short; + short temp_short2; + short temp_short3; + link * temp_link = NULL; + char * temp_char = NULL; + char * temp_char2 = NULL; + char * temp_char3 = NULL; + bool temp_bool = 0; + token * temp_token = NULL; + footnote * temp_note = NULL; + + switch (t->type) { + case DOC_START_TOKEN: + mmd_export_token_tree_odf(out, source, t->child, scratch); + break; + case AMPERSAND: + case AMPERSAND_LONG: + print_const("&"); + break; + case ANGLE_LEFT: + print_const("<"); + break; + case ANGLE_RIGHT: + print_const(">"); + break; + case APOSTROPHE: + if (!(scratch->extensions & EXT_SMART)) { + print_token(t); + } else { + print_localized(APOSTROPHE); + } + break; + case BACKTICK: + if (t->mate == NULL) + print_token(t); + else if (t->mate->type == QUOTE_RIGHT_ALT) + if (!(scratch->extensions & EXT_SMART)) { + print_token(t); + } else { + print_localized(QUOTE_LEFT_DOUBLE); + } + else if (t->start < t->mate->start) { + print_const(""); + } else { + print_const(""); + } + break; + case BLOCK_BLOCKQUOTE: + pad(out, 2, scratch); + scratch->padded = 2; + temp_short2 = scratch->odf_para_type; + + scratch->odf_para_type = BLOCK_BLOCKQUOTE; + + mmd_export_token_tree_odf(out, source, t->child, scratch); + scratch->padded = 0; + scratch->odf_para_type = temp_short2; + break; + case BLOCK_CODE_FENCED: + pad(out, 2, scratch); + print_const(""); + mmd_export_token_tree_odf_raw(out, source, t->child->next, scratch); + print_const(""); + scratch->padded = 0; + break; + case BLOCK_CODE_INDENTED: + pad(out, 2, scratch); + print_const(""); + mmd_export_token_tree_odf_raw(out, source, t->child, scratch); + print_const(""); + scratch->padded = 0; + break; + case BLOCK_EMPTY: + break; + case BLOCK_H1: + case BLOCK_H2: + case BLOCK_H3: + case BLOCK_H4: + case BLOCK_H5: + case BLOCK_H6: + case BLOCK_SETEXT_1: + case BLOCK_SETEXT_2: + pad(out, 2, scratch); + switch (t->type) { + case BLOCK_SETEXT_1: + temp_short = 1; + break; + case BLOCK_SETEXT_2: + temp_short = 2; + break; + default: + temp_short = t->type - BLOCK_H1 + 1; + } + + printf("", temp_short + scratch->base_header_level - 1); + + if (scratch->extensions & EXT_NO_LABELS) { + mmd_export_token_tree_odf(out, source, t->child, scratch); + } else { + temp_char = label_from_header(source, t); + printf("", temp_char); + mmd_export_token_tree_odf(out, source, t->child, scratch); + printf("", temp_char); + free(temp_char); + } + + print_const(""); + scratch->padded = 0; + break; + case BLOCK_LIST_BULLETED_LOOSE: + case BLOCK_LIST_BULLETED: + temp_short = scratch->list_is_tight; + switch (t->type) { + case BLOCK_LIST_BULLETED_LOOSE: + scratch->list_is_tight = false; + break; + case BLOCK_LIST_BULLETED: + scratch->list_is_tight = true; + break; + } + pad(out, 2, scratch); + print_const(""); + scratch->padded = 1; + mmd_export_token_tree_odf(out, source, t->child, scratch); + pad(out, 2, scratch); + print_const(""); + scratch->padded = 0; + scratch->list_is_tight = temp_short; + break; + case BLOCK_LIST_ENUMERATED_LOOSE: + case BLOCK_LIST_ENUMERATED: + temp_short = scratch->list_is_tight; + switch (t->type) { + case BLOCK_LIST_ENUMERATED_LOOSE: + scratch->list_is_tight = false; + break; + case BLOCK_LIST_ENUMERATED: + scratch->list_is_tight = true; + break; + } + pad(out, 2, scratch); + print_const(""); + scratch->padded = 1; + mmd_export_token_tree_odf(out, source, t->child, scratch); + pad(out, 2, scratch); + print_const(""); + scratch->padded = 0; + scratch->list_is_tight = temp_short; + break; + case BLOCK_LIST_ITEM: + pad(out, 2, scratch); + print_const("\n"); + scratch->padded = 2; + mmd_export_token_tree_odf(out, source, t->child, scratch); + print_const(""); + scratch->padded = 0; + break; + case BLOCK_LIST_ITEM_TIGHT: + pad(out, 2, scratch); + print_const("\n\n"); + scratch->padded = 2; + mmd_export_token_tree_odf(out, source, t->child, scratch); + print_const(""); + scratch->padded = 0; + break; + case BLOCK_PARA: + pad(out, 2, scratch); + print_const("odf_para_type) { + case BLOCK_BLOCKQUOTE: + print_const(" text:style-name=\"Quotations\">"); + break; + default: + print_const(" text:style-name=\"Standard\">"); + break; + } + + mmd_export_token_tree_odf(out, source, t->child, scratch); + + print_const(""); + scratch->padded = 0; + break; + case COLON: + print_char(':'); + break; + case EMPH_START: + print_const(""); + break; + case EMPH_STOP: + print_const(""); + break; + case EQUAL: + print_char('='); + break; + case INDENT_SPACE: + print_char(' '); + break; + case INDENT_TAB: + print_char('\t'); + break; + case LINE_LIST_BULLETED: + case LINE_LIST_ENUMERATED: + mmd_export_token_tree_odf(out, source, t->child, scratch); + break; + case MANUAL_LABEL: + case MARKER_BLOCKQUOTE: + case MARKER_H1: + case MARKER_H2: + case MARKER_H3: + case MARKER_H4: + case MARKER_H5: + case MARKER_H6: + case MARKER_LIST_BULLET: + case MARKER_LIST_ENUMERATOR: + break; + case NON_INDENT_SPACE: + print_char(' '); + break; + case PAIR_ANGLE: + temp_char = url_accept(source, t->start + 1, t->len - 2, NULL, true); + + if (temp_char) { + print_const(""); + mmd_print_string_odf(out, temp_char); + print_const(""); + } else if (scan_html(&source[t->start])) { + print_token(t); + } else { + mmd_export_token_tree_odf(out, source, t->child, scratch); + } + + free(temp_char); + break; + case PAIR_BACKTICK: + // Strip leading whitespace + switch (t->child->next->type) { + case TEXT_NL: + case INDENT_TAB: + case INDENT_SPACE: + case NON_INDENT_SPACE: + t->child->next->type = TEXT_EMPTY; + break; + case TEXT_PLAIN: + while (t->child->next->len && char_is_whitespace(source[t->child->next->start])) { + t->child->next->start++; + t->child->next->len--; + } + break; + } + + // Strip trailing whitespace + switch (t->child->mate->prev->type) { + case TEXT_NL: + case INDENT_TAB: + case INDENT_SPACE: + case NON_INDENT_SPACE: + t->child->mate->prev->type = TEXT_EMPTY; + break; + case TEXT_PLAIN: + while (t->child->mate->prev->len && char_is_whitespace(source[t->child->mate->prev->start + t->child->mate->prev->len - 1])) { + t->child->mate->prev->len--; + } + break; + } + t->child->type = TEXT_EMPTY; + t->child->mate->type = TEXT_EMPTY; + print_const(""); + mmd_export_token_tree_odf_raw(out, source, t->child, scratch); + print_const(""); + break; + case PAIR_BRACKET: + if ((scratch->extensions & EXT_NOTES) && + (t->next && t->next->type == PAIR_BRACKET_CITATION)) { +// goto parse_citation; + } + + case PAIR_BRACKET_IMAGE: + parse_brackets(source, scratch, t, &temp_link, &temp_short, &temp_bool); + + if (temp_link) { + if (t->type == PAIR_BRACKET) { + // Link + mmd_export_link_odf(out, source, t, temp_link, scratch); + } else { + // Image -- should it be a figure (e.g. image is only thing in paragraph)? + temp_token = t->next; + + if (temp_token && + ((temp_token->type == PAIR_BRACKET) || + (temp_token->type == PAIR_PAREN))) { + temp_token = temp_token->next; + } + + if (temp_token && temp_token->type == TEXT_NL) + temp_token = temp_token->next; + + if (temp_token && temp_token->type == TEXT_LINEBREAK) + temp_token = temp_token->next; + + if (t->prev || temp_token) { + mmd_export_image_odf(out, source, t, temp_link, scratch, false); + } else { + mmd_export_image_odf(out, source, t, temp_link, scratch, true); + } + } + + if (temp_bool) { + link_free(temp_link); + } + + scratch->skip_token = temp_short; + + return; + } + + // No links exist, so treat as normal + mmd_export_token_tree_odf(out, source, t->child, scratch); + break; + case PAIR_PAREN: + case PAIR_QUOTE_DOUBLE: + case PAIR_QUOTE_SINGLE: + case PAIR_STAR: + case PAIR_UL: + mmd_export_token_tree_odf(out, source, t->child, scratch); + break; + case PAREN_LEFT: + print_char('('); + break; + case PAREN_RIGHT: + print_char(')'); + break; + case SLASH: + print_char('/'); + break; + case STAR: + print_char('*'); + break; + case STRONG_START: + print_const(""); + break; + case STRONG_STOP: + print_const(""); + break; + case TEXT_EMPTY: + break; + case TEXT_LINEBREAK: + if (t->next) { + print_const("\n"); + scratch->padded = 0; + } + break; + case TEXT_NL: + if (t->next) + print_char('\n'); + break; + case TEXT_NUMBER_POSS_LIST: + case TEXT_PERIOD: + case TEXT_PLAIN: + print_token(t); + break; + default: + fprintf(stderr, "Unknown token type: %d\n", t->type); + token_describe(t, source); + break; + } +} + + +void mmd_export_token_tree_odf(DString * out, const char * source, token * t, scratch_pad * scratch) { + + // Prevent stack overflow with "dangerous" input causing extreme recursion + if (scratch->recurse_depth == kMaxExportRecursiveDepth) { + return; + } + + scratch->recurse_depth++; + + while (t != NULL) { + if (scratch->skip_token) { + scratch->skip_token--; + } else { + mmd_export_token_odf(out, source, t, scratch); + } + + t = t->next; + } + + scratch->recurse_depth--; +} + + +void mmd_export_token_odf_raw(DString * out, const char * source, token * t, scratch_pad * scratch) { + if (t == NULL) + return; + + switch (t->type) { + case AMPERSAND: + print_const("&"); + break; + case AMPERSAND_LONG: + print_const("&amp;"); + break; + case ANGLE_RIGHT: + print_const(">"); + break; + case ANGLE_LEFT: + print_const("<"); + break; + case ESCAPED_CHARACTER: + print_const("\\"); + mmd_print_char_odf(out, source[t->start + 1]); + break; + case QUOTE_DOUBLE: + print_const("""); + break; + case CODE_FENCE: + if (t->next) + t->next->type = TEXT_EMPTY; + case TEXT_EMPTY: + break; + case TEXT_NL: + print_const(""); + break; + default: + if (t->child) + mmd_export_token_tree_odf_raw(out, source, t->child, scratch); + else + print_token(t); + break; + } +} + + +void mmd_export_token_tree_odf_raw(DString * out, const char * source, token * t, scratch_pad * scratch) { + while (t != NULL) { + if (scratch->skip_token) { + scratch->skip_token--; + } else { + mmd_export_token_odf_raw(out, source, t, scratch); + } + + t = t->next; + } +} + + +void mmd_start_complete_odf(DString * out, const char * source, scratch_pad * scratch) { + print_const("\n" \ +"\n"); + + /* Font Declarations */ + print_const("\n" \ + " \n" \ + "\n"); + + /* Append basic style information */ + print_const("\n" \ + "\n" \ + " \n" \ + " \n" \ + "\n" \ + " \n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + " \n" \ + "\n" \ + "" \ + " " \ + " \n" \ + " \n" \ + " \n" \ + "\n"); + + /* Automatic style information */ + print_const("" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + "\n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + "\n" \ + "\n" \ + "\n" \ + "\n" \ + "\n" \ + " \n" \ + "\n" \ + "\n" \ + "\n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + " \n" \ + " Bibliography\n" \ + " \n" \ + " \n"); + + + + print_const("\n\n"); +} + + +void mmd_end_complete_odf(DString * out, const char * source, scratch_pad * scratch) { + print_const("\n\n"); +} + diff --git a/Sources/libMultiMarkdown/odf.h b/Sources/libMultiMarkdown/odf.h new file mode 100644 index 0000000..0d69030 --- /dev/null +++ b/Sources/libMultiMarkdown/odf.h @@ -0,0 +1,81 @@ +/** + + MultiMarkdown -- Lightweight markup processor to produce HTML, odf, and more. + + @file odf.h + + @brief Convert token tree to Flat OpenDocument (ODF/FODT) output + + + @author Fletcher T. Penney + @bug + +**/ + +/* + + Copyright © 2016 - 2017 Fletcher T. Penney. + + + The `MultiMarkdown 6` project is released under the MIT License.. + + GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project: + + https://github.com/fletcher/MultiMarkdown-4/ + + MMD 4 is released under both the MIT License and GPL. + + + CuTest is released under the zlib/libpng license. See CuTest.c for the + text of the license. + + + ## The MIT License ## + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +*/ + + +#ifndef ODF_MULTIMARKDOWN_H +#define ODF_MULTIMARKDOWN_H + + +#include "d_string.h" +#include "token.h" +#include "writer.h" + +void mmd_export_token_odf(DString * out, const char * source, token * t, scratch_pad * scratch); +void mmd_export_token_tree_odf(DString * out, const char * source, token * t, scratch_pad * scratch); + +void mmd_export_token_odf_raw(DString * out, const char * source, token * t, scratch_pad * scratch); +void mmd_export_token_tree_odf_raw(DString * out, const char * source, token * t, scratch_pad * scratch); + +void mmd_export_citation_list_odf(DString * out, const char * source, scratch_pad * scratch); +void mmd_export_footnote_list_odf(DString * out, const char * source, scratch_pad * scratch); + +void mmd_start_complete_odf(DString * out, const char * source, scratch_pad * scratch); +void mmd_end_complete_odf(DString * out, const char * source, scratch_pad * scratch); + +void mmd_export_citation_list_odf(DString * out, const char * source, scratch_pad * scratch); + + +#endif diff --git a/Sources/libMultiMarkdown/writer.c b/Sources/libMultiMarkdown/writer.c index 637e417..74ce6e3 100644 --- a/Sources/libMultiMarkdown/writer.c +++ b/Sources/libMultiMarkdown/writer.c @@ -67,6 +67,7 @@ #include "latex.h" #include "memoir.h" #include "mmd.h" +#include "odf.h" #include "scanners.h" #include "token.h" #include "writer.h" @@ -134,6 +135,8 @@ scratch_pad * scratch_pad_new(mmd_engine * e, short format) { p->base_header_level = 1; + p->odf_para_type = BLOCK_PARA; + // Store links in a hash for rapid retrieval when exporting p->link_hash = NULL; link * l; @@ -1665,6 +1668,13 @@ void mmd_export_token_tree(DString * out, mmd_engine * e, short format) { mmd_end_complete_latex(out, e->dstr->str, scratch); break; + case FORMAT_ODF: + mmd_start_complete_odf(out, e->dstr->str, scratch); + + mmd_export_token_tree_odf(out, e->dstr->str, e->root, scratch); + + mmd_end_complete_odf(out, e->dstr->str, scratch); + break; } scratch_pad_free(scratch); diff --git a/Sources/libMultiMarkdown/writer.h b/Sources/libMultiMarkdown/writer.h index 8932205..61d0eb6 100644 --- a/Sources/libMultiMarkdown/writer.h +++ b/Sources/libMultiMarkdown/writer.h @@ -116,7 +116,7 @@ typedef struct { short table_cell_count; char table_alignment[kMaxTableColumns]; - char _PADDING[4]; //!< pad struct for alignment + short odf_para_type; } scratch_pad; diff --git a/Sources/multimarkdown/main.c b/Sources/multimarkdown/main.c index fceebfe..285ef10 100644 --- a/Sources/multimarkdown/main.c +++ b/Sources/multimarkdown/main.c @@ -189,7 +189,7 @@ int main(int argc, char** argv) { a_rem1 = arg_rem("", ""), - a_format = arg_str0("t", "to", "FORMAT", "convert to FORMAT, FORMAT = html|latex|beamer|memoir|mmd"), + a_format = arg_str0("t", "to", "FORMAT", "convert to FORMAT, FORMAT = html|latex|beamer|memoir|mmd|odf"), a_o = arg_file0("o", "output", "FILE", "send output to FILE"), a_batch = arg_lit0("b", "batch", "process each file separately"), @@ -298,6 +298,8 @@ int main(int argc, char** argv) { format = FORMAT_MEMOIR; else if (strcmp(a_format->sval[0], "mmd") == 0) format = FORMAT_MMD; + else if (strcmp(a_format->sval[0], "odf") == 0) + format = FORMAT_ODF; else { // No valid format found fprintf(stderr, "%s: Unknown output format '%s'\n", binname, a_format->sval[0]); @@ -352,6 +354,9 @@ int main(int argc, char** argv) { case FORMAT_MEMOIR: output_filename = filename_with_extension(a_file->filename[i], ".tex"); break; + case FORMAT_ODF: + output_filename = filename_with_extension(a_file->filename[i], ".fodt"); + break; case FORMAT_MMD: output_filename = filename_with_extension(a_file->filename[i], ".mmdtext"); break; diff --git a/tests/MMD6Tests/Advanced Emph and Strong.fodt b/tests/MMD6Tests/Advanced Emph and Strong.fodt new file mode 100644 index 0000000..84bac2e --- /dev/null +++ b/tests/MMD6Tests/Advanced Emph and Strong.fodt @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bibliography + + + + +foobar + +foo*bar** + +foo*bar*** + +foo**bar* + +foobar + +5 + +foo**bar*** + +foo***bar* + +foo***bar** + +foobar + +foobar + +10 + +*foo**bar + +foobar* + +foo**bar + +foobar + +foo****bar + +15 + +foobarfoo + +**foo*bar + +foo*bar + +foobar** + +foobar + +20 + +foo****bar + +foo foobarfoo foo + +foobar + +foobar + +abduct instead of abduct + +25 + + + diff --git a/tests/MMD6Tests/Advanced Fenced Code Blocks.fodt b/tests/MMD6Tests/Advanced Fenced Code Blocks.fodt new file mode 100644 index 0000000..c08992a --- /dev/null +++ b/tests/MMD6Tests/Advanced Fenced Code Blocks.fodt @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bibliography + + + + +foo *2* + +foo *3* + +foo *4* + +foo *5* + +foo *6* + +5 + +``foo *3*`` + +```foo *4*``` + +````foo *5*```` + + + +foo 6 + + + +10 + +foo + +foo + +foo + + diff --git a/tests/MMD6Tests/Advanced Headers.fodt b/tests/MMD6Tests/Advanced Headers.fodt new file mode 100644 index 0000000..a104bda --- /dev/null +++ b/tests/MMD6Tests/Advanced Headers.fodt @@ -0,0 +1,281 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bibliography + + + + +foo + +foo + +foo bar + +foo bar + +foo bar + +5 + +foo + +foo bar + +foo +bar + +bar + +foo + + + diff --git a/tests/MMD6Tests/Amps and Angles.fodt b/tests/MMD6Tests/Amps and Angles.fodt new file mode 100644 index 0000000..4e0062c --- /dev/null +++ b/tests/MMD6Tests/Amps and Angles.fodt @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bibliography + + + + +AT&T has an ampersand in their name. + +AT&T is another way to write it. + +This & that. + +4 < 5. + +6 > 5. + +5 + +Here is a link with an ampersand in the URL. + +Here is a link with an amersand in the link text: AT&T. + +Here is an inline link. + +Here is an inline link. + +& and &amp; and < and > in code block. + +10 + + + diff --git a/tests/MMD6Tests/Automatic Links.fodt b/tests/MMD6Tests/Automatic Links.fodt new file mode 100644 index 0000000..1c698cb --- /dev/null +++ b/tests/MMD6Tests/Automatic Links.fodt @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bibliography + + + + +http://foo.com/ + +foo@bar.com + +mailto:foo@bar.com + + + diff --git a/tests/MMD6Tests/Automatic Links.html b/tests/MMD6Tests/Automatic Links.html index 7964b5c..245fed9 100644 --- a/tests/MMD6Tests/Automatic Links.html +++ b/tests/MMD6Tests/Automatic Links.html @@ -1,3 +1,5 @@

http://foo.com/

-

foo@bar.com

+

foo@bar.com

+ +

mailto:foo@bar.com

diff --git a/tests/MMD6Tests/Automatic Links.htmlc b/tests/MMD6Tests/Automatic Links.htmlc index 7964b5c..245fed9 100644 --- a/tests/MMD6Tests/Automatic Links.htmlc +++ b/tests/MMD6Tests/Automatic Links.htmlc @@ -1,3 +1,5 @@

http://foo.com/

-

foo@bar.com

+

foo@bar.com

+ +

mailto:foo@bar.com

diff --git a/tests/MMD6Tests/Automatic Links.tex b/tests/MMD6Tests/Automatic Links.tex index d863de3..c4fa4a5 100644 --- a/tests/MMD6Tests/Automatic Links.tex +++ b/tests/MMD6Tests/Automatic Links.tex @@ -1,3 +1,5 @@ \href{http://foo.com/}{http:\slash \slash foo.com\slash } \href{mailto:foo@bar.com}{foo@bar.com} + +\href{mailto:foo@bar.com}{mailto:foo@bar.com} diff --git a/tests/MMD6Tests/Automatic Links.text b/tests/MMD6Tests/Automatic Links.text index 60da45f..5621bbc 100644 --- a/tests/MMD6Tests/Automatic Links.text +++ b/tests/MMD6Tests/Automatic Links.text @@ -1,3 +1,5 @@ + + diff --git a/tests/MMD6Tests/Basic Blocks.fodt b/tests/MMD6Tests/Basic Blocks.fodt new file mode 100644 index 0000000..10fab23 --- /dev/null +++ b/tests/MMD6Tests/Basic Blocks.fodt @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bibliography + + + + +foo + +Heading foo + +Heading + +foo +bar + +5 + +foo +bar + +foo +bar + + + diff --git a/tests/MMD6Tests/Basic Lists.fodt b/tests/MMD6Tests/Basic Lists.fodt new file mode 100644 index 0000000..d6a9946 --- /dev/null +++ b/tests/MMD6Tests/Basic Lists.fodt @@ -0,0 +1,363 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bibliography + + + + + +foo + +foo + +foo + + +bar + + +foo + + +foo + + +foo + + + +bar + + +foo + + +foo + + +foo + + + +5 + + +foo + +foo + +foo + + +bar + + +foo + + +foo + + +foo + + + +bar + + +foo + + +foo + + +foo + + + +10 + + +foo + +foo + +foo + + +bar + + +foo + +foo + +foo + + +bar + + +foo +bar + +foo +bar + +foo +bar + + +15 + + + diff --git a/tests/MMD6Tests/Blockquotes.fodt b/tests/MMD6Tests/Blockquotes.fodt new file mode 100644 index 0000000..bb8c76d --- /dev/null +++ b/tests/MMD6Tests/Blockquotes.fodt @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bibliography + + + + +foo +bar + +foo + +bar +foo + +foo + +bar + +foo + +foo +bar + foo + + barfoo + +bar + + diff --git a/tests/MMD6Tests/Code Spans.fodt b/tests/MMD6Tests/Code Spans.fodt new file mode 100644 index 0000000..407e3d8 --- /dev/null +++ b/tests/MMD6Tests/Code Spans.fodt @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bibliography + + + + +foo + +foo ` bar + +`` + +foo``bar + +``foo` + +5 + +`foo`` + +foo + +foo barbaz + +foo `` bar + +foo + +10 + +foo bar + +*foo* + +[foo] + +-<>--&\&---... + +`foo` + +