]> granicus.if.org Git - multimarkdown/commitdiff
ADDED: Start working on LaTeX output
authorFletcher T. Penney <fletcher@fletcherpenney.net>
Sun, 12 Feb 2017 05:11:54 +0000 (00:11 -0500)
committerFletcher T. Penney <fletcher@fletcherpenney.net>
Sun, 12 Feb 2017 05:11:54 +0000 (00:11 -0500)
21 files changed:
CMakeLists.txt
src/html.c
src/latex.c [new file with mode: 0644]
src/latex.h [new file with mode: 0644]
src/main.c
src/writer.c
tests/MMD6Tests/Advanced Emph and Strong.tex [new file with mode: 0644]
tests/MMD6Tests/Advanced Fenced Code Blocks.tex [new file with mode: 0644]
tests/MMD6Tests/Advanced Headers.tex [new file with mode: 0644]
tests/MMD6Tests/Amps and Angles.tex [new file with mode: 0644]
tests/MMD6Tests/Basic Blocks.tex [new file with mode: 0644]
tests/MMD6Tests/Basic Lists.tex [new file with mode: 0644]
tests/MMD6Tests/Blockquotes.tex [new file with mode: 0644]
tests/MMD6Tests/Code Spans.tex [new file with mode: 0644]
tests/MMD6Tests/Dutch.tex [new file with mode: 0644]
tests/MMD6Tests/French.tex [new file with mode: 0644]
tests/MMD6Tests/German Guillemets.tex [new file with mode: 0644]
tests/MMD6Tests/German.tex [new file with mode: 0644]
tests/MMD6Tests/Smart Quotes.tex [new file with mode: 0644]
tests/MMD6Tests/Swedish.tex [new file with mode: 0644]
tests/MMD6Tests/Transclusion.tex [new file with mode: 0644]

index 9a1c812aa0ad00764271fbc548693e28641f8c9c..4c2d444eba89c25efd01787c2c0fe52c08860e8a 100644 (file)
@@ -171,6 +171,7 @@ set(src_files
        src/char.c
        src/d_string.c
        src/html.c
+       src/latex.c
        src/lexer.c
        src/mmd.c
        src/object_pool.c
@@ -190,6 +191,7 @@ set(header_files
        src/d_string.h
        src/char.h
        src/html.h
+       src/latex.h
        src/lexer.h
        src/libMultiMarkdown.h
        src/mmd.h
@@ -442,6 +444,8 @@ ADD_MMD_TEST(mmd-6 "" MMD6Tests html)
 
 ADD_MMD_TEST(mmd-6-compat "-c" MMD6Tests htmlc)
 
+ADD_MMD_TEST(mmd-6-latex "-t latex" MMD6Tests tex)
+
 ADD_MMD_TEST(pathologic "" ../build html)
 
 ADD_MMD_TEST(pathologic-compat "-c" ../build html)
index 4e4694243a3a295bad747a361f5f4462734e88fa..8d5488138b992466b54db276fdb91f8d6c5d27d3 100644 (file)
@@ -70,7 +70,6 @@
 #define print(x) d_string_append(out, x)
 #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 + offset]), t->len)
 #define print_token(t) d_string_append_c_array(out, &(source[t->start]), t->len)
 #define print_localized(x) mmd_print_localized_char_html(out, x, scratch)
 
diff --git a/src/latex.c b/src/latex.c
new file mode 100644 (file)
index 0000000..3598a71
--- /dev/null
@@ -0,0 +1,866 @@
+/**
+
+       MultiMarkdown 6 -- Lightweight markup processor to produce HTML, LaTeX, and more.
+
+       @file latex.c
+
+       @brief Convert token tree to LaTeX 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>    
+
+#include "char.h"
+#include "i18n.h"
+#include "latex.h"
+#include "parser.h"
+
+
+#define print(x) d_string_append(out, x)
+#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_latex(out, x, scratch)
+
+
+void mmd_print_char_latex(DString * out, char c) {
+       switch (c) {
+               case '\\':
+                       print("\\textbackslash{}");
+                       break;
+               default:
+                       print_char(c);
+                       break;
+       }
+}
+
+
+void mmd_print_string_latex(DString * out, const char * str) {
+       while (*str != '\0') {
+               mmd_print_char_latex(out, *str);
+               str++;
+       }
+}
+
+
+void mmd_print_localized_char_latex(DString * out, unsigned short type, scratch_pad * scratch) {
+       switch (type) {
+               case DASH_N:
+                       print("--");
+                       break;
+               case DASH_M:
+                       print("---");
+                       break;
+               case ELLIPSIS:
+                       print("{\\ldots}");
+                       break;
+               case APOSTROPHE:
+                       print("'");
+                       break;
+               case QUOTE_LEFT_SINGLE:
+                       switch (scratch->quotes_lang) {
+                               case SWEDISH:
+                                       print("'");
+                                       break;
+                               case FRENCH:
+                                       print("'");
+                                       break;
+                               case GERMAN:
+                                       print("‚");
+                                       break;
+                               case GERMANGUILL:
+                                       print("›");
+                                       break;
+                               default:
+                                       print("`");
+                               }
+                       break;
+               case QUOTE_RIGHT_SINGLE:
+                       switch (scratch->quotes_lang) {
+                               case GERMAN:
+                                       print("`");
+                                       break;
+                               case GERMANGUILL:
+                                       print("‹");
+                                       break;
+                               default:
+                                       print("'");
+                               }
+                       break;
+               case QUOTE_LEFT_DOUBLE:
+                       switch (scratch->quotes_lang) {
+                               case DUTCH:
+                               case GERMAN:
+                                       print("„");
+                                       break;
+                               case GERMANGUILL:
+                                       print("»");
+                                       break;
+                               case FRENCH:
+                                       print("«");
+                                       break;
+                               case SWEDISH:
+                                       print("''");
+                                       break;
+                               default:
+                                       print("``");
+                               }
+                       break;
+               case QUOTE_RIGHT_DOUBLE:
+                       switch (scratch->quotes_lang) {
+                               case GERMAN:
+                                       print("``");
+                                       break;
+                               case GERMANGUILL:
+                                       print("«");
+                                       break;
+                               case FRENCH:
+                                       print("»");
+                                       break;
+                               case SWEDISH:
+                               case DUTCH:
+                               default:
+                                       print("''");
+                               }
+                       break;
+       }
+}
+
+
+
+void mmd_export_link_latex(DString * out, const char * source, token * text, link * link, scratch_pad * scratch) {
+       attr * a = link->attributes;
+
+       if (link->url) {
+               print("<a href=\"");
+               mmd_print_string_latex(out, link->url);
+               print("\"");
+       } else
+               print("<a href=\"\"");
+
+       if (link->title && link->title[0] != '\0') {
+               print(" title=\"");
+               mmd_print_string_latex(out, link->title);
+               print("\"");
+       }
+
+       while (a) {
+               print(" ");
+               print(a->key);
+               print("=\"");
+               print(a->value);
+               print("\"");
+               a = a->next;
+       }
+
+       print(">");
+
+       // 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_latex(out, source, text->child, scratch);
+
+       print("</a>");
+}
+
+
+void mmd_export_image_latex(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 <p> markers
+               d_string_erase(out, out->currentStringLength - 3, 3);
+               print("<figure>\n");
+               scratch->close_para = false;
+       }
+
+       if (link->url)
+               printf("<img src=\"%s\"", link->url);
+       else
+               print("<img src=\"\"");
+
+       if (text) {
+               print(" alt=\"");
+               print_token_tree_raw(out, source, text->child);
+               print("\"");
+       }
+
+       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(" ");
+               print(a->key);
+               print("=\"");
+               print(a->value);
+               print("\"");
+               a = a->next;
+       }
+
+       print(" />");
+
+       if (is_figure) {
+               if (text) {
+                       print("\n<figcaption>");
+                       mmd_export_token_tree_latex(out, source, text->child, scratch);
+                       print("</figcaption>");
+               }
+               print("\n</figure>");
+       }
+}
+
+
+void mmd_export_token_latex(DString * out, const char * source, token * t, scratch_pad * scratch) {
+       if (t == NULL)
+               return;
+
+       short   temp_short;
+       short   temp_short2;
+       link *  temp_link       = NULL;
+       char *  temp_char       = NULL;
+       char *  temp_char2      = NULL;
+       bool    temp_bool       = 0;
+       token * temp_token      = NULL;
+
+       switch (t->type) {
+               case AMPERSAND:
+               case AMPERSAND_LONG:
+                       print("\\&");
+                       break;
+               case ANGLE_LEFT:
+                       print("$<$");
+                       break;
+               case ANGLE_RIGHT:
+                       print("$>$");
+                       break;
+               case APOSTROPHE:
+                       if (!(scratch->extensions & EXT_SMART)) {
+                               print_token(t);
+                       } else {
+                               print_localized(APOSTROPHE);
+                       }
+                       break;
+               case BACKTICK:
+                       print_token(t);
+                       break;
+               case BLOCK_BLOCKQUOTE:
+                       pad(out, 2, scratch);
+                       print("\\begin{quote}\n");
+                       scratch->padded = 2;
+                       mmd_export_token_tree_latex(out, source, t->child, scratch);
+                       pad(out, 1, scratch);
+                       print("\\end{quote}");
+                       scratch->padded = 0;
+                       break;
+               case BLOCK_CODE_FENCED:
+                       pad(out, 2, scratch);
+
+                       temp_char = get_fence_language_specifier(t->child->child, source);
+                       if (temp_char) {
+                               printf("\\begin{lstlisting}[language=%s]\n", temp_char);
+                       } else {
+                               print("\\begin{verbatim}\n");
+                       }
+
+                       mmd_export_token_tree_latex_raw(out, source, t->child->next, scratch);
+
+                       if (temp_char) {
+                               print("\\end{lstlisting}");
+                               free(temp_char);
+                       } else {
+                               print("\\end{verbatim}");
+                       }
+                       scratch->padded = 0;
+                       break;
+               case BLOCK_CODE_INDENTED:
+                       pad(out, 2, scratch);
+                       print("\\begin{verbatim}\n");
+                       mmd_export_token_tree_latex_raw(out, source, t->child, scratch);
+                       print("\\end{verbatim}");
+                       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;
+                       }
+
+                       switch (temp_short + scratch->base_header_level - 1) {
+                               case 1:
+                                       print("\\part{");
+                                       break;
+                               case 2:
+                                       print("\\chapter{");
+                                       break;
+                               case 3:
+                                       print("\\section{");
+                                       break;
+                               case 4:
+                                       print("\\subsection{");
+                                       break;
+                               case 5:
+                                       print("\\subsubsection{");
+                                       break;
+                               case 6:
+                                       print("\\paragraph{");
+                                       break;
+                               case 7:
+                                       print("\\subparagraph{");
+                                       break;
+                       }
+
+                       mmd_export_token_tree_latex(out, source, t->child, scratch);
+
+                       if (scratch->extensions & EXT_NO_LABELS) {
+                               print("}");
+                       } else {
+                               temp_token = manual_label_from_header(t, source);
+                               if (temp_token) {
+                                       temp_char = label_from_token(source, temp_token);
+                               } else {
+                                       temp_char = label_from_token(source, t);
+                               }
+                               printf("}\n\\label{%s}", temp_char);
+                               free(temp_char);
+                       }
+                       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("\\begin{itemize}");
+                       scratch->padded = 1;
+                       mmd_export_token_tree_latex(out, source, t->child, scratch);
+                       pad(out, 2, scratch);
+                       print("\\end{itemize}");
+                       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("\\begin{enumerate}");
+                       scratch->padded = 1;
+                       mmd_export_token_tree_latex(out, source, t->child, scratch);
+                       pad(out, 2, scratch);
+                       print("\\end{enumerate}");
+                       scratch->padded = 0;
+                       scratch->list_is_tight = temp_short;
+                       break;
+               case BLOCK_LIST_ITEM:
+                       pad(out, 2, scratch);
+                       print("\\item ");
+                       scratch->padded = 2;
+                       mmd_export_token_tree_latex(out, source, t->child, scratch);
+                       scratch->padded = 0;
+                       break;
+               case BLOCK_LIST_ITEM_TIGHT:
+                       pad(out, 2, scratch);
+                       print("\\item ");
+                       scratch->padded = 2;
+                       mmd_export_token_tree_latex(out, source, t->child, scratch);
+                       scratch->padded = 0;
+                       break;
+               case BLOCK_PARA:
+                       pad(out, 2, scratch);
+                       mmd_export_token_tree_latex(out, source, t->child, scratch);
+                       scratch->padded = 0;
+                       break;
+               case BRACE_DOUBLE_LEFT:
+                       print("\\{\\{");
+                       break;
+               case BRACE_DOUBLE_RIGHT:
+                       print("\\}\\}");
+                       break;
+               case BRACKET_LEFT:
+                       print("[");                     
+                       break;
+               case BRACKET_CITATION_LEFT:
+                       print("[#");
+                       break;
+               case BRACKET_FOOTNOTE_LEFT:
+                       print("[^");
+                       break;
+               case BRACKET_IMAGE_LEFT:
+                       print("![");
+                       break;
+               case BRACKET_VARIABLE_LEFT:
+                       print("[\%");
+                       break;
+               case BRACKET_RIGHT:
+                       print("]");
+                       break;
+               case CODE_FENCE:
+                       break;
+               case COLON:
+                       print(":");
+                       break;
+               case DASH_M:
+                       if (!(scratch->extensions & EXT_SMART)) {
+                               print_token(t);
+                       } else {
+                               print_localized(DASH_M);
+                       }
+                       break;
+               case DASH_N:
+                       if (!(scratch->extensions & EXT_SMART)) {
+                               print_token(t);
+                       } else {
+                               print_localized(DASH_N);
+                       }
+                       break;
+               case DOC_START_TOKEN:
+                       mmd_export_token_tree_latex(out, source, t->child, scratch);
+                       break;
+               case ELLIPSIS:
+                       if (!(scratch->extensions & EXT_SMART)) {
+                               print_token(t);
+                       } else {
+                               print_localized(ELLIPSIS);
+                       }
+                       break;
+               case EMPH_START:
+                       print("\\emph{");
+                       break;
+               case EMPH_STOP:
+                       print("}");
+                       break;
+               case ESCAPED_CHARACTER:
+                       mmd_print_char_latex(out, source[t->start + 1]);
+                       break;
+               case HASH1:
+               case HASH2:
+               case HASH3:
+               case HASH4:
+               case HASH5:
+               case HASH6:
+                       print_token(t);
+                       break;
+               case LINE_LIST_BULLETED:
+               case LINE_LIST_ENUMERATED:
+                       mmd_export_token_tree_latex(out, source, t->child, scratch);
+                       break;
+               case MANUAL_LABEL:
+                       break;
+               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_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("\\texttt{");
+                       mmd_export_token_tree_latex_raw(out, source, t->child, scratch);
+                       print("}");
+                       break;
+               case PAIR_BRACES:
+                       mmd_export_token_tree_latex(out, source, t->child, scratch);
+                       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_latex(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_latex(out, source, t, temp_link, scratch, false);
+                                       } else {
+                                               mmd_export_image_latex(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_latex(out, source, t->child, scratch);
+                       break;
+               case PAIR_BRACKET_CITATION:
+                       parse_citation:
+                       temp_bool = true;
+
+                       if (t->type == PAIR_BRACKET) {
+                               // This is a locator for subsequent citation
+                               temp_char = text_inside_pair(source, t);
+                               temp_char2 = label_from_string(temp_char);
+
+                               if (strcmp(temp_char2, "notcited") == 0) {
+                                       free(temp_char2);
+                                       free(temp_char);
+                                       temp_char = strdup("");
+                                       temp_bool = false;
+                               }
+
+                               if (temp_char[0] == '\0')
+                                       temp_char2 = strdup("");
+                               else
+                                       temp_char2 = strdup(", ");
+
+
+                               // Process the actual citation
+                               t = t->next;
+                       } else {
+                               temp_char = strdup("");
+                               temp_char2 = strdup("");
+                       }
+
+                       if (scratch->extensions & EXT_NOTES) {
+                               citation_from_bracket(source, scratch, t, &temp_short);
+
+                               if (temp_bool) {
+                                       if (temp_short < scratch->used_citations->size) {
+                                               // Re-using previous citation
+                                               printf("<a href=\"#cn:%d\" title=\"%s\" class=\"citation\">[%s%s%d]</a>",
+                                                               temp_short, LC("see citation"), temp_char, temp_char2, temp_short);
+                                       } else {
+                                               // This is a new citation
+                                               printf("<a href=\"#cn:%d\" id=\"cnref:%d\" title=\"%s\" class=\"citation\">[%s%s%d]</a>",
+                                                               temp_short, temp_short, LC("see citation"), temp_char, temp_char2, temp_short);
+                                       }
+                               }
+
+                               if (t->prev && (t->prev->type == PAIR_BRACKET)) {
+                                       // Skip citation on next pass
+                                       scratch->skip_token = 1;
+                               }
+                       } else {
+                               // Footnotes disabled
+                               mmd_export_token_tree_latex(out, source, t->child, scratch);
+                       }
+
+                       free(temp_char);
+                       free(temp_char2);
+                       break;
+               case PAIR_BRACKET_FOOTNOTE:
+                       if (scratch->extensions & EXT_NOTES) {
+                               footnote_from_bracket(source, scratch, t, &temp_short);
+
+                               if (temp_short < scratch->used_footnotes->size) {
+                                       // Re-using previous footnote
+                                       printf("<a href=\"#fn:%d\" title=\"%s\" class=\"footnote\">[%d]</a>",
+                                                  temp_short, LC("see footnote"), temp_short);
+                               } else {
+                                       // This is a new footnote
+                                       printf("<a href=\"#fn:%d\" id=\"fnref:%d\" title=\"%s\" class=\"footnote\">[%d]</a>",
+                                                  temp_short, temp_short, LC("see footnote"), temp_short);
+                               }
+                       } else {
+                               // Footnotes disabled
+                               mmd_export_token_tree_latex(out, source, t->child, scratch);
+                       }
+                       break;
+               case PAIR_MATH:
+               case PAIR_PAREN:
+               case PAIR_QUOTE_DOUBLE:
+               case PAIR_QUOTE_SINGLE:
+               case PAIR_STAR:
+               case PAIR_UL:
+                       mmd_export_token_tree_latex(out, source, t->child, scratch);
+                       break;
+               case PAREN_LEFT:
+                       print("(");
+                       break;
+               case PAREN_RIGHT:
+                       print(")");
+                       break;
+               case PIPE:
+                       print_token(t);
+                       break;
+               case PLUS:
+                       print_token(t);
+                       break;
+               case QUOTE_SINGLE:
+                       if ((t->mate == NULL) || (!(scratch->extensions & EXT_SMART)))
+                               print("'");
+                       else
+                               (t->start < t->mate->start) ? ( print_localized(QUOTE_LEFT_SINGLE) ) : ( print_localized(QUOTE_RIGHT_SINGLE) );
+                       break;
+               case QUOTE_DOUBLE:
+                       if ((t->mate == NULL) || (!(scratch->extensions & EXT_SMART)))
+                               print("&quot;");
+                       else
+                               (t->start < t->mate->start) ? ( print_localized(QUOTE_LEFT_DOUBLE) ) : ( print_localized(QUOTE_RIGHT_DOUBLE) );
+                       break;
+               case QUOTE_RIGHT_ALT:
+                       if ((t->mate == NULL) || (!(scratch->extensions & EXT_SMART)))
+                               print("''");
+                       else
+                               print_localized(QUOTE_RIGHT_DOUBLE);
+                       break;
+               case STAR:
+                       print_token(t);
+                       break;
+               case STRONG_START:
+                       print("\\textbf{");
+                       break;
+               case STRONG_STOP:
+                       print("}");
+                       break;
+               case TEXT_EMPTY:
+                       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_latex(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_latex(out, source, t, scratch);
+               }
+
+               t = t->next;
+       }
+
+       scratch->recurse_depth--;
+}
+
+
+void mmd_export_token_latex_raw(DString * out, const char * source, token * t, scratch_pad * scratch) {
+       if (t == NULL)
+               return;
+
+       switch (t->type) {
+               case AMPERSAND:
+                       print("\\&");
+                       break;
+               case AMPERSAND_LONG:
+                       print("\\textbackslash{}&");
+                       break;
+               case ANGLE_LEFT:
+                       print("$<$");
+                       break;
+               case ANGLE_RIGHT:
+                       print("$>$");
+                       break;
+               case DASH_N:
+                       print("-{}-");
+                       break;
+               case DASH_M:
+                       print("-{}-{}-");
+                       break;
+               case ESCAPED_CHARACTER:
+                       print("\\textbackslash{}");
+                       mmd_print_char_latex(out, source[t->start + 1]);
+                       break;
+               case CODE_FENCE:
+                       if (t->next)
+                               t->next->type = TEXT_EMPTY;
+               case TEXT_EMPTY:
+                       break;
+               default:
+                       if (t->child)
+                               mmd_export_token_tree_latex_raw(out, source, t->child, scratch);
+                       else
+                               print_token(t);
+                       break;
+       }
+}
+
+
+void mmd_export_token_tree_latex_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_latex_raw(out, source, t, scratch);
+               }
+
+               t = t->next;
+       }
+}
+
+
+void mmd_start_complete_latex(DString * out, const char * source, scratch_pad * scratch) {
+
+}
+
+
+void mmd_end_complete_latex(DString * out, const char * source, scratch_pad * scratch) {
+
+}
+
diff --git a/src/latex.h b/src/latex.h
new file mode 100644 (file)
index 0000000..ed2d38c
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+
+       MultiMarkdown 6 -- Lightweight markup processor to produce HTML, LaTeX, and more.
+
+       @file latex.h
+
+       @brief Convert token tree to LaTeX 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 LATEX_MULTIMARKDOWN_6_H
+#define LATEX_MULTIMARKDOWN_6_H
+
+
+#include "d_string.h"
+#include "token.h"
+#include "writer.h"
+
+void mmd_export_token_latex(DString * out, const char * source, token * t, scratch_pad * scratch);
+void mmd_export_token_tree_latex(DString * out, const char * source, token * t, scratch_pad * scratch);
+
+
+void mmd_export_token_latex_raw(DString * out, const char * source, token * t, scratch_pad * scratch);
+void mmd_export_token_tree_latex_raw(DString * out, const char * source, token * t, scratch_pad * scratch);
+
+void mmd_export_citation_list_latex(DString * out, const char * source, scratch_pad * scratch);
+void mmd_export_footnote_list_latex(DString * out, const char * source, scratch_pad * scratch);
+
+void mmd_start_complete_latex(DString * out, const char * source, scratch_pad * scratch);
+void mmd_end_complete_latex(DString * out, const char * source, scratch_pad * scratch);
+
+
+#endif
index cf6f88ae8cd7b3b66f24f05fa602f10f56f25aaa..8a1fdfddee5238d4f099bc10b53cbf7a3d013b6a 100644 (file)
@@ -290,6 +290,8 @@ int main(int argc, char** argv) {
        if (a_format->count > 0) {
                if (strcmp(a_format->sval[0], "html") == 0)
                        format = FORMAT_HTML;
+               else if (strcmp(a_format->sval[0], "latex") == 0)
+                       format = FORMAT_LATEX;
                else {
                        // No valid format found
                        fprintf(stderr, "%s: Unknown output format '%s'\n", binname, a_format->sval[0]);
@@ -338,6 +340,9 @@ int main(int argc, char** argv) {
                                case FORMAT_HTML:
                                        output_filename = filename_with_extension(a_file->filename[i], ".html");
                                        break;
+                               case FORMAT_LATEX:
+                                       output_filename = filename_with_extension(a_file->filename[i], ".tex");
+                                       break;
                        }
 
                        // Perform transclusion(s)
index 7f8e53f96400fb3149265c48ee022469457b9f70..489f06207817e4c8dbba9bdbcca6c46dac4a639c 100644 (file)
@@ -63,6 +63,7 @@
 #include "d_string.h"
 #include "html.h"
 #include "i18n.h"
+#include "latex.h"
 #include "mmd.h"
 #include "scanners.h"
 #include "token.h"
@@ -1312,6 +1313,16 @@ void mmd_export_token_tree(DString * out, mmd_engine * e, short format) {
                        if (scratch->extensions & EXT_COMPLETE)
                                mmd_end_complete_html(out, e->dstr->str, scratch);
 
+                       break;
+               case FORMAT_LATEX:
+                       if (scratch->extensions & EXT_COMPLETE)
+                               mmd_start_complete_latex(out, e->dstr->str, scratch);
+
+                       mmd_export_token_tree_latex(out, e->dstr->str, e->root, scratch);
+
+                       if (scratch->extensions & EXT_COMPLETE)
+                               mmd_end_complete_latex(out, e->dstr->str, scratch);
+
                        break;
        }
 
diff --git a/tests/MMD6Tests/Advanced Emph and Strong.tex b/tests/MMD6Tests/Advanced Emph and Strong.tex
new file mode 100644 (file)
index 0000000..992d167
--- /dev/null
@@ -0,0 +1,59 @@
+foo\emph{bar}
+
+foo*bar**
+
+foo*bar***
+
+foo**bar*
+
+foo\textbf{bar}
+
+5
+
+foo**bar***
+
+foo***bar*
+
+foo***bar**
+
+foo\textbf{\emph{bar}}
+
+\emph{foo}bar
+
+10
+
+*foo**bar
+
+\emph{foo}bar*
+
+\emph{foo**bar}
+
+\emph{foo}\textbf{bar}
+
+\emph{foo****bar}
+
+15
+
+\emph{foo\textbf{bar}foo}
+
+**foo*bar
+
+\textbf{foo*bar}
+
+\textbf{foo}bar**
+
+\textbf{foo}\emph{bar}
+
+20
+
+\textbf{foo****bar}
+
+\textbf{foo foo\emph{bar}foo foo}
+
+\textbf{\emph{foo}bar}
+
+\emph{\textbf{foo}bar}
+
+\emph{ab}duct instead of ab\emph{duct}
+
+25
diff --git a/tests/MMD6Tests/Advanced Fenced Code Blocks.tex b/tests/MMD6Tests/Advanced Fenced Code Blocks.tex
new file mode 100644 (file)
index 0000000..83a298e
--- /dev/null
@@ -0,0 +1,59 @@
+\texttt{foo *2*}
+
+\begin{verbatim}
+foo *3*
+\end{verbatim}
+
+\begin{verbatim}
+foo *4*
+\end{verbatim}
+
+\begin{verbatim}
+foo *5*
+\end{verbatim}
+
+\begin{verbatim}
+foo *6*
+\end{verbatim}
+
+5
+
+\begin{verbatim}
+``
+foo *3*
+``
+\end{verbatim}
+
+\begin{verbatim}
+```
+foo *4*
+```
+\end{verbatim}
+
+\begin{verbatim}
+````
+foo *5*
+````
+\end{verbatim}
+
+\begin{verbatim}
+\end{verbatim}
+
+foo \emph{6}
+
+\begin{verbatim}
+\end{verbatim}
+
+10
+
+\begin{lstlisting}[language=perl]
+foo
+\end{lstlisting}
+
+\begin{lstlisting}[language=perl]
+foo
+\end{lstlisting}
+
+\begin{lstlisting}[language=perl]
+foo
+\end{lstlisting}
diff --git a/tests/MMD6Tests/Advanced Headers.tex b/tests/MMD6Tests/Advanced Headers.tex
new file mode 100644 (file)
index 0000000..3a996fa
--- /dev/null
@@ -0,0 +1,19 @@
+\part{foo  }
+\label{bar}
+
+\part{foo}
+\label{bar}
+
+\part{foo [bar][bat]}
+\label{foobarbat}
+
+\part{foo [bar][bat]}
+\label{bat}
+
+\part{foo [bar][bat]}
+\label{baz}
+
+5
+
+\part{foo }
+\label{bar}
diff --git a/tests/MMD6Tests/Amps and Angles.tex b/tests/MMD6Tests/Amps and Angles.tex
new file mode 100644 (file)
index 0000000..0ace92d
--- /dev/null
@@ -0,0 +1,25 @@
+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 \href{http://example.com/?foo=1&bar=2}{link}\footnote{\href{http://example.com/?foo=1\&bar=2}{http:/\slash example.com\slash ?foo=1\&bar=2}} with an ampersand in the URL.
+
+Here is a link with an amersand in the link text: \href{http://att.com/}{AT\&T}\footnote{\href{http://att.com/}{http:/\slash att.com\slash }}.
+
+Here is an inline \href{/script?foo=1&bar=2}{link}\footnote{\href{/script?foo=1\&bar=2}{\slash script?foo=1\&bar=2}}.
+
+Here is an inline \href{/script?foo=1&bar=2}{link}\footnote{\href{/script?foo=1\&bar=2}{\slash script?foo=1\&bar=2}}.
+
+\begin{verbatim}
+& and &amp; and < and > in code block.
+\end{verbatim}
+
+10
diff --git a/tests/MMD6Tests/Basic Blocks.tex b/tests/MMD6Tests/Basic Blocks.tex
new file mode 100644 (file)
index 0000000..4504d06
--- /dev/null
@@ -0,0 +1,18 @@
+foo
+
+\part{Heading \emph{foo} }
+\label{headingfoo}
+
+\chapter{Heading }
+\label{heading}
+
+foo
+bar
+
+5
+
+foo
+bar
+
+foo
+bar
diff --git a/tests/MMD6Tests/Basic Lists.tex b/tests/MMD6Tests/Basic Lists.tex
new file mode 100644 (file)
index 0000000..859543b
--- /dev/null
@@ -0,0 +1,101 @@
+\begin{itemize}
+\item foo
+
+\item foo
+
+\item foo
+
+\end{itemize}
+
+bar
+
+\begin{itemize}
+\item foo
+
+\item foo
+
+\item foo
+
+\end{itemize}
+
+bar
+
+\begin{itemize}
+\item foo
+
+\item foo
+
+\item foo
+
+\end{itemize}
+
+5
+
+\begin{enumerate}
+\item foo
+
+\item foo
+
+\item foo
+
+\end{enumerate}
+
+bar
+
+\begin{enumerate}
+\item foo
+
+\item foo
+
+\item foo
+
+\end{enumerate}
+
+bar
+
+\begin{enumerate}
+\item foo
+
+\item foo
+
+\item foo
+
+\end{enumerate}
+
+10
+
+\begin{itemize}
+\item foo
+
+\item foo
+
+\item foo
+
+\end{itemize}
+
+bar
+
+\begin{itemize}
+\item foo
+
+\item foo
+
+\item foo
+
+\end{itemize}
+
+bar
+
+\begin{itemize}
+\item foo
+bar
+
+\item foo
+bar
+
+\item foo
+bar
+
+\end{itemize}
+
+15
diff --git a/tests/MMD6Tests/Blockquotes.tex b/tests/MMD6Tests/Blockquotes.tex
new file mode 100644 (file)
index 0000000..d958092
--- /dev/null
@@ -0,0 +1,33 @@
+\begin{quote}
+
+foo
+ bar
+
+foo
+
+\begin{quote}
+
+bar
+foo
+\end{quote}
+
+foo
+
+\begin{quote}
+
+bar
+\end{quote}
+
+foo
+
+foo
+ bar
+ foo
+
+\begin{verbatim}
+  bar
+\end{verbatim}
+
+foo
+bar
+\end{quote}
diff --git a/tests/MMD6Tests/Code Spans.tex b/tests/MMD6Tests/Code Spans.tex
new file mode 100644 (file)
index 0000000..7a4db6d
--- /dev/null
@@ -0,0 +1,34 @@
+\texttt{foo}
+
+\texttt{foo ` bar}
+
+\texttt{``}
+
+\texttt{foo``bar}
+
+``foo`
+
+5
+
+`foo``
+
+\texttt{foo}
+
+\texttt{foo   bar
+baz}
+
+\texttt{foo `` bar}
+
+\texttt{foo}
+
+10
+
+\texttt{foo   bar}
+
+\texttt{*foo*}
+
+\texttt{[foo]}
+
+\texttt{-$<$$>$-{}-\&\textbackslash{}\&-{}-{}-...}
+
+\texttt{`foo`}
diff --git a/tests/MMD6Tests/Dutch.tex b/tests/MMD6Tests/Dutch.tex
new file mode 100644 (file)
index 0000000..37d3679
--- /dev/null
@@ -0,0 +1,20 @@
+\def\language{nl}
+
+`foo'
+
+``foo''
+
+foo's
+
+foo --- bar
+
+foo -- bar
+
+5
+
+foo{\ldots}
+
+\footnote{foo}
+
+~\citep{bar}
+\end{document}
diff --git a/tests/MMD6Tests/French.tex b/tests/MMD6Tests/French.tex
new file mode 100644 (file)
index 0000000..14b9036
--- /dev/null
@@ -0,0 +1,20 @@
+\def\language{fr}
+
+`foo'
+
+``foo''
+
+foo's
+
+foo --- bar
+
+foo -- bar
+
+5
+
+foo{\ldots}
+
+\footnote{foo}
+
+~\citep{bar}
+\end{document}
diff --git a/tests/MMD6Tests/German Guillemets.tex b/tests/MMD6Tests/German Guillemets.tex
new file mode 100644 (file)
index 0000000..4b08470
--- /dev/null
@@ -0,0 +1,20 @@
+\def\language{de}
+
+›foo‹
+
+»foo«
+
+foo's
+
+foo --- bar
+
+foo -- bar
+
+5
+
+foo{\ldots}
+
+\footnote{foo}
+
+~\citep{bar}
+\end{document}
diff --git a/tests/MMD6Tests/German.tex b/tests/MMD6Tests/German.tex
new file mode 100644 (file)
index 0000000..5ba80e7
--- /dev/null
@@ -0,0 +1,20 @@
+\def\language{de}
+
+`foo'
+
+``foo''
+
+foo's
+
+foo --- bar
+
+foo -- bar
+
+5
+
+foo{\ldots}
+
+\footnote{foo}
+
+~\citep{bar}
+\end{document}
diff --git a/tests/MMD6Tests/Smart Quotes.tex b/tests/MMD6Tests/Smart Quotes.tex
new file mode 100644 (file)
index 0000000..25db09d
--- /dev/null
@@ -0,0 +1,69 @@
+`foo'
+
+``foo''
+
+``foo'' `bar'
+
+`foo' ``bar''
+
+`.foo'
+
+5
+
+"foo".
+
+'foo'.
+
+`foo'
+
+``foo''
+
+```foo'''
+
+10
+
+``'' foo ``''?
+
+'' foo ''?
+
+isn't
+
+foo -- bar
+
+foo --- bar
+
+15
+
+1--2
+
+1--2
+
+1---3
+
+\texttt{-{}-}
+
+\texttt{-{}-{}-}
+
+20
+
+{\ldots}
+
+{\ldots}
+
+\texttt{...}
+
+\texttt{. . .}
+
+l'année l'année
+
+25
+
+l'été l'année
+
+l'été l'été
+
+l'année l'été
+
+foo's and bar's
+
+\texttt{foo}'s and \texttt{bar}'s
diff --git a/tests/MMD6Tests/Swedish.tex b/tests/MMD6Tests/Swedish.tex
new file mode 100644 (file)
index 0000000..096f20d
--- /dev/null
@@ -0,0 +1,20 @@
+\def\language{sv}
+
+`foo'
+
+``foo''
+
+foo's
+
+foo --- bar
+
+foo -- bar
+
+5
+
+foo{\ldots}
+
+\footnote{foo}
+
+~\citep{bar}
+\end{document}
diff --git a/tests/MMD6Tests/Transclusion.tex b/tests/MMD6Tests/Transclusion.tex
new file mode 100644 (file)
index 0000000..4b60e6c
--- /dev/null
@@ -0,0 +1,17 @@
+This text is included in \texttt{foo.txt}.
+
+This should not be transcluded to avoid an infinite loop -- \{\{foo.txt\}\}
+
+This text is included in \texttt{bar.txt}.
+
+This can be transcluded without causing an infinite loop -- \{\{foo.txt\}\}
+
+\begin{verbatim}
+This is a file with no metadata.
+\end{verbatim}
+
+\begin{verbatim}
+This is a file with no metadata.
+\end{verbatim}
+
+\{\{transclusion\slash bat.*\}\}